In short: There are three common ways to develop PHP locally: the built-in web server (php -S) for quick experiments, the official PHP Docker images for reproducible environments, and tools such as DDEV that create ready-made project environments for WordPress, TYPO3, Shopware or Laravel. What matters is less the tool than the match: the same PHP version with the same extensions should run locally as in production. For security, though, the runtime on the server is what ultimately counts.
Why a development environment of your own?
Making changes directly on the live server is risky: every typo is immediately visible to customers, and test files tend to be left lying around. A local environment separates experimenting from operations. It is also the place where new PHP versions and security updates are tested first.
For management and IT leadership, the question is therefore not whether a development environment exists but whether it matches the live system. A local environment with PHP 8.4 while production still runs 8.1 conceals problems instead of revealing them. The same applies to the database version, web server and extensions such as intl, gd or pdo_mysql: even a single missing extension can mean an error is only noticed in production.
The options at a glance
| Option | Suited for | Strengths | Limits |
|---|---|---|---|
Built-in server php -S |
short experiments, small scripts | no further software to install | no replacement for Nginx or Apache, not for public networks |
| Official Docker image | teams, reproducible environments | version can be fixed via tag, close to container operation | configuration and interplay of several containers must be built yourself |
| DDEV | CMS and framework projects | ready-made project types, database and web server included | requires Docker, own conventions |
| Locally installed PHP | individual developers | fast, direct | versions for several projects are hard to keep in parallel |
The built-in web server
Since PHP 5.4, the command line has included a small web server. It starts in the current directory or, with -t, in a different document directory:
php -S localhost:8000 -t public/
Optionally you pass a router script that runs first on every request. The manual is unambiguous in its classification: the server is meant to support development, is not a full-fledged web server and should not be used on a public network. Therefore bind it to localhost, not to 0.0.0.0, otherwise it may be reachable on the company or hotel Wi-Fi. More about the command line under PHP on the command line.
Docker: official PHP images
The official images come in several variants: cli for the command line, apache with Apache and mod_php, fpm for operation behind Nginx or another proxy, plus Alpine variants for small images. A quick start with Apache:
docker run --rm -p 8080:80 -v "$PWD":/var/www/html php:8.4-apache
The images contain the templates php.ini-development and php.ini-production in the directory $PHP_INI_DIR. Locally, the development template makes sense because it displays errors. For production, the image documentation expressly recommends the production template. You install extensions with the bundled helper scripts docker-php-ext-configure, docker-php-ext-install and docker-php-ext-enable. How tags and digests work is explained in Official PHP Docker images.
DDEV: ready-made environments for CMS and frameworks
DDEV builds on Docker and sets up a web server, PHP, a database and auxiliary services for a project. The quick-start guides almost always follow the same pattern:
ddev config --project-type=wordpress --docroot=web
ddev start
ddev composer install
ddev launch
DDEV knows project types including wordpress, typo3, shopware6, laravel, symfony, drupal11, magento2 and the generic type php. You set the PHP version in .ddev/config.yaml; according to the documentation, versions from 5.6 to 8.5 are possible, and the default is 8.4:
# .ddev/config.yaml (excerpt)
php_version: "8.3"
webserver_type: nginx-fpm # alternatively apache-fpm
That way the local environment matches the live version, even if it is not the latest.
Local development, patch level and PHP2Shell
After PHP2Shell, several platform providers pointed out a typical fallacy. According to its bulletin, Vercel stresses that updating the local development environment, the application code or the plugins is not enough; what is decisive is the PHP runtime that actually serves requests. Having PHP 8.4.27 locally does not protect the server running 8.4.26.
Conversely, the development environment brings risks of its own that can migrate into production:
- Development ini in production:
display_errorsis switched on inphp.ini-development. If this file reaches the server, visitors see paths and error messages. See Switching off error output in production. - Forgotten test files:
phpinfo()files, database dumps or.envfiles with credentials do not belong in the deployment. - Open development servers:
php -Sor containers with published ports on machines in the network. - Outdated images: Local images are not updated by themselves; after a PHP security release, the development environment must also be rebuilt so that tests run with the corrected version.
Define the PHP version in one place, for example in the Dockerfile or in .ddev/config.yaml, and change it together with the server configuration when updating.
How does PHP SOS help?
In the security maintenance contract, we make sure development and operations fit together:
- Comparison: We compare PHP version and extensions across development, test and live environments.
- Update procedure: New PHP releases are applied first in the test environment, then live, each with a restart.
- Check deployment: We make sure that no development ini, test files or credentials reach the server.
- Documentation: You know at any time which version is running where.
Frequently asked questions
May I use the built-in PHP web server publicly?
No. The PHP manual states expressly that the server started with php -S is for development and should not be used on a public network. For production, PHP-FPM with Nginx or Apache is the right choice.
What is DDEV?
DDEV is a tool built on Docker that creates ready-made development environments for PHP projects. It knows project types for WordPress, TYPO3, Shopware 6, Laravel, Drupal and other systems.
Is it enough to update the local PHP version?
No. Against flaws like PHP2Shell, what counts is the PHP runtime that actually serves requests, that is, the one on the server. The local environment should have the same version, though, so that tests are meaningful.
Which php.ini do I use locally?
Locally, php.ini-development makes sense because it displays errors. In production, php.ini-production should be used. The official Docker images ship with both templates.