Am urmărit un `include` care pica doar pe PHP 8.2, nu pe 8.1

这篇内容暂时只有 Română 版本,以下是原文。

Un serviciu mic, PHP 8.2.11, Alpine în container, opcache activat, opcache.validate_timestamps=1. La deploy, un fișier Bootstrap.php dintr-un pachet intern era inclus cu require_once din două locuri diferite: unul cu cale absolută (/app/src/Bootstrap.php), altul cu cale relativă (../src/Bootstrap.php). Pe 8.1 mergea; pe 8.2, la al doilea include, primeam Cannot declare class Bootstrap, because the name is already in use doar când primul include venea din workerul FPM, nu din CLI.

Ce am făcut, pas cu pas. Am pornit de la opcache_get_status(true) în ambele contexte și am comparat scripts:

$s = opcache_get_status(true);
foreach ($s['scripts'] as $path => $info) {
    if (str_contains($path, 'Bootstrap.php')) {
        error_log($path . ' ' . $info['hits'] . ' ' . $info['last_used']);
    }
}

Cheile diferă: /app/src/Bootstrap.php și /app/src/../src/Bootstrap.php. Am normalizat calea la un singur punct de intrare, cu realpath calculat o dată și pus într-o constantă:

if (!defined('APP_SRC')) {
    define('APP_SRC', realpath(__DIR__ . '/..') . '/src');
}
require_once APP_SRC . '/Bootstrap.php';

Apoi am adăugat un test care rulează php -d opcache.enable_cli=1 -r 'require "vendor/autoload.php";' în CI, ca să prindă diferența dintre CLI și FPM.

De ce funcționează: opcache ține cheia pe calea rezolvată a fișierului, nu pe inode, iar ../ rămâne în cheie până la realpath. Pe 8.1, un cache mai vechi sau un include_path diferit masca problema; pe 8.2, cu OPcache-ul mai strict la file_cache, cele două intrări ajung la același inode, dar PHP tot încearcă să declare clasa. Costul normalizării: un realpath la boot, irelevant, dar și faptul că nu mai poți muta directorul fără să actualizezi constanta.

Unde se strică: dacă ai symlink-uri în deploy (release-uri atomice), realpath returnează calea fizică, deci cache-ul OPcache se invalidează la fiecare release — am pățit-o cu un deploy care schimba symlink-ul current. Și nu ajută dacă opcache.validate_timestamps=0 în producție: atunci nici nu vezi diferența până la restart.

Voi cum țineți sub control căile de include când aveți symlink-uri în deploy?

0
赞同度 100% · 0 条评论 · 4 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。