Skip to content

Commit 70f6bf4

Browse files
committed
Add new requirement to create obfuscator attribute
1 parent 81f6814 commit 70f6bf4

File tree

4 files changed

+153
-4
lines changed

4 files changed

+153
-4
lines changed

exercises/the-attributes-of-success/initial/deserialize.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,29 @@ function deserialize(string $data, string $className): object
1616

1717
$attrs[0]->newInstance();
1818

19-
$object = new $className;
19+
$object = new $className();
2020

2121
$data = json_decode($data, true);
2222

23+
$obfuscators = array_filter(
24+
$reflectionClass->getMethods(),
25+
fn (ReflectionMethod $m) => count($m->getAttributes(Obfuscate::class)) > 0
26+
);
27+
28+
$obfuscators = array_combine(
29+
array_map(
30+
fn(ReflectionMethod $m) => $m->getAttributes(Obfuscate::class)[0]->newInstance()->key,
31+
$obfuscators
32+
),
33+
$obfuscators
34+
);
35+
36+
foreach ($data as $key => $value) {
37+
if (isset($obfuscators[$key])) {
38+
$data[$key] = $object->{$obfuscators[$key]->getName()}($value);
39+
}
40+
}
41+
2342
foreach ($reflectionClass->getProperties() as $property) {
2443
if ($map = $property->getAttributes(Map::class)) {
2544
$key = $map[0]->newInstance()->mapFrom;

exercises/the-attributes-of-success/solution/deserialize.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,29 @@ function deserialize(string $data, string $className): object
1616

1717
$attrs[0]->newInstance();
1818

19-
$object = new $className;
19+
$object = new $className();
2020

2121
$data = json_decode($data, true);
2222

23+
$obfuscators = array_filter(
24+
$reflectionClass->getMethods(),
25+
fn (ReflectionMethod $m) => count($m->getAttributes(Obfuscate::class)) > 0
26+
);
27+
28+
$obfuscators = array_combine(
29+
array_map(
30+
fn(ReflectionMethod $m) => $m->getAttributes(Obfuscate::class)[0]->newInstance()->key,
31+
$obfuscators
32+
),
33+
$obfuscators
34+
);
35+
36+
foreach ($data as $key => $value) {
37+
if (isset($obfuscators[$key])) {
38+
$data[$key] = $object->{$obfuscators[$key]->getName()}($value);
39+
}
40+
}
41+
2342
foreach ($reflectionClass->getProperties() as $property) {
2443
if ($map = $property->getAttributes(Map::class)) {
2544
$key = $map[0]->newInstance()->mapFrom;

exercises/the-attributes-of-success/solution/solution.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
require_once __DIR__ . '/deserialize.php';
44
require_once __DIR__ . '/attributes.php';
55

6+
7+
#[Attribute(Attribute::TARGET_METHOD)]
8+
class Obfuscate {
9+
public function __construct(public string $key)
10+
{
11+
}
12+
}
13+
614
#[Deserialize]
715
class Review {
816
public string $comment;
@@ -13,7 +21,15 @@ class Review {
1321
public string $date;
1422

1523
#[Skip()]
24+
public string $id;
25+
1626
public ?string $reviewer = null;
27+
28+
#[Obfuscate('reviewer')]
29+
public function obfuscateReviewer(string $reviewer): string
30+
{
31+
return md5($reviewer);
32+
}
1733
}
1834

1935
$object = deserialize($argv[1], Review::class);

src/Exercise/TheAttributesOfSuccess.php

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
use Faker\Generator as FakerGenerator;
66
use PhpParser\Node;
7+
use PhpParser\Node\Scalar\String_;
78
use PhpParser\Node\Stmt;
89
use PhpParser\Node\Stmt\Class_;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Property;
912
use PhpParser\NodeFinder;
1013
use PhpParser\Parser;
1114
use PhpSchool\PhpWorkshop\Check\FileComparisonCheck;
@@ -79,6 +82,7 @@ public function getArgs(): array
7982
return [
8083
json_encode(
8184
[
85+
'id' => random_int(0, 100),
8286
'comment' => $this->faker->sentence(4),
8387
'rating' => $this->faker->numberBetween(0, 5),
8488
'reviewer' => $this->faker->userName(),
@@ -99,17 +103,108 @@ public function check(Input $input): ResultInterface
99103
return $node instanceof Class_ && $node->name && $node->name->name === 'Review';
100104
});
101105

102-
//not even sure we need this the var_dump will cover it
103106
if ($classStmt === null) {
104107
return new Failure($this->getName(), 'A class named Review was not found');
105108
}
106109

110+
/** @var ClassMethod|null $method */
111+
$method = (new NodeFinder())->findFirst($statements, function (Node $node) {
112+
return $node instanceof ClassMethod && $node->name->name === 'obfuscateReviewer';
113+
});
114+
115+
if ($method === null) {
116+
return new Failure($this->getName(), 'A method named obfuscateReviewer was not found');
117+
}
118+
119+
if (!isset($method->attrGroups[0]->attrs[0])) {
120+
return new Failure($this->getName(), 'No attributes found on method obfuscateReviewer');
121+
}
122+
123+
$attribute = $method->attrGroups[0]->attrs[0];
124+
125+
if ($attribute->name->toString() !== 'Obfuscate') {
126+
return new Failure($this->getName(), 'No attribute named Obfuscate found on method obfuscateReviewer');
127+
}
128+
129+
if (!isset($attribute->args[0])) {
130+
return new Failure($this->getName(), 'No property name argument was passed to the Obfuscate attribute');
131+
}
132+
133+
if (!$attribute->args[0]->value instanceof String_ || $attribute->args[0]->value->value !== 'reviewer') {
134+
return new Failure($this->getName(), 'The Obfuscate attribute was not passed the correct data property');
135+
}
136+
137+
/** @var Class_|null $attributeClass */
138+
$attributeClass = (new NodeFinder())->findFirst($statements, function (Node $node) {
139+
return $node instanceof Class_ && $node->name && $node->name->name === 'Obfuscate';
140+
});
141+
142+
if ($attributeClass === null) {
143+
return new Failure($this->getName(), 'A class named Obfuscate was not found');
144+
}
145+
146+
if (!isset($attributeClass->attrGroups[0]->attrs[0])) {
147+
return new Failure($this->getName(), 'No attributes found on class Obfuscate');
148+
}
149+
150+
$attribute = $attributeClass->attrGroups[0]->attrs[0];
151+
152+
if ($attribute->name->toString() !== 'Attribute') {
153+
return new Failure($this->getName(), 'The Obfuscate class was not defined as an Attribute');
154+
}
155+
156+
if (!isset($attribute->args[0])) {
157+
return new Failure($this->getName(), 'No flags were passed to Obfuscate Attribute definition');
158+
}
159+
160+
/** @var \PhpParser\Node\Expr\ClassConstFetch $value */
161+
$value = $attribute->args[0]->value;
162+
163+
if ($value->class->toString() !== 'Attribute' || $value->name->name !== 'TARGET_METHOD') {
164+
return new Failure(
165+
$this->getName(),
166+
'The Obfuscate Attribute was not configured as Attribute::TARGET_METHOD'
167+
);
168+
}
169+
170+
$prop = (new NodeFinder())->findFirst($attributeClass->getProperties(), function (Node $node) {
171+
return $node instanceof Property
172+
&& $node->isPublic()
173+
&& $node->type instanceof \PhpParser\Node\Identifier
174+
&& $node->type->name === 'string'
175+
&& $node->props[0] instanceof \PhpParser\Node\Stmt\PropertyProperty
176+
&& $node->props[0]->name instanceof \PhpParser\Node\VarLikeIdentifier
177+
&& $node->props[0]->name->name === 'key';
178+
});
179+
180+
$promotedProp = (new NodeFinder())->findFirst($attributeClass->getMethods(), function (Node $node) {
181+
return $node instanceof ClassMethod
182+
&& $node->name->name === '__construct'
183+
&& isset($node->params[0])
184+
&& $node->params[0]->flags === 1
185+
&& $node->params[0]->var->name === 'key'
186+
&& $node->params[0]->type instanceof \PhpParser\Node\Identifier
187+
&& $node->params[0]->type->name === 'string';
188+
189+
});
190+
191+
if ($prop === null && $promotedProp === null) {
192+
return new Failure(
193+
$this->getName(),
194+
'The Obfuscate Attribute has no public property named "key"'
195+
);
196+
}
197+
198+
107199
return new Success($this->getName());
108200
}
109201

110202
public function getRequiredFunctions(): array
111203
{
112-
return ['deserialize'];
204+
return [
205+
'deserialize',
206+
'var_dump'
207+
];
113208
}
114209

115210
public function getBannedFunctions(): array

0 commit comments

Comments
 (0)