In this first post, we are setting up a minimal development environment in a few minutes for fast prototyping, evaluation of a project or testing.
I love solutions like Lando or DDEV, they are shining for complex stacks. But I also like to have other options to run a Drupal website locally. Since 2 years, I find myself going back to the PHP built-in server whenever I can for projects development, because
- it's very lightweight to use
- it's fast to start / to switch between different projects
- it's resource efficient: less CPU / RAM / disk space used
- network configuration is simplified
- it can be used to run E2E tests with Playwright, Cypress, ... by spinning up in no time a setup with mock data / SQLite
It allows to cover most of development requirements actually. For other subsystems (e.g. caching with Redis, proxies, ...) I'm generally happy with dev/stage environments for that.
Fabien Potencier is explaining how to use the PHP built-in server in his introduction to Symfony. He's combining it with containers, so a mixed approach is also an option.
What do we need in most projects?
Docker can be slow, especially on macOS, so let's focus on this operating system.
Switching PHP version is a must have, fortunately there is a brew package for that: https://github.com/shivammathur/homebrew-php
Follow instructions from the maintainer. Once installed, switching PHP version is a one-liner.
Example to switch from 8.1 to 8.2:
brew unlink firstname.lastname@example.org && brew link email@example.com # Check PHP version with php -v
Dont' forget to restart your server if it's running.
Most extensions/modules required by Drupal (dom, gd, zip, ...) are already bundled, so not much to do there. Others (including e.g. ImageMagick) can be found in this list https://github.com/shivammathur/homebrew-extensions
Quick note for Ubuntu/Debian
Extensions have to be explicitly installed to run multiple PHP versions.
sudo apt install software-properties-common gnupg2 -y add-apt-repository ppa:ondrej/php apt-get update sudo apt install php8.1 php8.1-fpm php8.1-cli # Add extensions, here with SQLite sudo apt install php8.1-curl php8.1-zip php8.1-dom php8.1-gd php8.1-mbstring php8.1-sqlite3 # Then do the same for each version of PHP, e.g. 8.2 sudo apt install php8.2 php8.2-fpm php8.2-cli sudo apt install php8.2-curl php8.2-zip php8.2-dom php8.2-gd php8.2-mbstring php8.2-sqlite3
Switch PHP version
sudo update-alternatives --config php
The main benefit of using another DB engine than SQLite is to allow to sync other environments database with Drush aliases, ... That might not be necessary most of the time, but it's always nice to have this option.
I'm generally using MySQL, as MariaDB can be slow on macOS in some cases. I have no feedback about other engines so far.
Again, there is a brew package. Install it with
brew install mysql brew services start mysql mysql_secure_installation
Get the Solr formula (default 8.x is fine, 7.7 is not maintained anymore).
brew install solr
Install Search API Solr and download the Solr configuration from here http://localhost:8888/en/admin/config/search/search-api/server/solr ("Get config.zip") then extract it.
Start Solr with
solr_8.x_config extracted archive, create a core. Example for
solr create_core -c my_project -d solr_8.x_config -n my_project
settings.local.php (or whatever overrides settings locally) override the connector configuration
$config['search_api.server.solr']['backend_config']['connector_config']['host'] = '127.0.0.1'; $config['search_api.server.solr']['backend_config']['connector_config']['path'] = '/'; $config['search_api.server.solr']['backend_config']['connector_config']['core'] = 'my_project'; $config['search_api.server.solr']['backend_config']['connector_config']['port'] = 8983; $config['search_api.server.solr']['backend_config']['connector_config']['http_user'] = ''; $config['search_api.server.solr']['backend_config']['connector_config']['http']['http_user'] = ''; $config['search_api.server.solr']['backend_config']['connector_config']['http_pass'] = ''; $config['search_api.server.solr']['backend_config']['connector_config']['http']['http_pass'] = ''; $config['search_api.server.solr']['name'] = 'Homebrew Solr';
On the Search API index: Rebuild tracking information then re-index.
Install it with
pecl install xdebug
Configure the current version of PHP, get the active ini file path with
Set in the ini file
xdebug.mode=debug xdebug.start_with_request=yes zend_extension="xdebug.so"
brew install mailhog
Configure your php.ini with
sendmail_path = "/opt/homebrew/bin/mailhog sendmail firstname.lastname@example.org"
If you are using transactional mail services by default on other environments (like Mailgun, Sendgrid, Mandrill, ...) make sure to also override the transport and not set SMTP. Also, be extra cautions if you are using Symfony Mailer as the transport can be overridden by each policy.
Override Symfony Mailer "All" policy to use Sendmail
$config['symfony_mailer.mailer_policy._']['configuration']['email_transport']['value'] = 'sendmail';
If you get Expected response code "220" but got empty code, add
$config['symfony_mailer.mailer_transport.sendmail']['configuration']['query']['command'] = ini_get('sendmail_path') . ' -t';
When to use Apache or Nginx
When the scope of the task is redirects map, server specific configuration, ... it's still possible to do the local setup but that requires extra effort to run and maintain for several virtual hosts, ... and perhaps that's the good moment to think about Docker.
For multilingual sites, it happens that the language negotiation is by domain and not by path, I fix it with
$config['language.negotiation']['url']['source'] = 'path_prefix';
Switch between Docker based / PHP built-in
Some settings might differ, like the database connection, ... Most Docker based solutions will come with their own environment variables (that can be printed in the CLI container with
printenv). In this case, a simple test of one of the environment variables with
getenv() can be used to switch the settings. It could be done in the same settings.local.php or 2 settings files could be loaded based on this test.
It's possible to run multiple projects at the same time. Just use another port.
# Defaults to :8888 drush serve # Start on a new port with e.g. drush serve :9999
localhost is used, cookies will be shared (even if it's a different port). Which can be annoying when switching projects. For this, I'm using Firefox Multi-Account Containers.
Images are not displayed
- Check permissions on the sites/default/files directory
- Perhaps ImageMagick is configured, possibly do settings override to GD or install ImageMagick
- Verify Stage File Proxy configuration override (if any)
- Image Style Warmer or
drush image:derivecould be handy if Stage File Proxy is not used