找回密码
 立即注册
首页 业界区 业界 2025 年 PHP 常见面试题整理以及对应答案和代码示例 ...

2025 年 PHP 常见面试题整理以及对应答案和代码示例

廖雯华 2025-10-1 17:44:42
2025 年 PHP 常见面试题整理以及对应答案和代码示例

PHP 面试通常会考察基础知识(数组、OOP、错误处理)和现代特性(类型、属性、枚举)。关键是要展示你能写出简洁、可预测的代码,同时了解 PHP 8+ 的新变化。
我整理了以下一些常见 PHP 可能面试的。每个问题都有简洁的答案和可运行的代码示例,你可以直接复制去试试。
原文链接- 2025 年 PHP 常见面试题整理以及对应答案和代码示例
PHP 8+ 中有哪些面试官关心的变化?

类型系统:联合类型(int|float)、mixed、never、true|false|null、交集类型,以及可空类型 ?T。
OOP 改进:构造器属性提升、readonly 属性/类、枚举、#[\Override] 属性。
控制流:match(表达式,无穿透)、nullsafe 操作符 ?->。
开发体验优化:命名参数、属性/注解、JIT(性能提升)、array_is_list()。
错误处理更合理:字符串和数字比较的方式改进了(比如 0 == "foo" 现在返回 false)。
如果你能清楚地解释这些特性,面试就会顺利很多。
PHP 中 == 和 === 的区别是什么?

== 执行类型转换;=== 要求相同类型和相同值。
  1. var_dump(42 == "42");   // true  (宽松比较)
  2. var_dump(42 === "42");  // false (严格比较)
  3. // 从 PHP 8 开始:
  4. var_dump(0 == "foo");   // false (旧版本中曾经是 true)
复制代码
要点:默认使用 ===,除非有非常具体的理由不这样做。
联合类型、可空类型和交集类型如何工作?
  1. function area(int|float $w, int|float $h): float {
  2.     return $w * $h;
  3. }
  4. function greet(?string $name): string { // 可空类型
  5.     return "Hello, " . ($name ?? "stranger");
  6. }
  7. interface A { public function foo(): void; }
  8. interface B { public function bar(): void; }
  9. class C implements A, B {
  10.     public function foo(): void {}
  11.     public function bar(): void {}
  12. }
  13. function needsAandB(A&B $x): void { /* ... */ } // 交集类型
复制代码

  • int|float 表示两者之一
  • ?string 表示 string|null
  • A&B 表示必须实现 A 和 B 两个接口
nullsafe 操作符是什么,何时应该使用?

当左侧为 null 时,它会自动停止后续的方法/属性访问。
  1. $user = null;
  2. echo $user?->profile?->company?->name ?? 'No company'; // "No company"
复制代码
无需嵌套 if——简洁且安全。
什么时候应该使用 match 而不是 switch?

match 是表达式(可以返回值),必须处理所有情况,不会像 switch 那样穿透执行。
  1. $status = 404;
  2. $message = match ($status) {
  3.     200, 201 => 'OK',
  4.     404      => 'Not Found',
  5.     500      => 'Server Error',
  6.     default  => 'Unknown',
  7. };
复制代码
这减少了因遗忘 break; 引起的错误。
用 60 秒解释 Composer 自动加载和 PSR-4


  • 在 composer.json 中添加命名空间
  • 将文件放在正确的文件夹中
  • 让 Composer 生成自动加载器
  1. {
  2.   "autoload": {
  3.     "psr-4": {
  4.       "App\": "src/"
  5.     }
  6.   }
  7. }
复制代码
  1. // src/Service/Pinger.php
  2. namespace App\Service;
  3. class Pinger {
  4.     public function ping(): string { return 'pong'; }
  5. }
复制代码
  1. // index.php
  2. require __DIR__ . '/vendor/autoload.php';
  3. use App\Service\Pinger;
  4. echo (new Pinger())->ping(); // "pong"
复制代码
修改后运行 composer dump-autoload。
include 和 require 的区别(及 _once 变体)?


  • require → 失败时产生致命错误(停止脚本)
  • include → 失败时产生警告(继续执行)
  • *_once → 只加载文件一次
应用必须的文件使用 require(如 bootstrap),可选部分使用 include。
PHP 引用实际如何工作?

赋值是写时复制。大部分时候不需要用引用,只有在 API 必须修改参数值时才用。
  1. function bump(int &$n): void { $n++; }
  2. $x = 5;
  3. bump($x);
  4. echo $x; // 6
复制代码
避免为"微优化"使用引用。它们往往损害清晰度。
解释异常与错误(Throwable)

所有可抛出的都实现 Throwable。Exception 和 Error 是兄弟类。
  1. try {
  2.     throw new RuntimeException('Oops');
  3. } catch (\Throwable $e) { // 捕获 Exception 和 Error
  4.     error_log($e->getMessage());
  5. } finally {
  6.     // 清理工作
  7. }
复制代码
在应用的边界层(比如控制器)捕获 Throwable 很方便,但在库的内部应该捕获更具体的异常类型。
PDO 预处理语句防止 SQL 注入
  1. $pdo = new PDO($dsn, $user, $pass, [
  2.     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  3. ]);
  4. $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
  5. $stmt->execute(['email' => $email]);
  6. $user = $stmt->fetch(PDO::FETCH_ASSOC);
复制代码
经验法则:始终使用预处理语句;永不插入不可信输入。
如何正确存储密码?

使用 password_hash() 和 password_verify()。不要自己选择算法——让 PHP 选择默认的。
  1. $hash = password_hash($plainPassword, PASSWORD_DEFAULT);
  2. if (password_verify($loginPassword, $hash)) {
  3.     // 通过验证
  4. }
复制代码
PASSWORD_DEFAULT 会随着 PHP 版本更新而演进,当 password_needs_rehash() 返回 true 时需要重新哈希。
属性(Attributes)是什么,如何读取?

属性是原生注解。你可以装饰类、方法等,并通过反射读取。
  1. #[Attribute(Attribute::TARGET_METHOD)]
  2. class Route {
  3.     public function __construct(public string $path) {}
  4. }
  5. class BlogController {
  6.     #[Route('/posts')]
  7.     public function index() {}
  8. }
  9. // 读取:
  10. $ref = new ReflectionMethod(BlogController::class, 'index');
  11. $attrs = $ref->getAttributes(Route::class);
  12. $path  = $attrs[0]->newInstance()->path; // '/posts'
复制代码
框架会用属性来处理路由、验证、依赖注入等功能。
枚举是什么,为什么有用?

枚举用真正的类型替换"字符串化"的常量。
  1. enum Status: string {
  2.     case Draft = 'draft';
  3.     case Published = 'pub';
  4. }
  5. function publish(Status $s): bool {
  6.     return $s === Status::Published;
  7. }
  8. publish(Status::Draft); // false
复制代码
带值的枚举保证了比较的安全性,也能让 IDE 的自动完成更好用。
readonly 属性和不可变对象

用来创建值对象或 DTO 非常合适。
  1. final class Money {
  2.     public function __construct(
  3.         public readonly int $cents,
  4.         public readonly string $currency,
  5.     ) {}
  6. }
  7. $m = new Money(100, 'USD');
  8. // $m->cents = 200; // 错误
复制代码
也可以用 readonly 类来让全部属性都变成只读。
Trait vs 抽象类 vs 接口


  • 接口:仅契约(无实现)
  • 抽象类:共享基类 + 部分实现
  • Trait:跨不相关类的水平复用(混入方法)
  1. trait LoggerTrait {
  2.     public function log($m){ echo $m; }
  3. }
  4. interface Reportable {
  5.     public function report(): string;
  6. }
  7. abstract class BaseReport {
  8.     abstract public function data(): array;
  9. }
  10. class SalesReport extends BaseReport implements Reportable {
  11.     use LoggerTrait;
  12.     public function data(): array { return [1,2,3]; }
  13.     public function report(): string { return json_encode($this->data()); }
  14. }
复制代码
延迟静态绑定(static:: vs self::)

self:: 绑定到写代码的类;static:: 延迟到运行时类。
  1. class A {
  2.     public static function who(): string { return __CLASS__; }
  3.     public static function call(): string { return static::who(); }
  4. }
  5. class B extends A {
  6.     public static function who(): string { return __CLASS__; }
  7. }
  8. echo B::call(); // "B" (延迟静态绑定)
复制代码
基类如果要被继承的话,应该用 static::。
生成器(yield)处理大数据集

生成器采用懒加载——特别适合处理大量数据流。
  1. function lines(string $file): iterable {
  2.     $fh = fopen($file, 'r');
  3.     try {
  4.         while (($line = fgets($fh)) !== false) {
  5.             yield rtrim($line, "\n");
  6.         }
  7.     } finally {
  8.         fclose($fh);
  9.     }
  10. }
  11. foreach (lines('huge.txt') as $line) {
  12.     // 处理而不加载所有内容到内存
  13. }
复制代码
闭包、"use" 和箭头函数
  1. $total = 0;
  2. $add = function(int $n) use (&$total) { $total += $n; };
  3. array_map($add, [1,2,3]);
  4. echo $total; // 6
  5. $double = fn($x) => $x * 2; // 箭头函数自动按值捕获
复制代码
箭头函数写起来简洁,普通闭包能让你精确控制变量捕获(use)。
会话和 Cookie:安全默认检查清单
  1. session_set_cookie_params([
  2.     'httponly' => true,
  3.     'samesite' => 'Lax',
  4.     'secure' => isset($_SERVER['HTTPS']),
  5. ]);
  6. session_start();
  7. $_SESSION['user_id'] = 123;
复制代码

  • 登录时重新生成 ID(session_regenerate_id(true))
  • 设置 secure 和 httponly
  • 优先使用 SameSite=Lax 或 Strict,除非第三方流程需要 None
处理日期:优先使用 DateTimeImmutable
  1. $start = new DateTimeImmutable('2025-03-01 09:00', new DateTimeZone('UTC'));
  2. $meeting = $start->modify('+1 hour');
  3. echo $start->format('c');   // 未改变
  4. echo $meeting->format('c'); // 新实例
复制代码
不可变日期可以防止意外修改。记得要明确设置时区,或者早期用 default_timezone_set 设置。
OPcache 和性能基础


  • 生产环境启用 OPcache——它缓存编译的字节码
  • 避免过早的微优化;用专门的工具来测量性能(比如 Blackfire、Xdebug profiler)
  • 使用正确的数据结构;对大数字列表,SplFixedArray 比常规数组更节省内存
PHP 的垃圾收集器如何工作

PHP 使用引用计数 + 循环收集器的方式管理内存。大部分情况下不用操心这个问题。但如果是长时间运行的进程(比如 worker 或 daemon),又有大量数据结构或循环引用(父子对象互相引用),就需要手动 unset 引用并打破循环,这样能更早释放内存。
常见数组陷阱和高级技巧
  1. $a = ['x' => 1, 'y' => 2];
  2. $b = ['y' => 3, 'z' => 4];
  3. $merge = $a + $b;           // 按键联合:['x'=>1,'y'=>2,'z'=>4]
  4. $replace = array_merge($a, $b); // ['x'=>1,'y'=>3,'z'=>4]
  5. $list = ['a','b','c'];
  6. array_splice($list, 1, 1); // 在索引1处移除:['a','c']
  7. array_is_list(['a','b']);   // true(连续数字键)
  8. array_is_list(['1'=>'a']);  // false
复制代码
知道何时需要联合(+)vs 合并(array_merge)。
如何快速测试代码?


  • 将逻辑放入小函数或方法
  • 使用 PHPUnit 或 Pest;模拟外部服务
  • 快速检查,添加简单的 CLI 脚本:
  1. if (PHP_SAPI === 'cli') {
  2.     assert(area(3, 4) === 12.0);
  3.     echo "All good\n";
  4. }
复制代码
CLI 断言能让你快速验证代码的正确性,即使只是小示例也很有用。
关于 PHP 中的异步

PHP 本身是同步的请求-响应模式,但 Fiber(PHP 8.1)让 AMPHP 或 ReactPHP 等库能实现用户空间的并发处理。面试中不会要求你手写异步代码,只要能说明白什么时候需要用(比如 worker 中有大量 I/O 操作)就行。
快速问答

$_POST vs php://input? $_POST 解析表单编码数据;php://input 读取原始请求体(对 JSON 有用)。
**get/set?** 重载属性访问的魔术方法——谨慎使用。
__toString()? 对象的字符串表示(必须返回字符串)。
require_once 慢? 有 OPcache 时可忽略;为了正确性使用,不是"速度"。
到处都是静态方法? 对纯函数很方便;为了可测试性/可配置性优先使用 DI。
常量 vs 枚举? 枚举给你一个类型;常量只是值。
一个现代的 PHP 例子

[code]

相关推荐

您需要登录后才可以回帖 登录 | 立即注册