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:

user viewsnew scriptsbot views11 475112 563973 469103 2931 507 1853 205 618203 2047 624 3212 873 489314 82910 248 7887 732 939441 23918 984 1878 258 457558 53718 213 3189 499 271579 85127 431 6808 442 383709 50418 783 3136 439 193829 78715 450 8427 678 562948 82419 969 23817 681 5301 075 69513 791 02211 778 0812012201320142015201620172018201920202021202240 M32 M24 M16 M8 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 available 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.

10.6 ms | 409 KiB | 6 Q