Skip to content

Commit af668ef

Browse files
Implemented NamedConstructor::canHandle().
1 parent 827bd17 commit af668ef

13 files changed

Lines changed: 228 additions & 31 deletions

src/FeatureAnalyzers/VirtualMethods.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public function extractMetadata(ClassMetadata $classMetadata) : array
5555
$classMetadata->name,
5656
$methodAnnotation->methodName(),
5757
$parameters,
58-
$returnType
58+
$returnType,
59+
$methodAnnotation->isStatic()
5960
);
6061
}
6162

src/Magic/AbstractCallHandler.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,48 @@
1313
namespace ScaleUpStack\EasyObject\Magic;
1414

1515
use ScaleUpStack\EasyObject\FeatureAnalyzers\VirtualMethods;
16+
use ScaleUpStack\EasyObject\Metadata\VirtualMethodMetadata;
1617
use ScaleUpStack\Metadata\Metadata\ClassMetadata;
18+
use ScaleUpStack\Metadata\Metadata\PropertyMetadata;
19+
use ScaleUpStack\Reflection\Reflection;
1720

1821
abstract class AbstractCallHandler implements CallHandler
1922
{
23+
/* default implementation of CallHandler methods */
24+
2025
public function requiresObjectContext() : bool
2126
{
2227
return true;
2328
}
2429

25-
protected function checkForMethod(
26-
string $methodName,
27-
int $expectedNumberOfParameters,
28-
ClassMetadata $classMetadata
29-
)
30+
/* helper methods */
31+
32+
protected function getMethodMetadata(string $methodName, ClassMetadata $classMetadata) : ?VirtualMethodMetadata
3033
{
3134
$virtualMethods = $classMetadata->features[VirtualMethods::FEATURES_KEY];
3235

3336
// check for corresponding @method annotation
3437
if (! array_key_exists($methodName, $virtualMethods)) {
35-
return false;
38+
return null;
3639
}
3740

3841
// check for expected number of parameters
39-
$methodMetadata = $virtualMethods[$methodName];
42+
return $virtualMethods[$methodName];
43+
}
44+
45+
protected function checkMethodsArgumentsCount(
46+
string $methodName,
47+
int $expectedNumberOfParameters,
48+
ClassMetadata $classMetadata
49+
) : bool
50+
{
51+
$methodMetadata = $this->getMethodMetadata($methodName, $classMetadata);
4052

41-
if ($expectedNumberOfParameters !== count($methodMetadata->paramters)) {
53+
if (is_null($methodMetadata)) {
54+
return false;
55+
}
56+
57+
if ($expectedNumberOfParameters !== count($methodMetadata->parameters)) {
4258
return false;
4359
}
4460

@@ -77,4 +93,9 @@ protected function propertyName(
7793

7894
return null;
7995
}
96+
97+
protected function setProperty(object $object, string $propertyName, $value, PropertyMetadata $propertyMetadata)
98+
{
99+
Reflection::setPropertyValue($object, $propertyName, $value);
100+
}
80101
}

src/Magic/Dispatcher.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ public static function invokeStatically(
7070
return self::doInvocation($className, $methodName, $arguments, $prioritizedCallHandlerClassNames, true);
7171
}
7272

73+
/**
74+
* @param object|string $objectOrClassName
75+
*/
7376
private static function doInvocation(
7477
$objectOrClassName,
7578
string $methodName,
@@ -148,7 +151,7 @@ private function assertGivenParametersMatchMethodSignature(
148151
)
149152
{
150153
$methodMetadata = $classMetadata->features[VirtualMethods::FEATURES_KEY][$methodName];
151-
$expectedParameterCount = count($methodMetadata->paramters);
154+
$expectedParameterCount = count($methodMetadata->parameters);
152155

153156
$givenParametersCount = count($parameters);
154157

src/Magic/FixtureBuilder.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class FixtureBuilder extends AbstractCallHandler
2626
public function canHandle(string $methodName, ClassMetadata $classMetadata, array $options) : bool
2727
{
2828
// a build() method is always required
29-
if (! $this->checkForMethod('build', 0, $classMetadata)) {
29+
if (! $this->checkMethodsArgumentsCount('build', 0, $classMetadata)) {
3030
return false;
3131
}
3232

@@ -38,7 +38,7 @@ public function canHandle(string $methodName, ClassMetadata $classMetadata, arra
3838
// otherwise, a with<SomeProperty>() method:
3939

4040
// check if method is declared
41-
if (! $this->checkForMethod($methodName, 1, $classMetadata)) {
41+
if (! $this->checkMethodsArgumentsCount($methodName, 1, $classMetadata)) {
4242
return false;
4343
}
4444

@@ -111,9 +111,7 @@ private function executeBuild(object $object, ClassMetadata $toBeBuildClassMetad
111111
$value = eval($phpString);
112112
}
113113

114-
// TODO: validate data type
115-
116-
Reflection::setPropertyValue($newObject, $propertyName, $value);
114+
$this->setProperty($newObject, $propertyName, $value, $propertyMetadata);
117115
}
118116

119117
return $newObject;

src/Magic/NamedConstructor.php

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,65 @@
1212

1313
namespace ScaleUpStack\EasyObject\Magic;
1414

15+
use ScaleUpStack\EasyObject\Assert;
1516
use ScaleUpStack\Metadata\Metadata\ClassMetadata;
1617
use ScaleUpStack\Metadata\Metadata\PropertyMetadata;
1718
use ScaleUpStack\Reflection\Reflection;
1819

1920
final class NamedConstructor extends AbstractCallHandler
2021
{
22+
const OPTION_KEY_METHOD_NAME = 'methodName';
23+
2124
public function canHandle(string $methodName, ClassMetadata $classMetadata, array $options) : bool
2225
{
26+
Assert::keyExists(
27+
$options,
28+
self::OPTION_KEY_METHOD_NAME,
29+
sprintf(
30+
"The NamedConstructor CallHandler requires a '%s' option.",
31+
self::OPTION_KEY_METHOD_NAME
32+
)
33+
);
34+
Assert::string(
35+
$options[self::OPTION_KEY_METHOD_NAME],
36+
sprintf(
37+
"The '%s' option value of the NamedConstructor CallHandler must be a string.",
38+
self::OPTION_KEY_METHOD_NAME
39+
)
40+
);
41+
42+
if ($methodName !== $options[self::OPTION_KEY_METHOD_NAME]) {
43+
return false;
44+
}
45+
46+
$methodMetadata = $this->getMethodMetadata($methodName, $classMetadata);
47+
48+
if (
49+
is_null($methodMetadata) ||
50+
! $methodMetadata->isStatic ||
51+
'self' !== $methodMetadata->returnType->declaration()
52+
) {
53+
return false;
54+
}
55+
56+
if (
57+
! $this->checkMethodsArgumentsCount(
58+
$methodName,
59+
count($classMetadata->propertyMetadata),
60+
$classMetadata
61+
)
62+
) {
63+
return false;
64+
}
65+
66+
67+
foreach ($methodMetadata->parameters as $methodParameterName => $methodParameter) {
68+
if (! array_key_exists($methodParameterName, $classMetadata->propertyMetadata))
69+
{
70+
return false;
71+
}
72+
}
73+
2374
return true;
2475
}
2576

@@ -42,10 +93,11 @@ public function execute($objectOrClassName, string $methodName, array $arguments
4293
$counter = 0;
4394
/** @var PropertyMetadata $propertyMetadata */
4495
foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
45-
Reflection::setPropertyValue(
96+
$this->setProperty(
4697
$newObject,
4798
$propertyMetadata->name,
48-
$arguments[$counter]
99+
$arguments[$counter],
100+
$propertyMetadata
49101
);
50102

51103
$counter++;

src/Magic/VirtualGetter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class VirtualGetter extends AbstractCallHandler
2020
public function canHandle(string $methodName, ClassMetadata $classMetadata, array $options) : bool
2121
{
2222
return (
23-
$this->checkForMethod($methodName, 0, $classMetadata) &&
23+
$this->checkMethodsArgumentsCount($methodName, 0, $classMetadata) &&
2424
null !== $this->propertyName($methodName, 'get', false, $classMetadata)
2525
);
2626
}

src/Metadata/VirtualMethodMetadata.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,29 @@ final class VirtualMethodMetadata
3030
/**
3131
* @var DataTypeMetadata[]
3232
*/
33-
public $paramters = [];
33+
public $parameters = [];
3434

3535
/**
3636
* @var DataTypeMetadata
3737
*/
3838
public $returnType;
3939

40+
/**
41+
* @var bool
42+
*/
43+
public $isStatic;
44+
4045
/**
4146
* @param DataTypeMetadata[] $parameters
4247
* <parameterName> => <DataTypeMetadata>
4348
*/
44-
public function __construct(string $class, string $name, array $parameters, DataTypeMetadata $returnType)
49+
public function __construct(
50+
string $class,
51+
string $name,
52+
array $parameters,
53+
DataTypeMetadata $returnType,
54+
bool $isStatic
55+
)
4556
{
4657
Assert::allIsInstanceOf(
4758
$parameters,
@@ -55,7 +66,8 @@ public function __construct(string $class, string $name, array $parameters, Data
5566

5667
$this->class = $class;
5768
$this->name = $name;
58-
$this->paramters = $parameters;
69+
$this->parameters = $parameters;
5970
$this->returnType = $returnType;
71+
$this->isStatic = $isStatic;
6072
}
6173
}

tests/PhpUnit/FeatureAnalyzers/VirtualMethodsTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,17 @@ public function it_extracts_virtual_methods_metadata()
4949
$className,
5050
'someProperty',
5151
[],
52-
new DataTypeMetadata('string')
52+
new DataTypeMetadata('string'),
53+
false
5354
),
5455
'withSomeProperty' => new VirtualMethodMetadata(
5556
$className,
5657
'withSomeProperty',
5758
[
5859
'someProperty' => new DataTypeMetadata('string')
5960
],
60-
new DataTypeMetadata('self')
61+
new DataTypeMetadata('self'),
62+
false
6163
),
6264
],
6365
$classMetadata->features[VirtualMethods::FEATURES_KEY]

tests/PhpUnit/Magic/AbstractCallHandlerTest.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ScaleUpStack\EasyObject\Tests\Resources\Magic\ClassForAbstractCallHandlerTesting;
1717
use ScaleUpStack\EasyObject\Tests\Resources\TestCase;
1818
use ScaleUpStack\Metadata\Factory;
19+
use ScaleUpStack\Metadata\Metadata\ClassMetadata;
1920
use ScaleUpStack\Reflection\Reflection;
2021

2122
/**
@@ -28,6 +29,9 @@ final class AbstractCallHandlerTest extends TestCase
2829
*/
2930
private $callHandler;
3031

32+
/**
33+
* @var ClassMetadata
34+
*/
3135
private $classMetadata;
3236

3337
protected function setUp()
@@ -51,9 +55,10 @@ public function provides_virtual_method_names_with_parameter_count() : array
5155
/**
5256
* @test
5357
* @dataProvider provides_virtual_method_names_with_parameter_count
54-
* @covers ::checkForMethod()
58+
* @covers ::checkMethodsArgumentsCount()
59+
* @covers ::getMethodMetadata()
5560
*/
56-
public function it_checks_if_virtual_method_matches(
61+
public function it_checks_if_argument_count_of_virtual_method_matches(
5762
string $methodName,
5863
int $expectedParameterCount,
5964
bool $expectedResult
@@ -63,7 +68,7 @@ public function it_checks_if_virtual_method_matches(
6368
// and a method name, and expected number of parameters both as provided via test parameters
6469

6570
// when checking for the method
66-
$result = Reflection::methodOfClass(AbstractCallHandler::class, 'checkForMethod')
71+
$result = Reflection::methodOfClass(AbstractCallHandler::class, 'checkMethodsArgumentsCount')
6772
->invoke(
6873
$this->callHandler,
6974
$methodName,
@@ -100,7 +105,7 @@ public function it_returns_the_corresponding_property_name_to_a_method_name(
100105
)
101106
{
102107
// given a mocked AbstractCallHandler, and ClassMetadata of some object as provided in setUp()
103-
// and a method name, a method prefix, and if the prefix is required
108+
// and a method name, a method prefix, and if the prefix is required as provided by the test's parameters
104109

105110
// when requesting the property name
106111
$result = Reflection::methodOfClass(AbstractCallHandler::class, 'propertyName')
@@ -115,4 +120,34 @@ public function it_returns_the_corresponding_property_name_to_a_method_name(
115120
// then the result is as expected
116121
$this->assertSame($expectedPropertyResult, $result);
117122
}
123+
124+
/**
125+
* @test
126+
* @covers ::setProperty()
127+
*/
128+
public function it_sets_the_value_of_an_object_when_type_matches()
129+
{
130+
// given a mocked AbstractCallHandler, and ClassMetadata of some object as provided in setUp(),
131+
// and some object, a property name, and a new value
132+
$object = new ClassForAbstractCallHandlerTesting();
133+
$propertyName = 'someProperty';
134+
$newValue = 'newValue';
135+
// and a method name, a method prefix, and if the prefix is required as provided by the test's parameters
136+
137+
// when requesting the property name
138+
Reflection::methodOfClass(AbstractCallHandler::class, 'setProperty')
139+
->invoke(
140+
$this->callHandler,
141+
$object,
142+
$propertyName,
143+
$newValue,
144+
$this->classMetadata->propertyMetadata[$propertyName]
145+
);
146+
147+
// then the property of the object is set
148+
$this->assertSame(
149+
$newValue,
150+
Reflection::getPropertyValue($object, 'someProperty')
151+
);
152+
}
118153
}

0 commit comments

Comments
 (0)