Introduction: PHP Extensions

Introduction: PHP Extensions

PHPUG FFM, 2017-03-16
Web Engineering Düsseldorf, 2017-02-21
Symfony User Group Cologne, 2017-05-31 (Part I)

3f2fb8bbcd44609346e1cc0c06d0a39b?s=128

Thomas Weinert

May 31, 2017
Tweet

Transcript

  1. 6.

    DISCLAIMER This slides contain C code But this is NOT

    a talk about C I am not a core developer, yet. 3 . 4
  2. 8.
  3. 9.
  4. 11.

    CONFIG.M4 PHP_ARG_ENABLE(sample, [Whether to enable the "sample" extension], [ ­­enable­sample

    Enable "sample" extension support]) if test $PHP_SAMPLE != "no"; then PHP_SUBST(SAMPLE_SHARED_LIBADD) PHP_NEW_EXTENSION(sample, sample.c, $ext_shared) fi Makefile 5 . 2
  5. 12.

    PHP_SAMPLE.H #ifndef PHP_SAMPLE_H #define PHP_SAMPLE_H #define PHP_SAMPLE_EXT_NAME "sample" #define PHP_SAMPLE_EXT_VERSION

    "1.0" #define PHP_SAMPLE_EXT_NS "sample" #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "php.h" #ifdef ZTS #include "TSRM.h" #endif #if defined(ZTS) && defined(COMPILE_DL_SAMPLE) ZEND_TSRMLS_CACHE_EXTERN() #endif #endif /* PHP_SAMPLE_H */ C 5 . 3
  6. 13.

    SAMPLE.C #include "php_sample.h" zend_module_entry sample_module_entry = { STANDARD_MODULE_HEADER, PHP_SAMPLE_EXT_NAME, NULL,

    /* Functions */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ PHP_SAMPLE_EXT_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_SAMPLE C 5 . 4
  7. 19.

    TESTS ===================================================================== TEST RESULT SUMMARY ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Exts skipped : 0

    Exts tested : 26 ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Number of tests : 1 1 Tests skipped : 0 ( 0.0%) ‐‐‐‐‐‐‐‐ Tests warned : 0 ( 0.0%) ( 0.0%) Tests failed : 1 (100.0%) (100.0%) Expected fail : 0 ( 0.0%) ( 0.0%) Tests passed : 0 ( 0.0%) ( 0.0%) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Time taken : 1 seconds ===================================================================== ===================================================================== FAILED TEST SUMMARY ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Call function with string argument [tests/001.phpt] ===================================================================== 6 . 6
  8. 20.

    FEEDBACK TO PHP QA This report can be automatically sent

    to the PHP QA team at http://qa.php.net/reports and http://news.php.net/php.qa.reports This gives us a better understanding of PHP's behavior. If you don't want to send the report immediately you can choose option "s" to save it. You can then email it to qa‐reports@lists.php.net Do you want to send this report now? [Yns]: > export NO_INTERACTION=1 6 . 7
  9. 27.

    REGISTER MINFO FUNCTION zend_module_entry sample_module_entry = { STANDARD_MODULE_HEADER, PHP_SAMPLE_EXT_NAME, NULL,

    /* Functions */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ PHP_MINFO(sample), /* MINFO */ PHP_SAMPLE_EXT_VERSION, STANDARD_MODULE_PROPERTIES }; C 7 . 3
  10. 34.

    REGISTER FUNCTION LIST zend_module_entry sample_module_entry = { STANDARD_MODULE_HEADER, PHP_SAMPLE_EXT_NAME, php_sample_functions,

    /* Functions */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ PHP_SAMPLE_EXT_VERSION, STANDARD_MODULE_PROPERTIES }; C 8 . 5
  11. 35.

    > php ‐‐re sample Extension [ extension #26 sample version

    1.0 ] { ‐ Functions { Function [ function sample\helloWorld ] { } } } 8 . 6
  12. 40.

    USE VARIABLE sample.c PHP_FUNCTION(sample_get_value) { long value; value = SAMPLE_G(sample_value);

    php_printf("Current value: %d\n", value); SAMPLE_G(sample_value) = (value == 42) ? 21 : 42; } C 9 . 5
  13. 42.

    REGISTER INITIALIZATION sample.c zend_module_entry sample_module_entry = { STANDARD_MODULE_HEADER, PHP_SAMPLE_EXT_NAME, php_sample_functions,

    /* Functions */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ PHP_RINIT(sample), /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ PHP_SAMPLE_EXT_VERSION, STANDARD_MODULE_PROPERTIES }; C 9 . 7
  14. 44.

    IMPLEMENT PHP_FUNCTION(sample_hello_name) { char *name; size_t name_len; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(name,

    name_len) ZEND_PARSE_PARAMETERS_END(); php_printf("Hello %s!", name, name_len); } C 10 . 2
  15. 47.

    ALTERNATIVE SYNTAX PHP_FUNCTION(sample_hello_name) { char *name; size_t name_len; if (

    zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len ) == FAILURE ) { RETURN_NULL(); } php_printf("Hello %s!", name, name_len); } C 10 . 5
  16. 50.
  17. 52.

    RETURN ARRAY PHP_FUNCTION(sample_getGreetingParts) { array_init(return_value); add_assoc_str( return_value, "greeting", zend_string_init("Hello %s!",

    strlen("Hello %s!"), 0) ); add_assoc_str( return_value, "who", zend_string_init("World", strlen("World"), 0) ); } C 11 . 4
  18. 56.

    > php ‐‐re sample Extension [ <persistent> extension #26 sample

    version 1.0 ] { ‐ Functions { Function [ <internal:sample> function sample\hello ] { ‐ Parameters [1] { Parameter #0 [ <required> string $name ] } } } } 12 . 4
  19. 57.

    WITH RETURN VALUE ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( ArgInfo_sample_multiply, 0, 1, IS_LONG, NULL, 0

    ) ZEND_ARG_TYPE_INFO(0, first, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, second, IS_LONG, 0) ZEND_END_ARG_INFO() C 12 . 5
  20. 58.

    > php ‐‐re sample Extension [ <persistent> extension #26 sample

    version 1.0 ] { ‐ Functions { Function [ <internal:sample> function sample\multiply ] { ‐ Parameters [2] { Parameter #0 [ <required> integer $first ] Parameter #1 [ <optional> integer $second ] } ‐ Return [ integer ] } } } 12 . 6
  21. 59.

    TEMPLATES (1/2) ZEND_BEGIN_ARG_INFO(name, _unused) ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name type,

    class_name, allow_null) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(n return_reference, required_num_args, type class_name, allow_null) ZEND_END_ARG_INFO() 12 . 7
  22. 62.

    IMPLEMENT PHP_FUNCTION(sample_multiply) { zend_long first; zend_long second = 1; ZEND_PARSE_PARAMETERS_START(1,

    2) Z_PARAM_LONG(first) Z_PARAM_OPTIONAL Z_PARAM_LONG(second) ZEND_PARSE_PARAMETERS_END(); RETURN_LONG(first * second); } C 13 . 2
  23. 65.

    GET THE TYPE switch (Z_TYPE_P(value)) { case IS_NULL : php_printf("NULL");

    break; case IS_TRUE : php_printf("Boolean: TRUE"); break; case IS_FALSE : php_printf("Boolean: FALSE"); break; C 14 . 3
  24. 67.

    Z_*VAL_P case IS_DOUBLE : php_printf("Float: %f", Z_DVAL_P(value)); break; case IS_STRING

    : php_printf("String: %s", Z_STRVAL_P(value)); break; case IS_RESOURCE : php_printf("Resource: #%ld", Z_RESVAL_P(value)); break; C 14 . 5
  25. 70.

    _ZVAL_STRUCT struct _zval_struct { zend_value value; union { struct {

    ZEND_ENDIAN_LOHI_4( zend_uchar type, zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) } v; uint32_t type_info; } u1; union { uint32_t var_flags; uint32_t next; // hash collision chain uint32_t cache_slot; // literal cache slot C 15 . 2
  26. 71.

    _ZVAL_STRUCT typedef union _zend_value { zend_long lval; double dval; zend_refcounted

    *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; // Ignore these for now, they are special zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; C 15 . 3
  27. 72.

    _ZEND_REFCOUNTED struct _zend_refcounted { uint32_t refcount; union { struct {

    ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, uint16_t gc_info) } v; uint32_t type_info; } u; }; C 15 . 4
  28. 76.

    ITERATE HASHTABLE Zend/zend_hash.h ZEND_HASH_FOREACH_VAL(names, entry) { zend_string *name = zval_get_string(entry);

    php_printf("Hello %s!\n", ZSTR_VAL(name)); zend_string_release(name); } ZEND_HASH_FOREACH_END(); C 16 . 3
  29. 78.

    IN PHP USERLAND function foo(...$bar) { foreach ($bar as $drink)

    { ... } } foo('Cola', 'Beer', 'Gin Tonic'); PHP 17 . 2
  30. 80.

    ITERATE ARGUMENTS for (i = 0; i < argc; i++)

    { zval *arg = args + i; convert_to_string(arg); php_printf("Hello %s!\n", Z_STRVAL_P(arg)); } C 17 . 4
  31. 86.

    REGISTER MINIT FUNCTION zend_module_entry sample_module_entry = { STANDARD_MODULE_HEADER, PHP_SAMPLE_EXT_NAME, NULL,

    /* Functions */ PHP_MINIT(sample), /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ PHP_SAMPLE_EXT_VERSION, STANDARD_MODULE_PROPERTIES }; C 18 . 6
  32. 89.

    VARIABLES AND DESTRUCTOR sample.c #define SAMPLE_RESOURCE_NAME "sample_resource" int le_sample_resource; static

    void sample_resource_dtor(zend_resource *rsrc) { sample_resource *r = (sample_resource*)rsrc­>ptr; if (r) { efree(r); } } C 19 . 3
  33. 92.

    USE RESOURCE PHP_FUNCTION(sample_use_resource) { sample_resource *r; zval *zr; ZEND_PARSE_PARAMETERS_START(1, 1)

    Z_PARAM_RESOURCE(zr) ZEND_PARSE_PARAMETERS_END(); r = (sample_resource *)zend_fetch_resource( Z_RES_P(zr), SAMPLE_RESOURCE_NAME, le_sample_resource ); php_printf("Number: %ld", r­>number); } C 19 . 6
  34. 94.

    CALLABLE ARGUMENT PHP_FUNCTION(sample_hello_callback) { zend_fcall_info fci; zend_fcall_info_cache fci_cache; zval retval;

    ZEND_PARSE_PARAMETERS_START(1, ­1) Z_PARAM_FUNC(fci, fci_cache) Z_PARAM_VARIADIC('*', fci.params, fci.param_count) ZEND_PARSE_PARAMETERS_END(); fci.retval = &retval; C 20 . 2
  35. 95.

    EXECUTE CALLABLE if ( zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval)

    != IS_UNDEF ) { if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } convert_to_string(&retval); php_printf("Hello %s!\n", Z_STRVAL(retval)); } } C 20 . 3
  36. 96.
  37. 99.

    SPL EXCEPTION try { echo \sample\trigger(); } catch (\LogicException $e)

    { echo $e­>getCode(), ' ­> ', $e­>getMessage(); } PHP 23 . 2
  38. 101.

    CATCH EXCEPTION try { \sample\triggerException(); } catch (\Sample\ExceptionName $e) {

    echo $e­>getCode(), ' ­> ', $e­>getMessage(); } PHP 23 . 4
  39. 102.

    OWN EXCEPTION CLASS zend_class_entry *sample_exception; void sample_init_exception(TSRMLS_D) { zend_class_entry e;

    INIT_NS_CLASS_ENTRY( e, PHP_SAMPLE_EXT_NS, "ExceptionName", NULL ); sample_exception = zend_register_internal_class_ex( &e, (zend_class_entry*)zend_exception_get_default(TSRMLS_C) ); } PHP_MINIT_FUNCTION(sample) { C 23 . 5
  40. 116.

    CALL A METHOD class Hello { public function do($name) {

    printf("Hello %s!\n", $name); } } \sample\greet(new Hello("World")); PHP 26 . 1
  41. 117.

    PREPARE PHP_FUNCTION(sample_greet) { zval fname, params[1], *greeting; ZVAL_STRING(&fname, "do"); ZVAL_STRING(&params[0],

    "World"); ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT(greeting) ZEND_PARSE_PARAMETERS_END(); C 26 . 2
  42. 118.

    CALL if ( call_user_function( NULL, greeting, &fname, return_value, 1, params

    TSRMLS_CC ) == FAILURE ) { php_error_docref( NULL TSRMLS_CC, E_ERROR, "Unable to call method do() on object argument" ); RETVAL_FALSE; } C 26 . 3
  43. 122.

    UPDATE PROPERTY PHP_METHOD(sample_Greeting, __construct) zval *object; object = getThis(); zend_update_property_stringl(

    php_sample_greeting_class_entry, object, ZEND_STRL("name"), name, name_len ); C 27 . 3
  44. 123.

    READ PROPERTY PHP_METHOD(sample_Greeting, hello) { zval rv, *name, tmp; name

    = zend_read_property( php_sample_greeting_class_entry, getThis(), ZEND_STRL("name"), 0, // silent &rv ); ZVAL_COPY(&tmp, name); convert_to_string(&tmp); php_printf("Hello %s!", Z_STRVAL(tmp)); } C 27 . 4
  45. 124.

    DEFINE AN INTERFACE class SampleClass implements Sample\SampleInterface { public function

    sampleMethod() { echo ($this instanceof Sample\SampleInterface) ? 'Implemented Interface' : 'FAIL'; } } (new SampleClass())­>sampleMethod(); PHP 28 . 1
  46. 128.

    IMPLEMENT #define PHP_SAMPLE_CLASS_NAME "HelloWorld" static zend_class_entry *php_sample_class_entry; PHP_METHOD(sample_Class, jsonSerialize) {

    array_init(return_value); add_assoc_str( return_value, "greeting", zend_string_init("Hello World!", strlen("Hello World!"), ); add_assoc_long(return_value, "answer", 42); } C 29 . 2
  47. 130.

    REGISTER / DECLARE PHP_MINIT_FUNCTION(sample) { zend_class_entry ce; INIT_NS_CLASS_ENTRY( ce, PHP_SAMPLE_EXT_NS,

    PHP_SAMPLE_CLASS_NAME, php_sample_class_functions ); php_sample_class_entry = zend_register_internal_class(&ce TSRMLS_CC); zend_class_implements( php_sample_class_entry, 1, php_json_serializable_ce ); } C 29 . 4
  48. 133.

    TEMPLATES #define php_sample_greeting_from(o) \ ((php_sample_greeting_t*) ((char*) o ­ \ XtOffsetOf(php_sample_greeting_t,

    std))) #define php_sample_greeting_fetch(z) \ php_sample_greeting_from(Z_OBJ_P(z)) C 30 . 3
  49. 134.

    HANDLER: CREATE OBJECT zend_object* php_sample_greeting_create(zend_class_entry *ce) { php_sample_greeting_t *s =

    (php_sample_greeting_t*) emalloc( sizeof(php_sample_greeting_t) + zend_object_properties_size(ce) ); zend_object_std_init(&s­>std, ce); object_properties_init(&s­>std, ce); s­>std.handlers = &php_sample_greeting_handlers; return &s­>std; } C 30 . 4
  50. 135.

    HANDLER: FREE OBJECT void php_sample_greeting_free(zend_object *o) { php_sample_greeting_t *s =

    php_sample_greeting_from(o); zval_dtor(&s­>who); zend_object_std_dtor(o); } C 30 . 5
  51. 136.

    REGISTER HANDLERS PHP_MINIT_FUNCTION(sample) php_sample_greeting_class_entry = zend_register_internal_class( &ce TSRMLS_CC ); php_sample_greeting_class_entry­>create_object

    = php_sample_greeting_create; memcpy( &php_sample_greeting_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers) ); php_sample_greeting_handlers.offset = XtOffsetOf( php_sample_greeting_t, std ); php_sample_greeting_handlers.free_obj = php_sample_greeting_free; C 30 . 6
  52. 137.

    STORE VALUE PHP_METHOD(sample_Greeting, __construct) { char *name; size_t name_len; ZEND_PARSE_PARAMETERS_START(1,

    1) Z_PARAM_STRING(name, name_len) ZEND_PARSE_PARAMETERS_END(); php_sample_greeting_t *sample = php_sample_greeting_fetch( getThis() ); ZVAL_STRINGL(&sample­>who, name, name_len); } C 30 . 7
  53. 138.

    READ VALUE PHP_METHOD(sample_Greeting, hello) { zval rv, *name, tmp; php_sample_greeting_t

    *sample = php_sample_greeting_fetch( getThis() ); php_printf("Hello %s!", Z_STRVAL(sample­>who)); } C 30 . 8
  54. 139.

    LINKS Examples: Counter: Vagrant Box: Fast ZPP: github.com/ThomasWeinert/php- extension-sample/ github.com/ThomasWeinert/php-

    extension-sample-counter/ github.com/rlerdorf/php7dev https://wiki.php.net/rfc/fast_zpp 31