Skip to content

Commit

Permalink
Creating ComposerClassLoaderParser
Browse files Browse the repository at this point in the history
An advanced usage case where composer.json is not available at runtime, so using classloader object to fetch classes and data.
Created tests and documentation for new feature.

Also updated phpunit version to latest.
  • Loading branch information
spenserhale committed Apr 15, 2024
1 parent beedf72 commit 33a3a50
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 9 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"vendor-dir": "vendor"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
"phpunit/phpunit": "^10"
},
"scripts": {
"test": "phpunit"
Expand Down
10 changes: 2 additions & 8 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
<?xml version="1.0"?>
<phpunit
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="unit">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
</phpunit>
20 changes: 20 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ $classes = \SH\AutoHook\ComposerJsonParser::getClasses($composerJsonPath);
// Process classes to string
[$output] = \SH\AutoHook\AttributeResolver::processClassesToString($classes);

// Write to file
(bool) $written = \SH\AutoHook\FileWriter::write($output, $outputPath);
```

### Advanced Usage

Some projects may not have the composer.json available at runtime, so you can use the class loader object.

#### Build/Wire Up

```php
// Get Classloader object
$loader = require 'vendor/autoload.php';

// Get the list of classes
$classes = \SH\AutoHook\ComposerClassLoaderParser::getClasses($loader, ['App\\', 'MyNamespace\\']);

// Process classes to string
[$output] = \SH\AutoHook\AttributeResolver::processClassesToString($classes);

// Write to file
(bool) $written = \SH\AutoHook\FileWriter::write($output, $outputPath);
```
Expand Down
68 changes: 68 additions & 0 deletions src/ComposerClassLoaderParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace SH\AutoHook;

use \Composer\Autoload\ClassLoader;

class ComposerClassLoaderParser
{
/**
* Returns an array of classes in namespaces defined in the composer.json file.
*
* @param ClassLoader $classLoader
* @param array<string> $namespaces Since the classloader does not know which are project vs vendor namespaces, you can pass in an array of namespaces to filter by.
*
* @return array<string>
*/
public static function getClasses(ClassLoader $classLoader, array $namespaces = []): array
{
['namespaces' => $allNamespaces, 'classes' => $classes] = self::getNamespacesAndClasses($classLoader);

$namespaces = $namespaces ?: array_keys($allNamespaces);
$classes = array_keys($classes);

$matches = [];
foreach ($namespaces as $namespace) {
foreach ($classes as $key => $class) {
if (str_starts_with($class, $namespace)) {
$matches[] = $class;

unset($classes[$key]);
}
}
}

return $matches;
}

/**
* Returns an array with detailed information about namespaces and classes.
*
* Namespaces(k:namespace, v:paths array): map of namespaces and their absolute paths.
* Classes(k:class, v:path): map of classes and their absolute path.
*
* @return array{
* namespaces: array<string, array<string>>,
* classes: array<string, string>
* }
*
* Example:
* [
* 'namespaces' => [
* 'MyApp\\Controllers' => ['/Users/jphndpe/Project/src/Controllers/'],
* 'MyApp\\Models' => ['/Users/jphndpe/Project/src/Models/'],
* ],
* 'classes' => [
* 'MyApp\\Controllers\\HomeController' => '/Users/jphndpe/Project/src/Controllers/HomeController.php',
* 'MyApp\\Models\\UserModel' => '/Users/jphndpe/Project/src/Models/UserModel.php',
* ]
* ]
*/
public static function getNamespacesAndClasses(ClassLoader $classLoader): array
{
return [
'namespaces' => $classLoader->getPrefixesPsr4(),
'classes' => $classLoader->getClassMap()
];
}
}
40 changes: 40 additions & 0 deletions tests/ComposerClassLoaderParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace SH\AutoHook\Tests;

use PHPUnit\Framework\TestCase;
use \Composer\Autoload\ClassLoader;
use SH\AutoHook\ComposerClassLoaderParser;

class ComposerClassLoaderParserTest extends TestCase
{
public function testGetClasses(): void
{
$classes = ComposerClassLoaderParser::getClasses(self::getClassLoader());

self::assertContains(ComposerClassLoaderParser::class, $classes);

$classes = ComposerClassLoaderParser::getClasses(self::getClassLoader(), ['SH\\AutoHook\\']);

self::assertContains(ComposerClassLoaderParser::class, $classes);
}

public function testGetNamespacesAndClasses(): void
{
['namespaces' => $namespaces, 'classes' => $classes] = ComposerClassLoaderParser::getNamespacesAndClasses(self::getClassLoader());

self::assertArrayHasKey('SH\\AutoHook\\', $namespaces);
self::assertArrayHasKey(ComposerClassLoaderParser::class, $classes);
}

private static function getClassLoader(): ClassLoader
{
static $classLoader;

if (null === $classLoader) {
$classLoader = require __DIR__.'/../vendor/autoload.php';
}

return $classLoader;
}
}

0 comments on commit 33a3a50

Please sign in to comment.