• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 名称解析规则

    (PHP 5 >= 5.3.0, PHP 7)

    在说明名称解析规则之前,我们先看一些重要的定义:

    命名空间名称定义
    非限定名称Unqualified name

    名称中不包含命名空间分隔符的标识符,例如Foo

    限定名称Qualified name

    名称中含有命名空间分隔符的标识符,例如FooBar

    完全限定名称Fully qualified name

    名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如FooBarnamespaceFoo也是一个完全限定名称。

    名称解析遵循下列规则:

    1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如new AB解析为类AB
    2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间ABC被导入为C,那么对CDe()的调用就会被转换为ABCDe()
    3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间AB内部调用CDe(),则CDe()会被转换为ABCDe()
    4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间ABC导入为C,则new C()被转换为new ABC()
    5. 在命名空间内部(例如AB),对非限定名称的函数调用是在运行时解析的。例如对函数foo()的调用是这样解析的:
      1. 在当前命名空间中查找名为ABfoo()的函数
      2. 尝试查找并调用全局(global)空间中的函数foo()
    6. 在命名空间(例如AB)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用new C()new DE()的解析过程:new C()的解析:
      1. 在当前命名空间中查找ABC类。
      2. 尝试自动装载类ABC
      new DE()的解析:
      1. 在类名称前面加上当前命名空间名称变成:ABDE,然后查找该类。
      2. 尝试自动装载类ABDE
      为了引用全局命名空间中的全局类,必须使用完全限定名称new C()

    Example #1 名称解析示例

    <?php
    namespace A;
    use B\D, C\E as F;
    // 函数调用
    foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
                // 再尝试调用全局函数 "foo"
    \foo();     // 调用全局空间函数 "foo" 
    my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 
    F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
                // 再尝试调用全局函数 "F"
    // 类引用
    new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
                // 如果未找到,则尝试自动装载类 "A\B"
    new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
                // 如果未找到,则尝试自动装载类 "B\D"
    new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
                // 如果未找到,则尝试自动装载类 "C\E"
    new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
                // 如果未发现,则尝试自动装载类 "B"
    new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
                // 如果未发现,则尝试自动装载类 "D"
    new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
                // 如果未发现,则尝试自动装载类 "F"
    // 调用另一个命名空间中的静态方法或命名空间函数
    B\foo();    // 调用命名空间 "A\B" 中函数 "foo"
    B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
                // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"
    D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
                // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"
    \B\foo();   // 调用命名空间 "B" 中的函数 "foo" 
    \B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
                // 如果类 "B" 未找到,则尝试自动装载类 "B"
    // 当前命名空间中的静态方法或函数
    A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
                  // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"
    \A\B::foo();  // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法
                  // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
    ?>
    
    If you like to declare an __autoload function within a namespace or class, use the spl_autoload_register() function to register it and it will work fine.
    The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:
    ->Say you have the following directory structure:
    - root
       | - loader.php 
       | - ns
           | - foo.php
    ->foo.php
    <?php
    namespace ns;
    class foo
    {
      public $say;
      
      public function __construct()
      {
        $this->say = "bar";
      }
      
    }
    ?>
    -> loader.php
    <?php
    //GLOBAL SPACE <--
    function __autoload($c)
    {
      require_once $c . ".php";
    }
    class foo extends ns\foo // ns\foo is loaded here
    {
      public function __construct()
      {
        parent::__construct();
        echo "<br />foo" . $this->say;
      }
    }
    $a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
    echo $a->say;  // prints bar as expected.
    $b = new foo; // prints foobar just fine.
    ?>
    If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
    But... if you try to give loader.php a namespace you'll obviously get fatal errors. 
    My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.
    Cheers!
    For point 4, "In example, if the namespace A\B\C is imported as C" should be "In example, if the class A\B\C is imported as C".
    As working with namespaces and using (custom or basic) autoload structure; magic function __autoload must be defined in global scope, not in a namespace, also not in another function or method.
    <?php
    namespace Glue {
      /**
       * Define your custom structure and algorithms
       * for autoloading in this class.
       */
      class Import
      {
        public static function load ($classname)
        {
          echo 'Autoloading class '.$classname."\n";
          require_once $classname.'.php';
        }
      }
    }
    /**
     * Define function __autoload in global namespace.
     */
    namespace {
      
      function __autoload ($classname)
      {
        \Glue\Import::load($classname);
      }
    }
    ?>
    
    The mentioned filesystem analogy fails at an important point:
    Namespace resolution *only* works at declaration time. The compiler fixates all namespace/class references as absolute paths, like creating absolute symlinks.
    You can't expect relative symlinks, which should be evaluated during access -> during PHP runtime.
    In other words, namespaces are evaluated like __CLASS__ or self:: at parse-time. What's *not* happening, is the pendant for late static binding like static:: which resolves to the current class at runtime.
    So you can't do the following:
    namespace Alpha;
    class Helper {
      public static $Value = "ALPHA";
    }
    class Base {
      public static function Write() { 
        echo Helper::$Value;
      }
    }
    namespace Beta;
    class Helper extends \Alpha\Helper {
      public static $Value = 'BETA';
    }  
    class Base extends \Alpha\Base {}  
    \Beta\Base::Write(); // should write "BETA" as this is the executing namespace context at runtime.
    If you copy the write() function into \Beta\Base it works as expected.
    Namespaces may be case-insensitive, but autoloaders most often do.
    Do yourself a service, keep your cases consistent with file names, and don't overcomplicate autoloaders beyond necessity.
    Something like this should suffice for most times:
    <?php
    namespace org\example;
    function spl_autoload($className)
    {
     $file = new \SplFileInfo(__DIR__ . substr(strtr("$className.php", '\\', '/'), 11));
     $path = $file->getRealPath();
     if(empty($path))
     {
      return false;
     }
     else
     {
      return include_once $path;
     }
    }
    \spl_autoload_register('\org\example\spl_autoload');
    ?>
    
    The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:
    ->Say you have the following directory structure:
    - root
       | - loader.php 
       | - ns
           | - foo.php
    ->foo.php
    <?php
    namespace ns;
    class foo
    {
      public $say;
      
      public function __construct()
      {
        $this->say = "bar";
      }
      
    }
    ?>
    -> loader.php
    <?php
    //GLOBAL SPACE <--
    function __autoload($c)
    {
      require_once $c . ".php";
    }
    class foo extends ns\foo // ns\foo is loaded here
    {
      public function __construct()
      {
        parent::__construct();
        echo "<br />foo" . $this->say;
      }
    }
    $a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
    echo $a->say;  // prints bar as expected.
    $b = new foo; // prints foobar just fine.
    ?>
    If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
    But... if you try to give loader.php a namespace you'll obviously get fatal errors. 
    My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.
    Cheers!
    It took me playing with it a bit as I had a hard time finding documentation on when a class name matches a namespace, if that's even legal and what behavior to expect. It IS explained in #6 but I thought I'd share this with other souls like me that see it better by example. Assume all 3 files below are in the same directory.
    file1.php
    <?php
    namespace foo;
    class foo {
     static function hello() {
      echo "hello world!";
     }
    }
    ?>
    file2.php
    <?php
    namespace foo; 
    include('file1.php');
    foo::hello(); //you're in the same namespace, or scope.
    \foo\foo::hello(); //called on a global scope.
    ?>
    file3.php
    <?php
    include('file1.php');
    foo\foo::hello(); //you're outside of the namespace
    \foo\foo::hello(); //called on a global scope.
    ?>
    Depending upon what you're building (example: a module, plugin, or package on a larger application), sometimes declaring a class that matches a namespace makes sense or may even be required. Just be aware that if you try to reference any class that shares the same namespace, omit the namespace unless you do it globally like the examples above.
    I hope this is useful, particularly for those that are trying to wrap your head around this 5.3 feature.
    Can someone explain to me - why do we need p.4 if we have p.2 (which covers both unqualified and qualified names)?