Completed
Push — master ( a3b87b...c9d36f )
by Andreas
14:59
created

midcom_application::relocate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 2
rs 10
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\HttpKernel;
12
13
/**
14
 * Main controlling instance of the MidCOM Framework
15
 *
16
 * @property midcom_services_i18n $i18n
17
 * @property midcom_helper__componentloader $componentloader
18
 * @property midcom_services_dbclassloader $dbclassloader
19
 * @property midcom_helper__dbfactory $dbfactory
20
 * @property midcom_helper_head $head
21
 * @property midcom_helper__styleloader $style
22
 * @property midcom_services_auth $auth
23
 * @property midcom_services_permalinks $permalinks
24
 * @property midcom_services_toolbars $toolbars
25
 * @property midcom_services_uimessages $uimessages
26
 * @property midcom_services_metadata $metadata
27
 * @property midcom_services_rcs $rcs
28
 * @property midcom_services__sessioning $session
29
 * @property midcom_services_indexer $indexer
30
 * @property midcom_config $config
31
 * @property midcom_services_cache $cache
32
 * @property midcom\events\dispatcher $dispatcher
33
 * @property midcom_debug $debug
34
 * @package midcom
35
 */
36
class midcom_application
37
{
38
    /**
39
     * Host prefix cache to avoid computing it each time.
40
     *
41
     * @var string
42
     * @see get_host_prefix()
43
     */
44
    private $_cached_host_prefix = '';
45
46
    /**
47
     * Page prefix cache to avoid computing it each time.
48
     *
49
     * @var string
50
     * @see get_page_prefix()
51
     */
52
    private $_cached_page_prefix = '';
53
54
    /**
55
     * @var Request
56
     */
57
    private $request;
58
59
    /**
60
     * @var HttpKernel
61
     */
62
    private $httpkernel;
63
64
    /**
65
     * Set this variable to true during the handle phase of your component to
66
     * not show the site's style around the component output. This is mainly
67
     * targeted at XML output like RSS feeds and similar things. The output
68
     * handler of the site, excluding the style-init/-finish tags will be executed
69
     * immediately after the handle phase
70
     *
71
     * Changing this flag after the handle phase or for dynamically loaded
72
     * components won't change anything.
73
     *
74
     * @var boolean
75
     */
76
    public $skip_page_style = false;
77
78
    public function __construct(HttpKernel $httpkernel)
79
    {
80
        $this->httpkernel = $httpkernel;
81
        midcom_compat_environment::initialize();
82
        midcom_exception_handler::register($httpkernel);
83
        $this->request = Request::createFromGlobals();
84
    }
85
86
    /**
87
     * Magic getter for service loading
88
     */
89 730
    public function __get($key)
90
    {
91 730
        return midcom::get($key);
92
    }
93
94
    /**
95
     * Magic setter
96
     */
97
    public function __set($key, $value)
98
    {
99
        midcom::get()->$key = $value;
100
    }
101
102
    /* *************************************************************************
103
     * Control framework:
104
     * codeinit      - Handle the current request
105
     * dynamic_load   - Dynamically load and execute a URL
106
     * finish         - Cleanup Work
107
     */
108
109
    /**
110
     * Initialize the URL parser and process the request.
111
     *
112
     * This function must be called before any output starts.
113
     */
114
    public function codeinit()
115
    {
116
        $this->httpkernel->handle($this->request)->send();
117
    }
118
119
    /**
120
     * Dynamically execute a subrequest and insert its output in place of the
121
     * function call.
122
     *
123
     * It tries to load the component referenced with the URL $url and executes
124
     * it as if it was used as primary component.
125
     *
126
     * This is only possible if the system is in the Page-Style output phase. It
127
     * cannot be used within code-init or during the output phase of another
128
     * component.
129
     *
130
     * Example code, executed on a site's homepage, it will load the news listing from
131
     * the given URL and display it using a substyle of the node style that is assigned
132
     * to the loaded one:
133
     *
134
     * <code>
135
     * $blog = '/blog/latest/3/';
136
     * $substyle = 'homepage';
137
     * midcom::get()->dynamic_load("/midcom-substyle-{$substyle}/{$blog}");
138
     * </code>
139
     *
140
     * Results of dynamic_loads are cached with the system cache strategy
141
     *
142
     * @param string $url                The URL, relative to the Midgard Page, that is to be requested.
143
     */
144 16
    public function dynamic_load($url)
145
    {
146 16
        debug_add("Dynamic load of URL {$url}");
147 16
        $url = midcom_connection::get_url('prefix') . $url;
148
149
        // Determine new Context ID and set current context,
150
        // enter that context and prepare its data structure.
151 16
        $oldcontext = midcom_core_context::get();
152 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

152
        $context = midcom_core_context::enter($url, /** @scrutinizer ignore-type */ $oldcontext->get_key(MIDCOM_CONTEXT_ROOTTOPIC));
Loading history...
153
154 16
        $request = $this->request->duplicate([], null, []);
155 16
        $request->attributes->set('context', $context);
156
157 16
        $cached = $this->cache->content->check_dl_hit($request);
158 16
        if ($cached !== false) {
159
            echo $cached;
160
            midcom_core_context::leave();
161
            return;
162
        }
163
164 16
        $backup = $this->skip_page_style;
165 16
        $this->skip_page_style = true;
166
        try {
167 16
            $response = $this->httpkernel->handle($request, HttpKernelInterface::SUB_REQUEST, false);
168 12
        } catch (midcom_error $e) {
169 12
            if ($e instanceof midcom_error_notfound || $e instanceof midcom_error_forbidden) {
170 12
                $e->log();
171 12
                midcom_core_context::leave();
172 12
                return;
173
            }
174
            throw $e;
175 4
        } finally {
176 16
            $this->skip_page_style = $backup;
177
        }
178
179 4
        $dl_cache_data = $response->getContent();
180 4
        echo $dl_cache_data;
181
182
        /* Cache DL the content */
183 4
        $this->cache->content->store_dl_content($context->id, $dl_cache_data, $request);
184
185 4
        midcom_core_context::leave();
186 4
    }
187
188
    /**
189
     * Exit from the framework, execute after all output has been made.
190
     *
191
     * <b>WARNING:</b> Anything done after calling this method will be lost.
192
     */
193
    public function finish()
194
    {
195
        debug_add("End of MidCOM run: " . $this->request->server->get('REQUEST_URI'));
196
        _midcom_stop_request();
197
    }
198
199
    /* *************************************************************************
200
     * Framework Access Helper functions
201
     */
202
203
    /**
204
     * Retrieves the name of the current host, fully qualified with protocol and
205
     * port.
206
     *
207
     * @return string Full Hostname (http[s]://www.my.domain.com[:1234])
208
     */
209 37
    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...
210
    {
211 37
        return $this->request->getSchemeAndHttpHost();
212
    }
213
214
    /**
215
     * Return the prefix required to build relative links on the current site.
216
     * This includes the http[s] prefix, the hosts port (if necessary) and the
217
     * base url of the Midgard Page. Be aware, that this does *not* point to the
218
     * base host of the site.
219
     *
220
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/page_prefix/
221
     *
222
     * @return string The current MidCOM page URL prefix.
223
     */
224
    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...
225
    {
226
        if (!$this->_cached_page_prefix) {
227
            $host_name = $this->get_host_name();
228
            $this->_cached_page_prefix = $host_name . midcom_connection::get_url('self');
229
        }
230
231
        return $this->_cached_page_prefix;
232
    }
233
234
    /**
235
     * Return the prefix required to build relative links on the current site.
236
     * This includes the http[s] prefix, the hosts port (if necessary) and the
237
     * base url of the main host. This is not necessarily the currently active
238
     * MidCOM Page however, use the get_page_prefix() function for that.
239
     *
240
     * e.g. something like http[s]://www.domain.com[:8080]/host_prefix/
241
     *
242
     * @return string The host's root page URL prefix.
243
     */
244 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...
245
    {
246 7
        if (!$this->_cached_host_prefix) {
247 1
            $host_name = $this->get_host_name();
248 1
            $host_prefix = midcom_connection::get_url('prefix');
249 1
            if (substr($host_prefix, 0, 1) != '/') {
250
                $host_prefix = "/{$host_prefix}";
251
            }
252 1
            if (substr($host_prefix, -1, 1) != '/') {
253
                $host_prefix .= '/';
254
            }
255 1
            $this->_cached_host_prefix = "{$host_name}{$host_prefix}";
256
        }
257
258 7
        return $this->_cached_host_prefix;
259
    }
260
261
    /* *************************************************************************
262
     * Generic Helper Functions not directly related with MidCOM:
263
     *
264
     * relocate           - executes a HTTP relocation to the given URL
265
     */
266
267
    /**
268
     * Sends a header out to the client.
269
     *
270
     * This function is syntactically identical to
271
     * the regular PHP header() function, but is integrated into the framework. Every
272
     * Header you sent must go through this function or it might be lost later on;
273
     * this is especially important with caching.
274
     *
275
     * @param string $header    The header to send.
276
     * @param integer $response_code HTTP response code to send with the header
277
     */
278 17
    public function header($header, $response_code = null)
279
    {
280 17
        $this->cache->content->register_sent_header($header);
281 17
        midcom_compat_environment::get()->header($header, true, $response_code);
282 17
    }
283
284
    /**
285
     * Relocate to another URL.
286
     *
287
     * The helper actually can distinguish between site-local, absolute redirects and external
288
     * redirects. If the url does not start with http[s] or /, it is taken as a URL relative to
289
     * the current anchor prefix, which gets prepended automatically (no other characters
290
     * as the anchor prefix get inserted).
291
     *
292
     * Fully qualified urls (starting with http[s]) are used as-is.
293
     *
294
     * Note, that this function automatically makes the page uncacheable, calls
295
     * midcom_finish and exit, so it will never return. If the headers have already
296
     * been sent, this will leave you with a partially completed page, so beware.
297
     *
298
     * @param string $url    The URL to redirect to, will be preprocessed as outlined above.
299
     * @param int $response_code HTTP response code to send with the relocation, from 3xx series
300
     */
301
    public function relocate($url, $response_code = 302)
302
    {
303
        $response = new midcom_response_relocate($url, $response_code);
304
        $response->send();
305
        $this->finish();
306
    }
307
308
    /**
309
     * Raise some PHP limits for resource-intensive tasks
310
     */
311 8
    public function disable_limits()
312
    {
313 8
        $stat = @ini_set('max_execution_time', $this->config->get('midcom_max_execution_time'));
314 8
        if (false === $stat) {
315
            debug_add('ini_set("max_execution_time", ' . $this->config->get('midcom_max_execution_time') . ') returned false', MIDCOM_LOG_WARN);
316
        }
317 8
        $stat = @ini_set('memory_limit', $this->config->get('midcom_max_memory'));
318 8
        if (false === $stat) {
319
            debug_add('ini_set("memory_limit", ' . $this->config->get('midcom_max_memory') . ') returned false', MIDCOM_LOG_WARN);
320
        }
321 8
    }
322
}
323