命名空间和动态语言特征
(PHP 5 >= 5.3.0, PHP 7)
PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中:
Example #1 动态访问元素
example1.php:
<?php class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname = "global"; $a = 'classname'; $obj = new $a; // prints classname::__construct $b = 'funcname'; $b(); // prints funcname echo constant('constname'), "\n"; // prints global ?>必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。
Example #2 动态访问命名空间的元素
<?php namespace namespacename; class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname = "namespaced"; include 'example1.php'; $a = 'classname'; $obj = new $a; // prints classname::__construct $b = 'funcname'; $b(); // prints funcname echo constant('constname'), "\n"; // prints global /* note that if using double quotes, "\\namespacename\\classname" must be used */ $a = '\namespacename\classname'; $obj = new $a; // prints namespacename\classname::__construct $a = 'namespacename\classname'; $obj = new $a; // also prints namespacename\classname::__construct $b = 'namespacename\funcname'; $b(); // prints namespacename\funcname $b = '\namespacename\funcname'; $b(); // also prints namespacename\funcname echo constant('\namespacename\constname'), "\n"; // prints namespaced echo constant('namespacename\constname'), "\n"; // also prints namespaced ?>
请一定别忘了阅读对字符串中的命名空间名称转义的注解.
When extending a class from another namespace that should instantiate a class from within the current namespace, you need to pass on the namespace. <?php // File1.php namespace foo; class A { public function factory() { return new C; } } class C { public function tell() { echo "foo"; } } ?> <?php // File2.php namespace bar; class B extends \foo\A {} class C { public function tell() { echo "bar"; } } ?> <?php include "File1.php"; include "File2.php"; $b = new bar\B; $c = $b->factory(); $c->tell(); // "foo" but you want "bar" ?> You need to do it like this: When extending a class from another namespace that should instantiate a class from within the current namespace, you need to pass on the namespace. <?php // File1.php namespace foo; class A { protected $namespace = __NAMESPACE__; public function factory() { $c = $this->namespace . '\C'; return new $c; } } class C { public function tell() { echo "foo"; } } ?> <?php // File2.php namespace bar; class B extends \foo\A { protected $namespace = __NAMESPACE__; } class C { public function tell() { echo "bar"; } } ?> <?php include "File1.php"; include "File2.php"; $b = new bar\B; $c = $b->factory(); $c->tell(); // "bar" ?> (it seems that the namespace-backslashes are stripped from the source code in the preview, maybe it works in the main view. If not: fooA was written as \foo\A and barB as bar\B)
Please be aware of FQCN (Full Qualified Class Name) point. Many people will have troubles with this: <?php // File1.php namespace foo; class Bar { ... } function factory($class) { return new $class; } // File2.php $bar = \foo\factory('Bar'); // Will try to instantiate \Bar, not \foo\Bar ?> To fix that, and also incorporate a 2 step namespace resolution, you can check for \ as first char of $class, and if not present, build manually the FQCN: <?php // File1.php namespace foo; function factory($class) { if ($class[0] != '\\') { echo '->'; $class = '\\' . __NAMESPACE__ . '\\' . $class; } return new $class(); } // File2.php $bar = \foo\factory('Bar'); // Will correctly instantiate \foo\Bar $bar2 = \foo\factory('\anotherfoo\Bar'); // Wil correctly instantiate \anotherfoo\Bar ?>
Case you are trying call a static method that's the way to go: <?php class myClass { public static function myMethod() { return "You did it!\n"; } } $foo = "myClass"; $bar = "myMethod"; echo $foo::$bar(); // prints "You did it!"; ?>
It might make it more clear if said this way: One must note that when using a dynamic class name, function name or constant name, the "current namespace", as in http://www.php.net/manual/en/language.namespaces.basics.php is global namespace. One situation that dynamic class names are used is in 'factory' pattern. Thus, add the desired namespace of your target class before the variable name. namespaced.php <?php // namespaced.php namespace Mypackage; class Foo { public function factory($name, $global = FALSE) { if ($global) $class = $name; else $class = 'Mypackage\\' . $name; return new $class; } } class A { function __construct() { echo __METHOD__ . "<br />\n"; } } class B { function __construct() { echo __METHOD__ . "<br />\n"; } } ?> global.php <?php // global.php class A { function __construct() { echo __METHOD__; } } ?> index.php <?php // index.php namespace Mypackage; include('namespaced.php'); include('global.php'); $foo = new Foo(); $a = $foo->factory('A'); // Mypackage\A::__construct $b = $foo->factory('B'); // Mypackage\B::__construct $a2 = $foo->factory('A',TRUE); // A::__construct $b2 = $foo->factory('B',TRUE); // Will produce : Fatal error: Class 'B' not found in ...namespaced.php on line ... ?>
Be careful when using dynamic accessing namespaced elements. If you use double-quote backslashes will be parsed as escape character. <?php $a="\namespacename\classname"; //Invalid use and Fatal error. $a="\\namespacename\\classname"; //Valid use. $a='\namespacename\classname'; //Valid use. ?>
as noted by guilhermeblanco at php dot net, <?php // fact.php namespace foo; class fact { public function create($class) { return new $class(); } } ?> <?php // bar.php namespace foo; class bar { ... } ?> <?php // index.php namespace foo; include('fact.php'); $foofact = new fact(); $bar = $foofact->create('bar'); // attempts to create \bar // even though foofact and // bar reside in \foo ?>
Important to know is that you need to use the *fully qualified name* in a dynamic class name. Here is an example that emphasizes the difference between a dynamic class name and a normal class name. <?php namespace namespacename\foo; class classname { function __construct() { echo 'bar'; } } $a = '\namespacename\foo\classname'; // Works, is fully qualified name $b = 'namespacename\foo\classname'; // Works, is treated as it was with a prefixed "\" $c = 'foo\classname'; // Will not work, it should be the fully qualified name // Use dynamic class name new $a; // bar new $b; // bar new $c; // [500]: / - Uncaught Error: Class 'foo\classname' not found in // Use normal class name new \namespacename\foo\classname; // bar new namespacename\foo\classname; // [500]: / - Uncaught Error: Class 'namespacename\foo\namespacename\foo\classname' not found new foo\classname; // [500]: / - Uncaught Error: Class 'namespacename\foo\foo\classname' not found