2018年9月4日 星期二

PHP中的自動加載

目的:
new一個class時不需要寫require。 PHP 5.2 新增自動加載的魔術方法__autoload($class_name)

一、__autoload()的使用

__autoload.php
$db = new DB();

Height::test();  //也是支持静态方法直接调用的

function __autoload($className)
{
    require $className . '.php';
}
DB.php(同個目錄下)
class DB
{
    public function __construct()
    {
        echo 'Hello DB';
    }
}
Height.php(同個目錄下)
class Height
{
    public function __construct()
    {
        echo 'Hello Height';
    }

    public static function test(){
     echo 'Hello Height test';
    }
}

二、spl_autoload_register 使用

__autoload()不允許加載不同路徑的文件,因為一個項目中只能有一個__autoload()。因此spl_autoload_register()誕生。當我們new一個找不到的class時,PHP會自動調用spl_autoload_register註冊的函數
class autoloading{
    //必须是静态方法,不然报错
    public static function load($className)
    {
        require $className . '.php';
    }
}
//2种方法都可以
spl_autoload_register(array('autoloading','load')); 
// spl_autoload_register('autoloading::load');
$db = new DB(); //会自动找到

三、多个spl_autoload_register的使用

function load1($className)
{
    echo 1;
    if (is_file($className . '.php')) {
        require $className . '.php';
    }
}
function load2($className)
{
    echo 2;
    if (is_file('./app/' . $className . '.php')) {
        require './app/' . $className . '.php';
    }
}
function __autoload($className)
{
    echo 3;
    if (is_file('./lib/' . $className . '.php')) {
        require './lib/' . $className . '.php';
    }
}
//注册了3个
spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('__autoload');
$db = new DB(); //DB就在本目录下
$info = new LibInfo(); //LibInfo 在/lib/LibInfo.php

var_dump(spl_autoload_functions());

輸出:
1
Hello DB
1
2
3
Hello LibInfo
array (size=3)
  0 => string 'load1' (length=5)
  1 => string 'load2' (length=5)
  2 => string '__autoload' (length=10)

使用spl_autoload_functions() 來查一共註冊了多少個自動加載
spl_autoload_register()使用時,__autoload()會無效,必須重新註冊進來才有效

四、spl_autoload_register自動加載+namespace命名空間

根據 PSR-0 的規範,使用namespace就能找到詳細的路徑,從而找到class文件
spl_autoload_register_namespace.php
//定义当前的目录绝对路径
define('DIR', dirname(__FILE__));
//加载这个文件
require DIR . '/loading.php';
//采用`命名空间`的方式注册。php 5.3 加入的
//也必须是得是static静态方法调用,然后就像加载namespace的方式调用,注意:不能使用use
spl_autoload_register("\\AutoLoading\\loading::autoload"); 

$oMySQL = new Libary\Db\MySQL();
$oMySQL->bear();

// 调用2个namespace类
//定位到Libary目录下的Name.php 
Libary\Name::test();
//定位到App目录下Android目录下的Name.php
App\Android\Name::test();

loading.php
namespace AutoLoading;
class loading {
    public static function autoload($className) {
        //根据PSR-O的第4点 把 \ 转换层(目录风格符) DIRECTORY_SEPARATOR ,
        //便于兼容Linux文件找。Windows 下(/ 和 \)是通用的
        //由于namspace 很规格,所以直接很快就能找到
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, DIR . '\\' . $className) . '.php';
        if (is_file($fileName)) {
            require $fileName;
        } else {
            echo $fileName . ' is not exist';die;
        }
    }
}

Libary/Db/MySQL.php
namespace Libary\Db;
use Libary\Name; // use放這
class MySQL
{
    public function __construct()
    {
        echo __NAMESPACE__ . "";
    }
    public static function test()
    {
        echo  __NAMESPACE__ . ' static function test ';
    }
    public function bear(){
        Name::getInstance(); // 呼叫 Libary/Name.php 的 static function getInstance()
    }
}

Libary/Name.php
namespace Libary;
use Libary\Db\MySQL; // use放在這
class Name
{
    public function __construct()
    {
        echo __NAMESPACE__ . "";
    }
    public static function test()
    {
        // $oMySQL = new Libary\Db\MySQL();  // D:\bear\www\test\autoload\Libary\Libary\Db\MySQL.php is not exist
        // $oMySQL = new Db\MySQL(); // OK
        // use Libary\Db\MySQL; // Parse error: syntax error, unexpected 'use' (T_USE)
        $oMySQL = new MySQL(); // 打印第二個 Libary\Db
        echo  __NAMESPACE__ . ' static function test ';
    }
    public static function getInstance(){
        echo __NAMESPACE__ . ":getInstance";
    }
}

App/Android/Name.php
namespace App\Android;
class Name
{
    public function __construct()
    {
        echo __NAMESPACE__ . "";
    }
    public static function test()
    {
        echo  __NAMESPACE__ . ' static function test ';
    }
}

輸出:
Libary\Db
Libary:getInstance
Libary\Db
Libary static function test
App\Android static function test

s


...using __autoload() is discouraged and may be deprecated or removed in the future.
使用 __autoload() 是將會被棄用的(PHP 7.2.0),因為只能使用一次。應使用spl_autoload_register() 

use Libary\Db\MySQL;  // use 使用絕對路徑,放在class外面,namespace後面

參考資料:
https://www.zybuluo.com/phper/note/66447  PHP中的自动加载
https://stackoverflow.com/questions/12702527/how-to-call-autoload-in-php-code  How to call __autoload() in php code
https://stackoverflow.com/questions/33342994/unexpected-use-t-use-when-trying-to-use-composer  unexpected 'use' (T_USE) 
https://www.php-fig.org/psr/psr-0/  PSR-0: Autoloading Standard

沒有留言:

張貼留言