mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Add basic container implementation
This commit is contained in:
@ -82,6 +82,9 @@
|
|||||||
"providers": [
|
"providers": [
|
||||||
"Pest\\Laravel\\PestServiceProvider"
|
"Pest\\Laravel\\PestServiceProvider"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "dev-feature/add-container"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/Support/Container.php
Normal file
82
src/Support/Container.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Support;
|
||||||
|
|
||||||
|
use Pest\Exceptions\ShouldNotHappen;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionParameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class Container
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
private $instances = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a dependency from the container.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function get(string $id)
|
||||||
|
{
|
||||||
|
if (array_key_exists($id, $this->instances)) {
|
||||||
|
return $this->instances[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->instances[$id] = $this->build($id);
|
||||||
|
|
||||||
|
return $this->instances[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given instance to the container.
|
||||||
|
*
|
||||||
|
* @param mixed $instance
|
||||||
|
*/
|
||||||
|
public function add(string $id, $instance): void
|
||||||
|
{
|
||||||
|
$this->instances[$id] = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to build the given instance.
|
||||||
|
*/
|
||||||
|
private function build(string $id): object
|
||||||
|
{
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
$reflectionClass = new ReflectionClass($id);
|
||||||
|
|
||||||
|
if ($reflectionClass->isInstantiable()) {
|
||||||
|
$constructor = $reflectionClass->getConstructor();
|
||||||
|
|
||||||
|
if ($constructor !== null) {
|
||||||
|
$params = array_map(
|
||||||
|
function (ReflectionParameter $param) use ($id) {
|
||||||
|
$candidate = null;
|
||||||
|
|
||||||
|
if ($param->getType() !== null && $param->getType()->isBuiltin()) {
|
||||||
|
$candidate = $param->getName();
|
||||||
|
} elseif ($param->getClass() !== null) {
|
||||||
|
$candidate = $param->getClass()->getName();
|
||||||
|
} else {
|
||||||
|
throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get($candidate);
|
||||||
|
},
|
||||||
|
$constructor->getParameters()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $reflectionClass->newInstanceArgs($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ShouldNotHappen::fromMessage(sprintf('A dependency with the name `%s` cannot be resolved.', $id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -114,6 +114,15 @@
|
|||||||
PASS Tests\Unit\Support\Backtrace
|
PASS Tests\Unit\Support\Backtrace
|
||||||
✓ it gets file name from called file
|
✓ it gets file name from called file
|
||||||
|
|
||||||
|
PASS Tests\Unit\Support\Container
|
||||||
|
✓ it exists
|
||||||
|
✓ it gets an instance
|
||||||
|
✓ it creates an instance and resolves parameters
|
||||||
|
✓ it creates an instance and resolves also sub parameters
|
||||||
|
✓ it can resolve builtin value types
|
||||||
|
✓ it cannot resolve a parameter that requires additional dependencies
|
||||||
|
✓ it cannot resolve a parameter without type
|
||||||
|
|
||||||
PASS Tests\Unit\Support\Reflection
|
PASS Tests\Unit\Support\Reflection
|
||||||
✓ it gets file name from closure
|
✓ it gets file name from closure
|
||||||
✓ it gets property values
|
✓ it gets property values
|
||||||
@ -130,5 +139,5 @@
|
|||||||
WARN Tests\Visual\Success
|
WARN Tests\Visual\Success
|
||||||
s visual snapshot of test suite on success
|
s visual snapshot of test suite on success
|
||||||
|
|
||||||
Tests: 6 skipped, 69 passed
|
Tests: 6 skipped, 76 passed
|
||||||
Time: 2.34s
|
Time: 3.32s
|
||||||
|
|||||||
71
tests/Unit/Support/Container.php
Normal file
71
tests/Unit/Support/Container.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Pest\Exceptions\ShouldNotHappen;
|
||||||
|
use Pest\Support\Container;
|
||||||
|
use Pest\TestSuite;
|
||||||
|
|
||||||
|
uses()->group('container');
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->container = new Container();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exists')
|
||||||
|
->assertTrue(class_exists(Container::class));
|
||||||
|
|
||||||
|
it('gets an instance', function () {
|
||||||
|
$this->container->add(Container::class, $this->container);
|
||||||
|
assertSame($this->container, $this->container->get(Container::class));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates an instance and resolves parameters', function () {
|
||||||
|
$this->container->add(Container::class, $this->container);
|
||||||
|
$instance = $this->container->get(ClassWithDependency::class);
|
||||||
|
|
||||||
|
assertInstanceOf(ClassWithDependency::class, $instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates an instance and resolves also sub parameters', function () {
|
||||||
|
$this->container->add(Container::class, $this->container);
|
||||||
|
$instance = $this->container->get(ClassWithSubDependency::class);
|
||||||
|
|
||||||
|
assertInstanceOf(ClassWithSubDependency::class, $instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can resolve builtin value types', function () {
|
||||||
|
$this->container->add('rootPath', getcwd());
|
||||||
|
|
||||||
|
$instance = $this->container->get(TestSuite::class);
|
||||||
|
assertInstanceOf(TestSuite::class, $instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot resolve a parameter that requires additional dependencies', function () {
|
||||||
|
$this->expectException(ShouldNotHappen::class);
|
||||||
|
$this->container->get(ClassWithDependency::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot resolve a parameter without type', function () {
|
||||||
|
$this->expectException(ShouldNotHappen::class);
|
||||||
|
$this->container->get(ClassWithoutTypeParameter::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
class ClassWithDependency
|
||||||
|
{
|
||||||
|
public function __construct(Container $container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassWithSubDependency
|
||||||
|
{
|
||||||
|
public function __construct(ClassWithDependency $param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassWithoutTypeParameter
|
||||||
|
{
|
||||||
|
public function __construct($param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user