3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
21 // phpcs:disable Generic.Arrays.DisallowLongArraySyntax,PSR2.Classes.PropertyDeclaration,MediaWiki.Usage.DirUsage
22 // phpcs:disable Squiz.Scope.MemberVarScope.Missing,Squiz.Scope.MethodScope.Missing
24 * Check PHP Version, as well as for composer dependencies in entry points,
25 * and display something vaguely comprehensible in the event of a totally
26 * unrecoverable error.
28 * @note Since we can't rely on anything external, the minimum PHP versions
29 * and MW current version are hardcoded in this class.
31 * @note This class uses setter methods instead of a constructor so that
32 * it can be compatible with PHP 4 through PHP 8 (without warnings).
34 class PHPVersionCheck
{
35 /** @var string The number of the MediaWiki version used. If you're updating MW_VERSION in Defines.php, you must also update this value. */
36 var $mwVersion = '1.44';
38 /** @var string[] A mapping of PHP functions to PHP extensions. */
39 var $functionsExtensionsMapping = array(
40 'mb_substr' => 'mbstring',
41 'xml_parser_create' => 'xml',
42 'ctype_digit' => 'ctype',
43 'json_decode' => 'json',
45 'mime_content_type' => 'fileinfo',
46 'intl_is_failure' => 'intl',
50 * @var string The format used for errors. One of "text" or "html"
57 var $scriptPath = '/';
60 * Set the format used for errors.
62 * @param string $format One of "text" or "html"
64 function setFormat( $format ) {
65 $this->format
= $format;
69 * Set the script path used for images in HTML-formatted errors.
71 * @param string $scriptPath
73 function setScriptPath( $scriptPath ) {
74 $this->scriptPath
= $scriptPath;
78 * Displays an error, if the installed PHP version does not meet the minimum requirement.
80 function checkRequiredPHPVersion() {
81 $minimumVersion = '7.4.3';
84 * This is a list of known-bad ranges of PHP versions. Syntax is like SemVer – either:
86 * - '1.2.3' to prohibit a single version of PHP, or
87 * - '1.2.3 – 1.2.5' to block a range, inclusive.
89 * Whitespace will be ignored.
91 * The key is not shown to users; use it to prompt future developers as to why this was
92 * chosen, ideally one or more Phabricator task references.
94 * Remember to drop irrelevant ranges when bumping $minimumVersion.
99 $passes = version_compare( PHP_VERSION
, $minimumVersion, '>=' );
101 $versionString = "PHP $minimumVersion or higher";
103 // Left as a programmatic check to make it easier to update.
104 if ( count( $knownBad ) ) {
105 $versionString .= ' (and not ' . implode( ', ', array_values( $knownBad ) ) . ')';
107 foreach ( $knownBad as $range ) {
108 // As we don't have composer at this point, we have to do our own version range checking.
109 if ( strpos( $range, '-' ) ) {
110 $passes = $passes && !(
111 version_compare( PHP_VERSION
, trim( strstr( $range, '-', true ) ), '>=' )
112 && version_compare( PHP_VERSION
, trim( substr( strstr( $range, '-', false ), 1 ) ), '<' )
115 $passes = $passes && version_compare( PHP_VERSION
, trim( $range ), '<>' );
121 $cliText = "Error: You are using an unsupported PHP version (PHP " . PHP_VERSION
. ").\n"
122 . "MediaWiki $this->mwVersion needs $versionString.\n\nCheck if you might have a newer "
123 . "PHP executable with a different name.\n\n";
126 $web['intro'] = "MediaWiki $this->mwVersion requires $versionString; you are using PHP "
129 $web['longTitle'] = "Supported PHP versions";
130 // phpcs:disable Generic.Files.LineLength
131 $web['longHtml'] = <<<HTML
133 Please consider <a href="https://www.php.net/downloads.php">upgrading your copy of PHP</a>.
134 PHP versions less than v8.1.0 are no longer <a href="https://www.php.net/supported-versions.php">supported</a>
135 by the PHP Group and will not receive security or bugfix updates.
138 If for some reason you are unable to upgrade your PHP version, you will need to
139 <a href="https://www.mediawiki.org/wiki/Download">download</a> an older version of
140 MediaWiki from our website. See our
141 <a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
142 for details of which versions are compatible with prior versions of PHP.
145 // phpcs:enable Generic.Files.LineLength
154 * Displays an error, if the vendor/autoload.php file could not be found.
156 function checkVendorExistence() {
157 if ( !file_exists( dirname( __FILE__
) . '/../vendor/autoload.php' ) ) {
158 $cliText = "Error: You are missing some dependencies. \n"
159 . "MediaWiki has dependencies that need to be installed via Composer\n"
160 . "or from a separate repository. Please see\n"
161 . "https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries\n"
162 . "for help with installing them.";
165 $web['intro'] = "Installing some dependencies is required.";
166 $web['longTitle'] = 'Dependencies';
167 // phpcs:disable Generic.Files.LineLength
168 $web['longHtml'] = <<<HTML
170 MediaWiki has dependencies that need to be installed via Composer
171 or from a separate repository. Please see the
172 <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">instructions
173 for installing external libraries</a> on MediaWiki.org.
176 // phpcs:enable Generic.Files.LineLength
178 $this->triggerError( $web, $cliText );
183 * Displays an error, if a PHP extension does not exist.
185 function checkExtensionExistence() {
186 $missingExtensions = array();
187 foreach ( $this->functionsExtensionsMapping
as $function => $extension ) {
188 if ( !function_exists( $function ) ) {
189 $missingExtensions[] = $extension;
193 if ( $missingExtensions ) {
194 $missingExtText = '';
195 $missingExtHtml = '';
196 $baseUrl = 'https://www.php.net';
197 foreach ( $missingExtensions as $ext ) {
198 $missingExtText .= " * $ext <$baseUrl/$ext>\n";
199 $missingExtHtml .= "<li><b>$ext</b> "
200 . "(<a href=\"$baseUrl/$ext\">more information</a>)</li>";
203 $cliText = "Error: Missing one or more required PHP extensions. Please see\n"
204 . "https://www.mediawiki.org/wiki/Manual:Installation_requirements#PHP\n"
205 . "for help with installing them.\n"
206 . "Please install or enable:\n" . $missingExtText;
209 $web['intro'] = "Installing some PHP extensions is required.";
210 $web['longTitle'] = 'Required PHP extensions';
211 $web['longHtml'] = <<<HTML
213 You are missing one or more extensions to PHP that MediaWiki requires to run. Please see the
214 <a href="https://www.mediawiki.org/wiki/Manual:Installation_requirements#PHP">PHP
215 installation requirements</a> on MediaWiki.org.
217 <p>Please install or enable:</p>
223 $this->triggerError( $web, $cliText );
228 * Output headers that prevents error pages to be cached.
230 function outputHTMLHeader() {
231 $protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ?
$_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
233 header( "$protocol 500 MediaWiki configuration Error" );
234 // Don't cache error pages! They cause no end of trouble...
235 header( 'Cache-Control: no-cache' );
239 * Returns an error page, which is suitable for output to the end user via a web browser.
241 * @param string $introText
242 * @param string $longTitle
243 * @param string $longHtml
246 function getIndexErrorOutput( $introText, $longTitle, $longHtml ) {
248 htmlspecialchars( str_replace( '//', '/', $this->scriptPath
. '/' ) .
249 'resources/assets/mediawiki.png' );
251 $introHtml = htmlspecialchars( $introText );
252 $longTitleHtml = htmlspecialchars( $longTitle );
254 header( 'Content-type: text/html; charset=UTF-8' );
256 $finalOutput = <<<HTML
258 <html lang="en" dir="ltr">
260 <meta charset="UTF-8" />
261 <title>MediaWiki {$this->mwVersion}</title>
262 <style media="screen">
265 background-color: #fff;
266 font-family: sans-serif;
283 <img src="{$encLogo}" alt="The MediaWiki logo" />
284 <h1>MediaWiki {$this->mwVersion} internal error</h1>
288 <h2>{$longTitleHtml}</h2>
298 * Display something vaguely comprehensible in the event of a totally unrecoverable error.
299 * Does not assume access to *anything*; no globals, no autoloader, no database, no localisation.
300 * Safe for PHP4 (and putting this here means that WebStart.php and GlobalSettings.php
301 * no longer need to be).
303 * This function immediately terminates the PHP process.
305 * @param string[] $web
306 * - (string) intro: Short error message, displayed on top.
307 * - (string) longTitle: Title for the longer message.
308 * - (string) longHtml: The longer message, as raw HTML.
309 * @param string $cliText
311 function triggerError( $web, $cliText ) {
312 if ( $this->format
=== 'html' ) {
313 // Used by index.php and mw-config/index.php
314 $this->outputHTMLHeader();
315 $finalOutput = $this->getIndexErrorOutput(
321 // Used by Maintenance.php (CLI)
322 $finalOutput = $cliText;
325 echo "$finalOutput\n";
331 * Check PHP version and that external dependencies are installed, and
332 * display an informative error if either condition is not satisfied.
334 * @param string $format One of "text" or "html"
335 * @param string $scriptPath Used when an error is formatted as HTML.
337 function wfEntryPointCheck( $format = 'text', $scriptPath = '/' ) {
338 $phpVersionCheck = new PHPVersionCheck();
339 $phpVersionCheck->setFormat( $format );
340 $phpVersionCheck->setScriptPath( $scriptPath );
341 $phpVersionCheck->checkRequiredPHPVersion();
342 $phpVersionCheck->checkVendorExistence();
343 $phpVersionCheck->checkExtensionExistence();