Failed Conditions
Push — rbac ( be68b4...52c28b )
by Michael
03:11
created

PageBase::runPage()   B

Complexity

Conditions 6
Paths 93

Size

Total Lines 72
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 72
ccs 0
cts 39
cp 0
rs 8.8017
cc 6
nc 93
nop 0
crap 42

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Tasks;
10
11
use Exception;
12
use Waca\DataObjects\SiteNotice;
13
use Waca\DataObjects\User;
14
use Waca\Exceptions\ApplicationLogicException;
15
use Waca\Exceptions\OptimisticLockFailedException;
16
use Waca\Fragments\TemplateOutput;
17
use Waca\Security\TokenManager;
18
use Waca\SessionAlert;
19
use Waca\WebRequest;
20
21
abstract class PageBase extends TaskBase implements IRoutedTask
22
{
23
    use TemplateOutput;
24
    /** @var string Smarty template to display */
25
    protected $template = "base.tpl";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal base.tpl does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
26
    /** @var string HTML title. Currently unused. */
27
    protected $htmlTitle;
28
    /** @var bool Determines if the page is a redirect or not */
29
    protected $isRedirecting = false;
30
    /** @var array Queue of headers to be sent on successful completion */
31
    protected $headerQueue = array();
32
    /** @var string The name of the route to use, as determined by the request router. */
33
    private $routeName = null;
34
    /** @var TokenManager */
35
    protected $tokenManager;
36
    /** @var string[] Extra CSS files to include */
37
    private $extraCss = array();
38
    /** @var string[] Extra JS files to include */
39
    private $extraJs = array();
40
41
    /**
42
     * Sets the route the request will take. Only should be called from the request router or barrier test.
43
     *
44
     * @param string $routeName        The name of the route
45
     * @param bool   $skipCallableTest Don't use this unless you know what you're doing, and what the implications are.
46
     *
47
     * @throws Exception
48
     * @category Security-Critical
49
     */
50
    final public function setRoute($routeName, $skipCallableTest = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$skipCallableTest" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$skipCallableTest"; expected 0 but found 1
Loading history...
51
    {
52
        // Test the new route is callable before adopting it.
53
        if (!$skipCallableTest && !is_callable(array($this, $routeName))) {
54
            throw new Exception("Proposed route '$routeName' is not callable.");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $routeName instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
55
        }
56
57
        // Adopt the new route
58
        $this->routeName = $routeName;
59
    }
0 ignored issues
show
Coding Style introduced by
Expected //end setRoute()
Loading history...
60
61
    /**
62
     * Gets the name of the route that has been passed from the request router.
63
     * @return string
64
     */
65
    final public function getRouteName()
66
    {
67
        return $this->routeName;
68
    }
0 ignored issues
show
Coding Style introduced by
Expected //end getRouteName()
Loading history...
69
70
    /**
71
     * Performs generic page setup actions
72
     */
73
    final protected function setupPage()
74
    {
75
        $this->setUpSmarty();
76
77
        $siteNoticeText = SiteNotice::get($this->getDatabase());
78
79
        $this->assign('siteNoticeText', $siteNoticeText);
80
81
        $currentUser = User::getCurrent($this->getDatabase());
82
        $this->assign('currentUser', $currentUser);
83
        $this->assign('loggedIn', (!$currentUser->isCommunityUser()));
84
    }
0 ignored issues
show
Coding Style introduced by
Expected //end setupPage()
Loading history...
85
86
    /**
87
     * Runs the page logic as routed by the RequestRouter
88
     *
89
     * Only should be called after a security barrier! That means only from execute().
90
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
91
    final protected function runPage()
92
    {
93
        $database = $this->getDatabase();
94
95
        // initialise a database transaction
96
        if (!$database->beginTransaction()) {
97
            throw new Exception('Failed to start transaction on primary database.');
98
        }
99
100
        try {
101
            // run the page code
102
            $this->{$this->getRouteName()}();
103
104
            $database->commit();
105
        }
106
        catch (ApplicationLogicException $ex) {
107
            // it's an application logic exception, so nothing went seriously wrong with the site. We can use the
108
            // standard templating system for this.
109
110
            // Firstly, let's undo anything that happened to the database.
111
            $database->rollBack();
112
113
            // Reset smarty
114
            $this->setUpSmarty();
115
116
            // Set the template
117
            $this->setTemplate('exception/application-logic.tpl');
118
            $this->assign('message', $ex->getMessage());
119
120
            // Force this back to false
121
            $this->isRedirecting = false;
122
            $this->headerQueue = array();
123
        }
124
        catch (OptimisticLockFailedException $ex) {
125
            // it's an optimistic lock failure exception, so nothing went seriously wrong with the site. We can use the
126
            // standard templating system for this.
127
128
            // Firstly, let's undo anything that happened to the database.
129
            $database->rollBack();
130
131
            // Reset smarty
132
            $this->setUpSmarty();
133
134
            // Set the template
135
            $this->setTemplate('exception/optimistic-lock-failure.tpl');
136
            $this->assign('message', $ex->getMessage());
137
138
            // Force this back to false
139
            $this->isRedirecting = false;
140
            $this->headerQueue = array();
141
        }
142
        finally {
143
            // Catch any hanging on transactions
144
            if ($database->hasActiveTransaction()) {
145
                $database->rollBack();
146
            }
147
        }
148
149
        // run any finalisation code needed before we send the output to the browser.
150
        $this->finalisePage();
151
152
        // Send the headers
153
        $this->sendResponseHeaders();
154
155
        // Check we have a template to use!
156
        if ($this->template !== null) {
157
            $content = $this->fetchTemplate($this->template);
158
            ob_clean();
159
            print($content);
160
            ob_flush();
161
162
            return;
163
        }
164
    }
0 ignored issues
show
Coding Style introduced by
Expected //end runPage()
Loading history...
165
166
    /**
167
     * Performs final tasks needed before rendering the page.
168
     */
169
    protected function finalisePage()
170
    {
171
        if ($this->isRedirecting) {
172
            $this->template = null;
173
174
            return;
175
        }
176
177
        $this->assign('extraCss', $this->extraCss);
178
        $this->assign('extraJs', $this->extraJs);
179
180
        // If we're actually displaying content, we want to add the session alerts here!
181
        $this->assign('alerts', SessionAlert::getAlerts());
182
        SessionAlert::clearAlerts();
183
184
        $this->assign('htmlTitle', $this->htmlTitle);
185
    }
0 ignored issues
show
Coding Style introduced by
Expected //end finalisePage()
Loading history...
186
187
    /**
188
     * @return TokenManager
189
     */
190
    public function getTokenManager()
191
    {
192
        return $this->tokenManager;
193
    }
0 ignored issues
show
Coding Style introduced by
Expected //end getTokenManager()
Loading history...
194
195
    /**
196
     * @param TokenManager $tokenManager
197
     */
198
    public function setTokenManager($tokenManager)
199
    {
200
        $this->tokenManager = $tokenManager;
201
    }
0 ignored issues
show
Coding Style introduced by
Expected //end setTokenManager()
Loading history...
202
203
    /**
204
     * Sends the redirect headers to perform a GET at the destination page.
205
     *
206
     * Also nullifies the set template so Smarty does not render it.
207
     *
208
     * @param string      $page   The page to redirect requests to (as used in the UR)
209
     * @param null|string $action The action to use on the page.
210
     * @param null|array  $parameters
211
     * @param null|string $script The script (relative to index.php) to redirect to
212
     */
213
    final protected function redirect($page = '', $action = null, $parameters = null, $script = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$page" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$page"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$action" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$action"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$parameters" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$parameters"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$script" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$script"; expected 0 but found 1
Loading history...
214
    {
215
        $currentScriptName = WebRequest::scriptName();
216
217
        // Are we changing script?
218
        if ($script === null || substr($currentScriptName, -1 * count($script)) === $script) {
0 ignored issues
show
Bug introduced by
$script of type string is incompatible with the type Countable|array expected by parameter $var of count(). ( Ignorable by Annotation )

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

218
        if ($script === null || substr($currentScriptName, -1 * count(/** @scrutinizer ignore-type */ $script)) === $script) {
Loading history...
219
            $targetScriptName = $currentScriptName;
220
        }
221
        else {
222
            $targetScriptName = $this->getSiteConfiguration()->getBaseUrl() . '/' . $script;
223
        }
224
225
        $pathInfo = array($targetScriptName);
226
227
        $pathInfo[1] = $page;
228
229
        if ($action !== null) {
230
            $pathInfo[2] = $action;
231
        }
232
233
        $url = implode('/', $pathInfo);
234
235
        if (is_array($parameters) && count($parameters) > 0) {
236
            $url .= '?' . http_build_query($parameters);
237
        }
238
239
        $this->redirectUrl($url);
240
    }
0 ignored issues
show
Coding Style introduced by
Expected //end redirect()
Loading history...
241
242
    /**
243
     * Sends the redirect headers to perform a GET at the new address.
244
     *
245
     * Also nullifies the set template so Smarty does not render it.
246
     *
247
     * @param string $path URL to redirect to
248
     */
249
    final protected function redirectUrl($path)
250
    {
251
        // 303 See Other = re-request at new address with a GET.
252
        $this->headerQueue[] = 'HTTP/1.1 303 See Other';
253
        $this->headerQueue[] = "Location: $path";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $path instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
254
255
        $this->setTemplate(null);
256
        $this->isRedirecting = true;
257
    }
0 ignored issues
show
Coding Style introduced by
Expected //end redirectUrl()
Loading history...
258
259
    /**
260
     * Sets the name of the template this page should display.
261
     *
262
     * @param string $name
263
     *
264
     * @throws Exception
265
     */
266
    final protected function setTemplate($name)
267
    {
268
        if ($this->isRedirecting) {
269
            throw new Exception('This page has been set as a redirect, no template can be displayed!');
270
        }
271
272
        $this->template = $name;
273
    }
0 ignored issues
show
Coding Style introduced by
Expected //end setTemplate()
Loading history...
274
275
    /**
276
     * Adds an extra CSS file to to the page
277
     *
278
     * @param string $path The path (relative to the application root) of the file
279
     */
280
    final protected function addCss($path) {
281
        if(in_array($path, $this->extraCss)){
282
            // nothing to do
283
            return;
284
        }
285
286
        $this->extraCss[] = $path;
287
    }
0 ignored issues
show
Coding Style introduced by
Expected //end addCss()
Loading history...
288
289
    /**
290
     * Adds an extra JS file to to the page
291
     *
292
     * @param string $path The path (relative to the application root) of the file
293
     */
294
    final protected function addJs($path){
295
        if(in_array($path, $this->extraJs)){
296
            // nothing to do
297
            return;
298
        }
299
300
        $this->extraJs[] = $path;
301
    }
0 ignored issues
show
Coding Style introduced by
Expected //end addJs()
Loading history...
302
303
    /**
304
     * Main function for this page, when no specific actions are called.
305
     * @return void
306
     */
307
    abstract protected function main();
308
309
    /**
310
     * @param string $title
311
     */
312
    final protected function setHtmlTitle($title)
313
    {
314
        $this->htmlTitle = $title;
315
    }
0 ignored issues
show
Coding Style introduced by
Expected //end setHtmlTitle()
Loading history...
316
317
    public function execute()
318
    {
319
        if ($this->getRouteName() === null) {
0 ignored issues
show
introduced by
The condition $this->getRouteName() === null is always false.
Loading history...
320
            throw new Exception('Request is unrouted.');
321
        }
322
323
        if ($this->getSiteConfiguration() === null) {
324
            throw new Exception('Page has no configuration!');
325
        }
326
327
        $this->setupPage();
328
329
        $this->runPage();
330
    }
0 ignored issues
show
Coding Style introduced by
Expected //end execute()
Loading history...
331
332
    public function assignCSRFToken()
333
    {
334
        $token = $this->tokenManager->getNewToken();
335
        $this->assign('csrfTokenData', $token->getTokenData());
336
    }
0 ignored issues
show
Coding Style introduced by
Expected //end assignCSRFToken()
Loading history...
337
338
    public function validateCSRFToken()
339
    {
340
        if (!$this->tokenManager->validateToken(WebRequest::postString('csrfTokenData'))) {
341
            throw new ApplicationLogicException('Form token is not valid, please reload and try again');
342
        }
343
    }
0 ignored issues
show
Coding Style introduced by
Expected //end validateCSRFToken()
Loading history...
344
345
    protected function sendResponseHeaders()
346
    {
347
        if (headers_sent()) {
348
            throw new ApplicationLogicException          ('Headers have already been sent! This is likely a bug in the application.');
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
349
        }
350
351
        foreach ($this->headerQueue as $item) {
352
            if (mb_strpos($item, "\r") !== false || mb_strpos($item, "\n") !== false) {
353
                // Oops. We're not allowed to do this.
354
                throw new Exception('Unable to split header');
355
            }
356
357
            header($item);
358
        }
359
    }
0 ignored issues
show
Coding Style introduced by
Expected //end sendResponseHeaders()
Loading history...
360
}
0 ignored issues
show
Coding Style introduced by
Expected //end class
Loading history...
361