文字

命名空间和动态语言特征

(PHP 5 >= 5.3.0)

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


$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
?>

请一定别忘了阅读 对字符串中的命名空间名称转义的注解.

用户评论:

[#1] akhoondi+php at gmail dot com [2013-08-09 16:17:21]

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 
{
    function 
__construct()
    {
        echo 
__METHOD__ "<br />\n";
    }
}
class 
{
    function 
__construct()
    {
        echo 
__METHOD__ "<br />\n";
    }
}
?>


global.php
<?php 
// global.php
class {
    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 ...
?>

[#2] Alexander Kirk [2011-07-06 00:57:08]

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 
{
    public function 
factory() {
        return new 
C;
    }
}
class 
{
    public function 
tell() {
        echo 
"foo";
    }
}
?>


<?php // File2.php
namespace bar;
class 
extends \foo\{}
class 
{
    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 
{
    protected 
$namespace __NAMESPACE__;
    public function 
factory() {
        
$c $this->namespace '\C';
        return new 
$c;
    }
}
class 
{
    public function 
tell() {
        echo 
"foo";
    }
}
?>


<?php // File2.php
namespace bar;
class 
extends \foo\{
    protected 
$namespace __NAMESPACE__;
}
class 
{
    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)

[#3] scott at intothewild dot ca [2009-08-07 15:33:34]

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

?>

[#4] guilhermeblanco at php dot net [2009-06-16 12:04:00]

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

?>

上一篇: 下一篇: