PHP Runtimes

Content:

  1. Introduction
  2. Examples
  3. Standard
  4. Hip Hop
  5. Java via Quercus
  6. .NET via PeachPie
  7. Conclusion

Introduction:

Almost everybody use standard PHP runtime. So many assume there is only one PHP runtime. But actually there are alternative runtimes.

Here we will look at some of the alternative runtimes and how to use them and how compatible they are.

Examples:

hello.php:

<html>
<head>
<title>Hello world</title>
</head>
<body>
<?php
echo "<p>PHP says: Hello world!</p>\r\n";
?>
</body>
</html>

basic.php:

<html>
<head>
<title>Basic stuff</title>
</head>
<body>
<?php
$rep = 3;
$s = 'ABC';
$res = '';
for($i = 0; $i < $rep; $i++) {
    if(strlen($res) > 0) {
        $res .= ' ';
    }
    $res .= $s;
}
echo "<p>$rep * '$s' = '$res'</p>\r\n";
?>
</body>
</html>

func.php:

<html>
<head>
<title>Functions</title>
</head>
<body>
<?php
function fac($n) {
    if($n < 2) {
        return 1;
    } else {
        return $n * fac($n - 1);
    }
}

$fac5 = fac(5);
echo "<p>5! = $fac5\r\n";
?>
</body>
</html>

form.php:

<html>
<head>
<title>Form</title>
</head>
<body>
<form method='POST'>
F1 : <input name='f1' type='TEXT'>
<br>
F2 : <input name='f2' type='TEXT'>
<br>
<input type='SUBMIT' value='Submit'>
</form>
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $f1 = $_POST['f1'];
    $f2 = $_POST['f2'];
    echo "<p>$f1 $f2</p>\r\n";
}
?>
</body>
</html>

oop.php:

<html>
<head>
<title>OOP</title>
</head>
<body>
<?php
abstract class P {
    public abstract function getId();
    public function printId() {
        $id = $this->getId();
        echo "<p>Instance says: I am $id</p>\r\n";
    }
}

class C1 extends P {
    public function getId() {
        return 'C1';
    }
}

class C2 extends P {
    public function getId() {
        return 'C2';
    }
}

$o1 = new C1();
$o1->printId();

$o2 = new C2();
$o2->printId();
?>
</body>
</html>

mysqli.php:

<html>
<head>
<title>mysqli extension</title>
</head>
<body>
<?php
$con = new mysqli('localhost', 'root', '', 'Test');
if(mysqli_connect_errno()) die(mysqli_connect_error());
$stmt = $con->prepare('SELECT f1,f2 FROM T1 WHERE f1 BETWEEN ? AND ?') or die(mysqli_error($con));
$start = 2;
$end = 4;
$stmt->bind_param('ii', $start, $end);
$stmt->execute() or die(mysqli_error($con));
$rs = $stmt->get_result();
echo "<table border='1'>\r\n";
echo "<tr>\r\n";
echo "<th>F1</th>\r\n";
echo "<th>F2</th>\r\n";
echo "</tr>\r\n";
while($row = $rs->fetch_array(MYSQLI_ASSOC)) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "<tr>\r\n";
    echo "<td>$f1</td>\r\n";
    echo "<td>$f2</td>\r\n";
    echo "</tr>\r\n";
}
echo "</table>\r\n";
$stmt->close();
$con->close();
?>
</body>
</html>

For more about mysqli extension see PHP Database Access.

pdo.php:

<html>
<head>
<title>PDO extension (for MySQL)</title>
</head>
<body>
<?php
$con = new PDO('mysql:host=localhost;dbname=Test', 'root', '');
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $con->prepare('SELECT f1,f2 FROM T1 WHERE f1 BETWEEN :start AND :end');
$start = 2;
$end = 4;
$stmt->execute(array(':start' => $start, ':end' => $end));
echo "<table border='1'>\r\n";
echo "<tr>\r\n";
echo "<th>F1</th>\r\n";
echo "<th>F2</th>\r\n";
echo "</tr>\r\n";
while($row = $stmt->fetch()) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "<tr>\r\n";
    echo "<td>$f1</td>\r\n";
    echo "<td>$f2</td>\r\n";
    echo "</tr>\r\n";
}
echo "</table>\r\n";
?>
</body>
</html>

For more about PDO extension see PHP Database Access.

json.php:

<body>
<?php
$a = array('f1' => 99, 'f2' => 'XXX');
$json = json_encode($a);
echo "<p>JSON = $json<p>\r\n";
$o = json_decode($json);
$f1 = $o->f1;
$f2 = $o->f2;
echo "<p>$f1 $f2</p>\r\n"; 
?>
</body>
</html>

For more about JSON usage in PHP see JSON Processing.

cpu.php:

<html>
<head>
<title>JSON</title>
</head>
<html>
<head>
<title>CPU usage</title>
</head>
<body>
<?php
define('REP', 10);
define('M', 10);
define('N', 1000000);

function testint() {
    $t1 = microtime(true);
    for($i = 0; $i < M; $i++) {
        $sum = $i;
        for($j = 0; $j < N; $j++) {
            $sum = (int)((($sum + 1) * 2 + 1) / 2);
        }
        if($sum != ($i + N)) {
            die('Error');
        }
    }
    $t2 = microtime(true);
    echo sprintf("<p>%.3f seconds\n", $t2 - $t1);
}

for($i = 0; $i < REP; $i++) {
    testint();
}
?>
</body>
</html>

All the code is very simple, but if this code works then a lot of PHP code will work.

Standard:

What it is and how it works:

Standard PHP is what you either get from the operating system distribution or download and install from PHP.NET.

When people talk about PHP then this is what they mean.

It is sometimes called Zend PHP because it since version 4.0 has been using the socalled Zend Engine.

Current versions are 7.x and 8.x. Versions 5.x (and obviously earlier) are obsolete. Note that there were no version 6.x.

It is bascially a PHP interpreter written in C.

It can be used in many diffent ways.

Standalone:

A not widely known fact is that PHP can be run standalone.

Simply:

php -S localhost:8000

Note that standalone server is not intended for production usage - it is only intended for development usage.

With Apache httpd:

PHP integrates nicely with Apache. Running PHP via Apache is undoubetly the most common way to run PHP.

PHP comes with an Apache module, so integration basically mean modifying Apache config to load the PHP Apache module and associate it with the the .php file extension.

With other web servers:

PHP also comes with integration modules for Microsoft IIS, nginx and LightHTTPD. Plus PHP supports CGI and FastCGI so any web server supporting those can integrate with PHP.

Results:

Test Result
Hello world OK
Basic stuff OK
Functions OK
Form OK
OOP OK
mysqli extension OK
PDO extension (for MySQL) OK
JSON OK
CPU usage 5.6 (32 bit) : 1.24s
7.2 (32 bit) : 1.19s
7.4 (32 bit) : 1.29s
7.4 (64 bit) : 1.17s

No surprise that everything works in the standard implementation.

Hip Hop:

What it is and how it works:

Hip Hip is a series of PHP implementations from FaceBook.

HPHPc (HipHop for PHP) from 2010 was a transpiler from PHP to C++.

In 2013 it was replaced by HHVM (HipHop VM) a VM that JIT compiles and execute PHP code.

Version 1 to 3 supported both PHP and Hack. Version 4+ only supports Hack.

(Hack is an extension of PHP allowing for more type safe code)

Latest HHVM (4.x) can usually be installed on Linux using normal package manager.

Getting HHVM 3.x installed on Linux require installing dependencies, download source code and building. Definitely doable but it takes some time.

(to run PHP code one needs HHVM 3.x)

To run HHVM use:

hhvm -m server -p 8000

Results:

Test Result
Hello world OK
Basic stuff OK
Functions OK
Form OK
OOP OK
mysqli extension Fail
PDO extension (for MySQL) OK
JSON OK
CPU usage 1.93 s

Hack on HHVM 4.x:

Hack is really just PHP 7 as FaceBook thougth it should have been, so migrating from PHP to hack is not difficult.

hello.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>Hello world</title>
</head>
<body>
EOS;
echo "<p>PHP says: Hello world!</p>\r\n";
echo <<< EOS
</body>
</html>
EOS;
}

basic.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>Basic stuff</title>
</head>
<body>
EOS;
$rep = 3;
$s = 'ABC';
$res = '';
for($i = 0; $i < $rep; $i++) {
    if(strlen($res) > 0) {
        $res .= ' ';
    }
    $res .= $s;
}
echo "<p>$rep * '$s' = '$res'</p>\r\n";
echo <<< EOS
</body>
</html>
EOS;
}

func.hack:

function fac($n) {
    if($n < 2) {
        return 1;
    } else {
        return $n * fac($n - 1);
    }
}

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>Functions</title>
</head>
<body>
EOS;
$fac5 = fac(5);
echo "<p>5! = $fac5\r\n";
echo <<< EOS
</body>
</html>
EOS;
}

form.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>Form</title>
</head>
<body>
<form method='POST'>
F1 : <input name='f1' type='TEXT'>
<br>
F2 : <input name='f2' type='TEXT'>
<br>
<input type='SUBMIT' value='Submit'>
</form>
EOS;
if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $f1 = $_POST['f1'];
    $f2 = $_POST['f2'];
    echo "<p>$f1 $f2</p>\r\n";
}
echo <<< EOS
</body>
</html>
EOS;
}

oop.hack:

abstract class P {
    public abstract function getId();
    public function printId() {
        $id = $this->getId();
        echo "<p>Instance says: I am $id</p>\r\n";
    }
}

class C1 extends P {
    public function getId() {
        return 'C1';
    }
}

class C2 extends P {
    public function getId() {
        return 'C2';
    }
}

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>OOP</title>
</head>
<body>
EOS;
$o1 = new C1();
$o1->printId();

$o2 = new C2();
$o2->printId();
echo <<< EOS
</body>
</html>
EOS;
}

mysqli.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>mysqli extension</title>
</head>
<body>
EOS;
$con = new mysqli('192.168.0.140', 'root', 'hemmeligt', 'Test');
if(mysqli_connect_errno()) die(mysqli_connect_error());
$stmt = $con->prepare('SELECT f1,f2 FROM T1 WHERE f1 BETWEEN ? AND ?');
if(!$stmt) die(mysqli_error($con));
$start = 2;
$end = 4;
$stmt->bind_param('ii', $start, $end);
$sts = $stmt->execute();
if(!$sts) die(mysqli_error($con));
$rs = $stmt->get_result();
echo "<table border='1'>\r\n";
echo "<tr>\r\n";
echo "<th>F1</th>\r\n";
echo "<th>F2</th>\r\n";
echo "</tr>\r\n";
while($row = $rs->fetch_array(MYSQLI_ASSOC)) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "<tr>\r\n";
    echo "<td>$f1</td>\r\n";
    echo "<td>$f2</td>\r\n";
    echo "</tr>\r\n";
}
echo "</table>\r\n";
$stmt->close();
$con->close();
echo <<< EOS
</body>
</html>
EOS;
}

pdo.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>PDO extension (for MySQL)</title>
</head>
<body>
EOS;
$con = new PDO('mysql:host=192.168.0.140;dbname=Test', 'root', 'hemmeligt');
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $con->prepare('SELECT f1,f2 FROM T1 WHERE f1 BETWEEN :start AND :end');
$start = 2;
$end = 4;
$stmt->execute(dict[':start' => $start, ':end' => $end]);
echo "<table border='1'>\r\n";
echo "<tr>\r\n";
echo "<th>F1</th>\r\n";
echo "<th>F2</th>\r\n";
echo "</tr>\r\n";
while($row = $stmt->fetch()) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "<tr>\r\n";
    echo "<td>$f1</td>\r\n";
    echo "<td>$f2</td>\r\n";
    echo "</tr>\r\n";
}
echo "</table>\r\n";
echo <<< EOS
</body>
</html>
EOS;
}

json.hack:

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>JSON</title>
</head>
<body>
EOS;
$a = dict['f1' => 99, 'f2' => 'XXX'];
$json = json_encode($a);
echo "<p>JSON = $json<p>\r\n";
$o = json_decode($json);
$f1 = $o->f1;
$f2 = $o->f2;
echo "<p>$f1 $f2</p>\r\n"; 
echo <<< EOS
</body>
</html>
EOS;
}

cpu.hack:

const REP = 10;
const M = 10;
const N = 1000000;

function testint() {
    $t1 = microtime(true);
    for($i = 0; $i < M; $i++) {
        $sum = $i;
        for($j = 0; $j < N; $j++) {
            $sum = (int)((($sum + 1) * 2 + 1) / 2);
        }
        if($sum != ($i + N)) {
            die('Error');
        }
    }
    $t2 = microtime(true);
    echo sprintf("<p>%.3f seconds\n", $t2 - $t1);
}

<<__EntryPoint>>
function main(): void {
echo <<< EOS
<html>
<head>
<title>CPU usage</title>
</head>
<body>
EOS;
for($i = 0; $i < REP; $i++) {
    testint();
}
echo <<< EOS
</body>
</html>
EOS;
}

Results:

Test Result
Hello world OK
Basic stuff OK
Functions OK
Form OK
OOP OK
mysqli extension Fail
PDO extension (for MySQL) OK
JSON OK
CPU usage 0.08 s

It is obvious that HHVM JIT compiler works very well.

Java via Quercus:

What it is and how it works:

Quercus is a PHP implementation for Java servlet engine from Caucho. It has existed since 2007.

There is an open source version (GPL license) and a commercial version.

The open source version interprets PHP. The commercial version compiles PHP.

It seems like the development of quercus is somewhat slowed down.

To use Quercus:

web.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
  <servlet>
    <servlet-name>Quercus Servlet</servlet-name>
    <servlet-class>com.caucho.quercus.servlet.QuercusServlet</servlet-class>
    <init-param>
      <param-name>license-directory</param-name>
      <param-value>WEB-INF/licenses</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>Quercus Servlet</servlet-name>
    <url-pattern>*.php</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>

For more info about how to use Quercus see Java EE tricks.

Interoperability:

PHP running via Quercus can use Java code, which may be useful.

Test.java:

package demo;

import java.util.List;

public class Test {
    public int add(int a, int b) {
        return a + b;
    }
    public String dup(String s) {
        return s + s;
    }
    public long sum(List lst) {
        long res = 0;
        for(long v : (List<Long>)lst) res += v;
        return res;
    }
    public String conc(List lst) {
        String res = "";
        for(String v : (List<String>)lst) res += v;
        return res;
    }
}

call.php:

<html>
<head>
<title>Calling Java</title>
</head>
<body>
<?php
import demo.Test;

$o = new Test();
$a = 123;
$b = 456;
$c = $o->add($a, $b);
echo "<p>$a + $b = $c</p>\r\n";
$s = "ABC";
$s2 = $o->dup($s);
echo "<p>$s + $s = $s2</p>\r\n";
$a1 = array(1, 2, 3);
$sum1 = $o->sum($a1);
echo "<p>array sum = $sum1</p>\r\n";
$a2 = array('A', 'BB', 'CCC');
$conc2 = $o->conc($a2);
echo "<p>array conc = $conc2</p>\r\n";
?>
</body>
</html>

just add class file to WEB-INF/classes/demo or jar file to WEB-INF/lib.

Results:

Test Result
Hello world OK
Basic stuff OK
Functions OK
Form OK
OOP OK
mysqli extension OK
PDO extension (for MySQL) OK
JSON OK
CPU usage 1.48s

.NET via PeachPie:

What it is and how it works:

PeachPie is an open source PHP compiler for .NET platform. It has existed since 2016, but is based on a similar open source projects Phalanger that goes back to 2013.

PeachPie targets .NET Core / .NET 5+ while Phalanger targeted .NET Framework 4.

To create PeachPie project:

dotnet new -i "Peachpie.Templates::*"
dotnet new web -lang PHP

HTML and PHP files are put in Website or subdirs under Website.

To build:

dotnet build

Update Server/Server.csproj if needed. For database access add:

<Project Sdk="Microsoft.NET.Sdk.Web">
  ...
  <ItemGroup>
    ...
    <PackageReference Include="Peachpie.Library.MySql" Version="1.0.6" />
    <PackageReference Include="Peachpie.Library.PDO" Version="1.0.6" />
    <PackageReference Include="Peachpie.Library.PDO.MySql" Version="1.0.6" />
    ...
  </ItemGroup>
  ...
</Project>

To run:

cd Server
dotnet run

Results:

Test Result
Hello world OK
Basic stuff OK
Functions OK
Form OK
OOP OK
mysqli extension Fail
PDO extension (for MySQL) OK
JSON OK
CPU usage 1.63s

Interoperability:

PHP running via PeachPie can use .NET code, which may be useful.

somelib.cs:

using System.Collections;

namespace Demo
{
    public interface IHandler
    {
        void DoIt(int v);
    }
    public class Test
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
        public string Dup(string s)
        {
            return s + s;
        }
        public long Sum(IList lst) 
        {
            long res = 0;
            foreach(long v in lst) res += v;
            return res;
        }
        public string Conc(IList lst) 
        {
            string res = "";
            foreach(string v in lst) res += v;
            return res;
        }
        public void Process(IHandler h, int v) 
        {
            h.DoIt(v);
        }
    }
}

call.php:

<html>
<head>
<title>Calling C#</title>
</head>
<body>
<?php
use Demo\IHandler;
use Demo\Test;

class MyHandler implements IHandler {
    public function DoIt($v) {
        echo "<p>MyHandler.DoIt: $v</p>\r\n";
    }
}

$o = new Test();
$a = 123;
$b = 456;
$c = $o->Add($a, $b);
echo "<p>$a + $b = $c</p>\r\n";
$s = "ABC";
$s2 = $o->Dup($s);
echo "<p>$s + $s = $s2</p>\r\n";
$a1 = array(1, 2, 3);
$sum1 = $o->Sum($a1);
echo "<p>array sum = $sum1</p>\r\n";
$a2 = array('A', 'BB', 'CCC');
$conc2 = $o->Conc($a2);
echo "<p>array conc = $conc2</p>\r\n";
$o->Process(new MyHandler(), 123);
?>
</body>
</html>

add a reference to DLL in Server.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">
  ...
  <ItemGroup>
    ...
    <Reference Include="SomeLib">
        <HintPath>..\..\somelib.dll</HintPath>
    </Reference>
    ...
  </ItemGroup>
  ...
</Project>

and modify somelib.csproj to build for the same .NET version as PeachPie (at the time of writing that is netstandard2.1!):

<Project Sdk="Microsoft.NET.Sdk">
  ...
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
  </PropertyGroup>
  ...
</Project>

Conclusion:

I will conclude that:

Article history:

Version Date Description
1.0 September 9th 2021 Initial version
1.1 September 17th 2021 Add Hack section and Java/C# interoperability sections

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj