Skip to content

Commit 4d07c88

Browse files
committed
Large refactor and new features
1 parent 01418cc commit 4d07c88

24 files changed

+785
-441
lines changed

extension-mage-autoload.neon

+45-13
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
# https://github.com/phpstan/phpstan/issues/6744#event-6194525980
66
parametersSchema:
77
magentoRootPath: string()
8+
enforceMagicMethodDocBlock: bool()
89
parameters:
910
magentoRootPath: %currentWorkingDirectory%/htdocs
11+
enforceMagicMethodDocBlock: false
1012
excludePaths:
1113
- */app/code/local/*/*/data/*
1214
- */app/code/local/*/*/sql/*
@@ -22,35 +24,65 @@ parameters:
2224
- throwException
2325

2426
services:
27+
mageCoreConfig:
28+
class: PHPStanMagento1\Config\MageCoreConfig
29+
30+
## Dynamic Return Type Extension to return correct class from Mage::getModel() etc
2531
-
26-
class: PHPStanMagento1\Reflection\Varien\Object\MagicMethodsReflectionExtension
32+
class: PHPStanMagento1\Type\MageTypeExtension
33+
arguments:
34+
mageCoreConfig: @mageCoreConfig
35+
className: Mage
2736
tags:
28-
- phpstan.broker.methodsClassReflectionExtension
37+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
2938
-
30-
class: PHPStanMagento1\Type\Mage\CoreBlockAbstract\Helper
39+
class: PHPStanMagento1\Type\MageTypeExtension
40+
arguments:
41+
mageCoreConfig: @mageCoreConfig
42+
className: Mage_Core_Block_Abstract
3143
tags:
3244
- phpstan.broker.dynamicMethodReturnTypeExtension
3345
-
34-
class: PHPStanMagento1\Type\Mage\CoreModelLayout\Helper
46+
class: PHPStanMagento1\Type\MageTypeExtension
47+
arguments:
48+
mageCoreConfig: @mageCoreConfig
49+
className: Mage_Core_Model_Abstract
3550
tags:
3651
- phpstan.broker.dynamicMethodReturnTypeExtension
3752
-
38-
class: PHPStanMagento1\Type\Mage\CoreModelLayout\GetBlockSingleton
53+
class: PHPStanMagento1\Type\MageTypeExtension
54+
arguments:
55+
mageCoreConfig: @mageCoreConfig
56+
className: Mage_Core_Model_Layout
3957
tags:
4058
- phpstan.broker.dynamicMethodReturnTypeExtension
4159
-
42-
class: PHPStanMagento1\Type\Mage\GetModel
60+
class: PHPStanMagento1\Type\MageTypeExtension
61+
arguments:
62+
mageCoreConfig: @mageCoreConfig
63+
className: Mage_Core_Controller_Varien_Action
4364
tags:
44-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
65+
- phpstan.broker.dynamicMethodReturnTypeExtension
66+
67+
## Rule to detect invalid class names returned by the Dynamic Return Type Extension
4568
-
46-
class: PHPStanMagento1\Type\Mage\GetResourceModel
69+
class: PHPStanMagento1\Rules\MageInvalidTypeRule
70+
arguments:
71+
mageCoreConfig: @mageCoreConfig
4772
tags:
48-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
73+
- phpstan.rules.rule
74+
75+
## Class Reflection Extension for Varien_Object's magic methods
4976
-
50-
class: PHPStanMagento1\Type\Mage\GetSingleton
77+
class: PHPStanMagento1\Reflection\VarienObjectReflectionExtension
78+
arguments:
79+
enforceDocBlock: %enforceMagicMethodDocBlock%
5180
tags:
52-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
81+
- phpstan.broker.methodsClassReflectionExtension
82+
83+
## PHP-Parser Extension to allow phtml and data install scripts to access protected methods with $this
5384
-
54-
class: PHPStanMagento1\Type\Mage\Helper
85+
class: PHPStanMagento1\PhpDoc\BindThisScopeResolverExtension
5586
tags:
56-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
87+
- phpstan.parser.richParserNodeVisitor
88+
- phpstan.phpDoc.typeNodeResolverExtension

extension.neon

+45-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
parametersSchema:
22
magentoRootPath: string()
3+
enforceMagicMethodDocBlock: bool()
34
parameters:
45
magentoRootPath: %currentWorkingDirectory%/htdocs
6+
enforceMagicMethodDocBlock: false
57
excludePaths:
68
- */app/code/local/*/*/data/*
79
- */app/code/local/*/*/sql/*
@@ -17,35 +19,65 @@ parameters:
1719
- throwException
1820

1921
services:
22+
mageCoreConfig:
23+
class: PHPStanMagento1\Config\MageCoreConfig
24+
25+
## Dynamic Return Type Extension to return correct class from Mage::getModel() etc
2026
-
21-
class: PHPStanMagento1\Reflection\Varien\Object\MagicMethodsReflectionExtension
27+
class: PHPStanMagento1\Type\MageTypeExtension
28+
arguments:
29+
mageCoreConfig: @mageCoreConfig
30+
className: Mage
2231
tags:
23-
- phpstan.broker.methodsClassReflectionExtension
32+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
2433
-
25-
class: PHPStanMagento1\Type\Mage\CoreModelLayout\Helper
34+
class: PHPStanMagento1\Type\MageTypeExtension
35+
arguments:
36+
mageCoreConfig: @mageCoreConfig
37+
className: Mage_Core_Block_Abstract
2638
tags:
2739
- phpstan.broker.dynamicMethodReturnTypeExtension
2840
-
29-
class: PHPStanMagento1\Type\Mage\CoreBlockAbstract\Helper
41+
class: PHPStanMagento1\Type\MageTypeExtension
42+
arguments:
43+
mageCoreConfig: @mageCoreConfig
44+
className: Mage_Core_Model_Abstract
3045
tags:
3146
- phpstan.broker.dynamicMethodReturnTypeExtension
3247
-
33-
class: PHPStanMagento1\Type\Mage\CoreModelLayout\GetBlockSingleton
48+
class: PHPStanMagento1\Type\MageTypeExtension
49+
arguments:
50+
mageCoreConfig: @mageCoreConfig
51+
className: Mage_Core_Model_Layout
3452
tags:
3553
- phpstan.broker.dynamicMethodReturnTypeExtension
3654
-
37-
class: PHPStanMagento1\Type\Mage\GetModel
55+
class: PHPStanMagento1\Type\MageTypeExtension
56+
arguments:
57+
mageCoreConfig: @mageCoreConfig
58+
className: Mage_Core_Controller_Varien_Action
3859
tags:
39-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
60+
- phpstan.broker.dynamicMethodReturnTypeExtension
61+
62+
## Rule to detect invalid class names returned by the Dynamic Return Type Extension
4063
-
41-
class: PHPStanMagento1\Type\Mage\GetResourceModel
64+
class: PHPStanMagento1\Rules\MageInvalidTypeRule
65+
arguments:
66+
mageCoreConfig: @mageCoreConfig
4267
tags:
43-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
68+
- phpstan.rules.rule
69+
70+
## Class Reflection Extension for Varien_Object's magic methods
4471
-
45-
class: PHPStanMagento1\Type\Mage\GetSingleton
72+
class: PHPStanMagento1\Reflection\VarienObjectReflectionExtension
73+
arguments:
74+
enforceDocBlock: %enforceMagicMethodDocBlock%
4675
tags:
47-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
76+
- phpstan.broker.methodsClassReflectionExtension
77+
78+
## PHP-Parser Extension to allow phtml and data install scripts to access protected methods with $this
4879
-
49-
class: PHPStanMagento1\Type\Mage\Helper
80+
class: PHPStanMagento1\PhpDoc\BindThisScopeResolverExtension
5081
tags:
51-
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
82+
- phpstan.parser.richParserNodeVisitor
83+
- phpstan.phpDoc.typeNodeResolverExtension

src/Config/MageCoreConfig.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace PHPStanMagento1\Config;
4+
5+
final class MageCoreConfig
6+
{
7+
/**
8+
* @var MagentoCore|\Mage_Core_Model_Config|null
9+
*/
10+
protected static $config;
11+
12+
/**
13+
* Load Magento XML configuration
14+
*
15+
* @return MagentoCore|\Mage_Core_Model_Config
16+
*/
17+
public function getConfig()
18+
{
19+
if (self::$config) {
20+
return self::$config;
21+
}
22+
23+
//change this to DI of staticReflection config
24+
if (\defined('staticReflection')) {
25+
$config = new MagentoCore();
26+
$config->loadBase();
27+
$config->loadModules();
28+
} else {
29+
$config = \Mage::app()->getConfig();
30+
}
31+
self::$config = $config;
32+
return self::$config;
33+
}
34+
35+
/**
36+
* @return ?callable(string): (string|false)
37+
*/
38+
public function getClassNameConverterFunction(string $class, string $method): ?callable
39+
{
40+
switch ("$class::$method") {
41+
case 'Mage::getModel':
42+
case 'Mage::getSingleton':
43+
return fn (string $alias) => $this->getConfig()->getModelClassName($alias);
44+
case 'Mage::getResourceModel':
45+
case 'Mage::getResourceSingleton':
46+
return fn (string $alias) => $this->getConfig()->getResourceModelClassName($alias);
47+
case 'Mage_Core_Model_Layout::createBlock':
48+
case 'Mage_Core_Model_Layout::getBlockSingleton':
49+
return fn (string $alias) => $this->getConfig()->getBlockClassName($alias);
50+
case 'Mage::helper':
51+
case 'Mage_Core_Model_Layout::helper':
52+
case 'Mage_Core_Block_Abstract::helper':
53+
return fn (string $alias) => $this->getConfig()->getHelperClassName($alias);
54+
case 'Mage_Admin_Model_User::_helper':
55+
case 'Mage_Adminhtml_Controller_Rss_Abstract::_helper':
56+
case 'Mage_Api_Model_User::_helper':
57+
case 'Mage_Customer_AccountController::_helper':
58+
case 'Mage_Customer_Model_Customer::_helper':
59+
case 'Mage_Rss_Controller_Abstract::_helper':
60+
case 'Mage_SalesRule_Model_Validator::_helper':
61+
case 'Mage_Weee_Helper_Data::_helper':
62+
case 'Mage_Weee_Model_Config_Source_Fpt_Tax::_helper':
63+
// Deprecated _helper calls
64+
return fn (string $alias) => $this->getConfig()->getHelperClassName($alias);
65+
}
66+
return null;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace PHPStanMagento1\PhpDoc;
4+
5+
use PHPStanMagento1\Reflection\PublicMethodReflection;
6+
use PHPStanMagento1\Reflection\PublicPropertyReflection;
7+
use PhpParser\Comment;
8+
use PhpParser\Node;
9+
use PhpParser\NodeTraverser;
10+
use PhpParser\NodeVisitorAbstract;
11+
use PHPStan\Analyser\NameScope;
12+
use PHPStan\PhpDoc\TypeNodeResolverExtension;
13+
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
14+
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
15+
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
16+
use PHPStan\Reflection\ClassMemberAccessAnswerer;
17+
use PHPStan\Reflection\ExtendedMethodReflection;
18+
use PHPStan\Reflection\ExtendedPropertyReflection;
19+
use PHPStan\Reflection\WrappedExtendedMethodReflection;
20+
use PHPStan\Reflection\WrappedExtendedPropertyReflection;
21+
use PHPStan\Type\ObjectType;
22+
use PHPStan\Type\Type;
23+
use function count;
24+
use function is_string;
25+
use function preg_replace;
26+
27+
final class BindThisScopeResolverExtension extends NodeVisitorAbstract implements TypeNodeResolverExtension
28+
{
29+
private const GENERIC_TYPE = 'bind-this-scope';
30+
private const PHPDOC_PATTERN = '/@var\s+((\\\?\w+)+)\s+\$this/';
31+
private const PHPDOC_REPLACE = '@var ' . self::GENERIC_TYPE . '<$1> $this';
32+
33+
public function beforeTraverse(array $nodes)
34+
{
35+
foreach ($nodes as $node) {
36+
if ($node instanceof Node\Stmt\Namespace_) {
37+
$this->beforeTraverse($node->stmts);
38+
break;
39+
}
40+
41+
if ($node->getDocComment() === null) {
42+
continue;
43+
}
44+
45+
$comment = preg_replace(self::PHPDOC_PATTERN, self::PHPDOC_REPLACE, $node->getDocComment()->getText(), 1, $match);
46+
47+
if (is_string($comment) && $match === 1) {
48+
$node->setDocComment(new Comment\Doc($comment));
49+
break;
50+
}
51+
}
52+
return null;
53+
}
54+
55+
public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type
56+
{
57+
if (!$typeNode instanceof GenericTypeNode) {
58+
return null;
59+
}
60+
61+
$typeName = $typeNode->type;
62+
if ($typeName->name !== self::GENERIC_TYPE) {
63+
return null;
64+
}
65+
66+
/** @var IdentifierTypeNode[] */
67+
$arguments = $typeNode->genericTypes;
68+
if (count($arguments) !== 1) {
69+
return null;
70+
}
71+
72+
$className = $nameScope->resolveStringName($arguments[0]->name);
73+
74+
return new class($className) extends ObjectType {
75+
public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
76+
{
77+
return new WrappedExtendedMethodReflection(
78+
new PublicMethodReflection(parent::getMethod($methodName, $scope))
79+
);
80+
}
81+
public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
82+
{
83+
return new WrappedExtendedPropertyReflection(
84+
new PublicPropertyReflection(parent::getProperty($propertyName, $scope))
85+
);
86+
}
87+
};
88+
}
89+
}

0 commit comments

Comments
 (0)