• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 命名空间和动态语言特征

    (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