PHP 面试笔试常见题汇总

PHP 面试笔试常见题汇总

目录

PHP 基础知识

1. 变量与数据类型

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

答案:

基本数据类型:

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

类型判断方法:

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

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

答案:

  • == (宽松比较): 仅比较值,会进行类型转换
  • === (严格比较): 同时比较值和类型,不进行类型转换
// 宽松比较示例
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 中的引用与值传递的区别?

答案:

  • 值传递(默认): 复制变量的值,函数内修改不影响原变量
  • 引用传递: 传递变量的内存地址,函数内修改会影响原变量
// 值传递示例
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 关键字实现单继承
  • 子类可以重写父类方法
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 定义
  • 可包含抽象方法和具体方法
  • 不能直接实例化
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 定义
  • 所有方法都是抽象的
  • 类可以实现多个接口
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:自动加载规范

    • 命名空间与文件路径映射
    • 一个文件一个类
// 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. 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 中的文件上传处理?

答案:

前端表单设置:

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

后端处理流程:

<?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 使用示例:

// 启动 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 使用示例:

// 设置 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 中的魔术方法有哪些?

答案:

常用魔术方法:

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 命名空间的作用和使用方法?

答案:

作用:

  • 解决类名、函数名、常量名冲突
  • 提供更好的代码组织结构
  • 支持自动加载机制
// 文件: 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 单继承的补充)
  • 水平代码复用,避免深层继承
  • 解决多重继承问题
// 定义 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 致命错误,停止执行 较慢 必需的类文件
// 性能测试示例
$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 生成器的作用和使用场景?

答案:

作用:

  • 节省内存,处理大数据集
  • 延迟计算,按需生成数据
  • 简化迭代器实现
// 传统方式:占用大量内存
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. 联合类型
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. 枚举(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. 只读类
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. 减少重复计算
// 优化前
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);
}

性能测试:

// 性能对比工具
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 基础:

// 安装 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. 先写测试
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 设计原则:

// 路由设计
// 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
// 详细示例
$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() 保持原键 后者覆盖前者 后者覆盖前者 中等
$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";
    }
}

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

答案:

// 字符串基础操作
$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. 超全局变量

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

2. 变量作用域

  • 作用:确定变量的可见性与生命周期。
  • 分类:全局作用域、局部作用域(函数内)。
  • 示例:
    $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. 函数与作用域

  • 函数定义:封装可复用代码,提高代码模块化。
  • 作用域:
    • 全局作用域:在函数外部定义,在所有函数内部可见。
    • 局部作用域:在函数内部定义,仅在函数内部可见。
  • 示例:
    $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中的应用?

  • 答案:
    • 单例模式:确保一个类只有一个实例(如数据库连接)
    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 标准的类自动加载
  • 版本控制:精确控制依赖版本
  • 脚本执行:自定义命令和钩子
# 基本命令
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 配置:

{
    "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
}

版本约束:

{
    "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"  // 开发分支
    }
}

自动加载使用:

// 引入自动加载文件
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. 总是提交 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. 服务拆分示例
// 用户服务 (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 容器化:

# 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"]
# 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 配置:

# .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 多阶段构建:

# 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 服务集成:

// 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');
        }
    }
}

多级缓存策略:

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

项目经验问题:

// 展示代码质量和思考深度
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. 谈判技巧

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

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

职业发展路径:

初级开发者 → 中级开发者 → 高级开发者 → 技术专家/架构师
                                    ↓
                              团队领导/技术经理
                                    ↓
                              技术总监/CTO

技能发展建议:

  1. 技术深度

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

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

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

面试准备清单:

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

总结

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

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

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

持续学习建议:

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

祝你面试顺利!🚀