通过命名空间实现自动加载的框架雏形

来源:admin  更新:2019-08-30 15:49  分类:网络技术  标签:php  源文件

了解命名空间

namespace是PHP5.3版本加入的新特性,用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

PHP 命名空间中的元素使用了类似文件系统的原理(同一目录下不允许有两个相同名称的文件)。例如,类名可以通过三种方式引用:

  1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespacefoo 将被解析为 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。 另外注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen()\Exception\INI_ALL如果代码是在命名空间内,那基本上是必须以完全限定名称来声明使用

<?php
use My\Full\Classname as Another, My\Full\NSname; //别名导入,如果不用as就是最后一级的名称

$obj = new Another; // 实例化一个 My\Full\Classname 对象(非限定名称)
$obj = new \Another; // 实例化一个Another对象(完全限定名称)
$obj = new Another\thing; // 实例化一个My\Full\Classname\thing对象(限定名称,限定最后一级)
$obj = new \Another\thing; // 实例化一个Another\thing对象(完全限定名称)

$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

命名空间的使用及use引入。有些小伙伴意思大概明白,也很好理解,命名空间还好说,给这个空间起个名字嘛,但是use具体操作起来就不行 了,怎么也搞不通。后来看一个yii学习视频才恍然明白。

比方说建三个文件。

第一个文件 A.php,里边有两个类,命名空间是 a\b\c;

第二个文件 B.php 命名空间 a\b\d;

第三个文件 index.php ,用来使用上边两个文件的类。

比方说我们现在想实例化A.php里的类,该怎么实现呢?

首先要包含这个文件

require_once('A.php');

然后 use a\b\c;? 还是a\b\c\A? 起初我也是这么认为的。其实这是错的,use应该是这样的,命名空间\这个空间下你要实例化类的类名。比方说我们要实例化 A.php里的Apply类,那么 use a\b\c\Apply; 这就相当于引入这个类,接下来再 new Apply();调用里边的方法,跟平常是一样的。如果要实例化类C,就 use a\b\c\C;

注意:use不等于require_once或者include,use的前提是已经把文件包含进当前文件。

顺便提一句,在MVC模式中,类名和文件名是相同的,所以use的时候会让不了解的人以为use后面跟的是文件名,我之前就这么以为的。其实use的还是类名。

有的人可能问了,那我不同的命名空间下,有相同的类名,在同一个文件中使用怎么办?比方说我们上边的index.php,把A.php和B.php都包含进来,然后new Apply();这个时候是会报错的,解决方案就是起别名,比方说 use a\b\d\Apply as b; 此时我们new 的时候不应该写成 new Apply();而是new b();这样就不会发生冲突了。

PSR-0 与 PSR-4

PSR是由PHP Framework Interoperability Group(PHP通用性框架小组)发布的一系列标准/规范,目前官方发布文件版本

PSR-0(已弃用)
PSR-1 基本代码规范
PSR-2 代码风格规范
PSR-2 补充文档
PSR-3 日志接口规范
PSR-4 自动载入
PSR-5和PSR-6投票还未通过

PSR-0就是其中的自动加载标准,其后的PSR-4称为改进的自动加载的标准,是PSR-0的补充。所以,PSR-0虽然被弃用了,但是我们还是了解一下。

  1. 命名空间必须与绝对路径一致
  2. 类名首字母必须大写,并且与文件名相同
  3. 除去入口文件外,其他“.php”必须只有一个类
  4. php类文件必须自动载入,不采用include等
  5. 单一入口

[info] 命名空间还为PHP-FIG制定的PSR-4自动加载标准奠定了坚实的基础,现在的框架基本上都是基于psr-4来加载(内部spl_autoload_register加载时,通过命名空间知道其具体的绝对路径), 其核心就是 作为“命名空间前缀”,其必须与至少一个“文件基目录”相对应; 所谓的文件基目录:

  1. 文件根目录
  2. omposer.json里面定义的psr-4,比如psr-4定义了/app, 那/app/service/class1.php 其命名空间就是app/service; new app/service/class1 都可以自动加载
  3. 框架中提前定义好的,比如tp5中的extend目录, extend/libs/log.php 其命名空间可以直接是libs,因为其在自动加载时会自动加上extend目录,来加载

一个简单的PSR-0的框架模型

\index.php

<?php
define('BASEURL' , __DIR__);
require_once(BASEURL . '/Config/Loader.php');
spl_autoload_register('\\Config\\Loader::autoload');
Controller\Home\index::tbb();

Config\Loader.php

<?php
namespace Config;
class Loader{
  public function test(){
    echo __DIR__ , "\n"; // 通过Config\Loader::test()调用;由此可见已经和路径一致,更便于代码的阅读
  }

  //需要被spl_autoload_register注册的autoloader函数必须是static;
  static function autoload($class){
    //echo $class; // 连命名空间也一起打印出来的,所以根据这个命名空间的特性再加上PSR-0规范的基础上可以做类自动加载是很方便的;
    $class = BASEURL . '/' . str_replace('\\' , '/' , $class) . '.php'; //Linux系统的目录都是以/来分割的,同时windows系统虽然以\来分割目录,但也识别/来分割,可以做一个替换;
    require_once($class);
  }
}

Controller\Home\index

<?php
namespace Controller\Home;
class index{
  public function tbb(){
    echo 'i am tbb,hahahah~';
  }
}

版权声明嗨网博客部分文章源自网络收集,不代表嗨网立场,如涉及侵权请联系嗨网删除。
其他若无特别说明则为嗨网原创文章、持续更新。未授权媒体、微信公众号不得使用嗨网内容。 个人自媒体可署名、保留原始链接的情况下转载
转载请注明 来源嗨网higrid.net,链接: https://higrid.net/posts/php_autoload.html
本站为非盈利网站,作品由网友提供上传,如无意中有侵犯您的版权,请联系删除