Passed
Push — master ( 8740f6...9e8e87 )
by Andreas
15:46
created

midcom_application   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 298
Duplicated Lines 0 %

Test Coverage

Coverage 47.96%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 87
c 1
b 0
f 0
dl 0
loc 298
ccs 47
cts 98
cp 0.4796
rs 9.6
wmc 35

19 Methods

Rating   Name   Duplication   Size   Complexity  
A get_host_name() 0 3 1
A __construct() 0 4 1
A header() 0 4 1
A codeinit() 0 9 2
A get_page_prefix() 0 3 1
A disable_limits() 0 9 3
A buildContainer() 0 5 1
A __set() 0 6 2
A registerContainerConfiguration() 0 9 4
A initializeContainer() 0 4 1
A registerBundles() 0 3 1
A relocate() 0 5 1
A __get() 0 6 2
A get_request() 0 3 1
A get_host_prefix() 0 10 3
A dynamic_load() 0 42 4
A finish() 0 3 1
A getProjectDir() 0 11 3
A getCacheDir() 0 3 2
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
16
/**
17
 * Main controlling instance of the MidCOM Framework
18
 *
19
 * @property midcom_services_i18n $i18n
20
 * @property midcom_helper__componentloader $componentloader
21
 * @property midcom_services_dbclassloader $dbclassloader
22
 * @property midcom_helper__dbfactory $dbfactory
23
 * @property midcom_helper_head $head
24
 * @property midcom_helper_style $style
25
 * @property midcom_services_auth $auth
26
 * @property midcom_services_permalinks $permalinks
27
 * @property midcom_services_toolbars $toolbars
28
 * @property midcom_services_uimessages $uimessages
29
 * @property midcom_services_metadata $metadata
30
 * @property midcom_services_rcs $rcs
31
 * @property midcom_services__sessioning $session
32
 * @property midcom_services_indexer $indexer
33
 * @property midcom_config $config
34
 * @property midcom_services_cache $cache
35
 * @property Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
36
 * @property midcom_debug $debug
37
 * @package midcom
38
 */
39
class midcom_application extends Kernel
40
{
41
    private ?Request $request = null;
42
43
    /**
44
     * Set this variable to true during the handle phase of your component to
45
     * not show the site's style around the component output. This is mainly
46
     * targeted at XML output like RSS feeds and similar things. The output
47
     * handler of the site, excluding the style-init/-finish tags will be executed
48
     * immediately after the handle phase
49
     *
50
     * Changing this flag after the handle phase or for dynamically loaded
51
     * components won't change anything.
52
     */
53
    public bool $skip_page_style = false;
54
55
    private ?string $project_dir = null;
56
57
    private midcom_config $cfg;
58
59
    public function __construct(string $environment, bool $debug)
60
    {
61
        $this->cfg = new midcom_config;
62
        parent::__construct($environment, $debug);
63
    }
64
65 44
    private function get_request() : Request
66
    {
67 44
        return $this->request ?? Request::createFromGlobals();
68
    }
69
70
    public function registerContainerConfiguration(LoaderInterface $loader)
71
    {
72
        if (file_exists($this->getProjectDir() . '/config/services.yml')) {
73
            $loader->load($this->getProjectDir() . '/config/services.yml');
74
        }
75
        if ($classes = midcom::get_registered_service_classes()) {
76
            $loader->load(function (ContainerBuilder $container) use ($classes) {
77
                foreach ($classes as $id => $class) {
78
                    $container->findDefinition($id)->setClass($class);
79
                }
80
            });
81
        }
82
    }
83
84
    protected function initializeContainer()
85
    {
86
        parent::initializeContainer();
87
        $this->container->set('config', $this->cfg);
88
    }
89
90
    protected function buildContainer()
91
    {
92
        $container = parent::buildContainer();
93
        $this->cfg->export_to($container);
94
        return $container;
95
    }
96
97
    public function registerBundles()
98
    {
99
        return [new midcomBundle];
100
    }
101
102 3
    public function getProjectDir()
103
    {
104 3
        if ($this->project_dir === null) {
105
            if (basename(dirname(__DIR__, 4)) === 'vendor') {
106
                // this is the case where we're installed as a dependency
107
                $this->project_dir = dirname(__DIR__, 5);
108
            } else {
109
                $this->project_dir = dirname(__DIR__, 2);
110
            }
111
        }
112 3
        return $this->project_dir;
113
    }
114
115 352
    public function getCacheDir()
116
    {
117 352
        return $this->cfg->get('cache_base_directory') ?: parent::getCacheDir();
118
    }
119
120
    /**
121
     * Magic getter for service loading
122
     */
123 730
    public function __get($key)
124
    {
125 730
        if (!$this->booted) {
126
            $this->boot();
127
        }
128 730
        return $this->getContainer()->get($key);
129
    }
130
131
    /**
132
     * Magic setter
133
     */
134
    public function __set($key, $value)
135
    {
136
        if (!$this->booted) {
137
            $this->boot();
138
        }
139
        $this->getContainer()->set($key, $value);
140
    }
141
142
    /* *************************************************************************
143
     * Control framework:
144
     * codeinit      - Handle the current request
145
     * dynamic_load   - Dynamically load and execute a URL
146
     * finish         - Cleanup Work
147
     */
148
149
    /**
150
     * Initialize the URL parser and process the request.
151
     *
152
     * This function must be called before any output starts.
153
     */
154
    public function codeinit()
155
    {
156
        $request = $this->get_request();
157
        try {
158
            $response = $this->handle($request);
159
            $response->send();
160
            $this->terminate($request, $response);
161
        } catch (Error $e) {
162
            $this->getHttpKernel()->terminateWithException($e);
0 ignored issues
show
Bug introduced by
The method terminateWithException() does not exist on Symfony\Component\HttpKernel\HttpKernelInterface. It seems like you code against a sub-type of Symfony\Component\HttpKernel\HttpKernelInterface such as Symfony\Component\HttpKernel\HttpKernel. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

162
            $this->getHttpKernel()->/** @scrutinizer ignore-call */ terminateWithException($e);
Loading history...
163
        }
164
    }
165
166
    /**
167
     * Dynamically execute a subrequest and insert its output in place of the
168
     * function call.
169
     *
170
     * It tries to load the component referenced with the URL $url and executes
171
     * it as if it was used as primary component.
172
     *
173
     * This is only possible if the system is in the Page-Style output phase. It
174
     * cannot be used within code-init or during the output phase of another
175
     * component.
176
     *
177
     * Example code, executed on a site's homepage, it will load the news listing from
178
     * the given URL and display it using a substyle of the node style that is assigned
179
     * to the loaded one:
180
     *
181
     * <code>
182
     * $blog = '/blog/latest/3/';
183
     * $substyle = 'homepage';
184
     * midcom::get()->dynamic_load("/midcom-substyle-{$substyle}/{$blog}");
185
     * </code>
186
     *
187
     * Results of dynamic_loads are cached with the system cache strategy
188
     *
189
     * @param string $url                The URL, relative to the Midgard Page, that is to be requested.
190
     */
191 17
    public function dynamic_load(string $url, string $substyle = '')
192
    {
193 17
        debug_add("Dynamic load of URL {$url}");
194 17
        $url = midcom_connection::get_url('prefix') . $url;
195
196
        // Determine new Context ID and set current context,
197
        // enter that context and prepare its data structure.
198 17
        $oldcontext = midcom_core_context::get();
199 17
        $context = midcom_core_context::enter($url, $oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC));
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

199
        $context = midcom_core_context::enter($url, /** @scrutinizer ignore-type */ $oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC));
Loading history...
200 17
        if ($substyle) {
201
            $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $substyle);
202
        }
203
204 17
        $request = $this->get_request()->duplicate([], null, []);
205 17
        $request->attributes->set('context', $context);
206
207 17
        $cached = $this->cache->content->check_dl_hit($request);
208 17
        if ($cached !== false) {
209
            echo $cached;
210
            midcom_core_context::leave();
211
            return;
212
        }
213
214 17
        $backup = $this->skip_page_style;
215 17
        $this->skip_page_style = true;
216
        try {
217 17
            $response = $this->handle($request, HttpKernelInterface::SUB_REQUEST, false);
218 13
        } catch (midcom_error_notfound | midcom_error_forbidden $e) {
219 13
            $e->log();
220 13
            midcom_core_context::leave();
221 13
            return;
222
        } finally {
223 17
            $this->skip_page_style = $backup;
224
        }
225
226 4
        $dl_cache_data = $response->getContent();
227 4
        echo $dl_cache_data;
228
229
        /* Cache DL the content */
230 4
        $this->cache->content->store_dl_content($context->id, $dl_cache_data, $request);
231
232 4
        midcom_core_context::leave();
233
    }
234
235
    /**
236
     * Stop the PHP process
237
     *
238
     * @deprecated
239
     */
240
    public function finish()
241
    {
242
        _midcom_stop_request();
243
    }
244
245
    /* *************************************************************************
246
     * Framework Access Helper functions
247
     */
248
249
    /**
250
     * Retrieves the name of the current host, fully qualified with protocol and
251
     * port (http[s]://www.my.domain.com[:1234])
252
     */
253 29
    function get_host_name() : string
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
254
    {
255 29
        return $this->get_request()->getSchemeAndHttpHost();
256
    }
257
258
    /**
259
     * Return the prefix required to build relative links on the current site.
260
     * This includes the http[s] prefix, the hosts port (if necessary) and the
261
     * base url of the Midgard Page. Be aware, that this does *not* point to the
262
     * base host of the site.
263
     *
264
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/page_prefix/
265
     */
266
    function get_page_prefix() : string
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
267
    {
268
        return $this->get_host_name() . midcom_connection::get_url('self');
269
    }
270
271
    /**
272
     * Return the prefix required to build relative links on the current site.
273
     * This includes the http[s] prefix, the hosts port (if necessary) and the
274
     * base url of the main host. This is not necessarily the currently active
275
     * MidCOM Page however, use the get_page_prefix() function for that.
276
     *
277
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/
278
     */
279 7
    function get_host_prefix() : string
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
280
    {
281 7
        $host_prefix = midcom_connection::get_url('prefix');
282 7
        if (!str_starts_with($host_prefix, '/')) {
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

282
        if (!str_starts_with(/** @scrutinizer ignore-type */ $host_prefix, '/')) {
Loading history...
283
            $host_prefix = '/' . $host_prefix;
284
        }
285 7
        if (!str_ends_with($host_prefix, '/')) {
286
            $host_prefix .= '/';
287
        }
288 7
        return $this->get_host_name() . $host_prefix;
289
    }
290
291
    /* *************************************************************************
292
     * Generic Helper Functions not directly related with MidCOM:
293
     *
294
     * relocate           - executes a HTTP relocation to the given URL
295
     */
296
297
    /**
298
     * Sends a header out to the client.
299
     *
300
     * This function is syntactically identical to
301
     * the regular PHP header() function, but is integrated into the framework. Every
302
     * Header you sent must go through this function or it might be lost later on;
303
     * this is especially important with caching.
304
     */
305 17
    public function header(string $header, int $response_code = 0)
306
    {
307 17
        $this->cache->content->register_sent_header($header);
308 17
        midcom_compat_environment::header($header, true, $response_code);
309
    }
310
311
    /**
312
     * Relocate to another URL.
313
     *
314
     * Note, that this function automatically makes the page uncacheable, calls
315
     * midcom_finish and exit, so it will never return. If the headers have already
316
     * been sent, this will leave you with a partially completed page, so beware.
317
     */
318
    public function relocate(string $url, int $response_code = 302)
319
    {
320
        $response = new midcom_response_relocate($url, $response_code);
321
        $response->send();
322
        $this->finish();
0 ignored issues
show
Deprecated Code introduced by
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 ignore-deprecated  annotation

322
        /** @scrutinizer ignore-deprecated */ $this->finish();
Loading history...
323
    }
324
325
    /**
326
     * Raise some PHP limits for resource-intensive tasks
327
     */
328 8
    public function disable_limits()
329
    {
330 8
        $stat = ini_set('max_execution_time', $this->config->get('midcom_max_execution_time'));
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

330
        $stat = ini_set('max_execution_time', /** @scrutinizer ignore-type */ $this->config->get('midcom_max_execution_time'));
Loading history...
331 8
        if (false === $stat) {
332
            debug_add('ini_set("max_execution_time", ' . $this->config->get('midcom_max_execution_time') . ') returned false', MIDCOM_LOG_WARN);
333
        }
334 8
        $stat = ini_set('memory_limit', $this->config->get('midcom_max_memory'));
335 8
        if (false === $stat) {
336
            debug_add('ini_set("memory_limit", ' . $this->config->get('midcom_max_memory') . ') returned false', MIDCOM_LOG_WARN);
337
        }
338
    }
339
}
340