2222use MaplePHP \Emitron \Emitters \CliEmitter ;
2323use MaplePHP \Emitron \Emitters \HttpEmitter ;
2424use MaplePHP \Http \Interfaces \PathInterface ;
25- use MaplePHP \Http \ResponseFactory ;
2625use MaplePHP \Http \Stream ;
2726use Psr \Container \ContainerInterface ;
2827use Psr \Http \Message \ResponseInterface ;
3231
3332abstract class AbstractKernel implements KernelInterface
3433{
35- public const CONFIG_FILE_PATH = __DIR__ . '/../emitron.config.php ' ;
36- protected static ?string $ configFilePath = null ;
37- protected static ?string $ routerFilePath = null ;
38- protected ContainerInterface $ container ;
39- protected array $ userMiddlewares ;
40- protected ?DispatchConfigInterface $ dispatchConfig = null ;
41- protected array $ config = [];
42-
43- /**
44- * @param ContainerInterface $container
45- * @param array $userMiddlewares
46- * @param DispatchConfigInterface|null $dispatchConfig
47- * @throws \Exception
48- */
49- public function __construct (
50- ContainerInterface $ container ,
51- array $ userMiddlewares = [],
52- ?DispatchConfigInterface $ dispatchConfig = null ,
53- ) {
54- $ this ->userMiddlewares = $ userMiddlewares ;
55- $ this ->container = $ container ;
56- $ this ->dispatchConfig = ($ dispatchConfig === null ) ?
57- new DispatchConfig (static ::getConfigFilePath ()) : $ dispatchConfig ;
58- }
59-
60- /**
61- * Makes it easy to specify a config file inside a custom kernel file
62- *
63- * @param string|null $path
64- * @return void
65- */
66- public static function setConfigFilePath (?string $ path ): void
67- {
68- static ::$ configFilePath = $ path ;
69- }
70-
71- /**
72- * Get expected config file
73- *
74- * @return string
75- */
76- public static function getConfigFilePath (): string
77- {
78- if (static ::$ configFilePath === null ) {
79- return static ::CONFIG_FILE_PATH ;
80- }
81- return static ::$ configFilePath ;
82- }
83-
84- /**
85- * Set router path
86- *
87- * @param string|null $path
88- * @return void
89- */
90- public static function setRouterFilePath (?string $ path ): void
91- {
92- static ::$ routerFilePath = $ path ;
93- }
94-
95- /**
96- * Get router path
97- *
98- * @return string
99- */
100- public static function getRouterFilePath (): string
101- {
102- if (static ::$ routerFilePath === null ) {
103- return realpath (dirname (self ::getConfigFilePath ()));
104- }
105- return static ::$ routerFilePath ;
106- }
107-
108- /**
109- * Get config instance for configure dispatch result
110- *
111- * @return DispatchConfigInterface
112- */
113- public function getDispatchConfig (): DispatchConfigInterface
114- {
115- return $ this ->dispatchConfig ;
116- }
117-
118- /**
119- * Will initialize the request handler with default
120- * functionality that you would want.
121- *
122- * @param ServerRequestInterface $request
123- * @param StreamInterface $stream
124- * @param RequestHandlerInterface $finalHandler
125- * @param array $middlewares
126- * @return ResponseInterface
127- * @throws \ReflectionException
128- */
129- protected function initRequestHandler (
130- ServerRequestInterface $ request ,
131- StreamInterface $ stream ,
132- PathInterface $ path ,
133- RequestHandlerInterface $ finalHandler ,
134- array $ middlewares = []
135- ): ResponseInterface {
136-
137- $ this ->bindInterfaces ([
138- "ContainerInterface " => $ this ->container ,
139- "RequestInterface " => $ request ,
140- "ServerRequestInterface " => $ request ,
141- "StreamInterface " => $ stream ,
142- "PathInterface " => $ path
143- ]);
144-
145- $ middlewares = array_merge ($ this ->userMiddlewares , $ middlewares );
146- $ handler = new RequestHandler ($ middlewares , $ finalHandler );
147- $ app = $ this ->container ->has ("app " ) ? $ this ->container ->get ("app " ) : null ;
148-
149- ob_start ();
150- $ response = $ handler ->handle ($ request );
151- $ output = ob_get_clean ();
152-
153- if ((string )$ output !== "" && ($ app instanceof AppInterface && !$ app ->isProd ())) {
154- throw new \RuntimeException (
155- 'Unexpected output detected during request dispatch. Controllers must write to the response body instead of using echo. '
156- );
157- }
158-
159- return $ response ;
160- }
161-
162- /**
163- * Bind instances (singletons) to interface classes so they can be resolved
164- * through the dependency injector.
165- *
166- * @param array<string, object> $bindings
167- * @return void
168- */
169- protected function bindInterfaces (array $ bindings ): void
170- {
171- Reflection::interfaceFactory (function (string $ className ) use ($ bindings ) {
172- return $ bindings [$ className ] ?? null ;
173- });
174- }
175-
176- /**
177- * Get the expected body (stream)
178- *
179- * @param StreamInterface|null $stream
180- * @return StreamInterface
181- */
182- protected function getBody (?StreamInterface $ stream ): StreamInterface
183- {
184- if ($ stream === null ) {
185- return new Stream ($ this ->isCli () ? Stream::STDOUT : Stream::TEMP );
186- }
187- return $ stream ;
188- }
189-
190- /**
191- * Check if is inside a command line interface (CLI)
192- *
193- * @return bool
194- */
195- protected function isCli (): bool
196- {
197- return PHP_SAPI === 'cli ' ;
198- }
199-
200- /**
201- * Get emitter based on a platform
202- *
203- * @return EmitterInterface
204- */
205- protected function createEmitter (): EmitterInterface
206- {
207- return $ this ->isCli () ? new CliEmitter () : new HttpEmitter ();
208- }
34+ public const CONFIG_FILE_PATH = __DIR__ . '/../emitron.config.php ' ;
35+ protected static ?string $ configFilePath = null ;
36+ protected static ?string $ routerFilePath = null ;
37+ protected ContainerInterface $ container ;
38+ protected array $ userMiddlewares ;
39+ protected ?DispatchConfigInterface $ dispatchConfig = null ;
40+ protected array $ config = [];
41+
42+ /**
43+ * @param ContainerInterface $container
44+ * @param array $userMiddlewares
45+ * @param DispatchConfigInterface|null $dispatchConfig
46+ * @throws \Exception
47+ */
48+ public function __construct (
49+ ContainerInterface $ container ,
50+ array $ userMiddlewares = [],
51+ ?DispatchConfigInterface $ dispatchConfig = null ,
52+ )
53+ {
54+ $ this ->userMiddlewares = $ userMiddlewares ;
55+ $ this ->container = $ container ;
56+ $ this ->dispatchConfig = ($ dispatchConfig === null ) ?
57+ new DispatchConfig (static ::getConfigFilePath ()) : $ dispatchConfig ;
58+ }
59+
60+ /**
61+ * Makes it easy to specify a config file inside a custom kernel file
62+ *
63+ * @param string|null $path
64+ * @return void
65+ */
66+ public static function setConfigFilePath (?string $ path ): void
67+ {
68+ static ::$ configFilePath = $ path ;
69+ }
70+
71+ /**
72+ * Get expected config file
73+ *
74+ * @return string
75+ */
76+ public static function getConfigFilePath (): string
77+ {
78+ if (static ::$ configFilePath === null ) {
79+ return static ::CONFIG_FILE_PATH ;
80+ }
81+ return static ::$ configFilePath ;
82+ }
83+
84+ /**
85+ * Set router path
86+ *
87+ * @param string|null $path
88+ * @return void
89+ */
90+ public static function setRouterFilePath (?string $ path ): void
91+ {
92+ static ::$ routerFilePath = $ path ;
93+ }
94+
95+ /**
96+ * Get router path
97+ *
98+ * @return string
99+ */
100+ public static function getRouterFilePath (): string
101+ {
102+ if (static ::$ routerFilePath === null ) {
103+ return realpath (dirname (self ::getConfigFilePath ()));
104+ }
105+ return static ::$ routerFilePath ;
106+ }
107+
108+ /**
109+ * Get config instance for configure dispatch result
110+ *
111+ * @return DispatchConfigInterface
112+ */
113+ public function getDispatchConfig (): DispatchConfigInterface
114+ {
115+ return $ this ->dispatchConfig ;
116+ }
117+
118+ /**
119+ * Will initialize the request handler with default
120+ * functionality that you would want.
121+ *
122+ * @param ServerRequestInterface $request
123+ * @param StreamInterface $stream
124+ * @param RequestHandlerInterface $finalHandler
125+ * @param array $middlewares
126+ * @return ResponseInterface
127+ * @throws \ReflectionException
128+ */
129+ protected function initRequestHandler (
130+ ServerRequestInterface $ request ,
131+ StreamInterface $ stream ,
132+ PathInterface $ path ,
133+ RequestHandlerInterface $ finalHandler ,
134+ array $ middlewares = []
135+ ): ResponseInterface
136+ {
137+
138+ $ this ->bindInterfaces ([
139+ "ContainerInterface " => $ this ->container ,
140+ "RequestInterface " => $ request ,
141+ "ServerRequestInterface " => $ request ,
142+ "StreamInterface " => $ stream ,
143+ "PathInterface " => fn () => $ path
144+ ]);
145+
146+ $ middlewares = array_merge ($ this ->userMiddlewares , $ middlewares );
147+ $ handler = new RequestHandler ($ middlewares , $ finalHandler );
148+ $ app = $ this ->container ->has ("app " ) ? $ this ->container ->get ("app " ) : null ;
149+
150+ ob_start ();
151+ $ response = $ handler ->handle ($ request );
152+ $ output = ob_get_clean ();
153+
154+ if ((string )$ output !== "" && ($ app instanceof AppInterface && !$ app ->isProd ())) {
155+ throw new \RuntimeException (
156+ 'Unexpected output detected during request dispatch. Controllers must write to the response body instead of using echo. '
157+ );
158+ }
159+
160+ return $ response ;
161+ }
162+
163+ /**
164+ * Bind instances (singletons) to interface classes so they can be resolved
165+ * through the dependency injector.
166+ *
167+ * @param array<string, object> $bindings
168+ * @return void
169+ */
170+ protected function bindInterfaces (array $ bindings ): void
171+ {
172+ Reflection::interfaceFactory (function (string $ className ) use ($ bindings ) {
173+ $ instance = $ bindings [$ className ] ?? null ;
174+ if (is_callable ($ instance )) {
175+ $ instance = $ instance ();
176+ }
177+ return $ instance ;
178+ });
179+ }
180+
181+ /**
182+ * Get the expected body (stream)
183+ *
184+ * @param StreamInterface|null $stream
185+ * @return StreamInterface
186+ */
187+ protected function getBody (?StreamInterface $ stream ): StreamInterface
188+ {
189+ if ($ stream === null ) {
190+ return new Stream ($ this ->isCli () ? Stream::STDOUT : Stream::TEMP );
191+ }
192+ return $ stream ;
193+ }
194+
195+ /**
196+ * Check if is inside a command line interface (CLI)
197+ *
198+ * @return bool
199+ */
200+ protected function isCli (): bool
201+ {
202+ return PHP_SAPI === 'cli ' ;
203+ }
204+
205+ /**
206+ * Get emitter based on a platform
207+ *
208+ * @return EmitterInterface
209+ */
210+ protected function createEmitter (): EmitterInterface
211+ {
212+ return $ this ->isCli () ? new CliEmitter () : new HttpEmitter ();
213+ }
209214}
0 commit comments