Skip to content

Commit 96c9265

Browse files
Import project files
1 parent 0f809d3 commit 96c9265

File tree

8 files changed

+226
-0
lines changed

8 files changed

+226
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/composer.lock
2+
/vendor/

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# phpstan-magento1 extension
2+
3+
Extension for [PHPStan](https://github.com/phpstan/phpstan) to allow analysis of Magento 1 code.
4+
5+
Currently it assumes Magento is installed in the public/ directory of the project root. Further work is needed in phpstan itself to allow more intellegence for extensions to be more customised whilst working also with phpstan/phpstan-shim.
6+
7+
By default phpstan with this extension will test public/app/code/local only.
8+
9+
## Usage
10+
11+
Add `phpstan.neon` to your Magento 1 project.
12+
13+
Make sure it has
14+
15+
```neon
16+
includes:
17+
- vendor/inviqa/phpstan-magento1/extension.neon
18+
```
19+
20+
Whilst this extension depends on phpstan/phpstan, it can also depend on phpstan/phpstan-shim, which decouples its dependencies from the project's own use of them.
21+
22+
With coupled dependencies:
23+
24+
```bash
25+
composer require inviqa/phpstan-magento2 phpstan/phpstan
26+
```
27+
28+
With uncoupled phar package:
29+
30+
```bash
31+
composer require inviqa/phpstan-magento2 phpstan/phpstan-shim
32+
```

composer.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "inviqa/phpstan-magento1",
3+
"description": "Extension for PHPStan to allow analysis of Magento 1 code.",
4+
"type": "library",
5+
"require": {
6+
"phpstan/phpstan": "0.11.*"
7+
},
8+
"autoload": {
9+
"psr-4": {
10+
"PHPStanMagento1\\": "src/"
11+
}
12+
},
13+
"license": "MIT"
14+
}

extension.neon

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
parameters:
2+
vendor_path: %currentWorkingDirectory%/vendor
3+
paths:
4+
- %currentWorkingDirectory%/public/app/code/local
5+
bootstrap: %vendor_path%/inviqa/phpstan-magento1/phpstan-bootstrap.php
6+
excludes_analyse:
7+
- %currentWorkingDirectory%/public/app/code/local/*/*/data/*
8+
- %currentWorkingDirectory%/public/app/code/local/*/*/sql/*
9+
10+
services:
11+
-
12+
class: PHPStanMagento1\Type\Mage\HelperMethodsReturnTypeExtension
13+
tags:
14+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
15+
-
16+
class: PHPStanMagento1\Reflection\Varien\Object\MagicMethodsReflectionExtension
17+
tags:
18+
- phpstan.broker.methodsClassReflectionExtension

phpstan-bootstrap.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
if (!class_exists('Mage')) {
4+
require_once __DIR__ . '/../../../public/app/Mage.php';
5+
}
6+
7+
// workaround Magento's use of date phpdoc typehint for string type
8+
// better would be to implement the typehint to make it appear string type
9+
class date {}
10+
11+
Mage::app();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace PHPStanMagento1\Reflection\Varien\Object;
4+
5+
use PHPStan\Reflection\ClassMemberReflection;
6+
use PHPStan\Reflection\ClassReflection;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Reflection\TrivialParametersAcceptor;
9+
10+
class MagicMethodReflection implements MethodReflection
11+
{
12+
/**
13+
* @var ClassReflection
14+
*/
15+
private $declaringClass;
16+
17+
public function __construct(ClassReflection $declaringClass, string $name)
18+
{
19+
$this->declaringClass = $declaringClass;
20+
$this->name = $name;
21+
}
22+
23+
public function getDeclaringClass(): ClassReflection
24+
{
25+
return $this->declaringClass;
26+
}
27+
28+
public function getPrototype(): ClassMemberReflection
29+
{
30+
return $this;
31+
}
32+
33+
public function isStatic(): bool
34+
{
35+
return false;
36+
}
37+
38+
public function isPrivate(): bool
39+
{
40+
return false;
41+
}
42+
43+
public function isPublic(): bool
44+
{
45+
return true;
46+
}
47+
48+
public function getName(): string
49+
{
50+
return $this->name;
51+
}
52+
53+
/**
54+
* @return \PHPStan\Reflection\ParametersAcceptor[]
55+
*/
56+
public function getVariants(): array
57+
{
58+
return [
59+
new TrivialParametersAcceptor(),
60+
];
61+
}
62+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace PHPStanMagento1\Reflection\Varien\Object;
4+
5+
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\MethodReflection;
7+
use PHPStan\Reflection\MethodsClassReflectionExtension;
8+
9+
use Varien_Object;
10+
11+
class MagicMethodsReflectionExtension implements MethodsClassReflectionExtension
12+
{
13+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
14+
{
15+
$magentoMagicMethods = ['get', 'set', 'uns', 'has'];
16+
return $classReflection->isSubclassOf(Varien_Object::class)
17+
&& in_array(substr($methodName, 0, 3), $magentoMagicMethods);
18+
}
19+
20+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
21+
{
22+
return new MagicMethodReflection($classReflection, $methodName);
23+
}
24+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace PHPStanMagento1\Type\Mage;
4+
5+
use PhpParser\Node\Expr\StaticCall;
6+
use PhpParser\Node\Scalar\String_;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\MethodReflection;
9+
use PHPStan\ShouldNotHappenException;
10+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
11+
use PHPStan\Type\ObjectType;
12+
use PHPStan\Type\Type;
13+
14+
use Mage;
15+
16+
class HelperMethodsReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
17+
{
18+
public function getClass(): string
19+
{
20+
return Mage;
21+
}
22+
23+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
24+
{
25+
return in_array(
26+
$methodReflection->getName(),
27+
[
28+
'getModel',
29+
'getResourceModel',
30+
'getResourceSingleton',
31+
'getSingleton',
32+
'helper',
33+
]
34+
);
35+
}
36+
37+
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
38+
{
39+
if (!isset($methodCall->args[0]) || !$methodCall->args[0]->value instanceof String_) {
40+
throw new ShouldNotHappenException();
41+
}
42+
43+
$name = $methodCall->args[0]->value->value;
44+
$class = $this->getClassFromHelperMethod($methodReflection->getName(), $name);
45+
return new ObjectType($class);
46+
}
47+
48+
private function getClassFromHelperMethod($method, $name)
49+
{
50+
$config = Mage::getConfig();
51+
switch ($method) {
52+
case 'getModel':
53+
case 'getSingleton':
54+
return $config->getModelClassName($name);
55+
case 'getResourceModel':
56+
case 'getResourceSingleton':
57+
return $config->getModelClassName($name);
58+
case 'helper':
59+
return $config->getHelperClassName($name);
60+
}
61+
throw new ShouldNotHappenException();
62+
}
63+
}

0 commit comments

Comments
 (0)