-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from BackEndTea/dont-call
Dont `__call` nor `__callStatic`
- Loading branch information
Showing
11 changed files
with
319 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Dont; | ||
|
||
use Dont\Exception\NonCallableObject; | ||
use Dont\Exception\TypeError; | ||
|
||
trait DontCall | ||
{ | ||
/** | ||
* @throws NonCallableObject | ||
* @throws TypeError | ||
*/ | ||
final public function __call($name, $arguments) | ||
{ | ||
throw NonCallableObject::fromAttemptedCall($this, $name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Dont; | ||
|
||
use Dont\Exception\NonStaticCallableClass; | ||
|
||
trait DontCallStatic | ||
{ | ||
/** | ||
* @throws NonStaticCallableClass | ||
*/ | ||
final public static function __callStatic($name, $arguments) | ||
{ | ||
throw NonStaticCallableClass::fromAttemptedStaticCall(static::class, $name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Dont\Exception; | ||
|
||
use LogicException; | ||
|
||
class NonCallableObject extends LogicException implements ExceptionInterface | ||
{ | ||
private const ERROR_TEMPLATE = <<<'ERROR' | ||
The given object %s#%s is not designed to allow any undefined or inaccessible methods to be called. | ||
You tried to call a method called "%s". | ||
Perhaps you made a typo in the method name, or tried to call an inaccessible method? | ||
ERROR; | ||
|
||
/** | ||
* @param object $object | ||
* @throws TypeError | ||
*/ | ||
public static function fromAttemptedCall($object, string $method) : self | ||
{ | ||
if (! is_object($object)) { | ||
throw TypeError::fromNonObject($object); | ||
} | ||
|
||
$className = get_class($object); | ||
|
||
return new self(sprintf( | ||
self::ERROR_TEMPLATE, | ||
$className, | ||
spl_object_hash($object), | ||
$method | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Dont\Exception; | ||
|
||
use LogicException; | ||
|
||
class NonStaticCallableClass extends LogicException implements ExceptionInterface | ||
{ | ||
private const ERROR_TEMPLATE = <<<'ERROR' | ||
The given class %s is not designed to allow any undefined or inaccessible static methods to be called. | ||
You tried to call a static method called "%s". | ||
Perhaps you made a typo in the method name, or tried to call an inaccessible method? | ||
ERROR; | ||
|
||
/** | ||
* @throws TypeError | ||
*/ | ||
public static function fromAttemptedStaticCall(string $class, string $method) : self | ||
{ | ||
return new self(sprintf( | ||
self::ERROR_TEMPLATE, | ||
$class, | ||
$method | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTest; | ||
|
||
use Dont\DontCallStatic; | ||
use Dont\Exception\NonStaticCallableClass; | ||
use DontTestAsset\NonStaticCallable; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* @covers \Dont\DontCallStatic | ||
*/ | ||
final class DontCallStaticTest extends TestCase | ||
{ | ||
public function testWillThrowOnStaticCallAttempt() : void | ||
{ | ||
$this->expectException(NonStaticCallableClass::class); | ||
$this->expectExceptionMessage('NonStaticCallable'); | ||
|
||
NonStaticCallable::undefinedMethod(); | ||
} | ||
|
||
public function testCallStaticPreventionIsFinal() : void | ||
{ | ||
self::assertTrue((new \ReflectionMethod(DontCallStatic::class, '__callStatic'))->isFinal()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTest; | ||
|
||
use Dont\DontCall; | ||
use Dont\Exception\NonCallableObject; | ||
use DontTestAsset\NonCallable; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* @covers \Dont\DontCall | ||
*/ | ||
final class DontCallTest extends TestCase | ||
{ | ||
public function testWillThrowOnCallingAttempt() : void | ||
{ | ||
$object = new NonCallable(); | ||
|
||
$this->expectException(NonCallableObject::class); | ||
|
||
$object->undefinedMethod(); | ||
} | ||
|
||
public function testCallPreventionIsFinal() : void | ||
{ | ||
self::assertTrue((new \ReflectionMethod(DontCall::class, '__call'))->isFinal()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTest\Exception; | ||
|
||
use Dont\Exception\ExceptionInterface; | ||
use Dont\Exception\NonCallableObject; | ||
use Dont\Exception\TypeError; | ||
use LogicException; | ||
use PHPUnit\Framework\TestCase; | ||
use stdClass; | ||
|
||
/** | ||
* @covers \Dont\Exception\NonCallableObject | ||
*/ | ||
final class NonCallableObjectTest extends TestCase | ||
{ | ||
/** | ||
* @dataProvider objectProvider | ||
* | ||
* @param object $object | ||
*/ | ||
public function testFromAttemptedCall($object) : void | ||
{ | ||
$exception = NonCallableObject::fromAttemptedCall($object, 'methodName'); | ||
|
||
self::assertInstanceOf(NonCallableObject::class, $exception); | ||
self::assertInstanceOf(LogicException::class, $exception); | ||
self::assertInstanceOf(ExceptionInterface::class, $exception); | ||
|
||
$expected = 'The given object ' . get_class($object) | ||
. '#' . spl_object_hash($object) . " is not designed to allow any undefined or inaccessible methods to be called.\n\n" | ||
. 'You tried to call a method called "methodName".' . "\n\n" | ||
. 'Perhaps you made a typo in the method name, or tried to call an inaccessible method?'; | ||
|
||
self::assertSame($expected, $exception->getMessage()); | ||
} | ||
|
||
/** | ||
* @return object[][] | ||
*/ | ||
public function objectProvider() : array | ||
{ | ||
return [ | ||
[new stdClass()], | ||
[$this], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider nonObjectProvider | ||
* | ||
* @param mixed $nonObject | ||
*/ | ||
public function testWillThrowOnNonObject($nonObject) : void | ||
{ | ||
$this->expectException(TypeError::class); | ||
|
||
NonCallableObject::fromAttemptedCall($nonObject, 'propertyName'); | ||
} | ||
|
||
/** | ||
* @return mixed[][] | ||
*/ | ||
public function nonObjectProvider() : array | ||
{ | ||
return [ | ||
[null], | ||
[true], | ||
[123], | ||
[12.3], | ||
['foo'], | ||
[[]], | ||
[STDERR], | ||
]; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTest\Exception; | ||
|
||
use Dont\Exception\ExceptionInterface; | ||
use Dont\Exception\NonStaticCallableClass; | ||
use LogicException; | ||
use PHPUnit\Framework\TestCase; | ||
use stdClass; | ||
|
||
/** | ||
* @covers \Dont\Exception\NonStaticCallableClass | ||
*/ | ||
final class NonStaticCallableClassTest extends TestCase | ||
{ | ||
/** | ||
* @dataProvider objectProvider | ||
*/ | ||
public function testFromAttemptedCall(string $class) : void | ||
{ | ||
$exception = NonStaticCallableClass::fromAttemptedStaticCall($class, 'methodName'); | ||
|
||
self::assertInstanceOf(NonStaticCallableClass::class, $exception); | ||
self::assertInstanceOf(LogicException::class, $exception); | ||
self::assertInstanceOf(ExceptionInterface::class, $exception); | ||
|
||
$expected = 'The given class ' . $class | ||
. " is not designed to allow any undefined or inaccessible static methods to be called.\n\n" | ||
. 'You tried to call a static method called "methodName".' . "\n\n" | ||
. 'Perhaps you made a typo in the method name, or tried to call an inaccessible method?'; | ||
|
||
self::assertSame($expected, $exception->getMessage()); | ||
} | ||
|
||
/** | ||
* @return string[][] | ||
*/ | ||
public function objectProvider() : array | ||
{ | ||
return [ | ||
[stdClass::class], | ||
['Dont\Call\Me\Maybe'], | ||
[self::class], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTestAsset; | ||
|
||
use Dont\DontCall; | ||
|
||
final class NonCallable | ||
{ | ||
use DontCall; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DontTestAsset; | ||
|
||
use Dont\DontCallStatic; | ||
|
||
final class NonStaticCallable | ||
{ | ||
use DontCallStatic; | ||
} |