Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge 58f5d0d into 1177ce5
Browse files Browse the repository at this point in the history
  • Loading branch information
tux-rampage committed Nov 15, 2017
2 parents 1177ce5 + 58f5d0d commit 15200ad
Show file tree
Hide file tree
Showing 16 changed files with 962 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/Container/InjectorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private function createConfig(ContainerInterface $container) : ConfigInterface
public function create(ContainerInterface $container) : InjectorInterface
{
$config = $this->createConfig($container);
return new Injector($config, null, null, $this);
return new Injector($config, $container);
}

/**
Expand Down
61 changes: 58 additions & 3 deletions src/Resolver/DependencyResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ class DependencyResolver implements DependencyResolverInterface
'array',
'resource',
'callable',
'iterable'
];

private $gettypeMap = [
'boolean' => 'bool',
'integer' => 'int',
'double' => 'float'
];

public function __construct(DefinitionInterface $definition, ConfigInterface $config)
Expand Down Expand Up @@ -97,7 +104,7 @@ private function getConfiguredParameters(string $requestedType) : array
// A type configuration may define a parameter should be auto resolved
// even it was defined earlier
$params = array_filter($params, function ($value) {
return ($value != '*');
return ($value !== '*');
});

return $params;
Expand Down Expand Up @@ -139,6 +146,16 @@ private function isUsableType(string $type, string $requiredType) : bool
&& (! $this->container || $this->container->has($type));
}

/**
* @param mixed $value
* @return string
*/
private function getTypeNameFromValue($value): string
{
$type = gettype($value);
return (isset($this->gettypeMap[$type])) ? $this->gettypeMap[$type] : $type;
}

/**
* Check if the given value sadisfies the given type
*
Expand All @@ -159,7 +176,15 @@ private function isValueOf($value, string $type) : bool
return (is_array($value) || ($value instanceof Traversable));
}

return ($type == gettype($value));
$valueType = $this->getTypeNameFromValue($value);
$numerics = ['int', 'float'];

// PHP accepts float for int and vice versa, as well as numeric string values
if (in_array($type, $numerics)) {
return in_array($valueType, $numerics) || (is_string($value) && is_numeric($value));
}

return ($type == $valueType);
}

private function isBuiltinType(string $type) : bool
Expand All @@ -176,6 +201,26 @@ public function setContainer(ContainerInterface $container)
return $this;
}

/**
* @param string $type
* @return bool
*/
private function isCallableType(string $type): bool
{
if ($this->config->isAlias($type)) {
$type = $this->config->getClassForAlias($type);
}

if (! class_exists($type) && ! interface_exists($type)) {
return false;
}

$reflection = new \ReflectionClass($type);

return $reflection->hasMethod('__invoke') &&
$reflection->getMethod('__invoke')->isPublic();
}

/**
* Prepare a candidate for injection
*
Expand All @@ -196,10 +241,20 @@ private function prepareInjection($value, ?string $requiredType) : ?AbstractInje
return $isAvailableInContainer ? new TypeInjection($value) : new ValueInjection($value);
}

if (is_string($value) && ($requiredType != 'string')) {
if (is_string($value) && ! $this->isBuiltinType($requiredType)) {
return $this->isUsableType($value, $requiredType) ? new TypeInjection($value) : null;
}

// Classes may implement iterable
if (is_string($value) && ($requiredType === 'iterable')) {
return $this->isUsableType($value, 'Traversable') ? new TypeInjection($value) : null;
}

// Classes may implement callable, but strings could be callable as well
if (is_string($value) && ($requiredType === 'callable') && $this->isCallableType($value)) {
return new TypeInjection($value);
}

return $this->isValueOf($value, $requiredType) ? new ValueInjection($value) : null;
}

Expand Down
32 changes: 32 additions & 0 deletions test/ConfigProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* @see https://github.com/zendframework/zend-di for the canonical source repository
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-di/blob/master/LICENSE.md New BSD License
*/

namespace ZendTest\Di;

use PHPUnit\Framework\TestCase;
use Zend\Di\ConfigProvider;
use PHPUnit\Framework\Constraint\IsType;

/**
* @coversDefaultClass Zend\Di\Module
*/
class ConfigProviderTest extends TestCase
{
public function testInstanceIsInvokable()
{
$this->assertInternalType(IsType::TYPE_CALLABLE, new ConfigProvider());
}

public function testProvidesDependencies()
{
$provider = new ConfigProvider();
$result = $provider();

$this->assertArrayHasKey('dependencies', $result);
$this->assertEquals($provider->getDependencyConfig(), $result['dependencies']);
}
}
77 changes: 77 additions & 0 deletions test/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

use PHPUnit\Framework\TestCase;
use Zend\Di\Config;
use Zend\Di\Exception;

/**
* @coversDefaultClass Zend\Di\Config
*/
class ConfigTest extends TestCase
{
/**
Expand Down Expand Up @@ -73,4 +77,77 @@ public function testGetTypePreference()
$this->assertNull($config->getTypePreference('B', 'NotDefinedType'));
$this->assertNull($config->getTypePreference('NotDefined', 'NotDefinedType'));
}

public function testConstructWithInvalidOptionsThrowsException()
{
$this->expectException(Exception\InvalidArgumentException::class);
new Config(new \stdClass());
}

public function testSetParameters()
{
$instance = new Config();
$expected = [
'bar' => 'Baz'
];

$this->assertEmpty($instance->getParameters('Foo'));
$instance->setParameters('Foo', $expected);
$this->assertEquals($expected, $instance->getParameters('Foo'));
}

public function testSetGlobalTypePreference()
{
$instance = new Config();
$this->assertNull($instance->getTypePreference('Foo'));
$instance->setTypePreference('Foo', 'Bar');
$this->assertEquals('Bar', $instance->getTypePreference('Foo'));
}

public function testSetTypePreferenceForTypeContext()
{
$instance = new Config();
$this->assertNull($instance->getTypePreference('Foo', 'Baz'));
$instance->setTypePreference('Foo', 'Bar', 'Baz');
$this->assertEquals('Bar', $instance->getTypePreference('Foo', 'Baz'));
}

public function provideValidClassNames()
{
return [
[ TestAsset\A::class ],
[ TestAsset\DummyInterface::class ],
];
}

/**
* @dataProvider provideValidClassNames
*/
public function testSetAlias($className)
{
$instance = new Config();

$this->assertFalse($instance->isAlias('Foo.Bar'));

$instance->setAlias('Foo.Bar', $className);

$this->assertTrue($instance->isAlias('Foo.Bar'));
$this->assertEquals($className, $instance->getClassForAlias('Foo.Bar'));
}

public function provideInvalidClassNames()
{
return [
[ 'Bad.Class.Name.For.PHP' ],
];
}

/**
* @dataProvider provideInvalidClassNames
*/
public function testSetAliasThrowsExceptionForInvalidClass(string $invalidClass)
{
$this->expectException(Exception\ClassNotFoundException::class);
(new Config())->setAlias(uniqid('Some.Alias'), $invalidClass);
}
}
66 changes: 66 additions & 0 deletions test/Container/InjectorFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/**
* @see https://github.com/zendframework/zend-di for the canonical source repository
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-di/blob/master/LICENSE.md New BSD License
*/

namespace ZendTest\Di\Container;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Constraint\IsType;
use Zend\Di\Container\InjectorFactory;
use Psr\Container\ContainerInterface;
use Zend\Di\ConfigInterface;
use Zend\Di\InjectorInterface;

/**
* @coversDefaultClass Zend\Di\Container\InjectorFactory
*/
class InjectorFactoryTest extends TestCase
{
public function testFactoryIsInvokable()
{
$this->assertInternalType(IsType::TYPE_CALLABLE, new InjectorFactory());
}

public function testCreateWillReturnAnInjectorInstance()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMockForAbstractClass();
$result = (new InjectorFactory())->create($container);

$this->assertInstanceOf(InjectorInterface::class, $result);
}

public function testInvokeWillReturnAnInjectorInstance()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMockForAbstractClass();
$factory = new InjectorFactory();
$result = $factory($container);

$this->assertInstanceOf(InjectorInterface::class, $result);
}

public function testUsesConfigServiceFromContainer()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMockForAbstractClass();
$configMock = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass();
$container->expects($this->atLeastOnce())
->method('has')
->with(ConfigInterface::class)
->willReturn(true);

$container->expects($this->atLeastOnce())
->method('get')
->with(ConfigInterface::class)
->willReturn($configMock);

$injector = (new InjectorFactory())->create($container);

$reflection = new \ReflectionObject($injector);
$property = $reflection->getProperty('config');

$property->setAccessible(true);
$this->assertSame($configMock, $property->getValue($injector));
}
}
Loading

0 comments on commit 15200ad

Please sign in to comment.