Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit add5a64

Browse files
Use new process factory
1 parent 6dbd332 commit add5a64

File tree

11 files changed

+116
-217
lines changed

11 files changed

+116
-217
lines changed

‎app/config.php‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
use PhpSchool\PhpWorkshop\Output\OutputInterface;
7272
use PhpSchool\PhpWorkshop\Output\StdOutput;
7373
use PhpSchool\PhpWorkshop\Patch;
74+
use PhpSchool\PhpWorkshop\Process\HostProcessFactory;
75+
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
7476
use PhpSchool\PhpWorkshop\Result\Cgi\CgiResult;
7577
use PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure as CgiGenericFailure;
7678
use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure as CgiRequestFailure;
@@ -187,12 +189,16 @@
187189
//Exercise Runners
188190
RunnerManager::class => function (ContainerInterface $c) {
189191
$manager = new RunnerManager();
190-
$manager->addFactory(new CliRunnerFactory($c->get(EventDispatcher::class)));
191-
$manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class)));
192+
$manager->addFactory(new CliRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
193+
$manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
192194
$manager->addFactory(new CustomVerifyingRunnerFactory());
193195
return $manager;
194196
},
195197

198+
ProcessFactory::class => function (ContainerInterface $c) {
199+
return new HostProcessFactory();
200+
},
201+
196202
//commands
197203
MenuCommand::class => function (ContainerInterface $c) {
198204
return new MenuCommand($c->get('menu'));

‎src/ExerciseRunner/CgiRunner.php‎

Lines changed: 31 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
2020
use PhpSchool\PhpWorkshop\Input\Input;
2121
use PhpSchool\PhpWorkshop\Output\OutputInterface;
22+
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
23+
use PhpSchool\PhpWorkshop\Process\ProcessInput;
2224
use PhpSchool\PhpWorkshop\Result\Cgi\CgiResult;
2325
use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure;
2426
use PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure;
@@ -32,31 +34,18 @@
3234
use Symfony\Component\Process\ExecutableFinder;
3335
use Symfony\Component\Process\Process;
3436

37+
use function PHPStan\dumpType;
38+
3539
/**
3640
* The `CGI` runner. This runner executes solutions as if they were behind a web-server. They populate the `$_SERVER`,
3741
* `$_GET` & `$_POST` super globals with information based of the request objects returned from the exercise.
3842
*/
3943
class CgiRunner implements ExerciseRunnerInterface
4044
{
41-
/**
42-
* @var CgiExercise&ExerciseInterface
43-
*/
44-
private $exercise;
45-
46-
/**
47-
* @var EventDispatcher
48-
*/
49-
private $eventDispatcher;
50-
51-
/**
52-
* @var string
53-
*/
54-
private $phpLocation;
55-
5645
/**
5746
* @var array<class-string>
5847
*/
59-
private static $requiredChecks = [
48+
private static array$requiredChecks = [
6049
FileExistsCheck::class,
6150
CodeExistsCheck::class,
6251
PhpLintCheck::class,
@@ -68,26 +57,13 @@ class CgiRunner implements ExerciseRunnerInterface
6857
* be available. It will check for it's existence in the system's $PATH variable or the same
6958
* folder that the CLI php binary lives in.
7059
*
71-
* @param CgiExercise $exercise The exercise to be invoked.
72-
* @param EventDispatcher $eventDispatcher The event dispatcher.
60+
* @param CgiExercise&ExerciseInterface $exercise The exercise to be invoked.
7361
*/
7462
public function __construct(
75-
CgiExercise $exercise,
76-
EventDispatcher $eventDispatcher
63+
private CgiExercise $exercise,
64+
private EventDispatcher $eventDispatcher,
65+
private ProcessFactory $processFactory
7766
) {
78-
$php = (new ExecutableFinder())->find('php-cgi');
79-
80-
if (null === $php) {
81-
throw new RuntimeException(
82-
'Could not load php-cgi binary. Please install php using your package manager.'
83-
);
84-
}
85-
86-
$this->phpLocation = $php;
87-
88-
/** @var CgiExercise&ExerciseInterface $exercise */
89-
$this->eventDispatcher = $eventDispatcher;
90-
$this->exercise = $exercise;
9167
}
9268

9369
/**
@@ -172,7 +148,7 @@ private function getHeaders(ResponseInterface $response): array
172148
*/
173149
private function executePhpFile(string $fileName, RequestInterface $request, string $type): ResponseInterface
174150
{
175-
$process = $this->getProcess($fileName, $request);
151+
$process = $this->getPhpProcess(dirname($fileName), basename($fileName), $request);
176152

177153
$process->start();
178154
$this->eventDispatcher->dispatch(new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $request));
@@ -196,47 +172,38 @@ private function executePhpFile(string $fileName, RequestInterface $request, str
196172
* @param RequestInterface $request
197173
* @return Process
198174
*/
199-
private function getProcess(string $fileName, RequestInterface $request): Process
175+
private function getPhpProcess(string$workingDirectory, string $fileName, RequestInterface $request): Process
200176
{
201-
$env = $this->getDefaultEnv();
202-
$env += [
177+
$env = [
203178
'REQUEST_METHOD' => $request->getMethod(),
204179
'SCRIPT_FILENAME' => $fileName,
205-
'REDIRECT_STATUS' => 302,
180+
'REDIRECT_STATUS' => '302',
206181
'QUERY_STRING' => $request->getUri()->getQuery(),
207182
'REQUEST_URI' => $request->getUri()->getPath(),
208183
'XDEBUG_MODE' => 'off',
209184
];
210185

211-
$cgiBinary = sprintf(
212-
'%s -dalways_populate_raw_post_data=-1 -dhtml_errors=0 -dexpose_php=0',
213-
$this->phpLocation
214-
);
215-
216186
$content = $request->getBody()->__toString();
217-
$cmd = sprintf('echo %s | %s', escapeshellarg($content), $cgiBinary);
218-
$env['CONTENT_LENGTH'] = $request->getBody()->getSize();
187+
$env['CONTENT_LENGTH'] = (string) $request->getBody()->getSize();
219188
$env['CONTENT_TYPE'] = $request->getHeaderLine('Content-Type');
220189

221190
foreach ($request->getHeaders() as $name => $values) {
222191
$env[sprintf('HTTP_%s', strtoupper($name))] = implode(", ", $values);
223192
}
224193

225-
return Process::fromShellCommandline($cmd, null, $env, null, 10);
226-
}
227-
228-
/**
229-
* We need to reset env entirely, because Symfony inherits it. We do that by setting all
230-
* the current env vars to false
231-
*
232-
* @return array<string, false>
233-
*/
234-
private function getDefaultEnv(): array
235-
{
236-
$env = array_map(fn () => false, $_ENV);
237-
$env + array_map(fn () => false, $_SERVER);
194+
$processInput = new ProcessInput(
195+
'php-cgi',
196+
[
197+
'-dalways_populate_raw_post_data=-1',
198+
'-dhtml_errors=0',
199+
'-dexpose_php=0',
200+
],
201+
$workingDirectory,
202+
$env,
203+
$content
204+
);
238205

239-
return $env;
206+
return $this->processFactory->create($processInput);
240207
}
241208

242209
/**
@@ -297,7 +264,11 @@ public function run(Input $input, OutputInterface $output): bool
297264
$event = $this->eventDispatcher->dispatch(
298265
new CgiExecuteEvent('cgi.run.student-execute.pre', $request)
299266
);
300-
$process = $this->getProcess($input->getRequiredArgument('program'), $event->getRequest());
267+
$process = $this->getPhpProcess(
268+
dirname($input->getRequiredArgument('program')),
269+
$input->getRequiredArgument('program'),
270+
$event->getRequest()
271+
);
301272

302273
$process->start();
303274
$this->eventDispatcher->dispatch(

‎src/ExerciseRunner/CliRunner.php‎

Lines changed: 44 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
1919
use PhpSchool\PhpWorkshop\Input\Input;
2020
use PhpSchool\PhpWorkshop\Output\OutputInterface;
21+
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
22+
use PhpSchool\PhpWorkshop\Process\ProcessInput;
2123
use PhpSchool\PhpWorkshop\Result\Cli\RequestFailure;
2224
use PhpSchool\PhpWorkshop\Result\Cli\CliResult;
2325
use PhpSchool\PhpWorkshop\Result\Cli\GenericFailure;
2426
use PhpSchool\PhpWorkshop\Result\Cli\Success;
2527
use PhpSchool\PhpWorkshop\Result\Cli\ResultInterface as CliResultInterface;
2628
use PhpSchool\PhpWorkshop\Result\ResultInterface;
2729
use PhpSchool\PhpWorkshop\Utils\ArrayObject;
30+
use PhpSchool\PhpWorkshop\Utils\Collection;
2831
use RuntimeException;
2932
use Symfony\Component\Process\ExecutableFinder;
3033
use Symfony\Component\Process\Process;
@@ -39,25 +42,10 @@
3942
*/
4043
class CliRunner implements ExerciseRunnerInterface
4144
{
42-
/**
43-
* @var CliExercise&ExerciseInterface
44-
*/
45-
private $exercise;
46-
47-
/**
48-
* @var EventDispatcher
49-
*/
50-
private $eventDispatcher;
51-
52-
/**
53-
* @var string
54-
*/
55-
private $phpLocation;
56-
5745
/**
5846
* @var array<class-string>
5947
*/
60-
private static $requiredChecks = [
48+
private static array$requiredChecks = [
6149
FileExistsCheck::class,
6250
CodeExistsCheck::class,
6351
PhpLintCheck::class,
@@ -67,24 +55,13 @@ class CliRunner implements ExerciseRunnerInterface
6755
/**
6856
* Requires the exercise instance and an event dispatcher.
6957
*
70-
* @param CliExercise $exercise The exercise to be invoked.
71-
* @param EventDispatcher $eventDispatcher The event dispatcher.
58+
* @param CliExercise&ExerciseInterface $exercise The exercise to be invoked.
7259
*/
73-
public function __construct(CliExercise $exercise, EventDispatcher $eventDispatcher)
74-
{
75-
$php = (new ExecutableFinder())->find('php');
76-
77-
if (null === $php) {
78-
throw new RuntimeException(
79-
'Could not load php binary. Please install php using your package manager.'
80-
);
81-
}
82-
83-
$this->phpLocation = $php;
84-
85-
/** @var CliExercise&ExerciseInterface $exercise */
86-
$this->eventDispatcher = $eventDispatcher;
87-
$this->exercise = $exercise;
60+
public function __construct(
61+
private CliExercise $exercise,
62+
private EventDispatcher $eventDispatcher,
63+
private ProcessFactory $processFactory
64+
) {
8865
}
8966

9067
/**
@@ -105,59 +82,6 @@ public function getRequiredChecks(): array
10582
return self::$requiredChecks;
10683
}
10784

108-
/**
109-
* @param string $fileName
110-
* @param ArrayObject<int, string> $args
111-
* @param string $type
112-
* @return string
113-
*/
114-
private function executePhpFile(string $fileName, ArrayObject $args, string $type): string
115-
{
116-
$process = $this->getPhpProcess($fileName, $args);
117-
118-
$process->start();
119-
$this->eventDispatcher->dispatch(new CliExecuteEvent(sprintf('cli.verify.%s.executing', $type), $args));
120-
$process->wait();
121-
122-
if (!$process->isSuccessful()) {
123-
throw CodeExecutionException::fromProcess($process);
124-
}
125-
126-
return $process->getOutput();
127-
}
128-
129-
/**
130-
* @param string $fileName
131-
* @param ArrayObject<int, string> $args
132-
*
133-
* @return Process
134-
*/
135-
private function getPhpProcess(string $fileName, ArrayObject $args): Process
136-
{
137-
return new Process(
138-
$args->prepend($fileName)->prepend($this->phpLocation)->getArrayCopy(),
139-
dirname($fileName),
140-
$this->getDefaultEnv() + ['XDEBUG_MODE' => 'off'],
141-
null,
142-
10
143-
);
144-
}
145-
146-
/**
147-
* We need to reset env entirely, because Symfony inherits it. We do that by setting all
148-
* the current env vars to false
149-
*
150-
* @return array<string, false>
151-
*/
152-
private function getDefaultEnv(): array
153-
{
154-
$env = array_map(fn () => false, $_ENV);
155-
$env + array_map(fn () => false, $_SERVER);
156-
157-
return $env;
158-
}
159-
160-
16185
/**
16286
* Verifies a solution by invoking PHP from the CLI passing the arguments gathered from the exercise
16387
* as command line arguments to PHP.
@@ -272,7 +196,12 @@ public function run(Input $input, OutputInterface $output): bool
272196

273197
$args = $event->getArgs();
274198

275-
$process = $this->getPhpProcess($input->getRequiredArgument('program'), $args);
199+
$process = $this->getPhpProcess(
200+
dirname($input->getRequiredArgument('program')),
201+
$input->getRequiredArgument('program'),
202+
$args
203+
);
204+
276205
$process->start();
277206
$this->eventDispatcher->dispatch(
278207
new CliExecuteEvent('cli.run.student.executing', $args, ['output' => $output])
@@ -296,4 +225,32 @@ public function run(Input $input, OutputInterface $output): bool
296225
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.run.finish', $this->exercise, $input));
297226
return $success;
298227
}
228+
229+
/**
230+
* @param ArrayObject<int, string> $args
231+
*/
232+
private function executePhpFile(string $fileName, ArrayObject $args, string $type): string
233+
{
234+
$process = $this->getPhpProcess(dirname($fileName), $fileName, $args);
235+
236+
$process->start();
237+
$this->eventDispatcher->dispatch(new CliExecuteEvent(sprintf('cli.verify.%s.executing', $type), $args));
238+
$process->wait();
239+
240+
if (!$process->isSuccessful()) {
241+
throw CodeExecutionException::fromProcess($process);
242+
}
243+
244+
return $process->getOutput();
245+
}
246+
247+
/**
248+
* @param ArrayObject<int, string> $args
249+
*/
250+
private function getPhpProcess(string $workingDirectory, string $fileName, ArrayObject $args): Process
251+
{
252+
return $this->processFactory->create(
253+
new ProcessInput('php', [$fileName, ...$args->getArrayCopy()], $workingDirectory, [])
254+
);
255+
}
299256
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /