Completed
Push — master ( e090e4...18d79e )
by Damian
02:26
created

code/EnvironmentCheckSuite.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Represents a suite of environment checks.
5
 * Specific checks can be registered against a named instance of EnvironmentCheckSuite.
6
 *
7
 * Usage #1 - _config.php
8
 * EnvironmentCheckSuite::register('health', 'MyHealthCheck("my param")', 'Title of my health check');
9
 *
10
 * Usage #2 - config.yml
11
 * EnvironmentCheckSuite:
12
 *   registered_checks:
13
 *     mycheck:
14
 *       definition: 'MyHealthCheck("my param")'
15
 *       title: 'Title of my health check'
16
 *   registered_suites:
17
 *     health:
18
 *       - mycheck
19
 *
20
 * $result = EnvironmentCheckSuite::inst('health')->run();
21
 */
22
class EnvironmentCheckSuite extends Object {
23
	/**
24
	 * Name of this suite.
25
	 *
26
	 * @var string
27
	 */
28
	protected $name;
29
30
	/**
31
	 * @var array
32
	 */
33
	protected $checks = array();
34
35
	/**
36
	 * Associative array of named checks registered via the config system. Each check should specify:
37
	 * - definition (e.g. 'MyHealthCheck("my param")')
38
	 * - title (e.g. 'Is my feature working?')
39
	 * - state (setting this to 'disabled' will cause suites to skip this check entirely.
40
	 *
41
	 * @var array
42
	 */
43
	private static $registered_checks = array();
44
45
	/**
46
	 * Associative array of named suites registered via the config system. Each suite should enumerate
47
	 * named checks that have been configured in 'registered_checks'.
48
	 *
49
	 * @var array
50
	 */
51
	private static $registered_suites = array();
52
53
	/**
54
	 * Load checks for this suite from the configuration system. This is an alternative to the
55
	 * EnvironmentCheckSuite::register - both can be used, checks will be appended to the suite.
56
	 *
57
	 * @param string $suiteName The name of this suite.
58
	 */
59
	public function __construct($suiteName) {
60
		parent::__construct();
61
62
		if (empty($this->config()->registered_suites[$suiteName])) {
63
			// Not registered via config system, but it still may be configured later via self::register.
64
			return;
65
		}
66
67
		foreach ($this->config()->registered_suites[$suiteName] as $checkName) {
68
			if (empty($this->config()->registered_checks[$checkName])) {
69
				throw new InvalidArgumentException(
70
					"Bad EnvironmentCheck: '$checkName' - the named check has not been registered."
71
				);
72
			}
73
74
			$check = $this->config()->registered_checks[$checkName];
75
76
			// Existing named checks can be disabled by setting their 'state' to 'disabled'.
77
			// This is handy for disabling checks mandated by modules.
78
			if (!empty($check['state']) && $check['state']==='disabled') continue;
79
			
80
			// Add the check to this suite.
81
			$this->push($check['definition'], $check['title']);
82
		}
83
	}
84
85
	/**
86
	 * Run this test suite and return the result code of the worst result.
87
	 *
88
	 * @return int
89
	 */
90
	public function run() {
91
		$result = new EnvironmentCheckSuiteResult();
92
93
		foreach($this->checkInstances() as $check) {
94
			list($checkClass, $checkTitle) = $check;
95
			try {
96
				list($status, $message) = $checkClass->check();
97
			// If the check fails, register that as an error
98
			} catch(Exception $e) {
99
				$status = EnvironmentCheck::ERROR;
100
				$message = $e->getMessage();
101
			}
102
			$result->addResult($status, $message, $checkTitle);
103
		}
104
105
		return $result;
106
	}
107
108
	/**
109
	 * Get instances of all the environment checks.
110
	 *
111
	 * @return array
112
	 */
113
	protected function checkInstances() {
114
		$output = array();
115
		foreach($this->checks as $check) {
116
			list($checkClass, $checkTitle) = $check;
117
			if(is_string($checkClass)) {
118
				$checkInst = Object::create_from_string($checkClass);
119
				if($checkInst instanceof EnvironmentCheck) {
120
					$output[] = array($checkInst, $checkTitle);
121
				} else {
122
					throw new InvalidArgumentException("Bad EnvironmentCheck: '$checkClass' - the named class doesn't implement EnvironmentCheck");
123
				}
124
			} else if($checkClass instanceof EnvironmentCheck) {
125
				$output[] = array($checkClass, $checkTitle);
126
			} else {
127
				throw new InvalidArgumentException("Bad EnvironmentCheck: " . var_export($check, true));
128
			}
129
		}
130
		return $output;
131
	}
132
133
	/**
134
	 * Add a check to this suite.
135
	 *
136
	 * @param mixed $check
137
	 * @param string $title
138
	 */
139
	public function push($check, $title = null) {
140
		if(!$title) {
141
			$title = is_string($check) ? $check : get_class($check);
142
		}
143
		$this->checks[] = array($check, $title);
144
	}
145
146
	/////////////////////////////////////////////////////////////////////////////////////////////
147
148
	/**
149
	 * @var array
150
	 */
151
	protected static $instances = array();
152
153
	/**
154
	 * Return a named instance of EnvironmentCheckSuite.
155
	 *
156
	 * @param string $name
157
	 *
158
	 * @return EnvironmentCheckSuite
159
	 */
160
	static function inst($name) {
0 ignored issues
show
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...
161
		if(!isset(self::$instances[$name])) self::$instances[$name] = new EnvironmentCheckSuite($name);
162
		return self::$instances[$name];
163
	}
164
165
	/**
166
	 * Register a check against the named check suite.
167
	 *
168
	 * @param string|array $names
169
	 * @param EnvironmentCheck $check
170
	 * @param string|array
171
	 */
172
	static function register($names, $check, $title = null) {
0 ignored issues
show
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...
173
		if(!is_array($names)) $names = array($names);
174
		foreach($names as $name) self::inst($name)->push($check, $title);
175
	}
176
177
	/**
178
	 * Unregisters all checks.
179
	 */
180
	static function reset() {
0 ignored issues
show
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...
181
		self::$instances = array();
182
	}
183
}
184
185
/**
186
 * A single set of results from running an EnvironmentCheckSuite
187
 */
188
class EnvironmentCheckSuiteResult extends ViewableData {
189
	/**
190
	 * @var ArrayList
191
	 */
192
	protected $details;
193
194
	/**
195
	 * @var int
196
	 */
197
	protected $worst = 0;
198
199
	function __construct() {
0 ignored issues
show
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...
200
		parent::__construct();
201
		$this->details = new ArrayList();
202
	}
203
204
	/**
205
	 * @param int $status
206
	 * @param string $message
207
	 * @param string $checkIdentifier
208
	 */
209
	function addResult($status, $message, $checkIdentifier) {
0 ignored issues
show
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
		$this->details->push(new ArrayData(array(
211
			'Check' => $checkIdentifier,
212
			'Status' => $this->statusText($status),
213
			'StatusCode' => $status,
214
			'Message' => $message,
215
		)));
216
217
		$this->worst = max($this->worst, $status);
218
	}
219
220
	/**
221
	 * Returns true if there are no errors.
222
	 *
223
	 * @return bool
224
	 */
225
	public function ShouldPass() {
226
		return $this->worst <= EnvironmentCheck::WARNING;
227
	}
228
229
	/**
230
	 * Returns overall (i.e. worst) status as a string.
231
	 *
232
	 * @return string
233
	 */
234
	function Status() {
0 ignored issues
show
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...
235
		return $this->statusText($this->worst);
236
	}
237
238
	/**
239
	 * Returns detailed status information about each check.
240
	 *
241
	 * @return ArrayList
242
	 */
243
	function Details() {
0 ignored issues
show
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...
244
		return $this->details;
245
	}
246
247
	/**
248
	 * Convert the final result status and details to JSON.
249
	 *
250
	 * @return string
251
	 */
252
	function toJSON() {
0 ignored issues
show
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...
253
		$result = array(
254
			'Status' => $this->Status(),
255
			'ShouldPass' => $this->ShouldPass(),
256
			'Checks' => array()
257
		);
258
		foreach($this->details as $detail) {
259
			$result['Checks'][] = $detail->toMap();
260
		}
261
		return json_encode($result);
262
	}
263
264
	/**
265
	 * Return a text version of a status code.
266
	 *
267
	 * @return string
268
	 */
269
	protected function statusText($status) {
270
		switch($status) {
271
			case EnvironmentCheck::ERROR: return "ERROR";
272
			case EnvironmentCheck::WARNING: return "WARNING";
273
			case EnvironmentCheck::OK: return "OK";
274
			case 0: return "NO CHECKS";
275
			default: throw new InvalidArgumentException("Bad environment check status '$status'");
276
		}
277
	}
278
}
279