PHP 面试笔试常见题汇总

目录

PHP 基础知识

1. 变量与数据类型

问题:PHP 有哪些基本数据类型?如何判断变量类型?

答案:

基本数据类型:

  • 标量类型: boolean、integer、float、string
  • 复合类型: array、object、callable、iterable
  • 特殊类型: null、resource

类型判断方法:

  • gettype($var) - 返回变量类型字符串
  • is_*() 系列函数 - 如 is_array()is_string()
  • instanceof - 检查对象类型
  • var_dump() - 详细输出变量信息
1
2
3
4
$var = "Hello";
echo gettype($var); // string
echo is_string($var); // true
echo var_dump($var); // string(5) "Hello"

问题:== 和 === 的区别?

答案:

  • == (宽松比较): 仅比较值,会进行类型转换
  • === (严格比较): 同时比较值和类型,不进行类型转换
1
2
3
4
5
6
7
8
9
// 宽松比较示例
var_dump(5 == '5'); // true (字符串转为数字)
var_dump(true == 1); // true (布尔转为数字)
var_dump(null == ''); // true (都被视为"空")

// 严格比较示例
var_dump(5 === '5'); // false (类型不同)
var_dump(true === 1); // false (类型不同)
var_dump(null === ''); // false (类型不同)

最佳实践: 推荐使用 === 进行比较,避免意外的类型转换。

问题:PHP 中的引用与值传递的区别?

答案:

  • 值传递(默认): 复制变量的值,函数内修改不影响原变量
  • 引用传递: 传递变量的内存地址,函数内修改会影响原变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 值传递示例
function valuePass($var) {
$var = 'modified';
}
$original = 'original';
valuePass($original);
echo $original; // 输出: original

// 引用传递示例
function referencePass(&$var) {
$var = 'modified';
}
$original = 'original';
referencePass($original);
echo $original; // 输出: modified

// 引用赋值
$a = 'hello';
$b = &$a; // $b 引用 $a
$b = 'world';
echo $a; // 输出: world

面向对象编程(OOP)

2. 继承、抽象类和接口

问题:PHP 如何实现继承、抽象类和接口?

答案:

1. 继承(Inheritance)

  • 使用 extends 关键字实现单继承
  • 子类可以重写父类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
protected $name;

public function __construct($name) {
$this->name = $name;
}

public function speak() {
return "Animal makes a sound";
}
}

class Dog extends Animal {
public function speak() {
return "{$this->name} barks";
}
}

$dog = new Dog("Buddy");
echo $dog->speak(); // Buddy barks

2. 抽象类(Abstract Class)

  • 使用 abstract class 定义
  • 可包含抽象方法和具体方法
  • 不能直接实例化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
abstract class Shape {
protected $color;

public function __construct($color) {
$this->color = $color;
}

// 具体方法
public function getColor() {
return $this->color;
}

// 抽象方法,子类必须实现
abstract public function getArea();
}

class Circle extends Shape {
private $radius;

public function __construct($color, $radius) {
parent::__construct($color);
$this->radius = $radius;
}

public function getArea() {
return pi() * $this->radius * $this->radius;
}
}

3. 接口(Interface)

  • 使用 interface 定义
  • 所有方法都是抽象的
  • 类可以实现多个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Flyable {
public function fly();
}

interface Swimmable {
public function swim();
}

class Duck implements Flyable, Swimmable {
public function fly() {
return "Duck is flying";
}

public function swim() {
return "Duck is swimming";
}
}

3. PSR 规范

问题:PSR 规范是什么?常见规范有哪些?

答案:

PSR(PHP Standards Recommendations) 是 PHP 社区的代码规范标准,由 PHP-FIG(Framework Interop Group)制定。

常见 PSR 规范:

  • PSR-1:基础编码标准

    • 文件必须使用 <?php<?= 标签
    • 类名使用 StudlyCaps(大驼峰)
    • 方法名使用 camelCase(小驼峰)
  • PSR-4:自动加载规范

    • 命名空间与文件路径映射
    • 一个文件一个类
1
2
3
4
5
6
7
// PSR-4 示例
// 文件路径: src/Model/User.php
namespace App\Model;

class User {
// 类实现
}
  • PSR-7:HTTP 消息接口

    • 标准化 HTTP 请求/响应对象
    • 不可变对象设计
  • PSR-11:容器接口

    • 依赖注入容器的标准接口
  • PSR-12:扩展编码风格

    • 更详细的代码风格规范
    • 缩进、换行、空格等规则

4. 错误处理机制

问题:PHP 中的错误处理机制?

答案:

PHP 错误类型:

  1. Fatal Error(致命错误)

    • 语法错误、内存不足、调用未定义函数等
    • 导致脚本终止执行
  2. Warning(警告)

    • 非致命错误,脚本继续执行
    • 如包含不存在的文件
  3. Notice(提示)

    • 轻微错误,如使用未定义变量
  4. Exception(异常)

    • 可捕获的运行时错误
    • 通过 throw 抛出,try-catch 捕获

错误处理方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. try-catch 异常处理
try {
$pdo = new PDO($dsn, $user, $pass);
// 数据库操作
} catch (PDOException $e) {
echo "数据库错误: " . $e->getMessage();
} catch (Exception $e) {
echo "其他错误: " . $e->getMessage();
} finally {
// 无论是否有异常都会执行
echo "清理资源";
}

// 2. 自定义异常
class CustomException extends Exception {
public function errorMessage() {
return "错误行号 {$this->getLine()}: {$this->getMessage()}";
}
}

// 3. 全局异常处理
set_exception_handler(function($exception) {
error_log($exception->getMessage());
echo "系统错误,请稍后重试";
});

// 4. 错误日志
error_log("自定义错误信息", 3, "/path/to/error.log");

5. 文件上传处理

问题:PHP 中的文件上传处理?

答案:

前端表单设置:

1
2
3
4
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="upload" accept=".jpg,.png,.pdf">
<input type="submit" value="上传">
</form>

后端处理流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload'])) {
$file = $_FILES['upload'];

// 1. 检查上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
die('上传失败: ' . $file['error']);
}

// 2. 验证文件大小(2MB限制)
if ($file['size'] > 2 * 1024 * 1024) {
die('文件过大');
}

// 3. 验证文件类型
$allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);

if (!in_array($mimeType, $allowedTypes)) {
die('不支持的文件类型');
}

// 4. 生成安全的文件名
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = uniqid() . '.' . $extension;
$uploadPath = 'uploads/' . $filename;

// 5. 移动文件
if (move_uploaded_file($file['tmp_name'], $uploadPath)) {
echo "文件上传成功: $filename";
} else {
echo "文件移动失败";
}
}
?>

安全注意事项:

  • 验证文件类型(使用 finfo 而非扩展名)
  • 限制文件大小
  • 重命名文件(防止路径遍历攻击)
  • 存储在 Web 根目录外
  • 设置适当的文件权限

答案:

特性 Session Cookie
存储位置 服务器端 客户端(浏览器)
安全性 相对安全 可被用户查看/修改
大小限制 无限制(受服务器内存限制) 4KB
生命周期 浏览器关闭或超时失效 可设置过期时间
网络传输 仅传输 Session ID 每次请求都传输
使用场景 敏感信息、用户状态 用户偏好、追踪

Session 使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 启动 session
session_start();

// 设置 session
$_SESSION['username'] = 'john';
$_SESSION['user_id'] = 123;

// 读取 session
echo $_SESSION['username'];

// 销毁 session
session_destroy();

// 设置 session 配置
ini_set('session.gc_maxlifetime', 3600); // 1小时过期
ini_set('session.cookie_httponly', 1); // 防止 XSS
ini_set('session.cookie_secure', 1); // HTTPS only

Cookie 使用示例:

1
2
3
4
5
6
7
8
9
10
11
// 设置 cookie(7天过期)
setcookie('username', 'john', time() + 7*24*3600, '/', '', true, true);
// 名称 值 过期时间 路径 域名 HTTPS HttpOnly

// 读取 cookie
if (isset($_COOKIE['username'])) {
echo $_COOKIE['username'];
}

// 删除 cookie
setcookie('username', '', time() - 3600);

7. 魔术方法

问题:PHP 中的魔术方法有哪些?

答案:

常用魔术方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class MagicExample {
private $data = [];

// 构造和析构
public function __construct($name) {
echo "对象被创建: $name\n";
}

public function __destruct() {
echo "对象被销毁\n";
}

// 属性访问
public function __get($name) {
return $this->data[$name] ?? null;
}

public function __set($name, $value) {
$this->data[$name] = $value;
}

public function __isset($name) {
return isset($this->data[$name]);
}

public function __unset($name) {
unset($this->data[$name]);
}

// 方法调用
public function __call($name, $args) {
echo "调用不存在的方法: $name\n";
}

public static function __callStatic($name, $args) {
echo "调用不存在的静态方法: $name\n";
}

// 对象调用
public function __invoke($param) {
echo "对象被当作函数调用: $param\n";
}

// 字符串转换
public function __toString() {
return "MagicExample对象";
}

// 对象克隆
public function __clone() {
echo "对象被克隆\n";
}

// 序列化
public function __sleep() {
return ['data']; // 指定要序列化的属性
}

public function __wakeup() {
echo "对象被反序列化\n";
}

// 调试信息
public function __debugInfo() {
return ['debug_data' => $this->data];
}
}

// 使用示例
$obj = new MagicExample('test');
$obj->name = 'John'; // 调用 __set
echo $obj->name; // 调用 __get
$obj('hello'); // 调用 __invoke
echo $obj; // 调用 __toString

8. 命名空间(Namespace)

问题:PHP 命名空间的作用和使用方法?

答案:

作用:

  • 解决类名、函数名、常量名冲突
  • 提供更好的代码组织结构
  • 支持自动加载机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 文件: App/Model/User.php
<?php
namespace App\Model;

class User {
public function getName() {
return 'User from Model';
}
}

// 文件: App/Controller/User.php
<?php
namespace App\Controller;

class User {
public function index() {
return 'User Controller';
}
}

// 使用命名空间
<?php
// 1. 完全限定名称
$modelUser = new \App\Model\User();
$controllerUser = new \App\Controller\User();

// 2. 使用 use 导入
use App\Model\User as ModelUser;
use App\Controller\User as ControllerUser;

$modelUser = new ModelUser();
$controllerUser = new ControllerUser();

// 3. 导入函数和常量
use function App\Helpers\formatDate;
use const App\Config\DB_HOST;

9. Trait(特性)

问题:PHP Trait 的作用和使用场景?

答案:

作用:

  • 实现代码复用(PHP 单继承的补充)
  • 水平代码复用,避免深层继承
  • 解决多重继承问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 定义 Trait
trait Loggable {
public function log($message) {
echo "[" . date('Y-m-d H:i:s') . "] $message\n";
}

public function logError($error) {
$this->log("ERROR: $error");
}
}

trait Cacheable {
private $cache = [];

public function setCache($key, $value) {
$this->cache[$key] = $value;
}

public function getCache($key) {
return $this->cache[$key] ?? null;
}
}

// 使用 Trait
class User {
use Loggable, Cacheable;

private $name;

public function __construct($name) {
$this->name = $name;
$this->log("User $name created");
}

public function getName() {
// 先检查缓存
$cached = $this->getCache('name');
if ($cached) {
return $cached;
}

// 设置缓存
$this->setCache('name', $this->name);
return $this->name;
}
}

// Trait 冲突解决
trait A {
public function test() {
echo "A::test";
}
}

trait B {
public function test() {
echo "B::test";
}
}

class MyClass {
use A, B {
A::test insteadof B; // 使用 A 的 test 方法
B::test as testB; // 给 B 的 test 方法起别名
}
}

10. 文件包含

问题:include、require、include_once、require_once 的区别和性能影响?

答案:

函数 文件不存在时 重复包含检查 性能 使用场景
include 警告,继续执行 最快 可选的模板文件
require 致命错误,停止执行 必需的核心文件
include_once 警告,继续执行 较慢 可选的配置文件
require_once 致命错误,停止执行 较慢 必需的类文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 性能测试示例
$start = microtime(true);

// 方式1:直接 require(最快)
require 'config.php';

// 方式2:require_once(检查开销)
require_once 'config.php';

// 方式3:条件包含(推荐)
if (!defined('CONFIG_LOADED')) {
require 'config.php';
define('CONFIG_LOADED', true);
}

$end = microtime(true);
echo "执行时间: " . ($end - $start) . "秒";

性能优化建议:

  • 避免在循环中使用 *_once 函数
  • 使用自动加载代替手动包含
  • 合理使用 OPcache 缓存

11. 生成器(Generator)

问题:PHP 生成器的作用和使用场景?

答案:

作用:

  • 节省内存,处理大数据集
  • 延迟计算,按需生成数据
  • 简化迭代器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 传统方式:占用大量内存
function getNumbers($max) {
$numbers = [];
for ($i = 1; $i <= $max; $i++) {
$numbers[] = $i;
}
return $numbers; // 返回完整数组
}

// 生成器方式:节省内存
function generateNumbers($max) {
for ($i = 1; $i <= $max; $i++) {
yield $i; // 逐个生成
}
}

// 使用对比
$numbers = getNumbers(1000000); // 占用约 400MB 内存
$generator = generateNumbers(1000000); // 占用几乎 0 内存

foreach ($generator as $number) {
echo $number . "\n";
if ($number > 10) break; // 可以提前终止
}

// 生成器委托
function gen1() {
yield 1;
yield 2;
}

function gen2() {
yield 3;
yield 4;
}

function combinedGenerator() {
yield from gen1(); // 委托给其他生成器
yield from gen2();
yield 5;
}

// 双向生成器(发送数据)
function logger() {
while (true) {
$message = yield; // 接收数据
if ($message) {
echo "[LOG] $message\n";
}
}
}

$log = logger();
$log->next(); // 启动生成器
$log->send("Hello World"); // 发送数据

12. PHP 8+ 新特性

问题:PHP 8.0/8.1/8.2 的主要新特性?

答案:

PHP 8.0 新特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 1. 联合类型
function processId(int|string $id): int|string {
return $id;
}

// 2. 命名参数
function createUser(string $name, string $email, bool $active = true) {
// 实现
}
$user = createUser(email: '[email protected]', name: 'John');

// 3. 属性(Attributes)
#[Route('/api/users', methods: ['GET'])]
class UserController {
#[Validate('required|email')]
public function create(string $email) {
// 实现
}
}

// 4. 构造器属性提升
class User {
public function __construct(
public string $name,
public string $email,
private int $age = 0
) {}
}

// 5. match 表达式
$result = match($status) {
'pending' => 'Waiting for approval',
'approved', 'active' => 'User is active',
'rejected' => 'Access denied',
default => 'Unknown status'
};

// 6. 空安全操作符
$user = getUser();
$email = $user?->getProfile()?->getEmail();

// 7. JIT 编译器(性能提升)
// 在 php.ini 中启用:opcache.jit=1255

PHP 8.1 新特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 1. 枚举(Enums)
enum Status: string {
case PENDING = 'pending';
case APPROVED = 'approved';
case REJECTED = 'rejected';

public function getLabel(): string {
return match($this) {
Status::PENDING => '待审核',
Status::APPROVED => '已通过',
Status::REJECTED => '已拒绝',
};
}
}

$status = Status::PENDING;
echo $status->value; // 'pending'
echo $status->getLabel(); // '待审核'

// 2. 只读属性
class User {
public function __construct(
public readonly string $id,
public readonly string $email
) {}
}

// 3. 纤程(Fibers)- 协程支持
$fiber = new Fiber(function (): void {
echo "Fiber start\n";
Fiber::suspend();
echo "Fiber resume\n";
});

$fiber->start();
echo "Main\n";
$fiber->resume();

// 4. 数组解包支持字符串键
$array1 = ['a' => 1, 'b' => 2];
$array2 = ['c' => 3, ...$array1]; // ['c' => 3, 'a' => 1, 'b' => 2]

PHP 8.2 新特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. 只读类
readonly class User {
public function __construct(
public string $name,
public string $email
) {}
}

// 2. 析取范式类型(DNF Types)
function process((A&B)|C $param): (X&Y)|Z {
// 实现
}

// 3. 敏感参数
function login(
string $username,
#[SensitiveParameter] string $password
) {
// $password 在堆栈跟踪中会被隐藏
}

// 4. 新的随机扩展
$randomizer = new Random\Randomizer();
$randomBytes = $randomizer->getBytes(16);
$randomInt = $randomizer->getInt(1, 100);

13. 循环优化

问题:如何进行循环优化?原理是什么?

答案:

优化策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 1. 减少重复计算
// 优化前
for ($i = 0; $i < count($array); $i++) {
// 每次循环都调用 count()
}

// 优化后
$length = count($array);
for ($i = 0; $i < $length; $i++) {
// count() 只调用一次
}

// 2. 使用哈希表减少嵌套
// 优化前:O(n³) 复杂度
foreach ($users as $user) {
foreach ($orders as $order) {
foreach ($products as $product) {
if ($order['user_id'] === $user['id'] &&
$order['product_id'] === $product['id']) {
// 处理逻辑
}
}
}
}

// 优化后:O(n) 复杂度
$ordersByUser = [];
foreach ($orders as $order) {
$ordersByUser[$order['user_id']][] = $order;
}

$productsById = [];
foreach ($products as $product) {
$productsById[$product['id']] = $product;
}

foreach ($users as $user) {
if (isset($ordersByUser[$user['id']])) {
foreach ($ordersByUser[$user['id']] as $order) {
$product = $productsById[$order['product_id']] ?? null;
if ($product) {
// 处理逻辑
}
}
}
}

// 3. 使用内置函数
// 优化前
$result = [];
foreach ($array as $item) {
if ($item > 10) {
$result[] = $item * 2;
}
}

// 优化后
$result = array_map(
fn($x) => $x * 2,
array_filter($array, fn($x) => $x > 10)
);

// 4. 生成器处理大数据
// 优化前:内存占用大
function processLargeFile($filename) {
$lines = file($filename); // 全部加载到内存
$result = [];
foreach ($lines as $line) {
$result[] = strtoupper(trim($line));
}
return $result;
}

// 优化后:内存占用小
function processLargeFileGenerator($filename) {
$handle = fopen($filename, 'r');
while (($line = fgets($handle)) !== false) {
yield strtoupper(trim($line));
}
fclose($handle);
}

性能测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 性能对比工具
class PerformanceTest {
public static function benchmark(callable $func, int $iterations = 1000): float {
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$func();
}
return microtime(true) - $start;
}
}

// 测试示例
$array = range(1, 10000);

$time1 = PerformanceTest::benchmark(function() use ($array) {
for ($i = 0; $i < count($array); $i++) {
// 重复计算 count()
}
});

$time2 = PerformanceTest::benchmark(function() use ($array) {
$length = count($array);
for ($i = 0; $i < $length; $i++) {
// 缓存 count() 结果
}
});

echo "重复计算: {$time1}s\n";
echo "缓存结果: {$time2}s\n";
echo "性能提升: " . round($time1 / $time2, 2) . "x\n";

14. 单元测试

问题:PHP 单元测试的最佳实践?

答案:

PHPUnit 基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 安装 PHPUnit
// composer require --dev phpunit/phpunit

// tests/UserTest.php
use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
private User $user;

protected function setUp(): void
{
$this->user = new User('John', '[email protected]');
}

public function testUserCreation(): void
{
$this->assertEquals('John', $this->user->getName());
$this->assertEquals('[email protected]', $this->user->getEmail());
}

public function testEmailValidation(): void
{
$this->expectException(InvalidArgumentException::class);
new User('John', 'invalid-email');
}

/**
* @dataProvider ageProvider
*/
public function testAgeValidation(int $age, bool $expected): void
{
$this->assertEquals($expected, $this->user->isAdult($age));
}

public function ageProvider(): array
{
return [
[17, false],
[18, true],
[25, true],
];
}

public function testDatabaseInteraction(): void
{
// 使用模拟对象
$mockDb = $this->createMock(Database::class);
$mockDb->expects($this->once())
->method('save')
->with($this->user)
->willReturn(true);

$userService = new UserService($mockDb);
$result = $userService->saveUser($this->user);

$this->assertTrue($result);
}
}

// phpunit.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
testdox="true">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
</phpunit>

测试驱动开发(TDD):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. 先写测试
class CalculatorTest extends TestCase
{
public function testAddition(): void
{
$calculator = new Calculator();
$result = $calculator->add(2, 3);
$this->assertEquals(5, $result);
}
}

// 2. 运行测试(失败)
// 3. 编写最少代码使测试通过
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
}

// 4. 重构代码
// 5. 重复循环

15. API 开发

问题:RESTful API 设计原则和实现?

答案:

RESTful 设计原则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 路由设计
// GET /api/users - 获取用户列表
// GET /api/users/123 - 获取特定用户
// POST /api/users - 创建用户
// PUT /api/users/123 - 更新用户
// DELETE /api/users/123 - 删除用户

// API 控制器
class UserApiController
{
public function index(Request $request): JsonResponse
{
$page = $request->get('page', 1);
$limit = $request->get('limit', 10);

$users = User::paginate($limit, ['*'], 'page', $page);

return response()->json([
'data' => $users->items(),
'meta' => [
'current_page' => $users->currentPage(),
'total' => $users->total(),
'per_page' => $users->perPage(),
],
'links' => [
'first' => $users->url(1),
'last' => $users->url($users->lastPage()),
'prev' => $users->previousPageUrl(),
'next' => $users->nextPageUrl(),
]
]);
}

public function show(int $id): JsonResponse
{
$user = User::find($id);

if (!$user) {
return response()->json([
'error' => 'User not found'
], 404);
}

return response()->json(['data' => $user]);
}

public function store(CreateUserRequest $request): JsonResponse
{
try {
$user = User::create($request->validated());

return response()->json([
'data' => $user,
'message' => 'User created successfully'
], 201);
} catch (Exception $e) {
return response()->json([
'error' => 'Failed to create user',
'message' => $e->getMessage()
], 500);
}
}
}

// 请求验证
class CreateUserRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8|confirmed',
];
}

public function messages(): array
{
return [
'email.unique' => 'This email is already taken.',
'password.confirmed' => 'Password confirmation does not match.',
];
}
}

// API 资源转换
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
// 条件字段
'phone' => $this->when($this->phone, $this->phone),
// 关联数据
'posts' => PostResource::collection($this->whenLoaded('posts')),
];
}
}

// API 中间件
class ApiAuthMiddleware
{
public function handle(Request $request, Closure $next)
{
$token = $request->bearerToken();

if (!$token) {
return response()->json(['error' => 'Token required'], 401);
}

try {
$user = JWTAuth::parseToken()->authenticate();
$request->setUserResolver(function () use ($user) {
return $user;
});
} catch (Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}

return $next($request);
}
}

// API 版本控制
// v1/UserController.php
namespace App\Http\Controllers\Api\V1;

class UserController extends BaseController
{
// V1 实现
}

// v2/UserController.php
namespace App\Http\Controllers\Api\V2;

class UserController extends BaseController
{
// V2 实现,向后兼容
}

16. 数组与字符串处理

问题:isset() 和 empty() 的区别?

答案:

函数 检查内容 NULL false 0 “” “0” [] 未定义变量
isset() 变量是否设置且不为NULL false true true true true true false
empty() 变量是否为”空” true true true true true true true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 详细示例
$tests = [
'null' => null,
'false' => false,
'zero' => 0,
'empty_string' => '',
'zero_string' => '0',
'empty_array' => [],
'space' => ' ',
'non_empty' => 'hello'
];

foreach ($tests as $name => $value) {
echo "$name:\n";
echo " isset(): " . (isset($value) ? 'true' : 'false') . "\n";
echo " empty(): " . (empty($value) ? 'true' : 'false') . "\n";
echo "\n";
}

// 实际应用场景
function validateInput($data) {
$errors = [];

// 检查必填字段
if (!isset($data['name']) || empty(trim($data['name']))) {
$errors[] = 'Name is required';
}

// 检查可选字段
if (isset($data['age']) && !is_numeric($data['age'])) {
$errors[] = 'Age must be numeric';
}

// 检查数组字段
if (isset($data['tags']) && !empty($data['tags']) && !is_array($data['tags'])) {
$errors[] = 'Tags must be an array';
}

return $errors;
}

// 空值合并操作符(PHP 7+)
$username = $_GET['user'] ?? $_POST['user'] ?? 'guest';
$config = $userConfig ?? $defaultConfig;

// 空值合并赋值操作符(PHP 7.4+)
$config['timeout'] ??= 30; // 等同于:$config['timeout'] = $config['timeout'] ?? 30;

问题:合并两个数组的方法及区别?

答案:

方法 数字键处理 字符串键处理 重复键处理 性能
array_merge() 重新索引 后者覆盖前者 后者覆盖前者 中等
+ 操作符 保持原键 前者优先 前者优先 最快
array_merge_recursive() 重新索引 递归合并为数组 合并为数组 最慢
array_replace() 保持原键 后者覆盖前者 后者覆盖前者 中等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$arr1 = [0 => 'a', 1 => 'b', 'key1' => 'value1'];
$arr2 = [0 => 'c', 2 => 'd', 'key1' => 'value2', 'key2' => 'value3'];

// 1. array_merge() - 重新索引数字键
$result1 = array_merge($arr1, $arr2);
// [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 'key1' => 'value2', 'key2' => 'value3']

// 2. + 操作符 - 保持原键,左侧优先
$result2 = $arr1 + $arr2;
// [0 => 'a', 1 => 'b', 2 => 'd', 'key1' => 'value1', 'key2' => 'value3']

// 3. array_merge_recursive() - 递归合并
$nested1 = ['a' => ['x' => 1], 'b' => 2];
$nested2 = ['a' => ['y' => 2], 'c' => 3];
$result3 = array_merge_recursive($nested1, $nested2);
// ['a' => ['x' => 1, 'y' => 2], 'b' => 2, 'c' => 3]

// 4. array_replace() - 替换值
$result4 = array_replace($arr1, $arr2);
// [0 => 'c', 1 => 'b', 2 => 'd', 'key1' => 'value2', 'key2' => 'value3']

// 5. 展开操作符(PHP 7.4+)
$result5 = [...$arr1, ...$arr2]; // 类似 array_merge,但更快

// 性能对比
function benchmarkMerge($arr1, $arr2, $iterations = 100000) {
$methods = [
'array_merge' => fn() => array_merge($arr1, $arr2),
'plus_operator' => fn() => $arr1 + $arr2,
'spread_operator' => fn() => [...$arr1, ...$arr2],
'array_replace' => fn() => array_replace($arr1, $arr2),
];

foreach ($methods as $name => $method) {
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$method();
}
$time = microtime(true) - $start;
echo "$name: {$time}s\n";
}
}

问题:字符串处理的常用函数和最佳实践?

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 字符串基础操作
$text = " Hello World! ";

// 1. 长度和截取
strlen($text); // 15 (包含空格)
mb_strlen($text, 'UTF-8'); // 15 (多字节安全)
substr($text, 2, 5); // "Hell"
mb_substr($text, 2, 5, 'UTF-8'); // 多字节安全截取

// 2. 清理和格式化
trim($text); // "Hello World!"
ltrim($text); // "Hello World! "
rtrim($text); // " Hello World!"
strip_tags('<p>Hello</p>'); // "Hello"
htmlspecialchars('<script>'); // "&lt;script&gt;"

// 3. 大小写转换
strtoupper($text); // " HELLO WORLD! "
strtolower($text); // " hello world! "
ucfirst(trim($text)); // "Hello world!"
ucwords(trim($text)); // "Hello World!"
mb_strtoupper($text, 'UTF-8'); // 多字节安全

// 4. 查找和替换
strpos($text, 'World'); // 8
stripos($text, 'world'); // 8 (不区分大小写)
str_replace('World', 'PHP', $text); // " Hello PHP! "
str_ireplace('world', 'PHP', $text); // 不区分大小写替换
preg_replace('/\s+/', ' ', $text); // 正则替换多个空格为单个

// 5. 分割和连接
explode(' ', trim($text)); // ["Hello", "World!"]
implode('-', ['Hello', 'World']); // "Hello-World"
str_split('Hello', 2); // ["He", "ll", "o"]

// 6. 现代字符串处理(PHP 8+)
str_contains($text, 'Hello'); // true
str_starts_with($text, ' Hello'); // true
str_ends_with($text, 'World! '); // true

// 7. 多字节字符串处理
$chinese = "你好世界";
mb_strlen($chinese, 'UTF-8'); // 4
mb_substr($chinese, 0, 2, 'UTF-8'); // "你好"

// 8. 反转包含中文的字符串
function mb_strrev($string, $encoding = 'UTF-8') {
$length = mb_strlen($string, $encoding);
$reversed = '';
for ($i = $length - 1; $i >= 0; $i--) {
$reversed .= mb_substr($string, $i, 1, $encoding);
}
return $reversed;
}

echo mb_strrev("你好世界"); // "界世好你"

// 9. 字符串模板和格式化
$name = "John";
$age = 25;

// sprintf 格式化
$formatted = sprintf("Name: %s, Age: %d", $name, $age);

// 字符串插值
$interpolated = "Name: $name, Age: $age";

// heredoc 和 nowdoc
$heredoc = <<<EOT
Name: $name
Age: $age
EOT;

$nowdoc = <<<'EOT'
Name: $name (不会解析变量)
Age: $age
EOT;

// 10. 字符串验证
function validateString($input, $options = []) {
$errors = [];

// 长度验证
if (isset($options['min_length']) && mb_strlen($input) < $options['min_length']) {
$errors[] = "String too short (min: {$options['min_length']})";
}

if (isset($options['max_length']) && mb_strlen($input) > $options['max_length']) {
$errors[] = "String too long (max: {$options['max_length']})";
}

// 模式验证
if (isset($options['pattern']) && !preg_match($options['pattern'], $input)) {
$errors[] = "String doesn't match required pattern";
}

// 禁止的字符
if (isset($options['forbidden']) && str_contains($input, $options['forbidden'])) {
$errors[] = "String contains forbidden characters";
}

return $errors;
}

// 使用示例
$errors = validateString($userInput, [
'min_length' => 3,
'max_length' => 50,
'pattern' => '/^[a-zA-Z0-9_]+$/',
'forbidden' => '<script>'
]);

问题:如何在 PHP 中实现字符串的截取?

  • 答案:
    • substr() 函数:substr(string $string, int $start, int $length = ?)
      • $string:要截取的字符串。
      • $start:开始位置(从 0 开始)。
      • $length:可选,截取长度。
    • mb_substr() 函数:多字节字符串截取,处理中文字符。

问题:PHP 中的变量作用域有哪些?

  • 答案:
    • 全局作用域:在函数外部定义,在所有函数内部可见。
    • 局部作用域:在函数内部定义,仅在函数内部可见。
    • 静态作用域:在函数内部定义,在函数调用结束后不销毁。
    • 类作用域:在类内部定义,在类的所有方法中可见。
    • 命名空间作用域:在命名空间内部定义,在命名空间的所有类和函数中可见。

PHP 核心机制

1. 超全局变量

  • 作用:提供全局访问变量,无需在每个函数中声明。
  • 类型:数组,键名对应变量名,值对应变量值。
  • 示例:
    1
    2
    $_GET['name'] = 'Alice';
    echo $_GET['name']; // 输出 'Alice'

2. 变量作用域

  • 作用:确定变量的可见性与生命周期。
  • 分类:全局作用域、局部作用域(函数内)。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    $globalVar = 'Global'; // 全局作用域
    function localVar() {
    $localVar = 'Local'; // 局部作用域
    echo $localVar; // 输出 'Local'
    }
    echo $globalVar; // 输出 'Global'
    localVar();
    echo $localVar; // 报错,未定义 $localVar

3. 数据类型与内存管理

  • 数据类型:标量(布尔、整数、浮点数、字符串)、复合(数组、对象)。
  • 内存管理:
    • 引用计数:PHP使用引用计数机制管理内存,每个变量都有一个引用计数器,当引用计数为0时自动释放内存。
    • 垃圾回收:PHP 5.3+引入了垃圾回收机制,用于处理循环引用问题。
    • 内存限制:可通过php.ini中的memory_limit配置项设置PHP脚本的最大内存使用量。
    • 内存泄漏:常见原因包括未释放的资源、循环引用、长时间运行的脚本等。
    • 优化策略:
      • 及时释放大型变量(使用unset())
      • 避免在循环中创建大量对象
      • 使用生成器(Generator)处理大数据集
      • 合理设置内存限制
    • 动态分配:变量在使用时分配内存,无需声明。
    • 引用计数:每个变量有引用计数,当计数为 0 时立即释放内存。
    • 循环引用处理:通过 周期回收算法 检测并清理循环引用的变量(如两个对象互相引用)。
    • 内存泄漏:长时间占用内存,导致系统性能下降。
    • 内存优化:及时释放不再使用的变量,如 unset() 函数。

4. 函数与作用域

  • 函数定义:封装可复用代码,提高代码模块化。
  • 作用域:
    • 全局作用域:在函数外部定义,在所有函数内部可见。
    • 局部作用域:在函数内部定义,仅在函数内部可见。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $globalVar = 'Global';
    function localVar() {
    $localVar = 'Local';
    echo $localVar; // 输出 'Local'
    }
    function localVar2() {
    global $globalVar; // 使用global关键字访问全局变量
    echo $globalVar; // 输出 'Global'
    echo $localVar; // 报错,未定义 $localVar
    }
    localVar();
    localVar2();

框架与设计模式

问题:Laravel框架的核心特性有哪些?

  • 答案:
    • 依赖注入容器:实现控制反转(IoC)
    • 优雅的路由系统:支持RESTful API
    • Eloquent ORM:ActiveRecord模式的ORM实现
    • 中间件:处理HTTP请求的过滤器
    • Blade模板引擎:轻量级且功能强大的模板系统
    • Artisan命令行工具:自动化常见任务
    • 事件系统:实现观察者模式的事件处理

问题:常见的设计模式及其在PHP中的应用?

  • 答案:
    • 单例模式:确保一个类只有一个实例(如数据库连接)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Database {
    private static $instance = null;
    private function __construct() {}
    public static function getInstance() {
    if (self::$instance === null) {
    self::$instance = new self();
    }
    return self::$instance;
    }
    }
    • 工厂模式:创建对象的接口,让子类决定实例化哪个类
    • 观察者模式:对象间的一对多依赖关系(如事件监听)
    • 策略模式:定义一系列算法,使它们可以互相替换(如支付方式)
    • 装饰器模式:动态地给对象添加额外的职责(如中间件)

问题:MVC架构的优缺点?

  • 答案:
    • 优点:
      • 关注点分离,提高代码可维护性
      • 便于团队协作(前端/后端分工)
      • 代码复用性高
      • 利于测试(各层可独立测试)
    • 缺点:
      • 学习成本较高
      • 小型项目可能过度设计
      • 性能开销(多层调用)

数据库与缓存

问题:MySQL的索引是什么?什么情况会导致索引失效?如何查看是否命中索引?

  • 答案:
    • 索引:数据库中一种数据结构,用于快速查找表中的数据。

    • 失效情况:

      • 全表扫描:无索引或索引失效
      • 索引列参与计算:如使用函数、表达式
      • 类型转换:如字符串与数字比较
      • 索引列使用!=或<>:索引失效
      • 索引列使用OR:索引失效
      • 索引列使用LIKE ‘%xxx’:索引失效
    • 查看是否命中索引:

      • 执行计划:EXPLAIN语句查看执行计划,判断是否使用了索引
      • 日志分析:查看数据库日志,分析是否有全表扫描操作
      • 性能分析工具:如MySQL Workbench、Navicat等

      Explain

      • id:查询的标识符,这里是 1,表示这是一个简单查询。
      • select_type:查询类型,SIMPLE 表示这是一个简单的查询,没有子查询、联合查询等复杂结构。
      • table:涉及的表,这里是 sa_user 表。
      • partitions:使用的分区,这里为 (Null),表示没有使用分区。
      • type:连接类型,const 表示通过索引一次就能找到数据,是非常高效的类型。
      • possible_keys:可能使用的索引,这里是 openid 索引。
      • key:实际使用的索引,这里使用了 openid 索引。
      • key_len:使用的索引长度,这里是 1023。
      • ref:与索引比较的列或常量,这里是 const,表示与常量进行比较。
      • rows:预计需要扫描的行数,这里是 1,表示只需要扫描一行数据。
      • filtered:按条件过滤后返回的行数百分比,这里是 100.00,表示全部行都符合条件。
      • Extra:额外信息,这里为 (Null),没有额外的信息。

问题:MySQL索引的类型及其适用场景?

  • 答案:
    • 主键索引:表中的主键,唯一且非空
    • 唯一索引:确保列中的值唯一,可以为空
    • 普通索引:加速查询,无唯一性约束
    • 全文索引:用于全文搜索
    • 复合索引:多列组合索引,遵循最左前缀原则

问题:Redis的数据类型及应用场景?

  • 答案:
    • String:缓存、计数器、分布式锁
    • Hash:存储对象,如用户信息
    • List:消息队列、最新动态
    • Set:去重、交集运算(共同好友)
    • Sorted Set:排行榜、优先级队列

问题:如何优化慢查询?

  • 答案:
    • 索引优化:创建合适的索引,避免全表扫描
    • SQL优化:避免SELECT *,使用LIMIT限制结果集
    • 表结构优化:合理设计表结构,适当冗余
    • 缓存策略:使用Redis等缓存热点数据
    • 分库分表:水平/垂直拆分大表

安全与性能

问题:常见的Web安全漏洞及防范措施?

  • 答案:
    • SQL注入:
      • 漏洞:未过滤的用户输入直接拼接SQL
      • 防范:预处理语句、参数绑定、ORM
    • XSS(跨站脚本):
      • 漏洞:未过滤的用户输入被渲染为HTML
      • 防范:htmlspecialchars()、CSP策略
    • CSRF(跨站请求伪造):
      • 漏洞:利用用户已认证的身份执行未授权操作
      • 防范:CSRF令牌、SameSite Cookie
    • 文件上传漏洞:
      • 漏洞:上传恶意文件(如PHP木马)
      • 防范:文件类型验证、重命名文件、存储在非Web目录

问题:PHP性能优化的方法?

  • 答案:
    • 代码层面:
      • 使用适当的数据结构和算法
      • 避免重复计算(缓存结果)
      • 减少I/O操作(合并读写)
    • 配置层面:
      • 启用OPcache(字节码缓存)
      • 调整PHP-FPM配置(进程数、内存限制)
      • 使用PHP 7+(性能显著提升)
    • 架构层面:
      • 使用缓存系统(Redis、Memcached)
      • 负载均衡(多服务器分担请求)
      • CDN加速静态资源

现代PHP开发

问题:Composer的作用及基本使用?

答案:

Composer 是 PHP 的依赖管理工具,类似于 Node.js 的 npm 或 Python 的 pip。

核心功能:

  • 依赖管理:自动下载和管理第三方库
  • 自动加载:PSR-4 标准的类自动加载
  • 版本控制:精确控制依赖版本
  • 脚本执行:自定义命令和钩子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 基本命令
composer init # 初始化项目
composer require vendor/package # 安装依赖
composer install # 安装所有依赖
composer update # 更新依赖
composer remove vendor/package # 移除依赖
composer dump-autoload # 重新生成自动加载文件

# 开发依赖
composer require --dev phpunit/phpunit

# 全局安装
composer global require laravel/installer

# 指定版本
composer require "monolog/monolog:^2.0"

composer.json 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"name": "mycompany/myproject",
"description": "项目描述",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "[email protected]"
}
],
"require": {
"php": ">=8.0",
"guzzlehttp/guzzle": "^7.0",
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
"psr-4": {
"App\\": "src/",
"Tests\\": "tests/"
},
"files": [
"src/helpers.php"
]
},
"scripts": {
"test": "phpunit",
"cs-fix": "phpcbf --standard=PSR12 src/",
"post-install-cmd": [
"php artisan clear-compiled",
"php artisan optimize"
]
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"minimum-stability": "stable",
"prefer-stable": true
}

版本约束:

1
2
3
4
5
6
7
8
9
10
{
"require": {
"vendor/package": "1.0.0", // 精确版本
"vendor/package": ">=1.0", // 最小版本
"vendor/package": ">=1.0,<2.0", // 版本范围
"vendor/package": "~1.2", // 等同于 >=1.2,<2.0
"vendor/package": "^1.2.3", // 等同于 >=1.2.3,<2.0.0
"vendor/package": "dev-master" // 开发分支
}
}

自动加载使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 引入自动加载文件
require_once 'vendor/autoload.php';

// 使用已安装的包
use GuzzleHttp\Client;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// 创建 HTTP 客户端
$client = new Client();
$response = $client->request('GET', 'https://api.example.com/users');

// 创建日志记录器
$log = new Logger('name');
$log->pushHandler(new StreamHandler('app.log', Logger::WARNING));
$log->warning('This is a warning');

// 使用自己的类(PSR-4 自动加载)
use App\Models\User;
use App\Services\UserService;

$user = new User();
$userService = new UserService();

最佳实践:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 总是提交 composer.lock 文件
git add composer.lock

# 2. 生产环境优化安装
composer install --no-dev --optimize-autoloader

# 3. 检查安全漏洞
composer audit

# 4. 查看过期包
composer outdated

# 5. 验证 composer.json
composer validate

# 6. 清理缓存
composer clear-cache

问题:PHP 8的新特性有哪些?

  • 答案:
    • JIT编译:即时编译,提升性能
    • 命名参数:提高代码可读性
    • 属性(Attributes):替代注释文档
    • 联合类型:更严格的类型检查
    • 空安全操作符(?->):简化空值检查
    • match表达式:更强大的switch替代品

问题:微服务架构在PHP中如何实现?

答案:

微服务架构设计原则:

  • 单一职责:每个服务负责一个业务领域
  • 独立部署:服务可以独立开发、测试、部署
  • 去中心化:避免单点故障
  • 容错性:服务间故障隔离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// 1. 服务拆分示例
// 用户服务 (UserService)
class UserController {
public function getUser($id) {
$user = User::find($id);
return response()->json($user);
}

public function createUser(Request $request) {
$user = User::create($request->all());

// 发布事件到消息队列
event(new UserCreated($user));

return response()->json($user, 201);
}
}

// 订单服务 (OrderService)
class OrderController {
private $userService;

public function __construct(UserServiceClient $userService) {
$this->userService = $userService;
}

public function createOrder(Request $request) {
// 调用用户服务验证用户
$user = $this->userService->getUser($request->user_id);

if (!$user) {
return response()->json(['error' => 'User not found'], 404);
}

$order = Order::create($request->all());
return response()->json($order, 201);
}
}

// 2. 服务间通信
// HTTP 客户端
class UserServiceClient {
private $httpClient;
private $baseUrl;

public function __construct() {
$this->httpClient = new GuzzleHttp\Client();
$this->baseUrl = env('USER_SERVICE_URL', 'http://user-service');
}

public function getUser($id) {
try {
$response = $this->httpClient->get("{$this->baseUrl}/users/{$id}");
return json_decode($response->getBody(), true);
} catch (Exception $e) {
// 熔断器模式
return $this->getUserFromCache($id);
}
}

private function getUserFromCache($id) {
return Cache::get("user:{$id}");
}
}

// 3. API 网关
class ApiGateway {
private $services = [
'users' => 'http://user-service',
'orders' => 'http://order-service',
'products' => 'http://product-service',
];

public function route(Request $request) {
$path = $request->getPathInfo();
$segments = explode('/', trim($path, '/'));

if (count($segments) < 2) {
return response()->json(['error' => 'Invalid path'], 400);
}

$service = $segments[1]; // /api/users/123 -> users

if (!isset($this->services[$service])) {
return response()->json(['error' => 'Service not found'], 404);
}

// 转发请求到对应服务
return $this->forwardRequest($request, $this->services[$service]);
}

private function forwardRequest(Request $request, $serviceUrl) {
$client = new GuzzleHttp\Client();

try {
$response = $client->request(
$request->getMethod(),
$serviceUrl . $request->getPathInfo(),
[
'headers' => $request->headers->all(),
'json' => $request->all(),
'timeout' => 30,
]
);

return response($response->getBody(), $response->getStatusCode());
} catch (Exception $e) {
return response()->json(['error' => 'Service unavailable'], 503);
}
}
}

// 4. 消息队列通信
class UserCreatedListener {
public function handle(UserCreated $event) {
// 发送到消息队列
Queue::push(new SendWelcomeEmail($event->user));
Queue::push(new UpdateUserCache($event->user));
}
}

// 5. 服务发现
class ServiceRegistry {
private $redis;

public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}

public function register($serviceName, $host, $port) {
$serviceInfo = [
'host' => $host,
'port' => $port,
'timestamp' => time(),
];

$this->redis->setex(
"service:{$serviceName}",
60, // TTL 60
json_encode($serviceInfo)
);
}

public function discover($serviceName) {
$serviceInfo = $this->redis->get("service:{$serviceName}");
return $serviceInfo ? json_decode($serviceInfo, true) : null;
}
}

// 6. 健康检查
class HealthCheckController {
public function check() {
$checks = [
'database' => $this->checkDatabase(),
'redis' => $this->checkRedis(),
'external_api' => $this->checkExternalApi(),
];

$allHealthy = array_reduce($checks, function($carry, $check) {
return $carry && $check['status'] === 'ok';
}, true);

return response()->json([
'status' => $allHealthy ? 'ok' : 'error',
'checks' => $checks,
'timestamp' => now()->toISOString(),
], $allHealthy ? 200 : 503);
}

private function checkDatabase() {
try {
DB::select('SELECT 1');
return ['status' => 'ok'];
} catch (Exception $e) {
return ['status' => 'error', 'message' => $e->getMessage()];
}
}
}

Docker 容器化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Dockerfile
FROM php:8.1-fpm-alpine

# 安装依赖
RUN apk add --no-cache \
git \
curl \
libpng-dev \
oniguruma-dev \
libxml2-dev \
zip \
unzip

# 安装 PHP 扩展
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# 安装 Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# 设置工作目录
WORKDIR /var/www

# 复制应用代码
COPY . /var/www

# 安装依赖
RUN composer install --no-dev --optimize-autoloader

# 设置权限
RUN chown -R www-data:www-data /var/www

EXPOSE 9000

CMD ["php-fpm"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# docker-compose.yml
version: '3.8'

services:
user-service:
build: ./user-service
ports:
- "8001:9000"
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- mysql
- redis

order-service:
build: ./order-service
ports:
- "8002:9000"
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
- USER_SERVICE_URL=http://user-service:9000
depends_on:
- mysql
- redis

api-gateway:
build: ./api-gateway
ports:
- "8000:9000"
environment:
- USER_SERVICE_URL=http://user-service:9000
- ORDER_SERVICE_URL=http://order-service:9000

mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: microservices
volumes:
- mysql_data:/var/lib/mysql

redis:
image: redis:alpine
ports:
- "6379:6379"

volumes:
mysql_data:

推荐框架和工具:

  • Lumen:Laravel 的微服务版本
  • Slim Framework:轻量级 PHP 框架
  • Swoole:高性能协程框架
  • ReactPHP:事件驱动的异步框架
  • API Platform:API 优先的框架

现代开发技术栈

17. CI/CD 和 DevOps

问题:PHP 项目的 CI/CD 最佳实践?

答案:

GitHub Actions 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# .github/workflows/ci.yml
name: CI/CD Pipeline

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
php-version: [8.0, 8.1, 8.2]

services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: test_db
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

redis:
image: redis:alpine
ports:
- 6379:6379

steps:
- uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, redis
coverage: xdebug

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Copy environment file
run: cp .env.testing .env

- name: Generate application key
run: php artisan key:generate

- name: Run database migrations
run: php artisan migrate --force

- name: Run code style check
run: vendor/bin/phpcs --standard=PSR12 src/

- name: Run static analysis
run: vendor/bin/phpstan analyse src/ --level=8

- name: Run tests
run: vendor/bin/phpunit --coverage-clover=coverage.xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella

deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v3

- name: Deploy to production
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/html
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo systemctl reload php8.1-fpm
sudo systemctl reload nginx

Docker 多阶段构建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# Dockerfile
# 构建阶段
FROM composer:2 AS composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

# 生产阶段
FROM php:8.1-fpm-alpine AS production

# 安装系统依赖
RUN apk add --no-cache \
nginx \
supervisor \
curl \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
zip \
unzip

# 安装 PHP 扩展
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
gd \
pdo_mysql \
opcache \
bcmath

# 配置 OPcache
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=20000" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini

# 复制应用代码
WORKDIR /var/www/html
COPY . .
COPY --from=composer /app/vendor ./vendor

# 设置权限
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html/storage

# 配置 Nginx
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 80

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

18. 云服务和缓存策略

问题:PHP 应用如何集成云服务和实现高效缓存?

答案:

AWS 服务集成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// AWS SDK 使用
use Aws\S3\S3Client;
use Aws\Ses\SesClient;
use Aws\Sqs\SqsClient;

class CloudService {
private $s3;
private $ses;
private $sqs;

public function __construct() {
$this->s3 = new S3Client([
'version' => 'latest',
'region' => env('AWS_DEFAULT_REGION'),
'credentials' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
],
]);
}

// S3 文件上传
public function uploadFile($file, $bucket, $key) {
try {
$result = $this->s3->putObject([
'Bucket' => $bucket,
'Key' => $key,
'Body' => fopen($file, 'r'),
'ACL' => 'public-read',
'ContentType' => mime_content_type($file),
]);

return $result['ObjectURL'];
} catch (Exception $e) {
Log::error('S3 upload failed: ' . $e->getMessage());
throw new Exception('File upload failed');
}
}
}

多级缓存策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CacheManager {
private $redis;
private $memcached;

public function get($key, $callback = null, $ttl = 3600) {
// L1: 内存缓存(最快)
static $memory = [];
if (isset($memory[$key])) {
return $memory[$key];
}

// L2: Redis 缓存(快)
$value = $this->redis->get($key);
if ($value !== false) {
$memory[$key] = unserialize($value);
return $memory[$key];
}

// L3: 数据库或回调(慢)
if ($callback) {
$value = $callback();
$this->set($key, $value, $ttl);
return $value;
}

return null;
}
}

面试技巧和职业发展

19. 常见面试场景

问题:PHP 面试中的常见技术问题和回答技巧?

答案:

技术深度问题:

  1. “请解释 PHP 的垃圾回收机制”

    • 回答要点:引用计数、循环引用检测、标记清除算法
    • 展示深度:提及 PHP 7+ 的改进、内存优化实践
  2. “如何优化一个慢查询?”

    • 分析步骤:EXPLAIN 分析 → 索引优化 → 查询重写 → 缓存策略
    • 实际案例:分享具体的优化经验
  3. “设计一个高并发的秒杀系统”

    • 架构思路:限流 → 缓存 → 队列 → 数据库优化
    • 技术选型:Redis、消息队列、CDN

项目经验问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 展示代码质量和思考深度
class OrderService {
private $db;
private $cache;
private $queue;

public function createOrder(array $orderData): Order {
// 1. 参数验证
$this->validateOrderData($orderData);

// 2. 库存检查(使用 Redis 原子操作)
if (!$this->checkInventory($orderData['product_id'], $orderData['quantity'])) {
throw new InsufficientInventoryException();
}

// 3. 数据库事务
return $this->db->transaction(function() use ($orderData) {
$order = Order::create($orderData);
$this->updateInventory($orderData['product_id'], $orderData['quantity']);

// 4. 异步处理
$this->queue->push(new SendOrderConfirmationJob($order));

return $order;
});
}

private function checkInventory(int $productId, int $quantity): bool {
$key = "inventory:{$productId}";
$script = "
local current = redis.call('GET', KEYS[1])
if current and tonumber(current) >= tonumber(ARGV[1]) then
return redis.call('DECRBY', KEYS[1], ARGV[1])
else
return -1
end
";

$result = $this->cache->eval($script, [$key], [$quantity]);
return $result >= 0;
}
}

20. 薪资谈判和职业规划

问题:如何进行薪资谈判和职业发展规划?

答案:

薪资谈判策略:

  1. 市场调研

    • 了解行业薪资水平
    • 准备技能清单和项目成果
    • 量化个人贡献(性能提升、成本节约等)
  2. 谈判技巧

    1
    2
    "基于我在 PHP 开发方面的经验,特别是在微服务架构和性能优化方面的贡献,
    我期望的薪资范围是 X-Y。这个范围是基于我对市场的了解和我能为公司带来的价值。"
  3. 非薪资福利

    • 学习机会和培训预算
    • 远程工作政策
    • 股权激励
    • 技术会议参与

职业发展路径:

1
2
3
4
5
初级开发者 → 中级开发者 → 高级开发者 → 技术专家/架构师

团队领导/技术经理

技术总监/CTO

技能发展建议:

  1. 技术深度

    • 精通 PHP 生态系统
    • 掌握多种数据库技术
    • 了解前端技术栈
    • 学习 DevOps 和云服务
  2. 软技能

    • 沟通协调能力
    • 项目管理经验
    • 团队协作精神
    • 持续学习能力
  3. 行业认知

    • 关注技术趋势
    • 参与开源项目
    • 技术分享和写作
    • 建立专业网络

面试准备清单:

  • 复习核心 PHP 概念
  • 准备项目案例和代码示例
  • 了解目标公司的技术栈
  • 准备技术问题和业务问题
  • 模拟编程题练习
  • 准备个人介绍和职业规划
  • 了解薪资市场行情
  • 准备反问面试官的问题

总结

这份 PHP 面试题涵盖了从基础语法到高级架构的各个方面,包括:

  • 基础知识:变量、数据类型、面向对象编程
  • 进阶特性:命名空间、Trait、生成器、PHP 8+ 新特性
  • 实用技能:数据库操作、缓存策略、API 开发
  • 现代技术:微服务、容器化、CI/CD、云服务
  • 软技能:面试技巧、职业规划、薪资谈判

建议根据自己的经验水平和目标职位,重点复习相关章节,并通过实际项目练习来加深理解。记住,面试不仅是技术能力的展示,更是沟通能力和解决问题思路的体现。

持续学习建议:

  • 关注 PHP 官方文档和 RFC
  • 参与开源项目贡献
  • 阅读优秀的 PHP 项目源码
  • 参加技术会议和社区活动
  • 建立个人技术博客

祝你面试顺利!🚀