Skip to content

Commit 5ea074b

Browse files
authored
Merge pull request #25 from php-school/fdiv
2 parents 9b0e664 + 7fbe03d commit 5ea074b

File tree

10 files changed

+281
-0
lines changed

10 files changed

+281
-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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Write a program that accepts two floating point numbers as arguments. The first number is the dividend, and the second is the divisor. The divisor may be zero.
2+
3+
First, divide the numbers using the traditional binary operator (`/`). Make sure to wrap it in a try/catch statement because the operator can throw a `DivisionByZeroError` error.
4+
5+
In the case of an exception, you should print the exception message followed by a new line.
6+
7+
Second, use the `fdiv` function with the same numbers. There are a few different return values you can expect from `fdiv` based on the values you pass to it.
8+
9+
Based on those values you should print a specific message followed by a new line:
10+
11+
* INF -> print "Infinite"
12+
* -INF -> print "Minus infinite"
13+
* A valid float -> print the number, rounding it to three decimal places.
14+
15+
`fdiv` will return INF (which is a PHP constant) if you attempt to divide a positive number by zero.
16+
`fdiv` will return -INF if you attempt to divide a negative number by zero.
17+
`fdiv` will return a valid float if your divisor is greater than zero.
18+
19+
20+
### The advantages of the fdiv function
21+
22+
* No exceptions are thrown if you divide by zero
23+
24+
----------------------------------------------------------------------
25+
## HINTS
26+
27+
Documentation on the fdiv function can be found by pointing your browser here:
28+
[https://www.php.net/manual/en/function.fdiv.php]()
29+
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 => round($res, 2)
13+
};
14+
15+
echo "\n";

src/Exercise/InfiniteDivisions.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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->randomFloat(3, 10, 100),
62+
'0'
63+
],
64+
[
65+
(string) $this->faker->randomFloat(3, 10, 100),
66+
(string) $this->faker->randomFloat(3, 0, 10)
67+
]
68+
];
69+
}
70+
71+
public function getRequiredFunctions(): array
72+
{
73+
return ['fdiv', 'round'];
74+
}
75+
76+
public function getBannedFunctions(): array
77+
{
78+
return [];
79+
}
80+
81+
public function check(Input $input): ResultInterface
82+
{
83+
/** @var array<Stmt> $statements */
84+
$statements = $this->parser->parse((string) file_get_contents($input->getRequiredArgument('program')));
85+
86+
$finder = new NodeFinder();
87+
88+
/** @var Stmt\TryCatch|null $tryCatch */
89+
$tryCatch = $finder->findFirstInstanceOf($statements, Stmt\TryCatch::class);
90+
91+
if (!$tryCatch) {
92+
return new Failure($this->getName(), 'No try/catch statement could be found');
93+
}
94+
95+
/** @var Div|null $divOp */
96+
$divOp = $finder->findFirstInstanceOf($tryCatch->stmts, Div::class);
97+
98+
if (!$divOp) {
99+
return new Failure($this->getName(), 'No division operation could be found in the try block');
100+
}
101+
102+
/** @var Catch_|null $catch */
103+
$catch = $finder->findFirst($tryCatch->catches, function (Node $node) {
104+
if ($node instanceof Catch_) {
105+
return in_array(
106+
DivisionByZeroError::class,
107+
array_map(fn (Name $n) => $n->toString(), $node->types),
108+
true
109+
);
110+
}
111+
112+
return false;
113+
});
114+
115+
if (!$catch) {
116+
return new Failure($this->getName(), 'No catch block for the DivisionByZeroError exception found');
117+
}
118+
119+
return new Success($this->getName());
120+
}
121+
}
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 => round($res, 2)
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 => round($res, 2)
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 => round($res, 2)
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 => round($res, 2)
13+
};
14+
15+
echo "\n";

0 commit comments

Comments
 (0)