Completed
Push — master ( 01ca5d...148252 )
by Andreas
17:12
created

midcom_application::get_page_prefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
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
        midcom_compat_environment::initialize();
86
        $this->request = Request::createFromGlobals();
87
        $this->cfg = new midcom_config;
88
        parent::__construct($environment, $debug);
89
    }
90
91
    public function registerContainerConfiguration(LoaderInterface $loader)
92
    {
93
        if (file_exists($this->getProjectDir() . '/config/services.yml')) {
94
            $loader->load($this->getProjectDir() . '/config/services.yml');
95
        }
96
        if ($classes = midcom::get_registered_service_classes()) {
97
            $loader->load(function (ContainerBuilder $container) use ($classes) {
98
                foreach ($classes as $id => $class) {
99
                    $container->findDefinition($id)->setClass($class);
100
                }
101
            });
102
        }
103
    }
104
105
    protected function initializeContainer()
106
    {
107
        parent::initializeContainer();
108
        $this->container->set('config', $this->cfg);
109
    }
110
111
    protected function buildContainer()
112
    {
113
        $container = parent::buildContainer();
114
        $this->cfg->export_to($container);
115
        return $container;
116
    }
117
118
    public function registerBundles()
119
    {
120
        return [new midcomBundle];
121
    }
122
123
    public function getProjectDir()
124
    {
125
        if ($this->project_dir === null) {
126
            if (basename(dirname(__DIR__, 4)) === 'vendor') {
127
                // this is the case where we're installed as a dependency
128
                $this->project_dir = dirname(__DIR__, 5);
129
            } else {
130
                $this->project_dir = dirname(__DIR__, 2);
131
            }
132
        }
133
        return $this->project_dir;
134
    }
135
136 324
    public function getCacheDir()
137
    {
138 324
        return $this->cfg->get('cache_base_directory') ?: parent::getCacheDir();
139
    }
140
141
    /**
142
     * Magic getter for service loading
143
     */
144 678
    public function __get($key)
145
    {
146 678
        return $this->getContainer()->get($key);
147
    }
148
149
    /**
150
     * Magic setter
151
     */
152
    public function __set($key, $value)
153
    {
154
        $this->getContainer()->set($key, $value);
155
    }
156
157
    /* *************************************************************************
158
     * Control framework:
159
     * codeinit      - Handle the current request
160
     * dynamic_load   - Dynamically load and execute a URL
161
     * finish         - Cleanup Work
162
     */
163
164
    /**
165
     * Initialize the URL parser and process the request.
166
     *
167
     * This function must be called before any output starts.
168
     */
169
    public function codeinit()
170
    {
171
        try {
172
            $response = $this->handle($this->request);
173
            $response->send();
174
            $this->terminate($this->request, $response);
175
        } catch (Error $e) {
176
            $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

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

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

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

349
        /** @scrutinizer ignore-deprecated */ $this->finish();
Loading history...
350
    }
351
352
    /**
353
     * Raise some PHP limits for resource-intensive tasks
354
     */
355 8
    public function disable_limits()
356
    {
357 8
        $stat = @ini_set('max_execution_time', $this->config->get('midcom_max_execution_time'));
358 8
        if (false === $stat) {
359
            debug_add('ini_set("max_execution_time", ' . $this->config->get('midcom_max_execution_time') . ') returned false', MIDCOM_LOG_WARN);
360
        }
361 8
        $stat = @ini_set('memory_limit', $this->config->get('midcom_max_memory'));
362 8
        if (false === $stat) {
363
            debug_add('ini_set("memory_limit", ' . $this->config->get('midcom_max_memory') . ') returned false', MIDCOM_LOG_WARN);
364
        }
365 8
    }
366
}
367