Skip to content

Commit

Permalink
Merge pull request #10 from BackEndTea/dont-call
Browse files Browse the repository at this point in the history
Dont `__call` nor `__callStatic`
  • Loading branch information
Ocramius authored Mar 16, 2019
2 parents 86cc7bf + 54894f8 commit 35a93c4
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ composer require roave/dont

## Usage

The package currently provides five traits:
The package currently provides seven traits:

* `Dont\DontDeserialise`
* `Dont\DontSerialize`
* `Dont\DontClone`
* `Dont\DontGet`
* `Dont\DontSet`
* `Dont\DontCall`
* `Dont\DontCallStatic`

Usage is straightforward:

Expand Down
20 changes: 20 additions & 0 deletions src/Dont/DontCall.php
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);
}
}
18 changes: 18 additions & 0 deletions src/Dont/DontCallStatic.php
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);
}
}
38 changes: 38 additions & 0 deletions src/Dont/Exception/NonCallableObject.php
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
));
}
}
30 changes: 30 additions & 0 deletions src/Dont/Exception/NonStaticCallableClass.php
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
));
}
}
29 changes: 29 additions & 0 deletions tests/DontTest/DontCallStaticTest.php
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());
}
}
30 changes: 30 additions & 0 deletions tests/DontTest/DontCallTest.php
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());
}
}
79 changes: 79 additions & 0 deletions tests/DontTest/Exception/NonCallableObjectTest.php
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],
];
}

}
48 changes: 48 additions & 0 deletions tests/DontTest/Exception/NonStaticCallableClassTest.php
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],
];
}
}
12 changes: 12 additions & 0 deletions tests/DontTestAsset/NonCallable.php
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;
}
12 changes: 12 additions & 0 deletions tests/DontTestAsset/NonStaticCallable.php
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;
}

0 comments on commit 35a93c4

Please sign in to comment.