A while ago, I learned about Symfony's local web server when I bumped into Fabien Potencier's slides from SymfonyCon Lisbon. I previously used docker for local development but that didn't always go smoothly, especially in larger projects it tends to get rather slow.

Going through the slides, I was rather excited to try it out and to my surprise - it was a breeze to set up on macOS!

So far, I've been using it with projects using Symfony and old non-framework projects.

Download the Symfony client

To get started, go download and install the symfony client. For convenience, here's the command:

curl -sS https://get.symfony.com/cli/installer | bash

Once installed, you should be able to run symfony in the command line.

Getting the correct PHP version

The Symfony client is able to discover which PHP versions you have installed. To get a list of installed versions, run symfony local:php:list and you'll get an output similar to the following:

┌─────────┬──────────────────────────────────┬─────────┬──────────────┬─────────────┬─────────┐
│ Version │            Directory             │ PHP CLI │   PHP FPM    │   PHP CGI   │ Server  │
├─────────┼──────────────────────────────────┼─────────┼──────────────┼─────────────┼─────────┤
│ 5.6.40  │ /usr/local/Cellar/php@5.6/5.6.40 │ bin/php │ sbin/php-fpm │ bin/php-cgi │ PHP FPM │
│ 7.1.23  │ /usr                             │ bin/php │ sbin/php-fpm │             │ PHP FPM │
│ 7.1.28  │ /usr/local/Cellar/php@7.1/7.1.28 │ bin/php │ sbin/php-fpm │ bin/php-cgi │ PHP FPM │
│ 7.2.15  │ /usr/local/Cellar/php@7.2/7.2.15 │ bin/php │ sbin/php-fpm │ bin/php-cgi │ PHP FPM │
└─────────┴──────────────────────────────────┴─────────┴──────────────┴─────────────┴─────────┘

If I recall correctly, macOS ships with PHP pre-installed, for additional versions, I suggest you take a look at Homebrew.

For completeness, let's install the latest PHP version with Homebrew:

brew install php@7.3

PHP 7.3 should now be included in the list when running symfony local:php:list.

Running the local server

To run the local server, cd into your project root and run symfony server:start - your output should look similar to the following:

↳  symfony server:start
May  9 22:01:04 |INFO | PHP    listening path="/usr/local/sbin/php-fpm" php="7.3.3" port=60205
May  9 22:01:04 |DEBUG| PHP    started
May  9 22:01:04 |INFO | PHP    'user' directive is ignored when FPM is not running as root
May  9 22:01:04 |INFO | PHP    'group' directive is ignored when FPM is not running as root
May  9 22:01:04 |INFO | PHP    fpm is running, pid 93955
May  9 22:01:04 |INFO | PHP    ready to handle connections

 [OK] Web server listening on http://127.0.0.1:8000 (PHP FPM 7.3.3)


WARNING unable to find the application log

You'll now be able to access your project on http://127.0.0.1:8000, it will run on PHP7.3 that we've installed earlier.

The Symfony client will always pick the highest PHP version, so you'll always want to assign a specific version to your project. To do this, you create a new file in the root of your project called .php-version with the contents being the PHP version your project requires.

Let's try with PHP 7.2, install it with brew brew install php@7.2 and create the .php-version file with 7.2 as its content:

echo "7.2" > .php-version

Then, run symfony server:start and you can see it's now using PHP 7.2.15

↳  symfony server:start
May  9 22:09:31 |INFO | PHP    listening path="/usr/local/Cellar/php@7.2/7.2.15/sbin/php-fpm" php="7.2.15" port=62298
May  9 22:09:31 |DEBUG| PHP    started
May  9 22:09:31 |INFO | PHP    'user' directive is ignored when FPM is not running as root
May  9 22:09:31 |INFO | PHP    'group' directive is ignored when FPM is not running as root
May  9 22:09:31 |INFO | PHP    fpm is running, pid 97778
May  9 22:09:31 |INFO | PHP    ready to handle connections

 [OK] Web server listening on http://127.0.0.1:8000 (PHP FPM 7.2.15)


WARNING unable to find the application log

With the -d option, you can run the server as a daemon - pretty handy when you don't want multiple terminal tabs/windows running.

↳  symfony server:start -d

 [OK] Web server listening on http://127.0.0.1:8000 (PHP FPM 7.2.15)


Stream the logs via symfony server:log.

You can always pull out the logs with symfony server:log

↳  symfony server:log
May  9 22:14:45 |INFO | PHP    listening path="/usr/local/Cellar/php@7.2/7.2.15/sbin/php-fpm" php="7.2.15" port=63583
May  9 22:14:45 |DEBUG| PHP    started
May  9 22:14:45 |INFO | PHP    'user' directive is ignored when FPM is not running as root
May  9 22:14:45 |INFO | PHP    'group' directive is ignored when FPM is not running as root
May  9 22:14:45 |INFO | PHP    fpm is running, pid 344
May  9 22:14:45 |INFO | PHP    ready to handle connections

And view which servers are running with symfony server:list.

↳  symfony server:list
┌────────────────────────┬────────┬─────────────────────────────────────────────────────────┐
│          URL           │ Domain │                        Directory                        │
├────────────────────────┼────────┼─────────────────────────────────────────────────────────┤
│ http://127.0.0.1:8000  │        │ /Users/kevinvandenborne/Development/symfony-server-test │
└────────────────────────┴────────┴─────────────────────────────────────────────────────────┘

The Symfony client comes with various wrappers that take into account the PHP version defined for your project (`.php-version`).

↳  symfony help
...
Available wrappers:
Runs PHP (version depends on project's configuration).
Environment variables to use SymfonyCloud relationships or Docker services are automatically defined.

  composer                                       Runs Composer without memory limit
  console                                        Runs the Symfony Console (bin/console) for current project
  php, pecl, pear, php-fpm, php-cgi, php-config  Runs the named binary using the configured PHP version

Enabling TLS/HTTPS

You'll often (or always) want to use or test with https://, the Symfony client has you covered with symfony server:ca:install. When you restart your server, both http:// and https:// are available.

↳  symfony server:stop
Stopping web server


 [OK] Stopped 1 process(es) successfully


↳  symfony server:start -d

 [OK] Web server listening on https://127.0.0.1:8000 (PHP FPM 7.2.15)


Stream the logs via symfony server:log.

Local domain names

Having https://127.0.0.1:8000 as your project address may become confusing and tedious to type, especially when you have many projects running. For this, the Symfony client also has you covered with its built-in proxy.

↳  symfony proxy:start

 [OK] Proxy server listening on http://127.0.0.1:7080

You'll have to configure this proxy in your network preferences. On macOS, open network preferences and go to "Advanced.." (make sure you press the lock icon to make changes). Then, click on the "Proxies" tab.

In the proxies tab, check "Automatic Proxy Configuration" and enter the url that was outputted when you started the proxy appended with proxy.pac, in this case, http://127.0.0.1:7080/proxy.pac

Proxies tab in advanced network preferences

All that is left now is attaching a domain to our project, this can be done with the proxy:domain:attach command.

↳  symfony proxy:domain:attach symfony-server-test.com
The proxy is now configured with the following domains for this directory:
 * http://symfony-server-test.com.wip

↳  symfony server:start -d
Stream the logs via symfony server:log.

 [OK] Web server listening on https://127.0.0.1:8000 (PHP FPM 7.2.15)
                              https://symfony-server-test.com.wip

You should now be able to access your project using the domain you've attached, in my case that's https://symfony-server-test.com.wip

You can configure the proxy by editing the ~/.symfony/proxy.json file. There's also a handy list available on http://127.0.0.1:7080/

All set!

You're all set now, enjoy your local development set with custom domain names and TLS, all thanks to Symfony!

For special project requirements such as elasticsearch/redis/.. I personally still use docker containers, saves me from installing all that stuff locally and isolates it from the rest of my projects. I run MySQL locally unless the production server runs another flavor of is (such as MariaDB), then I also use docker for that as there can be slight changes. Ideally, you have a staging/testing server that is identical to the production server.