skip to content

Engine & Runtime Setup

Every PHP request compiles and runs the script afresh under a server API, shaped by php.ini and the loaded extensions. Interviewers probe it to see if you know where your code actually runs.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

20

In PHP, how do you find which php.ini file is actually loaded, and why might editing php.ini seem to change nothing?

level: juniorimportance: must knowfreq 58%

answer

  1. php --ini for the CLI
  2. php_ini_loaded_file() from a web page
  3. CLI and FPM may load different files
  4. read once at startup, so restart
  5. (none) means built-in defaults

basics

~20 s

Run php --ini for the CLI, or call php_ini_loaded_file() or phpinfo() from a web page, since each SAPI may load a different file. php.ini is read at startup, so PHP-FPM or Apache must be restarted after an edit.

solid answer

~40 s

`php --ini` prints the CLI's configuration: the directory searched, the **Loaded Configuration File** (or `(none)`), the scan directory and the additional `.ini` files parsed. But the CLI and the web SAPI are separate processes and often load **different** files, so to see what the web app uses, call `php_ini_loaded_file()` and `php_ini_scanned_files()` (both `string|false`) or `phpinfo()` from a page served by it. Edits appear to do nothing for three usual reasons: you edited the file another SAPI loads; PHP-FPM or Apache's module read php.ini **once at startup** and must be reloaded; or a later file, such as one in the scan directory or a per-directory override, sets the same directive again. If no file loads at all, PHP runs on built-in defaults, which are not the production-safe values shipped in `php.ini-production`.

code

bash · 5 lines
bash
php --ini
# Configuration File (php.ini) Path: "/usr/local/etc/php"
# Loaded Configuration File:         "/usr/local/etc/php/php.ini"
# Scan for additional .ini files in: "/usr/local/etc/php/conf.d"
# Additional .ini files parsed:      /usr/local/etc/php/conf.d/20-intl.ini

go deeper

for a junior

Know php --ini and phpinfo(), and that PHP-FPM or Apache must be restarted to pick up php.ini changes.

for a middle

Explain that each SAPI may load its own file, how scan directories and per-directory overrides layer on top, and how master and local values differ.

for a senior

Make the loaded configuration part of deployment review: confirm the production template is loaded by the web SAPI and that nothing later overrides it.

for a principal

Standardise configuration across a fleet so every SAPI's effective settings are known, versioned and reviewable rather than drifting per server.

## Where configuration comes from PHP reads its main configuration file, **php.ini**, when the process starts. The manual lists the search order: a SAPI-specific location (for example the `-c` option of the CLI), the `PHPRC` environment variable, the current directory for non-CLI SAPIs, then the compile-time default path. If a file named `php-SAPI.ini` exists, such as `php-cli.ini`, it is used **instead of** `php.ini` for that SAPI. After the main file, PHP parses every `.ini` file in the **scan directory**. The result: one server can have several SAPIs, each with its own loaded files. ## Finding the loaded files | Tool | Shows | Which SAPI | |---|---|---| | `php --ini` | search path, loaded file, scan dir, extra files | the CLI only | | `php_ini_loaded_file()` | path of the main file, or `false` | whichever SAPI runs the call | | `php_ini_scanned_files()` | comma-separated extra files, or `false` | whichever SAPI runs the call | | `phpinfo()` | everything, including each directive's local and master value | whichever SAPI runs the call | | `ini_get('memory_limit')` | the current value of one directive, as a string | whichever SAPI runs the call | The key habit: **ask the SAPI you care about**. `php --ini` on a web server tells you about cron jobs and Composer, not about the pages PHP-FPM serves. ## Why an edit seems to do nothing 1. **Wrong file.** The web SAPI loads a different php.ini from the one you edited. Check `php_ini_loaded_file()` from a web page. 2. **No restart.** The manual notes that server-module SAPIs read php.ini only once when the server starts; CGI and CLI read it on every invocation. PHP-FPM also reads it at startup, so it needs a reload. 3. **Overridden later.** A file in the scan directory, a `.user.ini` or `.htaccess` in the app's directory, or an `ini_set()` call in code sets the same directive again. `phpinfo()` shows both the **master value** (from the configuration files) and the **local value** (after per-directory and runtime changes). 4. **Typo or wrong section.** An unknown directive name is silently ignored in php.ini, so a misspelling looks exactly like no change. ## No php.ini at all When `php --ini` reports `Loaded Configuration File: (none)`, PHP runs on its **built-in defaults**. Those are not tuned for production. The header of `php.ini-production` lists where the shipped files differ from the defaults, for example: | Directive | Built-in default | php.ini-development | php.ini-production | |---|---|---|---| | `display_errors` | On | On | Off | | `log_errors` | Off | On | On | | `zend.exception_ignore_args` | Off | Off | On | | `zend.assertions` | 1 | 1 | -1 | | `output_buffering` | Off | 4096 | 4096 | ## A step-by-step check When a setting seems wrong on a live site, work through it in order: 1. From a page served by the site, print `PHP_SAPI`, `php_ini_loaded_file()` and `php_ini_scanned_files()`. 2. Open those exact files and find the directive; remember a later scan file wins over php.ini. 3. Compare `ini_get()` with the master value in `phpinfo()`: if they differ, a per-directory file or `ini_set()` changed it. 4. After fixing the file, reload PHP-FPM or the web server so the processes read it again. 5. Re-run step 1 to confirm, and remove the diagnostic page. ## Production versus development files PHP ships two templates, **`php.ini-development`** and **`php.ini-production`**. Neither is loaded automatically; an installer or you copy one to the loaded location. - The **development** file favours visibility: errors on screen, assertions active. - The **production** file favours safety and speed: errors logged rather than shown, exception arguments left out of stack traces, assertion code not even compiled. A common incident is a server that has **no** php.ini, or a copy of the development file, in production, which shows errors and file paths to visitors. Checking the loaded file is part of any deployment review. The production file is a starting point, not a finished configuration: limits such as `memory_limit` or upload sizes still need values that fit the application, set in php.ini or a scan-directory file that is kept under version control.

  • Why does php --ini on the server disagree with phpinfo() in the browser?
    php --ini describes the CLI SAPI, while phpinfo() in the browser describes the web SAPI, usually PHP-FPM or Apache's module. They start separately and can load different php.ini files and scan directories, so each must be asked about itself.
  • What happens if no php.ini is found at all?
    PHP starts with its built-in defaults, and php --ini reports the loaded file as (none). Those defaults are not production-safe: display_errors is on and log_errors is off, among others, so a production server should always load a reviewed file.
  • Do you need to restart PHP-FPM after editing php.ini?
    Yes. FPM reads php.ini when its processes start, so edits take effect after a reload or restart. The CLI and CGI read it on every invocation, which is why a quick test from the shell can show the new value while the site does not.

Like a thermostat with a schedule stored in two places: changing the copy in the drawer does nothing, and even the right copy only takes effect after the unit reboots and reads it again.

saying these in an interview costs you the question

  • php --ini shows the configuration the web server uses.
  • Editing php.ini takes effect on the next request under PHP-FPM.
  • Every PHP process on a server loads the same php.ini.
  • Without a php.ini, PHP uses the production-safe values by default.
  • A misspelled directive in php.ini triggers a startup error.
open as a page

In PHP, why does a page-view counter kept in a static variable reset on every request to a website?

level: juniorimportance: must knowfreq 72%

basics

~20 s

PHP runs every web request share-nothing: variables, statics, globals and objects are created for that request and freed when it ends. A static counter starts from its initial value on each page view, so real counts need an external store.

open as a page

On a PHP photo-contest site on a shared host, why does ini_set('upload_max_filesize', '20M') fail to raise the upload limit?

level: middleimportance: must knowfreq 52%

basics

~20 s

upload_max_filesize is changeable only at INI_PERDIR or INI_SYSTEM level, so ini_set() from a script refuses it and returns false. The upload is also parsed before the script runs. Set it in php.ini, .user.ini or .htaccess instead.

open as a page

Which headline language features did each PHP release from 8.0 to 8.5 introduce?

level: middleimportance: must knowfreq 62%

basics

~20 s

8.0: named arguments, attributes, union types, match, nullsafe, promotion, JIT. 8.1: enums, readonly, fibers, first-class callables. 8.2: readonly classes, DNF types. 8.3: typed class constants, #[\Override]. 8.4: property hooks, asymmetric visibility. 8.5: pipe operator, clone-with.

open as a page

When upgrading a ten-year-old PHP 7.4 invoicing application to PHP 8.5, which changes are most likely to break it, and how do you sequence the work?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Most breakage comes from 8.0: 0 == "" is false, many warnings became TypeError, count(null) throws, PDO throws by default. Then come later deprecations. Add tests, fix on 7.4, run 8.x in CI, walk the migration guides, then switch.

open as a page

How do you run a local site with PHP's built-in php -S server and a router script, and why is it unfit for production?

level: juniorimportance: should knowfreq 45%

basics

~20 s

php -S localhost:8000 -t public router.php starts PHP's development server; the router runs on every request and returning false hands the request back to default file handling. It runs one single-threaded process and is not built for public traffic.

open as a page

How does the PHP release cycle work: how often does a new minor version ship, and what support does each branch get?

level: juniorimportance: should knowfreq 34%

basics

~20 s

PHP ships one new major or minor version a year, in late November, after a 20-week alpha, beta and RC cycle. Each branch then gets patch releases, first bug fixes, later security fixes only, until end of life.

open as a page

In PHP, how are extra .ini files in a conf.d scan directory loaded, and when do you use zend_extension= instead of extension=?

level: middleimportance: should knowfreq 32%

basics

~20 s

After php.ini, PHP parses every .ini file in the scan directory in alphabetical order; PHP_INI_SCAN_DIR overrides that directory. extension= loads a regular extension, while zend_extension= loads one that hooks the Zend engine itself, such as Xdebug.

open as a page

In PHP, how do .user.ini files work, which SAPIs read them, and why can a change take minutes to appear?

level: middleimportance: should knowfreq 34%

basics

~20 s

.user.ini files are per-directory ini files read only by the CGI and FastCGI SAPIs, PHP-FPM included. PHP scans from the script's directory up to the document root and caches them for user_ini.cache_ttl, 300 seconds by default.

open as a page

In PHP, what steps turn a .php source file into running code, and which of them repeat on every request?

level: middleimportance: should knowfreq 48%

basics

~20 s

PHP tokenizes the source, parses the tokens into an abstract syntax tree, compiles the tree into opcodes and runs them on the Zend virtual machine. Without an opcode cache, every request repeats all four steps for every file it loads.

open as a page

In PHP, when do callbacks registered with register_shutdown_function() run, and what are they typically used for?

level: middleimportance: should knowfreq 40%

basics

~20 s

register_shutdown_function() queues a callback that runs when the request ends: after the script finishes, calls exit, or dies on an uncaught exception or fatal error. Callbacks run in registration order; common uses are logging fatal errors and final cleanup.

open as a page

Why does a PHP cron script run through the CLI never hit max_execution_time, while the same code times out on the web?

level: middleimportance: should knowfreq 48%

basics

~20 s

The CLI SAPI hardcodes max_execution_time=0, meaning no limit, and php.ini cannot change that; web SAPIs use the configured value, 30 seconds by default. The same script is unlimited from cron and cut off on the web.

open as a page

In PHP, how does a script tell whether the CLI or a web server started it, and which names can it get?

level: middleimportance: should knowfreq 45%

basics

~10 s

php_sapi_name() and the PHP_SAPI constant name the server API running the script: cli from a terminal or cron, cli-server under php -S, fpm-fcgi under PHP-FPM, apache2handler under Apache's module, cgi-fcgi under php-cgi.

open as a page

In PHP, what does an E_DEPRECATED notice tell you, and how should a team act on deprecations before an upgrade?

level: middleimportance: should knowfreq 42%

basics

~10 s

E_DEPRECATED means the code still works but uses something PHP plans to remove or change, usually at the next major version. Surface the notices in development and CI, fix them first, then upgrade.

open as a page

In PHP, how do you check which version is running, and when should you prefer PHP_VERSION_ID over version_compare()?

level: middleimportance: should knowfreq 36%

basics

~10 s

PHP_VERSION is the version string, such as "8.5.11"; PHP_VERSION_ID is an integer, 80511, that compares with plain < and >=. version_compare() compares arbitrary version strings, including RC and dev suffixes.

open as a page

How do PHP's disable_functions and open_basedir directives harden a server, and why is neither a real security boundary on its own?

level: seniorimportance: should knowfreq 30%

basics

~20 s

disable_functions removes named internal functions and open_basedir limits which paths PHP's file functions may open. Both are useful defence in depth, but the manual says disable_functions can be circumvented, and open_basedir does not bind child processes.

open as a page

In the PHP engine, what do the MINIT, RINIT, RSHUTDOWN and MSHUTDOWN phases do, and what survives from one request to the next?

level: seniorimportance: should knowfreq 30%

basics

~20 s

MINIT and MSHUTDOWN run once per process, when extensions and php.ini are loaded and unloaded; RINIT and RSHUTDOWN run around every request. Only engine-level state from module startup survives between requests; all script state and request memory is torn down.

open as a page

On Linux, why can a PHP web request stuck waiting on a slow external API run far past max_execution_time = 30, and what does set_time_limit() actually reset?

level: seniorimportance: should knowfreq 33%

basics

~20 s

On standard non-thread-safe Linux builds, max_execution_time counts CPU time the process uses, so waiting on I/O such as an API call or query barely counts. set_time_limit() sets a new limit and restarts the counter from zero.

open as a page

In PHP, what happens to a running web request when the client disconnects, and how do ignore_user_abort() and connection_aborted() change that?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

By default PHP aborts a request once it notices the client has gone, which happens when it next tries to send output; shutdown callbacks still run. ignore_user_abort(true) lets the script finish, and connection_aborted() tells code whether the client left.

open as a page

How would you plan PHP version upgrades across a fleet of dozens of PHP applications that currently run different versions?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Inventory every app's PHP version and dependencies, rank by support dates and risk, and make upgrading a yearly routine: next version in CI, deprecations fixed continuously, shared libraries first, and a standard base image per version.

open as a page