php自動加載

php的自動加載

自動加載原理

咱們使用另外一個文件定義的一個class的時候,傳統的狀況下須要require XXX.phpphp

//Good.php
<?php
  class Good{
    //......
  }
 
//use.php
<?php
  require 'Good.php';   //包含進來文件    
  $good = new Good();   //如今可使用Good類

可是寫require真的很煩!!如今可使用一個自動加載函數,發現類沒有被定義的時候自動根據咱們定義的規則幫咱們去require須要的文件。git

//Good.php
<?php
  class Good{
    //......
  }

//use.php
<?php
function __autoload($className){
  require $className . '.php';  //定義包含的規則
}

$good = new Good(); //可使用Good類,發現沒有定義,會自動調用__autoload函數,如今'Good'做爲參數傳入了__autoload函數,去包含須要的文件

spl_autoload_register

__autoload函數不能解決全部的問題,如今的項目已經很是複雜,光一個autoload函數已經不能知足須要了,由於可能不一樣的模塊使用了不一樣的自動加載規則。github

spl_autoload_register將函數註冊到SPL autoload函數隊列中,它實際上建立了 __autoload 函數的隊列,按定義時的順序逐個執行。相比之下,__ autoload() 只能夠定義一次。web

函數原型

bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
  • autoload_functionsql

    欲註冊的自動裝載函數。若是沒有提供任何參數,則自動註冊 autoload 的默認實現函數spl_autoload()。app

  • throw函數

    此參數設置了 autoload_function 沒法成功註冊時, spl_autoload_register()是否拋出異常。ui

  • prependthis

    若是是 true,spl_autoload_register() 會添加函數到隊列之首,而不是隊列尾部。spa

函數使用

sql_autoload_resister('load_function'); //函數名
sql_autoload_resister(array('load_object', 'load_function')); //類和靜態方法
sql_autoload_resister('load_object::load_function'); //類和方法的靜態調用

//php 5.3以後,也能夠像這樣支持匿名函數了。
spl_autoload_register(function($className){
    if (is_file('./lib/' . $className . '.php')) {
        require './lib/' . $className . '.php';
    }
});

note:使用了spl_autoload_register以後原來的__autoload函數就失效了,若是須要繼續使用,須要顯示的註冊它

if (function_exists('__autoload')) {
   spl_autoload_register('__autoload');
 }

//。。。。。。

 spl_autoload_register('your_autoload_function');   //如今註冊另外一個

是否繼續在自動加載函數隊列中查找不取決於加載函數的返回值和是否require了文件,只有真正require須要的文件纔會中止

//Good.php位於app文件夾下
<?php
  class Good{
    //......
  }


//Another.php,位於use同級文件夾下
<?php
class Another{
}


//use.php
<?php
function load1($className)
{
    echo "load1\n";
    if (file_exists($className.'.php')) {
        echo 'found: '.$className.PHP_EOL;
        require $className;
    } else {
        echo 'not found: '.$className.PHP_EOL;
    }
}

function load2($className)
{
    echo "load2\n";
    require 'Another.php';
    echo 'require another class'.PHP_EOL;
    return true;
}

function load3($className)
{
    echo "load3\n";
    if (file_exists('app/'.$className.'.php')) {
        echo 'found '.$className.PHP_EOL;
        require 'app/'.$className.'.php';
    } else {
        echo 'not found: '.$className.PHP_EOL;
    }
}


spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('load3');

$a = new Good();

輸出以下

load1
not found: Good
load2
require another class
load3
found Good

可使用spl_autoload_functions()得到註冊的全部函數

Array
(
    [0] => load1
    [1] => load2
    [2] => load3
)

取消註冊的自動加載函數spl_autoload_unregister

spl_autoload_unregister('load1');

spl_autoload()是__autoload()函數的默認實現

提供了autoload()的一個默認實現。若是不使用任何參數調用 spl_autoload_register() 函數,則之後在進行 autoload() 調用時會自動使用此函數。

自動加載和命名空間

PSR-4自動加載規範

完整的規範請參考PSR-4: Autoloader

標準的類名應該具備下列形式

\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

須要強調的:

  • 完整的類名中任意一部分中的下滑線都是沒有特殊含義的
  • 全部類名都必須是大小寫敏感的
  • 全部類名都必須是大小寫敏感的

當根據命名空間載入文件時候:

  • 完整的類名中,去掉最前面的命名空間分隔符,前面連續的一個或多個命名空間和子命名空間,做爲「命名空間前綴」,其必須與至少一個「文件基目錄」相對應。
  • 緊接命名空間前綴後的子命名空間必須與相應的」文件基目錄「相匹配,其中的命名空間分隔符將做爲目錄分隔符。
  • 末尾的類名必須與對應的以 .php 爲後綴的文件同名。
  • 自動加載器(autoloader)的實現必定不能拋出異常、必定不能觸發任一級別的錯誤信息以及不該該有返回值。

例子

完整類名 命名空間前綴 文件基目錄 文件路徑
\Acme\Log\Writer\File_Writer Acme\Log\Writer ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\Status Aura\Web /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php
\Symfony\Core\Request Symfony\Core ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php
\Zend\Acl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php

實現示例

採用了匿名函數的方式

<?php
/**
 * An example of a project-specific implementation.
 *
 * After registering this autoload function with SPL, the following line
 * would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
 * from /path/to/project/src/Baz/Qux.php:
 *
 *      new \Foo\Bar\Baz\Qux;
 *
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {
    // project-specific namespace prefix
    $prefix = 'Foo\\Bar\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/src/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    //下邊這一行很關鍵
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }

});

參考資料:

相關文章
相關標籤/搜索