Completed
Branch master (1655eb)
by
unknown
22:24
created

PHPVersionCheck.php ➔ wfEntryPointCheck()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
// @codingStandardsIgnoreFile Generic.Arrays.DisallowLongArraySyntax
3
// @codingStandardsIgnoreFile Generic.Files.LineLength
4
// @codingStandardsIgnoreFile MediaWiki.Usage.DirUsage.FunctionFound
5
/**
6
 * Check PHP Version, as well as for composer dependencies in entry points,
7
 * and display something vaguely comprehensible in the event of a totally
8
 * unrecoverable error.
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License along
21
 * with this program; if not, write to the Free Software Foundation, Inc.,
22
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
 * http://www.gnu.org/copyleft/gpl.html
24
 *
25
 * @file
26
 */
27
class PHPVersionCheck {
28
	/* @var string The number of the MediaWiki version used */
29
	var $mwVersion = '1.29';
30
	/* @var string The minimum php version for MediaWiki to run */
31
	var $minimumVersionPHP = '5.5.9';
32
	var $functionsExtensionsMapping = array(
33
		'mb_substr'   => 'mbstring',
34
		'utf8_encode' => 'xml',
35
		'ctype_digit' => 'ctype',
36
		'json_decode' => 'json',
37
		'iconv'       => 'iconv',
38
	);
39
40
	/**
41
	 * @var string Which entry point we are protecting. One of:
42
	 *   - index.php
43
	 *   - load.php
44
	 *   - api.php
45
	 *   - mw-config/index.php
46
	 *   - cli
47
	 */
48
	var $entryPoint = null;
49
50
	/**
51
	 * @param string $entryPoint Which entry point we are protecting. One of:
52
	 *   - index.php
53
	 *   - load.php
54
	 *   - api.php
55
	 *   - mw-config/index.php
56
	 *   - cli
57
	 * @return $this
58
	 */
59
	function setEntryPoint( $entryPoint ) {
60
		$this->entryPoint = $entryPoint;
61
	}
62
63
	/**
64
	 * Returns the version of the installed php implementation.
65
	 *
66
	 * @return string
67
	 */
68
	function getPHPImplVersion() {
69
		return PHP_VERSION;
70
	}
71
72
	/**
73
	 * Displays an error, if the installed php version does not meet the minimum requirement.
74
	 *
75
	 * @return $this
76
	 */
77
	function checkRequiredPHPVersion() {
78
		if ( !function_exists( 'version_compare' )
79
		     || version_compare( $this->getPHPImplVersion(), $this->minimumVersionPHP ) < 0
80
		) {
81
			$shortText = "MediaWiki $this->mwVersion requires at least PHP version"
82
			             . " $this->minimumVersionPHP, you are using PHP {$this->getPHPImplVersion()}.";
83
84
			$longText = "Error: You might be using on older PHP version. \n"
85
			            . "MediaWiki $this->mwVersion needs PHP $this->minimumVersionPHP or higher.\n\n"
86
			            . "Check if you have a newer php executable with a different name, "
87
			            . "such as php5.\n\n";
88
89
			$longHtml = <<<HTML
90
			Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
91
			PHP versions less than 5.5.0 are no longer supported by the PHP Group and will not receive
92
			security or bugfix updates.
93
		</p>
94
		<p>
95
			If for some reason you are unable to upgrade your PHP version, you will need to
96
			<a href="https://www.mediawiki.org/wiki/Download">download</a> an older version
97
			of MediaWiki from our website.  See our
98
			<a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
99
			for details of which versions are compatible with prior versions of PHP.
100
HTML;
101
			$this->triggerError( 'Supported PHP versions', $shortText, $longText, $longHtml );
102
		}
103
	}
104
105
	/**
106
	 * Displays an error, if the vendor/autoload.php file could not be found.
107
	 *
108
	 * @return $this
109
	 */
110
	function checkVendorExistence() {
111
		if ( !file_exists( dirname( __FILE__ ) . '/../vendor/autoload.php' ) ) {
112
			$shortText = "Installing some external dependencies (e.g. via composer) is required.";
113
114
			$longText = "Error: You are missing some external dependencies. \n"
115
			            . "MediaWiki now also has some external dependencies that need to be installed\n"
116
			            . "via composer or from a separate git repo. Please see\n"
117
			            . "https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries\n"
118
			            . "for help on installing the required components.";
119
120
			$longHtml = <<<HTML
121
		MediaWiki now also has some external dependencies that need to be installed via
122
		composer or from a separate git repo. Please see
123
		<a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a>
124
		for help on installing the required components.
125
HTML;
126
127
			$this->triggerError( 'External dependencies', $shortText, $longText, $longHtml );
128
		}
129
	}
130
131
	/**
132
	 * Displays an error, if a PHP extension does not exist.
133
	 *
134
	 * @return $this
135
	 */
136
	function checkExtensionExistence() {
137
		$missingExtensions = array();
138
		foreach ( $this->functionsExtensionsMapping as $function => $extension ) {
139
			if ( !function_exists( $function ) ) {
140
				$missingExtensions[] = $extension;
141
			}
142
		}
143
144
		if ( $missingExtensions ) {
145
			$shortText = "Installing some PHP extensions is required.";
146
147
			$missingExtText = '';
148
			$missingExtHtml = '';
149
			$baseUrl = 'https://secure.php.net';
150
			foreach ( $missingExtensions as $ext ) {
151
				$missingExtText .= " * $ext <$baseUrl/$ext>\n";
152
				$missingExtHtml .= "<li><b>$ext</b> "
153
				                   . "(<a href=\"$baseUrl/$ext\">more information</a>)</li>";
154
			}
155
156
			$cliText = "Error: Missing one or more required components of PHP.\n"
157
			           . "You are missing a required extension to PHP that MediaWiki needs.\n"
158
			           . "Please install:\n" . $missingExtText;
159
160
			$longHtml = <<<HTML
161
		You are missing a required extension to PHP that MediaWiki
162
		requires to run. Please install:
163
		<ul>
164
		$missingExtHtml
165
		</ul>
166
HTML;
167
168
			$this->triggerError( 'Required components', $shortText, $cliText, $longHtml );
169
		}
170
	}
171
172
	/**
173
	 * Output headers that prevents error pages to be cached.
174
	 */
175
	function outputHTMLHeader() {
0 ignored issues
show
Coding Style introduced by
outputHTMLHeader 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...
176
		$protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
177
178
		header( "$protocol 500 MediaWiki configuration Error" );
179
		// Don't cache error pages!  They cause no end of trouble...
180
		header( 'Cache-control: none' );
181
		header( 'Pragma: no-cache' );
182
	}
183
184
	/**
185
	 * Returns an error page, which is suitable for output to the end user via a web browser.
186
	 *
187
	 * @param $title
188
	 * @param $longHtml
189
	 * @param $shortText
190
	 * @return string
191
	 */
192
	function getIndexErrorOutput( $title, $longHtml, $shortText ) {
0 ignored issues
show
Coding Style introduced by
getIndexErrorOutput 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...
193
		$pathinfo = pathinfo( $_SERVER['SCRIPT_NAME'] );
194
		if ( $this->entryPoint == 'mw-config/index.php' ) {
195
			$dirname = dirname( $pathinfo['dirname'] );
196
		} else {
197
			$dirname = $pathinfo['dirname'];
198
		}
199
		$encLogo =
200
			htmlspecialchars( str_replace( '//', '/', $dirname . '/' ) .
201
			                  'resources/assets/mediawiki.png' );
202
		$shortHtml = htmlspecialchars( $shortText );
203
204
		header( 'Content-type: text/html; charset=UTF-8' );
205
206
		$finalOutput = <<<HTML
207
<!DOCTYPE html>
208
<html lang="en" dir="ltr">
209
	<head>
210
		<meta charset="UTF-8" />
211
		<title>MediaWiki {$this->mwVersion}</title>
212
		<style media='screen'>
213
			body {
214
				color: #000;
215
				background-color: #fff;
216
				font-family: sans-serif;
217
				padding: 2em;
218
				text-align: center;
219
			}
220
			p, img, h1, h2, ul  {
221
				text-align: left;
222
				margin: 0.5em 0 1em;
223
			}
224
			h1 {
225
				font-size: 120%;
226
			}
227
			h2 {
228
				font-size: 110%;
229
			}
230
		</style>
231
	</head>
232
	<body>
233
		<img src="{$encLogo}" alt='The MediaWiki logo' />
234
		<h1>MediaWiki {$this->mwVersion} internal error</h1>
235
		<div class='error'>
236
		<p>
237
			{$shortHtml}
238
		</p>
239
		<h2>{$title}</h2>
240
		<p>
241
			{$longHtml}
242
		</p>
243
		</div>
244
	</body>
245
</html>
246
HTML;
247
248
		return $finalOutput;
249
	}
250
251
	/**
252
	 * Display something vaguely comprehensible in the event of a totally unrecoverable error.
253
	 * Does not assume access to *anything*; no globals, no autoloader, no database, no localisation.
254
	 * Safe for PHP4 (and putting this here means that WebStart.php and GlobalSettings.php
255
	 * no longer need to be).
256
	 *
257
	 * Calling this function kills execution immediately.
258
	 *
259
	 * @param string $title HTML code to be put within an <h2> tag
260
	 * @param string $shortText
261
	 * @param string $longText
262
	 * @param string $longHtml
263
	 */
264
	function triggerError( $title, $shortText, $longText, $longHtml ) {
265
		switch ( $this->entryPoint ) {
266
			case 'cli':
267
				$finalOutput = $longText;
268
				break;
269
			case 'index.php':
270
			case 'mw-config/index.php':
271
				$this->outputHTMLHeader();
272
				$finalOutput = $this->getIndexErrorOutput( $title, $longHtml, $shortText );
273
				break;
274
			case 'load.php':
275
				$this->outputHTMLHeader();
276
				$finalOutput = "/* $shortText */";
277
				break;
278
			default:
279
				$this->outputHTMLHeader();
280
				// Handle everything that's not index.php
281
				$finalOutput = $shortText;
282
		}
283
284
		echo "$finalOutput\n";
285
		die( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method triggerError() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
286
	}
287
}
288
289
/**
290
 * Check php version and that external dependencies are installed, and
291
 * display an informative error if either condition is not satisfied.
292
 *
293
 * @note Since we can't rely on anything, the minimum PHP versions and MW current
294
 * version are hardcoded here
295
 */
296
function wfEntryPointCheck( $entryPoint ) {
297
	$phpVersionCheck = new PHPVersionCheck();
298
	$phpVersionCheck->setEntryPoint( $entryPoint );
299
	$phpVersionCheck->checkRequiredPHPVersion();
300
	$phpVersionCheck->checkVendorExistence();
301
	$phpVersionCheck->checkExtensionExistence();
302
}
303