A modern PHP toolchain with Composer
Install a current PHP, serve it locally, and let Composer handle dependencies, PSR-4 autoloading and the tooling around the language.
PHP 8.4 or 8.5, served locally
php -v # aim for 8.4 or 8.5
php -m # extensions actually loaded
php -i | grep "Loaded Configuration" # which php.ini is in use
php -S localhost:8000 -t public # built-in server, docroot public/- Install through a package manager or a version manager so one machine can host projects on different PHP versions.
- Pin the version in
composer.jsonwith"php": "^8.4"; CI then refuses to run on anything older. - Enable
mbstring,curland one PDO driver (pdo_sqliteis enough to practise). Missing extensions fail in ways that do not obviously point at the extension. - The built-in server is single-threaded and rewrites nothing beyond PHP files. Treat it as a development convenience only.
Composer and PSR-4 autoloading
composer init --name=acme/blog --require=php:^8.4
composer require monolog/monolog:^3 # production dependency
composer require --dev phpunit/phpunit:^11 # development only
composer install # installs exactly what composer.lock pins
composer update monolog/monolog # deliberately move one package forward<?php
// public/index.php - the single entry point the web server hands requests to
require __DIR__ . '/../vendor/autoload.php';
use Acme\Blog\Post; // resolves to src/Post.php through the psr-4 rule
$post = new Post();
echo $post->title();| Key in composer.json | What it does |
|---|---|
require | Runtime dependencies; installed in production |
require-dev | Tests and analysis tools; skipped by --no-dev |
autoload.psr-4 | Maps a namespace prefix to a directory; the prefix ends in a backslash |
scripts | Named commands such as composer test or composer stan |
config.platform | Pretends to be a given PHP version while resolving packages |
💡
Commit
composer.lock. It makes every machine and every deploy install identical versions; without it two developers can run the same code against different libraries and see different bugs.Editor, Xdebug and static analysis
composer require --dev phpstan/phpstan friendsofphp/php-cs-fixer
vendor/bin/phpstan analyse src --level=6
vendor/bin/php-cs-fixer fix --dry-run --diff
# Xdebug is an extension; enable it per command instead of globally
XDEBUG_MODE=coverage php vendor/bin/phpunit- Point the editor at
vendor/autoload.phpso autocomplete and go-to-definition work across packages. - Run PHPStan early: level 6 already catches wrong argument types and impossible branches before runtime does.
- Keep Xdebug off by default. The step debugger slows every request; switch it on for one command when you actually need stepping.
FAQ
Should I install tools globally with composer global?
Only for machine-wide commands you invoke by name. Project libraries always belong in the project, so the lockfile records them and CI installs the same set.
What is the difference between install and update?
install reads composer.lock and changes nothing; update re-resolves constraints and rewrites the lock. Deploys always run install.Related
Composer, dependencies and coding standards Functions, control flow and type declarations
Last refreshed 2026-09-18.