Page MenuHomeDevCentral

No OneTemporary

diff --git a/composer.json b/composer.json
index 6449de0..d6228d5 100644
--- a/composer.json
+++ b/composer.json
@@ -1,68 +1,68 @@
{
"name": "keruald/keruald",
"type": "library",
"description": "Modular libraries to build frameworks and applications",
"keywords": [
"framework",
"keruald"
],
"license": "BSD-2-Clause",
"homepage": "https://keruald.nasqueron.org",
"authors": [
{
"name": "Sébastien Santoro",
"email": "dereckson@espace-win.org"
},
{
"name": "Keruald contributors"
}
],
"provide": {
"psr/simple-cache-implementation": "1.0|2.0|3.0"
},
"require": {
"psr/simple-cache": "^1.0|^2.0|^3.0",
"ext-intl": "*"
},
"require-dev": {
"ext-mbstring": "*",
"ext-mysqli": "*",
"ext-xmlwriter": "*",
"nasqueron/codestyle": "^0.0.1",
"phan/phan": "^5.3.1",
"phpunit/phpunit": "^10.2",
"symfony/yaml": "^6.0.3",
"squizlabs/php_codesniffer": "^3.6"
},
"suggest": {
"ext-memcached": "*",
"ext-redis": "*"
},
"replace": {
"keruald/cache": "0.1.0",
"keruald/commands": "0.0.1",
"keruald/database": "0.4.0",
- "keruald/omnitools": "0.11.0",
+ "keruald/omnitools": "0.11.1",
"keruald/report": "0.1.0"
},
"autoload": {
"psr-4": {
"Keruald\\Cache\\": "cache/src/",
"Keruald\\Cache\\Tests\\": "cache/tests/",
"Keruald\\Commands\\": "commands/src/",
"Keruald\\Commands\\Tests\\": "commands/tests/",
"Keruald\\Database\\": "database/src/",
"Keruald\\Database\\Tests\\": "database/tests/",
"Keruald\\OmniTools\\": "omnitools/src/",
"Keruald\\OmniTools\\Tests\\": "omnitools/tests/",
"Keruald\\Reporting\\": "report/src/",
"Keruald\\Reporting\\Tests\\": "report/tests/"
}
},
"scripts": {
"lint-src": "find */src -type f -name '*.php' | xargs -n1 php -l",
"lint-tests": "find */tests -type f -name '*.php' | xargs -n1 php -l",
"test": "vendor/bin/phpunit"
},
"minimum-stability": "dev"
}
diff --git a/omnitools/src/Reflection/CodeClass.php b/omnitools/src/Reflection/CodeClass.php
index d827e76..077fe0d 100644
--- a/omnitools/src/Reflection/CodeClass.php
+++ b/omnitools/src/Reflection/CodeClass.php
@@ -1,111 +1,132 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Reflection;
use Keruald\OmniTools\Collections\Vector;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
class CodeClass {
///
/// CodeClass constructor
///
public function __construct (
private readonly string $className,
) {
}
public static function from (object $object) : self {
return new self($object::class);
}
///
/// Class name helper methods
///
public function getClassName () : string {
return $this->className;
}
/**
* @throws ReflectionException
*/
public function getShortClassName () : string {
$class = new ReflectionClass($this->className);
return $class->getShortName();
}
+ public static function extractClassName (string $fullClassName) : string {
+ // Trivial cases - no classes
+ if ($fullClassName === "" || $fullClassName[-1] === '\\') {
+ return "";
+ }
+
+ // When class exists, we can use ReflectionClass
+ try {
+ $class = new self($fullClassName);
+ return $class->getShortClassName();
+ } catch (ReflectionException) {
+ }
+
+ // Class doesn't exist in code, we can compute it
+ $pos = strrpos($fullClassName, "\\");
+ return match ($pos) {
+ false => $fullClassName,
+ default => substr($fullClassName, $pos + 1),
+ };
+ }
+
///
/// Represented class constructor helper methods
///
/**
* @throws ReflectionException
* @throws InvalidArgumentException
*/
public function getConstructor () : ReflectionMethod {
$class = new ReflectionClass($this->className);
$constructor = $class->getConstructor();
return match ($constructor) {
null => throw new InvalidArgumentException(
"This class doesn't have a constructor."
),
default => $constructor,
};
}
/**
* @throws ReflectionException
*/
public function getConstructorArgumentsTypes () : Vector {
$class = new ReflectionClass($this->className);
$constructor = $class->getConstructor();
if ($constructor === null) {
return new Vector;
}
return CodeMethod::fromReflectionMethod($constructor)
->getArgumentsType();
}
/**
*
* This method can be used for dependency injection to build a class,
* like a controller in a MVC model, from a services' container.
*
* Each argument of the constructor is substituted by an item from
* $services. To match properly services and constructor arguments,
* each arguments need to have a type, and those types should properly
* exist in $services, without duplicate.
*
* @param iterable $services a collection with keys as type names and values
* @return object A new instance of the reflected class
* @throws ReflectionException
*/
public function newInstanceFromServices (iterable $services) : object {
$args = $this->getConstructorArgumentsTypes()
->map(function (string $type) use ($services) : mixed {
foreach ($services as $value) {
if (CodeVariable::from($value)->hasType($type)) {
return $value;
}
}
throw new InvalidArgumentException("No instance of type $type can be found.");
});
$class = new ReflectionClass($this->className);
return $class->newInstance(...$args);
}
}
diff --git a/omnitools/tests/Reflection/CodeClassTest.php b/omnitools/tests/Reflection/CodeClassTest.php
index 00fb36d..bf71024 100644
--- a/omnitools/tests/Reflection/CodeClassTest.php
+++ b/omnitools/tests/Reflection/CodeClassTest.php
@@ -1,129 +1,151 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\Reflection;
use Keruald\OmniTools\Collections\ArrayUtilities;
use Keruald\OmniTools\Collections\HashMap;
use Keruald\OmniTools\Collections\Vector;
use Keruald\OmniTools\DateTime\DateStamp;
use Keruald\OmniTools\HTTP\Requests\Request;
use Keruald\OmniTools\Network\IPv4Range;
use Keruald\OmniTools\Reflection\CodeClass;
+use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use InvalidArgumentException;
class CodeClassTest extends TestCase {
private CodeClass $class;
protected function setUp () : void {
$this->class = new CodeClass(AcmeApplication::class);
}
public function testFrom () {
$date = new DateStamp(2010, 11, 25);
$class = CodeClass::from($date);
$this->assertInstanceOf(CodeClass::class, $class);
$this->assertEquals(DateStamp::class, $class->getClassName());
}
public function testGetClassName () {
$this->assertEquals(
AcmeApplication::class,
$this->class->getClassName(),
);
}
public function testGetShortClassName () {
$this->assertEquals(
"AcmeApplication",
$this->class->getShortClassName(),
);
}
+ public static function provideFullyQualifiedClassNames () : iterable {
+ // Example from PSR-4 canonical document
+ // Fully qualified class name, class
+ yield [CodeClass::class, 'CodeClass'];
+ yield ['\Acme\Log\Writer\File_Writer', 'File_Writer'];
+ yield ['\Aura\Web\Response\Status', 'Status'];
+ yield ['\Symfony\Core\Request', 'Request'];
+
+ yield ['Foo', 'Foo'];
+ yield ['', ''];
+ yield ["Foo\\", ''];
+
+ }
+
+ #[DataProvider("provideFullyQualifiedClassNames")]
+ public function testExtractClassName ($class, $expected) {
+ $actual = CodeClass::extractClassName($class);
+
+ $this->assertEquals($expected, $actual);
+ }
+
public function testNewInstanceFromServices () {
$services = [
// Some objects (in different order than the constructor)
"request" => new Request(),
"date" => DateStamp::fromUnixTime(), // another name than in class
"session" => new HashMap(),
// Scalar values
"counter" => 666,
"isSecure" => false,
"temperature" => 26.6,
"inventory" => [],
// An object not needed by the controller
"ip_range" => IPv4Range::from("127.0.0.1/32"),
];
$app = $this->class->newInstanceFromServices($services);
$this->assertInstanceOf(AcmeApplication::class, $app);
$this->assertEquals($services["date"], $app->getDateStamp());
}
public function testNewInstanceFromServicesWithMissingService () {
$incompleteServices = [
"foo",
];
$this->expectException(InvalidArgumentException::class);
$this->class->newInstanceFromServices($incompleteServices);
}
public function testNewInstanceFromServicesWithoutConstructor () {
$services = [
"foo",
];
$class = new CodeClass(ArrayUtilities::class); // No constructor
$utilities = $class->newInstanceFromServices($services);
$this->assertInstanceOf(ArrayUtilities::class, $utilities);
}
public function testGetConstructorArgumentsTypes () {
$expected = Vector::from([
Request::class,
HashMap::class,
DateStamp::class,
"int",
"array",
"float",
"bool",
]);
$actual = $this->class->getConstructorArgumentsTypes();
$this->assertEquals($expected, $actual);
}
public function testGetConstructorArgumentsTypesWhenNotExisting () {
$class = new CodeClass(ArrayUtilities::class); // No constructor
$this->assertEquals(new Vector, $class->getConstructorArgumentsTypes());
}
public function testGetConstructor () {
$constructor = $this->class->getConstructor();
$this->assertEquals("__construct", $constructor->getName());
$this->assertEquals(
AcmeApplication::class,
$constructor->getDeclaringClass()->getName()
);
}
public function testGetConstructorWhenNotExisting () {
$this->expectException(InvalidArgumentException::class);
$class = new CodeClass(ArrayUtilities::class); // No constructor
$class->getConstructor();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 19:41 (6 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2257638
Default Alt Text
(11 KB)

Event Timeline