Skip to content

Commit 1378a2c

Browse files
Implemented Dispatcher::invokeStatically().
1 parent b16b6be commit 1378a2c

7 files changed

Lines changed: 177 additions & 11 deletions

File tree

src/Magic/AbstractCallHandler.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717

1818
abstract class AbstractCallHandler implements CallHandler
1919
{
20+
public function executeStatic(
21+
string $className,
22+
string $methodName,
23+
array $arguments,
24+
ClassMetadata $classMetadata
25+
)
26+
{
27+
throw new \Error("Calling non-static method when not in object context.");
28+
}
29+
2030
protected function checkForMethod(
2131
string $methodName,
2232
int $expectedNumberOfParameters,

src/Magic/CallHandler.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,11 @@ public function canHandle(string $methodName, ClassMetadata $classMetadata, arra
4040
* @return mixed
4141
*/
4242
public function execute(object $object, string $methodName, array $arguments, ClassMetadata $classMetadata);
43+
44+
/**
45+
* Executes a static method on a given class (name).
46+
*
47+
* Cf. self::execute()
48+
*/
49+
public function executeStatic(string $className, string $methodName, array $arguments, ClassMetadata $classMetadata);
4350
}

src/Magic/Dispatcher.php

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,60 @@ public static function invoke(
5151
array $arguments,
5252
array $prioritizedCallHandlerClassNames
5353
)
54+
{
55+
return self::doInvocation($object, $methodName, $arguments, $prioritizedCallHandlerClassNames, false);
56+
}
57+
58+
public static function invokeStatically(
59+
string $className,
60+
string $methodName,
61+
array $arguments,
62+
array $prioritizedCallHandlerClassNames
63+
)
64+
{
65+
return self::doInvocation($className, $methodName, $arguments, $prioritizedCallHandlerClassNames, true);
66+
}
67+
68+
private static function doInvocation(
69+
$objectOrClassName,
70+
string $methodName,
71+
array $arguments,
72+
array $prioritizedCallHandlerClassNames,
73+
bool $isStatic
74+
)
5475
{
5576
$instance = self::instance();
5677

57-
$classMetadata = $instance->classMetadata(
58-
get_class($object)
59-
);
78+
$className = $isStatic ? $objectOrClassName : get_class($objectOrClassName);
79+
$classMetadata = $instance->classMetadata($className);
80+
81+
foreach ($prioritizedCallHandlerClassNames as $callHandlerData) {
82+
// prepare $callHandlerClassName and $options
83+
if (is_array($callHandlerData)) {
84+
$callHandlerClassName = $callHandlerData[0];
85+
$options = $callHandlerData[1];
86+
} else {
87+
$callHandlerClassName = $callHandlerData;
88+
$options = [];
89+
}
6090

61-
foreach ($prioritizedCallHandlerClassNames as $callHandlerClassName) {
91+
// retrieve $callHandler
6292
if (! array_key_exists($callHandlerClassName, $instance->callHandlers)) {
6393
$instance->callHandlers[$callHandlerClassName] = new $callHandlerClassName();
6494
}
6595

6696
$callHandler = $instance->callHandlers[$callHandlerClassName];
6797

68-
if ($callHandler->canHandle($methodName, $classMetadata, [])) {
69-
$return = $callHandler->execute($object, $methodName, $arguments, $classMetadata);
98+
// execute
99+
if ($callHandler->canHandle($methodName, $classMetadata, $options)) {
100+
if (! $isStatic) {
101+
$return = $callHandler->execute($objectOrClassName, $methodName, $arguments, $classMetadata);
102+
} else {
103+
$return = $callHandler->executeStatic($objectOrClassName, $methodName, $arguments, $classMetadata);
104+
}
105+
106+
$instance->assertReturnType($objectOrClassName, $methodName, $return, $classMetadata);
70107

71-
$instance->assertReturnType($object, $methodName, $return, $classMetadata);
72108
return $return;
73109
}
74110
}
@@ -88,8 +124,11 @@ private function classMetadata(string $className) : ClassMetadata
88124
->classMetadata[$className];
89125
}
90126

127+
/**
128+
* @param object|string $objectContext
129+
*/
91130
private function assertReturnType(
92-
object $object,
131+
$objectContext,
93132
string $methodName,
94133
$returnValue,
95134
ClassMetadata $classMetadata
@@ -100,7 +139,7 @@ private function assertReturnType(
100139
$returnType = $virtualMethodMetadata->returnType;
101140

102141
if (! is_null($returnType->declaration())) {
103-
$isTypeValid = $returnType->validateVariable($returnValue, $object);
142+
$isTypeValid = $returnType->validateVariable($returnValue, $objectContext);
104143

105144
if (! $isTypeValid) {
106145
// TODO: Handle error on strict_types declaration of calling context (not file defining the class) :-/

src/Magic/NamedConstructor.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php declare(strict_types = 1);
2+
3+
/**
4+
* This file is part of ScaleUpStack/EasyObject
5+
*
6+
* For the full copyright and license information, please view the README.md and LICENSE.md files that were distributed
7+
* with this source code.
8+
*
9+
* @copyright 2019 - present ScaleUpVentures GmbH, https://www.scaleupventures.com
10+
* @link https://github.com/scaleupstack/easy-object
11+
*/
12+
13+
namespace ScaleUpStack\EasyObject\Magic;
14+
15+
use ScaleUpStack\Metadata\Metadata\ClassMetadata;
16+
use ScaleUpStack\Metadata\Metadata\PropertyMetadata;
17+
use ScaleUpStack\Reflection\Reflection;
18+
19+
final class NamedConstructor extends AbstractCallHandler
20+
{
21+
public function canHandle(string $methodName, ClassMetadata $classMetadata, array $options) : bool
22+
{
23+
return true;
24+
}
25+
26+
public function execute(object $object, string $methodName, array $arguments, ClassMetadata $classMetadata)
27+
{
28+
return $this->executeStatic(
29+
get_class($object),
30+
$methodName,
31+
$arguments,
32+
$classMetadata
33+
);
34+
}
35+
36+
public function executeStatic(string $className, string $methodName, array $arguments, ClassMetadata $classMetadata)
37+
{
38+
$newObject = Reflection::classByName($className)
39+
->newInstanceWithoutConstructor();
40+
41+
$counter = 0;
42+
/** @var PropertyMetadata $propertyMetadata */
43+
foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
44+
Reflection::setPropertyValue(
45+
$newObject,
46+
$propertyMetadata->name,
47+
$arguments[$counter]
48+
);
49+
50+
$counter++;
51+
}
52+
53+
return $newObject;
54+
}
55+
}

tests/PhpUnit/Magic/AbstractCallHandlerTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,20 @@ public function it_throws_an_exception_on_assertGivenParametersMatchMethodSignat
166166
$this->classMetadata
167167
);
168168
}
169+
170+
/**
171+
* @test
172+
* @covers ::executeStatic()
173+
*/
174+
public function it_throws_an_exception_when_executing_a_call_handler_statically_that_requires_object_context()
175+
{
176+
// given a mocked AbstractCallHandler, and ClassMetadata of some object as provided in setUp()
177+
178+
// when calling a CallHandler, that requires an object context, statically
179+
// then an exception is thrown
180+
$this->expectException(\Error::class);
181+
$this->expectExceptionMessage("Calling non-static method when not in object context.");
182+
183+
$this->callHandler->executeStatic('SomeClassName', 'someMethodName', [], $this->classMetadata);
184+
}
169185
}

tests/PhpUnit/Magic/DispatcherTest.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace ScaleUpStack\EasyObject\Tests\PhpUnit\Magic;
1414

1515
use ScaleUpStack\EasyObject\Magic\Dispatcher;
16+
use ScaleUpStack\EasyObject\Magic\NamedConstructor;
1617
use ScaleUpStack\EasyObject\Magic\VirtualGetter;
1718
use ScaleUpStack\EasyObject\Tests\Resources\Magic\ClassForDispatcherTesting;
1819
use ScaleUpStack\EasyObject\Tests\Resources\TestCase;
@@ -29,9 +30,10 @@ final class DispatcherTest extends TestCase
2930
* @covers ::__construct()
3031
* @covers ::classMetadata()
3132
* @covers ::invoke()
33+
* @covers ::doInvocation()
3234
* @covers ::assertReturnType()
3335
*/
34-
public function it_invokes_a_virtual_method_on_some_object_via_some_call_handler()
36+
public function it_invokes_a_virtual_non_static_method_on_some_object_via_some_call_handler()
3537
{
3638
// given an object, and a list of supported call handlers
3739
$object = new ClassForDispatcherTesting();
@@ -51,6 +53,42 @@ public function it_invokes_a_virtual_method_on_some_object_via_some_call_handler
5153
$this->assertSame(42, $result);
5254
}
5355

56+
/**
57+
* @test
58+
* @covers ::invokeStatically()
59+
* @covers ::doInvocation()
60+
*/
61+
public function it_invokes_a_virtual_static_method_on_some_object_via_some_call_handler()
62+
{
63+
// given a class name, and a list of supported call handlers
64+
$className = ClassForDispatcherTesting::class;
65+
$supportedCallHandlers = [
66+
[
67+
NamedConstructor::class,
68+
[
69+
'methodName' => 'myFactoryMethod',
70+
]
71+
],
72+
];
73+
74+
// when invoking an allowed method
75+
$result = Dispatcher::invokeStatically(
76+
$className,
77+
'myFactoryMethod',
78+
[
79+
17,
80+
],
81+
$supportedCallHandlers
82+
);
83+
84+
// then the result is as expected
85+
$this->assertInstanceOf(ClassForDispatcherTesting::class, $result);
86+
$this->assertSame(
87+
17,
88+
Reflection::getPropertyValue($result, 'someProperty')
89+
);
90+
}
91+
5492
/**
5593
* @test
5694
* @covers ::assertReturnType()
@@ -88,7 +126,7 @@ public function it_throws_an_exception_when_the_return_type_is_invalid()
88126

89127
/**
90128
* @test
91-
* @covers ::invoke()
129+
* @covers ::doInvocation()
92130
*/
93131
public function it_throws_an_exception_if_no_call_handler_can_handle_the_method()
94132
{

tests/Resources/Magic/ClassForDispatcherTesting.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace ScaleUpStack\EasyObject\Tests\Resources\Magic;
1414

1515
/**
16+
* @method static self myFactoryMethod(int $someProperty)
1617
* @method int getSomeProperty()
1718
*/
1819
final class ClassForDispatcherTesting

0 commit comments

Comments
 (0)