at 2007-07-21
in Further reading
by friebe
(0 comments)
Beginning of this month, Dmitry Stogov from Zend published a patch implementing namespaces in PHP6.
To be able to begin playing around with it without adding further points of possible failures for our XP unittests with new PHP6 (e.g., unicode-related breakage), I started and after a day's work succeeded in backporting the patch to current CVS HEAD, that is, PHP 5.2.4-dev.Here's a list of problems encountered backporting the patch:
Unicode API PHP6 has a two types of strings, unicode and binary (the latter is what PHP5 has today). Therefore, any place handling strings in PHP's C sourcecode must test for both of them and handle them differently. The patch contains numerous places which look like this:
if (Z_TYPE_P(callable) == IS_UNICODE && Z_USTRVAL_P(callable)[0] == ':' && Z_USTRVAL_P(callable)[1] == ':') { lmname = zend_u_str_case_fold(IS_UNICODE, (zstr)(Z_USTRVAL_P(callable)+2), Z_USTRLEN_P(callable)-2, 1, &mlen); } else if (Z_TYPE_P(callable) == IS_STRING && Z_STRVAL_P(callable)[0] == ':' && Z_STRVAL_P(callable)[1] == ':') { lmname = zend_u_str_case_fold(IS_STRING, (zstr)(Z_STRVAL_P(callable)+2), Z_STRLEN_P(callable)-2, 1, &mlen); } ...thus significantly bloating the patch (and the effort to develop it, in the first place! - but that's another story ) This makes it impossible to apply the patch directly and turns backporting it into a tedious task, having to reduce the above to the second branch (IS_STRING) and removing ifs where only IS_UNICODE tests are done.
Additionally, other unicode artifacts need refactoring - for example, zend_u_str_case_fold (not existant in PHP5 API) to zend_str_tolower_dup; zstr*s must become char*s, and the unions involving these must be removed. Here's an example: if (Z_TYPE(class_name->u.constant) == IS_UNICODE) { compound.u = u_memchr(Z_USTRVAL(class_name->u.constant), ':', Z_USTRLEN(class_name->u.constant)); } else { compound.s = memchr(Z_STRVAL(class_name->u.constant), ':', Z_STRLEN(class_name->u.constant)); } if (compound.v) { /* ... */ } and what became of it in PHP5: compound = memchr(Z_STRVAL(class_name->u.constant), ':', Z_STRLEN(class_name->u.constant)); if (compound) { /* ... */ } Line number changes Because of the unicode changes in PHP6 and extensive source lines shifts resulting of them, it proved to be quite difficult to find the correct places to patch in some situations.
Missing documentation As always and any place in PHP, inline documentation is rare to find - the almost 2800 lines patch contains exactly one new inline comment. It took me quite a while to understand the inner workings of the patch.
WTF Several cases in the original patch cannot possibly work, or I am missing something. So for example the code inside zend_is_callable_check_func (which is responsbile of checking if a given value is callable) do not consider the ::foo() case (which should call the global function foo()) or the ::ClassName (which should look up a class ClassName inside the main namespace). For both situations, .phpt files were supplied. Note: I did not check the PHP6 implementation because I have so far not succeeded in compiling PHP6 due to missing requirements, so I'm not sure... but the relevant pieces of source still look suspicious.
General C rant
len= 2 + Z_STRLEN(name->u.constant); Z_STRVAL(result->u.constant)= (char*) safe_emalloc(len+ 1, 1, 1); Z_STRVAL(result->u.constant)[0]= ':'; Z_STRVAL(result->u.constant)[1]= ':'; memcpy(Z_STRVAL(result->u.constant)+2, Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant)+1); Z_STRVAL(result->u.constant)[len]= '\0';
Don't you just hate this? Wait, here's the PHP version:
<?php $result= '::'.$name; ?>
Inner workings Here's some key figures:
Compile Time- The compiler prefixes declarations by prepending the current namespace name.
- Prefixing happens in class and interface declarations, function declarations, and catch blocks and uses zend_do_build_namespace_name()
- A list of current imports is kept in a hashtable called CG(current_import)
- When a class constant is encountered (e.g. new Exception(), the name is resolved to its fully qualified name using zend_resolve_class_name()
Runtime- Statements such as new $class or $func(); cannot be resolved at compile time and are deferred until runtime
- For function and static method calls, everything is done at runtime. This is to resolve ambigouos statements such as foo::bar::baz(); which may either be a function call to a function baz() inside the namespace foo::bar or a static member call to a class bar (inside namespace foo)'s static method baz.
Success! After a day's work I finally succeeded in running all .phpt tests Dmitry had written. Here's the patch:
Download PHP5 namespaces patch
It should apply cleanly against PHP's PHP_5_2 branch. Recompile (ensure you've excuted make clean before, some of the Zend structs have changed!) and you're set up with a PHP5 with PHP6 namespace support!
|
Subscribe
You can subscribe to the XP framework's news by using RSS syndication.
CategoriesNews General PHP5 Announcements RFCs Further reading Examples Editorial EASC Experiments Unittests Databases
RelatedFind related articles by a search for «PHP6».
|