Skip to content

Commit 4140c38

Browse files
authored
Explicit exception message for unsupported magic methods (#11)
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.
1 parent e67ce4c commit 4140c38

File tree

5 files changed

+52
-11
lines changed

5 files changed

+52
-11
lines changed

src/CallableResolver.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function resolve($callable)
4040
$callable = $this->resolveFromContainer($callable);
4141

4242
if (! is_callable($callable)) {
43-
throw NotCallableException::fromInvalidCallable($callable);
43+
throw NotCallableException::fromInvalidCallable($callable, true);
4444
}
4545

4646
return $callable;
@@ -73,10 +73,7 @@ private function resolveFromContainer($callable)
7373
if ($this->container->has($callable)) {
7474
return $this->container->get($callable);
7575
} else {
76-
throw new NotCallableException(sprintf(
77-
'"%s" is neither a callable nor a valid container entry',
78-
$callable
79-
));
76+
throw NotCallableException::fromInvalidCallable($callable, true);
8077
}
8178
}
8279

src/Exception/NotCallableException.php

+11-4
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,23 @@ class NotCallableException extends InvocationException
1111
{
1212
/**
1313
* @param string $value
14+
* @param bool $containerEntry
1415
* @return self
1516
*/
16-
public static function fromInvalidCallable($value)
17+
public static function fromInvalidCallable($value, $containerEntry = false)
1718
{
1819
if (is_object($value)) {
1920
$message = sprintf('Instance of %s is not a callable', get_class($value));
20-
} elseif (is_array($value) && isset($value[0]) && isset($value[1]) && is_object($value[0])) {
21-
$message = sprintf('%s::%s() is not a callable', get_class($value[0]), $value[1]);
21+
} elseif (is_array($value) && isset($value[0]) && isset($value[1])) {
22+
$class = is_object($value[0]) ? get_class($value[0]) : $value[0];
23+
$extra = method_exists($class, '__call') ? ' A __call() method exists but magic methods are not supported.' : '';
24+
$message = sprintf('%s::%s() is not a callable.%s', $class, $value[1], $extra);
2225
} else {
23-
$message = var_export($value, true) . ' is neither a callable nor a valid container entry';
26+
if ($containerEntry) {
27+
$message = var_export($value, true) . ' is neither a callable nor a valid container entry';
28+
} else {
29+
$message = var_export($value, true) . ' is not a callable';
30+
}
2431
}
2532

2633
return new self($message);

src/Reflection/CallableReflection.php

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public static function create($callable)
3131
if (is_array($callable)) {
3232
list($class, $method) = $callable;
3333

34+
if (! method_exists($class, $method)) {
35+
throw NotCallableException::fromInvalidCallable($callable);
36+
}
37+
3438
return new \ReflectionMethod($class, $method);
3539
}
3640

tests/CallableResolverTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public function resolves_string_method_call_with_service()
125125
/**
126126
* @test
127127
* @expectedException \Invoker\Exception\NotCallableException
128-
* @expectedExceptionMessage "foo" is neither a callable nor a valid container entry
128+
* @expectedExceptionMessage 'foo' is neither a callable nor a valid container entry
129129
*/
130130
public function throws_resolving_non_callable_from_container()
131131
{

tests/InvokerTest.php

+34-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ public function should_invoke_method()
5151
$this->assertTrue($fixture->wasCalled);
5252
}
5353

54+
/**
55+
* @test
56+
* @expectedException \Invoker\Exception\NotCallableException
57+
* @expectedExceptionMessage Invoker\Test\InvokerTestFixture::bar() is not a callable.
58+
*/
59+
public function cannot_invoke_unknown_method()
60+
{
61+
$this->invoker->call(array(new InvokerTestFixture, 'bar'));
62+
}
63+
64+
/**
65+
* @test
66+
* @expectedException \Invoker\Exception\NotCallableException
67+
* @expectedExceptionMessage Invoker\Test\InvokerTestMagicMethodFixture::foo() is not a callable. A __call() method exists but magic methods are not supported.
68+
*/
69+
public function cannot_invoke_magic_method()
70+
{
71+
$this->invoker->call(array(new InvokerTestMagicMethodFixture, 'foo'));
72+
}
73+
5474
/**
5575
* @test
5676
*/
@@ -295,7 +315,7 @@ public function should_throw_if_calling_non_callable_without_container_2()
295315
/**
296316
* @test
297317
* @expectedException \Invoker\Exception\NotCallableException
298-
* @expectedExceptionMessage "foo" is neither a callable nor a valid container entry
318+
* @expectedExceptionMessage 'foo' is neither a callable nor a valid container entry
299319
*/
300320
public function should_throw_if_calling_non_callable_with_container()
301321
{
@@ -387,3 +407,16 @@ public static function foo()
387407
return 'bar';
388408
}
389409
}
410+
411+
class InvokerTestMagicMethodFixture
412+
{
413+
public $wasCalled = false;
414+
public function __call($name, $args)
415+
{
416+
if ($name === 'foo') {
417+
$this->wasCalled = true;
418+
return 'bar';
419+
}
420+
throw new \Exception('Unknown method');
421+
}
422+
}

0 commit comments

Comments
 (0)