Zephir: PHP On Steroids — Extensions Made Easy

Charles Martin
7 min readSep 17, 2024

--

Photo by Ben Griffiths on Unsplash

Of all the programming languages out there, PHP certainly is one of the most widely used, with well over 70% of websites being written with PHP. The introduction of PHP 7 and PHP 8 added a lot of features many people wanted and quickly turned PHP into a powerhouse (not that it wasn’t already). But what if you could make PHP even more of a powerhouse? Let’s peel this onion and learn how we can do exactly that.

Getting the Most out of PHP

PHP 8 is already blazingly fast. Despite this, it remains true that PHP is an `Interpreted` language. Like Java, PHP files have to be jit-compiled into bytecode and then executed by the PHP engine. While you can use options like OPCache to help speed this process up, you will still lose cycles to this. Fortunately, PHP makes this all seamless for us, so most people don’t have to worry about this…but what if you want to? What if you want to make PHP as fast as possible? Well, the only way to do that is to write PHP Extensions.

PHP Extensions

Most anyone who has written a PHP file will have worked with a PHP Extension. Probably the most well-known extension is the PDO extension for communicating with a database. You cannot read the DB without it. The next best-known one is probably XDebug, the extension for debugging scripts.

PHP Extensions are typically written in C or C++. They get compiled into SO or DLL files that are included or sucked into the PHP Global Namespace. Put another way, an extension, once enabled, is part of the PHP Engine. PHP sees it as if it was always there. Because these are compiled, they don’t get interrupted like normal PHP files. Because they are part of the PHP Global Namespace, you can take full advantage of the speed and memory optimizations of it being ‘one’ with PHP.

The problem with PHP Extensions is that they can be tricky to write. You have to be an expert in C or C++, along with the somewhat complex structure required for PHP to recognize a piece of code as a method in the PHP Global Namespace…or are they? What if you could write an extension using a structure similar to that of a PHP file? What if you could still use both strongly typed and loosely typed variables, while also getting the benefits of a compiled plugin? Enter Zephir.

Zephir

Zephir is to PHP Extensions as TypeScript is to Javascript. If you have ever worked with TypeScript (or CoffeeScript), you know that it gets compiled into native Javascript for actual usage. Zephir is the same way, but it gets compiled into C code, prebuilt for a PHP Extension. Zephir’s syntax is very familiar to any PHP developer, so it is very easy to dive right into building your first PHP Extension. It allows you to write code in a strongly typed manner that is directly compatible with PHP and will give you an edge you didn’t have before.

It’s worth noting that Zephir is exclusively an Object Oriented language. You don’t have concepts like the Global Namespace or Global Variables (outside the $_ ones). Everything has to be in a Namespace, and everything has to be a class or interface.

Design aside, Zephir’s greatest selling point from a language perspective is that you can write strongly typed code. From an execution side, Zephir’s greatest selling point is the speed at which your code runs. To be fair, because Zephir is compiled into a formal C extension, it’s actually the C code that is what gives you the speed gains.

Somewhat ironically, Zephir is written mostly in PHP. The Zephir Parser is written in C, but everything else is PHP.

Zephir’s Origin — Phalcon

You can’t talk about Zephir without mentioning its primary benefactor, Phalcon. Phalcon is a PHP framework that is written as a PHP Extension. It’s meant to be a toolkit-style framework that will be very familiar to most application developers. Originally, it was a literal C Extension, but Zephir was later developed as a way to open up and ease that development.

Phalcon is still in active development. It is however worth noting that due to the advances of PHP 8, Phalcon 6.x+ is written in native PHP with certain components like the Volt Engine being written in Zephir. Phalcon 5.x is still in active development and many of the Phalcon purists will continue to use 5.x even after 6.x is released.

Show Me the Code

namespace Test;
/**
* This is a sample class
*/
class Hello
{
/**
* This is a sample method
*/
public function say()
{
echo "Hello World!";
}
}

For a starter snippet of Zephir, this looks identical to PHP. Once you start getting into more advanced concepts, you’ll start to see some differences.

namespace Test;

/**
* MyTest (test/mytest.zep)
*/
class MyTest
{
public function someMethod() -> array
{
/* Variables must be declared */
var myArray;
int i = 0, length;
/* Create an array */
let myArray = ["hello", 0, 100.25, false, null];
/* Count the array into a 'int' variable */
let length = count(myArray);
/* Print value types */
while i < length {
echo typeof myArray[i], "\n";
let i++;
}
return myArray;
}
}

What Can You Build?

Whatever you want. I build all my websites using Zephir. The only downside right now is that the only Framework that is fully compatible with Zephir is Phalcon. Fortunately, it is easy to include Phalcon and you can still build an amazing website with it. Alternatively, you could build just utility classes much like PDO or YAML.

What about Composer Packages?

Yes! You can absolutely use Composer Packages with Zephir. The only time I have seen Zephir struggle with Composer Packages is for static calls. It tends to fail if you try to reference a method statically from a composer package. You have to use forward_static_call in this case.

  var mailer, transport;

let transport = forward_static_call(
[
"Symfony\\Component\\Mailer\\Transport",
"fromDsn"
],
this->{"get"}("config")->path("mailer.dsn")
);

let mailer = this->{"get"}(
"Symfony\\Component\\Mailer\\Mailer",
[
transport
]
);

return mailer;

Outside of Static Methods Calls, there is nothing wrong with referencing these packages. You can even extend them if you need to.

Not for the Faint of Heart

I love Zephir. However, while Zephir is a drop-in replacement for PHP, Zephir isn’t a drop-in replacement for developing PHP. Zephir makes building a PHP Extension extremely easy, but it comes with some gotchas that may not be worth it for you.

Fast Code vs Coding Fast

Zephir is about making your code blazing fast, while also giving you a bit more in the real of strongly typed languages. This means that you will have to spend more time coding, and chances are high you may have to deal with situations that will not “just work”.

I’m just going to go ahead and say this upfront: If you are an “absolute” fan of Laravel, in that you Live, Eat, Breathe, and S**t Laravel and Laravel is the only framework in the PHP landscape you know, then you need to stay away from both Phalcon and Zephir. Laravel has a lot of ‘magic’ it does behind the scenes that allows you to develop sites fast, but that magic is often hidden from you. It “just works”. Phalcon and Zephir are completly opposite of this, in that they force software engineers to actually be software engineers.

To this day, I have yet to meet a Laravel “fan” who enjoys Phalcon, much less Zephir. On the other hand, if you have ever done desktop or mobile application development, then Zephir (and Phalcon) is going to feel a lot more natural.

To be fair, I’m generalizing here. However, every Laraval “fan” I have talked to who also knows about Phalcon always hates Phalcon because it doesn’t do the ‘magic’ Laravel does, making it much slower to develop. Ironically, every Phalcon “fan” I have met respects Laravel, but avoids it.

Debugging & Code Coverage

XDebug is typically used to step the PHP Code. Since Zephir is ultimately compiled into C, you have to instead do everything the ‘C’ way. This means for debugging, you have to use GDB to step the code. This is much more complicated to do since you have to compile PHP in Debug Mode to be able to use GDB with it. Similarly, Code Coverage is on the C code. Fortunately, Zephir compiles it in such a way that you still get a Zephir Coverage result.

Seg Faults

One of the hardest parts of using Zephir is the Seg Faults. Zephir isn’t perfect and is still growing as a Language. There will be random errors that can be more difficult to track down. For example, if you write a child class and do not override the method correctly, Zephir will still compile, but PHP will crash upon loading the extension. A lot of effort has been put into place to prevent these types of errors, but they do occasionally crop up. Normally the parser and warning system are spot on in finding errors. I highly advise you to hit 100% code coverage to help capture these errors.

Is Zephir Right for You?

Zephir isn’t for everyone. If you want something to “just work”, you’re better off going with Laravel. If you want to squeeze every ounce of performance out of PHP as you can or just want to have a reusable extension, then Zephir is the way to go. Keep in mind that Zephir will be difficult for anyone who has only developed PHP and has had no exposure to countless frameworks or other languages, but it is in my opinion well worth the learning curve. My websites typically load in 300ms or less usually, even with development and debug modes enabled. With classic PHP, this is usually closer to 800ms or over a second when debug and development modes are enabled.

Conclusion

I hope you enjoy this introduction to Zephir. If you’ve enjoyed this brief introduction to Zephir, please let me know through claps or comments. If there is enough interest, I can also put together an article for your first Zephir project, if there is enough interest.

--

--

Charles Martin

I'm a Principal Software Engineer with over 20 years of development experience in websites and web applications as well as mobile and desktop applications.