Skip to content
Prev Previous commit
Next Next commit
Shadow PhpClassReflectionExtension natively
PHPStan\Reflection\Php\PhpClassReflectionExtension is the member factory
behind every ClassReflection::has*()/get*() call: hasMethod() 807K,
hasNativeMethod() 746K, touchMemberCacheKey() 399K, getNativeMethod() 292K,
hasProperty() 156K and getNativeProperty() 78K times in a self-analysis of
src/Analyser, src/Rules and src/Type. The whole class is ported (the nine
public methods natively, the member construction below them as C++), and
its hot path is absorbed on both sides: the service's own bodies and
self-calls, ClassReflection::getCacheKey() and ::getNativeReflection()
through the pt_class_reflection_*() direct calls, the shared member
LRU natively, and new readers of the BetterReflection adapter's member
memos, which answer hasMethod()/hasProperty()/getMethod() out of
ReflectionClass::$cachedMethods/$cachedProperties instead of three PHP
frames (lazy population, the empty name and a missing method stay the
library's, through its own methods).

Values that reach a PHP collaborator's concrete typed parameter or an
instanceof it performs - TrinaryLogic, TemplateTypeMap,
TemplateTypeVariance, MixedType and what TypehintHelper and TypeCombinator
build - are created through the classes' real names, which are the native
classes in a production run and the PHP twins under the prefixed
differential activation.

tests/php-class-reflection-family.php builds both sides from the
container's own collaborators with named arguments and compares every
public method over inherited, trait, magic, promoted, hooked, attributed,
enum, interface and signature-mapped members, in and out of class scope,
plus the member-cache memo and its eviction.

Interleaved A/B on src/Analyser + src/Rules + src/Type, user CPU, 8 ABBA
pairs: 41.03s -> 40.55s, -1.16% (median pair delta -1.22%); analysis output
byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy
  • Loading branch information
ondrejmirtes and claude committed Sep 22, 2026
commit 8712c9baf53616562413c46eac16e3734262ee32
14 changes: 14 additions & 0 deletions build/PHPStan/Build/TurboAttributeCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
Expand All @@ -34,12 +35,17 @@
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\VariadicPlaceholder;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumBackedCase;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionIntersectionType;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionNamedType;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionUnionType;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
Expand Down Expand Up @@ -174,6 +180,14 @@ final class TurboAttributeCollector
'listExpr' => List_::class,
'reflectionEnumBackedCase' => ReflectionEnumBackedCase::class,
'arg' => Arg::class,
'adapterReflectionMethod' => ReflectionMethod::class,
'expressionStmt' => Expression::class,
'assignExpr' => Assign::class,
'namespaceStmt' => Namespace_::class,
'declareStmt' => Declare_::class,
'classMethodStmt' => ClassMethod::class,
'adapterReflectionClass' => \PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass::class,
'betterReflectionClass' => \PHPStan\BetterReflection\Reflection\ReflectionClass::class,
];

private string $realRoot;
Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Attribute/PrivateProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection\Attribute;

use Attribute;
use PHPStan\Turbo\ReferencedByTurboExtension;

/**
* Marks a property the phar build made public that is private in the
Expand All @@ -13,6 +14,7 @@
* the phar cannot access it.
*/
#[Attribute(flags: Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
#[ReferencedByTurboExtension(key: 'privatePropertyAttribute')]
final class PrivateProperty
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Attribute/ProtectedProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection\Attribute;

use Attribute;
use PHPStan\Turbo\ReferencedByTurboExtension;

/**
* Marks a property the phar build made public that is protected in the
Expand All @@ -13,6 +14,7 @@
* the phar cannot access it from outside the class hierarchy.
*/
#[Attribute(flags: Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
#[ReferencedByTurboExtension(key: 'protectedPropertyAttribute')]
final class ProtectedProperty
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Native/ExtendedNativeParameterReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
use PHPStan\Reflection\ParameterAllowedConstants;
use PHPStan\Reflection\PassedByReference;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

#[ReferencedByTurboExtension(key: 'extendedNativeParameterReflection')]
final class ExtendedNativeParameterReflection implements ExtendedParameterReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Native/NativeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Type;
use PHPStan\Type\TypehintHelper;
use ReflectionException;
use function count;
use function strtolower;

#[ReferencedByTurboExtension(key: 'nativeMethodReflection')]
final class NativeMethodReflection implements ExtendedMethodReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Php/EnumCasesMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
use PHPStan\Reflection\ExtendedParametersAcceptor;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

#[ReferencedByTurboExtension(key: 'enumCasesMethodReflection')]
final class EnumCasesMethodReflection implements ExtendedMethodReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryDecimalIntegerStringType;
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
use PHPStan\Type\ArrayType;
Expand Down Expand Up @@ -78,6 +79,7 @@
use function strtolower;

#[AutowiredService]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../../turbo-ext/src/PhpClassReflectionExtension.cpp')]
final class PhpClassReflectionExtension
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Php/PhpPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\Reflection\ExtendedPropertyReflection;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
Expand All @@ -19,6 +20,7 @@
/**
* @api
*/
#[ReferencedByTurboExtension(key: 'phpPropertyReflection')]
final class PhpPropertyReflection implements ExtendedPropertyReflection
{

Expand Down
2 changes: 1 addition & 1 deletion turbo-ext/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if (PHP_PHPSTAN_TURBO != "no") {
// directory by splitting on backslashes only — with forward slashes the
// objects compile flat while the link list expects the subpath
EXTENSION("phpstan_turbo",
"src\\main.cpp src\\support.cpp src\\AbsorbedTemplateArgumentType.cpp src\\AcceptsResult.cpp src\\AccessoryArrayListType.cpp src\\AccessoryDecimalIntegerStringType.cpp src\\AccessoryLiteralStringType.cpp src\\AccessoryLowercaseStringType.cpp src\\AccessoryNonEmptyStringType.cpp src\\AccessoryNonFalsyStringType.cpp src\\AccessoryNumericStringType.cpp src\\AccessoryUppercaseStringType.cpp src\\ArenaCache.cpp src\\ArrayType.cpp src\\BenevolentUnionType.cpp src\\BooleanType.cpp src\\CallableType.cpp src\\CallableTypeHelper.cpp src\\CallbackUnresolvedMethodPrototypeReflection.cpp src\\CallbackUnresolvedPropertyPrototypeReflection.cpp src\\CalledOnTypeUnresolvedMethodPrototypeReflection.cpp src\\CalledOnTypeUnresolvedPropertyPrototypeReflection.cpp src\\CircularTypeAliasErrorType.cpp src\\ClassConstantAccessType.cpp src\\ClassStringType.cpp src\\ClosureType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConditionalType.cpp src\\ConditionalTypeForParameter.cpp src\\ConstantArrayType.cpp src\\ConstantArrayTypeBuilder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ConstantTypeHelper.cpp src\\EnumCaseObjectType.cpp src\\ErrorType.cpp src\\ExpressionResult.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FiniteTypeSet.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\GenericObjectType.cpp src\\GenericStaticType.cpp src\\GetTemplateTypeType.cpp src\\HasMethodType.cpp src\\HasOffsetType.cpp src\\HasOffsetValueType.cpp src\\HasPropertyType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IntersectionType.cpp src\\IsSuperTypeOfResult.cpp src\\IterableType.cpp src\\KeyOfType.cpp src\\LateResolvableArrayShapeType.cpp src\\LruCache.cpp src\\MixedType.cpp src\\NativeParameterReflection.cpp src\\NeverType.cpp src\\NewObjectType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NonAcceptingNeverType.cpp src\\NonEmptyArrayType.cpp src\\NonexistentParentClassType.cpp src\\NullType.cpp src\\ObjectShapeType.cpp src\\ObjectType.cpp src\\ObjectWithoutClassType.cpp src\\OffsetAccessType.cpp src\\OversizedArrayType.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\RecursionGuard.cpp src\\ReflectionAccess.cpp src\\ResourceType.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StaticType.cpp src\\StaticTypeFactory.cpp src\\StrictMixedType.cpp src\\StringAlwaysAcceptingObjectWithToStringType.cpp src\\StringNeverAcceptingObjectWithToStringType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TemplateArrayType.cpp src\\TemplateBenevolentUnionType.cpp src\\TemplateBooleanType.cpp src\\TemplateConstantArrayType.cpp src\\TemplateConstantIntegerType.cpp src\\TemplateConstantStringType.cpp src\\TemplateFloatType.cpp src\\TemplateGenericObjectType.cpp src\\TemplateIntegerType.cpp src\\TemplateIntersectionType.cpp src\\TemplateIterableType.cpp src\\TemplateKeyOfType.cpp src\\TemplateMixedType.cpp src\\TemplateNullType.cpp src\\TemplateObjectShapeType.cpp src\\TemplateObjectType.cpp src\\TemplateObjectWithoutClassType.cpp src\\TemplateStrictMixedType.cpp src\\TemplateStringType.cpp src\\TemplateTypeArgumentStrategy.cpp src\\TemplateTypeFactory.cpp src\\TemplateTypeHelper.cpp src\\TemplateTypeMap.cpp src\\TemplateTypeParameterStrategy.cpp src\\TemplateTypeReference.cpp src\\TemplateTypeScope.cpp src\\TemplateTypeVariance.cpp src\\TemplateTypeVarianceMap.cpp src\\TemplateUnionType.cpp src\\ThisType.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinator.cpp src\\TypeCombinatorCache.cpp src\\TypehintHelper.cpp src\\TypeOps.cpp src\\TypeProjectionHelper.cpp src\\TypeResult.cpp src\\TypeTraits.cpp src\\TypeTraverser.cpp src\\TypeUtils.cpp src\\UnionType.cpp src\\UnionTypeHelper.cpp src\\UnresolvableTypeHelper.cpp src\\UnresolvedTemplateArgumentType.cpp src\\ValueOfType.cpp src\\VariableFlow.cpp src\\VariableFlowBuilder.cpp src\\VariableLivenessResolver.cpp src\\VerbosityLevel.cpp src\\VoidType.cpp src\\VolatileExpressionHelper.cpp src\\ClassReflection.cpp",
"src\\main.cpp src\\support.cpp src\\AbsorbedTemplateArgumentType.cpp src\\AcceptsResult.cpp src\\AccessoryArrayListType.cpp src\\AccessoryDecimalIntegerStringType.cpp src\\AccessoryLiteralStringType.cpp src\\AccessoryLowercaseStringType.cpp src\\AccessoryNonEmptyStringType.cpp src\\AccessoryNonFalsyStringType.cpp src\\AccessoryNumericStringType.cpp src\\AccessoryUppercaseStringType.cpp src\\ArenaCache.cpp src\\ArrayType.cpp src\\BenevolentUnionType.cpp src\\BooleanType.cpp src\\CallableType.cpp src\\CallableTypeHelper.cpp src\\CallbackUnresolvedMethodPrototypeReflection.cpp src\\CallbackUnresolvedPropertyPrototypeReflection.cpp src\\CalledOnTypeUnresolvedMethodPrototypeReflection.cpp src\\CalledOnTypeUnresolvedPropertyPrototypeReflection.cpp src\\CircularTypeAliasErrorType.cpp src\\ClassConstantAccessType.cpp src\\ClassReflection.cpp src\\ClassStringType.cpp src\\ClosureType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConditionalType.cpp src\\ConditionalTypeForParameter.cpp src\\ConstantArrayType.cpp src\\ConstantArrayTypeBuilder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ConstantTypeHelper.cpp src\\EnumCaseObjectType.cpp src\\ErrorType.cpp src\\ExpressionResult.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FiniteTypeSet.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\GenericObjectType.cpp src\\GenericStaticType.cpp src\\GetTemplateTypeType.cpp src\\HasMethodType.cpp src\\HasOffsetType.cpp src\\HasOffsetValueType.cpp src\\HasPropertyType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IntersectionType.cpp src\\IsSuperTypeOfResult.cpp src\\IterableType.cpp src\\KeyOfType.cpp src\\LateResolvableArrayShapeType.cpp src\\LruCache.cpp src\\MixedType.cpp src\\NativeParameterReflection.cpp src\\NeverType.cpp src\\NewObjectType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NonAcceptingNeverType.cpp src\\NonEmptyArrayType.cpp src\\NonexistentParentClassType.cpp src\\NullType.cpp src\\ObjectShapeType.cpp src\\ObjectType.cpp src\\ObjectWithoutClassType.cpp src\\OffsetAccessType.cpp src\\OversizedArrayType.cpp src\\PharForkGuard.cpp src\\PhpClassReflectionExtension.cpp src\\PhpFileCleaner.cpp src\\RecursionGuard.cpp src\\ReflectionAccess.cpp src\\ResourceType.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StaticType.cpp src\\StaticTypeFactory.cpp src\\StrictMixedType.cpp src\\StringAlwaysAcceptingObjectWithToStringType.cpp src\\StringNeverAcceptingObjectWithToStringType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TemplateArrayType.cpp src\\TemplateBenevolentUnionType.cpp src\\TemplateBooleanType.cpp src\\TemplateConstantArrayType.cpp src\\TemplateConstantIntegerType.cpp src\\TemplateConstantStringType.cpp src\\TemplateFloatType.cpp src\\TemplateGenericObjectType.cpp src\\TemplateIntegerType.cpp src\\TemplateIntersectionType.cpp src\\TemplateIterableType.cpp src\\TemplateKeyOfType.cpp src\\TemplateMixedType.cpp src\\TemplateNullType.cpp src\\TemplateObjectShapeType.cpp src\\TemplateObjectType.cpp src\\TemplateObjectWithoutClassType.cpp src\\TemplateStrictMixedType.cpp src\\TemplateStringType.cpp src\\TemplateTypeArgumentStrategy.cpp src\\TemplateTypeFactory.cpp src\\TemplateTypeHelper.cpp src\\TemplateTypeMap.cpp src\\TemplateTypeParameterStrategy.cpp src\\TemplateTypeReference.cpp src\\TemplateTypeScope.cpp src\\TemplateTypeVariance.cpp src\\TemplateTypeVarianceMap.cpp src\\TemplateUnionType.cpp src\\ThisType.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinator.cpp src\\TypeCombinatorCache.cpp src\\TypehintHelper.cpp src\\TypeOps.cpp src\\TypeProjectionHelper.cpp src\\TypeResult.cpp src\\TypeTraits.cpp src\\TypeTraverser.cpp src\\TypeUtils.cpp src\\UnionType.cpp src\\UnionTypeHelper.cpp src\\UnresolvableTypeHelper.cpp src\\UnresolvedTemplateArgumentType.cpp src\\ValueOfType.cpp src\\VariableFlow.cpp src\\VariableFlowBuilder.cpp src\\VariableLivenessResolver.cpp src\\VerbosityLevel.cpp src\\VoidType.cpp src\\VolatileExpressionHelper.cpp",
true,
flags);
ADD_SOURCES(configure_module_dirname + "/src/parser",
Expand Down
11 changes: 11 additions & 0 deletions turbo-ext/src/ExpressionResultStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,14 @@ void pt_register_expression_result_storage()
}

/* }}} */

/* {{{ direct entries: the native bodies for a native storage, the methods
* of anything else (the PHP twin under the prefixed differential
* activation) */

zv::Val pt_expression_result_storage_new()
{
return pt_type_new_ce(pt_ce_expression_result_storage, 0, NULL);
}

/* }}} */
Loading