Skip to content
beginner Phase 1 · Web Foundations

Request/Response Lifecycle in Web Applications

Trace the complete lifecycle of a web request from browser to server and back, with PHP code examples for handling requests and sending responses.

45m
0 problems
Topic Progress 0%

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?

Question 1 options

2. In PHP, where can you read raw JSON request body data?

Question 2 options

3. In Magento, what maps a URL like /my-route/controller/action to PHP code?

Question 3 options

4. What does http_response_code() do in PHP?

Question 4 options

Flashcards

Question

What are the main stages of a web request lifecycle?

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?

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?

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?

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?

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?

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?

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?

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