VOOZH about

URL: https://deepwiki.com/MahoCommerce/maho-phpstan-plugin/3.2-phpstan-extension-points

⇱ PHPStan Extension Points | MahoCommerce/maho-phpstan-plugin | DeepWiki


Loading...
Menu

PHPStan Extension Points

This document explains the five PHPStan extension points that the maho-phpstan-plugin uses to integrate with PHPStan's analysis engine. Each extension point serves a specific purpose in the static analysis pipeline, and the plugin registers multiple service instances across these extension points to handle Magento/Maho-specific code patterns.

For the central configuration file that registers these services, see Plugin Configuration System. For detailed documentation of individual components, see Type Inference System and Reflection Extensions.

Extension Point Architecture

PHPStan provides a modular extension system through service tags defined in NEON configuration files. The maho-phpstan-plugin registers 7 service instances across 5 distinct extension points to comprehensively handle Magento/Maho code patterns.

Extension Point Registration Flow


Sources: extension.neon1-71

Complete Extension Point Mapping

Extension PointService ClassTarget ClassesLinesPurpose
phpstan.broker.dynamicStaticMethodReturnTypeExtensionMageTypeExtensionMageextension.neon20-26Resolve return types for Mage::getModel(), Mage::helper()
phpstan.broker.dynamicMethodReturnTypeExtensionMageTypeExtensionMage_Core_Block_Abstractextension.neon27-33Resolve return types for $block->helper(), $block->getLayout()
phpstan.broker.dynamicMethodReturnTypeExtensionMageTypeExtensionMage_Core_Model_Abstractextension.neon34-40Resolve return types for $model->getCollection()
phpstan.broker.dynamicMethodReturnTypeExtensionMageTypeExtensionMage_Core_Model_Layoutextension.neon41-47Resolve return types for $layout->createBlock(), $layout->getBlock()
phpstan.rules.ruleMageInvalidTypeRuleAll classesextension.neon50-55Validate resolved class names exist
phpstan.broker.methodsClassReflectionExtensionVarienObjectReflectionExtensionVarien_Object descendantsextension.neon57-63Provide magic getter/setter method reflection
phpstan.parser.richParserNodeVisitor + phpstan.phpDoc.typeNodeResolverExtensionBindThisScopeResolverExtension.phtml templates, install scriptsextension.neon65-70Enable protected method access via $this

Sources: extension.neon13-71

Dynamic Return Type Extensions

Dynamic return type extensions allow PHPStan to infer the return type of factory methods that would otherwise return generic types. The plugin registers 4 instances of MageTypeExtension, one for static methods and three for instance methods on different base classes.

Extension Point: phpstan.broker.dynamicStaticMethodReturnTypeExtension

This extension point intercepts static method calls and allows custom return type resolution. The plugin uses it to handle the Mage static class.


Registration Configuration:

extension.neon20-26 registers MageTypeExtension for the Mage class with the tag phpstan.broker.dynamicStaticMethodReturnTypeExtension. This service instance has:

  • className argument set to Mage
  • Reference to the @mageCoreConfig service for class name resolution

Target Methods: Mage::getModel(), Mage::getSingleton(), Mage::getResourceModel(), Mage::getResourceSingleton(), Mage::helper(), Mage::getStoreConfig()

Sources: extension.neon20-26

Extension Point: phpstan.broker.dynamicMethodReturnTypeExtension

This extension point intercepts instance method calls. The plugin registers three separate MageTypeExtension instances for different base classes.


Registration Configurations:

  1. Blocks: extension.neon27-33 registers MageTypeExtension for Mage_Core_Block_Abstract to handle $block->helper('catalog') and $block->getLayout()

  2. Models: extension.neon34-40 registers MageTypeExtension for Mage_Core_Model_Abstract to handle $model->getCollection()

  3. Layout: extension.neon41-47 registers MageTypeExtension for Mage_Core_Model_Layout to handle $layout->createBlock('catalog/product_list') and $layout->getBlock('content')

Each instance has the className argument set to the corresponding base class and receives the @mageCoreConfig service for alias resolution.

Sources: extension.neon27-47

Rule Extensions

Rule extensions add custom validation logic to PHPStan's analysis pipeline. Rules are executed after type inference and can report custom errors.

Extension Point: phpstan.rules.rule

This extension point allows registration of custom rules that validate code patterns and report errors.


Registration Configuration:

extension.neon50-55 registers MageInvalidTypeRule with the tag phpstan.rules.rule. This service validates that class names resolved by MageTypeExtension actually exist. It receives the @mageCoreConfig service to access the class name resolution logic.

Purpose: Prevents false positives when a factory method alias doesn't resolve to an existing class. For example, if Mage::getModel('nonexistent/model') is called, the rule reports an error instead of allowing PHPStan to continue with an invalid type.

Sources: extension.neon50-55

Reflection Extensions

Reflection extensions modify how PHPStan understands class structure, adding or modifying methods and properties visible during analysis.

Extension Point: phpstan.broker.methodsClassReflectionExtension

This extension point allows adding methods to classes that PHPStan's reflection doesn't normally see, particularly for magic methods implemented via __call().


Registration Configuration:

extension.neon57-63 registers VarienObjectReflectionExtension with the tag phpstan.broker.methodsClassReflectionExtension. The service has an enforceDocBlock argument that can be set via the %enforceMagicMethodDocBlock% parameter.

Target Classes: Any class extending Varien_Object or Maho\DataObject

Handled Patterns:

  • getName() → maps to getData('name')
  • setName($value) → maps to setData('name', $value)
  • unsName() → maps to unsetData('name')
  • hasName() → maps to hasData('name')

Sources: extension.neon57-63

Parser and Type Node Resolver Extensions

These extension points operate at different stages of PHPStan's analysis pipeline. Parser extensions modify the Abstract Syntax Tree (AST) before analysis, while type node resolver extensions customize how PHPDoc type annotations are interpreted.

Extension Points: phpstan.parser.richParserNodeVisitor and phpstan.phpDoc.typeNodeResolverExtension

The plugin registers BindThisScopeResolverExtension with both tags to implement a two-phase approach for handling $this scope in templates.


Registration Configuration:

extension.neon65-70 registers BindThisScopeResolverExtension with two tags:

  1. phpstan.parser.richParserNodeVisitor - for AST preprocessing
  2. phpstan.phpDoc.typeNodeResolverExtension - for custom type node resolution

The same service instance handles both phases, maintaining state about which classes need scope binding.

Target Files:

  • .phtml template files where $this refers to a block instance
  • data-install-*.php and data-upgrade-*.php scripts where $this refers to a setup resource

Problem Solved: Magento templates use $this to access protected methods of the containing block class. PHPStan normally forbids this, generating false positives. This extension makes protected members appear public for scope-bound $this variables.

Sources: extension.neon65-70

Extension Point Registration Pattern

All extension points follow a consistent registration pattern in extension.neon:


Common Service Arguments:

  • @mageCoreConfig - Injected into type extensions and rules for class name resolution
  • %enforceMagicMethodDocBlock% - Parameter injected into VarienObjectReflectionExtension
  • className - String argument specifying which class the extension targets

Dependency Graph:


Sources: extension.neon14-70

Extension Execution Order

PHPStan executes extensions at specific points in the analysis pipeline:

  1. Parser Extensions (richParserNodeVisitor) - Execute during file parsing, before any analysis. The BindThisScopeResolverExtension modifies PHPDoc AST nodes here.

  2. Type Node Resolver Extensions (typeNodeResolverExtension) - Execute when PHPStan resolves type annotations. The BindThisScopeResolverExtension creates custom object types here.

  3. Reflection Extensions (methodsClassReflectionExtension) - Execute when PHPStan queries class methods. The VarienObjectReflectionExtension adds magic methods here.

  4. Dynamic Return Type Extensions (dynamicStaticMethodReturnTypeExtension, dynamicMethodReturnTypeExtension) - Execute during type inference when analyzing method calls. All MageTypeExtension instances execute here.

  5. Rule Extensions (rules.rule) - Execute after type inference completes. The MageInvalidTypeRule validates resolved types here.

This ordering ensures that type information flows correctly through the analysis pipeline, with earlier phases providing context for later phases.

Sources: extension.neon1-71