Skip to content

Commit 1bcdba3

Browse files
authored
Merge pull request #284 from shochdoerfer/feature/prefer_local_extension_class
Ignore ext autoloader for local classes
2 parents d00470f + bfe87d0 commit 1bcdba3

File tree

6 files changed

+80
-34
lines changed

6 files changed

+80
-34
lines changed

extension.neon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,15 @@ services:
8888
class: bitExpert\PHPStan\Magento\Autoload\ExtensionInterfaceAutoloader
8989
arguments:
9090
cache: @autoloaderCache
91-
attributeDataProvider: @extensionAttributeDataProvider
9291
classLoaderProvider: @classLoaderProvider
92+
attributeDataProvider: @extensionAttributeDataProvider
9393
tags:
9494
- phpstan.magento.autoloader
9595
extensionAutoloader:
9696
class: bitExpert\PHPStan\Magento\Autoload\ExtensionAutoloader
9797
arguments:
9898
cache: @autoloaderCache
99+
classLoaderProvider: @classLoaderProvider
99100
attributeDataProvider: @extensionAttributeDataProvider
100101
tags:
101102
- phpstan.magento.autoloader

src/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloader.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace bitExpert\PHPStan\Magento\Autoload;
1414

15+
use bitExpert\PHPStan\Magento\Autoload\DataProvider\ClassLoaderProvider;
1516
use bitExpert\PHPStan\Magento\Autoload\DataProvider\ExtensionAttributeDataProvider;
1617
use Laminas\Code\Generator\ClassGenerator;
1718
use Laminas\Code\Generator\DocBlock\Tag\ParamTag;
@@ -28,6 +29,10 @@ class ExtensionAutoloader implements Autoloader
2829
* @var Cache
2930
*/
3031
private $cache;
32+
/**
33+
* @var ClassLoaderProvider
34+
*/
35+
private $classLoaderProvider;
3136
/**
3237
* @var ExtensionAttributeDataProvider
3338
*/
@@ -37,13 +42,16 @@ class ExtensionAutoloader implements Autoloader
3742
* ExtensionAutoloader constructor.
3843
*
3944
* @param Cache $cache
45+
* @param ClassLoaderProvider $classLoaderProvider
4046
* @param ExtensionAttributeDataProvider $attributeDataProvider
4147
*/
4248
public function __construct(
4349
Cache $cache,
50+
ClassLoaderProvider $classLoaderProvider,
4451
ExtensionAttributeDataProvider $attributeDataProvider
4552
) {
4653
$this->cache = $cache;
54+
$this->classLoaderProvider = $classLoaderProvider;
4755
$this->attributeDataProvider = $attributeDataProvider;
4856
}
4957

@@ -53,17 +61,18 @@ public function autoload(string $class): void
5361
return;
5462
}
5563

56-
$cachedFilename = $this->cache->load($class, '');
57-
if ($cachedFilename === null) {
58-
try {
64+
// fix for PHPStan 1.7.5 and later: Classes generated by autoloaders are supposed to "win" against
65+
// local classes in your project. We need to check first if classes exists locally before generating them!
66+
$pathToLocalClass = $this->classLoaderProvider->findFile($class);
67+
if ($pathToLocalClass === false) {
68+
$pathToLocalClass = $this->cache->load($class, '');
69+
if ($pathToLocalClass === null) {
5970
$this->cache->save($class, '', $this->getFileContents($class));
60-
$cachedFilename = $this->cache->load($class, '');
61-
} catch (\Exception $e) {
62-
return;
71+
$pathToLocalClass = $this->cache->load($class, '');
6372
}
6473
}
6574

66-
require_once($cachedFilename);
75+
require_once($pathToLocalClass);
6776
}
6877

6978
/**

src/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloader.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ class ExtensionInterfaceAutoloader implements Autoloader
2828
* @var Cache
2929
*/
3030
private $cache;
31-
/**
32-
* @var ExtensionAttributeDataProvider
33-
*/
34-
private $attributeDataProvider;
3531
/**
3632
* @var ClassLoaderProvider
3733
*/
3834
private $classLoaderProvider;
35+
/**
36+
* @var ExtensionAttributeDataProvider
37+
*/
38+
private $attributeDataProvider;
3939

4040
/**
4141
* ExtensionInterfaceAutoloader constructor.
@@ -46,12 +46,12 @@ class ExtensionInterfaceAutoloader implements Autoloader
4646
*/
4747
public function __construct(
4848
Cache $cache,
49-
ExtensionAttributeDataProvider $attributeDataProvider,
50-
ClassLoaderProvider $classLoaderProvider
49+
ClassLoaderProvider $classLoaderProvider,
50+
ExtensionAttributeDataProvider $attributeDataProvider
5151
) {
5252
$this->cache = $cache;
53-
$this->attributeDataProvider = $attributeDataProvider;
5453
$this->classLoaderProvider = $classLoaderProvider;
54+
$this->attributeDataProvider = $attributeDataProvider;
5555
}
5656

5757
public function autoload(string $interfaceName): void

tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace bitExpert\PHPStan\Magento\Autoload;
44

55
use bitExpert\PHPStan\Magento\Autoload\Cache\FileCacheStorage;
6+
use bitExpert\PHPStan\Magento\Autoload\DataProvider\ClassLoaderProvider;
67
use bitExpert\PHPStan\Magento\Autoload\DataProvider\ExtensionAttributeDataProvider;
78
use org\bovigo\vfs\vfsStream;
89
use PHPStan\Cache\Cache;
@@ -14,6 +15,10 @@ class ExtensionAutoloaderUnitTest extends TestCase
1415
* @var Cache|\PHPUnit\Framework\MockObject\MockObject
1516
*/
1617
private $cache;
18+
/**
19+
* @var ClassLoaderProvider|\PHPUnit\Framework\MockObject\MockObject
20+
*/
21+
private $classLoader;
1722
/**
1823
* @var ExtensionAttributeDataProvider|\PHPUnit\Framework\MockObject\MockObject
1924
*/
@@ -26,9 +31,11 @@ class ExtensionAutoloaderUnitTest extends TestCase
2631
protected function setUp(): void
2732
{
2833
$this->cache = $this->createMock(Cache::class);
34+
$this->classLoader = $this->createMock(ClassLoaderProvider::class);
2935
$this->extAttrDataProvider = $this->createMock(ExtensionAttributeDataProvider::class);
3036
$this->autoloader = new ExtensionAutoloader(
3137
$this->cache,
38+
$this->classLoader,
3239
$this->extAttrDataProvider
3340
);
3441
}
@@ -38,17 +45,38 @@ protected function setUp(): void
3845
*/
3946
public function autoloaderIgnoresClassesWithoutExtensionInterfacePostfix(): void
4047
{
48+
$this->classLoader->expects(self::never())
49+
->method('findFile');
4150
$this->cache->expects(self::never())
4251
->method('load');
4352

4453
$this->autoloader->autoload('SomeClass');
4554
}
4655

56+
/**
57+
* @test
58+
*/
59+
public function autoloaderPrefersLocalFile(): void
60+
{
61+
$this->classLoader->expects(self::once())
62+
->method('findFile')
63+
->willReturn(__DIR__ . '/HelperExtension.php');
64+
$this->cache->expects(self::never())
65+
->method('load');
66+
67+
$this->autoloader->autoload(HelperExtension::class);
68+
69+
self::assertTrue(class_exists(HelperExtension::class, false));
70+
}
71+
4772
/**
4873
* @test
4974
*/
5075
public function autoloaderUsesCachedFileWhenFound(): void
5176
{
77+
$this->classLoader->expects(self::once())
78+
->method('findFile')
79+
->willReturn(false);
5280
$this->cache->expects(self::once())
5381
->method('load')
5482
->willReturn(__DIR__ . '/HelperExtension.php');
@@ -66,18 +94,21 @@ public function autoloaderUsesCachedFileWhenFound(): void
6694
*/
6795
public function autoloadGeneratesInterfaceWhenNotCached(): void
6896
{
97+
$this->classLoader->expects(self::once())
98+
->method('findFile')
99+
->willReturn(false);
100+
$this->extAttrDataProvider->expects(self::once())
101+
->method('getAttributesForInterface')
102+
->willReturn(['attr' => 'string']);
103+
69104
$className = 'MyUncachedExtension';
70105
// since the generated class implements an interface, we need to make it available here, otherwise
71106
// the autoloader will fail with an exception that the interface can't be found!
72107
class_alias(HelperExtensionInterface::class, 'MyUncachedExtensionInterface');
73108

74109
$root = vfsStream::setup('test');
75110
$cache = new Cache(new FileCacheStorage($root->url() . '/tmp/cache/PHPStan'));
76-
$autoloader = new ExtensionAutoloader($cache, $this->extAttrDataProvider);
77-
78-
$this->extAttrDataProvider->expects(self::once())
79-
->method('getAttributesForInterface')
80-
->willReturn(['attr' => 'string']);
111+
$autoloader = new ExtensionAutoloader($cache, $this->classLoader, $this->extAttrDataProvider);
81112

82113
$autoloader->autoload($className);
83114
static::assertTrue(class_exists($className));

tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class ExtensionInterfaceAutoloaderUnitTest extends TestCase
2323
/**
2424
* @var ClassLoaderProvider|\PHPUnit\Framework\MockObject\MockObject
2525
*/
26-
private $classyDataProvider;
26+
private $classLoader;
2727
/**
2828
* @var ExtensionInterfaceAutoloader
2929
*/
@@ -32,12 +32,12 @@ class ExtensionInterfaceAutoloaderUnitTest extends TestCase
3232
protected function setUp(): void
3333
{
3434
$this->cache = $this->createMock(Cache::class);
35+
$this->classLoader = $this->createMock(ClassLoaderProvider::class);
3536
$this->extAttrDataProvider = $this->createMock(ExtensionAttributeDataProvider::class);
36-
$this->classyDataProvider = $this->createMock(ClassLoaderProvider::class);
3737
$this->autoloader = new ExtensionInterfaceAutoloader(
3838
$this->cache,
39-
$this->extAttrDataProvider,
40-
$this->classyDataProvider
39+
$this->classLoader,
40+
$this->extAttrDataProvider
4141
);
4242
}
4343

@@ -46,7 +46,7 @@ protected function setUp(): void
4646
*/
4747
public function autoloaderIgnoresClassesWithoutExtensionInterfacePostfix(): void
4848
{
49-
$this->classyDataProvider->expects(self::never())
49+
$this->classLoader->expects(self::never())
5050
->method('findFile');
5151
$this->cache->expects(self::never())
5252
->method('load');
@@ -59,7 +59,7 @@ public function autoloaderIgnoresClassesWithoutExtensionInterfacePostfix(): void
5959
*/
6060
public function autoloaderPrefersLocalFile(): void
6161
{
62-
$this->classyDataProvider->expects(self::once())
62+
$this->classLoader->expects(self::once())
6363
->method('findFile')
6464
->willReturn(__DIR__ . '/HelperExtensionInterface.php');
6565
$this->cache->expects(self::never())
@@ -75,7 +75,7 @@ public function autoloaderPrefersLocalFile(): void
7575
*/
7676
public function autoloaderUsesCachedFileWhenFound(): void
7777
{
78-
$this->classyDataProvider->expects(self::once())
78+
$this->classLoader->expects(self::once())
7979
->method('findFile')
8080
->willReturn(false);
8181
$this->cache->expects(self::once())
@@ -100,14 +100,14 @@ public function autoloadDoesNotGenerateInterfaceWhenNoAttributesExist(): void
100100

101101
$interfaceName = 'NonExistentExtensionInterface';
102102

103-
$this->classyDataProvider->expects(self::once())
103+
$this->classLoader->expects(self::once())
104104
->method('findFile')
105105
->willReturn(false);
106106
$this->cache->expects(self::once())
107107
->method('load')
108108
->willReturn(null);
109109

110-
$this->classyDataProvider->expects(self::once())
110+
$this->classLoader->expects(self::once())
111111
->method('exists')
112112
->willReturn(false);
113113

@@ -123,13 +123,13 @@ public function autoloadGeneratesInterfaceWhenNotCached(): void
123123

124124
$root = vfsStream::setup('test');
125125
$cache = new Cache(new FileCacheStorage($root->url() . '/tmp/cache/PHPStan'));
126-
$autoloader = new ExtensionInterfaceAutoloader($cache, $this->extAttrDataProvider, $this->classyDataProvider);
126+
$autoloader = new ExtensionInterfaceAutoloader($cache, $this->classLoader, $this->extAttrDataProvider);
127127

128-
$this->classyDataProvider->expects(self::once())
128+
$this->classLoader->expects(self::once())
129129
->method('findFile')
130130
->willReturn(false);
131131

132-
$this->classyDataProvider->expects(self::once())
132+
$this->classLoader->expects(self::once())
133133
->method('exists')
134134
->willReturn(true);
135135

tests/bitExpert/PHPStan/Magento/Autoload/RegistrationUnitTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,15 @@ public function provideAutoloaders(): array
5252
[new MockAutoloader()],
5353
[new ProxyAutoloader($cache, $classLoader)],
5454
[new TestFrameworkAutoloader(__DIR__)],
55+
[new ExtensionAutoloader(
56+
$cache,
57+
new ClassLoaderProvider(__DIR__),
58+
new ExtensionAttributeDataProvider(__DIR__)
59+
)],
5560
[new ExtensionInterfaceAutoloader(
5661
$cache,
57-
new ExtensionAttributeDataProvider(__DIR__),
58-
new ClassLoaderProvider(__DIR__)
62+
new ClassLoaderProvider(__DIR__),
63+
new ExtensionAttributeDataProvider(__DIR__)
5964
)]
6065
];
6166
}

0 commit comments

Comments
 (0)