Skip to content

PHP

Last verified end to end: 2026-07-14 (query_code schema version 2).

For exact inbound and outbound symbol edges, proof tiers, and adapter-specific caveats, see Reference Traversal.

PHP exposes instance, static, nullsafe, and object-creation calls; named arguments through kwargs; attributes through decorators; and namespace imports separately from trait composition.

<?php
namespace App;
use App\Support\Formatter;
use App\Support\{Logger, Writer as WriterAlias};
#[Route('/run')]
class Service {
use Loggable;
public const LIMIT = -3;
public function run(string $code): string {
audit_named(code: $code);
$formatted = Formatter::format($code);
return $formatted;
}
}
function audit_named(string $code): string {
return $code;
}
$service = new Service();
$service?->run("input");

The named-argument query uses kwargs to distinguish audit_named(code: $code) from ordinary positional calls. A receiver constraint separately identifies the static formatter call.

(language php
(call :callee (name "audit_named")
:kwargs [(code (identifier :name "code" :capture "value"))]))
{"languages":["php"],"match":{"kind":"call","callee":{"name":"audit_named"},"kwargs":{"code":{"kind":"identifier","name":"code","capture":"value"}}}}
{
"results": [
{
"captures": [
{"name": "value", "start_line": 14, "text": "$code"}
],
"enclosing_symbol": "App.Service.run",
"end_line": 14,
"kind": "call",
"language": "php",
"result_type": "structural_match",
"path": "php/app.php",
"start_line": 14,
"text": "audit_named(code: $code)"
}
],
"truncated": false
}
(language php (call :callee (name "format") :receiver (name "Formatter")))
{"languages":["php"],"match":{"kind":"call","callee":{"name":"format"},"receiver":{"name":"Formatter"}}}
{
"results": [
{
"enclosing_symbol": "App.Service.run",
"end_line": 15,
"kind": "call",
"language": "php",
"result_type": "structural_match",
"path": "php/app.php",
"start_line": 15,
"text": "Formatter::format($code)"
}
],
"truncated": false
}

PHP attributes are normalized as decorators. Namespace use declarations are imports, while use Loggable inside the class is trait composition and must not become an import match.

(language php (class :decorators [(name "Route")]))
{"languages":["php"],"match":{"kind":"class","decorators":[{"name":"Route"}]}}
{
"results": [
{
"enclosing_symbol": "App.Service",
"end_line": 18,
"kind": "class",
"language": "php",
"result_type": "structural_match",
"path": "php/app.php",
"start_line": 7,
"text": "#[Route('/run')]…"
}
],
"truncated": false
}
(language php (import :module (name "WriterAlias")))
{"languages":["php"],"match":{"kind":"import","module":{"name":"WriterAlias"}}}
{
"results": [
{
"end_line": 5,
"kind": "import",
"language": "php",
"result_type": "structural_match",
"path": "php/app.php",
"start_line": 5,
"text": "use App\\Support\\{Logger, Writer as WriterAlias};"
}
],
"truncated": false
}
(language php (import :module (name "Loggable")))
{"languages":["php"],"match":{"kind":"import","module":{"name":"Loggable"}}}
{
"results": [],
"truncated": false
}

The adapter also exposes nullsafe calls, constructors, assignments, field access, literals, and lambdas. It deliberately does not reinterpret class-body trait composition as an import, so a zero-match result here is a correctness proof rather than a missing fallback.

<?php
class QueryRoot {
public function rootMember() {}
}
class QueryLeaf extends QueryRoot {
public function leafMember() {}
}
(supertypes (enclosing-decl (language php (class :name "QueryLeaf"))))
{"languages":["php"],"match":{"kind":"class","name":"QueryLeaf"},"steps":[{"op":"enclosing_decl"},{"op":"supertypes"}]}
{
"results": [
{
"end_line": 4,
"fq_name": "QueryRoot",
"kind": "class",
"language": "php",
"path": "php/hierarchy.php",
"provenance": [
{
"seed": {
"end_line": 8,
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "structural_match",
"start_line": 6
},
"steps": [
{
"op": "enclosing_decl",
"result": {
"end_line": 8,
"fq_name": "QueryLeaf",
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 6
}
},
{
"op": "supertypes",
"result": {
"end_line": 4,
"fq_name": "QueryRoot",
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 2
}
}
]
}
],
"result_type": "declaration",
"signature": "class QueryRoot {",
"start_line": 2
}
],
"truncated": false
}
(owner (members (subtypes :depth 2 (enclosing-decl (language php (class :name "QueryRoot"))))))
{"languages":["php"],"match":{"kind":"class","name":"QueryRoot"},"steps":[{"op":"enclosing_decl"},{"op":"subtypes","depth":2},{"op":"members"},{"op":"owner"}]}
{
"results": [
{
"end_line": 8,
"fq_name": "QueryLeaf",
"kind": "class",
"language": "php",
"path": "php/hierarchy.php",
"provenance": [
{
"seed": {
"end_line": 4,
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "structural_match",
"start_line": 2
},
"steps": [
{
"op": "enclosing_decl",
"result": {
"end_line": 4,
"fq_name": "QueryRoot",
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 2
}
},
{
"op": "subtypes",
"result": {
"end_line": 8,
"fq_name": "QueryLeaf",
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 6
}
},
{
"op": "members",
"result": {
"end_line": 7,
"fq_name": "QueryLeaf.leafMember",
"kind": "function",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 7
}
},
{
"op": "owner",
"result": {
"end_line": 8,
"fq_name": "QueryLeaf",
"kind": "class",
"path": "php/hierarchy.php",
"result_type": "declaration",
"start_line": 6
}
}
]
}
],
"result_type": "declaration",
"signature": "class QueryLeaf extends QueryRoot {",
"start_line": 6
}
],
"truncated": false
}