The Complete Request Lifecycle
From Click to Response
When a user clicks a link or submits a form, this sequence occurs:
1. DNS Resolution
Browser -> DNS -> IP Address
2. TCP Connection
Browser -> SYN -> Server -> SYN-ACK -> Browser -> ACK
3. TLS Handshake (HTTPS)
ClientHello -> ServerHello -> Key Exchange -> Encrypted
4. HTTP Request
Browser sends: METHOD + PATH + HEADERS + BODY
5. Web Server Processing
Apache/Nginx receives request
Checks .htaccess / nginx rules
Routes to PHP-FPM
6. PHP Processing
Autoloader loads classes
Router matches URL to controller
Controller executes action
View renders template
Response object created
7. HTTP Response
Status code + Headers + Body sent back
8. Browser Rendering
Parses HTML, loads CSS/JS, renders page
PHP: Capturing the Complete Request
<?php
// A complete request handler
class HttpRequest
{
private string $method;
private string $uri;
private array $headers;
private string $body;
private array $queryParams;
private array $serverParams;
public function __construct()
{
$this->method = $_SERVER['REQUEST_METHOD'];
$this->uri = $_SERVER['REQUEST_URI'];
$this->headers = getallheaders();
$this->body = file_get_contents('php://input');
$this->queryParams = $_GET;
$this->serverParams = $_SERVER;
}
public function getMethod(): string
{
return $this->method;
}
public function getUri(): string
{
return $this->uri;
}
public function getHeader(string $name): ?string
{
return $this->headers[$name] ?? null;
}
public function getBody(): string
{
return $this->body;
}
public function getJsonBody(): ?array
{
$data = json_decode($this->body, true);
return json_last_error() === JSON_ERROR_NONE ? $data : null;
}
public function getQuery(string $key, mixed $default = null): mixed
{
return $this->queryParams[$key] ?? $default;
}
}
// Usage
$request = new HttpRequest();
echo $request->getMethod(); // GET
echo $request->getUri(); // /catalog/product/view?id=123
echo $request->getHeader('Accept'); // text/html
PHP: Sending a Complete Response
<?php
class HttpResponse
{
private int $statusCode = 200;
private array $headers = [];
private string $body = '';
public function setStatusCode(int $code): self
{
$this->statusCode = $code;
return $this;
}
public function setHeader(string $name, string $value): self
{
$this->headers[$name] = $value;
return $this;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
public function send(): void
{
// Send status code
http_response_code($this->statusCode);
// Send headers
foreach ($this->headers as $name => $value) {
header("$name: $value");
}
// Send body
echo $this->body;
}
}
// Usage
$response = new HttpResponse();
$response->setStatusCode(200)
->setHeader('Content-Type', 'application/json')
->setHeader('X-Request-Id', uniqid())
->setBody(json_encode(['status' => 'success']))
->send();
The Request-Response Cycle in Magento
Browser Request
|
v
pub/index.php (Entry Point)
|
v
Magento\Framework\App\Bootstrap::create()
|
v
Magento\Framework\App\Http::run()
|
+---> Load Configuration
+---> Initialize Router
+---> Match Route (URL -> Controller)
+---> Dispatch to Controller
+---> Controller::execute()
+---> Create Result (Page/Redirect/Json)
+---> Render Response
|
v
HTTP Response sent to browser
Routing and URL Mapping
How URLs Map to Code
Web servers use routing to determine which PHP script handles a request.
Apache Routing (.htaccess)
# Magento's pub/.htaccess
RewriteEngine On
# Redirect all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
Nginx Routing
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
PHP Router Implementation
<?php
// Simple router - maps URLs to controller methods
class Router
{
private array $routes = [];
public function get(string $path, callable $handler): self
{
$this->routes['GET'][$path] = $handler;
return $this;
}
public function post(string $path, callable $handler): self
{
$this->routes['POST'][$path] = $handler;
return $this;
}
public function dispatch(string $method, string $uri): void
{
// Remove query string
$path = parse_url($uri, PHP_URL_PATH);
if (isset($this->routes[$method][$path])) {
$handler = $this->routes[$method][$path];
$handler();
} else {
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
}
}
}
// Define routes
$router = new Router();
$router->get('/api/products', function () {
echo json_encode(['products' => [
['id' => 1, 'name' => 'Widget']
]]);
});
$router->post('/api/products', function () {
$data = json_decode(file_get_contents('php://input'), true);
echo json_encode(['created' => true, 'id' => 456]);
});
// Handle the request
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
Magento Routing
Magento uses XML-based routing in each module:
<!-- app/code/Vendor/Module/etc/frontend/routes.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="vendor_module" frontName="my-route">
<module name="Vendor_Module" />
</route>
</router>
</config>
This maps URLs like /my-route/controller/action to:
- Module:
Vendor_Module - Controller:
Vendor\Module\Controller\<ControllerName> - Action:
execute()method
Middleware and Request Processing
What is Middleware?
Middleware sits between the request and the final handler. It can:
- Authenticate users
- Log requests
- Modify headers
- Rate limit requests
- Compress responses
Request -> [Middleware 1] -> [Middleware 2] -> [Handler] -> Response
^ |
|____________________|
(may modify)
PHP Middleware Implementation
<?php
// Middleware interface
class RequestMiddleware
{
private array $middlewares = [];
public function add(callable $middleware): self
{
$this->middlewares[] = $middleware;
return $this;
}
public function handle(callable $handler): callable
{
return function ($request) use ($handler) {
$response = $handler($request);
foreach (array_reverse($this->middlewares) as $middleware) {
$response = $middleware($request, $response);
}
return $response;
};
}
}
// Authentication middleware
function authMiddleware($request, $response) {
$token = $request->getHeader('Authorization');
if (!$token || !validateToken($token)) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
return $response;
}
// Logging middleware
function loggingMiddleware($request, $response) {
$log = sprintf(
"[%s] %s %s\n",
date('Y-m-d H:i:s'),
$request->getMethod(),
$request->getUri()
);
file_put_contents('/var/log/app.log', $log, FILE_APPEND);
return $response;
}
// Rate limiting middleware
function rateLimitMiddleware($request, $response) {
$ip = $_SERVER['REMOTE_ADDR'];
$key = "rate_limit:$ip";
$current = (int)(Redis::get($key) ?? 0);
if ($current > 100) { // 100 requests per hour
http_response_code(429);
echo json_encode(['error' => 'Too Many Requests']);
exit;
}
Redis::incr($key);
Redis::expire($key, 3600);
return $response;
}
// Stack middlewares
$middleware = new RequestMiddleware();
$middleware->add('loggingMiddleware')
->add('authMiddleware')
->add('rateLimitMiddleware');
$handler = $middleware->handle(function ($request) {
return ['status' => 200, 'body' => 'OK'];
});
Magento Middleware (Plugins)
Magento uses a similar concept through Plugins (Interceptors):
<?php
namespace Vendor\Module\Plugin;
class ProductPlugin
{
// Before plugin - runs before the original method
public function beforeGetName(
\Magento\Catalog\Model\Product $subject
): array {
// Modify arguments or add logic before method
return []; // Return modified arguments
}
// After plugin - runs after the original method
public function afterGetName(
\Magento\Catalog\Model\Product $subject,
$result // Original method's return value
): string {
// Modify the result
return strtoupper($result);
}
// Around plugin - wraps the original method
public function aroundGetPrice(
\Magento\Catalog\Model\Product $subject,
callable $proceed, // Calls the original method
$qty = null
): float {
$start = microtime(true);
$result = $proceed($qty); // Execute original method
$time = microtime(true) - $start;
error_log("getPrice took {$time}s");
return $result;
}
}
Key Takeaway
Middleware provides a clean way to add cross-cutting concerns (auth, logging, caching) without modifying the core application logic. Magento uses this pattern extensively through plugins and observers.
Quiz
1. What is the correct order of the request lifecycle?
2. In PHP, where can you read raw JSON request body data?
3. In Magento, what maps a URL like /my-route/controller/action to PHP code?
4. What does http_response_code() do in PHP?
Flashcards
Question
What are the main stages of a web request lifecycle?
Click to reveal answer
Answer
1. DNS Resolution, 2. TCP Connection, 3. TLS Handshake, 4. HTTP Request, 5. Web Server Processing, 6. PHP/Application Processing, 7. HTTP Response, 8. Browser Rendering
Question
How does Apache route requests to PHP using .htaccess?
Click to reveal answer
Answer
RewriteEngine checks if the file/directory exists. If not, all requests are routed to index.php using RewriteRule . index.php [L].
Question
What is middleware?
Click to reveal answer
Answer
Code that sits between the request and the final handler, processing requests before they reach the handler and responses after they are generated. Used for auth, logging, caching.
Question
How does Magento route URLs to controllers?
Click to reveal answer
Answer
Magento uses XML routes.xml files that map frontName prefixes to modules. URLs like /frontName/controller/action map to specific controller classes.
Question
What is the difference between $_GET and php://input?
Click to reveal answer
Answer
$_GET parses query string parameters (?key=value). php://input reads raw request body (for JSON, XML, or any payload).
Question
What does a Magento Plugin (Interceptor) do?
Click to reveal answer
Answer
Plugins intercept method calls on classes. Before plugins modify arguments, after plugins modify return values, around plugins wrap the entire method execution.
Question
What PHP function sets the HTTP status code?
Click to reveal answer
Answer
http_response_code(200) sets the status code. Alternatively, header('HTTP/1.1 200 OK') can be used.
Question
What is the role of index.php in Magento?
Click to reveal answer
Answer
index.php is the entry point that bootstraps the Magento application. It initializes the autoloader, creates the application instance, and runs the request through the framework.
Revision Notes
Key Takeaways
- 1. The request lifecycle: DNS -> TCP -> TLS -> HTTP Request -> Server Processing -> Response
- 2. Web servers use routing to map URLs to PHP scripts
- 3. Middleware provides a clean way to add cross-cutting concerns
- 4. Magento uses XML-based routing to map frontNames to controllers
- 5. php://input reads raw request body for JSON/XML payloads
- 6. Plugins in Magento act as middleware for method interception
Interview Tips
- • Be able to explain the complete request lifecycle step by step
- • Understand the difference between routing mechanisms (Apache, Nginx, PHP)
- • Explain what middleware is and give real examples (auth, logging, rate limiting)
- • Know how Magento routes URLs using XML configuration
- • Describe how Magento Plugins work (before, after, around)
Cheat Sheet
Request/Response Lifecycle Cheat Sheet
Complete Lifecycle:
DNS -> TCP -> TLS -> HTTP Request -> Web Server -> PHP -> Application -> Response
PHP Request Reading:
$_SERVER['REQUEST_METHOD'] // HTTP method
$_SERVER['REQUEST_URI'] // Request path
$_GET['key'] // Query parameter
$_POST['key'] // Form data
file_get_contents('php://input') // Raw body
getallheaders() // All headers
PHP Response Sending:
http_response_code(200) // Status code
header('Content-Type: ...') // Header
echo $body; // Body
Magento Routing:
routes.xml maps frontName -> Module -> Controller -> execute()
Middleware Pattern:
Request -> [Middleware] -> Handler -> [Middleware] -> Response