Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting started with PHP core development - phpCE 2017

Arnout Boks
November 04, 2017

Getting started with PHP core development - phpCE 2017

Joind.in: https://joind.in/talk/3e087

While PHP’s open source community is thriving, only a relatively small number of PHP developers contribute to the number one tool they use on a daily basis: PHP itself. Although it may seem daunting to help out on a project written in C, requiring compilation and linking, getting started is actually pretty easy.

In this talk I would like to share my own recent experiences of beginning to contribute to PHP itself. We will cover some practical tips, workflows and resources, so you can start giving back to your programming language straight away!

Arnout Boks

November 04, 2017
Tweet

More Decks by Arnout Boks

Other Decks in Programming

Transcript

  1. @arnoutboks #phpce17 PHP Open Source projects 4175 contributors 418 contributors

    689 contributors 1497 contributors 497 contributors?
  2. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagName( "bar" )->length;
  3. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagName( "bar" )->length; // returns 2
  4. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagNameNS( "http://www.example.com", "bar" )->length;
  5. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagNameNS( "http://www.example.com", "bar" )->length; // returns 1
  6. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagNameNS( "", "bar" )->length;
  7. @arnoutboks #phpce17 Once upon a time… <something xmlns:foo="http://www.example.com"> <foo:bar />

    <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagNameNS( "", "bar" )->length; // returns 0!
  8. @arnoutboks #phpce17 Sharpen bug details Report different platform/version: Don’t post

    “me too” comments – Do post other relevant details
  9. @arnoutboks #phpce17 1. Install dependencies • git • build-essential •

    autoconf • automake • libtool • re2c • bison • libxml2-dev Using apt-get, yum or similar
  10. @arnoutboks #phpce17 3. Generate ./configure script $ ./buildconf Uses autoconf

    to build ./configure from: • configure.ac • acinclude.m4 • sapi/*/config.m4 • ext/*/config.m4
  11. @arnoutboks #phpce17 4. Run ./configure script $ ./configure Configure a

    default PHP build: $ ./configure --disable-cgi --disable-dom --enable-opcache --with-sqlite3=/path /to/sqlite3 Customize extensions/SAPIs: $ ./configure --help See all options
  12. @arnoutboks #phpce17 Cleanup scripts $ make clean # try again

    $ make distclean # try again $ ./buildconf –force $ ./configure # try again If compilation fails, try (subsequently): Might be needed after git pull/git checkout
  13. @arnoutboks #phpce17 Running tests $ make test […] PASS Trivial

    "Hello World" test [tests/basic/001.phpt] SKIP Check libcurl config on windows [ext/curl/tests/check_win_config.phpt] reason: for windows only FAIL Compiled regex cache limit [ext/pcre/tests/cache_limit.phpt] XFAIL Inconsistencies when accessing protected members [Zend/tests/access_modifiers_008.phpt] XFAIL REASON: Discussion: http://marc.info/?l=php- internals&m=120221184420957&w=2 […10000 more lines of output…]
  14. @arnoutboks #phpce17 Running tests $ make test […] ========================================= TEST

    RESULT SUMMARY ----------------------------------------- Number of tests : 14896 12520 Tests skipped : 2376 ( 16.0%) -------- Tests warned : 2 ( 0.0%) ( 0.0%) Tests failed : 175 ( 1.2%) ( 1.4%) Expected fail : 44 ( 0.3%) ( 0.4%) Tests passed : 12299 ( 82.6%) ( 98.2%) ----------------------------------------- Time taken : 2937 seconds
  15. @arnoutboks #phpce17 Running tests $ make test […] You may

    have found a problem in PHP. 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- [email protected] later. Do you want to send this report now? [Yns]:|
  16. @arnoutboks #phpce17 Options for running tests $ make test TESTS=path/to/my/test.phpt

    $ make test TESTS=ext/dom $ make test TESTS="--verbose ext/dom" $ make test TESTS=--help
  17. @arnoutboks #phpce17 Steps to reproduce for bug #67474 Test script:

    --------------- $doc = new DOMDocument(); $doc->loadXML('<root xmlns:x="x"><a/><x:a/></root>'); $list = $doc->getElementsByTagNameNS('', 'a'); echo $list->length; Expected result: ---------------- 1 Actual result: -------------- 0
  18. @arnoutboks #phpce17 Testing tools My PHP projects: • Written in

    PHP • Tests written in PHP (PHPUnit, PHPSpec, CodeCeption) The PHP core: • Written in C • Tests written in …
  19. @arnoutboks #phpce17 PHPT tests • Basically plaintext with sections •

    Some sections (can) contain PHP code • Let PHP print some output… • …and check against expected output
  20. @arnoutboks #phpce17 Very simple PHPT test --TEST-- Basic arithmetic -

    addition --FILE-- <?php var_dump(42 + 1); ?> --EXPECT-- int(43)
  21. @arnoutboks #phpce17 Very simple PHPT test --TEST-- Basic arithmetic -

    addition --FILE-- <?php var_dump(42 + 1); ?> --EXPECT-- int(43)
  22. @arnoutboks #phpce17 Folder structure tests/ and Zend/tests/ tests for the

    Zend engine ext/standard/tests /<group>/ tests for ‘standard’ extension (array, string, filesystem functions, etc.) ext/<name>/tests/ tests for extensions sapi/<name>/tests/ tests for specific SAPI’s (CGI, CLI, PHP-FPM, etc.)
  23. @arnoutboks #phpce17 Naming PHPT files bug<bugid>.phpt tests for bugs <func>_basic[<no>].phpt

    <func>_variation[<no>].phpt <func>_error[<no>].phpt tests for specific function: basic behavior, variations (edge cases, etc.) and errors <extname><no>.phpt general tests for extension
  24. @arnoutboks #phpce17 PHPT test for bug #67474 --TEST-- Bug #67474

    getElementsByTagNameNS and default namespace --FILE-- <?php $doc = new DOMDocument(); $doc->loadXML('<root xmlns:x="x"><a/><x:a/></root>'); $list = $doc->getElementsByTagNameNS('', 'a'); var_dump($list->length); ?> --EXPECT-- int(1)
  25. @arnoutboks #phpce17 Running our PHPT test $ make test TESTS=ext/dom/tests/bug67474.phpt

    […] ============================================= FAILED TEST SUMMARY --------------------------------------------- Bug #67474 getElementsByTagNameNS and default namespace [ext/dom/tests/bug67474.phpt] =============================================
  26. @arnoutboks #phpce17 Running our PHPT test Files generated for our

    failing test: bug67474.php contents of the FILE section bug67474.sh shell script for running the .php file bug67474.exp expected output bug67474.out actual output bug67474.diff diff between expected and actual bug67474.log log file, including the expected and actual output
  27. @arnoutboks #phpce17 Running our PHPT test $ cat ext/dom/tests/bug67474.log ----

    EXPECTED OUTPUT int(1) ---- ACTUAL OUTPUT int(0) ---- FAILED
  28. @arnoutboks #phpce17 Sections in PHPT files SKIPIF: Skips the test

    if the code in SKIPIF generates output containing ‘skip’ somewhere --SKIPIF-- <?php if(!extension_loaded('dom')) { die('skip dom extension not available'); } ?>
  29. @arnoutboks #phpce17 Sections in PHPT files Tip: Extensions usually have

    a skipif.inc shared between tests: --SKIPIF-- <?php require_once('skipif.inc'); ?>
  30. @arnoutboks #phpce17 Sections in PHPT files GET, POST, PUT, COOKIE,

    HEADERS: Simulate HTTP input to the PHP script in FILE --GET-- foo=bar&baz=qux --COOKIE-- foo=bar;baz=qux --FILE-- <?php var_dump($_GET['foo'] . $_COOKIE['foo']); ?> --EXPECT-- string(6) "barbar"
  31. @arnoutboks #phpce17 Sections in PHPT files EXPECTF, EXPECTREGEX: Expect the

    output to match a certain format --FILE-- <?php $fp = fopen(__FILE__, 'r'); var_dump($fp); ?> --EXPECTF-- resource(%d) of type (stream)
  32. @arnoutboks #phpce17 Sections in PHPT files CLEAN: Cleans up after

    the test --FILE-- <?php touch(__DIR__ . '/foo.tmp'); var_dump(file_exists(__DIR__ . '/foo.tmp')); ?> --EXPECT-- bool(true) --CLEAN-- <?php unlink(__DIR__ . '/foo.tmp'); ?>
  33. @arnoutboks #phpce17 Sections in PHPT files INI: Specifies custom php.ini

    directives for the test --INI-- precision=7 --FILE-- <?php var_dump(pi()); ?> --EXPECT-- float(3.141593)
  34. @arnoutboks #phpce17 Sections in PHPT files XFAIL: Indicates the test

    is expected to fail, and gives an explanation why This is usually used for hard-to-fix bugs or bugs in upstream code. --XFAIL-- See bug #xxxx
  35. @arnoutboks #phpce17 PHP TestFest 2017 • September-December 2017 • Participate

    through a local User Group or by yourself • Write tests for PHP core • Win prizes! See phptestfest.org
  36. @arnoutboks #phpce17 Resources • https://qa.php.net/write-test.php • https://www.sammyk.me/compiling-php-from-source- writing-tests-for-php-source • https://phptestfest.org/tutorials/

    • http://www.phpinternalsbook.com/#testing-php- source • https://github.com/herdphp/docker-phpqa • https://gist.github.com/SammyK/4a5cf70d0973731d0c 85b151a323ea2d
  37. @arnoutboks #phpce17 A shot at fixing #67474: recap <something xmlns:foo="http://www.example.com">

    <foo:bar /> <bar /> </something> <?php $doc = /* … (DOMDocument) */; $doc->getElementsByTagNameNS( "", "bar" )->length; // returns 0!
  38. @arnoutboks #phpce17 A shot at fixing #67474 php-src/ext/dom/ • attr.c

    • document.c • dom_ce.h • dom_fe.h • element.c • node.c • php_dom.c • xpath.c • …
  39. @arnoutboks #phpce17 A shot at fixing #67474 const zend_function_entry php_dom_document_class_functions[]

    = { // ... PHP_FALIAS(createAttributeNS, dom_document_create_attribute_ns, arginfo_dom_document_create_attribute_ns) PHP_FALIAS(getElementsByTagNameNS, dom_document_get_elements_by_tag_name_ns, arginfo_dom_document_get_elements_by_tag_name_ns) PHP_FALIAS(getElementById, dom_document_get_element_by_id, arginfo_dom_document_get_element_by_id) // ... PHP_ME(domdocument, __construct, arginfo_dom_document_construct, ZEND_ACC_PUBLIC) PHP_FE_END };
  40. @arnoutboks #phpce17 A shot at fixing #67474 const zend_function_entry php_dom_document_class_functions[]

    = { // ... PHP_FALIAS(createAttributeNS, dom_document_create_attribute_ns, arginfo_dom_document_create_attribute_ns) PHP_FALIAS(getElementsByTagNameNS, dom_document_get_elements_by_tag_name_ns, arginfo_dom_document_get_elements_by_tag_name_ns) PHP_FALIAS(getElementById, dom_document_get_element_by_id, arginfo_dom_document_get_element_by_id) // ... PHP_ME(domdocument, __construct, arginfo_dom_document_construct, ZEND_ACC_PUBLIC) PHP_FE_END };
  41. @arnoutboks #phpce17 A shot at fixing #67474 PHP_FUNCTION(dom_document_get_elements_by_tag_name_ns) { //

    ... if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &id, dom_document_class_entry, &uri, &uri_len, &name, &name_len) == FAILURE) { return; } // ... local = xmlCharStrndup(name, name_len); nsuri = xmlCharStrndup(uri, uri_len); dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri); }
  42. @arnoutboks #phpce17 zend_parse_parameters (‘ZPP’) PHP_FUNCTION(dom_document_get_elements_by_tag_name_ns) { // ... if (zend_parse_method_parameters(ZEND_NUM_ARGS(),

    getThis(), "Oss", &id, dom_document_class_entry, &uri, &uri_len, &name, &name_len) == FAILURE) { return; } // ... local = xmlCharStrndup(name, name_len); nsuri = xmlCharStrndup(uri, uri_len); dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri); } s: string s: string O: object of given class
  43. @arnoutboks #phpce17 zend_parse_parameters (‘ZPP’) Specifier Type a array b boolean

    d double/float l long/int o object (any type) O object (specific type) r resource s string z mixed
  44. @arnoutboks #phpce17 zend_parse_parameters (‘ZPP’) Modifier Effect * variable number of

    arguments (0 or more) + variable number of arguments (1 or more) ! optional argument | remaining arguments are optional See README.PARAMETER_PARSING_API
  45. @arnoutboks #phpce17 A shot at fixing #67474 PHP_FUNCTION(dom_document_get_elements_by_tag_name_ns) { //

    ... if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &id, dom_document_class_entry, &uri, &uri_len, &name, &name_len) == FAILURE) { return; } // ... local = xmlCharStrndup(name, name_len); nsuri = xmlCharStrndup(uri, uri_len); dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri); }
  46. @arnoutboks #phpce17 A shot at fixing #67474 xmlNode *dom_get_elements_by_tag_name_ns_raw( xmlNodePtr

    nodep, char *ns, char *local /* ... */ ) { // ... if ( ns == NULL || (nodep->ns != NULL && ( xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns) ))) { { } // ... } Eventually:
  47. @arnoutboks #phpce17 A shot at fixing #67474 xmlNode *dom_get_elements_by_tag_name_ns_raw( xmlNodePtr

    nodep, char *ns, char *local /* ... */ ) { // ... if ( ns == NULL || (nodep->ns != NULL && ( xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns) ))) { { } // ... } Eventually: element to ‘test’ filter namespace and local name
  48. @arnoutboks #phpce17 A shot at fixing #67474 xmlNode *dom_get_elements_by_tag_name_ns_raw( xmlNodePtr

    nodep, char *ns, char *local /* ... */ ) { // ... if ( ns == NULL || (nodep->ns != NULL && ( xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns) ))) { { } // ... } Eventually: “no namespace filter”
  49. @arnoutboks #phpce17 A shot at fixing #67474 xmlNode *dom_get_elements_by_tag_name_ns_raw( xmlNodePtr

    nodep, char *ns, char *local /* ... */ ) { // ... if ( ns == NULL || (nodep->ns != NULL && ( xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns) ))) { { } // ... } Eventually: “node has a namespace”, and “it matches the filter” or “filter is a wildcard”
  50. @arnoutboks #phpce17 A shot at fixing #67474 xmlNode *dom_get_elements_by_tag_name_ns_raw( xmlNodePtr

    nodep, char *ns, char *local /* ... */ ) { // ... if ( ns == NULL || (nodep->ns == NULL && !strcmp(ns, "")) || (nodep->ns != NULL && ( xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns) ))) { { } // ... } Fix: “node has empty namespace”, and “filter is empty namespace”
  51. @arnoutboks #phpce17 A shot at fixing #67474 $ make test

    TESTS=ext/dom/tests/bug67474.phpt […] ============================================= Running selected tests. PASS Bug #67474 getElementsByTagNameNS and default namespace [ext/dom/tests/bug67474.phpt] ============================================= Number of tests : 1 1 Tests passed : 1 (100.0%) (100.0%)
  52. @arnoutboks #phpce17 PR for #67474 Submit bug fixes to the

    oldest supported branch: http://php.net/supported-versions.php
  53. @arnoutboks #phpce17 It’s not THAT difficult • Read past the

    C details • See where variables come from and go to • Try and see what happens • Rely on the tests
  54. @arnoutboks #phpce17 Bug fixes vs. features • Bug fixes •

    PR against oldest supported branch • No RFC needed • Refer to bug in bug tracker • Features • PR against master • Formal RFC needed
  55. @arnoutboks #phpce17 RFC Process • Introduce idea, measure initial reaction

    • Formal proposal • Discussion period • Voting See https://wiki.php.net/rfc/howto & https://benramsey.com/talks/2015/06/dpc-contributing-core/
  56. @arnoutboks #phpce17 Resources • http://php.net/internals • https://wiki.php.net/internals • https://wiki.php.net/internals/references •

    http://www.phpinternalsbook.com/ • http://blog.jpauli.tech/ • https://nikic.github.io/ • https://blog.ircmaxell.com/search/label/PHP-Internals • https://www.sammyk.me/how-to-find-and-patch-a- bug-in-php-source-php-internals • https://externals.io
  57. @arnoutboks #phpce17 1. Find the correct file Under “All files”

    in the left menu Top-level directory per language • reference/<ext|group>/ • functions/<function>.xml • <class>.xml • <class>/<method>.xml • language/<feature>.xml
  58. @arnoutboks #phpce17 1. Find the correct file Tip: Use the

    “edit” link from the documentation itself
  59. @arnoutboks #phpce17 Don’t like edit.php.net? Or set up your own

    development environment: https://www.sammyk.me/how-to-contribute-to-php- documentation
  60. @arnoutboks #phpce17 Where to help? • Bugs of type “Doc”

    in bug tracker • check-missing-docs.php • “Failures to meet strict standards” (in edit.php.net sidebar) • Translations • Missing translations • Out-of-date translations • Translations needing review • Translations with errors
  61. @arnoutboks #phpce17 Numerous ways to contribute • Bug reports •

    Bug triaging • Running tests • Writing tests • Bug fixes • New features • Documentation patches • Documentation translations • Tools
  62. @arnoutboks #phpce17 Why contribute? • Get ‘own’ bugs fixed •

    Fix ‘own’ bugs • Make PHP better • Make PHP more popular • Learn PHP • Learn C • Give back to the community
  63. @arnoutboks #phpce17 Feedback & Questions @arnoutboks @arnoutboks @aboks Arnout Boks

    Please leave your feedback on joind.in: https://joind.in/talk/3e087
  64. @arnoutboks #phpce17 Image Credits • https://photojournal.jpl.nasa.gov/jpeg/PIA19058.jpg • https://www.flickr.com/photos/vivianejl/22990832 • https://www.flickr.com/photos/dhuiz/13609080315/

    • https://www.flickr.com/photos/gotovan/8671348572 • https://www.flickr.com/photos/sidelong/246816211 • https://www.flickr.com/photos/pezon64/23764268828 • https://www.flickr.com/photos/crdot/6855538268/ • https://www.flickr.com/photos/borkurdotnet/9682277597