• Stars
    star
    793
  • Rank 57,419 (Top 2 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 9 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A compiler. For PHP

A compiler for PHP

CircleCI

Ok, so this used to be a dead project. It required calling out to all sorts of hackery to generate PHP extensions, or PHP itself.

Now, thanks to FFI landing in PHP 7.4, the potential for all sorts of crazy is HUGE.

So here we go :)

Installation

Install PHP 7.4, being sure to enable the FFI extension, OpenSSL extension, mbstring extension, and zlib extension (--with-ffi --with-openssl --enable-mbstring --with-zlib).

Also, you need to install the system dependency llvm-4.0. On Ubuntu:

me@local:~$ sudo apt-get install llvm-4.0-dev clang-4.0

Then run composer install.

Using docker

This project comes with one working and one non-working (yet) Dockerfile. The makefile uses a reasonably old version of Ubuntu (16.04), and once FFIMe is fixed for newer versions of glibc, it will switch to use 18.04 (or newer).

To build, use make:

me@local:~$ make build

This will take a while (upwards of 10 minutes likely). It will install an Ubuntu container with a custom compile of PHP-7.4 and everything you need to get up and running. It will also composer install all dependencies as well as run the pre-processor. Once it's done, you can run tests:

me@local:~$ make test

This will execute all unit tests inside the container.

To run your own code or play with the compiler, you can open a shell using make shell:

me@local:~$ make shell
root@662c59ae4527:/compiler# php bin/jit.php -r 'echo "Hello World\n";'
Hello World

Running Code

There are three main ways of using this compiler:

VM - Virtual Machine

This compiler mode implements its own PHP Virtual Machine, just like PHP does. This is effectively a giant switch statement in a loop.

No, seriously. It's literally a giant switch statement...

Practically, it's a REALLY slow way to run your PHP code. Well, it's slow because it's in PHP, and PHP is already running on top of a VM written in C.

But what if we could change that...

JIT - Just In Time

This compiler mode takes PHP code and generates machine code out of it. Then, instead of letting the code run in the VM above, it just calls to the machine code.

It's WAY faster to run (faster than PHP 7.4, when you don't account for compile time).

But it also takes a long time to compile (compiling is SLOW, because it's being compiled from PHP).

Every time you run it, it compiles again.

That brings us to our final mode:

Compile - Ahead Of Time Compilation

This compiler mode actually generates native machine code, and outputs it to an executable.

This means, that you can take PHP code, and generate a standalone binary. One that's implemented without a VM. That means it's (in theory at least) as fast as native C.

Well, that's not true. But it's pretty dang fast.

Okay, Enough, How can I try?

There are four CLI entrypoints, and all 4 behave (somewhat) like the PHP cli:

  • php bin/vm.php - Run code in a VM
  • php bin/jit.php - Compile all code, and then run it
  • php bin/compile.php - Compile all code, and output a .o file.
  • php bin/print.php - Compile and output CFG and the generated OpCodes (useful for debugging)

Executing Code

Specifying code from STDIN (this works for all 4 entrypoints):

me@local:~$ echo '<?php echo "Hello World\n";' | php bin/vm.php
Hello World

You can also specify on the CLI via -r argument:

me@local:~$ php bin/jit.php -r 'echo "Hello World\n";'
Hello World

And you can specify a file:

me@local:~$ echo '<?php echo "Hello World\n";' > test.php
me@local:~$ php bin/vm.php test.php

When compiling using bin/compile.php, you can also specify an "output file" with -o (this defaults to the input file, with .php removed). This will generate an executable binary on your system, ready to execute

me@local:~$ echo '<?php echo "Hello World\n";' > test.php
me@local:~$ php bin/compile.php -o other test.php
me@local:~$ ./other
Hello World

Or, using the default:

me@local:~$ echo '<?php echo "Hello World\n";' > test.php
me@local:~$ php bin/compile.php test.php
me@local:~$ ./test
Hello World

Linting Code

If you pass the -l parameter, it will not execute the code, but instead just perform the compilation. This will allow you to test to see if the code even will compile (hint: most currently will not).

Debugging

Sometimes, you want to see what's going on. If you do, try the bin/print.php entrypoint. It will output two types of information. The first is the Control Flow Graph, and the second is the compiled opcodes.

me@local:~$ php bin/print.php -r 'echo "Hello World\n";'

Control Flow Graph:

Block#1
    Terminal_Echo
        expr: LITERAL<inferred:string>('Hello World
        ')
    Terminal_Return


OpCodes:

block_0:
  TYPE_ECHO(0, null, null)
  TYPE_RETURN_VOID(null, null, null)

Future Work

Right now, this only supports an EXTREMELY limited subset of PHP. There is no support for dynamic anything. Arrays aren't supported. Neither Object properties nor methods are supported. And the only builtin functions that are supported are var_dump and strlen.

But it's a start...

Debugging

Since this is bleeding edge, debuggability is key. To that vein, both bin/jit.php and bin/compile.php accept a -y flag which will output a pair of debugging files (they default to the prefix of the name of the script, but you can specify another prefix following the flag).

me@local:~$ echo '<?php echo "Hello World\n";' > demo.php
me@local:~$ php bin/compile.php -y demo.php
# Produces: 
#   demo - executable of the code
#   demo.bc - LLVM intermediary bytecode associated with the compiled code
#   demo.s - assembly generated by the compiled code

Checkout the examples folder.

Performance

So, is this thing any fast? Well, let's look at the internal benchmarks. You can run them yourself with make bench, and it'll give you the following output (running 5 iterations of each test, and averaging the time).

Check out the results in the Benchmarks folder.

This is after the port to using LLVM under the hood. So the port to LLVM appears to have been well worth it, even just from a performance standpoint.

To run the benchmarks yourself, you need to pass a series of ENV vars for each PHP version you want to test. For example, the above chart is generated with::

Without opcache doing optimizations, the bin/jit.php is actually able to get close to native PHP with ack(3,9) and mandelbrot (without opcache) for 7.3 and 7.4. It's even able to hang with PHP 8's experimental JIT compiler for ack(3,9). For ack(3,10) it's able to be the fastest execution method.

Most other tests are actually WAY slower with the bin/jit.php compiler. That's because the test itself is slower than the baseline time to parse and compile a file (about 0.2 seconds right now).

And note that this is running the compiler on top of PHP. At some point, the goal is to get the compiler to compile itself, hopefully cutting the time to compile down by at least a few hundred percent.

Simply look at the difference between everything and the "compiled time" column (which is the result of the AOT compiler generating a binary). This shows the potential in this compilation approach. If we can solve the overhead of parsing/compiling in PHP for the bin/jit.php examples, then man could this fly...

So yeah, there's definitely potential here... evil grin

More Repositories

1

password_compat

Compatibility with the password_* functions that ship with PHP 5.5
PHP
2,151
star
2

RandomLib

A library for generating random numbers and strings
PHP
840
star
3

PHPPHP

A PHP VM implementation in PHP
PHP
811
star
4

PhpGenerics

Here be dragons
PHP
530
star
5

filterus

A simple filtering library for PHP
PHP
454
star
6

PHP-PasswordLib

A library for generating and validating passwords
PHP
373
star
7

monad-php

A simple Monad library for PHP
PHP
296
star
8

php-cfg

A Control Flow Graph implementation in PHP
PHP
243
star
9

Tuli

A static analysis engine
PHP
168
star
10

phpvm

A PHP version manager for CLI PHP
PHP
150
star
11

PHP-Yacc

A PHP port of kmyacc
PHP
149
star
12

PHP-CryptLib

A Cryptography Library for PHP
PHP
144
star
13

FFIMe

A FFI Wrapper library and header parser!
PHP
136
star
14

SecurityLib

SecurityLib
PHP
126
star
15

Stauros

A fast XSS sanitization library for PHP
PHP
118
star
16

php-security-scanner

A static security scanner for PHP
PHP
97
star
17

Tari-PHP

A middleware proposal for PHP
PHP
78
star
18

password-policy

A password policy enforcer for PHP and JavaScript
PHP
77
star
19

php-ast-visualizer

An AST visualizer, for PHP
PHP
75
star
20

prerano

A new language for PHP
PHP
65
star
21

php-preprocessor

A PreProcessing library for PHP
PHP
49
star
22

ErrorExceptions

A library for converting core PHP errors into ErrorExceptions
PHP
43
star
23

PHP-BrainFuck

A brainfuck interpreter for PHP
PHP
42
star
24

php-c-parser

A C parser built in and for PHP (yes, it's a bad idea)...
PHP
40
star
25

random_compat

Compatibility library for proposed simplified random number generator
PHP
40
star
26

php-llvm

A "lightweight" wrapper around LLVM-C in native PHP
PHP
39
star
27

php-types

A PHP Type reconstruction library
PHP
36
star
28

php-compiler-toolkit

A compiler toolkit. For PHP (yes, I am creative at naming things)...
PHP
30
star
29

resume

Anthony Ferrara's Resume (CV)
30
star
30

php-math-parser

A Shunting-Yard Based Math Engine For PHP
PHP
29
star
31

Protocol-Lib

A library for runtime checking of protocols
PHP
27
star
32

php-optimizer

A CFG Optimizer for PHP
PHP
24
star
33

RequirePHP

A RequireJS clone in PHP - As a dependency Loader
PHP
22
star
34

ballandchain

A PHP implementation of BallAndChain
PHP
20
star
35

libgccffi

libgccffi interface for PHP, based on 7.4's FFI and FFIMe
PHP
19
star
36

MixinPHP

A test mixin library for super-happy-crazy-time
PHP
18
star
37

php-ndata

NData PECL extension for dealing with native data types
C
15
star
38

cpu_assembler

An assembler for my custom CPU
PHP
13
star
39

TrueObjectStore

What SPLObjectStorage Should Have Been
PHP
13
star
40

haas

Hugs, As A Service
HTML
13
star
41

programming-with-anthony

Scripts for the Programming With Anthony series on YouTube
11
star
42

php-object-symbolresolver

A linux object file (ELF) parser
PHP
10
star
43

password-bad-web-app

A bad web app, to demonstrate password hashing issues DO NOT USE!!!
PHP
10
star
44

quality-checker

PHP Quality Checker
PHP
10
star
45

blog.ircmaxell.com

blog.ircmaxell.com future site
Less
9
star
46

Primitives

A collection of primitive types for PHP
PHP
6
star
47

blog-ideas

6
star
48

cryptography-presentation-tnphp

Slides for the Cryptography Presentation done at TrueNorthPHP on Nov 2, 2012
JavaScript
6
star
49

Ircmaxell.com

PHP
5
star
50

ZPP

A PHP implementation of Zend-Parse-Parameters
PHP
5
star
51

XssBadWebApp

A Intentionally Vulnerable Bad Web Application With XSS Vulnerabilities - *DO NOT USE!!!*
PHP
5
star
52

CodeReviewSecurityRepo

Code Review for Security Repository Of Code To Review
PHP
5
star
53

password-advice

The website behind password-advice.com
4
star
54

SetLib

A Badly Named Playground
PHP
4
star
55

DontBeStupid-Presentation

A repo of the Don't Be Stupid, Grasp Solid presentation at NYPHP on 5-22-12
JavaScript
3
star
56

hashguesser

Hash guesser
JavaScript
3
star
57

jQuery.OOP

A pseudo-port of MooTools OOP to jQuery
2
star
58

Intervalometer

An intervalometer
Arduino
2
star
59

BehaviorTest

A Proof-Of-Concept behavioral testing app
PHP
2
star
60

password-hashing-mini-presentation

Password-Hashing-Mini-Presentation
JavaScript
2
star
61

PHPTest

A Unit Testing Framework for PHP
PHP
2
star
62

ITL

Some silly test programming language thingy
Ruby
1
star
63

solid-presentation-tnphp

Slides for the SOLID OO Design presentation at True North PHP on Nov 3, 2012
JavaScript
1
star
64

PreProcessor

A trivial attempt at a PHP preprocessor (DO NOT USE!!! Experimental ONLY!!!)
PHP
1
star
65

jsGoodies

Just some JS snipits I've found useful
JavaScript
1
star
66

8bit-cpu-v2

Ruby
1
star