@@ -22,8 +22,12 @@ class_exists(ClosureResolver::class);
22
22
/**
23
23
* A runtime to do bare-metal PHP without using superglobals.
24
24
*
25
- * One option named "debug" is supported; it toggles displaying errors
26
- * and defaults to the "APP_ENV" environment variable.
25
+ * It supports the following options:
26
+ * - "debug" toggles displaying errors and defaults
27
+ * to the "APP_DEBUG" environment variable;
28
+ * - "runtimes" maps types to a GenericRuntime implementation
29
+ * that knows how to deal with each of them;
30
+ * - "error_handler" defines the class to use to handle PHP errors.
27
31
*
28
32
* The app-callable can declare arguments among either:
29
33
* - "array $context" to get a local array similar to $_SERVER;
@@ -42,42 +46,48 @@ class_exists(ClosureResolver::class);
42
46
*/
43
47
class GenericRuntime implements RuntimeInterface
44
48
{
45
- private $ debug ;
49
+ protected $ options ;
46
50
47
51
/**
48
52
* @param array {
49
53
* debug?: ?bool,
54
+ * runtimes?: ?array,
55
+ * error_handler?: string|false,
50
56
* } $options
51
57
*/
52
58
public function __construct (array $ options = [])
53
59
{
54
- $ this -> debug = $ options ['debug ' ] ?? $ _SERVER ['APP_DEBUG ' ] ?? $ _ENV ['APP_DEBUG ' ] ?? true ;
60
+ $ debug = $ options ['debug ' ] ?? $ _SERVER ['APP_DEBUG ' ] ?? $ _ENV ['APP_DEBUG ' ] ?? true ;
55
61
56
- if (!\is_bool ($ this -> debug )) {
57
- $ this -> debug = filter_var ($ this -> debug , \FILTER_VALIDATE_BOOLEAN );
62
+ if (!\is_bool ($ debug )) {
63
+ $ debug = filter_var ($ debug , \FILTER_VALIDATE_BOOLEAN );
58
64
}
59
65
60
- if ($ this ->debug ) {
66
+ if ($ debug ) {
67
+ umask (0000 );
61
68
$ _SERVER ['APP_DEBUG ' ] = $ _ENV ['APP_DEBUG ' ] = '1 ' ;
62
- $ errorHandler = new BasicErrorHandler ($ this ->debug );
63
- set_error_handler ($ errorHandler );
69
+
70
+ if (false !== $ errorHandler = ($ options ['error_handler ' ] ?? BasicErrorHandler::class)) {
71
+ $ errorHandler ::register ($ debug );
72
+ $ options ['error_handler ' ] = false ;
73
+ }
64
74
} else {
65
75
$ _SERVER ['APP_DEBUG ' ] = $ _ENV ['APP_DEBUG ' ] = '0 ' ;
66
76
}
77
+
78
+ $ this ->options = $ options ;
67
79
}
68
80
69
81
/**
70
82
* {@inheritdoc}
71
83
*/
72
- public function getResolver (callable $ callable ): ResolverInterface
84
+ public function getResolver (callable $ callable, \ ReflectionFunction $ reflector = null ): ResolverInterface
73
85
{
74
86
if (!$ callable instanceof \Closure) {
75
87
$ callable = \Closure::fromCallable ($ callable );
76
88
}
77
89
78
- $ function = new \ReflectionFunction ($ callable );
79
- $ parameters = $ function ->getParameters ();
80
-
90
+ $ parameters = ($ reflector ?? new \ReflectionFunction ($ callable ))->getParameters ();
81
91
$ arguments = function () use ($ parameters ) {
82
92
$ arguments = [];
83
93
@@ -95,7 +105,7 @@ public function getResolver(callable $callable): ResolverInterface
95
105
return $ arguments ;
96
106
};
97
107
98
- if ($ this -> debug ) {
108
+ if ($ _SERVER [ ' APP_DEBUG ' ] ) {
99
109
return new DebugClosureResolver ($ callable , $ arguments );
100
110
}
101
111
@@ -115,15 +125,19 @@ public function getRunner(?object $application): RunnerInterface
115
125
return $ application ;
116
126
}
117
127
118
- if (!\is_callable ($ application )) {
119
- throw new \LogicException (sprintf ('"%s" doesn \'t know how to handle apps of type "%s". ' , get_debug_type ($ this ), get_debug_type ($ application )));
120
- }
121
-
122
128
if (!$ application instanceof \Closure) {
129
+ if ($ runtime = $ this ->resolveRuntime (\get_class ($ application ))) {
130
+ return $ runtime ->getRunner ($ application );
131
+ }
132
+
133
+ if (!\is_callable ($ application )) {
134
+ throw new \LogicException (sprintf ('"%s" doesn \'t know how to handle apps of type "%s". ' , get_debug_type ($ this ), get_debug_type ($ application )));
135
+ }
136
+
123
137
$ application = \Closure::fromCallable ($ application );
124
138
}
125
139
126
- if ($ this -> debug && ($ r = new \ReflectionFunction ($ application )) && $ r ->getNumberOfRequiredParameters ()) {
140
+ if ($ _SERVER [ ' APP_DEBUG ' ] && ($ r = new \ReflectionFunction ($ application )) && $ r ->getNumberOfRequiredParameters ()) {
127
141
throw new \ArgumentCountError (sprintf ('Zero argument should be required by the runner callable, but at least one is in "%s" on line "%d. ' , $ r ->getFileName (), $ r ->getStartLine ()));
128
142
}
129
143
@@ -163,8 +177,56 @@ protected function getArgument(\ReflectionParameter $parameter, ?string $type)
163
177
return $ this ;
164
178
}
165
179
166
- $ r = $ parameter ->getDeclaringFunction ();
180
+ if (!$ runtime = $ this ->getRuntime ($ type )) {
181
+ $ r = $ parameter ->getDeclaringFunction ();
182
+
183
+ throw new \InvalidArgumentException (sprintf ('Cannot resolve argument "%s $%s" in "%s" on line "%d": "%s" supports only arguments "array $context", "array $argv" and "array $request", or a runtime named "Symfony\Runtime\%1$sRuntime". ' , $ type , $ parameter ->name , $ r ->getFileName (), $ r ->getStartLine (), get_debug_type ($ this )));
184
+ }
185
+
186
+ return $ runtime ->getArgument ($ parameter , $ type );
187
+ }
188
+
189
+ protected static function register (self $ runtime ): self
190
+ {
191
+ return $ runtime ;
192
+ }
193
+
194
+ private function getRuntime (string $ type ): ?self
195
+ {
196
+ if (null === $ runtime = ($ this ->options ['runtimes ' ][$ type ] ?? null )) {
197
+ $ runtime = 'Symfony\Runtime \\' .$ type .'Runtime ' ;
198
+ $ runtime = class_exists ($ runtime ) ? $ runtime : $ this ->options ['runtimes ' ][$ type ] = false ;
199
+ }
200
+
201
+ if (\is_string ($ runtime )) {
202
+ $ runtime = $ runtime ::register ($ this );
203
+ }
204
+
205
+ if ($ this === $ runtime ) {
206
+ return null ;
207
+ }
208
+
209
+ return $ runtime ?: null ;
210
+ }
211
+
212
+ private function resolveRuntime (string $ class ): ?self
213
+ {
214
+ if ($ runtime = $ this ->getRuntime ($ class )) {
215
+ return $ runtime ;
216
+ }
217
+
218
+ foreach (class_parents ($ class ) as $ type ) {
219
+ if ($ runtime = $ this ->getRuntime ($ type )) {
220
+ return $ runtime ;
221
+ }
222
+ }
223
+
224
+ foreach (class_implements ($ class ) as $ type ) {
225
+ if ($ runtime = $ this ->getRuntime ($ type )) {
226
+ return $ runtime ;
227
+ }
228
+ }
167
229
168
- throw new \ InvalidArgumentException ( sprintf ( ' Cannot resolve argument "%s $%s" in "%s" on line "%d": "%s" supports only arguments "array $context", "array $argv" and "array $request". ' , $ type , $ parameter -> name , $ r -> getFileName (), $ r -> getStartLine (), get_debug_type ( $ this ))) ;
230
+ return null ;
169
231
}
170
232
}
0 commit comments