1 | <?php |
||||
2 | /** |
||||
3 | * @package midcom |
||||
4 | * @author The Midgard Project, http://www.midgard-project.org |
||||
5 | * @copyright The Midgard Project, http://www.midgard-project.org |
||||
6 | * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License |
||||
7 | */ |
||||
8 | |||||
9 | use Symfony\Component\HttpFoundation\Request; |
||||
10 | use Symfony\Component\HttpKernel\HttpKernelInterface; |
||||
11 | use Symfony\Component\HttpKernel\Kernel; |
||||
12 | use Symfony\Component\Config\Loader\LoaderInterface; |
||||
13 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
||||
14 | use midcom\bundle\midcomBundle; |
||||
15 | use Symfony\Component\HttpFoundation\Response; |
||||
16 | |||||
17 | /** |
||||
18 | * Main controlling instance of the MidCOM Framework |
||||
19 | * |
||||
20 | * @property midcom_services_i18n $i18n |
||||
21 | * @property midcom_helper__componentloader $componentloader |
||||
22 | * @property midcom_services_dbclassloader $dbclassloader |
||||
23 | * @property midcom_helper__dbfactory $dbfactory |
||||
24 | * @property midcom_helper_head $head |
||||
25 | * @property midcom_helper_style $style |
||||
26 | * @property midcom_services_auth $auth |
||||
27 | * @property midcom_services_permalinks $permalinks |
||||
28 | * @property midcom_services_toolbars $toolbars |
||||
29 | * @property midcom_services_uimessages $uimessages |
||||
30 | * @property midcom_services_metadata $metadata |
||||
31 | * @property midcom_services_rcs $rcs |
||||
32 | * @property midcom_services__sessioning $session |
||||
33 | * @property midcom_services_indexer $indexer |
||||
34 | * @property midcom_config $config |
||||
35 | * @property midcom_services_cache $cache |
||||
36 | * @property Symfony\Component\EventDispatcher\EventDispatcher $dispatcher |
||||
37 | * @property midcom_debug $debug |
||||
38 | * @package midcom |
||||
39 | */ |
||||
40 | class midcom_application extends Kernel |
||||
41 | { |
||||
42 | private ?Request $request = null; |
||||
43 | |||||
44 | /** |
||||
45 | * Set this variable to true during the handle phase of your component to |
||||
46 | * not show the site's style around the component output. This is mainly |
||||
47 | * targeted at XML output like RSS feeds and similar things. The output |
||||
48 | * handler of the site, excluding the style-init/-finish tags will be executed |
||||
49 | * immediately after the handle phase |
||||
50 | * |
||||
51 | * Changing this flag after the handle phase or for dynamically loaded |
||||
52 | * components won't change anything. |
||||
53 | */ |
||||
54 | public bool $skip_page_style = false; |
||||
55 | |||||
56 | private ?string $project_dir = null; |
||||
57 | |||||
58 | private midcom_config $cfg; |
||||
59 | |||||
60 | public function __construct(string $environment, bool $debug) |
||||
61 | { |
||||
62 | $this->cfg = new midcom_config; |
||||
63 | parent::__construct($environment, $debug); |
||||
64 | } |
||||
65 | |||||
66 | 44 | private function get_request() : Request |
|||
67 | { |
||||
68 | 44 | return $this->request ??= Request::createFromGlobals(); |
|||
69 | } |
||||
70 | |||||
71 | public function registerContainerConfiguration(LoaderInterface $loader) |
||||
72 | { |
||||
73 | if (file_exists($this->getProjectDir() . '/config/services.yml')) { |
||||
74 | $loader->load($this->getProjectDir() . '/config/services.yml'); |
||||
75 | } |
||||
76 | if ($classes = midcom::get_registered_service_classes()) { |
||||
77 | $loader->load(function (ContainerBuilder $container) use ($classes) { |
||||
78 | foreach ($classes as $id => $class) { |
||||
79 | $container->findDefinition($id)->setClass($class); |
||||
80 | } |
||||
81 | }); |
||||
82 | } |
||||
83 | } |
||||
84 | |||||
85 | protected function initializeContainer() : void |
||||
86 | { |
||||
87 | parent::initializeContainer(); |
||||
88 | $this->container->set('config', $this->cfg); |
||||
89 | } |
||||
90 | |||||
91 | protected function buildContainer() : ContainerBuilder |
||||
92 | { |
||||
93 | $container = parent::buildContainer(); |
||||
94 | $this->cfg->export_to($container); |
||||
95 | return $container; |
||||
96 | } |
||||
97 | |||||
98 | public function registerBundles() : iterable |
||||
99 | { |
||||
100 | return [new midcomBundle]; |
||||
101 | } |
||||
102 | |||||
103 | 2 | public function getProjectDir() : string |
|||
104 | { |
||||
105 | 2 | if ($this->project_dir === null) { |
|||
106 | if (basename(dirname(__DIR__, 4)) === 'vendor') { |
||||
107 | // this is the case where we're installed as a dependency |
||||
108 | $this->project_dir = dirname(__DIR__, 5); |
||||
109 | } else { |
||||
110 | $this->project_dir = dirname(__DIR__, 2); |
||||
111 | } |
||||
112 | } |
||||
113 | 2 | return $this->project_dir; |
|||
114 | } |
||||
115 | |||||
116 | 365 | public function getCacheDir() : string |
|||
117 | { |
||||
118 | 365 | return $this->cfg->get('cache_base_directory') ?: parent::getCacheDir(); |
|||
119 | } |
||||
120 | |||||
121 | /** |
||||
122 | * Magic getter for service loading |
||||
123 | */ |
||||
124 | 732 | public function __get($key) |
|||
125 | { |
||||
126 | 732 | if (!$this->booted) { |
|||
127 | $this->boot(); |
||||
128 | } |
||||
129 | 732 | return $this->getContainer()->get($key); |
|||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * Magic setter |
||||
134 | */ |
||||
135 | public function __set($key, $value) |
||||
136 | { |
||||
137 | if (!$this->booted) { |
||||
138 | $this->boot(); |
||||
139 | } |
||||
140 | $this->getContainer()->set($key, $value); |
||||
141 | } |
||||
142 | |||||
143 | /* ************************************************************************* |
||||
144 | * Control framework: |
||||
145 | * codeinit - Handle the current request |
||||
146 | * dynamic_load - Dynamically load and execute a URL |
||||
147 | * finish - Cleanup Work |
||||
148 | */ |
||||
149 | |||||
150 | /** |
||||
151 | * Initialize the URL parser and process the request. |
||||
152 | * |
||||
153 | * This function must be called before any output starts. |
||||
154 | */ |
||||
155 | public function codeinit() |
||||
156 | { |
||||
157 | $request = $this->get_request(); |
||||
158 | try { |
||||
159 | $response = $this->handle($request); |
||||
160 | $response->send(); |
||||
161 | $this->terminate($request, $response); |
||||
162 | } catch (Error $e) { |
||||
163 | $this->getHttpKernel()->terminateWithException($e); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
164 | } |
||||
165 | } |
||||
166 | |||||
167 | /** |
||||
168 | * Dynamically execute a subrequest and insert its output in place of the |
||||
169 | * function call. |
||||
170 | * |
||||
171 | * It tries to load the component referenced with the URL $url and executes |
||||
172 | * it as if it was used as primary component. |
||||
173 | * |
||||
174 | * This is only possible if the system is in the Page-Style output phase. It |
||||
175 | * cannot be used within code-init or during the output phase of another |
||||
176 | * component. |
||||
177 | * |
||||
178 | * Example code, executed on a site's homepage, it will load the news listing from |
||||
179 | * the given URL and display it using a substyle of the node style that is assigned |
||||
180 | * to the loaded one: |
||||
181 | * |
||||
182 | * <code> |
||||
183 | * $blog = '/blog/latest/3/'; |
||||
184 | * $substyle = 'homepage'; |
||||
185 | * midcom::get()->dynamic_load("/midcom-substyle-{$substyle}/{$blog}"); |
||||
186 | * </code> |
||||
187 | * |
||||
188 | * Results of dynamic_loads are cached with the system cache strategy |
||||
189 | * |
||||
190 | * @param string $url The URL, relative to the Midgard Page, that is to be requested. |
||||
191 | */ |
||||
192 | 17 | public function dynamic_load(string $url, string $substyle = '') |
|||
193 | { |
||||
194 | 17 | debug_add("Dynamic load of URL {$url}"); |
|||
195 | 17 | $url = midcom_connection::get_url('prefix') . $url; |
|||
196 | |||||
197 | // Determine new Context ID and set current context, |
||||
198 | // enter that context and prepare its data structure. |
||||
199 | 17 | $oldcontext = midcom_core_context::get(); |
|||
200 | 17 | $context = midcom_core_context::enter($url, $oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC)); |
|||
0 ignored issues
–
show
It seems like
$oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC) can also be of type false ; however, parameter $topic of midcom_core_context::enter() does only seem to accept midcom_db_topic|null , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
201 | 17 | if ($substyle) { |
|||
202 | $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $substyle); |
||||
203 | } |
||||
204 | |||||
205 | 17 | $request = $this->get_request()->duplicate([], attributes: []); |
|||
206 | 17 | $request->attributes->set('context', $context); |
|||
207 | |||||
208 | 17 | $backup = $this->skip_page_style; |
|||
209 | 17 | $this->skip_page_style = true; |
|||
210 | try { |
||||
211 | 17 | $response = $this->handle($request, HttpKernelInterface::SUB_REQUEST, false); |
|||
212 | 4 | echo $response->getContent(); |
|||
213 | 15 | } catch (midcom_error_notfound | midcom_error_forbidden $e) { |
|||
214 | 15 | $e->log(); |
|||
215 | } finally { |
||||
216 | 17 | $this->skip_page_style = $backup; |
|||
217 | 17 | midcom_core_context::leave(); |
|||
218 | } |
||||
219 | } |
||||
220 | |||||
221 | /** |
||||
222 | * Stop the PHP process |
||||
223 | * |
||||
224 | * @deprecated |
||||
225 | */ |
||||
226 | public function finish() |
||||
227 | { |
||||
228 | _midcom_stop_request(); |
||||
229 | } |
||||
230 | |||||
231 | /* ************************************************************************* |
||||
232 | * Framework Access Helper functions |
||||
233 | */ |
||||
234 | |||||
235 | /** |
||||
236 | * Retrieves the name of the current host, fully qualified with protocol and |
||||
237 | * port (http[s]://www.my.domain.com[:1234]) |
||||
238 | */ |
||||
239 | 29 | function get_host_name() : string |
|||
0 ignored issues
–
show
|
|||||
240 | { |
||||
241 | 29 | return $this->get_request()->getSchemeAndHttpHost(); |
|||
242 | } |
||||
243 | |||||
244 | /** |
||||
245 | * Return the prefix required to build relative links on the current site. |
||||
246 | * This includes the http[s] prefix, the hosts port (if necessary) and the |
||||
247 | * base url of the Midgard Page. Be aware, that this does *not* point to the |
||||
248 | * base host of the site. |
||||
249 | * |
||||
250 | * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/page_prefix/ |
||||
251 | */ |
||||
252 | function get_page_prefix() : string |
||||
0 ignored issues
–
show
|
|||||
253 | { |
||||
254 | return $this->get_host_name() . midcom_connection::get_url('self'); |
||||
255 | } |
||||
256 | |||||
257 | /** |
||||
258 | * Return the prefix required to build relative links on the current site. |
||||
259 | * This includes the http[s] prefix, the hosts port (if necessary) and the |
||||
260 | * base url of the main host. This is not necessarily the currently active |
||||
261 | * MidCOM Page however, use the get_page_prefix() function for that. |
||||
262 | * |
||||
263 | * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/ |
||||
264 | */ |
||||
265 | 7 | function get_host_prefix() : string |
|||
0 ignored issues
–
show
|
|||||
266 | { |
||||
267 | 7 | $host_prefix = midcom_connection::get_url('prefix'); |
|||
268 | 7 | if (!str_starts_with($host_prefix, '/')) { |
|||
0 ignored issues
–
show
It seems like
$host_prefix can also be of type null ; however, parameter $haystack of str_starts_with() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
269 | 7 | $host_prefix = '/' . $host_prefix; |
|||
270 | } |
||||
271 | 7 | if (!str_ends_with($host_prefix, '/')) { |
|||
272 | $host_prefix .= '/'; |
||||
273 | } |
||||
274 | 7 | return $this->get_host_name() . $host_prefix; |
|||
275 | } |
||||
276 | |||||
277 | /* ************************************************************************* |
||||
278 | * Generic Helper Functions not directly related with MidCOM: |
||||
279 | * |
||||
280 | * relocate - executes a HTTP relocation to the given URL |
||||
281 | */ |
||||
282 | |||||
283 | /** |
||||
284 | * Sends a header out to the client. |
||||
285 | * |
||||
286 | * This function is syntactically identical to |
||||
287 | * the regular PHP header() function, but is integrated into the framework. Every |
||||
288 | * Header you sent must go through this function or it might be lost later on; |
||||
289 | * this is especially important with caching. |
||||
290 | */ |
||||
291 | 17 | public function header(string $header, int $response_code = 0) |
|||
292 | { |
||||
293 | 17 | $this->cache->content->register_sent_header($header); |
|||
294 | 17 | midcom_compat_environment::header($header, http_response_code: $response_code); |
|||
295 | } |
||||
296 | |||||
297 | /** |
||||
298 | * Relocate to another URL. |
||||
299 | * |
||||
300 | * Note, that this function automatically makes the page uncacheable, calls |
||||
301 | * midcom_finish and exit, so it will never return. If the headers have already |
||||
302 | * been sent, this will leave you with a partially completed page, so beware. |
||||
303 | */ |
||||
304 | public function relocate(string $url, int $response_code = Response::HTTP_FOUND) |
||||
305 | { |
||||
306 | $response = new midcom_response_relocate($url, $response_code); |
||||
307 | $response->send(); |
||||
308 | $this->finish(); |
||||
0 ignored issues
–
show
The function
midcom_application::finish() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
309 | } |
||||
310 | |||||
311 | /** |
||||
312 | * Raise some PHP limits for resource-intensive tasks |
||||
313 | */ |
||||
314 | 9 | public function disable_limits() |
|||
315 | { |
||||
316 | 9 | $stat = ini_set('max_execution_time', $this->config->get('midcom_max_execution_time')); |
|||
0 ignored issues
–
show
It seems like
$this->config->get('midcom_max_execution_time') can also be of type null ; however, parameter $value of ini_set() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
317 | 9 | if (false === $stat) { |
|||
318 | debug_add('ini_set("max_execution_time", ' . $this->config->get('midcom_max_execution_time') . ') returned false', MIDCOM_LOG_WARN); |
||||
319 | } |
||||
320 | 9 | $stat = ini_set('memory_limit', $this->config->get('midcom_max_memory')); |
|||
321 | 9 | if (false === $stat) { |
|||
322 | debug_add('ini_set("memory_limit", ' . $this->config->get('midcom_max_memory') . ') returned false', MIDCOM_LOG_WARN); |
||||
323 | } |
||||
324 | } |
||||
325 | } |
||||
326 |