iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
自我挑戰組

PHP 與 Laravel 雜記系列 第 8

PHP 關於 autoload

Autoload

PHP 可以透過 include、include_once、require、require_once 來將檔案引入, 一般來說以 OOP 寫法習慣, 一個檔案一個 class, 所以如果我們要用到 Car.php 的 Car class, 我們必須引入 Car.php

// index.php
include "Car.php";

$car = new Car();

這樣沒有問題, 但是如果我們有很多 class 要用時, 都要一個一個手動引入, 實在是有違工程師初心, 最好是用到的時候表面上可以直接用, 實際上系統幫我們自動引入相關 php 檔案, PHP 提供了一些 spl_autoload 相關函數來讓我們可以完成自動引入的功能

<?php
/*
spl_autoload_register(?callable $callback = null, bool $throw = true, bool $prepend = false): bool
註冊一個函數到 spl_autoload 佇列, 如果佇列還沒啟用就啟用
$callback: 欲註冊的自動引入函數, 如果沒有提供, 會自動註冊 spl_autoload 函數, 這是系統預設的自動引入函數
$throw: 無法註冊成功時, 是否拋出異常
$prepend: 若為 true, 表示註冊的函數放入佇列頭部, false, 放入尾部
概念: 當使用到未被定義的 class 或 interface 時, PHP 引擎會通過自動引入函數來引入所需要的 class, interface 
*/
// 假設 class 都放在 ./classes 目錄裡
// ./classes/Car.php
class Car
{
  public function start()
  {
    echo "car is starting";
  }
}

// index.php
function autoload($className)  $filename;
{
  $filename = __DIR__ . "/classes/" . $className . ".php";
  if (is_readable($filename)) {
    include $filename;
  } 
}

spl_autoload_register("autoload");
$car = new Car();
$car->start();

再搭配 namesapce 的使用可以, 實作出 PSR-4 的 Autoloader

<?php
// 自動引入類
class Psr4AutoLoad
{
    // 這裡面存放命名空間映射
    protected $maps = [];
    public function __construct()
    {
        spl_autoload_register([$this, 'autoload']);
    }

    // 自己寫的自動引入
    public function autoload($className)
    {
        // 完整的類名由命名空間名和類名組成
        // 得到命名空間名, 根據命名空間名得到其目錄路徑
        $pos = strrpos($className, '\\'); //找最右邊的'\'
        $namespace = substr($className, 0, $pos);
        // 得到類名
        $realClass = substr($className, $pos + 1);
        // 找到文件並且包含進來
        $this->mapLoad($namespace, $realClass);

    }

    // 根據命名空間名得到目錄路徑並且拼接真正的文件全路徑
    protected function mapLoad($namespace, $realClass)
    {
        if (array_key_exists($namespace, $this->maps)) {
            $namespace = $this->maps[$namespace];
        }

        // 處理路徑
        $namespace = rtrim(str_replace('\\/', '/', $namespace), '/') . '/';
        // 拼接文件全路徑
        $filePath = $namespace . $realClass . '.php';
        // 將該文件包含進來即可
        if (file_exists($filePath)) {
            include $filePath;
        } else {
            die('該文件不存在');
        }

    }

    // 給一個命名空間, 給一個路徑, 將命名空間和路徑保存到映射陣列中
    public function addMaps($namespace, $path)
    {
        if (array_key_exists($namespace, $this->maps)) {
            die('此命名空間已經映射過');
        }
        // 命名空間和路徑以鍵值對形式存放到陣列中
        $this->maps[$namespace] = $path;
    }
}
$autoloader = new Psr4AutoLoad();

上一篇
PHP 正則相關函數
下一篇
MVC 架構
系列文
PHP 與 Laravel 雜記14

尚未有邦友留言

立即登入留言