Beginner Runtime Path
PHP is not only a language. It is also a program installed on your machine, a set of configuration files, optional extensions, and several ways to run code.
Before you can debug real PHP projects, you need to know which PHP binary is running, which configuration it loaded, which extensions are enabled, and whether the code is running from the command line or through a web server.
The PHP Binary And PATH
When you type php, your shell looks through the directories listed in PATH and runs the first matching executable.
which php
php -v
Typical output might look like this:
/usr/bin/php
PHP 8.5.6 (cli)
The exact path and version depend on your machine. The important habit is checking them instead of assuming the project is using the PHP version you expected.
On Windows, the equivalent command is:
where php
php -v
If php -v fails, PHP is either not installed or not available on your PATH.
CLI PHP
CLI means command-line interface. It is the PHP runtime used when you run scripts, Composer commands, test suites, static analysis, queue workers, and framework console commands.
php script.php
php -r "echo PHP_VERSION . PHP_EOL;"
php -l public/index.php
php -l checks a file for syntax errors without running the application. It is a useful quick check when you are editing PHP files.
Here is a tiny CLI script:
<?php
declare(strict_types=1);
echo 'Running on PHP ' . PHP_VERSION . PHP_EOL;
// Prints:
// Running on PHP 8.5.6
Your exact version number may be different.
Local Web Server
Running PHP from a browser is different from running it from the CLI. A web request has HTTP headers, a URL, a document root, query strings, cookies, and server variables.
For local learning, PHP has a built-in development server:
php -S 127.0.0.1:8000 -t public
That command says:
- listen on
127.0.0.1:8000 - serve files from the
publicdirectory - route PHP requests through the built-in server
A minimal public/index.php can show the difference between CLI and web execution:
<?php
declare(strict_types=1);
echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'Method: ' . ($_SERVER['REQUEST_METHOD'] ?? 'not a web request') . PHP_EOL;
From the browser, PHP_SAPI will normally say cli-server when using the built-in server. From php public/index.php, it will say cli.
The built-in server is for development only. Production normally uses PHP-FPM behind Nginx, Apache with PHP-FPM, Apache's PHP module, or a specialist runtime.
Installation Is Only The First Step
Installing PHP gives you a runtime, but a project may still require:
- a specific PHP version
- Composer
- extensions such as
pdo_mysql,mbstring,intl,curl,zip, orgd - a suitable
php.ini - environment variables
- a local database, cache, mail tool, or queue worker
The first professional question is not "is PHP installed?" It is "is this the PHP runtime this project expects?"
Useful checks:
php -v
php -m
php --ini
composer check-platform-reqs
php -m lists loaded extensions. php --ini shows which configuration files PHP loaded. composer check-platform-reqs checks whether your installed PHP version and extensions match the project's Composer requirements.
php.ini
php.ini is the main PHP configuration file. It controls settings such as memory limits, upload limits, error reporting, timezones, and extension loading.
php --ini
php -i | grep "Loaded Configuration File"
On Windows PowerShell:
php --ini
php -i | Select-String "Loaded Configuration File"
Different SAPIs can load different configuration. CLI PHP and PHP-FPM may not use the same php.ini, so a setting that works in a terminal may not be active for a web request.
You can inspect important settings from PHP:
<?php
declare(strict_types=1);
echo 'memory_limit=' . ini_get('memory_limit') . PHP_EOL;
echo 'upload_max_filesize=' . ini_get('upload_max_filesize') . PHP_EOL;
echo 'date.timezone=' . ini_get('date.timezone') . PHP_EOL;
When a project behaves differently between the browser and terminal, compare the loaded configuration for both runtimes.
Extensions
Extensions add features to PHP. Some are built in, some are enabled by packages, and some are optional.
Common examples:
pdo_mysqlfor MySQL database accessmbstringfor multibyte string handlingintlfor locale-aware formattingcurlfor HTTP requestszipfor archive handlinggdorimagickfor image work
Check loaded extensions with:
php -m
php -m | grep mbstring
From PHP code:
<?php
declare(strict_types=1);
echo extension_loaded('mbstring') ? 'mbstring loaded' : 'mbstring missing';
echo PHP_EOL;
Missing extensions are a common cause of "works on my machine" bugs. Composer can declare required extensions, but the machine still needs to have them installed and enabled.
Environment Variables
Environment variables are values provided outside the code. Projects use them for configuration that changes between local, staging, and production environments.
Examples include database hosts, API keys, feature flags, and app modes.
APP_ENV=local php -r "echo getenv('APP_ENV') . PHP_EOL;"
Inside PHP:
<?php
declare(strict_types=1);
$environment = getenv('APP_ENV') ?: 'production';
echo 'Environment: ' . $environment . PHP_EOL;
In real projects, do not hard-code secrets in PHP files. Load them through environment configuration, secret managers, or the framework's configuration system.
Treat Runtime Identity As A Set Of Facts
"It runs PHP 8.5" is not a complete runtime description. Two processes with the same version can behave differently because they use different executables, SAPIs, configuration files, extensions, working directories, environment variables, users, or operating-system libraries.
A useful runtime fingerprint records:
binary path
PHP version and SAPI
loaded php.ini and scanned configuration directory
required extensions and their versions
current working directory
relevant environment-variable presence
process user or container identity
Collect only the facts needed for the problem. Do not publish an unrestricted diagnostic page containing every environment variable, request header, path, and configuration value. Runtime inspection is evidence, but excessive inspection output can expose secrets and internal infrastructure.
The built-in phpinfo() function produces a large diagnostic report. It can help in a controlled local environment, but a publicly accessible phpinfo() page can disclose versions, filesystem paths, server variables, headers, and configuration. Prefer focused checks and remove temporary diagnostic endpoints immediately after use.
Understand Configuration Sources And Precedence
php --ini shows the primary CLI configuration file and any directory scanned for additional .ini files. Extension packages often add separate files there. A setting can therefore come from the main php.ini, a scanned file, a command-line override, a server pool, or application code.
Inspect a resolved CLI value with:
php -r 'echo ini_get("memory_limit"), PHP_EOL;'
Temporarily override an INI setting for one CLI process with -d:
php -d memory_limit=256M -r 'echo ini_get("memory_limit"), PHP_EOL;'
This does not edit php.ini; it applies only to that invocation. Temporary overrides are useful for diagnosis, but project requirements should be recorded in reproducible environment configuration rather than hidden in one developer's shell history.
Some directives can be changed at runtime with ini_set(), while others are restricted to system or server configuration. Check the PHP manual for a directive's changeability before assuming application code can alter it.
When CLI and web values differ, compare them in their own execution contexts. Running php --ini does not reveal which configuration a separate PHP-FPM service loaded. Use service configuration, a narrowly scoped local endpoint, or framework diagnostics for the web process.
Choose A Safe Document Root
The built-in server's -t option defines the document root. For modern applications, that is commonly public/, not the repository root:
project/
config/
src/
vendor/
public/
index.php
Starting a server with the repository root can make files available that were never intended to be requested directly. A public document root reduces accidental exposure of configuration, source, dependency metadata, and local artifacts.
The built-in server serves one local development process. Stop it with Ctrl+C in the terminal where it is running. Do not expose it to untrusted networks, use it as a production server, or assume it reproduces Nginx, Apache, Caddy, or PHP-FPM behaviour.
If a framework relies on a front-controller routing script, follow that framework's documented local command. The simple -t public command does not automatically route every unknown URL through index.php.
Check Extensions Precisely
An extension name in php -m proves it is loaded for that process. It does not prove that every function, driver, or external library needed by the application is available.
For example, PDO is the database abstraction extension, while concrete drivers appear separately. Inspect them with:
php -r 'print_r(PDO::getAvailableDrivers());'
An application requiring MySQL through PDO needs the appropriate PDO driver, not only the base PDO extension. Image, internationalisation, database, and compression extensions can also depend on system libraries whose versions affect available behaviour.
Composer requirements such as ext-mbstring express a dependency, but Composer does not install the extension. The operating system, container image, or managed host must provide it.
Understand Environment Inheritance
Environment variables belong to a process environment. A variable set for one shell command is available to that process and its children:
APP_ENV=local php runtime-report.php
It does not permanently configure every future terminal, web server, queue worker, or service. On Windows PowerShell, setting an environment variable for the current session uses different syntax:
$env:APP_ENV = 'local'
php runtime-report.php
A PHP-FPM service started elsewhere will not automatically inherit a value set later in your interactive shell. Containers and deployment platforms also define environment values through their own configuration.
getenv() returns false when a variable is absent. Distinguish absence from an empty string when that difference matters. Never print secret values merely to prove they exist; report presence or a safely redacted form.
Diagnose Before Installing More Software
Match the symptom to the smallest relevant check:
| Symptom | First evidence |
|---|---|
php command unavailable |
Locate installation and inspect PATH |
| Wrong language syntax rejected | Check binary path and php -v |
| Function or class missing | Check extension or dependency providing it |
| CLI works but browser fails | Compare SAPI, web logs, configuration, and environment |
| Composer reports a platform mismatch | Run composer check-platform-reqs and inspect requirements |
| Local request reaches the wrong file | Confirm URL, document root, and entry point |
| Setting appears unchanged | Inspect resolved value in the affected SAPI |
Do not install random extensions until the error or project requirements identify one. Do not copy a different php.ini over the active file without recording the original and understanding the changed directives. A runtime problem is solved when the required environment is reproducible, not merely when one command happens to pass once.
A Beginner Runtime Checklist
When a PHP project does not run, check the runtime before changing application code:
- Which
phpbinary is running? - Which PHP version is it?
- Is this CLI, built-in server, PHP-FPM, or another SAPI?
- Which
php.inifiles are loaded? - Are the required extensions loaded?
- Are required environment variables available?
- Does Composer agree that the platform requirements are satisfied?
This checklist prevents a lot of wasted debugging. Many beginner PHP problems are runtime mismatches, not application bugs.
What You Should Be Able To Do
After this lesson, you should be able to find your PHP binary, check the PHP version, run a CLI script, start the built-in local server, locate loaded php.ini files, list extensions, and read an environment variable.
For junior PHP work, this matters because setup and debugging are part of the job. A developer who can prove what runtime is actually executing the code is much more useful than one who guesses.
Practice
Practice: Build A Safe Runtime Report
Create runtime-report.php to prove which PHP process is executing the file without exposing secret values.
Requirements
Print:
PHP_BINARY,PHP_VERSION,PHP_VERSION_ID, andPHP_SAPI;- the loaded
php.inipath ornone; - the directory scanned for additional INI files;
- the resolved
memory_limit; - whether
mbstringandpdoare loaded; - the available PDO drivers;
- the current working directory;
- whether
APP_ENVis absent, empty, or non-empty, without printing its value.
Run the report:
- from CLI with no
APP_ENV; - from CLI with
APP_ENV=localusing syntax appropriate for your shell; - through the built-in server from a
public/document root, if available.
Compare the SAPI, configuration, and environment results. Remove the web-accessible report after the exercise and explain why leaving it deployed would be unsafe.
Show solution
<?php
declare(strict_types=1);
$loadedIni = php_ini_loaded_file();
$scannedIniDirectory = get_cfg_var('cfg_file_scan_dir');
$appEnvironment = getenv('APP_ENV');
$workingDirectory = getcwd();
$appEnvironmentState = match (true) {
$appEnvironment === false => 'absent',
$appEnvironment === '' => 'empty',
default => 'present and non-empty',
};
echo 'Binary: ', PHP_BINARY, PHP_EOL;
echo 'Version: ', PHP_VERSION, PHP_EOL;
echo 'Version ID: ', PHP_VERSION_ID, PHP_EOL;
echo 'SAPI: ', PHP_SAPI, PHP_EOL;
echo 'Loaded php.ini: ', $loadedIni === false ? 'none' : $loadedIni, PHP_EOL;
echo 'INI scan directory: ', $scannedIniDirectory ?: 'none', PHP_EOL;
echo 'memory_limit: ', ini_get('memory_limit'), PHP_EOL;
echo 'mbstring: ', extension_loaded('mbstring') ? 'loaded' : 'missing', PHP_EOL;
echo 'pdo: ', extension_loaded('pdo') ? 'loaded' : 'missing', PHP_EOL;
echo 'PDO drivers: ', implode(', ', PDO::getAvailableDrivers()) ?: 'none', PHP_EOL;
echo 'Working directory: ', $workingDirectory === false ? 'unavailable' : $workingDirectory, PHP_EOL;
echo 'APP_ENV: ', $appEnvironmentState, PHP_EOL;
Run from a POSIX shell:
php runtime-report.php
APP_ENV=local php runtime-report.php
In PowerShell:
php runtime-report.php
$env:APP_ENV = 'local'
php runtime-report.php
Remove-Item Env:APP_ENV
For a local web comparison, place a temporary copy under public/ and start:
php -S 127.0.0.1:8000 -t public
The CLI SAPI should be cli; the built-in server normally reports cli-server. Configuration paths and environment state may differ because they belong to separate process contexts.
Delete the web-accessible report after comparing results. Even without printing APP_ENV, it reveals runtime versions, filesystem paths, loaded capabilities, and configuration details that can help an attacker map the server. A focused report is appropriate for controlled diagnosis, not as a permanent public endpoint.