Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3766714
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 24, 19:41 (3 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2257638
Default Alt Text
(11 KB)
Attached To
Mode
rKERUALD Keruald libraries development repository
Attached
Detach File
Event Timeline
Log In to Comment