Throughout the 4 previous months I got the chance to work on php applications again. This time not as a developer, but as an operator. I spent a good part of my internship trying to deploy, run and optimize php apps on my local environment and on the cloud.
This post will be in many parts (4+). This part will be a sort of introduction to the php ecosystem. We’ll try to build a general overview on the different ways we can deploy a php application. And in the next parts we’ll dive deeper on the process and go through the different solution in the market.
This series is based on my own experience only, and especially on the challenges I faced and how I did overcome them.
PHP is weird !
Fisrt things first, I don’t hate PHP, nor I beleive it is a bad programming language (I don’t believe there are such things as a good and bad programming languages). However PHP is a weird language(Ecosystem), this is mainly related to its legacy and how things were sone in the past.
What I find really weird and confusing (sometimes powerful) about PHP, is the absence of a strict definition and sometimes borders between the different language components.
Unlike python, ruby, GO, C … there is no such thing as a standard library in PHP, (SPL is actually a separate php extension containing some useful classes and functions).
Also there are a lot of extensions, some are written in C and should be compiled against the PHP Source, and others are written in PHP itself and can be run in Zend Engine.
The PHP standard distribution consists of 4 main components:
- Zend Engine: responsible for compiling the php code and running it. (There are many alternative implementations)
- PHP Core: it consists of the “core extensions” (ext/standard) that cannot be left out of php (disabled). These are not really extensions, but they represent PHP’s core functionalities i.e strings, arrays, math and classe/objects… Here we can find functions like
- PHP API: Extensions bundled with PHP (ext/), providing useful functionalities like json parsing (ext/JSON), a common interface for connecting to databases (ext/PDO) …
- PHP SAPI (Server API): It enables PHP to communicate with other application (Web Server, CLI, …), it handles the startup and shutdown of PHP inside those application and provides hooks for handling data such as cookies and POST data in an application-agnostic manner. There are SAPIs for Apache web server, cli, fpm and more.
In PHP’s world there are two types of extensions:
PHP Extensions (YES ! They are called that): These extensions are written in C and should be compiled and linked statically with the PHP Source code, or built as dynamic libraries (
.sofiles) to be loaded at runtime (using
PHP provides a lot of extensions out of the box in its source code. All these extensions live in the ext/ folder and are organised into 3 categories: Core Extensions, Bundled extensions (Belonging to the PHP API) and external extensions. The only Difference between them is that external extensions rely on external libraries in order to compile (e.g. PHP Curl on libcurl, PHP zip requires libzip …), while *Core and Bundled extensions are self-contained and do not rely on any external libraries to do their work.
Another type of PHP Extensions are PECL extensions. PECL or PHP Extension Community Library is a huge libraries hosting PHP extensions that didn’t make it into the standard distributions (are out of PHP’s tree). Since those are PHP extensions too, they can be compiled and linked statically with PHP, or installed as shared libraries.
PHP userland Libraries: Those are written entirely in PHP and use PHP extensions under the hood, they are the one that you’ll install using composer.
in this part, I’ll use “PHP extensions” for C extensions and “PHP package” for userland libraries written in PHP itself.
PHP packages (written in PHP) are often easier to install, we can find them in PEAR (PHP Extension and Application Repository) or elsewhere (often in git repos).
To install a PHP package we can either use the
pear cli tool or simply
composer. Even if
pear cli can install PHP extensions (written in C) while Composer can’t, the latter is more modern and has many more features including dependency management, easier installation, multiple sources (can fetch packages from PEAR and from version control systems(git) too), per project packages (with
pear all package should be installed globally) …
As for native PHP extensions, there is no way just yet to install them using composer. For this reason you should use
pecl cli tool (which is an alias for pear) or compile them manually, or maybe if you are lucky your linux distribution will already have the extension you are looking for in their official repositories.
In the containers context, php docker images come with a collection of neat tools (docker-php-ext-*) which can be used to install/compile php extension. (I’ll leave that for the next post).
Running a web application requires 2 layers: an HTTP layer which will handle external HTTP requests, and return HTTP responses, and a processing layer which will take care of interpreting the backend code.
Here is a very good article about the deploying web apps, you can read it if you want to learn more. What I called the processing layer is presented in the article by the Gateway + some sort of language runtime.
Deploying php web apps
For the web server layer the most common options are Apache and Nginx, while in processing layer there are many other options.
The other options
Let’s start off with the least common options:
- WEB Server with CLI SAPI: This the most common solution in the development phases. Unlike other high level languages, PHP does not have HTTP capabilities by itself. However It has networking capabilities (PHP NETWORK) which we can use to build our own HTTP server (Serving static files and executing PHP files …). Most of the PHP frameworks provide a built in web server for development purposes. Since 5.4.0 PHP include a dev web server too.
Those are often not production ready, and are there only for development purposes.
php -S localhost:8000
CGI (Common Gateway API): Actually CGI is a specification (Protocol) for web servers to be able to run external scripts (CGI scripts). In Apache for example there is
mod_cgiwhich make it capable of running PHP CGI script (php scripts written in a special way).
This approach is pretty old and has many drawbacks, most importantly speed, since each web request will result in spawning a new php process which will need to start and read the configuration from
Application Servers: Surprisingly PHP has application servers too! The most popular is Appserver which borrows some features from the Java EE world, in particular Web containers (in memory servlets) and beans.
There are also many other solutions like RoadRunner, Nginx-unit, and of course zend server. I’ll try to experience with them more and write something about them later.
There are many ways to host PHP web applications behind an Apache web server, the most common are
Apache by itself can’t interpret PHP files, however it has a module named
mod_php that does. When loaded the module starts the PHP interpreter after Apache startup.
This approach is much faster than
mod-cgi, since the PHP interpreter is started once, and the code is executed by the Apache process itself, eliminating the startup time (on request).
mod-php is less secure(tight coupling between the web server and the php interpreter), more cumbersome since we need to start a fat process containing the php interpreter even when serving non php files (static files), also it does only support
mpm-prefork in which an apache process can handle one HTTP request at a time (this is a side effect of running php in apache).
CGI is a simple unidirectional agent-less text-based protocol which means the web server forwards the HTTP request to the CGI script in a standard text format, and and wait for its output. Given that CGI is so limited and slow scale.
Then comes FastCGI, It is an improvement over CGI. First it is a binary protocol, which make it a bit faster, also it is bidirectional and rely on an agent i.e. it requires a communication socket (TCP or UNIX) between the web server and the php agent(the program responsible for PHP code interpretation).
The most common FastCGI implementation in PHP is PHP-FPM (FastCGI Process Manageer)
As its name stats php-fpm is a process manager, When it starts it creates a pool of processes that are able to process php files.
When the client requests a php page from the web server (Nginx or Apache), the latter forward the request to php-fpm via TCP or a UNIX socket. Then one of the worker processes handle it and return the result to the web server and then the client is served.
One important thing to note is that PHP-fpm can only handle php files, and can’t serve static files it self. So it often get deployed along-side a web server (Often Nginx).
in the next posts we’ll go deeper, and try to explore the different approches we talked about especially in containers and kubernetes context.
That’s it! Thanks for reading 😁
and See you soon
EDIT 1: I would like to thank the community for their positive feedback especially Mr. @Mohamed El Hachimi El Idrissi with whom I had an very interesting conversation. Hopefully I’ll be able to summerize my conversation with Mohamed in the next post.