Skip to content

Commit

Permalink
Added support for resolving intersection types to the dependency inje…
Browse files Browse the repository at this point in the history
…ction container
  • Loading branch information
freost committed Oct 10, 2024
1 parent 40dfb84 commit a03bd53
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The major version bump is due to upping the required PHP version from `8.1` to `
* It is now possible to customize how JSON request bodies are decoded using the following methods:
- `Body::setJsonMaxDepth()` to set the maximum JSON nesting level.
- `Body::setJsonFlags()` to set the JSON decoding options.
* Added support for resolving intersection types to the dependency injection container.
* Added `Deprecated` middleware that allows you to easily set the `Deprecation` and `Sunset` HTTP headers.
* Global constraints and middleware will now be listed and sorted by priority when using the `app:routes` command.

Expand Down
60 changes: 36 additions & 24 deletions src/mako/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,47 @@
* @license http://www.makoframework.com/license
*/

namespace mako;
namespace mako {
use function filter_var;
use function getenv;
use function json_encode;
use function substr;

/**
* Builds and returns a "function" used for middleware, route constraints and validation rules.
*/
function f(string $_name, mixed ...$_arguments): string
{
if (empty($_arguments)) {
return $_name;
}

return "{$_name}(" . substr(json_encode($_arguments), 1, -1) . ')';
}

use function filter_var;
use function getenv;
use function json_encode;
use function substr;
/**
* Returns the value of the chosen environment variable or NULL if it does not exist.
*/
function env(string $variableName, mixed $default = null, bool $isBool = false, bool $localOnly = false): mixed
{
$value = $_ENV[$variableName] ?? (getenv($variableName, $localOnly) ?: null);

/**
* Builds and returns a "function" used for middleware, route constraints and validation rules.
*/
function f(string $_name, mixed ...$_arguments): string
{
if (empty($_arguments)) {
return $_name;
}
if ($isBool && $value !== true && $value !== false && $value !== null) {
$value = filter_var($value, FILTER_VALIDATE_BOOL);
}

return "{$_name}(" . substr(json_encode($_arguments), 1, -1) . ')';
return $value ?? $default;
}
}

/**
* Returns the value of the chosen environment variable or NULL if it does not exist.
*/
function env(string $variableName, mixed $default = null, bool $isBool = false, bool $localOnly = false): mixed
{
$value = $_ENV[$variableName] ?? (getenv($variableName, $localOnly) ?: null);
namespace mako\syringe {
use function implode;

if ($isBool && $value !== true && $value !== false && $value !== null) {
$value = filter_var($value, FILTER_VALIDATE_BOOL);
/**
* Returns the string representation of the intersection of the provided types.
*/
function intersection(string ...$types): string
{
return implode('&', $types);
}

return $value ?? $default;
}
14 changes: 13 additions & 1 deletion src/mako/syringe/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use mako\syringe\traits\ContainerAwareTrait;
use ReflectionClass;
use ReflectionFunction;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;
Expand Down Expand Up @@ -251,7 +252,18 @@ protected function resolveParameter(ReflectionParameter $parameter, ?ReflectionC

// If the parameter should be a class instance then we'll try to resolve it using the container

$parameterClassName = ($parameterType instanceof ReflectionNamedType && !$parameterType->isBuiltin()) ? $parameterType->getName() : null;
$parameterClassName = null;

if ($parameterType instanceof ReflectionNamedType && !$parameterType->isBuiltin()) {
$parameterClassName = $parameterType->getName();
}
elseif ($parameterType instanceof ReflectionIntersectionType) {
$parameterClassName = (string) $parameterType;

if (!$this->has($parameterClassName)) {
$parameterClassName = null;
}
}

if ($parameterClassName !== null) {
if ($class !== null) {
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use function mako\env;
use function mako\f;
use function mako\syringe\intersection;

/**
* @group unit
Expand Down Expand Up @@ -82,4 +83,12 @@ public function testEnvWithBooleanValues(): void
$this->assertTrue(env('MAKO_TRUE', isBool: true));
$this->assertFalse(env('MAKO_FALSE', isBool: true));
}

/**
*
*/
public function testIntersection(): void
{
$this->assertSame('foo&bar&baz', intersection('foo', 'bar', 'baz'));
}
}
53 changes: 53 additions & 0 deletions tests/unit/syringe/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use mako\tests\TestCase;
use stdClass;

use function mako\syringe\intersection;

// --------------------------------------------------------------------------
// START CLASSES
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -175,6 +177,29 @@ public function __construct(
}
}

interface IA
{

}

interface IB
{

}

class AB implements IA, IB
{

}

class Intersection
{
public function __construct(
public IA&IB $ab
) {
}
}

// --------------------------------------------------------------------------
// END CLASSES
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -856,4 +881,32 @@ public function testRemoveInstance(): void

$this->assertEmpty($container->getInstanceClassNames());
}

/**
*
*/
public function testResolveIntersectionType(): void
{
$container = new Container;

$container->register(intersection(IA::class, IB::class), AB::class);

$object = $container->get(Intersection::class);

$this->assertInstanceOf(AB::class, $object->ab);
}

/**
*
*/
public function testResolveIntersectionTypeWithoutRegisteredHint(): void
{
$this->expectException(UnableToResolveParameterException::class);

$container = new Container;

$object = $container->get(Intersection::class);

$this->assertInstanceOf(AB::class, $object->ab);
}
}

0 comments on commit a03bd53

Please sign in to comment.