zephir - a wind of change for writing php extensions
TRANSCRIPT
ZephirA WIND OF CHANGE FOR WRITING PHP EXTENSIONS
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.
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.
What is Zephir? In a nutshell
Zephir makes it easy for high-level developers write low-level PHP Extensions.
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
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.
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.
Why might I write an Extension? Performance comparison
HHVM vs Zephir vs PHPhttps://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
What is Zephir? http://zephir-lang.com/ https://github.com/phalcon/zephir
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
Zephir – Installation (Ubuntu) $ sudo apt-get update
$ sudo apt-get install -y python-software-properties
$ sudo apt-get install -y curl
$ sudo apt-get install -y git gcc make re2c libpcre3-dev
$ sudo add-apt-repository ppa:ondrej/php5-5.6
$ sudo apt-get update
$ sudo apt-get install -y php5 dh-make-php php5-dev
$ sudo apt-get install -y php5-curl php5-gd php5-gmp php5-mcrypt \
php5-intl php5-cli
Zephir – Installation (Ubuntu) $ php –v
PHP 5.6.13-1+deb.sury.org~precise+3 (cli) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
$ phpize –v
Configuring for: PHP Api Version: 20131106 Zend Module Api No: 20131226 Zend Extension Api No: 220131226
Zephir – Installation (Ubuntu) $ git clone https://github.com/phalcon/zephir
$ cd zephir
$ ./install-json
$ ./install -c
$ cd ..
$ zephir version
0.8.0a
Zephir – Important Commands
$ zephir init [namespace] Initialises a Zephir extension
$ zephir compile Compiles a Zephir extension
$ zephir fullclean Cleans the object files generated in compilation
$ zephir install Installs the extension (requires root access)
$ zephir help Displays help
Zephir – First Steps $ zephir init helloworld
$ ls helloworld
helloworld/
ext/
helloworld/ ## Our zephir .zep files go here
config.json ## Configuration file for our extension
Zephir – First Steps $ cd helloworld
$ cat config.json
… "namespace": "helloworld", "name": "helloworld", "description": "Hello World Extension", "author": "Mark Baker", "version": "0.0.1", "verbose": false, "requires": { "extensions": [] }
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
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; } }
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
Zephir – First Steps $ zephir install
or
$ sudo cp ext/modules/helloworld.so /usr/lib/php5/20131226/helloworld.so
$ sudo nano /etc/php5/cli/php.ini
extension=helloworld.so
php -m
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
Zephir – First Steps $ cat helloworld.php
<?php
$instance = new \HelloWorld\greetings();
$instance->english();
$ php helloworld.php
Hello World
Zephir – Variables Variable names don’t begin with a $
Variables must be pre-defined/declaredvar 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"];
Zephir – Variables Variable variables do not exist in Zephir
But they can be “simulated”//Set variable $name in PHPlet {"name"} = "hello";
//Set variable $price in PHPlet name = "price";let {name} = 10.2;
Zephir – Variable Types Dynamic Typed Variables
Like PHP variables, and can change datatype between the different variable types supported by PHPDeclared with the keyword “var”
var name = "Mark";
Static Typed VariablesA subset of C-Datatypes
boolean, int, uint, char, uchar, long, ulong, string, arrayCan’t change datatype once declaredDeclared with the appropriate datatype name
uint counter = 1;
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;
Zephir – Arrays Array variables can be declared using the keywords “var” or “array”:
var a = []; // dynamic variablearray 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];
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
Zephir – Control Structures let counter = 0; while counter < 10 { echo counter, PHP_EOL; let counter += 1; }
Brackets around the evaluated condition are optional
Zephir – Control Structures let n = 10; loop { let n -= 2; if n == 0 { break; } echo n, PHP_EOL; }
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; }
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; }
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
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(); }
Special Features of Zephir Type Hints
Object/Interface Type Hintspublic function injectFilter(<App\FilterInterface> filter){ //...}
Similar to the existing type hints in PHP, although notice the syntax differences
Special Features of Zephir Type Hints
“Scalar” Type Hintspublic 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
Special Features of Zephir Type Hints
Strict Scalar Hintspublic function filterText(string! text, boolean escape=false){ //...}
this->filterText(1234, 0);
Will throw an Exception
Special Features of Zephir Return Type Hints
public function getClassFromFactory() -> <App\MyInterface> { //...}
Similar to the return type hints introduced in PHP 7, although notice the syntax differences
function isValidStatusCode(int $statusCode): bool {
//...
}
Special Features of Zephir Read-Only Arguments
public function filterText(const string text, boolean escape=false){ //...}
Used for compiler optimisations
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
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
Zephir – Pitfalls Unsupported Features of PHP
Array DereferencingCallbacks can’t use “use”
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 advancevar complexParts;
let validComplex = preg_match("/^....$/ui", complexNumber, complexParts);
Zephir – Pitfalls Overly-Complex or Ambiguous Syntax
An expression likeif (!is_object($complex) || !$complex instanceof Complex) { … }
Might logically be translated to Zephir asif !is_object(complex) || !complex instanceof Complex { … }
but Zephir has a different precedence to PHP for instanceof, so you need to doif !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
Zephir – Utilities and Helpers PHP to Zephir
https://github.com/fezfez/php-to-zephir
Zephir – Utilities and Helpers $ zephir init helloworld
$ cd helloworld
$ /home/vagrant/vendor/bin/php-to-zephir phpToZephir:convertDir \
<path to PHP source code>
Zephir – Testing
Zephir – Testing $ cd helloworld
helloworld/ ext/ /helloworld /modules run-tests.php ## PHP Test execution script helloworld/ compile-errors.log compile.log config.json
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
Zephir – Testing $ cat helloWorldTest001.phpt
--TEST-- Test Hello World display using helloworld extension --FILE-- <?php
$instance = new \HelloWorld\greetings();
$instance->english(); --EXPECT-- Hello World
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 =====================================================================
Zephir – Testing https://qa.php.net/write-test.php https://qa.php.net/phpt_details.php
Useful Zephir Links Zephir Documentation
http://docs.zephir-lang.com/
Zephir Bloghttp://blog.zephir-lang.com/