Skip to content

Commit e7a5e81

Browse files
committed
fdiv exercise
1 parent 9b0e664 commit e7a5e81

File tree

10 files changed

+269
-0
lines changed

10 files changed

+269
-0
lines changed

app/bootstrap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use PhpSchool\PHP8Appreciate\Exercise\AMatchMadeInHeaven;
2323
use PhpSchool\PHP8Appreciate\Exercise\CautionWithCatches;
2424
use PhpSchool\PHP8Appreciate\Exercise\HaveTheLastSay;
25+
use PhpSchool\PHP8Appreciate\Exercise\InfiniteDivisions;
2526
use PhpSchool\PHP8Appreciate\Exercise\PhpPromotion;
2627
use PhpSchool\PHP8Appreciate\Exercise\LordOfTheStrings;
2728
use PhpSchool\PHP8Appreciate\Exercise\UniteTheTypes;
@@ -35,6 +36,7 @@
3536
$app->addExercise(CautionWithCatches::class);
3637
$app->addExercise(LordOfTheStrings::class);
3738
$app->addExercise(UniteTheTypes::class);
39+
$app->addExercise(InfiniteDivisions::class);
3840

3941
$art = <<<ART
4042
_ __ _

app/config.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use PhpSchool\PHP8Appreciate\Exercise\AMatchMadeInHeaven;
44
use PhpSchool\PHP8Appreciate\Exercise\CautionWithCatches;
55
use PhpSchool\PHP8Appreciate\Exercise\HaveTheLastSay;
6+
use PhpSchool\PHP8Appreciate\Exercise\InfiniteDivisions;
67
use PhpSchool\PHP8Appreciate\Exercise\PhpPromotion;
78
use PhpSchool\PHP8Appreciate\Exercise\LordOfTheStrings;
89
use PhpSchool\PHP8Appreciate\Exercise\UniteTheTypes;
@@ -33,4 +34,7 @@
3334
UniteTheTypes::class => function (ContainerInterface $c) {
3435
return new UniteTheTypes($c->get(PhpParser\Parser::class), $c->get(\Faker\Generator::class));
3536
},
37+
InfiniteDivisions::class => function (ContainerInterface $c) {
38+
return new InfiniteDivisions($c->get(PhpParser\Parser::class), $c->get(\Faker\Generator::class));
39+
},
3640
];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
You have been given a piece of code (look for `the-return-of-static.php` in your working directory) which is using static return types.
2+
3+
You will find two classes. `File`, a base class, and `Image` a class extending and adding behavior to `File`. We instantiate `Image`, set some properties using a fluent interface and then dump the object using `var_dump`.
4+
5+
If you run the code using `{appname} run the-return-of-static.php` you will see it is broken.
6+
7+
Locate and fix the issue!
8+
9+
### The advantages of the static return type
10+
11+
* Enforces that an instance of the class the method is called from, is returned.
12+
* Most useful for fluent interfaces and static constructors to ensure an instance of a parent class is not returned.
13+
14+
----------------------------------------------------------------------
15+
## HINTS
16+
17+
(Brief) Documentation on the static return type feature can be found by pointing your browser here:
18+
[https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.static]()
19+
20+
The static return type enforces methods to return an instance of the class that the method was called from, rather than the one it was defined in.
21+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
try {
4+
$argv[1] / $argv[2];
5+
} catch (DivisionByZeroError $e) {
6+
echo $e->getMessage() . "\n";
7+
}
8+
9+
echo match ($res = fdiv($argv[1], $argv[2])) {
10+
INF => 'Infinite',
11+
-INF => 'Minus Infinite',
12+
default => $res
13+
};
14+
15+
echo "\n";

src/Exercise/InfiniteDivisions.php

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
namespace PhpSchool\PHP8Appreciate\Exercise;
4+
5+
use DivisionByZeroError;
6+
use Faker\Generator as FakerGenerator;
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\BinaryOp\Div;
9+
use PhpParser\Node\Name;
10+
use PhpParser\Node\Stmt;
11+
use PhpParser\Node\Stmt\Catch_;
12+
use PhpParser\NodeFinder;
13+
use PhpParser\Parser;
14+
use PhpSchool\PhpWorkshop\Check\FunctionRequirementsCheck;
15+
use PhpSchool\PhpWorkshop\Exercise\AbstractExercise;
16+
use PhpSchool\PhpWorkshop\Exercise\CliExercise;
17+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
18+
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
19+
use PhpSchool\PhpWorkshop\ExerciseCheck\FunctionRequirementsExerciseCheck;
20+
use PhpSchool\PhpWorkshop\ExerciseCheck\SelfCheck;
21+
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
22+
use PhpSchool\PhpWorkshop\Input\Input;
23+
use PhpSchool\PhpWorkshop\Result\Failure;
24+
use PhpSchool\PhpWorkshop\Result\ResultInterface;
25+
use PhpSchool\PhpWorkshop\Result\Success;
26+
27+
class InfiniteDivisions extends AbstractExercise implements
28+
ExerciseInterface,
29+
CliExercise,
30+
FunctionRequirementsExerciseCheck,
31+
SelfCheck
32+
{
33+
public function __construct(private Parser $parser, private FakerGenerator $faker)
34+
{
35+
}
36+
37+
public function getName(): string
38+
{
39+
return 'Infinite Divisions';
40+
}
41+
42+
public function getDescription(): string
43+
{
44+
return 'PHP 8\'s fdiv function and DivisionByZeroError exception';
45+
}
46+
47+
public function getType(): ExerciseType
48+
{
49+
return ExerciseType::CLI();
50+
}
51+
52+
public function configure(ExerciseDispatcher $dispatcher): void
53+
{
54+
$dispatcher->requireCheck(FunctionRequirementsCheck::class);
55+
}
56+
57+
public function getArgs(): array
58+
{
59+
return [
60+
[
61+
(string) $this->faker->numberBetween(10, 100),
62+
(string) $this->faker->numberBetween(0, 10)
63+
]
64+
];
65+
}
66+
67+
public function getRequiredFunctions(): array
68+
{
69+
return ['fdiv'];
70+
}
71+
72+
public function getBannedFunctions(): array
73+
{
74+
return [];
75+
}
76+
77+
public function check(Input $input): ResultInterface
78+
{
79+
/** @var array<Stmt> $statements */
80+
$statements = $this->parser->parse((string) file_get_contents($input->getRequiredArgument('program')));
81+
82+
$finder = new NodeFinder();
83+
84+
/** @var Stmt\TryCatch|null $tryCatch */
85+
$tryCatch = $finder->findFirstInstanceOf($statements, Stmt\TryCatch::class);
86+
87+
if (!$tryCatch) {
88+
return new Failure($this->getName(), 'No try/catch statement could be found');
89+
}
90+
91+
/** @var Div|null $divOp */
92+
$divOp = $finder->findFirstInstanceOf($tryCatch->stmts, Div::class);
93+
94+
if (!$divOp) {
95+
return new Failure($this->getName(), 'No division operation could be found in the try block');
96+
}
97+
98+
/** @var Catch_|null $catch */
99+
$catch = $finder->findFirst($tryCatch->catches, function (Node $node) {
100+
if ($node instanceof Catch_) {
101+
return in_array(
102+
DivisionByZeroError::class,
103+
array_map(fn (Name $n) => $n->toString(), $node->types),
104+
true
105+
);
106+
}
107+
108+
return false;
109+
});
110+
111+
if (!$catch) {
112+
return new Failure($this->getName(), 'No catch block for the DivisionByZeroError exception found');
113+
}
114+
115+
return new Success($this->getName());
116+
}
117+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace PhpSchool\PHP8AppreciateTest\Exercise;
4+
5+
use PhpSchool\PHP8Appreciate\Exercise\InfiniteDivisions;
6+
use PhpSchool\PhpWorkshop\Application;
7+
use PhpSchool\PhpWorkshop\Result\Failure;
8+
use PhpSchool\PhpWorkshop\TestUtils\WorkshopExerciseTest;
9+
10+
class InfiniteDivisionsTest extends WorkshopExerciseTest
11+
{
12+
public function getExerciseClass(): string
13+
{
14+
return InfiniteDivisions::class;
15+
}
16+
17+
public function getApplication(): Application
18+
{
19+
return require __DIR__ . '/../../app/bootstrap.php';
20+
}
21+
22+
public function testFailureWhenNoTryCatch(): void
23+
{
24+
$this->runExercise('no-try-catch.php');
25+
26+
$this->assertVerifyWasNotSuccessful();
27+
28+
$this->assertResultsHasFailure(Failure::class, 'No try/catch statement could be found');
29+
}
30+
31+
public function testFailureWhenNoDivisionOperationFoundInTryBlock(): void
32+
{
33+
$this->runExercise('no-divide-in-try.php');
34+
35+
$this->assertVerifyWasNotSuccessful();
36+
37+
$this->assertResultsHasFailure(Failure::class, 'No division operation could be found in the try block');
38+
}
39+
40+
public function testFailureWhenNoDivisionByZeroCatch(): void
41+
{
42+
$this->runExercise('no-division-by-zero-catch.php');
43+
44+
$this->assertVerifyWasNotSuccessful();
45+
46+
$this->assertResultsHasFailure(Failure::class, 'No catch block for the DivisionByZeroError exception found');
47+
}
48+
49+
public function testSuccessfulSolution(): void
50+
{
51+
$this->runExercise('solution-correct.php');
52+
53+
$this->assertVerifyWasSuccessful();
54+
}
55+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
try {
4+
} catch (DivisionByZeroError $e) {
5+
echo $e->getMessage() . "\n";
6+
}
7+
8+
echo match ($res = fdiv($argv[1], $argv[2])) {
9+
INF => 'Infinite',
10+
-INF => 'Minus infinite',
11+
default => $res
12+
};
13+
14+
echo "\n";
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
try {
4+
$argv[1] / $argv[2];
5+
} catch (Exception $e) {
6+
echo $e->getMessage() . "\n";
7+
}
8+
9+
echo match ($res = fdiv($argv[1], $argv[2])) {
10+
INF => 'Infinite',
11+
-INF => 'Minus infinite',
12+
default => $res
13+
};
14+
15+
echo "\n";
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
$argv[1] / $argv[2];
4+
5+
echo match ($res = fdiv($argv[1], $argv[2])) {
6+
INF => 'Infinite',
7+
-INF => 'Minus infinite',
8+
default => $res
9+
};
10+
11+
echo "\n";
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
try {
4+
$argv[1] / $argv[2];
5+
} catch (DivisionByZeroError $e) {
6+
echo $e->getMessage() . "\n";
7+
}
8+
9+
echo match ($res = fdiv($argv[1], $argv[2])) {
10+
INF => 'Infinite',
11+
-INF => 'Minus infinite',
12+
default => $res
13+
};
14+
15+
echo "\n";

0 commit comments

Comments
 (0)