当前位置:首页>php>PHP 单元测试进阶:PHPUnit + Mockery 实战指南,写出让你自己都放心的测试

PHP 单元测试进阶:PHPUnit + Mockery 实战指南,写出让你自己都放心的测试

  • 2026-07-02 16:49:04
PHP 单元测试进阶:PHPUnit + Mockery 实战指南,写出让你自己都放心的测试

摘要:你写的 PHP 代码真的可靠吗?本文带你从零掌握 PHPUnit 进阶技巧与 Mockery 模拟框架的黄金组合,通过真实业务场景演示数据提供者、边界测试、Mock/Stub/Spy 核心用法,手把手教你写出覆盖率 95%+ 的高质量单元测试,告别上线心跳加速的恐惧。

一、为什么你的单元测试总是「聊胜于无」?

相信你不陌生这种场景:

项目测试覆盖率:12%
PM:"这个功能测好了吗?"
你:"测了,没问题!"(心里:刚手动点了一下,应该没事吧……)

大多数 PHP 项目的测试问题不是"没写",而是写了也没用:

  • ✗ 只测最 happy path,边界条件全漏
  • ✗ 测试依赖真实数据库/外部接口,跑一次要 30 秒
  • ✗ 测试代码比业务代码还难懂
  • ✗ 不知道怎么 Mock 掉外部依赖
  • ✗ 测试通过了,但上线还是出 bug

本文的目标:用 PHPUnit + Mockery 的黄金组合,让你的测试从「摆设」变成「护城河」。


二、环境准备

# 安装 PHPUnit 10.x(需要 PHP 8.1+)
composer require --dev phpunit/phpunit ^10.0

# 安装 Mockery(最好用的 PHP Mock 框架)
composer require --dev mockery/mockery

# 验证安装
./vendor/bin/phpunit --version
# PHPUnit 10.5.20 by Sebastian Bergmann and contributors.

phpunit.xml 配置参考(放项目根目录):

<?xml version="1.0" encoding="UTF-8"?>
<phpunitxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">

<testsuites>
<testsuitename="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuitename="Feature">
<directory>tests/Feature</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<htmloutputDirectory="coverage-report"/>
</report>
</coverage>
<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>

三、PHPUnit 进阶技巧

3.1 数据提供者(Data Provider):一个测试覆盖 N 种场景

新手经常的写法:

// ❌ 低效写法:三个方法做同一件事
publicfunctiontestEmailValid(): void
{
$this->assertTrue(validateEmail('user@example.com'));
}

publicfunctiontestEmailInvalidMissingAt(): void
{
$this->assertFalse(validateEmail('userexample.com'));
}

publicfunctiontestEmailInvalidEmpty(): void
{
$this->assertFalse(validateEmail(''));
}

用 Data Provider 重写,优雅且全面:

<?php
// tests/Unit/ValidatorTest.php

usePHPUnit\Framework\TestCase;
usePHPUnit\Framework\Attributes\DataProvider;

classValidatorTestextendsTestCase
{
/**
     * 邮箱格式验证测试
     * 使用 Data Provider 覆盖所有边界场景
     */

#[DataProvider('emailProvider')]
publicfunctiontestEmailValidation(string$emailbool$expected): void
{
$validator = newEmailValidator();
$this->assertSame($expected$validator->isValid($email));
    }

/**
     * 提供测试数据:[邮箱, 期望结果]
     */

publicstaticfunctionemailProvider(): array
{
return [
// 正常场景
'有效邮箱-标准格式'        => ['user@example.com'true],
'有效邮箱-含子域名'        => ['user@mail.example.com'true],
'有效邮箱-含加号'          => ['user+tag@example.com'true],

// 边界场景
'无效邮箱-缺少@符号'       => ['userexample.com'false],
'无效邮箱-缺少域名'        => ['user@'false],
'无效邮箱-缺少用户名'      => ['@example.com'false],
'无效邮箱-空字符串'        => [''false],
'无效邮箱-含空格'          => ['user @example.com'false],
'无效邮箱-特殊字符域名'    => ['user@exam_ple.com'false],

// 极端场景
'超长邮箱-254字符以内'     => [str_repeat('a'243) . '@example.com'true],
'超长邮箱-超过254字符'     => [str_repeat('a'244) . '@example.com'false],
        ];
    }
}

效果对比:原来需要 10 个测试方法,现在 1 个方法覆盖 11 个场景,可读性反而更好。


3.2 异常测试:别只测成功,更要测失败

<?php
// src/OrderService.php

classOrderService
{
/**
     * 创建订单
     *
     * @throws InvalidArgumentException 金额不合法时
     * @throws OutOfStockException 库存不足时
     */

publicfunctioncreateOrder(int$productIdint$quantityfloat$amount): Order
{
if ($amount <= 0) {
thrownewInvalidArgumentException("订单金额必须大于0,当前值:{$amount}");
        }

if ($quantity <= 0) {
thrownewInvalidArgumentException("购买数量必须大于0,当前值:{$quantity}");
        }

// ... 业务逻辑
    }
}
<?php
// tests/Unit/OrderServiceTest.php

usePHPUnit\Framework\TestCase;
usePHPUnit\Framework\Attributes\DataProvider;

classOrderServiceTestextendsTestCase
{
private OrderService $service;

protectedfunctionsetUp(): void
{
// 每个测试方法执行前自动调用
$this->service = newOrderService();
    }

/**
     * 测试非法参数时抛出正确异常
     */

#[DataProvider('invalidOrderProvider')]
publicfunctiontestCreateOrderThrowsOnInvalidInput(
int$productId,
int$quantity,
float$amount,
string$expectedException,
string$expectedMessage
): void
{
$this->expectException($expectedException);
$this->expectExceptionMessageMatches($expectedMessage);

$this->service->createOrder($productId$quantity$amount);
    }

publicstaticfunctioninvalidOrderProvider(): array
{
return [
'金额为零'    => [110.0,  InvalidArgumentException::class'/金额必须大于0/'],
'金额为负数'  => [11, -10.0InvalidArgumentException::class'/金额必须大于0/'],
'数量为零'    => [10100.0InvalidArgumentException::class'/数量必须大于0/'],
'数量为负数'  => [1, -5100.0InvalidArgumentException::class'/数量必须大于0/'],
        ];
    }
}

3.3 测试夹具(Fixtures):setUp 与 tearDown 的正确姿势

<?php

classDatabaseTestextendsTestCase
{
private PDO $pdo;

/**
     * 在整个测试类执行前调用一次(静态方法)
     * 适合:创建数据库表、初始化连接池
     */

publicstaticfunctionsetUpBeforeClass(): void
{
// 创建测试用内存数据库(不污染正式库)
self::$pdo = newPDO('sqlite::memory:');
self::$pdo->exec('CREATE TABLE users (id INT, name TEXT, email TEXT)');
    }

/**
     * 每个测试方法执行前调用
     * 适合:重置状态、插入基准测试数据
     */

protectedfunctionsetUp(): void
{
$this->pdo->exec("DELETE FROM users"); // 清空旧数据
$this->pdo->exec(
"INSERT INTO users VALUES (1, '张三', 'zhangsan@test.com')"
        );
    }

/**
     * 每个测试方法执行后调用
     * 适合:清理临时文件、回滚事务
     */

protectedfunctiontearDown(): void
{
$this->pdo->exec("DELETE FROM users WHERE id > 1"); // 只保留基准数据
    }

publicfunctiontestFindUserById(): void
{
$repo = newUserRepository($this->pdo);
$user = $repo->findById(1);

$this->assertSame('张三'$user->name);
$this->assertSame('zhangsan@test.com'$user->email);
    }
}

四、Mockery 深度实战

这才是重头戏。真实项目里,你的类总是依赖其他服务:数据库、Redis、第三方 API、邮件服务……单元测试要快、独立、不副作用,就必须 Mock 掉这些依赖

4.1 三种 Test Double 搞清楚

类型
定义
使用场景
Stub
返回预设值,不关心是否被调用
模拟数据库查询结果
Mock
预设期望调用次数和参数,不满足则测试失败
验证某方法必须被调用
Spy
真实执行但记录调用情况,事后验证
验证方法被调用过,但不限制调用时机

4.2 实战:用户注册服务测试

先看被测代码:

<?php
// src/UserRegistrationService.php

classUserRegistrationService
{
publicfunction__construct(
privatereadonly UserRepositoryInterface $userRepo,   // 数据库操作
privatereadonly EmailServiceInterface $emailService, // 发送邮件
privatereadonly CacheInterface $cache,               // Redis 缓存
privatereadonly EventDispatcher $events              // 事件系统
{}

/**
     * 注册新用户
     *
     * @throws DuplicateEmailException 邮箱已被注册
     */

publicfunctionregister(string$emailstring$password): User
{
// 1. 检查邮箱是否重复
if ($this->userRepo->existsByEmail($email)) {
thrownewDuplicateEmailException("邮箱 {$email} 已被注册");
        }

// 2. 创建用户
$user = newUser(
            email: $email,
            passwordHash: password_hash($password, PASSWORD_ARGON2ID)
        );
$this->userRepo->save($user);

// 3. 发送欢迎邮件(异步,失败不影响注册)
try {
$this->emailService->sendWelcome($user);
        } catch (\Exception$e) {
// 记录日志但不抛出异常
logger()->warning('欢迎邮件发送失败', ['email' => $email'error' => $e->getMessage()]);
        }

// 4. 清除用户列表缓存
$this->cache->delete('users:list');

// 5. 触发事件
$this->events->dispatch(newUserRegistered($user));

return$user;
    }
}

测试代码(重点看 Mockery 用法):

<?php
// tests/Unit/UserRegistrationServiceTest.php

useMockery;
useMockery\MockInterface;
usePHPUnit\Framework\TestCase;

classUserRegistrationServiceTestextendsTestCase
{
private MockInterface $userRepo;
private MockInterface $emailService;
private MockInterface $cache;
private MockInterface $events;
private UserRegistrationService $service;

protectedfunctionsetUp(): void
{
// 创建 Mock 对象(实现对应接口)
$this->userRepo     = Mockery::mock(UserRepositoryInterface::class);
$this->emailService = Mockery::mock(EmailServiceInterface::class);
$this->cache        = Mockery::mock(CacheInterface::class);
$this->events       = Mockery::mock(EventDispatcher::class);

$this->service = newUserRegistrationService(
$this->userRepo,
$this->emailService,
$this->cache,
$this->events
        );
    }

/**
     * 测试场景1:正常注册流程
     * 验证:save/sendWelcome/cache.delete/events.dispatch 都被正确调用
     */

publicfunctiontestSuccessfulRegistration(): void
{
$email    = 'newuser@example.com';
$password = 'SecurePassword123!';

// ===== 设置期望(Mock)=====

// 邮箱不存在(Stub:返回 false)
$this->userRepo
            ->shouldReceive('existsByEmail')
            ->once()
            ->with($email)
            ->andReturn(false);

// 必须调用 save,参数是 User 类型(Mock:验证调用)
$this->userRepo
            ->shouldReceive('save')
            ->once()
            ->with(Mockery::type(User::class));

// 欢迎邮件必须发送一次(Mock)
$this->emailService
            ->shouldReceive('sendWelcome')
            ->once()
            ->with(Mockery::type(User::class));

// 缓存必须清除(Mock)
$this->cache
            ->shouldReceive('delete')
            ->once()
            ->with('users:list');

// 事件必须触发(Mock)
$this->events
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(UserRegistered::class));

// ===== 执行 =====
$user = $this->service->register($email$password);

// ===== 断言 =====
$this->assertInstanceOf(User::class$user);
$this->assertSame($email$user->email);
$this->assertTrue(password_verify($password$user->passwordHash));
    }

/**
     * 测试场景2:邮箱已存在时抛出异常
     */

publicfunctiontestRegistrationFailsWhenEmailExists(): void
{
$email = 'existing@example.com';

// 邮箱已存在
$this->userRepo
            ->shouldReceive('existsByEmail')
            ->once()
            ->with($email)
            ->andReturn(true);

// !! 关键:以下方法不应被调用
$this->userRepo->shouldNotReceive('save');
$this->emailService->shouldNotReceive('sendWelcome');
$this->cache->shouldNotReceive('delete');
$this->events->shouldNotReceive('dispatch');

$this->expectException(DuplicateEmailException::class);
$this->expectExceptionMessageMatches('/已被注册/');

$this->service->register($email'password123');
    }

/**
     * 测试场景3:发送邮件失败不影响注册成功
     * 演示:异常不向上传播的测试
     */

publicfunctiontestRegistrationSucceedsEvenIfEmailFails(): void
{
$email = 'user@example.com';

$this->userRepo->shouldReceive('existsByEmail')->andReturn(false);
$this->userRepo->shouldReceive('save')->once();

// 邮件服务抛出异常
$this->emailService
            ->shouldReceive('sendWelcome')
            ->once()
            ->andThrow(new\RuntimeException('SMTP 连接超时'));

// 缓存和事件仍然应该执行
$this->cache->shouldReceive('delete')->once()->with('users:list');
$this->events->shouldReceive('dispatch')->once();

// 不应该抛出异常(邮件失败被吞掉了)
$user = $this->service->register($email'password');
$this->assertInstanceOf(User::class$user);
    }

/**
     * 每个测试方法结束后清理 Mockery(必须!)
     */

protectedfunctiontearDown(): void
{
Mockery::close();
    }
}

4.3 高阶技巧:Spy、部分 Mock、自定义匹配器

<?php

classAdvancedMockeryTestextendsTestCase
{
/**
     * Spy:允许真实执行,事后验证是否调用过
     */

publicfunctiontestWithSpy(): void
{
$emailService = Mockery::spy(EmailServiceInterface::class);

$service = newNotificationService($emailService);
$service->notifyAdmins('系统报警');

// 事后验证(不预设期望,而是事后检查)
$emailService->shouldHaveReceived('send')
            ->with(Mockery::on(function ($to) {
returnstr_contains($to'@admin.');
            }));
    }

/**
     * 部分 Mock(Partial Mock):真实类 + 部分方法替换
     * 适合:只想 Mock 某个有副作用的方法
     */

publicfunctiontestPartialMock(): void
{
// 创建部分 Mock:只 Mock getCurrentTime 方法
$service = Mockery::mock(OrderService::class)->makePartial();
$service->shouldReceive('getCurrentTime')
            ->andReturn(new\DateTimeImmutable('2026-01-01 12:00:00'));

// 其他方法走真实逻辑
$order = $service->createTimedOrder(11100.0);
$this->assertSame('2026-01-01'$order->createdAt->format('Y-m-d'));
    }

/**
     * 自定义参数匹配器
     * 适合:复杂对象的参数验证
     */

publicfunctiontestCustomMatcher(): void
{
$repo = Mockery::mock(OrderRepositoryInterface::class);

$repo->shouldReceive('save')
            ->once()
            ->with(Mockery::on(function (Order $order) {
                // 自定义验证逻辑
return$order->getAmount() > 0
                    && $order->getStatus() === Order::STATUS_PENDING
                    && $order->getUserId() > 0;
            }));

$service = newOrderService($repo);
$service->placeOrder(userId42amount199.0);
    }

/**
     * 模拟连续调用返回不同值
     * 适合:测试重试逻辑
     */

publicfunctiontestConsecutiveCalls(): void
{
$apiClient = Mockery::mock(ApiClientInterface::class);

// 前两次失败,第三次成功
$apiClient->shouldReceive('request')
            ->times(3)
            ->andReturn(
                ['status' => 500'body' => 'Server Error'],  // 第1
                ['status' => 500'body' => 'Server Error'],  // 第2
                ['status' => 200'body' => '{"ok": true}']   // 第3次(成功)
            );

$retryService = newRetryableApiService($apiClient, maxRetries: 3);
$result = $retryService->callWithRetry('/api/endpoint');

$this->assertSame(200$result['status']);
    }

protectedfunctiontearDown(): void
{
Mockery::close();
    }
}

五、性能对比:Mock 与真实依赖的速度差异

用一个真实项目的测试套件对比:

测试套件:UserRegistrationService(含完整依赖链)

方案A:使用真实数据库 + 真实邮件服务
  运行 1 个测试:约 1200ms
  运行 100 个测试:约 120,000ms(2分钟)
  问题:依赖环境、有副作用、不能并行

方案B:使用 Mockery 隔离所有依赖
  运行 1 个测试:约 8ms
  运行 100 个测试:约 800ms(不到1秒)
  优势:纯内存、无副作用、可并行、CI友好

速度提升:150x(150倍!)
# 生成覆盖率报告(需要 xdebug 或 pcov)
./vendor/bin/phpunit --coverage-html=coverage-report

# 并行运行测试(PHPUnit 10 支持)
./vendor/bin/phpunit --processes=4

# 只跑指定测试套件
./vendor/bin/phpunit --testsuite=Unit

# 只跑匹配名字的测试
./vendor/bin/phpunit --filter=testSuccessfulRegistration

六、测试质量清单

在 Push 代码前,对照这 6 项检查:

✅ 1. 覆盖率达标

./vendor/bin/phpunit --coverage-text | grep "Lines:"
# 目标:核心业务类 ≥ 80%,关键算法 ≥ 95%

✅ 2. 边界条件已覆盖

  • 空字符串、空数组、null 值
  • 数字边界(0、负数、最大值)
  • 异常路径(方法抛出异常时)

✅ 3. Mock 验证到位

  • shouldReceive()->once()
     而不是懒写 shouldReceive()->andReturn()
  • shouldNotReceive()
     明确表达"不该被调用"
  • Mockery::close()
     在 tearDown 中清理

✅ 4. 测试名称表达意图

// ❌ 不知所云
publicfunctiontest1(): void{}

// ✅ 一目了然(方法名即文档)
publicfunctiontestRegistrationFailsWhenEmailAlreadyExists(): void{}
publicfunctiontestEmailValidationAcceptsSubdomainAddresses(): void{}

✅ 5. 测试相互独立

  • 每个测试不依赖其他测试的执行顺序
  • setUp() 保证每次都从干净状态开始

✅ 6. 测试够快

# 单元测试整套跑完应该 < 5 秒
time ./vendor/bin/phpunit --testsuite=Unit

七、总结

PHPUnit + Mockery 的黄金组合,给你带来的不只是测试覆盖率数字:

能力
效果
Data Provider
1 个方法覆盖 N 个场景,可读性更强
Mock/Stub
测试速度 150x,完全隔离外部依赖
shouldNotReceive
验证"不该发生"的行为
Partial Mock
只替换有副作用的方法
Spy
事后验证,更灵活的断言

写好测试的本质是:把你对代码的「期望」变成可执行的规范。当你能自信地跑通 100 个测试后按下 merge,那种安全感,值得。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 13:28:27 HTTP/2.0 GET : https://f.mffb.com.cn/a/494975.html
  2. 运行时间 : 0.221844s [ 吞吐率:4.51req/s ] 内存消耗:4,714.65kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=2a7481adbca1091ee11bba4e922d2bf3
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000532s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000603s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000765s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.003433s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000513s ]
  6. SELECT * FROM `set` [ RunTime:0.002408s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000525s ]
  8. SELECT * FROM `article` WHERE `id` = 494975 LIMIT 1 [ RunTime:0.002748s ]
  9. UPDATE `article` SET `lasttime` = 1783056507 WHERE `id` = 494975 [ RunTime:0.004418s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.003476s ]
  11. SELECT * FROM `article` WHERE `id` < 494975 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000993s ]
  12. SELECT * FROM `article` WHERE `id` > 494975 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.011716s ]
  13. SELECT * FROM `article` WHERE `id` < 494975 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.036194s ]
  14. SELECT * FROM `article` WHERE `id` < 494975 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.030488s ]
  15. SELECT * FROM `article` WHERE `id` < 494975 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.037414s ]
0.223454s