building custom php extensions
DESCRIPTION
TRANSCRIPT
Building Custom PHPExtensions
International PHP Conference 2012 Tbilisi, GeorgiaWednesday, December 12, 12
About me
‣ Ioseb Dzmanashvili
‣ Software Architect at AzRy LLC
‣ Teacher at Caucasus School of Technology
‣ V8 JavaScript engine contributor
‣ Author of uri_template PHP extension
‣ Author of Create-Form and Edit-Form link relation types (being RFCed now).
Wednesday, December 12, 12
This talk covers
‣ Setting up development environment
‣ Creating extension skeletons with automated tools
‣ Building and installing extensions
‣ Internals such as:
‣ Implementing and exposing C functions
‣ PHP parameter parsing
‣ Variables
Wednesday, December 12, 12
This talk doesn’t cover
‣Thread safety topics
‣Networking
‣Object oriented programming
‣Stream wrappers
‣Memory
Wednesday, December 12, 12
My Experience
‣ URI Template Pecl extension
‣ https://github.com/ioseb/uri-template
‣ Available from Pecl channel
‣ http://pecl.php.net/package/uri_template
‣ Used by Guzzle HTTP client library and Drupal 8
Wednesday, December 12, 12
What it does?
‣ Implementation of RFC6570(URI Template)
‣100% compatibility with RFC3986 (Uniform Resource Identifier - URI)
‣100% compatibility with RFC3629 (UTF-8)
Wednesday, December 12, 12
How to use it?
// URI Template Data$data = array( "id" => array("person","albums"), "token" => "12345", "fields" => array("id", "name", "picture"),);
// URI Template$template = "{/id*}{?fields,token}";
// Transformation$result = uri_template($template, $data);
//Produces:
/person/albums?fields=id,name,picture&token=12345
Wednesday, December 12, 12
Why it is important?
‣ Possibility to achieve outstanding performance
‣ Possibility to learn PHP from inside out
Wednesday, December 12, 12
Preparing development environment
Wednesday, December 12, 12
Installing PHP dev package
‣ Linux(Debian)
$ sudo apt-get install php5-dev
‣ Installing with Mac Ports on Mac OS X
$ sudo port install php5-devel
‣ Use XAMPP developer package as an alternative for Mac OS X (painless solution)
Wednesday, December 12, 12
Creating extension
‣ Creating the configuration and build files
‣ Creating the header and basic C files, which includes:
‣ Creating initialization and destruction functions
‣ Including the correct headers
‣ Creating functions for use by PHP info tools
‣ Creating test files
Wednesday, December 12, 12
Tools for creating extensions
‣Create extension manually
‣Use ext_skel to generate extension
‣Use pecl-gen to generate extension
Wednesday, December 12, 12
Generating extension with pecl-gen
‣ Install codegen_pecl
$ pear install codegen_pecl
‣Create XML descriptor for extension
‣Generate extension skeleton
$ pecl-gen hello-world.xml
Wednesday, December 12, 12
<?xml version="1.0" ?><extension name="hello_world" version="0.1.0"> <summary>Yet another hello world PHP Extension</summary> <description> This is a sample "hello world" extension for demonstration purposes only. </description> <maintainers> <maintainer> <user>ioseb</user> <name>Ioseb Dzmanashvili</name> <email>[email protected]</email> <role>lead</role> </maintainer> </maintainers> <license>PHP</license> <function name="hello_world" role="public"> <proto>void hello_world( string input )</proto> <summary>Prints a hello world message</summary> <code><![CDATA[ // C code goes here ]]></code> </function></extension>
XML descriptor (hello-world.xml)
Wednesday, December 12, 12
!"" config.m4!"" config.w32!"" hello-world.xml!"" hello_world.c!"" manual# !"" Makefile# !"" file-entities.ent# !"" functions.xml# !"" hello-world# # !"" ...# # !"" functions# # # $"" hello-world.xml# # !"" ini.xml# # $"" reference.xml# $"" manual.xml.in!"" ...!"" php_hello_world.h$"" tests $"" hello_world.phpt
Generated extension structure$ tree hello_world
Wednesday, December 12, 12
PHP_FUNCTION(hello_world){ const char * input = NULL; int input_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &input, &input_len) == FAILURE) {
return; }
do { // C code goes here } while (0);}
Generated hello_world() function
Wednesday, December 12, 12
PHP_FUNCTION(hello_world){ const char * input = NULL; int input_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &input, &input_len) == FAILURE) {
return; }
// our implementation php_printf("Hello world %s", input);}
Modifying hello_world() function
Wednesday, December 12, 12
Installing extension
‣ Prepare build environment for PHP extension
$ phpize
‣ Configure it
$ ./configure
‣ Compile & Install
$ make && sudo make install
‣ Enable extension
$ php -d extension=hello_world.so -m
Wednesday, December 12, 12
Testing hello_world() function
‣ Create test.php file
‣Write test code<?php
echo hello_world("PHP Rocks");
echo "\n";
‣ Run script
$ php test.php
‣ If you see following line everything is OK
$ Hello world PHP Rocks
Wednesday, December 12, 12
Cleaning extension folder
‣ Get rid off compilation output
$ make clean
‣ Get rid off build environment stuff
$ phpize --clean
Wednesday, December 12, 12
PHP_FUNCTION
Wednesday, December 12, 12
PHP_FUNCTION(hello_world){ // rest of code removed for brevity}
expands to
PHP_FUNCTION expansion
void zif_hello_world( zval *return_value, // 1) variable to store return value char return_value_used, // 2) if returned value was used zval *this_ptr TSRMLS_DC // 3) pointer to object's internal state)
Wednesday, December 12, 12
function_entry hello_world_functions[] = {
PHP_FE(hello_world, hello_world_arg_info) { NULL, NULL, NULL }
};
Exposing internal function
expands to
function_entry hello_world_functions[] = { {"hello_world", zif_hello_world, hello_world_arg_info}, { NULL, NULL, NULL }};
Wednesday, December 12, 12
Parsing Function Parameters
PHP_FUNCTION(hello_world){ const char *input = NULL; // variable to store parameter value int input_len = 0; // variable to store value length
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, // 1) number of arguments "s", // 2) format string &input, // 3) address of *input variable &input_len // 4) address of input_len variable ) == FAILURE) { return; // just return if something goes wrong }
// actual implementation php_printf("Hello world %s", input);}
Wednesday, December 12, 12
Available Parameter Types
Type Specifier C datatype PHP Type
b zend_bool Boolean
l long Integer
d double Floating point
s char*, int String
r zval* Resource
a zval* Array
o zval* Object instance
O zval*, zend_class_entry* Object of a specified type
z zval* Non-specific zval
Z zval** Dereferenced zval
Wednesday, December 12, 12
Examples of parameter formats
// means that function expects:// a) required long parameter// b) required string parameter// c) optional long parameter// d) optional zval of non-specific typezend_parse_parameters(..., "ls|lz", ...)
// menas that function expects:// a) required array parameter// b) required array parameter// c) required string parameter// d) optional long parameterzend_parse_parameters(..., "aas|l", ...)
Wednesday, December 12, 12
Zval
_zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc;};
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj;} zvalue_value;
typedef struct _zval_struct zval;
Wednesday, December 12, 12
Zval - Graphical Representation
value
refcount__gc
type
is_ref__gc
long lval
double dval
str
HashTable *ht
zend_object_value obj
char *val int len
Wednesday, December 12, 12
Internal Data Types
Type Value Access Macros
IS_NULL N/A
IS_BOOL Z_BVAL_P(value)
IS_LONG Z_LVAL_P(value)
IS_DOUBLE Z_DVAL_P(value)
IS_STRING Z_STRVAL_P(value), Z_STRLEN_P(value)
IS_ARRAY Z_ARRVAL_P(value)
IS_OBJECT Z_OBJVAL_P(value)
IS_RESOURCE Z_RESVAL_P(value)
Wednesday, December 12, 12
void display_zval(zval *value) { switch(Z_TYPE_P(value)) { case IS_NULL: php_printf("NULL"); break; case IS_BOOL: php_printf("BOOL %d", Z_BVAL_P(value) ? 1 : 0); break; case IS_LONG: php_printf("LONG %ld", Z_LVAL_P(value)); break; case IS_DOUBLE: php_printf("DOUBLE %f", Z_DVAL_P(value)); break; case IS_STRING: php_printf("STRING %s", Z_STRVAL_P(value)); break; case IS_RESOURCE: php_printf("RES #%ld", Z_RESVAL_P(value)); break; case IS_ARRAY: php_printf("ARRAY"); break; case IS_OBJECT: php_printf("OBJECT"); break; }}
Zval Reader Example
Wednesday, December 12, 12
Returning Values
Type Value Access Macros
ZVAL_NULL(return_value) RETVAL_NULL()
ZVAL_BOOL(return_value) RETVAL_BOOL(bval)
ZVAL_TRUE(return_value) RETVAL_TRUE
ZVAL_FALSE(return_value) RETVAL_FALSE
ZVAL_LONG(return_value, lval) RETVAL_LONG(lval)
ZVAL_DOUBLE(return_value, dval) RETVAL_DOUBLE(dval)
ZVAL_STRING(return_value, str, dup) RETVAL_STRING(str, dup)
ZVAL_RESOURCE(return_value, rval) RETVAL_RESOURCE(rval)
Wednesday, December 12, 12
PHP_FUNCTION(test_function){ ZVAL_NULL(return_value)}
Returning Values
Wednesday, December 12, 12
Books
Wednesday, December 12, 12
Questions?
Wednesday, December 12, 12
Thank You!
Wednesday, December 12, 12