Problem
MethodForwardingHandler::columnMatchesDynamicWhere() only validates single-segment dynamic where methods. Multi-segment forms like whereFirstNameAndLastName(\$a, \$b) or whereTitleOrSlug(\$x) are accepted even when one of the segments doesn't correspond to an actual model property.
Where it breaks
src/Handlers/Magic/MethodForwardingHandler.php:581-623. The method extracts \$methodName minus the leading where, normalizes it (strip \$/_, lowercase), and compares to each pseudo_property_get_types key. Multi-segment names like firstnameandlastname will never match any single property, so the cache stores false and the call falls through. Currently the isDynamicWhereMethod check (line 296) is broader and provides a permissive signature, so whereXxxAndYyy calls slip past unnoticed regardless of whether xxx/yyy exist on the model.
Reproduction
/** @property string \$first_name */
/** @property string \$last_name */
class User extends Model {}
User::query()->whereFirstNameAndLastName('A', 'B'); // OK — valid columns
User::query()->whereFirstNameAndNope('A', 'B'); // Should warn: 'nope' not a column
The second call should raise a column-existence error but is silently accepted.
Fix sketch
Split the suffix on (?:And|Or)(?=[A-Z]) (Larastan's regex), validate each segment independently:
private static function columnsMatchDynamicWhere(
Codebase \$codebase,
string \$modelClass,
string \$methodName,
): bool {
\$suffix = \\substr(\$methodName, 5);
\$segments = \\preg_split('/(?:And|Or)(?=[A-Z])/', \$suffix);
if (\$segments === false || \$segments === []) {
return false;
}
foreach (\$segments as \$segment) {
if (!self::singleColumnMatches(\$codebase, \$modelClass, \$segment)) {
return false;
}
}
return true;
}
Cache key remains model:method since result depends only on the pair.
References
- Larastan:
.alies/larastan/src/Methods/BuilderHelper.php:105 (uses preg_split('/(And|Or)(?=[A-Z])/', \$finder, -1, PREG_SPLIT_DELIM_CAPTURE))
- Laravel:
Illuminate\\Database\\Eloquent\\Builder::dynamicWhere()
Related
Argument typing for dynamic where is tracked separately (forthcoming issue).
Problem
MethodForwardingHandler::columnMatchesDynamicWhere()only validates single-segment dynamic where methods. Multi-segment forms likewhereFirstNameAndLastName(\$a, \$b)orwhereTitleOrSlug(\$x)are accepted even when one of the segments doesn't correspond to an actual model property.Where it breaks
src/Handlers/Magic/MethodForwardingHandler.php:581-623. The method extracts\$methodNameminus the leadingwhere, normalizes it (strip\$/_, lowercase), and compares to eachpseudo_property_get_typeskey. Multi-segment names likefirstnameandlastnamewill never match any single property, so the cache storesfalseand the call falls through. Currently theisDynamicWhereMethodcheck (line 296) is broader and provides a permissive signature, sowhereXxxAndYyycalls slip past unnoticed regardless of whetherxxx/yyyexist on the model.Reproduction
The second call should raise a column-existence error but is silently accepted.
Fix sketch
Split the suffix on
(?:And|Or)(?=[A-Z])(Larastan's regex), validate each segment independently:Cache key remains
model:methodsince result depends only on the pair.References
.alies/larastan/src/Methods/BuilderHelper.php:105(usespreg_split('/(And|Or)(?=[A-Z])/', \$finder, -1, PREG_SPLIT_DELIM_CAPTURE))Illuminate\\Database\\Eloquent\\Builder::dynamicWhere()Related
Argument typing for dynamic where is tracked separately (forthcoming issue).