19
19
use PhpSchool \PhpWorkshop \Exercise \ExerciseInterface ;
20
20
use PhpSchool \PhpWorkshop \Input \Input ;
21
21
use PhpSchool \PhpWorkshop \Output \OutputInterface ;
22
+ use PhpSchool \PhpWorkshop \Process \ProcessFactory ;
23
+ use PhpSchool \PhpWorkshop \Process \ProcessInput ;
22
24
use PhpSchool \PhpWorkshop \Result \Cgi \CgiResult ;
23
25
use PhpSchool \PhpWorkshop \Result \Cgi \RequestFailure ;
24
26
use PhpSchool \PhpWorkshop \Result \Cgi \GenericFailure ;
32
34
use Symfony \Component \Process \ExecutableFinder ;
33
35
use Symfony \Component \Process \Process ;
34
36
37
+ use function PHPStan \dumpType ;
38
+
35
39
/**
36
40
* The `CGI` runner. This runner executes solutions as if they were behind a web-server. They populate the `$_SERVER`,
37
41
* `$_GET` & `$_POST` super globals with information based of the request objects returned from the exercise.
38
42
*/
39
43
class CgiRunner implements ExerciseRunnerInterface
40
44
{
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
-
56
45
/**
57
46
* @var array<class-string>
58
47
*/
59
- private static $ requiredChecks = [
48
+ private static array $ requiredChecks = [
60
49
FileExistsCheck::class,
61
50
CodeExistsCheck::class,
62
51
PhpLintCheck::class,
@@ -68,26 +57,13 @@ class CgiRunner implements ExerciseRunnerInterface
68
57
* be available. It will check for it's existence in the system's $PATH variable or the same
69
58
* folder that the CLI php binary lives in.
70
59
*
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.
73
61
*/
74
62
public function __construct (
75
- CgiExercise $ exercise ,
76
- EventDispatcher $ eventDispatcher
63
+ private CgiExercise $ exercise ,
64
+ private EventDispatcher $ eventDispatcher ,
65
+ private ProcessFactory $ processFactory
77
66
) {
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 ;
91
67
}
92
68
93
69
/**
@@ -172,7 +148,7 @@ private function getHeaders(ResponseInterface $response): array
172
148
*/
173
149
private function executePhpFile (string $ fileName , RequestInterface $ request , string $ type ): ResponseInterface
174
150
{
175
- $ process = $ this ->getProcess ( $ fileName , $ request );
151
+ $ process = $ this ->getPhpProcess ( dirname ( $ fileName), basename ( $ fileName ) , $ request );
176
152
177
153
$ process ->start ();
178
154
$ this ->eventDispatcher ->dispatch (new CgiExecuteEvent (sprintf ('cgi.verify.%s.executing ' , $ type ), $ request ));
@@ -196,47 +172,38 @@ private function executePhpFile(string $fileName, RequestInterface $request, str
196
172
* @param RequestInterface $request
197
173
* @return Process
198
174
*/
199
- private function getProcess ( string $ fileName , RequestInterface $ request ): Process
175
+ private function getPhpProcess ( string $ workingDirectory , string $ fileName , RequestInterface $ request ): Process
200
176
{
201
- $ env = $ this ->getDefaultEnv ();
202
- $ env += [
177
+ $ env = [
203
178
'REQUEST_METHOD ' => $ request ->getMethod (),
204
179
'SCRIPT_FILENAME ' => $ fileName ,
205
- 'REDIRECT_STATUS ' => 302 ,
180
+ 'REDIRECT_STATUS ' => ' 302 ' ,
206
181
'QUERY_STRING ' => $ request ->getUri ()->getQuery (),
207
182
'REQUEST_URI ' => $ request ->getUri ()->getPath (),
208
183
'XDEBUG_MODE ' => 'off ' ,
209
184
];
210
185
211
- $ cgiBinary = sprintf (
212
- '%s -dalways_populate_raw_post_data=-1 -dhtml_errors=0 -dexpose_php=0 ' ,
213
- $ this ->phpLocation
214
- );
215
-
216
186
$ 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 ();
219
188
$ env ['CONTENT_TYPE ' ] = $ request ->getHeaderLine ('Content-Type ' );
220
189
221
190
foreach ($ request ->getHeaders () as $ name => $ values ) {
222
191
$ env [sprintf ('HTTP_%s ' , strtoupper ($ name ))] = implode (", " , $ values );
223
192
}
224
193
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
+ );
238
205
239
- return $ env ;
206
+ return $ this -> processFactory -> create ( $ processInput ) ;
240
207
}
241
208
242
209
/**
@@ -297,7 +264,11 @@ public function run(Input $input, OutputInterface $output): bool
297
264
$ event = $ this ->eventDispatcher ->dispatch (
298
265
new CgiExecuteEvent ('cgi.run.student-execute.pre ' , $ request )
299
266
);
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
+ );
301
272
302
273
$ process ->start ();
303
274
$ this ->eventDispatcher ->dispatch (
0 commit comments