php runtime and server environment

Configuration Modes

This matters because not every php.ini setting can be changed with ini_set(). If you try to change a system-level directive from application code, it may silently fail or return false.

The Main Modes

You will see these mode names in PHP documentation:

INI_USER
INI_PERDIR
INI_SYSTEM
INI_ALL

Plain-English meaning:

  • INI_USER: can be changed in user scripts with ini_set()
  • INI_PERDIR: can be changed in per-directory or server configuration
  • INI_SYSTEM: can be changed only in system-level configuration
  • INI_ALL: can be changed anywhere supported

The mode belongs to the directive, not to your preference.

Checking A Runtime Change

ini_set() returns the old value on success or false on failure.

PHP example
<?php

declare(strict_types=1);

$oldValue = ini_set('display_errors', '0');

if ($oldValue === false) {
    echo 'display_errors could not be changed' . PHP_EOL;
} else {
    echo 'display_errors changed from ' . $oldValue . ' to ' . ini_get('display_errors') . PHP_EOL;
}

This is a safe pattern when you need to know whether the change actually worked.

Runtime Settings

Some settings are commonly changed at runtime for a specific script or request.

PHP example
<?php

declare(strict_types=1);

ini_set('memory_limit', '256M');
ini_set('display_errors', '0');

echo 'memory_limit=' . ini_get('memory_limit') . PHP_EOL;
echo 'display_errors=' . ini_get('display_errors') . PHP_EOL;

Use runtime changes sparingly. If a setting describes the environment, it usually belongs in php.ini, FPM pool config, container config, or deployment configuration.

System-Level Settings

Some settings must be changed before PHP starts or at the server configuration layer.

Examples include many extension-loading settings and low-level runtime settings.

extension=mbstring
zend_extension=opcache
opcache.enable=1

If a setting controls how PHP starts, loads extensions, or configures an engine-level feature, assume it probably does not belong in ini_set().

Per-Directory Configuration

Depending on the SAPI and server, PHP may support per-directory configuration.

Common places:

  • .user.ini for CGI/FastCGI-style setups where enabled
  • .htaccess with Apache module setups
  • web server configuration
  • PHP-FPM pool configuration

Example .user.ini:

upload_max_filesize = 20M
post_max_size = 24M

Do not assume .user.ini is active everywhere. It depends on the SAPI and configuration.

PHP-FPM Pool Configuration

PHP-FPM can set PHP values at the pool level.

Example pool-style settings:

php_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 20M

php_admin_value is stronger than php_value; application code cannot override admin values with ini_set().

This is useful for production control, but it can surprise developers who try to change a value from code and see no effect.

Inspecting Changeability

ini_get_all() can show access metadata for many directives.

PHP example
<?php

declare(strict_types=1);

$settings = ini_get_all(null, false);

echo 'memory_limit=' . ($settings['memory_limit'] ?? 'unknown') . PHP_EOL;
echo 'display_errors=' . ($settings['display_errors'] ?? 'unknown') . PHP_EOL;

For exact changeability rules, use the PHP manual for the directive. In real debugging, combine documentation with a direct check: attempt the change, inspect the return value, and verify the resulting setting from the same SAPI.

Common Mistakes

Common configuration mode mistakes:

  • assuming ini_set() can change every directive
  • editing CLI php.ini while debugging PHP-FPM
  • changing .user.ini and expecting CLI scripts to see it
  • forgetting to reload PHP-FPM after config changes
  • setting production rules in application code instead of deployment config
  • assuming Apache .htaccess rules apply to Nginx or Caddy

What You Should Be Able To Do

After this lesson, you should be able to explain what configuration modes are, recognise when ini_set() is appropriate, know where per-directory and system settings live, and verify whether a setting changed in the runtime that actually matters.

For junior PHP work, this matters because configuration bugs often come from changing a setting in the wrong place. The fix starts by knowing where that directive is allowed to change.

Practice

Practice: Test Whether A Directive Can Change

Create a small PHP diagnostic that attempts to change a runtime setting and reports whether it worked.

Task

Build a script that:

  • prints the current SAPI
  • prints the current memory_limit
  • calls ini_set('memory_limit', '256M')
  • checks whether ini_set() returned false
  • prints the final memory_limit

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

Afterward, add a short note explaining why this test should be run in the same SAPI you are debugging.

Show solution

This diagnostic checks the return value from ini_set() instead of assuming the setting changed.

PHP example
<?php

declare(strict_types=1);

if (PHP_SAPI !== 'cli') {
    header('Content-Type: text/plain');
}

echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'Before: ' . ini_get('memory_limit') . PHP_EOL;

$oldValue = ini_set('memory_limit', '256M');

if ($oldValue === false) {
    echo 'Change result: failed' . PHP_EOL;
} else {
    echo 'Change result: changed from ' . $oldValue . PHP_EOL;
}

echo 'After: ' . ini_get('memory_limit') . PHP_EOL;

// Prints:
// SAPI: cli
// Before: 128M
// Change result: changed from 128M
// After: 256M

Run this in the same SAPI you are debugging because CLI, FPM, Apache module, and the built-in server can have different configuration sources and different override rules.