National Chengchi University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

政治大學 AQI 58 25°C PM2.5 12
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 15

命名空間

namespace 與 use,完整名稱與別名,自動載入與 Composer
Interaction實作導覽 20 minute(s)

Learning Objectives

  • 說明為什麼需要命名空間:不同套件的類別、函式可能同名
  • 用 namespace 宣告命名空間,分辨非限定名稱、限定名稱與以 \ 開頭的完整名稱
  • 用 use 匯入類別、用 as 取別名,用 use function/use const 匯入函式與常數
  • 說明在命名空間裡呼叫函式找不到時會退回全域,類別則不會(要寫 \Exception 或 use)
  • 比較 include、require 與 _once 版本,用 spl_autoload_register 依 PSR-4 規則自動載入類別
  • 用 Composer 建立專案、安裝套件,並載入 vendor/autoload.php

兩個 User 類別放在同一個專案

程式變大以後,會分成很多檔案,也會用到別人寫的套件。這時很容易撞名: 你的網站有一個 User 類別(會員),你裝的部落格套件也有一個 User 類別(作者); 你寫了一個 format() 函式,另一個套件也有 format()。同一個名稱定義兩次,PHP 會直接停下來:

Fatal error: Cannot declare class User, because the name is already in use

命名空間(namespace)的作法和電腦的資料夾一樣:不同資料夾裡可以各有一個 readme.txt, 因為它們的完整路徑不同。類別、函式、常數放進命名空間以後,名稱就多了一段「前綴」:

簡短名稱 完整名稱
User(你的會員) App\Models\User
User(部落格套件的作者) Blog\Models\User

PHP 的命名空間用反斜線 \ 分隔(不是 / 也不是 .)。你在 Laravel、Symfony 等框架看到的程式,第一行幾乎都是 namespace …;, 本課學會讀懂這些名稱,以及 PHP 怎麼找到每個類別所在的檔案。

宣告命名空間與三種名稱

在檔案開頭寫 namespace App\Models;,這個檔案之後定義的類別、介面、trait、enum、函式、常數都屬於 App\Models (變數不受命名空間影響)。namespace 必須是檔案裡第一個敘述(前面只能有 <?php 和 declare(strict_types=1);)。

在程式裡寫名稱時有三種寫法,就像檔案的相對路徑與絕對路徑:

寫法 例子 在 namespace App\Models; 裡實際找的是
非限定名稱(unqualified) User App\Models\User
限定名稱(qualified) Sub\User App\Models\Sub\User
完整名稱(fully qualified),以 \ 開頭 \Blog\Models\User \Blog\Models\User(就是它)

兩個好用的工具:

  • 類別名稱::class:取得類別的完整名稱字串(例如寫 User::class 得到 'App\Models\User'),不需要建立物件
  • __NAMESPACE__:目前所在的命名空間名稱

在逐步執行動畫裡,函式與方法的框架名稱會顯示完整名稱,例如 App\Models\label。

宣告命名空間:名稱自動加上前綴

整個檔案都在 App\Models 命名空間裡。第 20 行只寫 User,PHP 會補上目前的命名空間;第 24、25 行以 \ 開頭的完整名稱,不管寫在哪裡都指向同一個東西。

use:匯入名稱與取別名

每次都寫 \Blog\Models\User 很長。在檔案開頭用 use 匯入,之後就能用最後一段名稱:

use App\Models\User;                  // 之後寫 User=App\Models\User
use Blog\Models\User as Author;       // 名稱撞了:取別名,寫 Author=Blog\Models\User
use function App\Support\money;       // 匯入函式
use const App\Support\TAX;            // 匯入常數
use App\Models\{Order, Product};      // 同一個命名空間的多個類別,用大括號一次匯入
  • use 寫的一律是完整名稱(開頭的 \ 可以省略),它只是「取一個短名字」,不會載入檔案
  • use 只對目前這個檔案有效,每個檔案要各自寫
  • 一個檔案裡也可以用大括號寫好幾個命名空間(下面的範例為了放在同一個檔案裡才這樣寫);實務上一律一個檔案一個命名空間、一個類別

兩個 User 類別同時使用

Shop\Models 和 Blog\Models 各有一個 User。最下面的程式(全域命名空間)用 use 匯入前者、用 as Author 為後者取別名。看右邊兩個物件的標題:類別名稱不同。

想一想:名稱怎麼解析

1.

下面的程式會印出什麼?

<?php
namespace Shop;

class Cart {}

echo Cart::class, ' ', \Shop\Cart::class;
2.

下面的程式寫在 App\Report 命名空間,Summary 是 use 匯入的。執行 new Summary() 時,PHP 要找哪一個類別?

<?php
namespace App\Report;

use App\Models\Order as Summary;

$s = new Summary();
第 1 次作答

內建函式為什麼不用加 \:退回全域

strlen、count、DateTime、Exception 這些內建的名稱都在全域命名空間。那在 namespace App; 裡寫 strlen('abc'), 不是應該去找 App\strlen 嗎?PHP 的規則是:

種類 非限定名稱找不到時
函式、常數 先找目前命名空間(App\strlen),找不到就退回全域(strlen)
類別、介面、enum 不會退回:只找 App\DateTime,找不到就丟出 Error

所以在命名空間裡使用內建類別,要寫完整名稱 \DateTime、\Exception、\PDO,或在檔案開頭 use DateTime;。 這是初學者最常遇到的錯誤訊息之一:

Fatal error: Uncaught Error: Class "App\DateTime" not found

catch (Exception $e) 也要小心:在命名空間裡它指的是 App\Exception,不存在的類別不會被比對到,例外就接不住。要寫 catch (\Exception $e)。

函式退回全域,類別不會

這個檔案在 App 命名空間,而且自己定義了一個 App\strtoupper。注意第 11、12 行的差別,以及第 16 行的 new DateTime 為什麼會出錯。

一個檔案一個類別:include 與 require

慣例是一個檔案放一個類別,檔名和類別名稱相同(User 放在 User.php)。要使用另一個檔案的程式, 用 include 或 require 把它載入,PHP 會在這個位置執行那個檔案的內容:

寫法 找不到檔案時 同一個檔案載入第二次
include 'a.php' Warning,回傳 false,程式繼續執行 再執行一次
require 'a.php' 丟出 Error,沒有接住就停止 再執行一次
include_once、require_once 同上 略過(不會重複定義函式或類別)
  • 程式少了那個檔案就沒辦法正確執行(類別、函式、設定),用 require_once;include 適合「沒有也沒關係」的片段(例如網頁的頁尾)
  • 被載入的檔案可以 return 一個值,例如設定檔回傳一個陣列:$config = require 'config.php';
  • 相對路徑會依 include_path 與目前工作目錄尋找,容易出錯;寫成 __DIR__ . '/src/helpers.php' 以目前檔案所在的資料夾為準最穩定

逐步執行動畫只畫 index.php 的每一步:被載入的檔案、以及在那些檔案裡定義的函式與類別,執行時一次跑完,不逐行顯示,它們印出的內容一樣會出現在輸出裡。

載入其他檔案

網站根目錄還有兩個檔案:config.php(return 一個設定陣列)和 src/helpers.php(第 2 行 echo "載入 helpers.php\n";,接著定義函式 price_tag)。注意 require_once 寫了兩次,「載入 helpers.php」只印一次;以及 include 與 require 找不到檔案時的差別。

自動載入與 PSR-4

專案有幾十個類別時,在每個檔案開頭寫一長串 require_once 既麻煩又容易漏。自動載入(autoloading)讓 PHP 在 第一次用到某個還沒定義的類別時,呼叫你註冊的函式,由它負責 require 對應的檔案:

spl_autoload_register(function (string $class) {
    // $class 是完整類別名稱,例如 'App\Models\User'
});

要從類別名稱推算出檔案位置,大家約定了一套規則 PSR-4(PHP-FIG 制定的自動載入標準): 指定一個命名空間前綴對應一個資料夾,前綴之後的每一段命名空間就是一層子資料夾,類別名稱就是檔名。

前綴對應 類別 檔案
App\ → src/ App\Models\User src/Models/User.php
App\ → src/ App\Http\Controllers\HomeController src/Http/Controllers/HomeController.php
Blog\ → vendor/blog/src/ Blog\Models\User vendor/blog/src/Models/User.php

照這個規則放檔案,類別就能自動找到。注意 use 只是取名字,不會觸發自動載入;參數的型別宣告、instanceof 也不會。 要到 new、呼叫靜態方法、讀取類別常數這類真正用到類別的時候,才會呼叫載入函式。

用 spl_autoload_register 實作 PSR-4

網站裡有 src/Models/User.php(namespace App\Models; class User)和 src/Services/Greeter.php(namespace App\Services; class Greeter)。主程式沒有任何 require:看第一次 new User 時,PHP 怎麼呼叫載入函式(框架名稱 {closure}),以及第二次 new User 為什麼沒有再呼叫。左邊的字串值用程式碼的寫法顯示,"App\\Models\\User" 裡的 \\ 代表一個反斜線,實際的值是 App\Models\User。

Composer:安裝套件與自動載入

實務上不需要自己寫載入函式。Composer 是 PHP 的套件管理工具:它依照 composer.json 下載套件到 vendor/, 並產生一個 vendor/autoload.php,同時負責你自己的類別與所有套件的自動載入。 下面的指令要在自己的電腦上執行(需要先到 getcomposer.org 依說明安裝 Composer);套件的版本號依你執行的時間而定。

  1. 1

    建立 composer.json

    composer init 會一題一題問你專案名稱、作者等資料;加上 --no-interaction 就用參數直接建立。--autoload=src/ 設定 PSR-4:命名空間 Demo\Shop\ 對應 src/ 資料夾(由專案名稱 demo/shop 推算)。

  2. 2

    安裝套件

    composer require 套件名稱 會把套件與它需要的其他套件下載到 vendor/,並記錄在 composer.json 的 require 與 composer.lock。這裡以記錄日誌的套件 monolog 為例(輸出省略了部分行;版本號是 2026 年 10 月的最新版)。

  3. 3

    載入 vendor/autoload.php

    程式的入口只需要 require 一次 vendor/autoload.php,之後自己的類別(src/ 裡)和套件的類別都會自動載入。

  4. 4

    換一台電腦:composer install

    vendor/ 不放進 Git(在 .gitignore 加一行 /vendor/),只提交 composer.json 與 composer.lock。別人取得專案後執行 composer install,就會依 composer.lock 安裝完全相同的版本。之後改了 composer.json 的 autoload 設定,執行 composer dump-autoload 重新產生載入程式。

這段導覽完成了

想一想:載入與自動載入

1.

a.php 的內容是 <?php echo 'A';。下面的程式會印出什麼?

<?php
include_once 'a.php';
include 'a.php';
require_once 'a.php';
echo "\n";
2.

下面的程式會以哪一種錯誤結束?

<?php
namespace App;

echo strlen('abc'), "\n";
throw new Exception('設定檔不存在');
3.

composer.json 設定 "psr-4": {"App\\": "src/"}。類別 App\Http\Controllers\HomeController 應該放在哪個檔案?

第 1 次作答

動手做:寫一個自動載入器

網站裡已經有 src/Shop/Product.php(namespace Shop; class Product,建構子參數 name、price)和 src/Shop/Cart.php(namespace Shop; class Cart,方法 add(Product $p, int $qty)、count()、total())。請完成兩個 TODO,讓主程式不寫任何 require 就能使用這兩個類別:類別 Shop\Cart 對應檔案 src/Shop/Cart.php。

你的輸出
預期輸出

重點回顧

  • 命名空間讓同名的類別、函式、常數可以並存;用反斜線 \ 分隔,例如 App\Models\User
  • namespace App\Models; 寫在檔案最前面;之後的非限定名稱 User 會補上前綴,以 \ 開頭的是完整名稱
  • use 完整名稱; 匯入、as 取別名;函式與常數用 use function、use const。use 只取名字,不載入檔案,只對目前檔案有效
  • 類別::class 取得完整類別名稱,__NAMESPACE__ 是目前的命名空間
  • 在命名空間裡:函式、常數找不到會退回全域(strlen 照常可用);類別不會,要寫 \DateTime、\Exception 或 use
  • include 找不到檔案是 Warning;require 找不到丟出 Error;_once 版本不會重複載入同一個檔案
  • spl_autoload_register 在第一次用到類別時才載入檔案;PSR-4 規則:前綴對應資料夾、命名空間每一段對應一層資料夾
  • 實務上用 Composer:composer require 安裝套件,程式開頭 require 'vendor/autoload.php';;vendor/ 不進 Git,用 composer install 還原
這一課完成了!
Next lesson:Session 和 Cookie
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。