Skip to content

Instantly share code, notes, and snippets.

@coisa
Last active February 11, 2022 16:17
Show Gist options
  • Select an option

  • Save coisa/a59340cd7b2dc9e9654250a213ae2228 to your computer and use it in GitHub Desktop.

Select an option

Save coisa/a59340cd7b2dc9e9654250a213ae2228 to your computer and use it in GitHub Desktop.
Defines a class to help replace static calls inside objects, using dependency injection to control the unit tests, without the need to re-implement the static call (changing every place that calls methods statically from a specific class).
<?php
namespace CoiSA\Helper;
/**
* Defines a class that can be used to proxy non-static calls to the corresponding static method of an object.
* It is used to help implement unit tests for classes that have static methods.
*
* @example
* <code>
* // @param string|ObjectClassName $object
* public function __construct($object = null)
* {
* $this->object = new ProxyCallStaticMethodInterceptor($object ?: ObjectClassName::class);
* }
* </code>
*/
final class ProxyCallStaticMethodInterceptor
{
/** @var int Code for exception when method not exists. */
const METHOD_NOT_FOUND_CODE = 1;
/** @var int Code for exception when method is not static. */
const METHOD_NOT_STATIC_CODE = 2;
/** @var int Code for exception when method is not public. */
const METHOD_NOT_PUBLIC_CODE = 3;
/**
* @var object|string Either a string containing the name of the class to reflect, or an object.
*/
private $wrappedObject;
/**
* ProxyCallStaticMethodInterceptor constructor.
*
* @param object|string $wrappedObject Either a string containing the name of the class to reflect, or an object.
*/
public function __construct($wrappedObject)
{
$this->wrappedObject = $wrappedObject;
}
/**
* Redirects calls from non-static methods to the corresponding static method of the wrapped object.
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call($method, array $args = [])
{
try {
$reflectionMethod = new ReflectionMethod($this->wrappedObject, $method);
} catch (ReflectionException $reflectionException) {
throw new BadMethodCallException(
"Method $method does not exist",
self::METHOD_NOT_FOUND_CODE,
$reflectionException
);
}
if (false === $reflectionMethod->isStatic()) {
throw new BadMethodCallException("Method $method is not static", self::METHOD_NOT_STATIC_CODE);
}
if (false === $reflectionMethod->isPublic()) {
throw new BadMethodCallException("Method $method is not public", self::METHOD_NOT_PUBLIC_CODE);
}
return call_user_func_array([$this->wrappedObject, $method], $args);
}
}
<?php
namespace CoiSA\Helper\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
final class ProxyCallStaticMethodInterceptorTest extends TestCase
{
public function testCallNonExistentMethodThrowBadMethodCallException()
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionCode(ProxyCallStaticMethodInterceptor::METHOD_NOT_FOUND_CODE);
$proxy = new ProxyCallStaticMethodInterceptor(stdClass::class);
$proxy->nonExistentMethod();
}
public function testCallNonStaticMethodThrowBadMethodCallException()
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionCode(ProxyCallStaticMethodInterceptor::METHOD_NOT_STATIC_CODE);
$object = new class {
public function nonStaticMethod()
{
return 'nonStaticMethod';
}
};
$proxy = new ProxyCallStaticMethodInterceptor($object);
$proxy->nonStaticMethod();
}
public function testCallNonPublicStaticMethodThrowBadMethodCallException()
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionCode(ProxyCallStaticMethodInterceptor::METHOD_NOT_PUBLIC_CODE);
$object = new class {
private static function staticMethod()
{
return 'privateStaticMethod';
}
};
$proxy = new ProxyCallStaticMethodInterceptor($object);
$proxy->staticMethod();
}
public function testCallWillReturnStaticMethodResult()
{
$args = [uniqid('arg1'), uniqid('arg2'), uniqid('arg3')];
$expected = Stub\StubStaticMethodClass::staticMethod($args);
$proxy = new ProxyCallStaticMethodInterceptor(Stub\StubStaticMethodClass::class);
$this->assertEquals($expected, $proxy->staticMethod($args));
}
}
<?php
namespace CoiSA\Helper\Test\Stub;
final class StubStaticMethodClass
{
private static $value;
public static function staticMethod()
{
if (empty(self::$value)) {
self::$value = uniqid('test');
}
return [self::$value, func_get_args()];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment