Start Here

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 example
<?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 public directory
  • route PHP requests through the built-in server

A minimal public/index.php can show the difference between CLI and web execution:

PHP example
<?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, or gd
  • 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 example
<?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_mysql for MySQL database access
  • mbstring for multibyte string handling
  • intl for locale-aware formatting
  • curl for HTTP requests
  • zip for archive handling
  • gd or imagick for image work

Check loaded extensions with:

php -m
php -m | grep mbstring

From PHP code:

PHP example
<?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 example
<?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:

  1. Which php binary is running?
  2. Which PHP version is it?
  3. Is this CLI, built-in server, PHP-FPM, or another SAPI?
  4. Which php.ini files are loaded?
  5. Are the required extensions loaded?
  6. Are required environment variables available?
  7. 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, and PHP_SAPI;
  • the loaded php.ini path or none;
  • the directory scanned for additional INI files;
  • the resolved memory_limit;
  • whether mbstring and pdo are loaded;
  • the available PDO drivers;
  • the current working directory;
  • whether APP_ENV is absent, empty, or non-empty, without printing its value.

Run the report:

  1. from CLI with no APP_ENV;
  2. from CLI with APP_ENV=local using syntax appropriate for your shell;
  3. 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 example
<?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.