diff options
-rwxr-xr-x | README.namespaces | 174 | ||||
-rw-r--r-- | ext/tokenizer/tokenizer_data.c | 6 |
2 files changed, 180 insertions, 0 deletions
diff --git a/README.namespaces b/README.namespaces new file mode 100755 index 0000000000..80321f2ce4 --- /dev/null +++ b/README.namespaces @@ -0,0 +1,174 @@ +Design +====== + +Main assumption of the model is that the problem that we are to solve is the +problem of the very long class names in PHP libraries. We would not attempt +to take autoloader's job or create packaging model - only make names +manageable. + +Namespaces are defined the following way: + +Zend/DB/Connection.php: +<?php +namespace Zend::DB; + +class Connection { +} + +function connect() { +} +?> + +Namespace definition does the following: +All class and function names inside are automatically prefixed with +namespace name. Inside namespace, local name always takes precedence over +global name. Several files may be using the same namespace. +The namespace declaration statement must be the very first statement in +the file. The only exception is "declare" statement that can be used before. + +Every class and function in a namespace can be referred to by the full name +- e.g. Zend::DB::Connection or Zend::DB::connect - at any time. + +<?php +require 'Zend/Db/Connection.php'; +$x = new Zend::DB::Connection; +Zend::DB::connect(); +?> + +Namespace or class name can be imported: + +<?php +require 'Zend/Db/Connection.php'; +import Zend::DB; +import Zend::DB::Connection as DbConnection; + +$x = new Zend::DB::Connection(); +$y = new DB::connection(); +$z = new DbConnection(); +DB::connect(); +?> + +import statement only defines name aliasing. It may create name alias for +namespace or class. The simple form of statement "import A::B::C::D;" is +equivalent to "import A::B::C::D as D;". Import statement can be used at any +time in the global scope (not inside function/class) and takes effect from +the point of definition down to the end of file. It is recommended however to +place imports at the beginning of the file. Import statements have effect +only on the file where they appear. + +The special "empty" namespace (:: prefix) is useful as explicit global +namespace qualification. All class and function names started from :: +interpreted as global. + +<?php +namespace A::B::C; + +$con = ::mysql_connect(...); +?> + +A special constant __NAMESPACE__ contains the name of the current namespace. +It can be used to construct fully-qualified names to pass them as callbacks. + +<?php +namespace A::B::C; + +function foo() { +} + +set_error_handler(__NAMESPACE__ . "::foo"); +?> + +In global namespace __NAMESPACE__ constant has the value of empty string. + +Names inside namespace are resolved according to the following rules: + +1) all qualified names are translated during compilation according to +current import rules. So if we have "import A::B::C" and then "C::D::e()" +it is translated to "A::B::C::D::e()". +2) unqualified class names translated during compilation according to +current import rules. So if we have "import A::B::C" and then "new C()" it +is translated to "new A::B::C()". +3) inside namespace, calls to unqualified functions that are defined in +current namespace (and are known at the time the call is parsed) are +interpreted as calls to these namespace functions. +4) inside namespace, calls to unqualified functions that are not defined +in current namespace are resolved at run-time. The call to function foo() +inside namespace (A::B) first tries to find and call function from current +namespace A::B::foo() and if it doesn't exist PHP tries to call internal +function foo(). Note that using foo() inside namespace you can call only +internal PHP functions, however using ::foo() you are able to call any +function from the global namespace. +5) unqualified class names are resolved at run-time. E.q. "new Exception()" +first tries to use (and autoload) class from current namespace and in case +of failure uses internal PHP class. Note that using "new A" in namespace +you can only create class from this namespace or internal PHP class, however +using "new ::A" you are able to create any class from the global namespace. +6) Calls to qualified functions are resolved at run-time. Call to +A::B::foo() first tries to call function foo() from namespace A::B, then +it tries to find class A::B (__autoload() it if necessary) and call its +static method foo() +7) qualified class names are interpreted as class from corresponding +namespace. So "new A::B::C()" refers to class C from namespace A::B. + +Examples +-------- +<?php +namespace A; +foo(); // first tries to call "foo" defined in namespace "A" + // then calls internal function "foo" +::foo(); // calls function "foo" defined in global scope +?> + +<?php +namespace A; +new B(); // first tries to create object of class "B" defined in namespace "A" + // then creates object of internal class "B" +new ::B(); // creates object of class "B" defined in global scope +?> + +<?php +namespace A; +new A(); // first tries to create object of class "A" from namespace "A" (A::A) + // then creates object of internal class "A" +?> + +<?php +namespace A; +B::foo(); // first tries to call function "foo" from namespace "A::B" + // then calls method "foo" of internal class "B" +::B::foo(); // first tries to call function "foo" from namespace "B" + // then calls method "foo" of class "B" from global scope +?> + +The worst case if class name conflicts with namespace name +<?php +namespace A; +A::foo(); // first tries to call function "foo" from namespace "A::A" + // then tries to call method "foo" of class "A" from namespace "A" + // then tries to call function "foo" from namespace "A" + // then calls method "foo" of internal class "A" +::A::foo(); // first tries to call function "foo" from namespace "A" + // then calls method "foo" of class "A" from global scope +?> + +TODO +==== + +* Support for namespace constants? + +* performance problems + - calls to internal functions in namespaces are slower, because PHP first + looks for such function in current namespace + - calls to static methods are slower, because PHP first tries to look + for corresponding function in namespace + +* Extend the Reflection API? + * Add ReflectionNamespace class + + getName() + + getClasses() + + getFunctions() + + getFiles() + * Add getNamespace() methods to ReflectionClass and ReflectionFunction + +* Rename namespaces to packages? + diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index 392c37bb7a..1f48d9d72a 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -147,6 +147,9 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("T_DOLLAR_OPEN_CURLY_BRACES", T_DOLLAR_OPEN_CURLY_BRACES, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CURLY_OPEN", T_CURLY_OPEN, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_PAAMAYIM_NEKUDOTAYIM", T_PAAMAYIM_NEKUDOTAYIM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_NAMESPACE", T_NAMESPACE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_IMPORT", T_IMPORT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_NS_C", T_NS_C, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_DOUBLE_COLON", T_PAAMAYIM_NEKUDOTAYIM, CONST_CS | CONST_PERSISTENT); } @@ -272,6 +275,9 @@ char *get_token_type_name(int token_type) case T_DOLLAR_OPEN_CURLY_BRACES: return "T_DOLLAR_OPEN_CURLY_BRACES"; case T_CURLY_OPEN: return "T_CURLY_OPEN"; case T_PAAMAYIM_NEKUDOTAYIM: return "T_DOUBLE_COLON"; + case T_NAMESPACE: return "T_NAMESPACE"; + case T_IMPORT: return "T_IMPORT"; + case T_NS_C: return "T_NS_C"; } return "UNKNOWN"; |