I present to you my open-source project -
JPHP . This is an alternative PHP implementation for JavaVM with JIT support. I started the project alone in October 2013 and in 4 months I implemented the php compiler into JVM bytecode. The language is supported at the level of PHP 5.3, the capabilities of PHP 5.4 and 5.5 are partially supported. By its ideology, the project resembles JRuby and Jython.
I prepared a small presentation that tells about the project and does not take much of your time:
There is a desire to talk about the project as much as possible, but I'm afraid everything cannot be put in one article. I hope it turned out not too messy.
')
Objective of the project
JPHP is not a replacement for Zend PHP or Facebook HHVM, because the plans did not include the implementation of all the zend php runtime libraries, such as CURL, PRCE, PDO, etc. The main reasons for starting the project were as follows:
- It was an experiment.
- Using java libraries in PHP
- Increase productivity with JIT + JVM
- Replace the inconsistent and ugly Zend PHP runtime with something more decent
- Allow to write in PHP not only for Web
- Implement unicode and multithreading support
Technical details
The entire language is written from scratch in Java using the
ASM library to generate bytecode, it is used by all popular jvm languages, for example Groovy. Gradle was chosen as the build system.
To my surprise, the Java technology stack provides very convenient conditions for writing the jvm language. You do not need to write your VM with JIT, the garbage collector and the class system are already implemented, the head does not hurt about cross-platform, and the jvm bytecode itself is very easy to understand. However, there were many pitfalls, most of all because of the magic of the php language itself.
JIT allowed to increase performance by 1-10 times, depending on tests, on average by 1.5-3 times on real code. I also implemented an optimizer that helps make the code even faster.
Compatible with PHP?
It is necessary to distinguish between the language and the library to it, so compatibility at the level of languages and libraries are different things. Already at the beginning of development, I realized that it was impossible for one person to write all the PHP libraries with proper compatibility. I decided to concentrate only on the language, although I implemented basic things such as spl autoloading, Reflection, ob_ * functions,
<? ... ?>
<? ... ?>
and much more.
JPHP passes about 300+ unit tests from the original Zend PHP, including testing OOP, functions, operators, etc. There are also their tests. It helps to ensure that the language works properly. Next, I will list the features that are supported:
- PHP 5.3 language level (except goto)
- Typehinting for callable (5.4)
- Short syntax for arrays (5.4)
Class::{expr}(), (new Foo)->bar()
(5.4)- try ... finally (5.5)
- Array and string literal dereferencing (5.5)
- Function array dereferencing foo () [0] (5.4)
- System constant
class
to get class name (5.5)
On the way implementation of traits from php 5.4.
JIT and performance
What code do you think can affect performance? If you are familiar with Facebook HHVM, then I think you know what. The main problem with PHP performance is the global space for variables, the magic of variables, just magic, when you can access a variable by name at run time. For this reason, JPHP can compile the same code in different ways. Where there is no magic with variables, the compiler converts the variables into indices and, at run time, will immediately refer to them at the index. Let me give you some examples:
$var = 'foobar'; $x = 'var'; ${$x} = 'chanched';
$global_var = 100500; include 'file.php';
function foobar() { $x = 100500; $var = get_defined_vars();
Therefore, when it is assumed that variables are accessed by name at run time, JPHP saves a table of variable names, and when it does not, it does not save and refers to variables immediately by indices.
The magic of variables slows down your JPHP code about 2 times. In Zend, PHP code works the same way under any conditions.
Optimizer
The JPHP optimizer does quite a lot, here’s a small list of its features.
1. Constant expressions $x = (20 + 30) . 'foobar';
2. Static constantsThere are constants that JPHP knows at compile time, and there are dynamic constants declared via
define
. Static is the system constant
__FILE__, __DIR__, __CLASS__
, constants java extensions, constants that are declared within the same file through
const
. All of them can be replaced at compile time:
include __DIR__ . '/core.php';
3. Immutable functions and methodsThese are functions and methods, the result of which is unchanged, or which can be counted once during compilation, they do not affect the global environment and therefore it is safe.
for ($i = 0; $i < 100500; $i++){ $x = cos(1) + 3;
In this example, the cos () function is system and immutable, and the parameter passed to the function is constant, so the result cos (1) will never change.
The immutable also includes functions / methods declared by the programmer, which have no parameters and return a constant expression. Calling such a function in JPHP is comparable in speed with reference to a constant, for example:
function getVersion(){ return '1.0'; } $x = getVersion();
4. Impossible conditionsIf in if or else if you have a constant expression that is false, the compiler will drop the extra condition altogether. So far, only if, else and elseif are supported. For example, this can be very useful:
if (TRACE_ENABLED){
Caching classes, functions, bytecode?
The JPHP work model allows you to store compiled code in memory, i.e. classes and functions. Once compiled and loaded class will be used repeatedly. In the future, this will allow writing http servers, performance frameworks not inferior to Phalcon on PHP itself. Data is easy to store between requests, and the http server can be written on php itself, which I will discuss below.
In JPHP there is a special class
Environment that allows you to create isolated environments for executing scripts, this is somewhat similar to a sandbox from the runkit extension. Each such environment has its own set of classes, functions and global variables.
- Environment - an isolated environment for running scripts
- Similar sandbox from runkit
- Need for flexible multithreading implementation
- Allows you to organize HOT reload workflow
- Environments can interact with each other
I will use this class in the example below to organize a multi-threaded http server.
How to try? Build a test project?
To do this, you need to install Java (1.6+) and the Gradle build system (1.4+). Download the source from the git repository. JetBrains IDEA allows you to import a project from build.gradle. At this stage there is a test project in the
jphp-example-project
folder. This project is assembled into an executable jar file, inside which the php sources are located. When starting the jar, the
bootstrap.php
file is executed. You can build a jar with the command:
gradle jar
Or immediately run through:
gradle exampleStart
The project is built in the
build/libs/
folder in the jar file.
GUI? Programs?
I also wrote a wrapper extension for Swing (Java library for GUI). It allows you to create cross-platform GUI programs. For those who are familiar with Swing, I want to hope - I pretty much simplified api and layouts. A small window on the GUI:
use php\lang\System; use php\swing\SwingUtilities; use php\swing\UIForm; use php\swing\UIDialog; use php\swing\UIButton; SwingUtilities::invokeLater(function(){ $form = new UIForm(); $form->size = [500, 500]; $form->moveToCenter(); $form->visible = true; $p = new UIButton(); $p->size = [300, 40]; $p->align = 'top'; $p->h = 30; $p->text = ''; $p->on('click', function(){ UIDialog::message('', ''); }); $form->add($p); $form->on('windowClosing', function(){ System::halt(0); }); });
Multithreaded HTTP server?
On JPHP you can quite write a multi-threaded http server. For this, I imported the
Socket
and
ServerSokect
, as well as the
Thread
thread classes.
use php\concurrent\ExecutorService; use php\io\IOException; use php\lang\Environment; use php\net\ServerSocket; $server = new ServerSocket(); $server->bind('localhost', 8080); $service = ExecutorService::newFixedThreadPool(5);
Such a server quickly gives content, I tested through the ab utility and the results are impressive. On my machine (Java 7, i3, Windows 7) such a server is able to process 4000-5000 requests per second (
ab -n50000 -c100 localhost
ab -n50000 -c100 localhost
) and do not fall.
What's next?
I plan to develop JPHP myself, bring it to release, implement all language features to PHP 5.5. Maybe I'll try to implement compatibility with Android (like Roboto for JRuby). I implement normal extensions based on namespaces and OOP, in part I have already implemented something - streams, sockets, gui, json.
See the rest in the presentation. Thank you for attention.