Setting up a PHP development environment for Symfony with Windows and WSL2
A consistent API and great documentation are key elements for providing an enticing developer experience. The third pillar is the development environment. An easy-to-setup software stack lowers the barrier to get your hands dirty. A fast and carefree dev environment keeps the focus on productive tasks in the long term. Developers should be developing, not shaving the yak.
Arguably PHP owes its popularity to the developer experience. And the simple code-refresh-code cycle remains compelling to this day. However to me the LAMP (Linux, Apache, MySQL, PHP) development experience on Windows was always a bit lackluster. Over the years there have been a number of efforts to improve this: WAMP, Vagrant, Docker… the list goes on and on.
While these efforts have enabled PHP development on the most popular desktop operating system, to me they've always felt non-native. And ultimately I would deploy to a Linux production environment where the app might behave just a bit differently. Compared to macOS, with its BSD underpinnings, Windows is just from a different world from the Unix realm that PHP stems from.
WSL2 sneaks in a Linux kernel to Windows
Microsoft has acknowledged that a vast majority of the Open Source development projects (PHP included) are based on Unix derived operating systems, GNU/ Linux being the most popular one. In August 2016, Microsoft released Windows 10 Anniversary Update that included a novel technology to enable developing and running Linux apps: Windows Subsystem for Linux (WSL)
WSL is a translation layer that allows running a Linux compatible shell by translating system calls from Linux kernel to their Windows kernel equivalents. This technology was used in reverse to allow Microsoft to run their MS SQL Server database product on the Linux operating system, in addition to Windows Server. In addition to translating syscalls, WSL also implements a filesystem compatibility layer that allows sharing files between the Windows Shell and WSL terminals.
In the years following its release the WSL has been improved, addressing some key issues like slow disk access. But ultimately the team decided that trying to replicate everything. In May 2019 Microsoft announced the WSL would be succeeded by WSL2. The second iteration of the technology runs on a native Linux Kernel on HyperV, fully supported by Microsoft. Hard to imagine for those of us who remember the time when Microsoft CEO called Linux a cancer.
Fast forward a year from announcement, and WSL2 is ready for primetime. Microsoft plans to release WSL2 in the Windows 10 2020 May Update (also known as Windows 10 2004). Yes, Microsoft is still terrible at naming things, but their engineering recent efforts are impressive. WSL2 will ship as an optional package with an expected release date on the last week of May.
Installing Symfony and eZ Platform on WSL2
While the official release is still some weeks away, enthusiasts can already get their hands on WSL2 by installing an Insider Build. This is close to the final, undergoing testing and validation.
- Enable WSL2
- Install a Linux distribution
- Install system software (PHP, MariaDB, composer, etc.)
- Install Symfony CLI
- Install application (e.g. eZ Platform v3.0.3)
- Run and develop your app ?
Enable WSL2 and Virtual Machine Platform
The first step is to enable Windows Subsystem for Linux (WSL2) and the Virtual Machine Platform. Start PowerShell as administrator and run the following commands:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
Once this is complete you have the infrastructure to run an embedded Linux environment.
Install a Linux distribution
If you open up a system prompt (command.exe) and type "wsl", you will get the following notice:
PS C:\Users\LattePanda> wsl Windows Subsystem for Linux has no installed distributions. Distributions can be installed by visiting the Microsoft Store: https://aka.ms/wslstore PS C:\Users\LattePanda>
This indicates that a Linux distro needs to be installed. We'll do that soon, but before that let's set the default WSL version to 2. WSL1 will continue to work, but we want explicitly to use WSL2:
C:\Users\LattePanda>wsl --set-default-version 2 For information on key differences with WSL 2 please visit https://aka.ms/wsl2
Installation is done from the Windows Store: https://aka.ms/wslstore. In the store you can choose from a number of different available Linux distributions. For our case we will choose Ubuntu 20.04 LTS because of its long support cycle and recent default system packages.
Ubuntu 20.04 ships with PHP 7.4. In this article will install eZ Platform v3.0 which is compatible with PHP 7.4, but for previous versions of eZ Platform you might want to install Ubuntu 18.04 LTS with an older default PHP version. Alternatively you can install a compatible version by hand.
Once you've downloaded the package, the installation wizard will ask you for a username and password. These are exclusively used for the embedded Linux instance within your Windows 10 installation. While you're in the store I recommend installing the new Windows Terminal.
After installation you can verify you've got a functioning WSL2 distro from a Windows terminal:
PS C:\Users\LattePanda> wsl --list --verbose NAME STATE VERSION * Ubuntu-20.04 Running 2 PS C:\Users\LattePanda>
Install system software
Once you've installed a distribution, you will use its standard tools to manage system packages and updates. In the case of Ubuntu and other Debian derivatives, you will use APT.
NOTE: At the time of writing there is a known bug related to realtime clock with Ubuntu 20.04 and the WSL2. To prevent this bug from surfacing, we configure APT to freeze our installed libc6 version to the one from the original distribution by issuing the following command:
sudo apt-mark hold libc6
Once this is in place it is safe to start using APT tools, first by updating all the packages:
sudo apt update sudo apt upgrade
Next let's install the core dependencies to run eZ Platform (and many other Symfony apps):
sudo apt install composer php-xml php-intl php-gd php-curl php-fpm
I deliberately left out the database service from above to illustrate that eZ Platform is compatible with both MySQL/MariaDB and PostgreSQL databases. Choose the one suited for your needs. I chose to use MariaDB for this tutorial. Install MariaDB server and the PHP MySQL extension:
sudo apt install mariadb-server php-mysql
Next start the MariaDB server instance and and log in as the root user:
sudo service mysql start sudo mysql
To add a database for eZ Platform, issue the following SQL statements:
CREATE DATABASE ezp; GRANT ALL ON ezp.* TO 'ezp'@'localhost' IDENTIFIED BY 'wsl2ezp'; FLUSH PRIVILEGES; EXIT;
Finally verify that you can access the database:
mysql -uezp -pwsl2ezp ezp
A Linux environment with PHP, MariaDB and the composer package manager is ready to go:
janit@W1ND0Z1337:~$ php -version PHP 7.4.3 (cli) (built: May 5 2020 12:14:27) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies janit@W1ND0Z1337:~$ composer --version Composer 1.10.1 2020-03-13 20:34:27 janit@W1ND0Z1337:~$ mysql --version mysql Ver 15.1 Distrib 10.3.22-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt update sudo apt install yarn
Install Symfony CLI
What you may have noticed gone missing from the above installation procedure is the A in LAMP and the E in LEMP. In plain English: Our stack has no web server installed. Traditionally a PHP stack has included a separate web server. A fully-fledged HTTP server is still a for production installs, but for development I've gravitated towards a simpler option: Symfony Local Web Server
The Symfony CLI is a stand-alone command line application that you can install from your WSL terminal. Remember to follow the Linux instructions, NOT the Windows ones:
wget https://get.symfony.com/cli/installer -O - | bash
The above installs it to your user directory, but you can make it available globally by moving it:
sudo mv /home/janit/.symfony/bin/symfony /usr/local/bin/symfony
We can now verify that our stack is working by creating a new hello world Symfony app and run the web server:
symfony new hello-world cd hello-world symfony serve
Open a browser window at http://localhost:8000/ to view the app running. The first time you run you might get a prompt from Windows whether this traffic is allowed. Now you should have a bare bones Symfony app running in Windows using WSL2 as shown in the screenshot below.
For more details on setting up TLS, multiple PHP versions, etc. head over to the Symfony docs.
Installing eZ Platform and accessing files
Next up is installing and running a full Symfony app. This works like in any Linux environment (because it is one!). We'll install a copy of eZ Platform by using composer. Behind the scenes composer will clone the meta repository, check out a release and install the packages used:
composer create-project ezsystems/ezplatform ezplatform ^3 cd ezplatform
To install our database we need to define the database configuration and run the installer:
echo "DATABASE_URL=mysql://ezp:firstname.lastname@example.org:3306/ezp?serverVersion=10.3" > .env.local composer run-script ezplatform-install
Once this is complete you can enter the project directory and start the Symfony web server:
The output shows that the Symfony web server detects and uses the installed copy of PHP-FPM:
May 11 21:11:40 |DEBUG| PHP Reloading PHP versions May 11 21:11:40 |DEBUG| PHP Using PHP version 7.4.3 (from default version in $PATH) May 11 21:11:40 |INFO | PHP listening path="/usr/sbin/php-fpm7.4" php="7.4.3" port=38257 May 11 21:11:40 |DEBUG| PHP started May 11 21:11:40 |INFO | PHP 'user' directive is ignored when FPM is not running as root May 11 21:11:40 |INFO | PHP 'group' directive is ignored when FPM is not running as root May 11 21:11:40 |INFO | PHP fpm is running, pid 15263 May 11 21:11:40 |INFO | PHP ready to handle connections May 11 21:11:40 |INFO | PHP systemd monitor interval set to 10000ms [OK] Web server listening The Web server is using PHP FPM 7.4.3 http://127.0.0.1:8000
Now if you open a browser (in Windows) and head to http://localhost:8000/ you can see the application running. To access the files from Windows, you can execute the following command to open up a Windows Explorer window in your current working directory:
From here you can pick things up and use whatever Windows IDEs (or even notepad.exe!) you want to use for development. If you want to access the MariaDB database from Windows tools be sure to read this blog post on connecting to a MySQL DB from the Windows host with WSL2.
You can watch the whole installation process in this video:
The original WSL was something that brought Windows closer to the native PHP ecosystem, but it was lacking in performance and compatibility in some areas. WSL2 is a new take on the same task (running Linux applications on Windows), that should help alleviate the previous pain points. I wouldn't be surprised to find some corner cases where it fails, but it's definitely a good start.
It is also worth noting that while this tutorial focused on creating a minimalist development environment, many real-world projects are more complicated. You've got dependencies to additional daemons like ElasticSearch, Redis, RabbitMQ and maybe a host of microservices.
As a cherry on top, many of us need to work on multiple complex project setups like this simultaneously. For these cases tools like Docker and Kubernetes are valid choices. Changes done in WSL2 make it possible to run these workloads on Windows better than before, too.