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
14 changes: 7 additions & 7 deletions docs/best-practices/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,8 @@ $container = new FrameworkX\Container([
```

Factory functions used in the container configuration map may also reference
string variables defined in the container configuration. You may also use
factory functions that return string variables. This can be particularly useful
scalar variables defined in the container configuration. You may also use
factory functions that return scalar variables. This can be particularly useful
when combining autowiring with some manual configuration like this:

```php title="public/index.php"
Expand All @@ -295,11 +295,11 @@ when combining autowiring with some manual configuration like this:
require __DIR__ . '/../vendor/autoload.php';

$container = new FrameworkX\Container([
Acme\Todo\UserController::class => function (string $name, string $hostname) {
// example UserController class requires two string arguments
return new Acme\Todo\UserController($name, $hostname);
Acme\Todo\UserController::class => function (bool $debug, string $hostname) {
// example UserController class requires two scalar arguments
return new Acme\Todo\UserController($debug, $hostname);
},
'name' => 'Acme',
'debug' => false,
'hostname' => fn(): string => gethostname()
]);

Expand All @@ -308,7 +308,7 @@ $container = new FrameworkX\Container([

> ℹ️ **Avoiding name conflicts**
>
> Note that class names and string variables share the same container
> Note that class names and scalar variables share the same container
> configuration map and as such might be subject to name collisions as a single
> entry may only have a single value. For this reason, container variables will
> only be used for container functions by default. We highly recommend using
Expand Down
35 changes: 22 additions & 13 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
*/
class Container
{
/** @var array<string,object|callable():(object|string)|string>|ContainerInterface */
/** @var array<string,object|callable():(object|scalar)|scalar>|ContainerInterface */
private $container;

/** @var array<string,callable():(object|string) | object | string>|ContainerInterface $loader */
/** @var array<string,callable():(object|scalar) | object | scalar>|ContainerInterface $loader */
public function __construct($loader = [])
{
if (!\is_array($loader) && !$loader instanceof ContainerInterface) {
Expand All @@ -23,7 +23,7 @@ public function __construct($loader = [])
}

foreach (($loader instanceof ContainerInterface ? [] : $loader) as $name => $value) {
if (!\is_string($value) && !$value instanceof \Closure && !$value instanceof $name) {
if (!\is_scalar($value) && !$value instanceof \Closure && !$value instanceof $name) {
throw new \BadMethodCallException('Map for ' . $name . ' contains unexpected ' . (is_object($value) ? get_class($value) : gettype($value)));
}
}
Expand Down Expand Up @@ -154,6 +154,8 @@ private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+)
}

$this->container[$name] = $value;
} elseif (\is_scalar($this->container[$name])) {
throw new \BadMethodCallException('Map for ' . $name . ' contains unexpected ' . \gettype($this->container[$name]));
}

assert($this->container[$name] instanceof $name);
Expand Down Expand Up @@ -219,13 +221,13 @@ private function loadFunctionParams(\ReflectionFunctionAbstract $function, int $

assert($type instanceof \ReflectionNamedType);

// load string variables from container
if ($allowVariables && $type->getName() === 'string') {
$params[] = $this->loadVariable($parameter->getName(), $depth);
// load variables from container for primitive/scalar types
if ($allowVariables && \in_array($type->getName(), ['string', 'int', 'float', 'bool'])) {
$params[] = $this->loadVariable($parameter->getName(), $type->getName(), $depth);
continue;
}

// abort for other primitive types
// abort for other primitive types (array etc.)
if ($type->isBuiltin()) {
throw new \BadMethodCallException(self::parameterError($parameter) . ' expects unsupported type ' . $type->getName());
}
Expand All @@ -241,8 +243,11 @@ private function loadFunctionParams(\ReflectionFunctionAbstract $function, int $
return $params;
}

/** @throws \BadMethodCallException if $name is not a valid string variable */
private function loadVariable(string $name, int $depth): string
/**
* @return string|int|float|bool
* @throws \BadMethodCallException if $name is not a valid scalar variable
*/
private function loadVariable(string $name, string $type, int $depth) /*: string|int|float|bool (PHP 8.0+) */
{
if (!isset($this->container[$name])) {
throw new \BadMethodCallException('Container variable $' . $name . ' is not defined');
Expand All @@ -260,16 +265,20 @@ private function loadVariable(string $name, int $depth): string
// invoke factory with list of parameters
$value = $params === [] ? ($this->container[$name])() : ($this->container[$name])(...$params);

if (!\is_string($value)) {
throw new \BadMethodCallException('Container variable $' . $name . ' expected type string from factory, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
if (!\is_scalar($value)) {
throw new \BadMethodCallException('Container variable $' . $name . ' expected scalar type from factory, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
}

$this->container[$name] = $value;
}

$value = $this->container[$name];
if (!\is_string($value)) {
throw new \BadMethodCallException('Container variable $' . $name . ' expected type string, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
if (!\is_scalar($value)) {
throw new \BadMethodCallException('Container variable $' . $name . ' expected scalar type, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
}

if (($type === 'string' && !\is_string($value)) || ($type === 'int' && !\is_int($value)) || ($type === 'float' && !\is_float($value)) || ($type === 'bool' && !\is_bool($value))) {
throw new \BadMethodCallException('Container variable $' . $name . ' expected type ' . $type . ', but got ' . \gettype($value));
}

return $value;
Expand Down
Loading