php extensions
TRANSCRIPT
Congratulations!• You’ve decided to learn “down the stack”
• You’ll learn more than you do “across the stack”
• Your brain will hurt a bit
• This will require you to be involved in your own learning process
• It is HARD WORK to be good at anything new
Why do extensions?• Talk to a C or C++ library
• Modify the way the engine works (zend extensions usually)
• Make slow parts of code run faster
But I don’t know C!1. compiled
2. strictly typed
3. php internals do “hard” stuff
4. copy and paste! boilerplate and macros
5. cairo, pecl_http, date
6. Don’t do the hard stuff first!
lxr.php.net
Also try:• http://www.learn-c.org/
• http://c.learncodethehardway.org/book/
• http://aelinik.free.fr/c/
Practice Practice Practice!
How to Compile an Extension1. phpize
2. ./configure
3. make
4. make install
5. make test
configure might require –with-php-config=/path/to/something
but…
But wait – there’s more!• Compile your own PHP source, use a –prefix
/usr/local/php-5.6-debug-zts is one I use
I also have 20 php’s installed
• We want developer specific flags
--enable-maintainer-zts and --enable-debug
optionally –enable-gcov
• install gdb and valgrind and optionally lcov
• this is easy on Ubuntu and similar systems where packages are easy to get
• You can also compile php with clang instead of gccand do things like use static analysis
Rasmus to the Rescue!• https://github.com/rlerdorf/php7dev/blob/master/README.md
tl;dr
1. install virtualbox
2. install vagrant
3. git clone https://github.com/rlerdorf/php7dev.git
4. cd php7dev
5. vagrant up
6. vagrant ssh
7. sudo newphp 56 debug zts
How to Play alonggit clone https://github.com/auroraeosrose/php-extensions-code.git
git clone git://github.com/auroraeosrose/php-extensions-code.git
git checkout scaffolding
bookmark lxr.php.net – you will be using it a lot
phpize
./configure
make
make test
1. Set up compile environment
2. Write a module definition
3. Learn about the PHP lifecycle
4. Learn about zvals
5. Add functions
6. ???
7. Profit!
Every Other Extensions Talk
1. Do something you can’t do in userland
2. Utilize a C library
3. Make slow code faster
Maybe you should just use ffi!
Step 1. Why?
What is FFI?• Foreign function interface
• Java calls it JNI
• HHVM calls it HNI
• Python calls it “ctypes” (do not ask, stupidest name ever)
• C# calls it P/Invoke
• Ruby calls it FFI
• PHP calls it…
Oh wait…• php’s ffi is rather broken (last release is 2004-01-20)
• php’s ffi has no maintainer (ilia and wez were doing it)
• It needs some TLC
• Are you interested and not afraid? See me!
1. I hate parsing URIs
2. PHP’s parse_url is… not the best
3. How about an extension that wraps something
that parses uris in an excellent fashion
4. RESEARCH TIME
Step 2. What
C library to the rescue!• uriparser.sourceforge.net
• strictly RFC 3986 compliant
• available on many systems as packages
• cross platform
• documented http://uriparser.sourceforge.net/doc/html/
1. Think about what the API should be
2. Look at the C APIs but don’t mimic them
3. Write an example of how you WANT it to work
4. Write more then one example!
5. Turn these examples into your first tests
6. Yes this is TDD
Step 3. How
1. This is copy and paste
1. config.m4 & config.w32
2. macros for version api changes if necessary
3. main module file (php_{$ext}.c)
4. main header file (php_{$ext}.h)
2. Compile it and test!
Step 6. Extension Scaffolding
Scaffolding rules
1. make sure you name your files in a standard way
2. document! use a license header, proto statements
3. read the coding standards
http://lxr.php.net/xref/PHP_5_6/CODING_STANDA
RDS
4. FOLLOW THE CODING STANDARDS
5. use version control early on – github is easy!
php –d extension=myext.so –m
The scaffold extension should show up in the list
Make sure to
1. define a version constant
2. read and use PHP code standards
Check if it works
1. run-test.php
2. make test will magically have output
more at http://qa.php.net/write-test.php and docs
at http://qa.php.net/phpt_details.php
Step 7. Write the test
Typing systems compared
Static Typing (C)
• Variables must be declared before use
• Variables must be given a type at declaration
• “compile time” checking
Dynamic Typing (PHP)
• Variables don’t have to be declared before use
• Variables can change types
• “run time” checking
long any numeric
double numeric with decimal
char* + int(length) strings, binary
Hashtable dictionaries, arrays,structs
object any complicated type
How do I get C types from a ZVAL?
Z_LVAL(zval) Z_LVAL_P(zval_p) Z_LVAL_PP(zval_pp)
Z_BVAL(zval) Z_BVAL_P(zval_p) Z_BVAL_PP(zval_pp)
Z_DVAL(zval) Z_DVAL_P(zval_p) Z_DVAL_PP(zval_pp)
Z_STRVAL(zval) Z_STRVAL_P(zval_p) Z_STRVAL_PP(zval_pp)
Z_STRLEN(zval) Z_STRLEN_P(zval_p) Z_STRLEN_PP(zval_pp)
Z_ARRVAL(zval) Z_ARRVAL_P(zval_p) Z_ARRVAL_PP(zval_pp)
Z_OBJVAL(zval) Z_OBJVAL_P(zval_p) Z_OBJVAL_PP(zval_pp)
Z_OBJ_HANDLE(zv
al)
Z_OBJ_HANDLE_P(zval
_p)
Z_OBJ_HANDLE_PP(zval_
pp)
Z_OBJ_HT(zval) Z_OBJ_HT_P(zval_p) Z_OBJ_HT_PP(zval_pp)
Z_OBJCE(zval) Z_OBJCE_P(zval_p) Z_OBJCE_PP(zval_pp)
Z_OBJPROP(zval) Z_OBJPROP_P(zval_p) Z_OBJPROP_PP(zval_pp)
Z_TYPE(zval) Z_TYPE_P(zval_p) Z_TYPE_PP(zval_pp)
1. tell it to link to our C library
2. add a function
3. learn how to get data
4. learn how to return data
Step 8. Actually do something
I hate autotools…• Also known as the gnu build system
• provider of much pain, but much good use as well
• autoconf – generates a configure script (we cheat with phpize on shared extensions) makes a configure we run to set up our information,
mainly a config.h file
• automake – creates makefiles
• libtool – creates static and dynamic libraries
• Windows? Well php wrote it’s own version of autotools – in jscript (windows javascript variant)
Wait – what was that?• Define your function in C
use a special call to parse parameters passed by the user
use a special zval to return data to the user
• Tell PHP your extension provides this function
put it in your “giant struct of doom” that lists them all
send it to php when in your module struct
• Tell PHP what arguments your extension provides
If your argument information and zpp argue users will be angry
Yes it sucks you can’t just do one
php typecode
c type
array or object a zval *
boolean b zend_bool
class C zend_class_entry *
double d double
callable f zend_fcall_info andzend_fcall_info_cache
array or HASH_OF(object) H HashTable*
array h HashTable*
integer l long (NOT INT)
integer L long with LONG_MAX, LONG_MIN limits
object o zval *
object of specific type O zval *, zend_class_entry
string (no null bytes) p char*, int
resource r zval *
string (possible null bytes) s char*, int
actual zval z zval *
actual zval Z zval**
zend_parse_parameterstype
code
variable args (any) * int, zval***
variable args (1 or more) + int, zval***
| anything after is optional, use defaults
/ use SEPARATE_ZVAL_IF_NOT_REF
doesn’t apply to b, l, and d ! C NULL for zval null
return_valueRETURN_RESOURCE(l)
RETURN_BOOL(b)
RETURN_NULL()
RETURN_LONG(l)
RETURN_DOUBLE(d)
RETURN_STRING(s, duplicate)
RETURN_STRINGL(s, l, duplicate)
RETURN_EMPTY_STRING()
RETURN_ZVAL(zv, copy, dtor)
RETURN_FALSE
RETURN_TRUE
RETVAL_RESOURCE(l)
RETVAL_BOOL(b)
RETVAL_NULL()
RETVAL_LONG(l)
RETVAL_DOUBLE(d)
RETVAL_STRING(s, duplicate)
RETVAL_STRINGL(s, l, duplicate)
RETVAL_EMPTY_STRING()
RETVAL_ZVAL(zv, copy, dtor)
RETVAL_FALSE
RETVAL_TRUE
Complex Data
array_init()
add_(index|assoc)_long()
add_(index|assoc)_bool()
add_(index|assoc)_string()
object_init()
add_property_long()
add_property_bool()
add_property_string()
Classes
1. name, parent, and flags
2. hashtables of methods, default methods, static
methods
3. hashtables of static properties, default
properties, and properties
4. object handlers
5. union of either file information, or internal
structures (for internal classes)
Lifecycle
PHP stops
MSHUTDOWN – for each extension
RSHUTDOWN – for each request
cleanup after test.php
RINIT – for each request
execute test.php
MINIT – for each extension
request/parse test.php
PHP starts
php test.php
Lifecycle Threaded
PHP stops
MSHUTDOWN – for each extension
request index.php request foo.php
RINIT – for each request
• execute test.php
RSHUTDOWN – for each request
• cleanup after test.php
RINIT – for each request
• execute test.php
RSHUTDOWN – for each request
• cleanup after test.php
MINIT – for each extension
apache starts
Abstract, Interface, TraitClass Method
ZEND_ACC_IMPLICIT_ABSTRACT_CLA
SS
ZEND_ACC_STATIC
ZEND_ACC_EXPLICIT_ABSTRACT_CLA
SS
ZEND_ACC_ABSTRACT
ZEND_ACC_FINAL_CLASS ZEND_ACC_FINAL
ZEND_ACC_INTERFACE ZEND_ACC_PUBLIC
ZEND_ACC_TRAIT ZEND_ACC_PROTECTED
ZEND_ACC_PRIVATE
ZEND_ACC_CTOR
ZEND_ACC_DTOR
ZEND_ACC_CLONE spl_ce_FilterIterator->ce_flags |=
ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
PHP_ME(DateTime, __construct, arginfo_date_create,
ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
1. globals
2. memory management
3. custom objects
4. object handlers
5. thread safety
Step 10: Advanced topics
1. in your header – use
ZEND_BEGIN|END_MODULE_GLOBALS
2. create the global access macro in your header
(copy and paste)
3. ZEND_DECLARE_MODULE_GLOBALS in
every file where you will use them
4. use the macro to access
COUNTER_G(basic_counter_value)); }
5. Create ginit/gshutdown functions if your globals
need initializing , etc
Global Variables (threads = evil)
emalloc( )
• allocates the specified number of bytes
safe_emalloc()
• like emalloc but adds a special protection against overflows
efree( )
• releases the specified block of memory back to the system
estrdup( )
• allocate a buffer and copy the string into that buffer
estrndup( )
• same as estrdup when you already know the length of the string
ecalloc( )
• allocates the number of bytes and initializes them to zero
erealloc( )
• resizes the specified block of memory
https://wiki.php.net/internals/zend_mm
Object Handlers (black magic)
https://wiki.php.net/internals/engine/objects
TSRM
thread safe resource manager
ZTS
zend thread safety
• tsrm_lsTSRMLS_C
• void ***tsrm_lsTSRMLS_D
• , tsrm_lsTSRMLS_CC
• , void ***tsrm_lsTSRMLS_DC
1. http://edit.php.net
2. http://svn.php.net/viewvc/phpdoc/en/trunk/reference/
3. PhD is awesomesauce
• http://doc.php.net/phd/
4. email [email protected]
1. who you are
2. what you wrote (with links to your code!)
3. why you think it should be in pecl
5. poke me (or other devs)
Document, PECL, release
Stuff I didn’t talk about
1. resources (use custom objects instead)
2. ini entries (just DON’T)
3. threading and parallel processing
4. engine hooking
5. streams and transports
About Me
http://emsmith.net
twitter - @auroraeosrose
IRC – freenode – auroraeosrose
#phpmentoring
#gophp7-ext
Questions?
HELP WITH DOCS!
http://edit.php.net
http://wiki.php.net/internals
HELP WITH THE FUTURE
http://gophp7.org/gophp7-ext