run code in 300+ PHP versions simultaneously


I have been programming in PHP since 2002, and as me and my collegues used more advanced features we noticed PHP's behaviour would sometimes change without any reference in the changelog. An example of this was this parse_ini bug, which was an example where we needed to demonstrate the variation of results over time and 3v4l.org showed a clear advantage.

Since 2012 I have provided vanilla PHP binaries that make it simpler to demonstrate issues that would otherwise be harder to reproduce, and cumbersome to compare to previous version. Traffic has steadily been growing as more developers found this site to be a helpful daily tool, as the following sessions-graph shows:

submitsbotshomepageuncategorized112 56327 14213 636153 3411 507 18585 295122 9931 715 4737 624 321211 191225 7188 061 23010 248 788575 802348 73611 173 32618 984 187341 811562 12219 888 12018 213 318464 842863 80319 541 96327 431 680456 9991 059 90728 948 58618 783 313646 8451 281 58420 711 7424 578 3247 113 240633 02512 324 58920122013201420152016201720182019202036 M30 M24 M18 M12 M6 M0 M

This site is build and maintained by Sjon Hortensius.


I started in april 2012 and have been adding features ever since. If you like numbers, here are a some from the database:

Major changes

I have been adding features ever since I started. Here is a list with some major changes I made over the years:


There are a few tricks and some hidden functionality you might appreciate:




This site started out on a server with 256 MiB memory, which was enough to run one script at the time, as well as the site itself. However, when I added HHVM, it needed a lot more memory, so I upgraded to a 1 GiB machine. Because of dropping prices, the site current runs on a 4 GiB machine.

I use a setup where scripts are executed in a small virtual machine. For security reasons this machine has no network and only a minimal filesystem. Scripts are executed by a daemon (written in Golang) and results (with statistics) are reported to a PostgreSQL database. All results are stored and used to provide averages for the performance overview.

All binaries are compiled using the same settings; they are stripped and then compiled with upx to keep their filesize down.

I use the following php.ini settings for the scripts:

; ini_set should be in here too if you're concerned about security
disable_functions = pcntl_fork,phpinfo
max_execution_time = 3
memory_limit = 64M
enable_dl = Off

; for consistency of older versions
allow_call_time_pass_reference = Off
html_errors = Off

; show all errors by default, if we'd lower this in the script we'll miss some parser notices
error_reporting = -1
display_errors = On
display_startup_errors = On
log_errors = Off
report_memleaks = On

date.timezone = Europe/Amsterdam

When I started in 2012, this site was nothing more than a small bash script that looped through all installed PHP binaries and stored the output in /out/. For fun; here is the source-code of the script that I started with:

ulimit -f 64 -m 64 -t 2 -u 128

[[ ! -d /out/$1/ ]] && mkdir /out/$1/ || chmod u+w /out/$1/

for bin in /bin/php-*
echo $bin - $1
nice -n 15 sudo -u nobody $bin -c /etc/ -q "/in/$1" &>/out/$1/${bin##*-} & PID=$!
( sleep 3.1; kill -9 $PID 2>/dev/null ) &
wait $PID

[[ $ex -eq 0 && -f $sf ]] && rm $sf
[[ $ex -ne 0 ]] && echo -n $ex > $sf

chmod u-w /out/$1/

Because of the amount of files this generated (which did not play well with the average filesystem block-size) I replaced this with a SQLite based database with an inotify-based script that picked up changes in /out/ and imported them into the database.

For simplified connectivity to the database I replaced this with a C-based binary in 2013; and that was replaced by a Go-based program that's still in use today.


As for the website you are currently looking at, this was originally based on the TooBasic framework I build specifically for 3v4l.org. As the number of features increased I eventually migrated to another framework (which is not open-source, but it's called the Basic Framework) with an actual Model and Template-parser.

For performance reasons I strip the HTML, CSS and javascripts; but there have been a few invisible upgrades here as well. Originally I used MooTools but that has been replaced by Vanilla JS. If you'd like, you can find a human-readable version here. My work pays off, as can be seen when analyzing the performance of this site.

The layout has taken a few cues from Bootstrap but given the verboseness of both its HTML and CSS I don't actually use that. The initial layout allowed for only the input and output to be visible, which was why I did an update where I added the tabs allowing for various helpers to be included. You're currently looking at the third layout iteration, which was done specifically to allow for some global contextual links to be visible and allow for various other features.

Consistent dates and times

To get consistent output I try to eliminate as much environmental changes as possible. The chroot is rarely changed, and some methods are overloaded to provide consistent results. For example, php_uname will always return the same output.

This is to keep the number of unique results down, and make the bughunt a more useful tool. This fixation is also done on all date/time methods. A run will start at the same time for all versions, even though the actual walltime progresses.

For example - this simple echo date(); script was submitted in 2012. Since it was created, I have executed it on new versions as well - which all output 2012 even though most of the PHP versions weren't even released at at that time!

The date/time fixation uses the last submit time as reference. This means a quick-preview can sometimes show "outdated" timestamps as you are actually looking at output from a run with the previous submit-time.