2. What is Zephir?
Zephir – Zend Engine PHP Intermediate.
A high-level domain-specific language (DSL) that simplifies
the creation and maintain-ability of native C extensions for
PHP.
Developed by the team behind Phalcon, the PHP CMS
written in C.
3. What is Zephir?
The creators of Zephir actually pronounce it “zaefire”.
(/ˈzäfī(-ə)r/)
But I still pronounce it “Zephir” (/ˈzef.ər/)
The Zephir Language is an open source project licensed
under an MIT license.
Zephir is written in PHP.
4. What is Zephir?
In a nutshell
Zephir makes it easy for high-level developers write low-level PHP
Extensions.
5. Writing a PHP Extension
https://wiki.php.net/internals/references
http://www.phpinternalsbook.com/
http://www.amazon.com/Extending-Embedding-PHP-Sara-Golemon/dp/067232704X
6. Why might I write an Extension?
Native C Extensions to PHP can typically execute faster than
raw PHP code.
The ability to use native C datatypes in an Extension may
help save memory usage.
Deploying an Extension allows you to keep the source of
your code closed.
7. Why might I write an Extension?
If a class is heavily IO bound, or requires the allocation/
deallocation of large amounts of memory, then you will
probably not gain any performance benefits.
Unless you can take advantage of the native C datatypes
internally in the code, then you will probably not gain any
memory benefits.
8. Why might I write an Extension?
Performance comparison
HHVM vs Zephir vs PHP
https://www.simonholywell.com/post/2014/02/hhvm-vs-zephir-vs-php-the-
showdown/
https://www.simonholywell.com/static/files/2014-02-28/index.html
Simon Holywell
Australian Zend certified Development Director at Mosaic in Brighton, UK
10. Zephir – Installation (Ubuntu)
Requirements
◦gcc >= 4.x/clang >= 3.x
◦re2c 0.13 or later
◦gnu make 3.81 or later
◦autoconf 2.31 or later
◦automake 1.14 or later
◦libpcre3
◦php development headers and tools
17. Zephir – First Steps
Organise your code into files and namespaces
Uses Case-Sensitive file/folder names
Always use lower-case for file/folder names
Every file must contain one (and only one) class
Only OOP Code, no new PHP functions
Class names can be mixed-case
Classes must be namespaced
Top-level Namespace should match the namespace defined in config.json
but may be mixed-case
18. Zephir – First Steps
$ cd helloworld
$ cat greetings.zep
namespace HelloWorld;
class greetings {
public function english() {
// Variables must be defined before they can be used
var greeting;
// "let" is used to assign values to a variable
let greeting = "Hello World";
echo greeting, PHP_EOL;
}
}
19. Zephir – First Steps
$ cd ..
$ zephir compile
helloworld/
ext/
/helloworld ## zephir generates C source code here
/modules ## zephir builds the PHP Extension here
helloworld/
compile-errors.log
compile.log
config.json
21. Zephir – First Steps
$ php -i | grep -m 2 -A 4 helloworld
helloworld
Hello World Extension
helloworld => enabled
Author => Mark Baker
Version => 0.0.1
Build Date => Sep 27 2015 08:57:19
Powered by Zephir => Version 0.8.0a
22. Zephir – First Steps
$ cat helloworld.php
<?php
$instance = new HelloWorldgreetings();
$instance->english();
$ php helloworld.php
Hello World
23. Zephir – Variables
Variable names don’t begin with a $
Variables must be pre-defined/declared
var stringVar = "hello", boolVar = true, intVar = 1.0;
int answer = 42, question = 1;
PHP scope rules apply
Global variables don’t exist in Zephir (except that SuperGlobals can be accessed)
let requestMethod = _SERVER["REQUEST_METHOD"];
24. Zephir – Variables
Variable variables do not exist in Zephir
But they can be “simulated”
//Set variable $name in PHP
let {"name"} = "hello";
//Set variable $price in PHP
let name = "price";
let {name} = 10.2;
25. Zephir – Variable Types
Dynamic Typed Variables
Like PHP variables, and can change datatype between the different variable
types supported by PHP
Declared with the keyword “var”
var name = "Mark";
Static Typed Variables
A subset of C-Datatypes
boolean, int, uint, char, uchar, long, ulong, string, array
Can’t change datatype once declared
Declared with the appropriate datatype name
uint counter = 1;
26. Zephir – Strings
String literals (dynamic var, static string) must be wrapped in double quotes
var name = "Mark Baker";
Character literals (static char, static uchar) must be wrapped in single quotes
char initial = 'M';
Strings in Zephir do not support variable interpolation/parsing; use
concatenation instead:
let forename = "Mark";
let surname = "Baker";
let fullName = forename . " " . surname;
27. Zephir – Arrays
Array variables can be declared using the keywords “var” or “array”:
var a = []; // dynamic variable
array b = []; // static array variable
As in PHP, keys can only be string or integer values
Syntax is slightly different:
let elements = [
"foo": "bar", // Use of : rather than =>
"bar": "foo" // No trailing , permitted
];
28. Zephir – Control Structures
public function compare(a, b) {
if a < b {
return -1;
} elseif a > b {
return 1;
}
return 0;
}
Brackets around the evaluated condition are optional
29. Zephir – Control Structures
let counter = 0;
while counter < 10 {
echo counter, PHP_EOL;
let counter += 1;
}
Brackets around the evaluated condition are optional
30. Zephir – Control Structures
let n = 10;
loop {
let n -= 2;
if n == 0 { break; }
echo n, PHP_EOL;
}
31. Zephir – Control Structures
let items = ["a": 1, "b": 2, "c": 3, "d": 4];
for key, value in items {
echo key, " : ", value, PHP_EOL;
}
for key, value in reverse items {
echo key, " : ", value, PHP_EOL;
}
32. Zephir – Control Structures
string fullName = "Mark Baker"; char character;
for character in fullName {
echo character , PHP_EOL;
}
for character in reverse fullName {
echo character , PHP_EOL;
}
33. Zephir – Control Structures
let items = ["a": 1, "b": 2, "c": 3, "d": 4];
for key, _ in items {
echo key, PHP_EOL;
}
The value element in the for loop doesn’t need to be declared
34. Zephir – Exceptions
try {
// exceptions can be thrown here
if (firstCase) {
throw new RuntimeException("This is an exception");
} else {
throw "Untyped Exception";
}
} catch RuntimeException|Exception, e {
// handle exception
echo e->getMessage();
}
35. Special Features of Zephir
Type Hints
Object/Interface Type Hints
public function injectFilter(<AppFilterInterface> filter)
{
//...
}
Similar to the existing type hints in PHP, although notice the syntax
differences
36. Special Features of Zephir
Type Hints
“Scalar” Type Hints
public function filterText(string text, boolean escape=false)
{
//...
}
Allows “compatible” types, e.g.
this->filterText(1234, 0);
Will try to convert the data passed to the type-hinted datatype
37. Special Features of Zephir
Type Hints
Strict Scalar Hints
public function filterText(string! text, boolean escape=false)
{
//...
}
this->filterText(1234, 0);
Will throw an Exception
38. Special Features of Zephir
Return Type Hints
public function getClassFromFactory() -> <AppMyInterface> {
//...
}
Similar to the return type hints introduced in PHP 7, although notice the
syntax differences
function isValidStatusCode(int $statusCode): bool {
//...
}
39. Special Features of Zephir
Read-Only Arguments
public function filterText(const string text, boolean escape=false)
{
//...
}
Used for compiler optimisations
40. Special Features of Zephir
Named Arguments
public function crop(width = 600, height = 400) {
//...
}
this->crop(height: 200);
this->crop(height: 300, width: 400);
Adds a slight performance overhead
41. Zephir – Pitfalls
Silent compilation failures
$ zephir compile
helloworld/
ext/
/helloworld ## zephir generates C source code here
/modules ## zephir builds the PHP Extension here
helloworld/
compile-errors.log ## Always check this file
compile.log
config.json
43. Zephir – Pitfalls
Bad assumptions from lazy PHP practises
In PHP, a pass-by-reference variable in an expression like
$validComplex = preg_match('/^...$/ui', $complexNumber, $complexParts);
will automatically created $complexParts if it doesn't exist;
but Zephir won't do this, so you need to explicitly create it in advance
var complexParts;
let validComplex = preg_match("/^....$/ui", complexNumber, complexParts);
44. Zephir – Pitfalls
Overly-Complex or Ambiguous Syntax
An expression like
if (!is_object($complex) || !$complex instanceof Complex) { … }
Might logically be translated to Zephir as
if !is_object(complex) || !complex instanceof Complex { … }
but Zephir has a different precedence to PHP for instanceof, so you
need to do
if !is_object(complex) || !(complex instanceof Complex) { … }
otherwise it executes !complex and then tests the result of that (always
a boolean) for instanceOf Complex
cf. https://github.com/phalcon/zephir/issues/277
45. Zephir – Utilities and Helpers
PHP to Zephir
https://github.com/fezfez/php-to-zephir
46. Zephir – Utilities and Helpers
$ zephir init helloworld
$ cd helloworld
$ /home/vagrant/vendor/bin/php-to-zephir phpToZephir:convertDir
<path to PHP source code>
49. Zephir – Testing
$ cd ext/modules
$ php run-tests.php *.phpt <directory with test files>
$ php run-tests.php --help
Useful Option
-c <filename> ## Custom php.ini file to be used
50. Zephir – Testing
$ cat helloWorldTest001.phpt
--TEST--
Test Hello World display using helloworld extension
--FILE--
<?php
$instance = new HelloWorldgreetings();
$instance->english();
--EXPECT--
Hello World
51. Zephir – Testing
$ php run-tests.php *.phpt <directory with test files>
…
=====================================================================
Running selected tests.
PASS Test Hello World display using helloworld extension
[/srv/phpnw2015/helloworld/tests/helloWorldTest-001.phpt]
=====================================================================
Number of tests : 1 1
Tests skipped : 0 ( 0.0%) --------
Tests warned : 0 ( 0.0%) ( 0.0%)
Tests failed : 0 ( 0.0%) ( 0.0%)
Expected fail : 0 ( 0.0%) ( 0.0%)
Tests passed : 1 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken : 0 seconds
=====================================================================