Completed
Branch master (9dcfc4)
by Daniel
24:32
created

EnvironmentChecker::index()   C

Complexity

Conditions 11
Paths 32

Size

Total Lines 54
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 54
rs 6.6153
c 0
b 0
f 0
cc 11
eloc 34
nc 32
nop 0

How to fix   Long Method    Complexity   

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
namespace SilverStripe\EnvironmentCheck;
4
5
use Psr\Log\LogLevel;
6
use Psr\Log\LoggerInterface;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Control\Email\Email;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Control\HTTPResponse_Exception;
11
use SilverStripe\Control\RequestHandler;
12
use SilverStripe\Core\Config\Config;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Dev\Deprecation;
15
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
16
use SilverStripe\EnvironmentCheck\EnvironmentCheckSuite;
17
use SilverStripe\Security\BasicAuth;
18
use SilverStripe\Security\Member;
19
use SilverStripe\Security\Permission;
20
21
/**
22
 * Provides an interface for checking the given EnvironmentCheckSuite.
23
 *
24
 * @package environmentcheck
25
 */
26
class EnvironmentChecker extends RequestHandler
27
{
28
    /**
29
     * @var array
30
     */
31
    private static $url_handlers = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $url_handlers is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
32
        '' => 'index',
33
    ];
34
35
    /**
36
     * @var string
37
     */
38
    protected $checkSuiteName;
39
40
    /**
41
     * @var string
42
     */
43
    protected $title;
44
45
    /**
46
     * @var int
47
     */
48
    protected $errorCode = 500;
49
50
    /**
51
     * @var null|string
52
     */
53
    private static $to_email_address = null;
0 ignored issues
show
Unused Code introduced by
The property $to_email_address is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
54
55
    /**
56
     * @var null|string
57
     */
58
    private static $from_email_address = null;
0 ignored issues
show
Unused Code introduced by
The property $from_email_address is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
59
60
    /**
61
     * @var bool
62
     */
63
    private static $email_results = false;
0 ignored issues
show
Unused Code introduced by
The property $email_results is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
64
65
    /**
66
     * @var bool Log results via {@link \Psr\Log\LoggerInterface}
67
     */
68
    private static $log_results_warning = false;
0 ignored issues
show
Unused Code introduced by
The property $log_results_warning is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
69
70
    /**
71
     * @var string Maps to {@link \Psr\Log\LogLevel} levels. Defaults to LogLevel::WARNING
72
     */
73
    private static $log_results_warning_level = LogLevel::WARNING;
0 ignored issues
show
Unused Code introduced by
The property $log_results_warning_level is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
74
75
    /**
76
     * @var bool Log results via a {@link \Psr\Log\LoggerInterface}
77
     */
78
    private static $log_results_error = false;
0 ignored issues
show
Unused Code introduced by
The property $log_results_error is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
79
80
    /**
81
     * @var int Maps to {@link \Psr\Log\LogLevel} levels. Defaults to LogLevel::ALERT
82
     */
83
    private static $log_results_error_level = LogLevel::ALERT;
0 ignored issues
show
Unused Code introduced by
The property $log_results_error_level is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
84
85
    /**
86
     * @param string $checkSuiteName
87
     * @param string $title
88
     */
89
    public function __construct($checkSuiteName, $title)
90
    {
91
        parent::__construct();
92
93
        $this->checkSuiteName = $checkSuiteName;
94
        $this->title = $title;
95
    }
96
97
    /**
98
     * @param string $permission
99
     *
100
     * @throws HTTPResponse_Exception
101
     */
102
    public function init($permission = 'ADMIN')
0 ignored issues
show
Coding Style introduced by
init uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
103
    {
104
        // if the environment supports it, provide a basic auth challenge and see if it matches configured credentials
105
        if (getenv('ENVCHECK_BASICAUTH_USERNAME') && getenv('ENVCHECK_BASICAUTH_PASSWORD')) {
106
            if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
107
                // authenticate the input user/pass with the configured credentials
108
                if (!(
109
                        $_SERVER['PHP_AUTH_USER'] == getenv('ENVCHECK_BASICAUTH_USERNAME')
110
                        && $_SERVER['PHP_AUTH_PW'] == getenv('ENVCHECK_BASICAUTH_PASSWORD')
111
                    )
112
                ) {
113
                    $response = new HTTPResponse(null, 401);
114
                    $response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
115
                    // Exception is caught by RequestHandler->handleRequest() and will halt further execution
116
                    $e = new HTTPResponse_Exception(null, 401);
117
                    $e->setResponse($response);
118
                    throw $e;
119
                }
120
            } else {
121
                $response = new HTTPResponse(null, 401);
122
                $response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
123
                // Exception is caught by RequestHandler->handleRequest() and will halt further execution
124
                $e = new HTTPResponse_Exception(null, 401);
125
                $e->setResponse($response);
126
                throw $e;
127
            }
128
        } else {
129
            if (!$this->canAccess(null, $permission)) {
130
                return $this->httpError(403);
131
            }
132
        }
133
    }
134
135
    /**
136
     * @param null|int|Member $member
137
     * @param string $permission
138
     *
139
     * @return bool
140
     *
141
     * @throws HTTPResponse_Exception
142
     */
143
    public function canAccess($member = null, $permission = 'ADMIN')
144
    {
145
        if (!$member) {
146
            $member = Member::currentUser();
147
        }
148
149
        if (!$member) {
150
            $member = BasicAuth::requireLogin('Environment Checker', $permission, false);
151
        }
152
153
        // We allow access to this controller regardless of live-status or ADMIN permission only
154
        // if on CLI.  Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
155
        if (Director::isDev()
156
            || Director::is_cli()
157
            || empty($permission)
158
            || Permission::checkMember($member, $permission)
159
        ) {
160
            return true;
161
        }
162
163
        // Extended access checks.
164
        // "Veto" style, return NULL to abstain vote.
165
        $canExtended = null;
0 ignored issues
show
Unused Code introduced by
$canExtended is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
166
        $results = $this->extend('canAccess', $member);
167
        if ($results && is_array($results)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
168
            if (!min($results)) {
169
                return false;
170
            }
171
            return true;
172
        }
173
174
        return false;
175
    }
176
177
    /**
178
     * @return HTTPResponse
179
     */
180
    public function index()
181
    {
182
        $response = new HTTPResponse;
183
        $result = EnvironmentCheckSuite::inst($this->checkSuiteName)->run();
184
185
        if (!$result->ShouldPass()) {
186
            $response->setStatusCode($this->errorCode);
187
        }
188
189
        $resultText = $result->customise([
190
            'URL' => Director::absoluteBaseURL(),
191
            'Title' => $this->title,
192
            'Name' => $this->checkSuiteName,
193
            'ErrorCode' => $this->errorCode,
194
        ])->renderWith(__CLASS__);
195
196
        if ($this->config()->email_results && !$result->ShouldPass()) {
197
            $email = new Email(
198
                $this->config()->from_email_address,
199
                $this->config()->to_email_address,
200
                $this->title,
201
                $resultText
202
            );
203
            $email->send();
204
        }
205
206
        // Optionally log errors and warnings individually
207
        foreach ($result->Details() as $detail) {
208
            if ($this->config()->log_results_warning && $detail->StatusCode == EnvironmentCheck::WARNING) {
209
                $this->log(
210
                    sprintf('EnvironmentChecker warning at "%s" check. Message: %s', $detail->Check, $detail->Message),
211
                    $this->config()->log_results_warning_level
212
                );
213
            } elseif ($this->config()->log_results_error && $detail->StatusCode == EnvironmentCheck::ERROR) {
214
                $this->log(
215
                    sprintf('EnvironmentChecker error at "%s" check. Message: %s', $detail->Check, $detail->Message),
216
                    $this->config()->log_results_error_level
217
                );
218
            }
219
        }
220
221
        // output the result as JSON if requested
222
        if ($this->getRequest()->getExtension() == 'json'
223
            || strpos($this->getRequest()->getHeader('Accept'), 'application/json') !== false
224
        ) {
225
            $response->setBody($result->toJSON());
226
            $response->addHeader('Content-Type', 'application/json');
227
            return $response;
228
        }
229
230
        $response->setBody($resultText);
231
232
        return $response;
233
    }
234
235
    /**
236
     * Sends a log entry to the configured PSR-3 LoggerInterface
237
     *
238
     * @param string $message
239
     * @param int $level
240
     */
241
    public function log($message, $level)
242
    {
243
        Injector::inst()->get(LoggerInterface::class)->log($level, $message);
244
    }
245
246
    /**
247
     * Set the HTTP status code that should be returned when there's an error.
248
     *
249
     * @param int $errorCode
250
     */
251
    public function setErrorCode($errorCode)
252
    {
253
        $this->errorCode = $errorCode;
254
    }
255
256
    /**
257
     * @deprecated
258
     * @param string $from
259
     */
260
    public static function set_from_email_address($from)
261
    {
262
        Deprecation::notice('2.0', 'Use config API instead');
263
        Config::modify()->set(__CLASS__, 'from_email_address', $from);
0 ignored issues
show
Bug introduced by
The method modify() does not seem to exist on object<SilverStripe\Core\Config\Config>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
264
    }
265
266
    /**
267
     * @deprecated
268
     * @return null|string
269
     */
270
    public static function get_from_email_address()
271
    {
272
        Deprecation::notice('2.0', 'Use config API instead');
273
        return Config::inst()->get(__CLASS__, 'from_email_address');
274
    }
275
276
    /**
277
     * @deprecated
278
     * @param string $to
279
     */
280
    public static function set_to_email_address($to)
281
    {
282
        Deprecation::notice('2.0', 'Use config API instead');
283
        Config::modify()->set(__CLASS__, 'to_email_address',  $to);
0 ignored issues
show
Bug introduced by
The method modify() does not seem to exist on object<SilverStripe\Core\Config\Config>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
284
    }
285
286
    /**
287
     * @deprecated
288
     * @return null|string
289
     */
290
    public static function get_to_email_address()
291
    {
292
        Deprecation::notice('2.0', 'Use config API instead');
293
        return Config::inst()->get(__CLASS__, 'to_email_address');
294
    }
295
296
    /**
297
     * @deprecated
298
     * @param bool $results
299
     */
300
    public static function set_email_results($results)
301
    {
302
        Deprecation::notice('2.0', 'Use config API instead');
303
        Config::modify()->set(__CLASS__, 'email_results', $results);
0 ignored issues
show
Bug introduced by
The method modify() does not seem to exist on object<SilverStripe\Core\Config\Config>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
304
    }
305
306
    /**
307
     * @deprecated
308
     * @return bool
309
     */
310
    public static function get_email_results()
311
    {
312
        Deprecation::notice('2.0', 'Use config API instead');
313
        return Config::inst()->get(__CLASS__, 'email_results');
314
    }
315
}
316