Skip to content

Commit bb84725

Browse files
authored
Merge pull request #289 from php-school/05-07-use_context_objects_in_dispatcher_and_runners
Use context objects in dispatcher and runners
2 parents 6515c60 + b86f356 commit bb84725

17 files changed

+265
-281
lines changed

Diff for: app/config.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
3939
use PhpSchool\PhpWorkshop\ExerciseRenderer;
4040
use PhpSchool\PhpWorkshop\ExerciseRepository;
41+
use PhpSchool\PhpWorkshop\ExerciseRunner\EnvironmentManager;
4142
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CgiRunnerFactory;
4243
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CliRunnerFactory;
4344
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CustomVerifyingRunnerFactory;
44-
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\ServerRunnerFactory;
4545
use PhpSchool\PhpWorkshop\ExerciseRunner\RunnerManager;
4646
use PhpSchool\PhpWorkshop\Factory\EventDispatcherFactory;
4747
use PhpSchool\PhpWorkshop\Factory\MenuFactory;
@@ -134,7 +134,7 @@
134134
$c->get(RunnerManager::class),
135135
$c->get(ResultAggregator::class),
136136
$c->get(EventDispatcher::class),
137-
$c->get(CheckRepository::class)
137+
$c->get(CheckRepository::class),
138138
);
139139
},
140140
ResultAggregator::class => create(ResultAggregator::class),
@@ -193,8 +193,16 @@
193193
//Exercise Runners
194194
RunnerManager::class => function (ContainerInterface $c) {
195195
$manager = new RunnerManager();
196-
$manager->addFactory(new CliRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
197-
$manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
196+
$manager->addFactory(new CliRunnerFactory(
197+
$c->get(EventDispatcher::class),
198+
$c->get(ProcessFactory::class),
199+
$c->get(EnvironmentManager::class)
200+
));
201+
$manager->addFactory(new CgiRunnerFactory(
202+
$c->get(EventDispatcher::class),
203+
$c->get(ProcessFactory::class),
204+
$c->get(EnvironmentManager::class)
205+
));
198206
$manager->addFactory(new CustomVerifyingRunnerFactory());
199207
return $manager;
200208
},

Diff for: src/ExerciseDispatcher.php

+17-35
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PhpSchool\PhpWorkshop\Exception\ExerciseNotConfiguredException;
1515
use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException;
1616
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
17+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
1718
use PhpSchool\PhpWorkshop\ExerciseRunner\RunnerManager;
1819
use PhpSchool\PhpWorkshop\Input\Input;
1920
use PhpSchool\PhpWorkshop\Output\OutputInterface;
@@ -29,49 +30,26 @@ class ExerciseDispatcher
2930
/**
3031
* @var array<SimpleCheckInterface>
3132
*/
32-
private $checksToRunBefore = [];
33+
private array $checksToRunBefore = [];
3334

3435
/**
3536
* @var array<SimpleCheckInterface>
3637
*/
37-
private $checksToRunAfter = [];
38+
private array $checksToRunAfter = [];
3839

39-
/**
40-
* @var RunnerManager
41-
*/
42-
private $runnerManager;
43-
44-
/**
45-
* @var ResultAggregator
46-
*/
47-
private $results;
48-
49-
/**
50-
* @var EventDispatcher
51-
*/
52-
private $eventDispatcher;
53-
54-
/**
55-
* @var CheckRepository
56-
*/
57-
private $checkRepository;
5840

5941
/**
6042
* @param RunnerManager $runnerManager Factory capable of building an exercise runner based on the exercise type.
61-
* @param ResultAggregator $resultAggregator
43+
* @param ResultAggregator $results
6244
* @param EventDispatcher $eventDispatcher
6345
* @param CheckRepository $checkRepository
6446
*/
6547
public function __construct(
66-
RunnerManager $runnerManager,
67-
ResultAggregator $resultAggregator,
68-
EventDispatcher $eventDispatcher,
69-
CheckRepository $checkRepository
48+
private RunnerManager $runnerManager,
49+
private ResultAggregator $results,
50+
private EventDispatcher $eventDispatcher,
51+
private CheckRepository $checkRepository,
7052
) {
71-
$this->runnerManager = $runnerManager;
72-
$this->results = $resultAggregator;
73-
$this->eventDispatcher = $eventDispatcher;
74-
$this->checkRepository = $checkRepository;
7553
}
7654

7755
/**
@@ -129,6 +107,8 @@ public function requireCheck(string $requiredCheck): void
129107
*/
130108
public function verify(ExerciseInterface $exercise, Input $input): ResultAggregator
131109
{
110+
$context = ExecutionContext::fromInputAndExercise($input, $exercise);
111+
132112
$runner = $this->runnerManager->getRunner($exercise);
133113

134114
$exercise->defineListeners($this->eventDispatcher);
@@ -143,7 +123,7 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
143123
$this->validateChecks($this->checksToRunAfter, $exercise);
144124

145125
foreach ($this->checksToRunBefore as $check) {
146-
$this->results->add($check->check($exercise, $input));
126+
$this->results->add($check->check($context->getExercise(), $context->getInput()));
147127

148128
if (!$this->results->isSuccessful()) {
149129
return $this->results;
@@ -153,13 +133,13 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
153133
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.pre.execute', $exercise, $input));
154134

155135
try {
156-
$this->results->add($runner->verify($input));
136+
$this->results->add($runner->verify($context));
157137
} finally {
158138
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.execute', $exercise, $input));
159139
}
160140

161141
foreach ($this->checksToRunAfter as $check) {
162-
$this->results->add($check->check($exercise, $input));
142+
$this->results->add($check->check($context->getExercise(), $context->getInput()));
163143
}
164144

165145
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.check', $exercise, $input));
@@ -181,11 +161,13 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
181161
*/
182162
public function run(ExerciseInterface $exercise, Input $input, OutputInterface $output): bool
183163
{
164+
$context = ExecutionContext::fromInputAndExercise($input, $exercise);
165+
184166
$exercise->defineListeners($this->eventDispatcher);
185167

186168
/** @var PhpLintCheck $lint */
187169
$lint = $this->checkRepository->getByClass(PhpLintCheck::class);
188-
$result = $lint->check($exercise, $input);
170+
$result = $lint->check($context->getExercise(), $context->getInput());
189171

190172
if ($result instanceof FailureInterface) {
191173
throw CouldNotRunException::fromFailure($result);
@@ -196,7 +178,7 @@ public function run(ExerciseInterface $exercise, Input $input, OutputInterface $
196178
try {
197179
$exitStatus = $this->runnerManager
198180
->getRunner($exercise)
199-
->run($input, $output);
181+
->run($context, $output);
200182
} finally {
201183
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('run.finish', $exercise, $input));
202184
}

Diff for: src/ExerciseRunner/CgiRunner.php

+40-28
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PhpSchool\PhpWorkshop\Exception\SolutionExecutionException;
1919
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
2020
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
21+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
2122
use PhpSchool\PhpWorkshop\Input\Input;
2223
use PhpSchool\PhpWorkshop\Output\OutputInterface;
2324
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
@@ -63,7 +64,8 @@ class CgiRunner implements ExerciseRunnerInterface
6364
public function __construct(
6465
private CgiExercise $exercise,
6566
private EventDispatcher $eventDispatcher,
66-
private ProcessFactory $processFactory
67+
private ProcessFactory $processFactory,
68+
private EnvironmentManager $environmentManager
6769
) {
6870
}
6971

@@ -99,37 +101,42 @@ public function getRequiredChecks(): array
99101
* * cgi.verify.student.executing
100102
* * cgi.verify.student-execute.fail (if the student's solution fails to execute)
101103
*
102-
* @param Input $input The command line arguments passed to the command.
104+
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
103105
* @return CgiResult The result of the check.
104106
*/
105-
public function verify(Input $input): ResultInterface
107+
public function verify(ExecutionContext $context): ResultInterface
106108
{
107109
$scenario = $this->exercise->defineTestScenario();
108110

109-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $input));
111+
$this->environmentManager->prepareStudent($context, $scenario);
112+
$this->environmentManager->prepareReference($context, $scenario);
113+
114+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $context->getInput()));
110115

111116
$result = new CgiResult(
112117
array_map(
113-
function (RequestInterface $request) use ($input) {
114-
return $this->doVerify($request, $input);
118+
function (RequestInterface $request) use ($context) {
119+
return $this->doVerify($request, $context);
115120
},
116121
$scenario->getExecutions()
117122
)
118123
);
119-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $input));
124+
125+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $context->getInput()));
120126
return $result;
121127
}
122128

123-
private function doVerify(RequestInterface $request, Input $input): CgiResultInterface
129+
private function doVerify(RequestInterface $request, ExecutionContext $context): CgiResultInterface
124130
{
125131
try {
126132
/** @var CgiExecuteEvent $event */
127133
$event = $this->eventDispatcher->dispatch(
128-
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $input, $request)
134+
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $context->getInput(), $request)
129135
);
130136
$solutionResponse = $this->executePhpFile(
131-
$input,
132-
$this->exercise->getSolution()->getEntryPoint()->getAbsolutePath(),
137+
$context,
138+
$context->getReferenceExecutionDirectory(),
139+
$this->exercise->getSolution()->getEntryPoint()->getRelativePath(),
133140
$event->getRequest(),
134141
'reference'
135142
);
@@ -138,7 +145,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
138145
new CgiExecuteEvent(
139146
'cgi.verify.reference-execute.fail',
140147
$this->exercise,
141-
$input,
148+
$context->getInput(),
142149
$request,
143150
['exception' => $e]
144151
)
@@ -149,11 +156,12 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
149156
try {
150157
/** @var CgiExecuteEvent $event */
151158
$event = $this->eventDispatcher->dispatch(
152-
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $input, $request)
159+
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $context->getInput(), $request)
153160
);
154161
$userResponse = $this->executePhpFile(
155-
$input,
156-
$input->getRequiredArgument('program'),
162+
$context,
163+
$context->getStudentExecutionDirectory(),
164+
$context->getEntryPoint(),
157165
$event->getRequest(),
158166
'student'
159167
);
@@ -162,7 +170,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
162170
new CgiExecuteEvent(
163171
'cgi.verify.student-execute.fail',
164172
$this->exercise,
165-
$input,
173+
$context->getInput(),
166174
$request,
167175
['exception' => $e]
168176
)
@@ -202,16 +210,17 @@ private function getHeaders(ResponseInterface $response): array
202210
* @return ResponseInterface
203211
*/
204212
private function executePhpFile(
205-
Input $input,
213+
ExecutionContext $context,
214+
string $workingDirectory,
206215
string $fileName,
207216
RequestInterface $request,
208217
string $type
209218
): ResponseInterface {
210-
$process = $this->getPhpProcess(dirname($fileName), basename($fileName), $request);
219+
$process = $this->getPhpProcess($workingDirectory, $fileName, $request);
211220

212221
$process->start();
213222
$this->eventDispatcher->dispatch(
214-
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $input, $request)
223+
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $context->getInput(), $request)
215224
);
216225
$process->wait();
217226

@@ -280,25 +289,27 @@ private function getPhpProcess(string $workingDirectory, string $fileName, Reque
280289
* * cgi.run.student-execute.pre
281290
* * cgi.run.student.executing
282291
*
283-
* @param Input $input The command line arguments passed to the command.
292+
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
284293
* @param OutputInterface $output A wrapper around STDOUT.
285294
* @return bool If the solution was successfully executed, eg. exit code was 0.
286295
*/
287-
public function run(Input $input, OutputInterface $output): bool
296+
public function run(ExecutionContext $context, OutputInterface $output): bool
288297
{
289298
$scenario = $this->exercise->defineTestScenario();
290299

291-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $input));
300+
$this->environmentManager->prepareStudent($context, $scenario);
301+
302+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $context->getInput()));
292303

293304
$success = true;
294305
foreach ($scenario->getExecutions() as $i => $request) {
295306
/** @var CgiExecuteEvent $event */
296307
$event = $this->eventDispatcher->dispatch(
297-
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $input, $request)
308+
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $context->getInput(), $request)
298309
);
299310
$process = $this->getPhpProcess(
300-
dirname($input->getRequiredArgument('program')),
301-
$input->getRequiredArgument('program'),
311+
$context->getStudentExecutionDirectory(),
312+
$context->getEntryPoint(),
302313
$event->getRequest()
303314
);
304315

@@ -307,7 +318,7 @@ public function run(Input $input, OutputInterface $output): bool
307318
new CgiExecuteEvent(
308319
'cgi.run.student.executing',
309320
$this->exercise,
310-
$input,
321+
$context->getInput(),
311322
$request,
312323
['output' => $output]
313324
)
@@ -324,10 +335,11 @@ public function run(Input $input, OutputInterface $output): bool
324335
$output->lineBreak();
325336

326337
$this->eventDispatcher->dispatch(
327-
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $input, $request)
338+
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $context->getInput(), $request)
328339
);
329340
}
330-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $input));
341+
342+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $context->getInput()));
331343
return $success;
332344
}
333345
}

0 commit comments

Comments
 (0)