CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
PHP Web 入门/课时/函数与命名空间
RawBlame
课时目录12 节

PHP Web 入门

  1. 01PHP 是什么
  2. 02基础语法
  3. 03函数与命名空间
  4. 04面向对象
  5. 05Composer 依赖
  6. 06Web 请求处理
  7. 07PDO 与 MySQL
  8. 08REST API 风格
  9. 09PHP 8 新特性
  10. 10Docker 运行 PHP
  11. 11PHP vs Java 选型
  12. 12综合练习

函数与命名空间

function、use namespace、autoload 概念。

30 分钟

函数与命名空间

这一课你将能做到

  1. 写出带类型声明、默认参数、可变参数的现代 PHP 函数,并会匿名函数与箭头函数
  2. 用 namespace 组织代码,避免全局命名冲突
  3. 理解 PSR-4 规则,手写 spl_autoload_register 实现自动加载,告别满屏 require

为什么要学

把代码写进函数是复用的第一步,而把函数和类放进命名空间、再用自动加载按需引入,是 PHP 从「脚本」进化到「工程」的分水岭。Laravel、Symfony 这类框架的每一个类都遵循 命名空间 ↔ 目录路径 的 PSR-4 映射——看不懂这个映射,框架源码和 composer.json 就像天书。

人话:函数是「把一段活儿打包」,命名空间是「给代码分小区,防止撞名」,自动加载是「用到哪个文件才去读哪个文件」。三者合起来,就是 PHP 版的 package + import + classloader。

先建立图像

PSR-4 自动加载的核心映射:

composer.json 声明:
  "App\\": "src/"

那么:
App\Controllers\UserController
        │
        ▼ 把 \ 换成 / ,加前缀目录
src/Controllers/UserController.php
        │
        ▼ 第一次 new / 调用时自动 require
加载完成,运行

命名空间是逻辑名,目录是物理位置,PSR-4 就是两者之间那张「翻译表」。

跟着做

步骤 1:现代函数写法

新建 fn.php:

<?php
declare(strict_types=1);

// 类型声明 + 默认值 + 可变参数
function discount(float $price, float $rate = 0.9, ...$extra): float {
    return round($price * $rate, 2);
}

echo discount(100.0), PHP_EOL;              // 90(用默认折扣)
echo discount(100.0, 0.8), PHP_EOL;         // 80(覆盖默认值)
echo discount(price: 100.0, rate: 0.85), PHP_EOL;  // 85(命名参数,PHP 8+)

// 可变参数收进数组
function sum(int ...$nums): int {
    return array_sum($nums);
}
echo sum(1, 2, 3, 4), PHP_EOL;              // 10

步骤 2:匿名函数与箭头函数

<?php
declare(strict_types=1);

$users = [
    ['name' => '张三', 'age' => 28],
    ['name' => '李四', 'age' => 35],
    ['name' => '王五', 'age' => 22],
];

// 匿名函数:多行逻辑用 use 捕获外部变量
$minAge = 25;
$adults = array_filter($users, function (array $u) use ($minAge): bool {
    return $u['age'] >= $minAge;
});

// 箭头函数:单表达式,自动捕获外部变量(值捕获)
$names = array_map(fn(array $u) => $u['name'], $users);
print_r($names);          // 张三、李四、王五
print_r(array_keys($adults));  // 只有张三、李四满足条件

箭头函数 fn() => 是 PHP 7.4+ 语法,等价于自动 use 的单行匿名函数,数组操作场景下是绝对主力。

步骤 3:命名空间解决撞名

建两个文件,故意让类同名:

// src/Alibaba/User.php
namespace Alibaba;

class User {
    public function who(): string { return '阿里巴巴的用户'; }
}
// src/Tencent/User.php
namespace Tencent;

class User {
    public function who(): string { return '腾讯的用户'; }
}
// main.php
<?php
declare(strict_types=1);

require 'src/Alibaba/User.php';
require 'src/Tencent/User.php';

use Alibaba\User;
use Tencent\User as TUser;

echo (new User())->who(), PHP_EOL;    // 阿里巴巴的用户
echo (new TUser())->who(), PHP_EOL;   // 腾讯的用户

namespace 声明必须放文件最前;use 是起别名,不等于加载文件;同名类靠命名空间天然隔离。

步骤 4:手写自动加载,告别 require 瀑布

// autoloader.php
<?php
declare(strict_types=1);

spl_autoload_register(function (string $class): void {
    // 约定:App\ 开头的类,映射到 src/ 目录
    $prefix = 'App\\';
    if (str_starts_with($class, $prefix)) {
        $relative = substr($class, strlen($prefix));
        $file = __DIR__ . '/src/' . str_replace('\\', '/', $relative) . '.php';
        if (is_file($file)) {
            require $file;
        }
    }
});

// 假设存在 src/Service/PayService.php,命名空间为 App\Service
// 不再写任何 require,直接 new,类文件自动加载
$pay = new App\Service\PayService();

get_declared_classes() 可验证类是惰性加载的:没用到就不会读文件。Composer 的自动加载就是这套机制的工业级实现。

讲清楚要点

  1. include/require 本质相同,错误处理不同:找不到文件时 include 只给 Warning 继续跑,require 抛致命错误终止。生产代码一律 require;_once 后缀防止重复加载,但性能与心智成本更高,能被自动加载替代就替代。
  2. 命名空间是逻辑划分,不是目录:namespace App\Service; 本身不产生任何目录或文件行为;只有配合 PSR-4 自动加载,命名空间才和目录一一对应。同一个命名空间可以写在多个文件里。
  3. PSR-4 只认「前缀 + 子路径」:"App\\": "src/" 表示以 App\ 开头的类,去掉前缀后按 \ → / 规则去 src/ 找同名 .php。类名大小写敏感,文件名必须与类名完全一致。
  4. 自动加载是惰性的:spl_autoload_register 注册的加载器在类第一次被使用时才触发。所以 PSR-4 项目加载 100 个依赖包也不会拖慢启动——没 new 的类一个文件都不读。

你可能会懵的地方

  • use 和 require 是一回事吗? 不是。use App\Service\User; 只是在当前文件给类起短别名,编译期处理,不加载文件;真正加载文件的是 require 或自动加载器。开了自动加载后,use 之后直接 new 即可,无需 require。
  • 全局命名空间怎么写? 不写 namespace 的文件就在全局命名空间,类名直接可用(如 PHP 内置的 \PDO)。在命名空间文件里引用全局类要加反斜杠前缀:\Exception、\DateTimeImmutable。
  • PSR-0 和 PSR-4 有什么区别? PSR-0 把命名空间下划线和全部前缀都转成目录(已弃用);PSR-4 只映射去掉前缀后的子路径,目录更浅。现代项目一律 PSR-4。
  • 函数也有命名空间吗? 有。namespace App\Utils; function helper() {...} 声明在命名空间内的函数同样隔离。但函数不能被 PSR-4 自动加载(自动加载只针对类/接口/trait/enum),所以工程实践把逻辑都写进类。
  • 为什么报错说 class not found,但文件明明在? 三种可能:命名空间声明与目录/前缀映射不一致、类名与文件名大小写不一致(Linux 严格区分)、改了 composer.json 后没跑 composer dump-autoload。

错法对照

错法问题正解
每个 controller 顶部 10 个 require依赖关系手工维护、易漏易重PSR-4 + Composer 自动加载
use App\User; 后以为文件已加载use 不加载任何东西配置自动加载后直接实例化
命名空间写成 App/User 或开头带 \语法错误分隔符是 \:App\User
文件名 userController.php 类名 UserController大小写不一致,Linux 上加载失败文件名与类名逐字符一致
所有代码写在全局命名空间类名冲突、无法被框架加载统一 厂商\项目\模块 命名空间

卡点急救

  1. Class "App\xxx" not found:按顺序查三点——composer.json 的 autoload.psr-4 前缀对不对;命名空间声明与路径是否一致;改完配置是否执行了 composer dump-autoload。
  2. Cannot redeclare class:同一类被 require 两次。检查是否手动 require 又叠加了自动加载,或误用 include 而非 include_once。
  3. 命名空间内调 date()、PDO 报 not found:PHP 会优先在当前命名空间找。全局函数可省略前缀(PHP 有回退机制),全局类必须写全:\PDO、\Throwable。

面试常问

  • include 和 require 的区别? 加载行为相同,失败处理不同:include 产生 Warning 后脚本继续执行,require 产生 Fatal Error 直接终止。依赖文件必须用 require;_once 变体防止重复定义,现代项目由 Composer 自动加载统一解决。
  • PSR-4 自动加载的映射规则是什么? composer.json 中 "App\\": "src/" 表示:类名以 App\ 开头时,去掉该前缀,把剩余部分的 \ 替换为目录分隔符,在 src/ 下找「完整类名.php」。例如 App\Model\Order 对应 src/Model/Order.php。
  • spl_autoload_register 的工作原理? 向 SPL 注册一个或多个自动加载回调,PHP 引擎在遇到未定义类时按注册顺序依次调用,回调负责把类文件 require 进来;类是惰性加载的,未使用的类不产生文件 IO。Composer 的 vendor/autoload.php 本质就是一组预配置的 spl_autoload_register。

自测三问

  1. use 语句会触发文件加载吗?它的作用是什么?
  2. App\Controller\HomeController 在 "App\\": "src/" 映射下对应哪个文件路径?
  3. 匿名函数和箭头函数在捕获外部变量上的区别是什么?

小结一句话

函数管复用,命名空间管隔离,PSR-4 + spl_autoload_register 管「按需加载」——三者合力把 PHP 脚本变成可维护的工程。

延伸

  • PHP 官方手册:命名空间
  • 下一课:面向对象——类、接口、trait 与构造器属性提升。
加载本节练习…
← 上一课基础语法下一课 →面向对象