Skip to content

Commit

Permalink
Merge pull request #12 from cruxinator/composerUpdates
Browse files Browse the repository at this point in the history
Composer updates
  • Loading branch information
c-harris committed Sep 26, 2023
2 parents 0705339 + 9e6ac9a commit 1539439
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 86 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
- name: Fetch Current Releases
uses: ./.github/actions/php_ver
id: releases
with:
releases: 7.4
tests:
needs: output_releases
runs-on: ${{ matrix.operating-system }}
Expand Down Expand Up @@ -74,11 +76,13 @@ jobs:
- name: Install lowest dependencies
if: ${{ matrix.dependencies == 'lowest' }}
run: composer update --no-interaction --prefer-dist --no-progress --prefer-lowest

- name: migrate if required
continue-on-error: true
run: vendor/bin/phpunit --migrate-configuration
- name: Run unit tests
shell: bash
run: |
vendor/bin/phpunit --coverage-clover=clover.xml --path-coverage
vendor/bin/phpunit --coverage-clover=clover.xml
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
Expand Down
9 changes: 4 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
"description": "locates defined classes within the autoloader",
"type": "library",
"require": {
"php": "^7.1"
"php": "^7.4|^8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0|^7.0|^8.0",
"composer/composer": "^1.0",
"php-coveralls/php-coveralls": "^2.2",
"infection/infection": "^0.12|^0.13|^0.14|^0.15"
"phpunit/phpunit": "^9.6.13|10.0",
"composer/composer": "^2.0.0",
"psr/log": "^1.1.4"
},
"config": {
"optimize-autoloader": true,
Expand Down
37 changes: 12 additions & 25 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnError="false"
verbose="true"
>

<testsuites>
<testsuite name="tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>
</whitelist>
</filter>

</phpunit>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="./vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" stopOnError="false" verbose="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
75 changes: 48 additions & 27 deletions src/ClassFinder.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Cruxinator\ClassFinder;

use Composer\Autoload\ClassLoader;
Expand All @@ -11,8 +12,6 @@
* Class ClassFinder.
*
* Functionality similar to get_declared_classes(), with autoload support.
*
* @package Cruxinator\ClassFinder
*/
abstract class ClassFinder
{
Expand All @@ -36,8 +35,10 @@ abstract class ClassFinder
/**
* Explicitly loads a namespace before returning declared classes.
*
* @param string $namespace the namespace to load
* @param string $namespace the namespace to load
*
* @throws Exception
*
* @return array|string[] an array with the name of the defined classes
*/
private static function getProjectClasses(string $namespace): array
Expand All @@ -49,37 +50,45 @@ private static function getProjectClasses(string $namespace): array
self::strStartsWith($namespace, $className) && class_exists($className, true);
}, $namespace);
}

return get_declared_classes();
}

/**
* Attempts to get an optimised ClassMap failing that attempts to generate one for the namespace.
*
* @param string $namespace the namespace to generate for if necessary
* @param string $namespace the namespace to generate for if necessary
*
* @throws Exception
*
* @return null|array|string[] the class map, keyed by Classname values of files
*/
private static function getClassMap(string $namespace): array
{
self::checkState();

return null !== (self::$optimisedClassMap) ?
self::$optimisedClassMap :
array_reduce(self::getProjectSearchDirs($namespace),
array_reduce(
self::getProjectSearchDirs($namespace),
function ($map, $dir) {
// Use composer's ClassMapGenerator to pull the class list out of each project search directory
return array_merge($map, ClassMapGenerator::createMap($dir));
}, self::getComposerAutoloader()->getClassMap());
},
self::getComposerAutoloader()->getClassMap()
);
}

/**
* Checks if a string starts with another string.
* Simple Helper for readability.
*
* @param string $needle the string to check
* @param string $haystack the input string
* @return bool true if haystack starts with needle, false otherwise
* @param string $needle the string to check
* @param string $haystack the input string
*
* @return bool true if haystack starts with needle, false otherwise
*/
private static function strStartsWith(string $needle, string $haystack):bool
private static function strStartsWith(string $needle, string $haystack): bool
{
return substr($haystack, 0, strlen($needle)) === $needle;
}
Expand All @@ -88,34 +97,36 @@ private static function strStartsWith(string $needle, string $haystack):bool
* Gets the base vendor Directory.
*
* @throws ReflectionException
* @return string the vase Vendor Director
*
* @return string the vase Vendor Director
*/
private static function getVendorDir(): string
{
return empty(self::$vendorDir) ?
self::$vendorDir = dirname((new ReflectionClass(ClassLoader::class))->getFileName(), 2) :
self::$vendorDir ;
self::$vendorDir;
}

/**
* Checks the state requirements (package and autoloader).
*
* @throws Exception thrown when a combination of components is not available
*/
private static function checkState() : void
private static function checkState(): void
{
self::initClassMap();
if (null === self::$optimisedClassMap && !class_exists(ClassMapGenerator::class)) {
throw new Exception('Cruxinator/ClassFinder requires either composer/composer' .
throw new Exception('Cruxinator/ClassFinder requires either composer/composer'.
' or an optimised autoloader(`composer dump-autoload -o`)');
}
}

/**
* Initializes the optimised class map, if possible.
*
* @throws ReflectionException
*/
private static function initClassMap() :void
private static function initClassMap(): void
{
if (true === self::$classLoaderInit) {
return;
Expand All @@ -130,23 +141,26 @@ private static function initClassMap() :void
* Gets the Composer Class Loader.
*
* @throws ReflectionException
*
* @return ClassLoader
*/
private static function getComposerAutoloader(): ClassLoader
{
return include self::getVendorDir() . DIRECTORY_SEPARATOR . 'autoload.php';
return include self::getVendorDir().DIRECTORY_SEPARATOR.'autoload.php';
}

/**
* Gets a list of classes defined in the autoloader. get_declared_classes().
*
* @param string $namespace namespace prefix to restrict the list (must be configured psr4 namespace
* @param callable|null $conditional callable method of signature `conditional(string $className) : bool` to check to include
* @param bool $includeVendor whether classes in the vendor directory should be considered
* @param string $namespace namespace prefix to restrict the list (must be configured psr4 namespace
* @param callable|null $conditional callable method of signature `conditional(string $className) : bool` to check to include
* @param bool $includeVendor whether classes in the vendor directory should be considered
*
* @throws Exception
* @return array the list of classes
*
* @return array the list of classes
*/
public static function getClasses(string $namespace = '', callable $conditional = null, bool $includeVendor = true):array
public static function getClasses(string $namespace = '', callable $conditional = null, bool $includeVendor = true): array
{
$conditional = $conditional ?: 'is_string';
$classes = array_filter(self::getProjectClasses($namespace), function (string $class) use (
Expand All @@ -165,36 +179,43 @@ public static function getClasses(string $namespace = '', callable $conditional
/**
* Gets the Directories associated with a given namespace.
*
* @param string $namespace the namespace (without preceding \)
* @param string $namespace the namespace (without preceding \)
*
* @throws ReflectionException
* @return array a list of directories containing classes for that namespace
*
* @return array a list of directories containing classes for that namespace
*/
private static function getProjectSearchDirs(string $namespace): array
{
$raw = self::getComposerAutoloader()->getPrefixesPsr4();

return self::findCompatibleNamespace($namespace, $raw);
}

private static function findCompatibleNamespace(string $namespace, array $psr4): array
{
$namespaceParts = explode('\\', $namespace);
while (!array_key_exists($namespace, $psr4) && count($namespaceParts) !== 0) {
$namespace = implode('\\', $namespaceParts) . '\\';
$namespace = implode('\\', $namespaceParts).'\\';
array_pop($namespaceParts);
}

return array_key_exists($namespace, $psr4) ? $psr4[$namespace] : array_values($psr4);
}

/**
* Identify if the class is in the vendor directory.
*
* @param string $className the class to test
* @param string $className the class to test
*
* @throws ReflectionException
* @return bool true if in vendor otherwise false
*
* @return bool true if in vendor otherwise false
*/
private static function isClassInVendor(string $className) : bool
private static function isClassInVendor(string $className): bool
{
$filename = (new ReflectionClass($className))->getFileName();

return self::strStartsWith(self::getVendorDir(), $filename);
}
}
36 changes: 24 additions & 12 deletions tests/ClassFinderConcrete.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?php


namespace Cruxinator\ClassFinder\Tests;

use Composer\Autoload\ClassLoader;
Expand All @@ -10,22 +9,23 @@

/**
* Class ClassFinderConcrete.
*
* @property bool classLoaderInit
* @property null|array optimisedClassMap
* @property array loadedNamespaces
* @property string vendorDir
* @package Tests\Cruxinator\ClassFinder
* @method array getProjectClasses(string $namespace)
* @method array getClassMap(string $namespace)
* @method bool strStartsWith($needle, $haystack)
* @method void checkState()
* @method void initClassMap()
* @method array getClasses(string $namespace = '',callable $conditional = null, bool $includeVendor = true)
* @method array getProjectSearchDirs(string $namespace)
* @method bool isClassInVendor(string $className)
*
* @method array getProjectClasses(string $namespace)
* @method array getClassMap(string $namespace)
* @method bool strStartsWith($needle, $haystack)
* @method void checkState()
* @method void initClassMap()
* @method array getClasses(string $namespace = '',callable $conditional = null, bool $includeVendor = true)
* @method array getProjectSearchDirs(string $namespace)
* @method bool isClassInVendor(string $className)
* @method ClassLoader getComposerAutoloader()
* @method string getVendorDir()
* @method array findCompatibleNamespace(string $namespace, array $psr4)
* @method string getVendorDir()
* @method array findCompatibleNamespace(string $namespace, array $psr4)
*/
class ClassFinderConcrete extends ClassFinder
{
Expand All @@ -39,44 +39,54 @@ public function __construct()

/**
* @param $name
*
* @throws \ReflectionException
*
* @return \ReflectionMethod
*/
protected static function getMethod($name)
{
$class = new ReflectionClass(self::class);
$method = $class->getMethod($name);
$method->setAccessible(true);

return $method;
}

/**
* @param $name
*
* @throws \ReflectionException
*
* @return ReflectionProperty
*/
protected static function getProperty($name)
{
$reflectionProperty = new ReflectionProperty(parent::class, $name);
$reflectionProperty->setAccessible(true);

return $reflectionProperty;
}

/**
* @param $name
* @param $arguments
*
* @throws \ReflectionException
*
* @return mixed
*/
public function __call($name, $arguments)
{
$method = self::getMethod($name);

return $method->invokeArgs(null, $arguments);
}

/**
* @param $name
* @param $value
*
* @throws \ReflectionException
*/
public function __set($name, $value)
Expand All @@ -87,11 +97,13 @@ public function __set($name, $value)

/**
* @param $name
*
* @throws \ReflectionException
*/
public function __get($name)
{
$property = self::getProperty($name);

return $property->getValue();
}
}

0 comments on commit 1539439

Please sign in to comment.