ReflectionCompositeTypeAssumedNamed
error native on by default PHP ≥ 8.0Group: Probable bugs
Inspect the composite reflection type members.
Example
Finding:
<?php
function consume(int|string $x) {} $r = new ReflectionFunction("consume"); $t = $r->getParameters()[0]->getType(); $t->getName();Reported:
- line 2: Inspect the composite reflection type members.
Detection
- Resolve getType on a ReflectionParameter, ReflectionProperty or getReturnType on a ReflectionFunctionAbstract for a known union type (PHP 8.0+) or intersection type (PHP 8.1+). Report getName invoked on that result, because only ReflectionNamedType provides it. Follow local assignments and indexed parameter selection.
- D1a. A declared type is composite when it has an intersection (
&, including a parenthesised group inside a union) or when at least two union members remain after discarding a leading?and anynullmember.?T,T|nullandnull|Tare a single nullable named type and are never reported;int|string|nullis. - Emit one finding per violating operation. Flow facts require a reachable path with no intervening mutation, escaped alias, unknown call involving the tracked value or analysis-budget exhaustion. Uncertain facts cannot prove a violation.
Exceptions (no report)
Calls on members returned by getTypes and unknown reflected declarations are excluded. Nullable single named types remain ReflectionNamedType (D1a).
No report when the conditions guarding the call prove the receiver value is a ReflectionNamedType. A test proves it when it is
$t instanceof ReflectionNamedType(oris_a()with that class) and holds, or when it is such a test for the composite class the declared type reflects as (ReflectionUnionType for a union, ReflectionIntersectionType for a pure intersection) and fails. Negation with!, parentheses, comparisons withtrue/false, a local holding the test's result,&&/andand||/orcombine these the usual way: a true conjunction or a false disjunction needs one proving operand, the opposite needs both. Guarding positions:- the body of an
if/elseif/whilewhose condition proves it when true, and any laterelseifcondition,elseifbody orelsebody of a chain in which an earlier condition proves it when false; - the matching arm of a ternary (including the right side of the short form);
- the right operand of
&&/and(left proves when true) or||/or(left proves when false); - a
match (true)arm whose every condition proves it when true; - any code after a test whose other side always leaves (
if (!$t instanceof ReflectionNamedType) { return; }, with or without laterelseif/elsearms, or anelsethat leaves), or after a builtinassert()of a proving condition.
The test must concern the same unmodified receiver value: reassigning the variable between the test and the call, a test on another value, a dynamic class operand, a guard in an outer function scope, a branch that falls through or a jump label between the test and the call keep the report. The wrong side of a guard (
if ($t instanceof ReflectionUnionType) { $t->getName(); }, theelseof a ReflectionNamedType test) is still reported.- the body of an
Exclude shadowed builtin symbols, unresolved required types, incomplete syntax and unsupported PHP versions.
Fix
No automatic fix. Choosing a repair requires runtime information, error-handling policy or a semantic decision.
All edits use exact byte ranges, preserve comments and evaluation order, and require syntax supported by the target PHP version. A fix must never add an evaluation or silently change unrelated arguments.
PHP versions
Minimum PHP 8.0.
Configure
In custos.json:
{
"rules": {
"ReflectionCompositeTypeAssumedNamed": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
// @custos-ignore ReflectionCompositeTypeAssumedNamed