Skip to content

Commit 91059a4

Browse files
committed
Support scalar container variables for factory functions
1 parent 350cd67 commit 91059a4

3 files changed

Lines changed: 209 additions & 39 deletions

File tree

docs/best-practices/controllers.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ $container = new FrameworkX\Container([
285285
```
286286

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

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

297297
$container = new FrameworkX\Container([
298-
Acme\Todo\UserController::class => function (string $name, string $hostname) {
299-
// example UserController class requires two string arguments
300-
return new Acme\Todo\UserController($name, $hostname);
298+
Acme\Todo\UserController::class => function (bool $debug, string $hostname) {
299+
// example UserController class requires two scalar arguments
300+
return new Acme\Todo\UserController($debug, $hostname);
301301
},
302-
'name' => 'Acme',
302+
'debug' => false,
303303
'hostname' => fn(): string => gethostname()
304304
]);
305305

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

309309
> ℹ️ **Avoiding name conflicts**
310310
>
311-
> Note that class names and string variables share the same container
311+
> Note that class names and scalar variables share the same container
312312
> configuration map and as such might be subject to name collisions as a single
313313
> entry may only have a single value. For this reason, container variables will
314314
> only be used for container functions by default. We highly recommend using

src/Container.php

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
*/
1111
class Container
1212
{
13-
/** @var array<string,object|callable():(object|string)|string>|ContainerInterface */
13+
/** @var array<string,object|callable():(object|scalar)|scalar>|ContainerInterface */
1414
private $container;
1515

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

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

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

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

220222
assert($type instanceof \ReflectionNamedType);
221223

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

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

244-
/** @throws \BadMethodCallException if $name is not a valid string variable */
245-
private function loadVariable(string $name, int $depth): string
246+
/**
247+
* @return string|int|float|bool
248+
* @throws \BadMethodCallException if $name is not a valid string variable
249+
*/
250+
private function loadVariable(string $name, string $type, int $depth) /*: string|int|float|bool (PHP 8.0+) */
246251
{
247252
if (!isset($this->container[$name])) {
248253
throw new \BadMethodCallException('Container variable $' . $name . ' is not defined');
@@ -260,16 +265,20 @@ private function loadVariable(string $name, int $depth): string
260265
// invoke factory with list of parameters
261266
$value = $params === [] ? ($this->container[$name])() : ($this->container[$name])(...$params);
262267

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

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

270275
$value = $this->container[$name];
271-
if (!\is_string($value)) {
272-
throw new \BadMethodCallException('Container variable $' . $name . ' expected type string, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
276+
if (!\is_scalar($value)) {
277+
throw new \BadMethodCallException('Container variable $' . $name . ' expected scalar type, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value)));
278+
}
279+
280+
if (($type === 'string' && !\is_string($value)) || ($type === 'int' && !\is_int($value)) || ($type === 'float' && !\is_float($value)) || ($type === 'bool' && !\is_bool($value))) {
281+
throw new \BadMethodCallException('Container variable $' . $name . ' expected type ' . $type . ', but got ' . \gettype($value));
273282
}
274283

275284
return $value;

0 commit comments

Comments
 (0)