php命名空间及和autoload结合使用问题。

时间:2022-05-04 10:25:00

在讨论如何使用命名空间之前,必须了解 PHP 是如何知道要使用哪一个命名空间中的元素的。可以将 PHP 命名空间与文件系统作一个简单的类比。在文件系统中访问一个文件有三种方式:

  1. 相对文件名形式如foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为/home/foo/foo.txt
  2. 相对路径名形式如subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt
  3. 绝对路径名形式如/main/foo.txt。它会被解析为/main/foo.txt

PHP 命名空间中的元素使用同样的原理。例如,类名可以通过三种方式引用:

  1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。详情参见 使用命名空间:后备全局函数名称/常量名称
  2. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo
  3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo

下面是一个使用这三种方式的实例:

file1.php

<?php
namespace Foo\Bar\subnamespace; const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>

file2.php

<?php
namespace Foo\Bar;
include 'file1.php'; const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
} /* 非限定名称 */
foo(); // 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo的静态方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO /* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
// 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO /* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>

注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL

在命名空间内部访问全局类、函数和常量

<?php
namespace Foo; //*命名空间 function strlen() {}
const INI_ALL = 3;
class Exception {} $a = \strlen('hi'); // 调用全局函数strlen,不加\调用自己定义的。
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

转自:http://php.net/manual/zh/language.namespaces.basics.php

说明,php命名空间也可以使用:

namespace Myproject {

}
这种形式定义。
起别名。
use Myproject/School as School1;   // 别名
 定义多个命名空间,简单组合语法
<?php
namespace MyProject; const CONNECT_OK = ;
class Connection { /* ... */ }
function connect() { /* ... */ } namespace AnotherProject; const CONNECT_OK = ;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>

不建议使用这种语法在单个文件中定义多个命名空间。建议使用下面的大括号形式的语法。

<?php
namespace MyProject { const CONNECT_OK = ;
class Connection { /* ... */ }
function connect() { /* ... */ }
} namespace AnotherProject { const CONNECT_OK = ;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
?>

在实际的编程实践中,非常不提倡在同一个文件中定义多个命名空间。这种方式的主要用于将多个 PHP 脚本合并在同一个文件中

将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。全局代码必须用一个不带名称的 namespace 语句加上大括号括起来,例如:

定义多个命名空间和不包含在命名空间中的代码
<?php
namespace MyProject { const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
} namespace { // global code
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

很奇怪的一点。

file1.php

<?php
namespace Foo\Bar\subnamespace; class foo
{
static function staticmethod(){ print "file1:staticmethod"."<br/>";}
}

userFile1.php

<?php
use Foo\Bar\subnamespace;
require("file1.php");
$fooObj=new foo();
$fooObj->staticmethod();

运行错误:

Fatal error: Class 'foo' not found in F:\xampp\htdocs\php\pattern\namespace\userFile1.php

但是改为:

<?php
use Foo\Bar\subnamespace;
require("file1.php");
$fooObj=new Foo\Bar\subnamespace\foo();
$fooObj->staticmethod();

就正常了。

好像这里的use Foo\Bar\subnamespace;没什么用,我去掉后还是正常。

这里用了use和没有user没什么区别嘛!我换成下面一种形式,不要在类前面加namespace,也可以。

<?php
use Foo\Bar\subnamespace\foo; //命名空间后加类名
require("file1.php");
$fooObj=new foo();
$fooObj->staticmethod();

这个和autoload使用:

<?php
use Library\Controller\FrontController; function __autoload($className)
{ require_once($className.'.php'); } $frontController=new FrontController(array(
"Controller"=>'Test',
'action'=>'show',
'params'=>array()
)); $frontController->run();

FrontController在当前文件目录下定义,文件名和类名相同。但是报错:

Fatal error: require_once(): Failed opening required 'Library\Controller\FrontController.php' (include_path='.;F:\xampp\php\PEAR;F:\xampp\zend\library') inF:\xampp\htdocs\php\pattern\frontController\index.php

在autoload里面require的是

Library\Controller\FrontController

而不是:

FrontController

.

说明__autoload function will receive the full class-name, including the namespace name.参考:

http://*.com/questions/1830917/how-do-i-use-php-namespaces-with-autoload

看到

风雪 之隅的一篇文章:

PHP的命名空间, 实现的还真是简单.

当你有如下代码:

  1. <?php
  2. namespace Yaf;
  3. class Application {
  4. }

其实就相当于, 你声明了一个名字为A\B的类, 当然, 你不能直接这么申明(只是不能在PHP脚本中这么申明).

而在使用过程中, 根据PHP手册中命名空间一节所述, 执行时刻, 无论你是使用use, 还是直接写相对名字, 绝对名字, 最后去class表查询的时候, 都是被转换成了最终的类似上面的类名.

比如:

  1. <?php
  2. use Yaf as A;
  3. $a = new A\Application(); //转换为了Yaf\Application

不能不说, PHP的这种命名空间解决方案 ,是代价最小, 影响最小的解决方案.

对于扩展开发者来说, 这种影响更是微小, 你只需要在INIT_CLASS_ENTRY的时刻, 把原来的形如”Yaf_Application”变为”Yaf\\Application”, 就可以实现自己的命名空间.

http://www.laruence.com/2010/10/12/1763.html

php manual里有重要的将命名空间是如何解析的:

http://www.php.net/manual/zh/language.namespaces.rules.php

名称解析规则

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

命名空间名称定义

非限定名称Unqualified name

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

限定名称Qualified name

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

完全限定名称Fully qualified name

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

名称解析遵循下列规则:

  1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B
  2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为A\B\C\D\e()
  3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为A\B\C\D\e() 。
  4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 
  5. 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:
    1. 在当前命名空间中查找名为 A\B\foo() 的函数
    2. 尝试查找并调用 全局(global) 空间中的函数 foo()
  6. 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C()的解析:
    1. 在当前命名空间中查找A\B\C类。
    2. 尝试自动装载类A\B\C

    new D\E()的解析:

    1. 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
    2. 尝试自动装载类 A\B\D\E

    为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()

。。。后面没有理解。

一篇非常好的系列文章;

http://www.sitepoint.com/php-53-namespaces-basics/