@@ -22,8 +22,12 @@ class_exists(ClosureResolver::class);
2222/**
2323 * A runtime to do bare-metal PHP without using superglobals.
2424 *
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.
2731 *
2832 * The app-callable can declare arguments among either:
2933 * - "array $context" to get a local array similar to $_SERVER;
@@ -42,42 +46,48 @@ class_exists(ClosureResolver::class);
4246 */
4347class GenericRuntime implements RuntimeInterface
4448{
45- private $ debug ;
49+ protected $ options ;
4650
4751 /**
4852 * @param array {
4953 * debug?: ?bool,
54+ * runtimes?: ?array,
55+ * error_handler?: string|false,
5056 * } $options
5157 */
5258 public function __construct (array $ options = [])
5359 {
54- $ this -> debug = $ options ['debug ' ] ?? $ _SERVER ['APP_DEBUG ' ] ?? $ _ENV ['APP_DEBUG ' ] ?? true ;
60+ $ debug = $ options ['debug ' ] ?? $ _SERVER ['APP_DEBUG ' ] ?? $ _ENV ['APP_DEBUG ' ] ?? true ;
5561
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 );
5864 }
5965
60- if ($ this ->debug ) {
66+ if ($ debug ) {
67+ umask (0000 );
6168 $ _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+ }
6474 } else {
6575 $ _SERVER ['APP_DEBUG ' ] = $ _ENV ['APP_DEBUG ' ] = '0 ' ;
6676 }
77+
78+ $ this ->options = $ options ;
6779 }
6880
6981 /**
7082 * {@inheritdoc}
7183 */
72- public function getResolver (callable $ callable ): ResolverInterface
84+ public function getResolver (callable $ callable, \ ReflectionFunction $ reflector = null ): ResolverInterface
7385 {
7486 if (!$ callable instanceof \Closure) {
7587 $ callable = \Closure::fromCallable ($ callable );
7688 }
7789
78- $ function = new \ReflectionFunction ($ callable );
79- $ parameters = $ function ->getParameters ();
80-
90+ $ parameters = ($ reflector ?? new \ReflectionFunction ($ callable ))->getParameters ();
8191 $ arguments = function () use ($ parameters ) {
8292 $ arguments = [];
8393
@@ -95,7 +105,7 @@ public function getResolver(callable $callable): ResolverInterface
95105 return $ arguments ;
96106 };
97107
98- if ($ this -> debug ) {
108+ if ($ _SERVER [ ' APP_DEBUG ' ] ) {
99109 return new DebugClosureResolver ($ callable , $ arguments );
100110 }
101111
@@ -115,15 +125,19 @@ public function getRunner(?object $application): RunnerInterface
115125 return $ application ;
116126 }
117127
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-
122128 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+
123137 $ application = \Closure::fromCallable ($ application );
124138 }
125139
126- if ($ this -> debug && ($ r = new \ReflectionFunction ($ application )) && $ r ->getNumberOfRequiredParameters ()) {
140+ if ($ _SERVER [ ' APP_DEBUG ' ] && ($ r = new \ReflectionFunction ($ application )) && $ r ->getNumberOfRequiredParameters ()) {
127141 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 ()));
128142 }
129143
@@ -163,8 +177,56 @@ protected function getArgument(\ReflectionParameter $parameter, ?string $type)
163177 return $ this ;
164178 }
165179
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+ }
167229
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 ;
169231 }
170232}
0 commit comments