Skip to content

Commit

Permalink
Renamed IBodyNegotiator to IBodyDeserializer and removed some code th…
Browse files Browse the repository at this point in the history
…at was violating DRY (#255)

* Changed name of IBodyNegotiator to IBodyDeserializer and BodyNegotiator to NegotiatedBodyDeserializer to be a bit more specific

* Added support for deserializing null bodies as empty arrays, updated PHP-CS-Fixer to sort elements alphabetically

* Updated various API components to use IBodyDeserializer where appropriate

* Updated static analysis to order alphabetically elements at the same visibility
  • Loading branch information
davidbyoung committed Apr 30, 2023
1 parent 3d804a9 commit f962669
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
'method_protected',
'method_private_static',
'method_private'
]
],
],
'ordered_imports' => true,
'return_type_declaration' => ['space_before' => 'none'],
Expand Down
4 changes: 2 additions & 2 deletions src/Binders/Metadata/BinderMetadataCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/
final class BinderMetadataCollection
{
/** @var array<class-string, list<BinderMetadata>> The mapping of interfaces to binder metadata that universally resolve those interfaces */
private array $universalResolutions = [];
/** @var array<class-string, array<class-string, list<BinderMetadata>>> The mapping of targets to interfaces to binder metadata that resolve the interface for the target */
private array $targetedResolutions = [];
/** @var array<class-string, list<BinderMetadata>> The mapping of interfaces to binder metadata that universally resolve those interfaces */
private array $universalResolutions = [];

/**
* @param list<BinderMetadata> $binderMetadatas The list of all binder metadata
Expand Down
8 changes: 4 additions & 4 deletions src/Binders/Metadata/ContainerBinderMetadataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
*/
final class ContainerBinderMetadataCollector implements IBinderMetadataCollector, IContainer
{
/** @var Context The current context */
private Context $currentContext;
/** @var list<Context> The stack of contexts */
private array $contextStack = [];
/** @var list<BoundInterface> The list of bound interfaces that were found */
private array $boundInterfaces = [];
/** @var list<Context> The stack of contexts */
private array $contextStack = [];
/** @var Context The current context */
private Context $currentContext;
/** @var list<ResolvedInterface> The list of resolved interfaces that were found */
private array $resolvedInterfaces = [];

Expand Down
8 changes: 4 additions & 4 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ class Container implements IContainer
* @var Container|null
*/
public static ?Container $globalInstance = null;
/** @var Context The current context */
protected Context $currentContext;
/** @var list<Context> The stack of contexts */
protected array $contextStack = [];
/** @var array<string|class-string, array<string|class-string, IContainerBinding<object>>> The list of bindings */
protected array $bindings = [];
/** @var array<class-string, array{0: ReflectionMethod|null, 1: list<ReflectionParameter>|null}> The cache of reflection constructors and their parameters */
protected array $constructorReflectionCache = [];
/** @var list<Context> The stack of contexts */
protected array $contextStack = [];
/** @var Context The current context */
protected Context $currentContext;

public function __construct()
{
Expand Down
114 changes: 57 additions & 57 deletions tests/Binders/Metadata/BinderMetadataCollectionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ public function bind(IContainer $container): void
$this->assertEquals($expectedCollection, $actualCollection);
}

public function testCreatingCollectionThatMustRetryBinderKeepsTrackOfResolutionsThatWorkedTheSecondTime(): void
{
$this->expectException(ImpossibleBindingException::class);
$binderA = new class () extends Binder {
public function bind(IContainer $container): void
{
// This will fail the first time, but should pass the second time
$container->resolve(IFoo::class);
$container->bindClass(IPerson::class, Dave::class);
// This will continue to not be able to be resolved
$container->resolve(IBar::class);
}
};
$binderB = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IFoo::class, new Foo());
}
};
$this->expectExceptionMessage(
(new ImpossibleBindingException([IBar::class => [$binderA]]))->getMessage(),
);
$this->factory->createBinderMetadataCollection([$binderA, $binderB]);
}

public function testCreatingCollectionThatNeedsTargetedBindingWorksWhenOneHasUniversalBinding(): void
{
$target = new class () {
Expand Down Expand Up @@ -134,6 +159,38 @@ public function bind(IContainer $container): void
$this->assertEquals($expectedCollection, $actualCollection);
}

public function testCreatingCollectionThatReliesOnMultipleOtherBindersBindingStillWorks(): void
{
$binderA = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->resolve(IFoo::class);
$container->bindClass(IPerson::class, Dave::class);
$container->resolve(IBar::class);
}
};
$binderB = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IFoo::class, new Foo());
}
};
$binderC = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IBar::class, new Bar());
}
};
// Binder B will be before binder A because it isn't dependent on another binder's bindings
$expectedCollection = new BinderMetadataCollection([
new BinderMetadata($binderB, [new BoundInterface(IFoo::class, new UniversalContext())], []),
new BinderMetadata($binderC, [new BoundInterface(IBar::class, new UniversalContext())], []),
new BinderMetadata($binderA, [new BoundInterface(IPerson::class, new UniversalContext())], [new ResolvedInterface(IFoo::class, new UniversalContext()), new ResolvedInterface(IBar::class, new UniversalContext())])
]);
$actualCollection = $this->factory->createBinderMetadataCollection([$binderA, $binderB, $binderC]);
$this->assertEquals($expectedCollection, $actualCollection);
}

public function testCreatingCollectionThatReliesOnTargetedBindingSetInAnotherStillWorks(): void
{
$target = new class () {
Expand Down Expand Up @@ -170,63 +227,6 @@ public function bind(IContainer $container): void
$this->assertEquals($expectedCollection, $actualCollection);
}

public function testCreatingCollectionThatMustRetryBinderKeepsTrackOfResolutionsThatWorkedTheSecondTime(): void
{
$this->expectException(ImpossibleBindingException::class);
$binderA = new class () extends Binder {
public function bind(IContainer $container): void
{
// This will fail the first time, but should pass the second time
$container->resolve(IFoo::class);
$container->bindClass(IPerson::class, Dave::class);
// This will continue to not be able to be resolved
$container->resolve(IBar::class);
}
};
$binderB = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IFoo::class, new Foo());
}
};
$this->expectExceptionMessage(
(new ImpossibleBindingException([IBar::class => [$binderA]]))->getMessage(),
);
$this->factory->createBinderMetadataCollection([$binderA, $binderB]);
}

public function testCreatingCollectionThatReliesOnMultipleOtherBindersBindingStillWorks(): void
{
$binderA = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->resolve(IFoo::class);
$container->bindClass(IPerson::class, Dave::class);
$container->resolve(IBar::class);
}
};
$binderB = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IFoo::class, new Foo());
}
};
$binderC = new class () extends Binder {
public function bind(IContainer $container): void
{
$container->bindInstance(IBar::class, new Bar());
}
};
// Binder B will be before binder A because it isn't dependent on another binder's bindings
$expectedCollection = new BinderMetadataCollection([
new BinderMetadata($binderB, [new BoundInterface(IFoo::class, new UniversalContext())], []),
new BinderMetadata($binderC, [new BoundInterface(IBar::class, new UniversalContext())], []),
new BinderMetadata($binderA, [new BoundInterface(IPerson::class, new UniversalContext())], [new ResolvedInterface(IFoo::class, new UniversalContext()), new ResolvedInterface(IBar::class, new UniversalContext())])
]);
$actualCollection = $this->factory->createBinderMetadataCollection([$binderA, $binderB, $binderC]);
$this->assertEquals($expectedCollection, $actualCollection);
}

public function testCreatingCollectionWithBinderThatCannotResolveSomethingThrowsException(): void
{
$this->expectException(ImpossibleBindingException::class);
Expand Down
36 changes: 18 additions & 18 deletions tests/Binders/Metadata/BinderMetadataCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function testBinderThatResolvesTargetedInterfaceIsNotReturnedForTargetedB
$this->assertEmpty($collection->getBinderMetadataThatResolveInterface(new BoundInterface($boundInterface::class, new TargetedContext($target::class))));
}

public function testBinderThatResolvesTargetedInterfaceIsReturnedForUniversalBoundInterfaceWithSameInterface(): void
public function testBinderThatResolvesTargetedInterfaceIsReturnedForTargetedBoundInterfaceWithSameInterfaceAndTarget(): void
{
$resolvedInterface = new class () {
};
Expand All @@ -49,12 +49,14 @@ public function testBinderThatResolvesTargetedInterfaceIsReturnedForUniversalBou
new BinderMetadata($this->createMockBinder(), [], [new ResolvedInterface($resolvedInterface::class, new TargetedContext($target::class))])
];
$collection = new BinderMetadataCollection($binderMetadatas);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(new BoundInterface($resolvedInterface::class, new UniversalContext()));
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(
new BoundInterface($resolvedInterface::class, new TargetedContext($target::class))
);
$this->assertCount(1, $actualBinderMetadatas);
$this->assertSame($binderMetadatas[0], $actualBinderMetadatas[0]);
}

public function testBinderThatResolvesTargetedInterfaceIsReturnedForTargetedBoundInterfaceWithSameInterfaceAndTarget(): void
public function testBinderThatResolvesTargetedInterfaceIsReturnedForUniversalBoundInterfaceWithSameInterface(): void
{
$resolvedInterface = new class () {
};
Expand All @@ -64,58 +66,56 @@ public function testBinderThatResolvesTargetedInterfaceIsReturnedForTargetedBoun
new BinderMetadata($this->createMockBinder(), [], [new ResolvedInterface($resolvedInterface::class, new TargetedContext($target::class))])
];
$collection = new BinderMetadataCollection($binderMetadatas);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(
new BoundInterface($resolvedInterface::class, new TargetedContext($target::class))
);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(new BoundInterface($resolvedInterface::class, new UniversalContext()));
$this->assertCount(1, $actualBinderMetadatas);
$this->assertSame($binderMetadatas[0], $actualBinderMetadatas[0]);
}

public function testBinderThatUniversallyResolvesInterfaceIsNotReturnedForUniversalBoundInterfaceWithDifferentInterface(): void
public function testBinderThatUniversallyResolvesInterfaceIsNotReturnedForTargetedBoundInterfaceWithSameInterface(): void
{
$resolvedInterface = new class () {
};
$boundInterface = new class () {
$target = new class () {
};
$binderMetadatas = [
new BinderMetadata($this->createMockBinder(), [], [new ResolvedInterface($resolvedInterface::class, new UniversalContext())])
];
$collection = new BinderMetadataCollection($binderMetadatas);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(
new BoundInterface($boundInterface::class, new UniversalContext())
new BoundInterface($resolvedInterface::class, new TargetedContext($target::class))
);
$this->assertEmpty($actualBinderMetadatas);
}

public function testBinderThatUniversallyResolvesInterfaceIsReturnedForUniversalBoundInterfaceWithSameInterface(): void
public function testBinderThatUniversallyResolvesInterfaceIsNotReturnedForUniversalBoundInterfaceWithDifferentInterface(): void
{
$resolvedInterface = new class () {
};
$boundInterface = new class () {
};
$binderMetadatas = [
new BinderMetadata($this->createMockBinder(), [], [new ResolvedInterface($resolvedInterface::class, new UniversalContext())])
];
$collection = new BinderMetadataCollection($binderMetadatas);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(
new BoundInterface($resolvedInterface::class, new UniversalContext())
new BoundInterface($boundInterface::class, new UniversalContext())
);
$this->assertCount(1, $actualBinderMetadatas);
$this->assertSame($binderMetadatas[0], $actualBinderMetadatas[0]);
$this->assertEmpty($actualBinderMetadatas);
}

public function testBinderThatUniversallyResolvesInterfaceIsNotReturnedForTargetedBoundInterfaceWithSameInterface(): void
public function testBinderThatUniversallyResolvesInterfaceIsReturnedForUniversalBoundInterfaceWithSameInterface(): void
{
$resolvedInterface = new class () {
};
$target = new class () {
};
$binderMetadatas = [
new BinderMetadata($this->createMockBinder(), [], [new ResolvedInterface($resolvedInterface::class, new UniversalContext())])
];
$collection = new BinderMetadataCollection($binderMetadatas);
$actualBinderMetadatas = $collection->getBinderMetadataThatResolveInterface(
new BoundInterface($resolvedInterface::class, new TargetedContext($target::class))
new BoundInterface($resolvedInterface::class, new UniversalContext())
);
$this->assertEmpty($actualBinderMetadatas);
$this->assertCount(1, $actualBinderMetadatas);
$this->assertSame($binderMetadatas[0], $actualBinderMetadatas[0]);
}

public function testGetAllBinderMetadataReturnsAllMetadata(): void
Expand Down
11 changes: 5 additions & 6 deletions tests/Binders/Metadata/BoundInterfaceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@

class BoundInterfaceTest extends TestCase
{
public function testGetInterfaceReturnsSetInterface(): void
{
$interface = new BoundInterface(self::class, new UniversalContext());
$this->assertSame(self::class, $interface->interface);
}

public function testGetContextReturnsSetContext(): void
{
$target = new class () {
Expand All @@ -33,4 +27,9 @@ public function testGetContextReturnsSetContext(): void
$interface = new BoundInterface(self::class, $expectedContext);
$this->assertSame($expectedContext, $interface->context);
}
public function testGetInterfaceReturnsSetInterface(): void
{
$interface = new BoundInterface(self::class, new UniversalContext());
$this->assertSame(self::class, $interface->interface);
}
}
15 changes: 7 additions & 8 deletions tests/Binders/Metadata/ResolvedInterfaceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@

class ResolvedInterfaceTest extends TestCase
{
public function testGetInterfaceReturnsSetInterface(): void
{
$resolvedInterface = new class () {
};
$interface = new ResolvedInterface($resolvedInterface::class, new UniversalContext());
$this->assertSame($resolvedInterface::class, $interface->interface);
}

public function testGetContextReturnsSetContext(): void
{
$target = new class () {
Expand All @@ -37,4 +29,11 @@ public function testGetContextReturnsSetContext(): void
$interface = new ResolvedInterface($resolvedInterface::class, $expectedContext);
$this->assertSame($expectedContext, $interface->context);
}
public function testGetInterfaceReturnsSetInterface(): void
{
$resolvedInterface = new class () {
};
$interface = new ResolvedInterface($resolvedInterface::class, new UniversalContext());
$this->assertSame($resolvedInterface::class, $interface->interface);
}
}
14 changes: 7 additions & 7 deletions tests/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,6 @@ public function testCallingClosureWithPrimitiveTypes(): void
$this->assertSame('foo', $result);
}

public function testCallingMethodWithPrimitiveTypesWithoutSpecifyingValue(): void
{
$this->expectException(CallException::class);
$instance = new ConstructorWithSetters();
$this->container->callMethod($instance, 'setPrimitive');
}

public function testCallingClosureWithTypeHintedAndPrimitiveTypes(): void
{
$this->container->bindClass(IFoo::class, Bar::class, [], true);
Expand Down Expand Up @@ -166,6 +159,13 @@ public function testCallingClosureWithUnresolvableParametersThrowsException(): v
$this->container->callClosure(fn (IFoo $foo) => null);
}

public function testCallingMethodWithPrimitiveTypesWithoutSpecifyingValue(): void
{
$this->expectException(CallException::class);
$instance = new ConstructorWithSetters();
$this->container->callMethod($instance, 'setPrimitive');
}

public function testCallingNonExistentMethod(): void
{
$this->expectException(CallException::class);
Expand Down
4 changes: 2 additions & 2 deletions tests/Mocks/ConstructorWithDefaultValuePrimitives.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/
class ConstructorWithDefaultValuePrimitives
{
/** @var string A primitive stored by this class */
private string $foo;
/** @var string A primitive stored by this class */
private string $bar;
/** @var string A primitive stored by this class */
private string $foo;

/**
* @param string $foo A primitive to store in this class
Expand Down
4 changes: 2 additions & 2 deletions tests/Mocks/ConstructorWithPrimitives.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/
class ConstructorWithPrimitives
{
/** @var string A primitive stored by this class */
private string $foo;
/** @var string A primitive stored by this class */
private string $bar;
/** @var string A primitive stored by this class */
private string $foo;

/**
* @param string $foo A primitive to store in this class
Expand Down
Loading

0 comments on commit f962669

Please sign in to comment.