Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions src/Autowire.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
use ReflectionNamedType;

use function array_key_exists;
use function end;
use function key;

class Autowire extends Instantiator
{
Expand All @@ -21,41 +19,44 @@ public function setContainer(ContainerInterface $container): void
}

/** @inheritDoc */
protected function cleanupParams(array $params): array
protected function cleanupParams(array $params, bool $forConstructor = true): array
{
$constructor = $this->reflection()->getConstructor();
if ($constructor && $this->container) {
$container = $this->container;
if ($forConstructor && $constructor && $container) {
foreach ($constructor->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $this->params)) {
$value = $params[$name] ?? null;
if ($value instanceof Ref) {
$params[$name] = $this->container->get($value->id);
$params[$name] = $container->get($value->id);
} else {
$this->propagateContainer($value);
$params[$name] = $this->lazyLoad($value);
}

continue;
}

$type = $param->getType();
if (
!($type instanceof ReflectionNamedType) || $type->isBuiltin()
|| !$this->container->has($type->getName())
) {
continue;
} else {
$type = $param->getType();
if (
$type instanceof ReflectionNamedType && !$type->isBuiltin()
&& $container->has($type->getName())
) {
$params[$name] = $container->get($type->getName());
}
}

$params[$name] = $this->container->get($type->getName());
}

while (end($params) === null && ($key = key($params)) !== null) {
unset($params[$key]);
}

return $params;
return $this->stripTrailingNulls($params);
}

return parent::cleanupParams($params);
}

protected function propagateContainer(mixed $value): void
{
if (!$value instanceof self || $value->container !== null || $this->container === null) {
return;
}

$value->setContainer($this->container);
}
}
85 changes: 15 additions & 70 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@
use ArrayObject;
use Closure;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionNamedType;

use function array_filter;
use function array_map;
use function assert;
use function call_user_func;
use function class_exists;
use function func_get_args;
use function is_array;
use function is_callable;
use function is_string;

/** @extends ArrayObject<string, mixed> */
class Container extends ArrayObject implements ContainerInterface
Expand All @@ -39,7 +30,7 @@ public function has(string $id): bool
return false;
}

$entry = $this[$id];
$entry = parent::offsetGet($id);
if ($entry instanceof Instantiator) {
return class_exists($entry->getClassName());
}
Expand All @@ -49,12 +40,13 @@ public function has(string $id): bool

public function getItem(string $name, bool $raw = false): mixed
{
if (!isset($this[$name])) {
if (!parent::offsetExists($name)) {
throw new NotFoundException('Item ' . $name . ' not found');
}

if ($raw || !is_callable($this[$name])) {
return $this[$name];
$value = $this[$name];
if ($raw || !is_callable($value)) {
return $value;
}

try {
Expand Down Expand Up @@ -86,76 +78,27 @@ public function set(string $name, mixed $value): void
protected function lazyLoad(string $name): mixed
{
$callback = $this[$name];
if ($callback instanceof Instantiator && !$callback instanceof Factory) {
return $this[$name] = $callback();
if ($callback instanceof Instantiator) {
$result = $callback();
if ($callback->shouldCache()) {
$this[$name] = $result;
}

return $result;
}

if ($callback instanceof Closure) {
return $this[$name] = $callback($this);
}

return call_user_func($callback);
return $this[$name] = call_user_func($callback);
}

public function __isset(string $name): bool
{
return $this->has($name);
}

public function __invoke(mixed $spec): mixed
{
if (is_callable($spec)) {
if (is_array($spec)) {
[$class, $method] = $spec;
$class = new ReflectionClass($class);
$object = $class->newInstance();
$mirror = $class->getMethod($method);
} else {
$object = false;
assert($spec instanceof Closure || is_string($spec));
$mirror = new ReflectionFunction($spec);
}

$container = $this;
$arguments = array_map(
static function ($param) use ($container) {
$paramClass = $param->getType();
if ($paramClass instanceof ReflectionNamedType) {
return $container->getItem($paramClass->getName());
}

return null;
},
$mirror->getParameters(),
);
if ($object) {
return $mirror->invokeArgs($object, $arguments);
}

return $mirror->invokeArgs($arguments);
}

if ((bool) array_filter(func_get_args(), 'is_object')) {
foreach (func_get_args() as $dependency) {
$this[$dependency::class] = $dependency;
}
}

foreach ($spec as $name => $item) {
parent::offsetSet($name, $item);
}

return $this;
}

/** @param array<mixed> $dict */
public function __call(string $name, array $dict): mixed
{
$this->__invoke($dict[0]);

return $this->getItem($name);
}

public function __get(string $name): mixed
{
return $this->getItem($name);
Expand All @@ -164,6 +107,8 @@ public function __get(string $name): mixed
public function __set(string $name, mixed $value): void
{
if (isset($this[$name]) && $this[$name] instanceof Instantiator) {
// Update the Instantiator's cached instance so other Instantiators
// holding a reference to it resolve to the new value
$this[$name]->setInstance($value);
}

Expand Down
6 changes: 2 additions & 4 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@

class Factory extends Instantiator
{
public function getInstance(bool $forceNew = false): mixed
public function shouldCache(): bool
{
$this->instance = null;

return parent::getInstance(true);
return false;
}
}
53 changes: 33 additions & 20 deletions src/IniLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@
use Closure;
use InvalidArgumentException;

use function array_filter;
use function constant;
use function count;
use function current;
use function defined;
use function explode;
use function file_exists;
use function is_array;
use function is_object;
use function is_numeric;
use function is_string;
use function parse_ini_file;
use function parse_ini_string;
use function preg_match;
use function preg_replace;
use function preg_replace_callback;
use function str_contains;
use function strstr;
use function trim;

class IniLoader
Expand Down Expand Up @@ -82,14 +81,24 @@ public function fromFile(string $configurator): Container
/** @param array<string, mixed> $configurator */
public function fromArray(array $configurator): Container
{
foreach ($this->state() + $configurator as $key => $value) {
foreach ($configurator as $key => $value) {
$stringKey = (string) $key;

// State takes precedence: use existing non-Instantiator value instead
if (
$this->container->offsetExists($stringKey)
&& !$this->container[$stringKey] instanceof Instantiator
) {
$value = $this->container[$stringKey];
}

if ($value instanceof Closure) {
$this->container->offsetSet((string) $key, $value);
$this->container->offsetSet($stringKey, $value);
continue;
}

if ($value instanceof Instantiator) {
$this->container->offsetSet((string) $key, $value);
$this->container->offsetSet($stringKey, $value);
continue;
}

Expand All @@ -99,18 +108,11 @@ public function fromArray(array $configurator): Container
return $this->container;
}

/** @return array<string, mixed> */
protected function state(): array
protected function existingKeyFrom(string $key): string|false
{
return array_filter(
$this->container->getArrayCopy(),
static fn($v): bool => !is_object($v) || !$v instanceof Instantiator,
);
}
$k = strstr($key, ' ', true) ?: $key;

protected function keyHasStateInstance(string $key, mixed &$k): bool
{
return $this->container->offsetExists($k = current(explode(' ', $key)));
return $this->container->offsetExists($k) ? $k : false;
}

protected function keyHasInstantiator(string $key): bool
Expand All @@ -122,8 +124,9 @@ protected function parseItem(string|int $key, mixed $value): void
{
$key = trim((string) $key);
if ($this->keyHasInstantiator($key)) {
if ($this->keyHasStateInstance($key, $k)) {
$this->container->offsetSet($key, $this->container[$k]);
$existingKey = $this->existingKeyFrom($key);
if ($existingKey !== false) {
$this->container->offsetSet($key, $this->container[$existingKey]);
} else {
$this->parseInstantiator($key, $value);
}
Expand All @@ -146,7 +149,7 @@ protected function parseSubValues(array &$value): array
return $value;
}

protected function parseStandardItem(string $key, mixed &$value): void
protected function parseStandardItem(string $key, mixed $value): void
{
if (is_array($value)) {
$this->parseSubValues($value);
Expand All @@ -159,6 +162,10 @@ protected function parseStandardItem(string $key, mixed &$value): void

protected function removeDuplicatedSpaces(string $string): string
{
if (!str_contains($string, ' ')) {
return $string;
}

return (string) preg_replace('/\s+/', ' ', $string);
}

Expand Down Expand Up @@ -211,7 +218,7 @@ protected function parseValue(mixed $value): mixed
return $this->parseSubValues($value);
}

if (empty($value)) {
if ($value === '' || $value === null) {
return null;
}

Expand Down Expand Up @@ -243,6 +250,12 @@ protected function parseConstants(string $value): mixed
return constant($value);
}

if (is_numeric($value)) {
$isFloat = str_contains($value, '.') || str_contains($value, 'e') || str_contains($value, 'E');

return $isFloat ? (float) $value : (int) $value;
}

return $value;
}

Expand Down
Loading
Loading