Fourth and final R7b batch-2 port, checklist-first recipe (docs/design/php-kernel-port-checklist.md). Grammar bump first, validated standalone with the diff ENUMERATED + CLASSIFIED (unlike rust/ruby the php bump is NOT graph-neutral): tree-sitter-php ^0.22 (tree-sitter-wasms, 2023) → v0.24.2, the full HTML-interleaving `php` variant (the walker calls LANGUAGE_PHP, never PHP_ONLY) — crate pinned =0.24.2, wasm built from tag 5b5627f's checked-in php/src/parser.c + scanner.c + shared common/scanner.h (all sha-matched against the crates.io tarball, ABI 14→15). Old-vs-new full-init diffs decompose completely into: (1) the anonymous_class wrapper shape (anon-class nodes/methods re-shape — 2,532 rows), (2) grouped nested-clause skip (absent in the gate repos, fixture-pinned), (3) 32 formerly-erroring files parsing clean (monolog Level.php, symfony Request/Response with 8.4 property hooks), (4) a survey-missed category found at gate time: the 8.4 parenthesis-free `new X()->m()` chaining misparse fix (86 garbage instantiates refs disappear, precision-positive), plus resolution RIPPLE proven mechanically (every remaining ref-table flip pairs 1:1 with a resolved edge on the opposite side; node rows byte-stable outside 1/3/4). Walker (java.rs chassis + the php specifics) preserves bug-for-bug: the visitNode hook (const_declaration at ANY scope → bare `constant` nodes, values never walked; trait-use → implements refs WITH filePath via the ruby port's REF_FLAG_FILE_PATH wire slot), FIRST-namespace whole-file scoping (braced namespaces scope nothing; namespaced files DROP top-level const value-ref targets), the import trio (single/aliased/grouped incl. the nested-clause skip, include/require static-literal-only, `Foo\Bar::Baz` use refs), the call-encoding zoo (DOT-joined scoped calls, `this->prop.m` #1251 encoding, `Cls::factory().m` fluent with inner args dropped, nullsafe `?->` emitting nothing, unsuppressed literal receivers), interface multi-extends first-base-only drop, anon-class methods as file-level functions (top) or vanishing (in-body), property type-hints emitting no field refs, the final-modifier-as-type signature quirk, HOF-gated string callables (skipGate) + array callables, and the `name`-node value-ref reader. Gates: sweeps 0-diff monolog 217/217, laravel-framework 3007/3008, symfony 10726/10737 (13,950 files byte-parity; 12 deferrals = exactly the predicted genuinely-broken fixtures, ≈0–0.1%); full-init dumps byte-identical ×3 (16.1k/354.2k/702.8k lines); kernel-php-parity suite (torture + drupal .module + leading-HTML fixtures, CRLF variants, wire-flag pin, defer) + php grammar-parity row; full suite 2,622 green ×2 under CODEGRAPH_KERNEL_EXPECT=1. DEFAULT_ROUTED += php (13 languages). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
220 lines
4.9 KiB
PHP
220 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Contracts\Logger;
|
|
use App\Contracts\Cache as CacheAlias;
|
|
use Countable;
|
|
use function App\Helpers\format_id;
|
|
use const App\Config\MAX_TRIES;
|
|
use App\Models\{User, Post as PostAlias, Sub\Deep};
|
|
|
|
require 'lib/plain.php';
|
|
require_once('lib/parens.php');
|
|
include 'lib/inc.php';
|
|
include_once 'lib/inc_once.php';
|
|
require __DIR__ . '/dynamic.php';
|
|
|
|
const TOP_LEVEL_MAX = 10;
|
|
|
|
// non-ASCII before a symbol: café ünïcode line
|
|
function top_helper(?Logger $log, User|PostAlias $u, Logger&Countable $lc, (Foo&Bar)|Baz $dnf, \App\Models\User $qual, string $s, int ...$rest): UserModel
|
|
{
|
|
top_body_call();
|
|
return new UserModel();
|
|
}
|
|
|
|
/** Doc for Documented. */
|
|
#[Registry(param: Logger::class)]
|
|
class Documented extends BaseThing implements HasColor, \JsonSerializable
|
|
{
|
|
use SoftDeletes, Notify\Deeper;
|
|
use ConflictA, ConflictB {
|
|
ConflictA::greet insteadof ConflictB;
|
|
ConflictB::greet as protected greetB;
|
|
}
|
|
|
|
const MULTI_A = 1, MULTI_B = 2;
|
|
final public const int TYPED_MAX = 5;
|
|
|
|
public ?Logger $logger, $fallback;
|
|
private CacheAlias|string $union;
|
|
protected static iterable $registry;
|
|
public readonly int $count;
|
|
var $legacy;
|
|
final public Foo $finalTyped;
|
|
|
|
public function __construct(private Logger $promoted, protected string $name = 'x', ICache $plain = new NullMailer())
|
|
{
|
|
}
|
|
|
|
/** Doc over attribute. */
|
|
#[Route('/x')]
|
|
public function withAttr(): void
|
|
{
|
|
}
|
|
|
|
public static function make(): static
|
|
{
|
|
return new static();
|
|
}
|
|
|
|
public function selfRet(): self
|
|
{
|
|
return $this;
|
|
}
|
|
|
|
public function nullableRet(): ?Logger
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function unionRet(): Foo|Bar
|
|
{
|
|
return new Foo();
|
|
}
|
|
|
|
protected function callsZoo($x, User $u, $obj, $var, $cls, $arr, $a)
|
|
{
|
|
helper();
|
|
\App\Helpers\format_id(1);
|
|
App\Helpers\other(2);
|
|
$x->m1();
|
|
$this->m2();
|
|
$this->prop->m3();
|
|
$this->a->b->m4();
|
|
$obj->prop->m5();
|
|
UserModel::query();
|
|
self::sHelper();
|
|
static::sHelper();
|
|
parent::pHelper();
|
|
$var::vm();
|
|
\Qual\Cls::qm();
|
|
UserModel::factory($x)->where('a');
|
|
$this->factory($x)->go();
|
|
foo()->fluent();
|
|
$a?->maybe()->chained();
|
|
"chain"->upper();
|
|
$fn = 'x';
|
|
$fn();
|
|
($x)('arg');
|
|
strlen(...);
|
|
$this->m2(...);
|
|
Cls::sm(...);
|
|
new UserModel();
|
|
new \App\Models\User();
|
|
new Models\User(1);
|
|
new static();
|
|
new self();
|
|
new parent();
|
|
new $cls();
|
|
$anon = new class extends BaseAnon implements IAnon {
|
|
public function anonMethod(): void
|
|
{
|
|
inner_anon_call();
|
|
}
|
|
};
|
|
new Widget(make_arg());
|
|
$m = match ($x) {
|
|
1 => one_case(),
|
|
default => other_case(),
|
|
};
|
|
$$x = 5;
|
|
$interp = "{$this->x} and $u prefix";
|
|
$here = <<<EOT
|
|
heredoc {$this->y} text
|
|
EOT;
|
|
$now = <<<'EOT'
|
|
nowdoc plain
|
|
EOT;
|
|
echo SomeCls::CONST_READ;
|
|
$clsName = UserModel::class;
|
|
$propRead = UserModel::$conn;
|
|
$rel = self::REL_CONST;
|
|
$qualRead = \Qual\Cls::QCONST;
|
|
$suit = Suit::Hearts;
|
|
}
|
|
|
|
public function nester($arr, $x): void
|
|
{
|
|
function innerNamed(): void
|
|
{
|
|
inner_call();
|
|
}
|
|
if (!class_exists('Poly')) {
|
|
class Poly
|
|
{
|
|
const POLY_MAX = 3;
|
|
|
|
public function pm(): void
|
|
{
|
|
poly_call();
|
|
}
|
|
}
|
|
}
|
|
$c = function () use (&$x) {
|
|
closure_call();
|
|
};
|
|
$a = fn ($v) => arrow_call($v);
|
|
usort($arr, 'cmp_items');
|
|
array_map('App\Svc\namespaced_fn', $arr);
|
|
call_user_func([$this, 'm2']);
|
|
call_user_func([UserModel::class, 'sm']);
|
|
call_user_func(['Cls', 'sm']);
|
|
register_shutdown_function('Cls::shutdown');
|
|
$x->map('not_captured');
|
|
plain_call('not_a_hof_string');
|
|
}
|
|
|
|
public function reader(): int
|
|
{
|
|
$sum = MULTI_A + TYPED_MAX;
|
|
$s = "interp TYPED_MAX read: {$this->x} MULTI_B";
|
|
$varOccurrence = $MULTI_A;
|
|
return $sum + self::TYPED_MAX;
|
|
}
|
|
}
|
|
|
|
interface Shape extends Base1, Base2, \Qual\Base3
|
|
{
|
|
public function area(): float;
|
|
|
|
const SHAPE_KIND = 'geo';
|
|
}
|
|
|
|
trait SoftDeletes
|
|
{
|
|
const TRAIT_CONST = 1;
|
|
|
|
public function restore(): void
|
|
{
|
|
$this->doRestore();
|
|
}
|
|
}
|
|
|
|
enum Suit: string implements HasColor
|
|
{
|
|
case Hearts = 'H';
|
|
case Spades = 'S';
|
|
|
|
const ENUM_MAX = 4;
|
|
|
|
public function color(): string
|
|
{
|
|
return enum_color($this);
|
|
}
|
|
}
|
|
|
|
enum Pure
|
|
{
|
|
case A;
|
|
case B;
|
|
}
|
|
|
|
abstract class AbstractBase
|
|
{
|
|
abstract protected function hook(): void;
|
|
}
|