Skip to content

Commit 9ff260d

Browse files
committed
Analyse PSR-4 info from composer.json file - Close #51
1 parent cdcf0ce commit 9ff260d

File tree

4 files changed

+95
-13
lines changed

4 files changed

+95
-13
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
},
2727
"require": {
2828
"php": "^7.3 || ^8.0",
29-
"nikic/php-parser": "^4.2"
29+
"nikic/php-parser": "^4.2",
30+
"ext-json": "*"
3031
},
3132
"require-dev": {
3233
"laminas/laminas-filter": "^2.9",

src/Package/Psr4Info.php

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
namespace OpenCodeModeling\CodeAst\Package;
1212

13-
use Composer\Autoload\ClassLoader;
14-
1513
final class Psr4Info implements ClassInfo
1614
{
1715
/**
@@ -172,30 +170,67 @@ private function normalizePath(string $path): string
172170
/**
173171
* Creates an instance of the class Psr4Info based on the Composer configuration.
174172
*
175-
* @param ClassLoader $classLoader Composer ClassLoader instance
173+
* @param string $basePath Usually the composer.json root folder
174+
* @param string $composerFileContent Content of composer.json to detect registered namespaces
176175
* @param callable $filterDirectoryToNamespace Callable to filter a directory to a namespace
177-
* @param callable $filterNamespaceToDirectory Callable to filter a namespace to a directory
176+
* @param callable $filterNamespaceToDirectory Callable to filter a namespace to a directory
178177
* @param string $exclude Specifies which path should be ignored
179-
* @return array
178+
* @return Psr4Info[]
179+
* @throws \JsonException
180180
*/
181181
public static function fromComposer(
182-
ClassLoader $classLoader,
182+
string $basePath,
183+
string $composerFileContent,
183184
callable $filterDirectoryToNamespace,
184185
callable $filterNamespaceToDirectory,
185186
string $exclude = 'vendor' . DIRECTORY_SEPARATOR
186187
): array {
187188
$namespaces = [];
188189

189-
foreach ($classLoader->getPrefixesPsr4() as $namespace => $paths) {
190-
$realpath = \preg_replace('/^' . \addcslashes(\getcwd(), '/') . '\//', '', \realpath($paths[0]));
191-
if (false !== \stripos($realpath, $exclude)) {
190+
$composer = \json_decode($composerFileContent, true, 512, \JSON_BIGINT_AS_STRING | \JSON_THROW_ON_ERROR);
191+
192+
foreach ($composer['autoload']['psr-4'] ?? [] as $namespace => $paths) {
193+
$namespaces[] = self::fromNamespace($basePath, $namespace, (array) $paths, $filterDirectoryToNamespace, $filterNamespaceToDirectory, $exclude);
194+
}
195+
foreach ($composer['autoload-dev']['psr-4'] ?? [] as $namespace => $paths) {
196+
$namespaces[] = self::fromNamespace($basePath, $namespace, (array) $paths, $filterDirectoryToNamespace, $filterNamespaceToDirectory, $exclude);
197+
}
198+
199+
return \array_merge(...$namespaces);
200+
}
201+
202+
/**
203+
* @param string $basePath Usually the composer.json root folder
204+
* @param string $namespace
205+
* @param array $paths
206+
* @param callable $filterDirectoryToNamespace
207+
* @param callable $filterNamespaceToDirectory
208+
* @param string $exclude
209+
* @return Psr4Info[]
210+
*/
211+
public static function fromNamespace(
212+
string $basePath,
213+
string $namespace,
214+
array $paths,
215+
callable $filterDirectoryToNamespace,
216+
callable $filterNamespaceToDirectory,
217+
string $exclude = 'vendor' . DIRECTORY_SEPARATOR
218+
): array {
219+
$classInfoList = [];
220+
221+
$basePath = \rtrim($basePath, '/') . DIRECTORY_SEPARATOR;
222+
223+
foreach ($paths as $path) {
224+
$path = $basePath . $path;
225+
226+
if (false !== \stripos($path, $exclude)) {
192227
continue;
193228
}
194-
$classInfo = new self($realpath, $namespace, $filterDirectoryToNamespace, $filterNamespaceToDirectory);
229+
$classInfo = new self($path, $namespace, $filterDirectoryToNamespace, $filterNamespaceToDirectory);
195230

196-
$namespaces[] = $classInfo;
231+
$classInfoList[] = $classInfo;
197232
}
198233

199-
return $namespaces;
234+
return $classInfoList;
200235
}
201236
}

tests/Package/Psr4InfoTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,33 @@
1717

1818
final class Psr4InfoTest extends TestCase
1919
{
20+
/**
21+
* @test
22+
*/
23+
public function it_can_be_created_from_composer(): void
24+
{
25+
$psr4InfoList = Psr4Info::fromComposer(
26+
'/service',
27+
\file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'composer.json'),
28+
$this->filterDirectoryToNamespace(),
29+
$this->filterNamespaceToDirectory()
30+
);
31+
32+
$this->assertCount(4, $psr4InfoList);
33+
34+
$this->assertSame('OpenCodeModeling\\CodeAst', $psr4InfoList[0]->getPackagePrefix());
35+
$this->assertSame('/service/src', $psr4InfoList[0]->getSourceFolder());
36+
37+
$this->assertSame('Monolog', $psr4InfoList[1]->getPackagePrefix());
38+
$this->assertSame('/service/src', $psr4InfoList[1]->getSourceFolder());
39+
40+
$this->assertSame('Monolog', $psr4InfoList[2]->getPackagePrefix());
41+
$this->assertSame('/service/lib', $psr4InfoList[2]->getSourceFolder());
42+
43+
$this->assertSame('OpenCodeModelingTest\\CodeAst', $psr4InfoList[3]->getPackagePrefix());
44+
$this->assertSame('/service/tests', $psr4InfoList[3]->getSourceFolder());
45+
}
46+
2047
/**
2148
* @test
2249
* @dataProvider providerForGetClassNamespace

tests/Package/_files/composer.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"autoload": {
3+
"psr-0": {
4+
"Monolog\\": "src/"
5+
},
6+
"psr-4": {
7+
"OpenCodeModeling\\CodeAst\\": "src/",
8+
"Monolog\\": [
9+
"src/",
10+
"lib/"
11+
]
12+
}
13+
},
14+
"autoload-dev": {
15+
"psr-4": {
16+
"OpenCodeModelingTest\\CodeAst\\": "tests/"
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)