沈学 發表於 2026-1-8 08:16:00

2026 年 PHP 的三大优势 这门"老将"为何依然重要

<h1 id="2026-年-php-的三大优势-这门老将为何依然重要">2026 年 PHP 的三大优势 这门"老将"为何依然重要</h1>
<p>PHP "快死了"这句话喊了这么多年,如果真有来世,它的简历应该已经相当可观了。</p>
<p>你大概见过这些论调:"PHP 过时了"、"现在没人用 PHP 做正经系统了"、"只有老项目还在用"。这些说法也不算全错——确实有大量遗留 PHP 代码在运行。但还有另一个现实很少被提及:PHP 仍然在驱动大量生产环境的后端系统,新的 PHP 项目也在不断出现,因为团队想要的东西和五年、十年前一样:</p>
<ul>
<li>可预测的部署流程</li>
<li>快速迭代</li>
<li>成熟的生态</li>
<li>能够经受多人协作、多年维护的可读代码</li>
</ul>
<p>我喜欢这类问题,因为它逼你把话说清楚。不是"我喜欢用",不是"它很流行",而是你在生产代码中能实际指出的工程优势。</p>
<p>下面是我的回答,写给两类读者:</p>
<ul>
<li><strong>如果你刚接触后端开发</strong>:你会得到一个清晰的心智模型,理解 PHP 为何仍然适合 Web 系统。</li>
<li><strong>如果你经验丰富</strong>:你会看到现代 PHP 实践(类型、静态分析、清晰边界、务实的 API 模式)如何把"PHP 容易上手"变成"PHP 可靠"。</li>
</ul>
<p>我会尽量用平实的语言,但不会回避技术细节。真实的系统本来就是技术性的。目标是让这些技术内容变得易懂且实用。</p>
<p>原文 2026 年 PHP 的三大优势 这门"老将"为何依然重要</p>
<h2 id="什么是优势在真实后端工作中的定义">什么是"优势":在真实后端工作中的定义</h2>
<p>开发者比较语言时,讨论经常跑偏到性能基准、语法偏好或者互联网文化。但当你在构建 API 或 Web 后端时,"优势"通常意味着一些更无聊——也更重要的东西:</p>
<ul>
<li>能否快速交付功能而不埋下维护陷阱?</li>
<li>能否集成数据库、队列和第三方 API 而不从头造轮子?</li>
<li>能否处理混乱的数据和边缘情况而不把代码变成鬼屋?</li>
</ul>
<p>这就是 PHP 最擅长的领域。不是因为它最优雅,而是因为它的形状刚好契合大多数 Web 后端。</p>
<p>带着这个思路,来看 PHP 在 2026 年的三大优势。</p>
<h2 id="优势一web-原生的生产力php-天然契合-http-世界">优势一:Web 原生的生产力(PHP 天然契合 HTTP 世界)</h2>
<p>大多数后端都是 HTTP 机器。这不是比喻,而是日常工作:</p>
<ul>
<li>请求进来</li>
<li>校验并规范化</li>
<li>调用服务 / 数据库 / 外部 API</li>
<li>返回 JSON</li>
<li>记录日志和追踪</li>
<li>循环</li>
</ul>
<p>PHP 的第一个优势是它在这个循环里感觉很自然。你不需要在处理请求之前"搭建世界"。PHP 的默认模型就是面向 Web 的,这一点往往被低估了。</p>
<h3 id="经典的-php-执行模型为何仍然有用">经典的 PHP 执行模型为何仍然有用</h3>
<p>PHP 传统的请求生命周期很简单:</p>
<ol>
<li>开始请求</li>
<li>运行代码</li>
<li>返回响应</li>
<li>结束请求</li>
</ol>
<p>然后下一个请求从头开始。</p>
<p>有人把这当成相对于长驻服务器的劣势,但在实践中它往往是优势:</p>
<ul>
<li>内存泄漏不会那么致命,因为进程会回收。</li>
<li>每个请求天然隔离。</li>
<li>不太可能意外依赖内存状态。</li>
<li>调试往往更简单,因为每个请求有清晰的边界。</li>
</ul>
<p>你也可以用长驻模式跑 PHP(RoadRunner、Swoole 等),它们在特定场景下确实很好。但经典模型对许多 API 仍然是可靠的默认选择,因为它稳定且对运维友好。</p>
<h3 id="一个纯-php的-api-入口展示基本形态">一个"纯 PHP"的 API 入口(展示基本形态)</h3>
<p>即使你在生产环境使用 Laravel 或 Symfony(大多数正经应用确实该用),看看 PHP 为何在 Web 工作中高效还是有帮助的。</p>
<pre><code class="language-php">&lt;?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
header('Content-Type: application/json; charset=utf-8');
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
function jsonResponse(array $payload, int $status = 200): void {
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
function readJsonBody(): array {
    $raw = file_get_contents('php://input') ?: '';
    $data = json_decode($raw, true);
    return is_array($data) ? $data : [];
}
if ($method === 'GET' &amp;&amp; $path === '/health') {
    jsonResponse(['ok' =&gt; true, 'time' =&gt; date(DATE_ATOM)]);
}
if ($method === 'POST' &amp;&amp; $path === '/users') {
    $body = readJsonBody();
    $email = strtolower(trim((string)($body['email'] ?? '')));
    $name= trim((string)($body['name'] ?? ''));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
      jsonResponse(['error' =&gt; 'Invalid email'], 422);
    }
    if ($name === '') {
      jsonResponse(['error' =&gt; 'Name is required'], 422);
    }
    $id = random_int(1000, 9999);
    jsonResponse(['id' =&gt; $id, 'email' =&gt; $email, 'name' =&gt; $name], 201);
}
jsonResponse(['error' =&gt; 'Not found'], 404);
</code></pre>
<p>这不是"最佳实践架构",但它演示了核心思想:PHP 的 Web 循环直接且易懂。这就是基础的生产力优势。</p>
<h3 id="快与乱的区别薄-handler真服务">"快"与"乱"的区别:薄 handler,真服务</h3>
<p>PHP 的 Web 生产力要成为长期优势,前提是保持边界清晰。最容易失去这个优势的方式就是把所有东西都塞进控制器。</p>
<p>一个可扩展的模式:</p>
<ul>
<li><strong>Handler / 控制器</strong>:解码请求、调用服务、编码响应</li>
<li><strong>服务</strong>:业务逻辑 + 编排</li>
<li><strong>Repository / Client</strong>:存储 + 外部 API 调用</li>
</ul>
<p>这是一个与框架无关的小例子:</p>
<pre><code class="language-php">final class CreateUserHandler
{
    public function __construct(
      private readonly UserService $service
    ) {}
    public function __invoke(array $body): array
    {
      $input = CreateUserInput::fromArray($body);
      $user= $this-&gt;service-&gt;create($input);
      return UserResource::toArray($user);
    }
}
</code></pre>
<p>Handler 读起来像一段叙述。这就对了。</p>
<p>现在服务来做真正的决策:</p>
<pre><code class="language-php">final class UserService
{
    public function __construct(
      private readonly UserRepository $users
    ) {}
    public function create(CreateUserInput $input): User
    {
      if ($this-&gt;users-&gt;existsByEmail($input-&gt;email)) {
            throw new DomainException('Email already registered');
      }
      $user = User::register($input-&gt;email, $input-&gt;name);
      $this-&gt;users-&gt;save($user);
      return $user;
    }
}
</code></pre>
<p>这个结构并不花哨,但它能防止代码库在六个月内变成意大利面。</p>
<h3 id="真实世界的-api-工作超时和重试是功能的一部分">真实世界的 API 工作:超时和重试是功能的一部分</h3>
<p>PHP 保持实用的一个原因是,做 PHP 后端的团队往往很早就被迫面对 Web 的现实。不是因为 PHP 特殊,而是因为 Web 本身就不宽容。</p>
<p>如果你调用外部 API 却不设超时、不设重试策略,你就是在埋下未来的事故。</p>
<p>下面是一个用 Guzzle 写的封装,在生产环境中能正经干活:</p>
<pre><code class="language-php">use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
final class ShippingClient
{
    private Client $http;
    public function __construct(string $baseUrl, string $token)
    {
      $this-&gt;http = new Client([
            'base_uri' =&gt; rtrim($baseUrl, '/') . '/',
            'timeout' =&gt; 3.0,
            'connect_timeout' =&gt; 1.0,
            'headers' =&gt; [
                'Authorization' =&gt; "Bearer {$token}",
                'Accept' =&gt; 'application/json',
            ],
      ]);
    }
    public function createLabel(array $payload): array
    {
      $attempts = 0;
      while (true) {
            $attempts++;
            try {
                $resp = $this-&gt;http-&gt;post('labels', ['json' =&gt; $payload]);
                $data = json_decode((string)$resp-&gt;getBody(), true);
                return is_array($data) ? $data : [];
            } catch (GuzzleException $e) {
                if ($attempts &gt;= 3) {
                  throw new RuntimeException('Shipping API failed after retries', 0, $e);
                }
                // small exponential backoff + jitter
                usleep((int)(100_000 * $attempts) + random_int(0, 50_000));
            }
      }
    }
}
</code></pre>
<p>当互联网本身是你的依赖时,这种代码是必须的——而 PHP 很适应这个场景。</p>
<p><strong>为什么这是优势一</strong>:PHP 契合 HTTP 工作的形状,让团队能快速构建功能而不用与平台对抗。</p>
<h2 id="优势二生态成熟度composer--框架--标准降低风险">优势二:生态成熟度(Composer + 框架 + 标准降低风险)</h2>
<p>PHP 的第二个优势是杠杆。</p>
<p>很多语言都能做 Web 开发。但能让"无聊的部分"以可复用的方式被解决、让团队能招到已经熟悉这些模式的人,这样的成熟生态并不多。</p>
<p>当你选择 PHP,你选择的不只是语法,还有:</p>
<ul>
<li>Composer + Packagist</li>
<li>Laravel / Symfony(以及其他框架)</li>
<li>PSR 标准(互操作性)</li>
<li>稳定的工具链(测试、静态分析、格式化、重构)</li>
</ul>
<p>生态的成熟度能降低风险。风险才是真正花钱的地方。</p>
<h3 id="composer让结构化成为常态的安静基石">Composer:让结构化成为常态的安静基石</h3>
<p>Composer 不只是依赖管理——它推动你走向模块化的代码库,用自动加载和命名空间。</p>
<p>一个最小的例子:</p>
<pre><code class="language-json">{
"require": {
    "php": "^8.2",
    "monolog/monolog": "^3.0",
    "guzzlehttp/guzzle": "^7.0"
},
"autoload": {
    "psr-4": {
      "App\\": "src/"
    }
}
}
</code></pre>
<p>一旦你采用 PSR-4 自动加载,你的代码就不再是"文件",而开始变成"模块"。这个转变是现代 PHP 比老刻板印象更易维护的重要原因。</p>
<h3 id="框架用无聊的方案解决无聊的问题这正是重点">框架:用无聊的方案解决无聊的问题(这正是重点)</h3>
<p>框架可能被过度使用,但"不用框架"的做法在应用增长后往往更糟。</p>
<p>Laravel 和 Symfony 为你不想重新发明的东西提供了可靠的默认方案:</p>
<ul>
<li>路由和中间件</li>
<li>校验和请求处理</li>
<li>DI 容器模式</li>
<li>缓存和队列</li>
<li>数据库迁移</li>
<li>结构化的错误处理</li>
</ul>
<p>大多数生产事故不是来自精妙的业务逻辑,而是来自胶水代码:超时、重试、不一致的校验、部分失败、意外的 payload、不一致的错误响应。</p>
<p>框架默认方案减少这些事故,因为你建立在已经经历过成千上万个生产系统考验的模式之上。</p>
<h3 id="psr-标准让你的代码不再被锁死">PSR 标准:让你的代码不再被锁死</h3>
<p>标准在你集成库或者超出最初决策时最为重要。</p>
<p>例如:PSR-3 日志。</p>
<pre><code class="language-php">use Psr\Log\LoggerInterface;
final class BillingService
{
    public function __construct(private readonly LoggerInterface $logger) {}
    public function charge(int $userId, int $amountCents): void
    {
      $this-&gt;logger-&gt;info('Charge request', [
            'userId' =&gt; $userId,
            'amountCents' =&gt; $amountCents,
      ]);
      // ...
    }
}
</code></pre>
<p>这个类不在乎你今天用 Monolog 还是明天换成别的日志库。这种解耦才能让系统演进。</p>
<h3 id="工具链和-2015-年相比现代-php-像换了一门语言">工具链:和 2015 年相比,现代 PHP 像换了一门语言</h3>
<p>很多"PHP 黑"来自于对老代码库的体验:弱类型、不一致的模式、"先上线再说"的文化。</p>
<p>现代 PHP 团队通常会用一套简单的质量工具链:</p>
<ul>
<li>严格类型</li>
<li>静态分析(PHPStan / Psalm)</li>
<li>测试(PHPUnit / Pest)</li>
<li>格式化(PHP-CS-Fixer)</li>
<li>自动重构(Rector)</li>
</ul>
<p>这套组合会改变你写代码的方式。目标不是完美,而是尽早暴露问题,让代码在迭代中保持可读。</p>
<p>下面是一个小例子,静态分析帮你避免一个运行时 bug:</p>
<pre><code class="language-php">final class UserRepository
{
    public function findByEmail(string $email): User
    {
      // DB lookup...
      return null; // bug
    }
}
</code></pre>
<p>配置好静态分析后,这会立即被标记出来。</p>
<p>PHP 的测试也不必很重。一个聚焦的测试可以读起来像文档:</p>
<pre><code class="language-php">final class MoneyTest extends TestCase
{
    public function testItAddsMoneyInSameCurrency(): void
    {
      $a = Money::usd(1000);
      $b = Money::usd(250);
      $sum = $a-&gt;add($b);
      $this-&gt;assertSame(1250, $sum-&gt;cents());
      $this-&gt;assertSame('USD', $sum-&gt;currency());
    }
}
</code></pre>
<p><strong>为什么这是优势二</strong>:PHP 的生态让你能快速交付并安全构建,因为工具链和惯例都已成熟。</p>
<h2 id="优势三务实的数据管道php-擅长乱进干净出">优势三:务实的数据管道(PHP 擅长"乱进、干净出")</h2>
<p>如果你做过一段时间后端,你就知道真相:这份工作就是数据转换。</p>
<p>请求进来格式奇怪。数据库行取出来格式奇怪。外部 API 返回的是"差不多是你期望的"。Webhook 在不方便的时候重试。边缘情况在周五发生。</p>
<p>PHP 在这类工作上特别强,因为它在两种模式下都很自如:</p>
<ul>
<li>快速操作(字符串、数组、JSON)</li>
<li>结构化代码(DTO、值对象、枚举、readonly 属性)</li>
</ul>
<p>为了说明我的意思,来构建一个现实的管道:处理来自支付提供商的 webhook。</p>
<p>这是一个很好的测试,因为它结合了:</p>
<ul>
<li>安全验证</li>
<li>幂等性(重试)</li>
<li>载荷规范化</li>
<li>状态变更</li>
<li>优雅处理未知事件类型</li>
</ul>
<h3 id="步骤一保持载荷边界显式化dto-优于原始数组">步骤一:保持载荷边界显式化(DTO 优于原始数组)</h3>
<p>数组在边界处没问题,但在整个应用中传递原始数组会变得痛苦。所以:尽早解析,尽早结构化。</p>
<pre><code class="language-php">final class WebhookEvent
{
    public function __construct(
      public readonly string $id,
      public readonly string $type,
      public readonly int $createdAtEpoch,
      public readonly array $data
    ) {}
    public static function fromArray(array $payload): self
    {
      return new self(
            id: (string)($payload['id'] ?? ''),
            type: (string)($payload['type'] ?? ''),
            createdAtEpoch: (int)($payload['created_at'] ?? 0),
            data: is_array($payload['data'] ?? null) ? $payload['data'] : []
      );
    }
}
</code></pre>
<p>这是务实的做法:我们依赖的字段用强类型,原始数据留着灵活性,新字段加进来也不会炸。</p>
<h3 id="步骤二验证签名不要信任-json">步骤二:验证签名(不要"信任 JSON")</h3>
<pre><code class="language-php">final class WebhookSignatureVerifier
{
    public function __construct(private readonly string $secret) {}
    public function verify(string $rawBody, string $signatureHeader): bool
    {
      $expected = hash_hmac('sha256', $rawBody, $this-&gt;secret);
      return hash_equals($expected, $signatureHeader);
    }
}
</code></pre>
<p>这里 <code>hash_equals</code> 很重要,用于避免时序攻击。这是个小细节,但这类习惯正是区分业余代码和生产代码的地方。</p>
<h3 id="步骤三幂等性因为-webhook-会重试">步骤三:幂等性(因为 webhook 会重试)</h3>
<p>如果你处理同一个事件两次,你可能会:</p>
<ul>
<li>重复更新订阅</li>
<li>重复发送邮件</li>
<li>重复给账户加款</li>
</ul>
<p>所以要存储已处理的事件 ID。</p>
<pre><code class="language-php">final class WebhookIdempotencyStore
{
    public function __construct(private readonly PDO $pdo) {}
    public function hasProcessed(string $eventId): bool
    {
      $stmt = $this-&gt;pdo-&gt;prepare("SELECT 1 FROM processed_webhooks WHERE event_id = :id");
      $stmt-&gt;execute([':id' =&gt; $eventId]);
      return (bool)$stmt-&gt;fetchColumn();
    }
    public function markProcessed(string $eventId): void
    {
      $stmt = $this-&gt;pdo-&gt;prepare(
            "INSERT INTO processed_webhooks (event_id, processed_at)
             VALUES (:id, NOW())"
      );
      $stmt-&gt;execute([':id' =&gt; $eventId]);
    }
}
</code></pre>
<h3 id="步骤四干净地映射事件类型枚举很有帮助">步骤四:干净地映射事件类型(枚举很有帮助)</h3>
<pre><code class="language-php">enum PaymentEventType: string
{
    case PaymentSucceeded = 'payment.succeeded';
    case PaymentFailed = 'payment.failed';
}
</code></pre>
<h3 id="步骤五用事务型-handler-把它们组合起来">步骤五:用事务型 handler 把它们组合起来</h3>
<pre><code class="language-php">final class PaymentWebhookHandler
{
    public function __construct(
      private readonly WebhookSignatureVerifier $verifier,
      private readonly WebhookIdempotencyStore $idem,
      private readonly PaymentService $payments,
      private readonly PDO $pdo
    ) {}
    public function handle(string $rawBody, string $signatureHeader): void
    {
      if (!$this-&gt;verifier-&gt;verify($rawBody, $signatureHeader)) {
            throw new RuntimeException('Invalid webhook signature');
      }
      $payload = json_decode($rawBody, true);
      if (!is_array($payload)) {
            throw new RuntimeException('Invalid JSON');
      }
      $event = WebhookEvent::fromArray($payload);
      if ($event-&gt;id === '' || $event-&gt;type === '') {
            throw new RuntimeException('Missing event fields');
      }
      if ($this-&gt;idem-&gt;hasProcessed($event-&gt;id)) {
            return; // safe no-op
      }
      $this-&gt;pdo-&gt;beginTransaction();
      try {
            $this-&gt;dispatch($event);
            $this-&gt;idem-&gt;markProcessed($event-&gt;id);
            $this-&gt;pdo-&gt;commit();
      } catch (Throwable $e) {
            $this-&gt;pdo-&gt;rollBack();
            throw $e;
      }
    }
    private function dispatch(WebhookEvent $event): void
    {
      $type = PaymentEventType::tryFrom($event-&gt;type);
      if ($type === null) {
            // unknown event type: ignore or log
            return;
      }
      $paymentId = (string)($event-&gt;data['payment_id'] ?? '');
      if ($paymentId === '') return;
      match ($type) {
            PaymentEventType::PaymentSucceeded =&gt; $this-&gt;payments-&gt;markSucceeded($paymentId),
            PaymentEventType::PaymentFailed    =&gt; $this-&gt;payments-&gt;markFailed($paymentId),
      };
    }
}
</code></pre>
<p>这是一个干净的管道:</p>
<ol>
<li>验证真实性</li>
<li>解析载荷</li>
<li>规范化为 DTO</li>
<li>强制幂等性</li>
<li>事务</li>
<li>分发领域动作</li>
<li>容忍未知事件</li>
</ol>
<p>这就是 PHP 擅长的"Web 现实"代码:处理那些真正重要的脏活,同时保持可读。</p>
<p><strong>为什么这是优势三</strong>:大多数后端都是数据管道,而 PHP 在真实世界约束下构建可理解的管道方面很强。</p>
<h2 id="额外内容游标分页的实践深入因为它把三个优势串在一起">额外内容:游标分页的实践深入(因为它把三个优势串在一起)</h2>
<p>分页是那种看起来简单、等用户翻到深页才暴露问题的功能。它也是个好例子,能说明 PHP 为何还没过时:它同时涉及 HTTP、SQL 性能和响应设计。</p>
<h3 id="为什么-offset-会伤害你">为什么 OFFSET 会伤害你</h3>
<p>Offset 分页(<code>LIMIT 20 OFFSET 100000</code>)迫使数据库扫描并丢弃大量行。在大表上,深页会变慢。</p>
<p>它在并发写入时也可能不一致:插入/删除可能导致"窗口"移动时出现跳过或重复。</p>
<p>游标分页(keyset/seek)通过使用稳定的排序和代表"你上次停在哪里"的游标来避免这些问题。</p>
<h3 id="规则一游标逻辑必须匹配排序方向">规则一:游标逻辑必须匹配排序方向</h3>
<p>如果你按最新优先排序:</p>
<pre><code class="language-sql">ORDER BY created_at DESC, id DESC
</code></pre>
<ul>
<li>下一页应该获取"更早"的行</li>
<li>所以条件用 <code>&lt;</code></li>
</ul>
<p>如果你的数据库支持元组比较:</p>
<pre><code class="language-sql">SELECT id, created_at, total_cents
FROM orders
WHERE (created_at, id) &lt; (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT ?
</code></pre>
<p>如果不支持,用显式逻辑:</p>
<pre><code class="language-sql">SELECT id, created_at, total_cents
FROM orders
WHERE created_at &lt; ?
   OR (created_at = ? AND id &lt; ?)
ORDER BY created_at DESC, id DESC
LIMIT ?
</code></pre>
<h3 id="规则二游标应该是不透明的且防篡改">规则二:游标应该是不透明的且防篡改</h3>
<p>游标通常是一个类似 <code>(created_at, id)</code> 的对,序列化给客户端。Base64 编码在传输时没问题——但不是安全措施。如果你想防止客户端伪造游标,就签名它。</p>
<pre><code class="language-php">final class Cursor
{
    public function __construct(
      public readonly string $createdAtIso,
      public readonly int $id
    ) {}
}
final class CursorCodec
{
    public function __construct(private readonly string $secret) {}
    public function encode(Cursor $cursor): string
    {
      $json = json_encode([
            'created_at' =&gt; $cursor-&gt;createdAtIso,
            'id' =&gt; $cursor-&gt;id
      ], JSON_UNESCAPED_SLASHES);
      $b64 = rtrim(strtr(base64_encode($json), '+/', '-_'), '=');
      $sig = hash_hmac('sha256', $b64, $this-&gt;secret);
      return $b64 . '.' . $sig;
    }
    public function decode(string $token): Cursor
    {
      $parts = explode('.', $token, 2);
      if (count($parts) !== 2) {
            throw new InvalidArgumentException('Invalid cursor');
      }
      [$b64, $sig] = $parts;
      $expected = hash_hmac('sha256', $b64, $this-&gt;secret);
      if (!hash_equals($expected, $sig)) {
            throw new InvalidArgumentException('Cursor signature mismatch');
      }
      $json = base64_decode(strtr($b64, '-_', '+/'), true);
      if ($json === false) {
            throw new InvalidArgumentException('Invalid cursor encoding');
      }
      $data = json_decode($json, true);
      if (!is_array($data)) {
            throw new InvalidArgumentException('Invalid cursor payload');
      }
      return new Cursor(
            createdAtIso: (string)($data['created_at'] ?? ''),
            id: (int)($data['id'] ?? 0)
      );
    }
}
</code></pre>
<h3 id="repository-方法返回条目--下一个游标">Repository 方法:返回条目 + 下一个游标</h3>
<pre><code class="language-php">final class OrderRepository
{
    public function __construct(private readonly PDO $pdo) {}
    /**
   * @return array{items: list&lt;array&gt;, next: ?Cursor}
   */
    public function listPage(?Cursor $after, int $limit): array
    {
      $limit = max(1, min($limit, 100));
      if ($after === null) {
            $sql = "SELECT id, created_at, total_cents
                  FROM orders
                  ORDER BY created_at DESC, id DESC
                  LIMIT :limit";
            $stmt = $this-&gt;pdo-&gt;prepare($sql);
            $stmt-&gt;bindValue(':limit', $limit, PDO::PARAM_INT);
      } else {
            $sql = "SELECT id, created_at, total_cents
                  FROM orders
                  WHERE created_at &lt; :created_at
                     OR (created_at = :created_at AND id &lt; :id)
                  ORDER BY created_at DESC, id DESC
                  LIMIT :limit";
            $stmt = $this-&gt;pdo-&gt;prepare($sql);
            $stmt-&gt;bindValue(':created_at', $after-&gt;createdAtIso);
            $stmt-&gt;bindValue(':id', $after-&gt;id, PDO::PARAM_INT);
            $stmt-&gt;bindValue(':limit', $limit, PDO::PARAM_INT);
      }
      $stmt-&gt;execute();
      $rows = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
      $items = array_map(fn($r) =&gt; [
            'id' =&gt; (int)$r['id'],
            'created_at' =&gt; (string)$r['created_at'],
            'total_cents' =&gt; (int)$r['total_cents'],
      ], $rows);
      $next = null;
      if ($items !== []) {
            $last = $items;
            $next = new Cursor($last['created_at'], $last['id']);
      }
      return ['items' =&gt; $items, 'next' =&gt; $next];
    }
}
</code></pre>
<p>现在你的 handler 可以解码 <code>after</code>、获取结果、编码 <code>next_cursor</code>——一个横跨 HTTP + SQL + JSON 响应的干净端到端管道。</p>
<p>这就是实践中的最佳平衡点:PHP 的 Web 原生特性、生态工具和数据处理能力在这里汇合。</p>
<h2 id="php-不是最佳默认选择的场景以及好团队怎么做">PHP 不是最佳默认选择的场景(以及好团队怎么做)</h2>
<p>PHP 不是万能的最佳工具:</p>
<ul>
<li>CPU 密集型工作负载(视频处理、大规模数值计算)</li>
<li>将极高并发的 socket 服务器作为默认架构</li>
<li>要求前后端用同一门语言共享严格类型的组织</li>
</ul>
<p>但大多数成功的团队不会把这当成重写一切的理由。他们做的是 Web 一直鼓励的事:组合系统。</p>
<ul>
<li>在 PHP 强的地方保留 API 表面。</li>
<li>把重计算卸载给 worker 或专门的服务。</li>
<li>用队列处理后台任务。</li>
<li>在怪罪语言之前先优化数据库查询和缓存。</li>
</ul>
<p>这不是什么"PHP 用户的自我安慰",这就是正常的系统设计。</p>
<h2 id="结论">结论</h2>
<p>回到最初的问题——PHP 的三大优势:</p>
<ul>
<li><strong>Web 原生的生产力</strong>:PHP 天然契合 HTTP 工作,保持构建循环快速。</li>
<li><strong>生态成熟度</strong>:Composer + 框架 + 标准 + 工具链给你杠杆并降低风险。</li>
<li><strong>务实的数据管道</strong>:PHP 擅长把混乱的真实世界数据转换成干净、稳定的输出——同时不让代码变得不可读。</li>
</ul>
<p>如果你想让 PHP 感觉现代(而不是像那些刻板印象),方法始终如一:</p>
<ul>
<li>保持 handler/控制器薄</li>
<li>用 DTO/值对象建模边界</li>
<li>把超时、重试和幂等性当作一等功能</li>
<li>按读取方式建索引(尤其是分页)</li>
<li>用测试 + 静态分析保护重构</li>
</ul>
<p>PHP 不需要追热点。它只要继续做它擅长的事就够了:跑那些实用、好维护、能稳定上线的 Web 系统。</p><br><br>
来源:https://www.cnblogs.com/catchadmin/p/19454737
頁: [1]
查看完整版本: 2026 年 PHP 的三大优势 这门"老将"为何依然重要