php runtime and server environment

Memory And Execution Limits

PHP limits protect the server from scripts that use too much memory or run for too long. They are important safety rails, but raising them is not the first answer to every slow or memory-heavy task.

The professional approach is to measure, understand the workload, choose the right runtime, and only then adjust limits if the code genuinely needs more room.

Important Directives

Common directives:

memory_limit = 128M
max_execution_time = 30
max_input_time = 60

Meanings:

  • memory_limit caps memory used by a PHP script
  • max_execution_time caps how long a script can execute
  • max_input_time caps time spent parsing input data

CLI and web SAPIs may have different values. Always check the runtime that is failing.

Inspecting Limits

From PHP:

PHP example
<?php

declare(strict_types=1);

echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'memory_limit: ' . ini_get('memory_limit') . PHP_EOL;
echo 'max_execution_time: ' . ini_get('max_execution_time') . PHP_EOL;
echo 'memory usage: ' . memory_get_usage(true) . PHP_EOL;
echo 'memory peak: ' . memory_get_peak_usage(true) . PHP_EOL;

Use this in CLI or a temporary local web diagnostic to prove the active values.

Measuring Memory During Work

Measure before changing limits.

PHP example
<?php

declare(strict_types=1);

$rows = [];

for ($i = 1; $i <= 1000; $i++) {
    $rows[] = ['id' => $i, 'email' => 'user' . $i . '@example.com'];
}

echo 'Rows: ' . count($rows) . PHP_EOL;
echo 'Peak memory: ' . memory_get_peak_usage(true) . PHP_EOL;

// Prints:
// Rows: 1000
// Peak memory: 2097152

The exact memory value depends on the runtime. The useful habit is comparing memory before and after a change.

Avoid Loading Everything At Once

Memory problems often come from loading too much data into an array.

Instead of building one giant result, process data in chunks or streams.

PHP example
<?php

declare(strict_types=1);

function users(): Generator
{
    for ($i = 1; $i <= 3; $i++) {
        yield ['id' => $i, 'email' => 'user' . $i . '@example.com'];
    }
}

foreach (users() as $user) {
    echo $user['email'] . PHP_EOL;
}

// Prints:
// user1@example.com
// user2@example.com
// user3@example.com

Generators do not solve every problem, but they are useful when data can be processed one item at a time.

Execution Time

Web requests should usually be short. If a user action triggers a long import, report generation, or external API process, consider a queue worker or background job.

set_time_limit() can reset the execution timer in some SAPIs:

PHP example
<?php

declare(strict_types=1);

$changed = set_time_limit(30);

echo $changed ? 'time limit set' : 'time limit not changed';
echo PHP_EOL;

Do not use longer execution time to hide a bad request design. Long-running work usually belongs outside the browser request.

CLI And Workers

CLI scripts and workers often have different limits from web requests. Some CLI configurations use no execution time limit.

That does not mean memory no longer matters. Queue workers and daemons can leak memory across jobs because the process stays alive.

Worker practices:

  • free large variables after each job
  • avoid static request-specific state
  • restart workers after a number of jobs or memory threshold
  • monitor memory over time
  • keep each job's work bounded

Raising Limits Safely

Before raising limits, ask:

  1. Which SAPI is failing?
  2. What is the current limit?
  3. What workload triggers the failure?
  4. Is the code loading too much into memory?
  5. Should this be a background job?
  6. Will raising the limit affect server capacity?

For example, increasing memory_limit from 128M to 512M means each worker can consume much more memory. On a busy FPM pool, that affects how many requests the server can safely handle.

What You Should Be Able To Do

After this lesson, you should be able to inspect memory and execution limits, measure peak memory, recognise when code should stream or chunk data, understand why web requests should stay short, and decide when raising limits is appropriate.

For junior PHP work, this matters because "Allowed memory size exhausted" is common. The job is not just making the error disappear; it is understanding whether the code, runtime, or architecture needs to change.

Practice

Practice: Diagnose Memory And Time Limits

Create a small PHP diagnostic for memory and execution limits.

Task

Build a script that prints:

  • PHP_SAPI
  • memory_limit
  • max_execution_time
  • current memory usage
  • peak memory usage after building a small array

Then add a short checklist for what to investigate before raising memory_limit.

Use strict types. Keep example output inside the PHP code block as comments.

Show solution

This diagnostic prints the active limits and then does a small amount of work so peak memory changes.

PHP example
<?php

declare(strict_types=1);

echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'memory_limit: ' . ini_get('memory_limit') . PHP_EOL;
echo 'max_execution_time: ' . ini_get('max_execution_time') . PHP_EOL;
echo 'memory before: ' . memory_get_usage(true) . PHP_EOL;

$rows = [];

for ($i = 1; $i <= 1000; $i++) {
    $rows[] = ['id' => $i, 'email' => 'user' . $i . '@example.com'];
}

echo 'rows: ' . count($rows) . PHP_EOL;
echo 'memory after: ' . memory_get_usage(true) . PHP_EOL;
echo 'memory peak: ' . memory_get_peak_usage(true) . PHP_EOL;

// Prints:
// SAPI: cli
// memory_limit: 128M
// max_execution_time: 0
// memory before: 2097152
// rows: 1000
// memory after: 2097152
// memory peak: 2097152

Before raising memory_limit, check which SAPI is failing, how much data is loaded at once, whether chunking or streaming is possible, whether the work belongs in a queue, and what the higher limit means for total server capacity.