Passed
Push — master ( a58879...e66cf6 )
by Andreas
17:49
created

midcom_application   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 327
Duplicated Lines 0 %

Test Coverage

Coverage 44.14%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 94
dl 0
loc 327
ccs 49
cts 111
cp 0.4414
rs 9.44
c 4
b 0
f 0
wmc 37

18 Methods

Rating   Name   Duplication   Size   Complexity  
A initializeContainer() 0 4 1
A registerBundles() 0 3 1
A getProjectDir() 0 11 3
A getCacheDir() 0 3 2
A __construct() 0 5 1
A buildContainer() 0 5 1
A registerContainerConfiguration() 0 9 4
A header() 0 4 1
A codeinit() 0 8 2
A get_page_prefix() 0 8 2
A disable_limits() 0 9 3
A get_host_name() 0 3 1
A __set() 0 3 1
A relocate() 0 5 1
A __get() 0 6 2
A get_host_prefix() 0 15 4
B dynamic_load() 0 45 6
A finish() 0 3 1
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__styleloader $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
    /**
42
     * Host prefix cache to avoid computing it each time.
43
     *
44
     * @var string
45
     * @see get_host_prefix()
46
     */
47
    private $_cached_host_prefix = '';
48
49
    /**
50
     * Page prefix cache to avoid computing it each time.
51
     *
52
     * @var string
53
     * @see get_page_prefix()
54
     */
55
    private $_cached_page_prefix = '';
56
57
    /**
58
     * @var Request
59
     */
60
    private $request;
61
62
    /**
63
     * Set this variable to true during the handle phase of your component to
64
     * not show the site's style around the component output. This is mainly
65
     * targeted at XML output like RSS feeds and similar things. The output
66
     * handler of the site, excluding the style-init/-finish tags will be executed
67
     * immediately after the handle phase
68
     *
69
     * Changing this flag after the handle phase or for dynamically loaded
70
     * components won't change anything.
71
     *
72
     * @var boolean
73
     */
74
    public $skip_page_style = false;
75
76
    private $project_dir;
77
78
    /**
79
     * @var midcom_config
80
     */
81
    private $cfg;
82
83
    public function __construct(string $environment, bool $debug)
84
    {
85
        $this->request = Request::createFromGlobals();
86
        $this->cfg = new midcom_config;
87
        parent::__construct($environment, $debug);
88
    }
89
90
    public function registerContainerConfiguration(LoaderInterface $loader)
91
    {
92
        if (file_exists($this->getProjectDir() . '/config/services.yml')) {
93
            $loader->load($this->getProjectDir() . '/config/services.yml');
94
        }
95
        if ($classes = midcom::get_registered_service_classes()) {
96
            $loader->load(function (ContainerBuilder $container) use ($classes) {
97
                foreach ($classes as $id => $class) {
98
                    $container->findDefinition($id)->setClass($class);
99
                }
100
            });
101
        }
102
    }
103
104
    protected function initializeContainer()
105
    {
106
        parent::initializeContainer();
107
        $this->container->set('config', $this->cfg);
108
    }
109
110
    protected function buildContainer()
111
    {
112
        $container = parent::buildContainer();
113
        $this->cfg->export_to($container);
114
        return $container;
115
    }
116
117
    public function registerBundles()
118
    {
119
        return [new midcomBundle];
120
    }
121
122
    public function getProjectDir()
123
    {
124
        if ($this->project_dir === null) {
125
            if (basename(dirname(__DIR__, 4)) === 'vendor') {
126
                // this is the case where we're installed as a dependency
127
                $this->project_dir = dirname(__DIR__, 5);
128
            } else {
129
                $this->project_dir = dirname(__DIR__, 2);
130
            }
131
        }
132
        return $this->project_dir;
133
    }
134
135 324
    public function getCacheDir()
136
    {
137 324
        return $this->cfg->get('cache_base_directory') ?: parent::getCacheDir() . '/midcom';
138
    }
139
140
    /**
141
     * Magic getter for service loading
142
     */
143 680
    public function __get($key)
144
    {
145 680
        if (!$this->booted) {
146
            $this->boot();
147
        }
148 680
        return $this->getContainer()->get($key);
149
    }
150
151
    /**
152
     * Magic setter
153
     */
154
    public function __set($key, $value)
155
    {
156
        $this->getContainer()->set($key, $value);
157
    }
158
159
    /* *************************************************************************
160
     * Control framework:
161
     * codeinit      - Handle the current request
162
     * dynamic_load   - Dynamically load and execute a URL
163
     * finish         - Cleanup Work
164
     */
165
166
    /**
167
     * Initialize the URL parser and process the request.
168
     *
169
     * This function must be called before any output starts.
170
     */
171
    public function codeinit()
172
    {
173
        try {
174
            $response = $this->handle($this->request);
175
            $response->send();
176
            $this->terminate($this->request, $response);
177
        } catch (Error $e) {
178
            $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

178
            $this->getHttpKernel()->/** @scrutinizer ignore-call */ terminateWithException($e);
Loading history...
179
        }
180
    }
181
182
    /**
183
     * Dynamically execute a subrequest and insert its output in place of the
184
     * function call.
185
     *
186
     * It tries to load the component referenced with the URL $url and executes
187
     * it as if it was used as primary component.
188
     *
189
     * This is only possible if the system is in the Page-Style output phase. It
190
     * cannot be used within code-init or during the output phase of another
191
     * component.
192
     *
193
     * Example code, executed on a site's homepage, it will load the news listing from
194
     * the given URL and display it using a substyle of the node style that is assigned
195
     * to the loaded one:
196
     *
197
     * <code>
198
     * $blog = '/blog/latest/3/';
199
     * $substyle = 'homepage';
200
     * midcom::get()->dynamic_load("/midcom-substyle-{$substyle}/{$blog}");
201
     * </code>
202
     *
203
     * Results of dynamic_loads are cached with the system cache strategy
204
     *
205
     * @param string $url                The URL, relative to the Midgard Page, that is to be requested.
206
     */
207 16
    public function dynamic_load(string $url, string $substyle = '')
208
    {
209 16
        debug_add("Dynamic load of URL {$url}");
210 16
        $url = midcom_connection::get_url('prefix') . $url;
211
212
        // Determine new Context ID and set current context,
213
        // enter that context and prepare its data structure.
214 16
        $oldcontext = midcom_core_context::get();
215 16
        $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

215
        $context = midcom_core_context::enter($url, /** @scrutinizer ignore-type */ $oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC));
Loading history...
216 16
        if ($substyle) {
217
            $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $substyle);
218
        }
219
220 16
        $request = $this->request->duplicate([], null, []);
221 16
        $request->attributes->set('context', $context);
222
223 16
        $cached = $this->cache->content->check_dl_hit($request);
224 16
        if ($cached !== false) {
225
            echo $cached;
226
            midcom_core_context::leave();
227
            return;
228
        }
229
230 16
        $backup = $this->skip_page_style;
231 16
        $this->skip_page_style = true;
232
        try {
233 16
            $response = $this->handle($request, HttpKernelInterface::SUB_REQUEST, false);
234 12
        } catch (midcom_error $e) {
235 12
            if ($e instanceof midcom_error_notfound || $e instanceof midcom_error_forbidden) {
236 12
                $e->log();
237 12
                midcom_core_context::leave();
238 12
                return;
239
            }
240
            throw $e;
241 4
        } finally {
242 16
            $this->skip_page_style = $backup;
243
        }
244
245 4
        $dl_cache_data = $response->getContent();
246 4
        echo $dl_cache_data;
247
248
        /* Cache DL the content */
249 4
        $this->cache->content->store_dl_content($context->id, $dl_cache_data, $request);
250
251 4
        midcom_core_context::leave();
252 4
    }
253
254
    /**
255
     * Stop the PHP process
256
     *
257
     * @deprecated
258
     */
259
    public function finish()
260
    {
261
        _midcom_stop_request();
262
    }
263
264
    /* *************************************************************************
265
     * Framework Access Helper functions
266
     */
267
268
    /**
269
     * Retrieves the name of the current host, fully qualified with protocol and
270
     * port (http[s]://www.my.domain.com[:1234])
271
     */
272 23
    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...
273
    {
274 23
        return $this->request->getSchemeAndHttpHost();
275
    }
276
277
    /**
278
     * Return the prefix required to build relative links on the current site.
279
     * This includes the http[s] prefix, the hosts port (if necessary) and the
280
     * base url of the Midgard Page. Be aware, that this does *not* point to the
281
     * base host of the site.
282
     *
283
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/page_prefix/
284
     */
285
    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...
286
    {
287
        if (!$this->_cached_page_prefix) {
288
            $host_name = $this->get_host_name();
289
            $this->_cached_page_prefix = $host_name . midcom_connection::get_url('self');
290
        }
291
292
        return $this->_cached_page_prefix;
293
    }
294
295
    /**
296
     * Return the prefix required to build relative links on the current site.
297
     * This includes the http[s] prefix, the hosts port (if necessary) and the
298
     * base url of the main host. This is not necessarily the currently active
299
     * MidCOM Page however, use the get_page_prefix() function for that.
300
     *
301
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/
302
     */
303 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...
304
    {
305 7
        if (!$this->_cached_host_prefix) {
306 1
            $host_name = $this->get_host_name();
307 1
            $host_prefix = midcom_connection::get_url('prefix');
308 1
            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

308
            if (!str_starts_with(/** @scrutinizer ignore-type */ $host_prefix, '/')) {
Loading history...
309
                $host_prefix = "/{$host_prefix}";
310
            }
311 1
            if (!str_ends_with($host_prefix, '/')) {
312
                $host_prefix .= '/';
313
            }
314 1
            $this->_cached_host_prefix = "{$host_name}{$host_prefix}";
315
        }
316
317 7
        return $this->_cached_host_prefix;
318
    }
319
320
    /* *************************************************************************
321
     * Generic Helper Functions not directly related with MidCOM:
322
     *
323
     * relocate           - executes a HTTP relocation to the given URL
324
     */
325
326
    /**
327
     * Sends a header out to the client.
328
     *
329
     * This function is syntactically identical to
330
     * the regular PHP header() function, but is integrated into the framework. Every
331
     * Header you sent must go through this function or it might be lost later on;
332
     * this is especially important with caching.
333
     */
334 17
    public function header(string $header, int $response_code = 0)
335
    {
336 17
        $this->cache->content->register_sent_header($header);
337 17
        midcom_compat_environment::header($header, true, $response_code);
338 17
    }
339
340
    /**
341
     * Relocate to another URL.
342
     *
343
     * Note, that this function automatically makes the page uncacheable, calls
344
     * midcom_finish and exit, so it will never return. If the headers have already
345
     * been sent, this will leave you with a partially completed page, so beware.
346
     */
347
    public function relocate(string $url, int $response_code = 302)
348
    {
349
        $response = new midcom_response_relocate($url, $response_code);
350
        $response->send();
351
        $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

351
        /** @scrutinizer ignore-deprecated */ $this->finish();
Loading history...
352
    }
353
354
    /**
355
     * Raise some PHP limits for resource-intensive tasks
356
     */
357 8
    public function disable_limits()
358
    {
359 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

359
        $stat = @ini_set('max_execution_time', /** @scrutinizer ignore-type */ $this->config->get('midcom_max_execution_time'));
Loading history...
360 8
        if (false === $stat) {
361
            debug_add('ini_set("max_execution_time", ' . $this->config->get('midcom_max_execution_time') . ') returned false', MIDCOM_LOG_WARN);
362
        }
363 8
        $stat = @ini_set('memory_limit', $this->config->get('midcom_max_memory'));
364 8
        if (false === $stat) {
365
            debug_add('ini_set("memory_limit", ' . $this->config->get('midcom_max_memory') . ') returned false', MIDCOM_LOG_WARN);
366
        }
367 8
    }
368
}
369