Skip to content

Commit

Permalink
Explicit exception message for unsupported magic methods (#11)
Browse files Browse the repository at this point in the history
Fixes PHP-DI/PHP-DI#422

When trying to call a magic method, a `Invoker\Exception\NotCallableException` exception will be thrown with message:

> MyClass::foo() is not a callable. A __call() method exists but magic methods are not supported.
  • Loading branch information
mnapoli authored Jun 12, 2016
1 parent e67ce4c commit 4140c38
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 11 deletions.
7 changes: 2 additions & 5 deletions src/CallableResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function resolve($callable)
$callable = $this->resolveFromContainer($callable);

if (! is_callable($callable)) {
throw NotCallableException::fromInvalidCallable($callable);
throw NotCallableException::fromInvalidCallable($callable, true);
}

return $callable;
Expand Down Expand Up @@ -73,10 +73,7 @@ private function resolveFromContainer($callable)
if ($this->container->has($callable)) {
return $this->container->get($callable);
} else {
throw new NotCallableException(sprintf(
'"%s" is neither a callable nor a valid container entry',
$callable
));
throw NotCallableException::fromInvalidCallable($callable, true);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/Exception/NotCallableException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@ class NotCallableException extends InvocationException
{
/**
* @param string $value
* @param bool $containerEntry
* @return self
*/
public static function fromInvalidCallable($value)
public static function fromInvalidCallable($value, $containerEntry = false)
{
if (is_object($value)) {
$message = sprintf('Instance of %s is not a callable', get_class($value));
} elseif (is_array($value) && isset($value[0]) && isset($value[1]) && is_object($value[0])) {
$message = sprintf('%s::%s() is not a callable', get_class($value[0]), $value[1]);
} elseif (is_array($value) && isset($value[0]) && isset($value[1])) {
$class = is_object($value[0]) ? get_class($value[0]) : $value[0];
$extra = method_exists($class, '__call') ? ' A __call() method exists but magic methods are not supported.' : '';
$message = sprintf('%s::%s() is not a callable.%s', $class, $value[1], $extra);
} else {
$message = var_export($value, true) . ' is neither a callable nor a valid container entry';
if ($containerEntry) {
$message = var_export($value, true) . ' is neither a callable nor a valid container entry';
} else {
$message = var_export($value, true) . ' is not a callable';
}
}

return new self($message);
Expand Down
4 changes: 4 additions & 0 deletions src/Reflection/CallableReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public static function create($callable)
if (is_array($callable)) {
list($class, $method) = $callable;

if (! method_exists($class, $method)) {
throw NotCallableException::fromInvalidCallable($callable);
}

return new \ReflectionMethod($class, $method);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/CallableResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public function resolves_string_method_call_with_service()
/**
* @test
* @expectedException \Invoker\Exception\NotCallableException
* @expectedExceptionMessage "foo" is neither a callable nor a valid container entry
* @expectedExceptionMessage 'foo' is neither a callable nor a valid container entry
*/
public function throws_resolving_non_callable_from_container()
{
Expand Down
35 changes: 34 additions & 1 deletion tests/InvokerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ public function should_invoke_method()
$this->assertTrue($fixture->wasCalled);
}

/**
* @test
* @expectedException \Invoker\Exception\NotCallableException
* @expectedExceptionMessage Invoker\Test\InvokerTestFixture::bar() is not a callable.
*/
public function cannot_invoke_unknown_method()
{
$this->invoker->call(array(new InvokerTestFixture, 'bar'));
}

/**
* @test
* @expectedException \Invoker\Exception\NotCallableException
* @expectedExceptionMessage Invoker\Test\InvokerTestMagicMethodFixture::foo() is not a callable. A __call() method exists but magic methods are not supported.
*/
public function cannot_invoke_magic_method()
{
$this->invoker->call(array(new InvokerTestMagicMethodFixture, 'foo'));
}

/**
* @test
*/
Expand Down Expand Up @@ -295,7 +315,7 @@ public function should_throw_if_calling_non_callable_without_container_2()
/**
* @test
* @expectedException \Invoker\Exception\NotCallableException
* @expectedExceptionMessage "foo" is neither a callable nor a valid container entry
* @expectedExceptionMessage 'foo' is neither a callable nor a valid container entry
*/
public function should_throw_if_calling_non_callable_with_container()
{
Expand Down Expand Up @@ -387,3 +407,16 @@ public static function foo()
return 'bar';
}
}

class InvokerTestMagicMethodFixture
{
public $wasCalled = false;
public function __call($name, $args)
{
if ($name === 'foo') {
$this->wasCalled = true;
return 'bar';
}
throw new \Exception('Unknown method');
}
}

0 comments on commit 4140c38

Please sign in to comment.