web php

Connection Handling

Connection handling is about what happens when the browser disconnects, the request takes too long, or PHP needs to finish work after sending a response.

Most PHP requests should be short: read input, do the work, send a response. Connection handling matters when requests are slow, users close tabs, exports take time, or code tries to run background work inside a web request.

Detecting a disconnected client

PHP provides connection_aborted() to check whether the client connection has been aborted. In normal CLI examples it usually returns 0 because there is no browser connection.

PHP example
<?php

declare(strict_types=1);

$aborted = connection_aborted() === 1;

echo $aborted ? 'Client disconnected' : 'Client still connected';

// Output in CLI:
// Client still connected

In real web requests, this is useful for loops that stream data or generate large responses. If the client has gone away, you may stop doing expensive work.

Continuing after disconnect

ignore_user_abort(true) tells PHP to continue running even if the client disconnects.

PHP example
<?php

declare(strict_types=1);

$previous = ignore_user_abort(true);

echo 'Previous setting: ' . ($previous ? 'continue' : 'stop') . PHP_EOL;

// Output is environment-dependent.

Use this carefully. Continuing after disconnect can be useful for finishing a payment update or audit write, but it can also waste server resources if applied to expensive work.

Sending the response before slow cleanup

When PHP runs under FastCGI, fastcgi_finish_request() can flush the response to the client while PHP continues running cleanup code.

PHP example
<?php

declare(strict_types=1);

echo 'Response sent to the user.' . PHP_EOL;

if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
}

echo 'Cleanup can continue after the response in FastCGI.' . PHP_EOL;

// Output in CLI:
// Response sent to the user.
// Cleanup can continue after the response in FastCGI.

This is not a replacement for a queue. The PHP worker is still busy until the cleanup finishes.

Prefer queues for long work

If work takes seconds, talks to slow third-party APIs, sends many emails, processes uploads, or generates large exports, a queue is usually better than doing it inside the web request.

The web request can create a job and return quickly. A worker process handles the slow work separately.

PHP example
<?php

declare(strict_types=1);

$job = [
    'type' => 'export_report',
    'user_id' => 42,
    'requested_at' => time(),
];

echo 'Queued job: ' . $job['type'] . ' for user ' . $job['user_id'] . PHP_EOL;

// Prints:
// Queued job: export_report for user 42

Timeouts

PHP, the web server, load balancer, and browser may all have different timeout limits. If any layer gives up first, users see failed requests even if PHP is still working.

For long operations, design the feature around background jobs and status pages instead of stretching timeouts higher and higher.

Idempotency matters

If a client disconnects and retries, the server might receive the same action twice. Payment, email, import, and order flows should be designed so retries do not create duplicate side effects.

PHP example
<?php

declare(strict_types=1);

function alreadyProcessed(string $requestId, array $processedIds): bool
{
    return in_array($requestId, $processedIds, true);
}

echo alreadyProcessed('req_123', ['req_123']) ? 'Skip duplicate' : 'Process request';

// Prints:
// Skip duplicate

What to check in a project

Check long-running controller actions. They may need a queue, export job, or progress page.

Check whether the code should stop when the client disconnects or finish a critical update.

Check retry behaviour. A disconnected client may send the request again.

Check timeout settings across PHP, the web server, and any proxy.

Check that fastcgi_finish_request() is used only for small post-response cleanup, not as a hidden job runner.

What you should be able to do

After this lesson, you should be able to explain client disconnects, use connection_aborted() and ignore_user_abort() appropriately, understand what fastcgi_finish_request() does, and recognise when a queue is the better design.

Practice

Task: Decide How To Handle Slow Work

Write a small PHP script that decides whether work should run inside the request, after the response, or in a queue.

Requirements

  • Use declare(strict_types=1);.
  • Create a function that accepts work type and expected duration in seconds.
  • Return request for quick work.
  • Return after_response for short cleanup.
  • Return queue for slow work.
  • Include examples for all three outcomes.
  • Print the decisions.

Check Your Work

Run the script and confirm long work is not assigned to the web request.

Show solution
PHP example
<?php

declare(strict_types=1);

function handlingStrategy(string $workType, int $expectedSeconds): string
{
    if ($expectedSeconds <= 1) {
        return 'request';
    }

    if ($expectedSeconds <= 3 && in_array($workType, ['audit_log', 'small_cleanup'], true)) {
        return 'after_response';
    }

    return 'queue';
}

$work = [
    ['render_page', 1],
    ['audit_log', 2],
    ['export_report', 20],
];

foreach ($work as [$type, $seconds]) {
    echo $type . ': ' . handlingStrategy($type, $seconds) . PHP_EOL;
}

// Prints:
// render_page: request
// audit_log: after_response
// export_report: queue

Why This Works

The quick page work stays in the request. Small cleanup can happen after the response if the environment supports it. The report export becomes a queue job because it would tie up the web request for too long.