首页 > PHP > PHP命名空间namespace使用之spl_autoload

PHP命名空间namespace使用之spl_autoload

在github上很多项目都不会告诉你如何include或require他们的项目,有的项目会提供一个autoload.php文件直接引用即可(如Predis),有的项目什么也不会给,甚至在README中都不会给出require的代码(如Gaufrette)。毕竟国外的程序员都已经习以为常,使用autoload来包含项目非常容易。

下面,以自己写的一个小项目为例循序渐进讲解一下如果autoload项目文件。

项目地址:https://github.com/shuiguang/IpWatch

主要项目文件介绍:

IpWatch.class.php:类文件,IpWatch是主要用到的类。

FileCache.class.php:类文件,FileCache在IpWatch内部实例化。

RedisCache.class.php:类文件,RedisCache在IpWatch内部实例化。

其余的后面再说,传统方式使用只需要引用这三个文件即可使用本项目的所有功能,代码如下。

传统:

<?php
include 'IpWatch.class.php';
include 'FileCache.class.php';
include 'RedisCache.class.php';
use Shuiguang\IpWatch;
use Shuiguang\FileCache;
use Shuiguang\RedisCache;
$file_config = array(
    'interval' => 60,                       //最后一次请求到现在的间隔时间(s),如果大于此值则清除统计缓存
    'mode' => 'file',                       //可选file(文件记录),redis(需安装redis扩展)
    'type' => 'time',                       //可选number(数值记录),time(访问时间记录)
    'file_dir' => 'ip',                     //缓存文件路径,建议放在/tmp目录下或定时清除
    'prefix' => '',                         //缓存文件名前缀
    'suffix' => '.ip',                      //缓存文件名后缀
);
$ipWatcher = Shuiguang\IpWatch::getInstance($file_config);
//允许单个IP单位时间内最大访问次数
$max_request = 10;

//获取当前IP访问次数
$current = $ipWatcher->get();
//超过这个次数则退出脚本
if($current >= $max_request)
{
    die($current);
}else{
    $add = $ipWatcher->incr();
}

echo 'ok, go on';

使用传统的include或require方式:当项目文件数较小的时候,多处逻辑代码引用了这3个文件,如果后来该项目增加了其他很多的类文件,需要修改所有的逻辑代码,非常不便。

如果使用__autoload来定义include规则自动include类文件将会非常轻松。

进阶:

<?php
//自定义自动加载函数
function autoload($classname)
{
    $dir = './';
    if(is_file($dir.'/'.$classname.'.class.php'))
    {
        include $dir.'/'.$classname.'.class.php';
    }else{
        throw new Exception($dir.'/'.$classname.'.class.php not exists');
    }
}
//注册自动加载函数
spl_autoload_register('autoload');

use Shuiguang\IpWatch;
use Shuiguang\FileCache;
use Shuiguang\RedisCache;
$file_config = array(
    'interval' => 60,                       //最后一次请求到现在的间隔时间(s),如果大于此值则清除统计缓存
    'mode' => 'file',                       //可选file(文件记录),redis(需安装redis扩展)
    'type' => 'time',                       //可选number(数值记录),time(访问时间记录)
    'file_dir' => 'ip',                     //缓存文件路径,建议放在/tmp目录下或定时清除
    'prefix' => '',                         //缓存文件名前缀
    'suffix' => '.ip',                      //缓存文件名后缀
);
$ipWatcher = Shuiguang\IpWatch::getInstance($file_config);
//允许单个IP单位时间内最大访问次数
$max_request = 10;

//获取当前IP访问次数
$current = $ipWatcher->get();
//超过这个次数则退出脚本
if($current >= $max_request)
{
    die($current);
}else{
    $add = $ipWatcher->incr();
}

echo 'ok, go on';

然而这样的方式并不是非常合适,一方面autoload函数将会作用于全局这样并不好,另一方面autoload函数中的.class.php文件路径是固定的,而且无法使用命名空间、子空间的优势。

从Predis项目中可以找到一个这样的类:Autoloader.php。(本项目中已经将类文件重命名为Autoloader.class.php)。

优化:

<?php
/*
 * This file is part of IpWatch.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 * IpWatch for php
 * @author shuiguang
 * @link https://github.com/shuiguang/IpWatch
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */
namespace Shuiguang;

class Autoloader
{
    private $directory;
    private $prefix;
    private $prefixLength;

    /**
     * @param string $baseDirectory Base directory where the source files are located.
     */
    public function __construct($baseDirectory = __DIR__)
    {
        $this->directory = $baseDirectory;
        $this->prefix = __NAMESPACE__ . '\\';
        $this->prefixLength = strlen($this->prefix);
    }

    /**
     * Registers the autoloader class with the PHP SPL autoloader.
     *
     * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
     */
    public static function register($prepend = false)
    {
        spl_autoload_register(array(new self, 'autoload'), true, $prepend);
    }

    /**
     * Loads a class from a file using its fully qualified name.
     *
     * @param string $className Fully qualified name of a class.
     */
    public function autoload($className)
    {
        if (0 === strpos($className, $this->prefix)) {
            $parts = explode('\\', substr($className, $this->prefixLength));
            $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.class.php';
            if (is_file($filepath)) {
                require($filepath);
            }
        }
    }
}

该类中主要使用了上一篇文章《PHP命名空间namespace使用》中介绍的__NAMESPACE__自动获取命名空间(文件夹名)和子命名空间(子文件夹名),然后通过autoload.php文件中的Shuiguang\Autoloader::register();进行注册。原理大致相同,但是实现方式非常优雅,所有的逻辑程序文件可引入autoload.php后直接使用即可,代码如下。

<?php    
/*    
 * This file is part of IpWatch.    
 *    
 * Licensed under The MIT License    
 * For full copyright and license information, please see the MIT-LICENSE.txt    
 * Redistributions of files must retain the above copyright notice.    
 * IpWatch for php    
 * @author shuiguang    
 * @link https://github.com/shuiguang/IpWatch    
 * @license http://www.opensource.org/licenses/mit-license.php MIT License    
 */    
require __DIR__ . '/src/Shuiguang/Autoloader.class.php';    
Shuiguang\Autoloader::register();

逻辑代码如下:

include 'autoload.php';
use代码
调用代码

如果以后需要添加类文件,放在src/Shuiguang下然后use即可,无需手动导入。


案例:

从github上下载最新版的Gaufrette,官方未给出任何自动加载的教程,需要我们自己实现。

解压之后,项目文件存在于Gaufrette-master\src\Gaufrette目录下,将predis中的Autoloader.php文件复制到Gaufrette-master\src\Gaufrette目录下,并且修改为namespace Gaufrette;

<?php
namespace Gaufrette;
/**
 * Implements a lightweight PSR-0 compliant autoloader for Predis.
 *
 * @author Eric Naeseth <eric@thumbtack.com>
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class Autoloader
{
    private $directory;
    private $prefix;
    private $prefixLength;
    /**
     * @param string $baseDirectory Base directory where the source files are located.
     */
    public function __construct($baseDirectory = __DIR__)
    {
        $this->directory = $baseDirectory;
        $this->prefix = __NAMESPACE__.'\\';
        $this->prefixLength = strlen($this->prefix);
    }
    /**
     * Registers the autoloader class with the PHP SPL autoloader.
     *
     * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
     */
    public static function register($prepend = false)
    {
        spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
    }
    /**
     * Loads a class from a file using its fully qualified name.
     *
     * @param string $className Fully qualified name of a class.
     */
    public function autoload($className)
    {
        if (0 === strpos($className, $this->prefix)) {
            $parts = explode('\\', substr($className, $this->prefixLength));
            $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
            if (is_file($filepath)) {
                require $filepath;
            }
        }
    }
}

然后到Gaufrette-master目录建立一个autoload.php文件,该文件位置可任意放置,只需要保证require的路径正确即可。

<?php
require __DIR__ . '/src/Gaufrette/Autoloader.php';
Gaufrette\Autoloader::register();

最后,到逻辑代码处引用这个autoload.php文件即可,例如。

<?php
include 'autoload.php';

use Gaufrette\Filesystem;
use Gaufrette\Adapter\Local as LocalAdapter;

$adapter = new LocalAdapter(__DIR__);
$filesystem = new Filesystem($adapter);

$content = 'Hello I am the new content';

$filesystem->write('myFile.txt', $content);

完成引入该项目。

PS:外国人不喜欢使用.class.php作为类文件的后缀,通常直接使用.php文件名,其关键代码在Autoloader.php文件的这一行,怎么选取决于自己的习惯:

$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';

案例下载:

Gaufrette-master.zip


教程链接:http://blog.zhengshuiguang.com/php/spl_autoload.html

随意转载~但请保留教程地址★

标签:命名空间 自动加载 autoload gaufrette

相关文章

评论已关闭