rfc:backslashnamespaces

# Differences

This shows you the differences between two versions of the page.

 rfc:backslashnamespaces [2008/10/25 13:54]cellog fix error in Lukas's understanding of Classname->method() rfc:backslashnamespaces [2017/09/22 13:28] Line 1: Line 1: - ====== Request for Comments: problems of namespaces and potential solutions ====== - * Version: 1.1 - * Date: 2008-10-25 - * Author: Gregory Beaver <​cellog@php.net>​ - * Status: Draft - * First Published at: http://​wiki.php.net/​rfc/​backslashnamespaces - - - This RFC attempts to document all angles of the problems in namespaces in one location in order to facilitate a solution - - ===== Introduction ===== - - The amount of pain and grief all interested developers have undergone to perfect namespaces has got to end.  This RFC provides a close examination of the four potentially viable solutions and the major problems and advantages of each. - - First, ways of solving the conflict between namespaced elements and static class elements: - - * remove functions/​constants from namespaces - * conflict resolution via "use namespace"​ or other similar proposals - * introduce -> as Classname->​Method() syntax - * use \ as namespace separator - - This RFC assumes that since we are in agreement about how to resolve class names, that it is not in need of further discussion. ​ To be clear, the agreed upon resolution order is: - - * check nsname::​classname - * autoload nsname::​classname if necessary - * fail - - which requires all internal classes to be ::prefixed (or \prefixed if the separator is changed to \). - - ===== Remove functions/​constants from namespaces ===== - - ==== Advantages ==== - - * no naming conflicts possible between functions/​static methods, class constants/​namespace constants - * no syntax changes for those using namespaces for classes - * probably the smallest codebase change - - ==== Problems ==== - - * how to handle - - - - ​ - - put function in global namespace or fatal error? (code becomes more confusing) - - * those using PHP for functions get no benefit from namespaces, and significant headache - * implementation is quite complex because functions can be defined multiple ways at runtime as well as compile-time. - - ===== Conflict resolution via "use namespace"​ or other similar proposals ===== - - Unfortunately,​ this is not an option. ​ Here is the background: - - This is actually only implementable in one way: - - * Define a clear resolution order in favor of classes or in favor of namespaced elements - * Require the others to explicitly define an element as a class or as a namespace: - - If we favor classes: - - - - ​ - - and - - - - ​ - - If we favor namespaces: - - - - ​ - - and - - - - ​ - - ==== Advantages ==== - - * conflicts can be controlled explicitly via "use namespace"​ or "use class" - * resolution is clear if access to the whole file is available - - ==== Problems ==== - - * unqualified name (Blah::​blah::​thing or ::​Blah::​blah::​thing) is unavailable for elements accessed via "use namespace/​class"​ syntax, short name must always be used - * Code review (checking patches for errors) becomes impossible - one must always have full access to the entire file.  This is necessary at places like Google, where separate managers review commits of underlings and "​import/​use"​ is forbidden. - - In my opinion, the 2nd problem renders this solution and any like it completely unusable. ​ We need a solution that allows ::​fully::​delimited::​names. - - ===== Introduce -> as Classname->​Method() syntax ===== - - At first look, this appears to be an option, but as I explain below, the ambiguity between static class elements and namespaced elements is deeply embedded in the current implementation,​ and is not possible to resolve, thus disqualifying any solution that preserves :: as separator. ​ However, disregarding those reasons, here are the ways to consider this option: - - This could be done in a couple of ways: - - The first way (which was Stas's proposal): - - * allow -> as an alternate syntax - - - The second way (which was Lukas'​s understanding of Stas's proposal): - - * move all static method calls to -> syntax - * introduce E_STRICT (or E_DEPRECATED) for :: calls that resolve to static method in PHP 6, but allow both syntaxes without error in PHP 5.3 - - The second way is going to be a terrible headache for all OO authors, past and present, and will prevent migration to PHP 5.3 for those who are only looking for security fixes and performance improvements. ​ As such, it can be safely vetoed as impractical. ​ The next section assumes we are talking about Stas's original proposal, which is to allow this syntax: - - - ​bar();​ // "​bar"​ - ​ - - and this syntax in case of name conflict: - - - ​bar();​ // "​bar"​ - ​ - - The same is true for constants: - - - ​BAR;​ // 1 - ​ - - - ==== Advantages ==== - - * if classname->​method() is used, it solves the ambiguity issue - * Definitely the smallest codebase change - - ==== Problems ==== - - * if classname::​method() is used, the ambiguity problem still exists - * all existing code uses classname::​method() or classname::​CONST. ​ Thus, to be protected from the ambiguity, all existing OO code would need to - be modified. - * The syntax $blah::​hello() is introduced in PHP 5.3, and is equivalent to blah::​hello() if$blah = '​blah';​ blah::​hello() is equivalent to blah->​hello(),​ but $blah::​hello() is not equivalent to$blah->​hello(),​ and the same is true of constants - it introduces another logical failure path for new users - * it redefines a long-established definition of ->, which is not necessarily a problem, but could be confusing for both existing and new users - * only educated users would ever think to look for -> unless a massive PR campaign is mounted, which would also make PHP look disorganized. ​ This is simply a political and NOT a technical problem. - * does not solve the deeper problems inherent in :: - - ===== Use \ as namespace separator ===== - - Before discussing the advantages and problems, it is prudent to look at some of the intrinsic problems in the implementation of namespaces with :: - as the separator before continuing. - - ==== The big, non-obvious problems with :: as separator ==== - - ==== Resolution of T_STRING T_PAAMAYIM_NEKUDOTAYIM T_STRING and why this kills :: as namespace separator ==== - - With current CVS of PHP_5_3, these two code samples work very differently:​ - - file1.php: - - ​ - - main.php: - - include '​file1.php';​ - class foo { - static function bar(){echo "​method\n";​} - } - foo::bar(); // func - ?> - ​ - - - - ​ - - The actual line of code "​foo::​bar()"​ does not change, but the addition of a namespace to the file changes the name resolution. ​ In global code, "​foo::​bar()"​ always refers to namespaced function "​foo::​bar." ​ Inside of a namespace declaration,​ "​foo::​bar()"​ always refers to a class method.  ​ - - Although this may appear on the surface to simply be a name resolution bug, the problem is much deeper. ​ PHP performs resolution at run-time for namespaced functions and for class methods. ​ Basically, the opcode will check for two different scenarios: - - 1) in namespaced code: - * check for "​foo::​bar"​ function [fail] - * check for "​foo::​foo"​ class [succeed] - - 2) in global code: - * check for "​foo::​bar"​ function [succeed] - - Note that inside the namespaced code, there is no attempt to check for [nsname]::​foo::​bar (foo::​foo::​bar). ​ In order to implement this, we would need to have potentially 4 hash lookups for every static method call: - - * check for function nsname::​[called thing] - * check for function [called thing] - * check for class nsname::​[called thing] - * check for class [called thing] - - In this list, [called thing] is the way the method/​function is called, so in the code sample above, it would be "​foo::​bar",​ and we would check for: - - * check for function foo::​foo::​bar - * check for function foo::bar - * check for class foo::​foo::​bar - * check for class foo::bar - - This would potentially double the autoload lookups for classes, and thus is actually impossible to solve correctly with the current implementation. - - The only solution is to re-define how name lookup works, such that it is different for straight "​blah()"​ or "new blah" vs. "​that::​way()"​ or "new that::​way." ​ However, because we use :: for the namespace separator, it is literally impossible to decide whether "​that::​way()"​ should be treated as our unqualified "​way"​ or as qualified "​that::​way." ​ The only way to solve this problem is to use a different namespace separator. - - ==== inefficiency due to ambiguity ==== - - The current implementation of namespaced functions and static methods requires code execution to go through the same location. ​ The same is true of class constants vs. namespaced constants. ​ For every class static method and class static constant, there is at least 1 extra hash table lookup, regardless of whether any namespaced classes/​constants are defined. - - As a side note, PHP 5.3 is so much faster than PHP 5.2, this is not a worthwhile comparison, and is not the best base to compare against, so I will be instead comparing PHP 5.3 CVS vs. a patched PHP 5.3 with a different namespace separator. - - If another namespace separator is used, every static method call has 1 fewer hash table lookup, and every constant resolution saves potentially 2 hash table lookups (internal details can be explained if you'd like - nothing is more complicated under the hood than the way constants are resolved, so I will leave those out of this RFC).  In addition, it is possible to perform much more efficient parsing of namespaced function calls than it is under the current implementation,​ because zend_do_begin_function_call() can be used instead of zend_do_begin_class_member_function_call(),​ which is much more efficient due to checks in class_member_function_call that are unnecessary for namespaced functions, but absolutely necessary if the namespace separator is ::. - - ==== Code review gotchas ==== - - Neither human review nor automated tools can look at: - - - - and successfully resolve what it is in fact doing. ​ This will hamper automated security auditing tools, code correctness tools, and human review of patches, creating a situation that makes debugging and designing robust PHP code harder than it is now.  This can only be corrected via a new namespace separator. - - ===== Description of what it means to use \ as namespace separator ===== - - The main issues that using \ as namespace separator can address are: - - * name ambiguity between static class methods/​constants and namespace functions/​constants - * name resolution order differences and gotchas of foo::bar() in a namespace vs. in global code - * code review problems - - ==== Advantages ==== - - * name ambiguity is impossible - * \ is visually quite different from ::, and is easier to scan and detect as namespace instead of static class operator - * \ is a single keystroke on U.S. keyboard layout without shift key - * \this\is\used for paths on Windows and is intuitively familiar to those developers. ​ According to a php|arch survey (as relayed by Steph Fox), most of their readers develop on Windows and deploy on Unix, which would imply that \these\paths are familiar - * \this\maps\to\filesystem layouts often used by autoload intuitively for the above reason - * because \ is a single keystroke, it is possible to require \ prefix for all global functions/​classes/​constants,​ and conversion is minimally effortful (from personal experience trying to convert files to several namespace separators to see what it was like) - * code review ambiguities disappear permanently - * name resolution order problems disappear if we decide that "​foo/​bar"​ or "​foo::​bar"​ is always prefixed with namespace name, and only short names like "​bar()"​ or "new bar" are checked for both "​nsname/​bar()"​ and internal function "​bar()"​ or "class nsname/​bar"​ and internal class "​bar"​. ​ "/​foo/​bar"​ is used for global scoping. - * code coverage of namespace-related code is so good, it is possible to be very confident of the correctness of the patch. - - ==== Problems ==== - - * \ looks a lot like / and is easy to accidentally flip, especially for unix users - * \ is used for escaping - * inside a string a namespace name becomes\\like\\this or we can get weird characters. ​ This could be confusing to users at first. - * all existing namespaced code must be nearly rewritten, a simple search/​replace of :: would not be possible. - * the patch touches a lot of the engine, and will need rigorous battle-testing. - * to many, \this\way will look weird at first. - - ==== Patch ==== - - A patch against current CVS of PHP 5.3 is available at http://​pear.php.net/​~greg/​backslash.sep.patch.txt - - It is worth noting that 95% of this patch took me 2 hours to write, the constant resolution code took me an entire week, and the unit test modifications took only 1-2 hours, so porting this to HEAD should be pretty simple, but the initial work was non-trivial and I hope that the final solution could be based off of this work. - - ==== Rejected Features ==== - - * ::: as namespace separator - * namespace member nsname::​is::​here->​classname::​method() - - ===== Changelog ===== - - * 1.1: Correct mis-representation of Lukas'​s understanding of Classname->​method()