Skip to content

Commit 935ea41

Browse files
committed
Initial exercise
1 parent f090d58 commit 935ea41

File tree

13 files changed

+235
-2
lines changed

13 files changed

+235
-2
lines changed

app/bootstrap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use PhpSchool\PHP8Appreciate\Exercise\PhpPromotion;
2929
use PhpSchool\PHP8Appreciate\Exercise\LordOfTheStrings;
3030
use PhpSchool\PHP8Appreciate\Exercise\TheReturnOfStatic;
31+
use PhpSchool\PHP8Appreciate\Exercise\ThrowAnExpression;
3132
use PhpSchool\PHP8Appreciate\Exercise\UniteTheTypes;
3233
use PhpSchool\PhpWorkshop\Application;
3334

@@ -43,6 +44,7 @@
4344
$app->addExercise(ASafeSpaceForNulls::class);
4445
$app->addExercise(AllMixedUp::class);
4546
$app->addExercise(TheReturnOfStatic::class);
47+
$app->addExercise(ThrowAnExpression::class);
4648

4749
$art = <<<ART
4850
_ __ _

app/config.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpSchool\PHP8Appreciate\Exercise\PhpPromotion;
1010
use PhpSchool\PHP8Appreciate\Exercise\LordOfTheStrings;
1111
use PhpSchool\PHP8Appreciate\Exercise\TheReturnOfStatic;
12+
use PhpSchool\PHP8Appreciate\Exercise\ThrowAnExpression;
1213
use PhpSchool\PHP8Appreciate\Exercise\UniteTheTypes;
1314
use Psr\Container\ContainerInterface;
1415

@@ -48,5 +49,8 @@
4849
},
4950
TheReturnOfStatic::class => function (ContainerInterface $c) {
5051
return new TheReturnOfStatic($c->get(PhpParser\Parser::class));
52+
},
53+
ThrowAnExpression::class => function (ContainerInterface $c) {
54+
return new ThrowAnExpression($c->get(PhpParser\Parser::class), $c->get(\Faker\Generator::class));
5155
}
5256
];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
if (str_starts_with($_SERVER['REQUEST_URI'], '/forbidden')) {
4+
echo 'Welcome!';
5+
} else {
6+
throw new InvalidArgumentException('Access denied!');
7+
}

exercises/throw-an-expression/problem/problem.md

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
echo str_starts_with($_SERVER['REQUEST_URI'], '/forbidden')
4+
? 'Welcome!'
5+
: throw new InvalidArgumentException('Access denied!');

src/Exercise/ThrowAnExpression.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace PhpSchool\PHP8Appreciate\Exercise;
4+
5+
use Faker\Generator as FakerGenerator;
6+
use GuzzleHttp\Psr7\Request;
7+
use PhpParser\Node\Expr\Ternary;
8+
use PhpParser\Node\Expr\Throw_;
9+
use PhpParser\Node\Stmt;
10+
use PhpParser\Node\Stmt\If_;
11+
use PhpParser\NodeFinder;
12+
use PhpParser\Parser;
13+
use PhpSchool\PhpWorkshop\Exercise\AbstractExercise;
14+
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
15+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
16+
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
17+
use PhpSchool\PhpWorkshop\Exercise\ProvidesInitialCode;
18+
use PhpSchool\PhpWorkshop\Exercise\SubmissionPatchable;
19+
use PhpSchool\PhpWorkshop\ExerciseCheck\SelfCheck;
20+
use PhpSchool\PhpWorkshop\Input\Input;
21+
use PhpSchool\PhpWorkshop\Patch;
22+
use PhpSchool\PhpWorkshop\Result\Failure;
23+
use PhpSchool\PhpWorkshop\Result\ResultInterface;
24+
use PhpSchool\PhpWorkshop\Result\Success;
25+
use PhpSchool\PhpWorkshop\Solution\SingleFileSolution;
26+
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
27+
28+
class ThrowAnExpression extends AbstractExercise implements
29+
ExerciseInterface,
30+
CgiExercise,
31+
SelfCheck,
32+
ProvidesInitialCode,
33+
SubmissionPatchable
34+
{
35+
public function __construct(private Parser $parser, private FakerGenerator $faker)
36+
{
37+
}
38+
39+
public function getName(): string
40+
{
41+
return 'Throw an Expression';
42+
}
43+
44+
public function getDescription(): string
45+
{
46+
return 'PHP 8\'s throw expression';
47+
}
48+
49+
public function getType(): ExerciseType
50+
{
51+
return ExerciseType::CGI();
52+
}
53+
54+
public function getRequests(): array
55+
{
56+
return [
57+
(new Request('GET', 'https://top-secret.com/forbidden')),
58+
(new Request('GET', 'https://top-secret.com/blog'))
59+
];
60+
}
61+
62+
public function check(Input $input): ResultInterface
63+
{
64+
/** @var array<Stmt> $statements */
65+
$statements = $this->parser->parse((string) file_get_contents($input->getRequiredArgument('program')));
66+
67+
/** @var If_|null $if */
68+
$if = (new NodeFinder())->findFirstInstanceOf($statements, If_::class);
69+
70+
if ($if) {
71+
return Failure::fromNameAndReason($this->getName(), 'If statement found');
72+
}
73+
74+
/** @var Ternary|null $ternary */
75+
$ternary = (new NodeFinder())->findFirstInstanceOf($statements, Ternary::class);
76+
77+
if ($ternary === null) {
78+
return Failure::fromNameAndReason($this->getName(), 'No ternary statement found');
79+
}
80+
81+
if (!$ternary->else instanceof Throw_) {
82+
return Failure::fromNameAndReason($this->getName(), 'Ternary does not make use of throw expression');
83+
}
84+
85+
return new Success($this->getName());
86+
}
87+
88+
public function getInitialCode(): SolutionInterface
89+
{
90+
return SingleFileSolution::fromFile(
91+
__DIR__ . '/../../exercises/throw-an-expression/initial/throw-an-expression.php'
92+
);
93+
}
94+
95+
public function getPatch(): Patch
96+
{
97+
return (new Patch())
98+
->withTransformer(new Patch\WrapInTryCatch(\InvalidArgumentException::class));
99+
}
100+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace PhpSchool\PHP8AppreciateTest\Exercise;
4+
5+
use PhpSchool\PHP8Appreciate\Exercise\ThrowAnExpression;
6+
use PhpSchool\PhpWorkshop\Application;
7+
use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure;
8+
use PhpSchool\PhpWorkshop\Result\Cgi\Success;
9+
use PhpSchool\PhpWorkshop\Result\Failure;
10+
use PhpSchool\PhpWorkshop\TestUtils\WorkshopExerciseTest;
11+
12+
class ThrowAnExpressionTest extends WorkshopExerciseTest
13+
{
14+
public function getExerciseClass(): string
15+
{
16+
return ThrowAnExpression::class;
17+
}
18+
19+
public function getApplication(): Application
20+
{
21+
return require __DIR__ . '/../../app/bootstrap.php';
22+
}
23+
24+
public function testThrowingWrongException(): void
25+
{
26+
$this->runExercise('wrong-exception.php');
27+
28+
$this->assertVerifyWasNotSuccessful();
29+
30+
$output = $this->getOutputResult();
31+
32+
self::assertInstanceOf(Success::class, $output->getResults()[0]);
33+
self::assertInstanceOf(RequestFailure::class, $output->getResults()[1]);
34+
35+
self::assertMatchesRegularExpression(
36+
'/Fatal error: Uncaught Exception: Access denied!/',
37+
$output->getResults()[1]->getActualOutput()
38+
);
39+
40+
$this->assertOutputWasIncorrect();
41+
}
42+
43+
public function testUsingIfStatement(): void
44+
{
45+
$this->runExercise('if-statement.php');
46+
47+
$this->assertVerifyWasNotSuccessful();
48+
49+
$this->assertResultsHasFailure(Failure::class, 'If statement found');
50+
51+
$this->assertOutputWasCorrect();
52+
}
53+
54+
public function testUsingNoTernary(): void
55+
{
56+
$this->runExercise('no-ternary.php');
57+
58+
$this->assertVerifyWasNotSuccessful();
59+
60+
$this->assertResultsHasFailure(Failure::class, 'No ternary statement found');
61+
62+
$this->assertOutputWasIncorrect();
63+
}
64+
65+
public function testNoThrowExpression(): void
66+
{
67+
$this->runExercise('no-throw-expression.php');
68+
69+
$this->assertVerifyWasNotSuccessful();
70+
71+
$this->assertResultsHasFailure(Failure::class, 'Ternary does not make use of throw expression');
72+
73+
$this->assertOutputWasCorrect();
74+
}
75+
76+
public function testSuccessfulSolution(): void
77+
{
78+
$this->runExercise('solution-correct.php');
79+
80+
$this->assertVerifyWasSuccessful();
81+
$this->assertOutputWasCorrect();
82+
}
83+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
<?php
22

3+
date_default_timezone_set("Europe/London");
4+
error_reporting(E_ALL);
5+
ini_set("display_errors", "1");
36
echo match ($argv[1]) {
47
'enter' => 13 + 10,
58
'up' => 119 + 10,
69
'down' => 73 + 10,
710
'esc' => 27 + 10,
8-
default => 0 + 10
9-
};
11+
default => 0 + 10,
12+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
if (str_starts_with($_SERVER['REQUEST_URI'], '/forbidden')) {
4+
echo 'Welcome!';
5+
} else {
6+
throw new InvalidArgumentException('Access denied!');
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
echo 'Welcome!';
4+

0 commit comments

Comments
 (0)