Skip to content

Commit

Permalink
Merge b685fde into b1b4410
Browse files Browse the repository at this point in the history
  • Loading branch information
feryardiant committed Jun 20, 2020
2 parents b1b4410 + b685fde commit 3b95dec
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 7 deletions.
50 changes: 47 additions & 3 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,58 @@ public function unset(string $id): void
/**
* {@inheritDoc}
*/
public function make(string $concrete, ?\Closure $condition = null)
public function make($concrete, ...$params)
{
$instance = $this->resolver->resolve($concrete);

list($args, $condition) = count($params = array_filter($params))
? $this->assertParams($params)
: [[], null];

if (null !== $condition) {
$instance = $condition($instance) ?? $instance;
$instance = $condition($instance) ?: $instance;
}

return $this->resolver->handle($instance, $args);
}

/**
* Assert $argumens and $condition by $params
*
* @param array $params
* @return array List of [$argumens, $condition]
*/
private function assertParams(array $params = []): array
{
$count = count($params);

if (2 === $count) {
$error = null;
if (! is_array($params[0])) {
$error = sprintf('Expect parameter 2 to be an array, %s given', gettype($params[0]));
} elseif (! ($params[1] instanceof \Closure) && null !== $params[1]) {
$error = sprintf('Expect parameter 3 to be a Closure, %s given', gettype($params[1]));
} else {
return [$params[0], $params[1]];
}
throw new \InvalidArgumentException($error);
}

if (1 === $count) {
if (! is_array($params[0]) && ! ($params[0] instanceof \Closure)) {
throw new \InvalidArgumentException(
sprintf('Expect parameter 2 to be an array or Closure, %s given', gettype($params[0]))
);
}

$arguments = is_array($params[0]) ? $params[0] : [];
$condition = $params[0] instanceof \Closure ? $params[0] : null;

return [$arguments, $condition];
}

return $this->resolver->handle($instance);
throw new \InvalidArgumentException(
sprintf('Could not accept more than 3 arguments, %d given', $count + 1)
);
}
}
30 changes: 27 additions & 3 deletions src/Container/ContainerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,33 @@ public function unset(string $id): void;
/**
* Resolve an instance without adding it to the stack.
*
* @param string $concrete
* @param null|\Closure $condition
* It's possible to add 2nd parameter as an array and it will pass it to
* `Resolver::handle($instance, $args)`. While if it was a Closure, it will
* treaten as condition.
*
* ```php
* // Treat 2nd parameter as arguments
* $container->make(SomeClass::class, ['a value'])
*
* // Treat 2nd parameter as condition
* $container->make(SomeClass::class, function ($instance) {
* if ($instance instanceof CertainInterface) {
* return [$instance, 'theMethod'];
* }
*
* return null; // Accepts falsy or $instance of the class
* })
*
* // Treat 2nd parameter as arguments and 3rd as condition
* $container->make(SomeClass::class, ['a value'], function ($instance) {
* // a condition
* })
* ```
*
* @link https://github.com/projek-xyz/container/pull/12
* @param string|callable $concrete String of class name or callable
* @param null|array|\Closure ...$args
* @return mixed
*/
public function make(string $concrete, ?\Closure $condition = null);
public function make($concrete, ...$args);
}
8 changes: 7 additions & 1 deletion src/Container/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ public function handle($instance, array $args = [])
: new \ReflectionFunction($instance);

if ($isMethod) {
$params[] = is_object($instance[0]) ? $instance[0] : null;
$obj = is_object($instance[0]) ? $instance[0] : null;

if (! $reflector->isStatic() && is_string($instance[0])) {
$obj = $this->createInstance($instance[0]);
}

$params[] = $obj;
}

// If it was internal method resolve its params as a closure.
Expand Down
132 changes: 132 additions & 0 deletions test/spec/Container.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,136 @@
});
})->toThrow(new BadMethodCallException);
});

it('Should accept addtional params', function () {
expect(
$this->c->make(Stubs\SomeClass::class, function ($instance) {
return [$instance, 'shouldCalled'];
})
)->toEqual('a value');

expect(function () {
$this->c->make(Stubs\SomeClass::class, 'string');
})->toThrow(new InvalidArgumentException(
'Expect parameter 2 to be an array or Closure, string given'
));

expect(function () {
// only asserting 2nd param if 3rd one is falsy
$this->c->make(Stubs\SomeClass::class, 'string', null);
})->toThrow(new InvalidArgumentException(
'Expect parameter 2 to be an array or Closure, string given'
));

expect(function () {
$this->c->make(Stubs\SomeClass::class, ['string'], 'condition');
})->toThrow(new InvalidArgumentException(
'Expect parameter 3 to be a Closure, string given'
));

expect(function () {
$this->c->make(Stubs\SomeClass::class, ['string'], 'condition', 'more');
})->toThrow(new InvalidArgumentException(
'Could not accept more than 3 arguments, 4 given'
));

expect(function () {
// Correct condition with incorrect argument
$this->c->make(Stubs\SomeClass::class, 'string', function ($instance) {
return [$instance, 'shouldCalled'];
});
})->toThrow(new InvalidArgumentException(
'Expect parameter 2 to be an array, string given'
));

expect(
// Correct condition and argument
$this->c->make(Stubs\SomeClass::class, ['new value'], function ($instance) {
return [$instance, 'shouldCalled'];
})
)->toEqual('new value');
});

it('Should ignore the value of 2nd argument if 1st argument is not callable ', function () {
expect(
// Returns the class instance
$this->c->make(Stubs\SomeClass::class, ['new value'])
)->toBeAnInstanceOf(Stubs\CertainInterface::class);

expect(
// Iggnore falsy param
$this->c->make(Stubs\SomeClass::class, ['new value'], null)
)->toBeAnInstanceOf(Stubs\CertainInterface::class);

expect(
// Iggnore falsy params
$this->c->make(Stubs\SomeClass::class, null, null)
)->toBeAnInstanceOf(Stubs\CertainInterface::class);
});

it('Should accept closure', function () {
expect($this->c->make(function ($param) {
return $param;
}, ['value']))->toEqual('value');

expect($this->c->make(function () {
return new Stubs\SomeClass;
}, ['value'], function ($closure) {
$instance = $closure();

return $instance instanceof Stubs\CertainInterface
? [$instance, 'shouldCalled'] // `shouldCalled` method will get the 'value'
: $closure;
}))->toEqual('value');
});

it('Should returns default if condition is falsy', function () {
expect(
// Returns class instance because it's not a callable
$this->c->make(Stubs\SomeClass::class, function () {
return false;
})
)->toBeAnInstanceOf(Stubs\CertainInterface::class);

expect(
// Return value of the callable method if condition returns falsy
$this->c->make('Stubs\SomeClass::shouldCalled', function () {
return null; // Should accepts null, 0, '', false
})
)->toEqual('a value');

expect(
// Return value of the callable method if condition returns the instance it-self
$this->c->make(['Stubs\SomeClass', 'shouldCalled'], function ($instance) {
return $instance;
})
)->toEqual('a value');
});

it('Should be able to invoke a method directly without condition', function () {
expect(
// Resolve and handle non-static method like a static method
$this->c->make('Stubs\SomeClass::shouldCalled', ['new value'])
)->toEqual('new value');

expect(
// Resolve and handle non-static method like a static method
$this->c->make(['Stubs\SomeClass', 'shouldCalled'], ['new value'])
)->toEqual('new value');

expect(
// Resolve and handle actual static mathod
$this->c->make('Stubs\ConcreteBar::std', ['new value'])
)->toEqual('new value');

expect(
// Resolve and handle actual static mathod
$this->c->make(['Stubs\ConcreteBar', 'std'], ['new value'])
)->toEqual('new value');

expect(
// Resolve and handle non-static method like a static method
$this->c->make([new Stubs\SomeClass(), 'shouldCalled'], ['new value'])
)->toEqual('new value');
});
});
5 changes: 5 additions & 0 deletions test/stub/SomeClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public function handle(AbstractFoo $dummy): string
{
return $dummy->lorem();
}

public function shouldCalled($param = 'a value')
{
return $param;
}
}

0 comments on commit 3b95dec

Please sign in to comment.