True PHP7 Multi-Threading: How to Rebuild PHP and use pthreads
Building PHP on Mac and Linux with Multi-Threading and pthreads examples

Your PHP apps could really do with multi-threading capabilities for running tasks in parallel, but you know the PHP building process can be troublesome and time consuming — not only this, migrating your production server PHP to a new build also sounds like a headache. Well, things are not all bad if we utilise a couple of PHP utilities at our disposal to aid the process.
This article will walk you through this process of compiling a new, multi-threading capable PHP build, before migrating to that version, then demonstrate how to code multi-threaded applications with the pthreads extension.
Off-the-shelf PHP builds from package managers do not support multi-threading. What we need to do is rebuild PHP with a flag that enables ZTS, or “Zend Thread Safety”, which then allows us to expand our thread use. To facilitate this build process we will utilise a tool named phpbrew, designed to build multiple versions of PHP and switch between them with ease. Then, only once our builds are completed can we experiment with pthreads.
There are some platform specific gotchas, but we will overcome these with simple solutions.
You understandably have a development machine and production ready servers, therefore we will go through the phpbrew building process on both Mac OS and CentOS. If you are using another distribution I will highlight a resource for specific OS requirements.
Let’s start with building PHP on a Mac.
phpbrew Introduction
phpbrew has been around since 2012 and is still being bumped to this day — some committed maintainers demonstrating that PHP is still a critical tool for a lot of developers. The installation and usage instructions of phpbrew are coherent and easy to understand. Although not required, it’s beneficial to familiarise yourself with phpbrew at this point.
Why use phpbrew over a vanilla build command?
For a few reasons. Not only can phpbrew build multiple versions of PHP and allow us to easily switch between them, it also makes the build process a lot easier with the use of “Variants”. Instead of copying many flags as we would in a vanilla build command, we can utilise keywords that act as either one flag or a group of flags. An example of a Variant is +mb
, which will build PHP with mbstring
and mbregex
. Go directly to the list of variants here to see what is available.
Beyond Variants, we can install extensions and configure our ini file with only one command. In other words, we don’t have to worry about maintaining the ini after installing an extension — phpbrew does that for us. For example, we will be utilising the following command to install pthreads into our newly built PHP version:
phpbrew ext install github:krakjoe/pthreads
As you can see, we are installing the latest version (important!) of the pthreads extension directly from Github and including it in our configuration file, all from this command. Pretty nice. This is part of the installing extensions support phpbrew offers.
Their Github link to the project home page is here:
Using phpbrew on Mac OS
Let’s install phpbrew first and foremost. Within Terminal, download and install phpbrew with the following commands:
curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew
chmod +x phpbrew
sudo mv phpbrew /usr/local/bin/phpbrew
After moving phpbrew into your local bin, initialise a bash script for your shell environment:
phpbrew init
[[ -e ~/.phpbrew/bashrc ]] && source ~/.phpbrew/bashrc
Now run the following commands to update the PHP builds available to phpbrew:
phpbrew known
phpbrew update
We will be building the latest version of PHP7.2, being 7.2.12 at the time of this writing. Feel free to use a newer release if you have access to it.
At this point you could also run phpbrew variants
for a full list of available Variants at your disposal.
phpbrew is now ready to use — and only one command is now needed to build PHP. But we have one more thing to do — install the PHP dependencies.
Mac: Installing PHP dependencies
PHP has a lot of requirements, and phpbrew have dedicated a whole page of their documentation to outline platform specific installation instructions for these requirements.
We are using a Mac at this point, and according to the above Requirement page’s Mac OS section, we can either use HomeBrew or MacPorts to install them.
Take my advice and use MacPorts. I firstly (and eagerly) attempted to use HomeBrew, but ran into consistent linking failures and build errors with no resolution in sight. If you have not got MacPorts installed, grab the latest version for your OS (10.14 Mojave being the latest at this time) from the MacPorts Downloads page. Once installed, run the following commands:
port install curl automake autoconf icu depof:php72 depof:php72-gd mcrypt bison re2c gettext openssl
Some of these may already be installed on your system. That is not a problem — just run the entire command and let the installer do the rest.
Mac: Building PHP
We are now ready to build PHP with ZTS enabled. Run the following phpbrew install command to do so:
phpbrew install php-7.2.12 +default -- --enable-maintainer-zts
We are using the +default
Variant here, which includes commonly used PHP extensions. We are also extending our variants with plain flags, which can be appended to the command with -- --flag1 --flagN
.
This process may take a while depending on your machine. On a first generation 12" MacBook that I tested this build on, the build completed in just over 30 minutes. A build log is provided as it is happening that you can tail, giving you real time feedback.
Once the build is completed we are greeted with outputs of where the build is located, along with our ini configuration file location. Very handy.
Mac: Using the New Build
At this point you can run phpbrew list
listing all your PHP builds. You will notice php-7.2.12
on that list — the version just built.
phpbrew also provides the switch
and use
commands to switch PHP version, dependent on our builds. Permanently switch to the new build like so:
phpbrew use php-7.2.12
Within the Terminal window you are in now, the php
command will always refer to the newly built PHP7.2.
Mac: Installing pthreads
Our last job is to install the pthreads extension. Do so with the following command:
phpbrew ext install github:krakjoe/pthreads
Let’s consider a couple of other extensions you may wish to install. I use Composer and MongoDB, and also Xdebug on my development environment. To install these extensions, run the following:
phpbrew ext install mongodb
phpbrew ext install xdebug stable
phpbrew app get composer
If you require any other extension, go ahead and use phpbrew ext install
to get your build up to date with what you need.
At this point we are ready to use pthreads on a Mac. In the next section we will run through the installation process on CentOS. Skip ahead to the pthreads section if you do not need further installations!
Using phpbrew on Cent OS7
Let’s run through installing the dependencies for building PHP on CentOS first. Install the requirements with the following comamnds:
sudo yum install make automake gcc gcc-c++ kernel-develsudo yum install php php-devel php-pear bzip2-devel yum-utils bison re2c libmcrypt-devel libpqxx-devel libxslt-devel pcre-devel libcurl-devel libgsasl-devel openldap-devel, httpd-devel
I also needed to install readline on my CentOS system:
yum install readline-devel
If you do not have PHP installed on your CentOS server, the phpbrew Requirements recommend you install it firstly. I prefer using RPM to install PHP. Do so with the following comamnds:
sudo yum -y install epel-release
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7*.rpm
Update the PHP7.2 remi file to enable php7.2. Change enabled=0
to enabled=1
in the [remi-php7.2]
block only:
cd /etc/yum.repos.d/#open php7.2 repo file and change enabled=0 to enabled=1
sudo vi remi-php72.repo
Finally, install the following PHP packages using yum:
sudo yum install php php-gd php-common php-mysql php-mcrypt php-devel php-xml
CentOS: Installing phpbrew
Installing phpbrew is identical to the Mac. For completeness, the installation commands are listed here again:
curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew
chmod +x phpbrew
sudo mv phpbrew /usr/local/bin/phpbrew
phpbrew init
[[ -e ~/.phpbrew/bashrc ]] && source ~/.phpbrew/bashrc
phpbrew known
phpbrew update
It is worth noting that if you reboot your server you will need to re-inititate phpbrew with phpbrew init
and amend bashrc again. You may wish to set up a daemon at boot time to do this automatically after finishing the article.
CentOS: Building PHP
Now, the build command differs slightly from that of the Mac. On CentOS, build PHP with:
phpbrew install php-7.2.12 +default +openssl=/usr -- --enable-maintainer-zts --with-libdir=lib64
We are now using the +openssl=/usr
Variant and and additional—-with-libdir=lib64
flag. Why is this? Because unlike the Mac build, we explicitly need to define the OpenSSL location for the PHP build to compile. If not, we will get an error during the build process stating no OpenSSL libraries were found. This oversight requires a simple fix, but the issue has been discussed on Github here if you would like to investigate it.
Note on memory requirements building PHP on VPSs: If you are using a VPS make sure you have at least 1GB of free memory. I have attempted to build PHP on around 400mb of free memory — which ran out and consequently the build failed. If you need to, upgrade your VPS resources temporarily to build PHP, then revert back once completed.
CentOS: Using the New Build
With a successful new build, we can now use it in an identical way to the Mac process:
phpbrew use php-7.2.12
CentOS: Installing pthreads
Installing the pthreads extension — and the others of interest — is also identical to the Mac:
phpbrew ext install github:krakjoe/pthreads
phpbrew ext install mongodb
phpbrew ext install xdebug stable
phpbrew app get composer
With this section completed, we now have a suitable PHP build for multithreading on a local machine and production environment! The last piece of the puzzle is using pthreads to realise our new found threading capabilities.
Using pthreads
pthreads brings PHP’s threading capability to what developers now take for granted in other languages. It provides multi-threading that is compatible with PHP based on Posix Threads. This is true multi threading at its core.
So how do we use pthreads? Well, it provides a class named Threads that we extend from. From here, we utilise Thread’s run()
method to code the logic we wish to carry out in a separate thread. Think something like this:
class MyProgram extends Thread { public function run() { //run my program echo "Woo, I am running in a new thread!";
}
}$program1 = new MyProgram;$program1->start() && $program1->join();echo 'Thread finished';
Here we are utilising two pthread methods start()
and join()
, which are inherited from Thread
.
start()
is self explanatory — it calls the run()
method in a separate thread, allowing for our parallel processing to begin.
join()
on the other hand requires a little more explanation:
join()
allows us to bring a thread into the current context, which therefore wait for it to finish executing before our current context continues executing.
In other words, if we join a thread to our main process, the main process will wait for our joined thread to finish before it continues executing.
In the snippet of code above, $program1
’s thread is joined to our main process, therefore our last echo statement is called after the thread has finished processing.
Example 1: Successively Joining Threads
To understand what is happening here, consider the following example where 5 thread jobs are instantiated, started and joined in order. Each thread process sleeps for X seconds (X being the thread index) before echoing a closing statement:
Because we are bringing each thread into context as it is started, the execution will wait until said thread has finished executing before moving onto the next one.
Within your phpbrew Terminal window, run the above script to examine this behaviour, with php multi-thread1.php
.
Example 2: Running Threads Simultaneously
Let’s take the same class and behaviour as above, but this time not join each thread as they start.
In this example all 5 threads are started simultaneously in our loop, with the join()
method removed. Our main execution will then sleep for 5 seconds as the threads finish processing in the background. We will then try to join the threads again. Run the following example to examine the behaviour:
A couple of interesting points to note here:
- Even though the threads are running simultaneously in separate threads, we are still receiving their echo statements within the Terminal.
- We attempt to join the threads once they finish executing — which fails. Instead of PHP throwing an error, execution simply continues on our main process, as demonstrated by the last echo statement.
Can Threads be used in namespace classes?
Yes they can indeed. If you are using Composer autoloading for example, you can simply extend your classes with extends \Thread
as per our previous examples.
Exploring pthreads
The best way to explore the pthreads library is via the php.net documentation. In total, pthreads consists of 9 classes and 53 methods at the time of writing. In reality you may not need to use the majority of these. In fact if you simply wish to speed up execution by running parallel tasks, the methods used in the examples above will suffice for you. However, it is always worth familiarising yourself with the capabilities of a package for future reference.
Take a look at the pthreads documentation index to get started. All the methods listed there come with examples, likeThread::getCurrentThreadId
— an identification function that can be called within the run()
method of a thread to get its ID.
The Threaded
class contains valuable state detection methods, such as isRunning
, another useful method allowing us to check whether the run()
method is executing at a given time on a particular thread.
To Conclude
You should now be ready to upgrade your PHP apps to a multi-threaded setup. It is very satisfying to unlock this added capability and see your scripts performing faster. Much faster.
Take some time to design your threads in such a way that maximises speed, but to execute tasks in a clean order. Do some threads depend on the completion of others? If so, keep track of your execution state. Remember that threads add flexibility and speed, but they also add complexity to your apps.
How are you using pthreads? It would be great to hear in the Responses!