xref: /web-php/include/errors.inc (revision faf2e759)
1<?php
2/*
3  This script provides functions to print out
4  error messages for users in case something is
5  not available.
6*/
7
8// A 'good looking' 404 error message page
9function error_404(): void
10{
11    global $MYSITE;
12    status_header(404);
13    site_header('404 Not Found', ["noindex"]);
14    echo "<h1>Not Found</h1>\n<p><strong>" .
15         htmlspecialchars(substr($MYSITE, 0, -1) . $_SERVER['REQUEST_URI']) .
16         "</strong> not found on this server.</p>\n";
17    site_footer();
18    exit;
19}
20
21// A 'good looking' 404 error message page for manual pages
22function error_404_manual(): void
23{
24    global $MYSITE;
25    status_header(404);
26    site_header('404 Not Found', ["noindex"]);
27    echo "<h1>Not Found</h1>\n" .
28         "<p>The manual page you are looking for (<strong>" .
29         htmlspecialchars(substr($MYSITE, 0, -1) . $_SERVER['REQUEST_URI']) .
30         "</strong>) is not available on this server right now. " .
31         "Please check back later, or if the problem persists, " .
32         "<a href=\"/contact.php\">contact the webmasters</a>.</p>\n";
33    site_footer();
34    exit;
35}
36
37// An error message page for manual pages from inactive languages
38function error_inactive_manual_page($lang_name, $en_page): void
39{
40    global $MYSITE, $ACTIVE_ONLINE_LANGUAGES;
41    status_header(404);
42    site_header('Page gone', ["noindex"]);
43    echo "<h1>Page gone</h1>\n" .
44         "<p>The " . htmlspecialchars($lang_name) . " manual page you are looking for (<strong>" .
45         htmlspecialchars(substr($MYSITE, 0, -1) . $_SERVER['REQUEST_URI']) .
46         "</strong>) is no longer available.</p>\n";
47    $en_url = htmlspecialchars(substr($MYSITE, 0, -1) . $en_page);
48    echo "<p>The English page is available at <a href=\"{$en_url}\">{$en_url}</a></p>\n";
49    echo "<p>Several other languages are also available:</p>\n";
50    echo "<ul>\n";
51    foreach ($ACTIVE_ONLINE_LANGUAGES as $alt_lang => $alt_lang_name) {
52        if ($alt_lang === "en") {
53            continue;
54        }
55        $alt_url = htmlspecialchars(substr($MYSITE, 0, -1) . str_replace("/en/", "/{$alt_lang}/", $en_page));
56        echo "<li><a href=\"{$alt_url}\">" . htmlspecialchars($alt_lang_name) . "</a></li>\n";
57    }
58    echo "</ul>\n";
59    site_footer();
60    exit;
61}
62
63// This service is not working right now
64function error_noservice(): void
65{
66    global $MYSITE;
67    site_header('Service not working', ["noindex"]);
68    echo "<h1>Service not working</h1>\n" .
69         "<p>The service you tried to access with <strong>" .
70         htmlspecialchars(substr($MYSITE, 0, -1) . $_SERVER['REQUEST_URI']) .
71         "</strong> is not available on this server right now. " .
72         "Please check back later, or if the problem persists, " .
73         "<a href=\"/contact.php\">contact the webmasters</a>.</p>\n";
74    site_footer();
75    exit;
76}
77
78// There is no such mirror
79function error_nomirror($mirror): void {
80    site_header("No such mirror", ["noindex"]);
81    echo "<h1>No such mirror</h1>\n<p>The mirror you tried to access (" .
82         htmlspecialchars($mirror) .
83         ") is not registered php.net mirror. Please check back later," .
84         " or if the problem persists, " .
85         "<a href=\"/contact.php\">contact the webmasters</a>.</p>\n";
86    site_footer();
87    exit;
88}
89
90// Send out a proper status header
91function status_header(int $status): bool
92{
93    $text = [
94        200 => 'OK',
95        301 => 'Moved Permanently',
96        302 => 'Found',
97        404 => 'Not Found',
98    ];
99    if (!isset($text[$status])) {
100        return false;
101    }
102
103    // Only respond with HTTP/1.0 for a 1.0 request specifically.
104    // Respond with 1.1 for anything else.
105    $proto = strcasecmp($_SERVER['SERVER_PROTOCOL'], 'HTTP/1.0') ? '1.1' : '1.0';
106
107    @header("HTTP/$proto $status {$text[$status]}");
108    @header("Status: $status {$text[$status]}", true, $status);
109
110    return true;
111}
112
113/******************************************************************************
114In the future every mirror will have SQLite instead of hacks like this. Or, some
115other means like a ginormous array! But the point is we'll end up storing
116everything like functions, variables, constants, common search terms, etc.
117
118This exists today because as of PHP 5.3.0 there are over 200 links within the
119php.ini files so these create nicer urls and allow language detection to work.
120
121The most commonly searched terms have also been added.
122
123TODO: Determine if we want to continue 301 -OR- make these official URLs.
124******************************************************************************/
125
126function is_known_ini(string $ini): ?string {
127    $inis = [
128        'engine' => 'apache.configuration.php#ini.engine',
129        'short-open-tag' => 'ini.core.php#ini.short-open-tag',
130        'asp-tags' => 'ini.core.php#ini.asp-tags',
131        'precision' => 'ini.core.php#ini.precision',
132        'y2k-compliance' => 'ini.core.php#ini.y2k-compliance',
133        'output-buffering' => 'outcontrol.configuration.php#ini.output-buffering',
134        'output-handler' => 'outcontrol.configuration.php#ini.output-handler',
135        'zlib.output-compression' => 'zlib.configuration.php#ini.zlib.output-compression',
136        'zlib.output-compression-level' => 'zlib.configuration.php#ini.zlib.output-compression-level',
137        'zlib.output-handler' => 'zlib.configuration.php#ini.zlib.output-handler',
138        'implicit-flush' => 'outcontrol.configuration.php#ini.implicit-flush',
139        'allow-call-time-pass-reference' => 'ini.core.php#ini.allow-call-time-pass-reference',
140        'open-basedir' => 'ini.core.php#ini.open-basedir',
141        'disable-functions' => 'ini.core.php#ini.disable-functions',
142        'disable-classes' => 'ini.core.php#ini.disable-classes',
143        'zend.assertions' => 'ini.core.php#ini.zend.assertions',
144        'syntax-highlighting' => 'misc.configuration.php#ini.syntax-highlighting',
145        'ignore-user-abort' => 'misc.configuration.php#ini.ignore-user-abort',
146        'realpath-cache-size' => 'ini.core.php#ini.realpath-cache-size',
147        'realpath-cache-ttl' => 'ini.core.php#ini.realpath-cache-ttl',
148        'expose-php' => 'ini.core.php#ini.expose-php',
149        'max-execution-time' => 'info.configuration.php#ini.max-execution-time',
150        'max-input-time' => 'info.configuration.php#ini.max-input-time',
151        'max-input-vars' => 'info.configuration.php#ini.max-input-vars',
152        'max-input-nesting-level' => 'info.configuration.php#ini.max-input-nesting-level',
153        'memory-limit' => 'ini.core.php#ini.memory-limit',
154        'error-reporting' => 'errorfunc.configuration.php#ini.error-reporting',
155        'display-errors' => 'errorfunc.configuration.php#ini.display-errors',
156        'display-startup-errors' => 'errorfunc.configuration.php#ini.display-startup-errors',
157        'log-errors' => 'errorfunc.configuration.php#ini.log-errors',
158        'log-errors-max-len' => 'errorfunc.configuration.php#ini.log-errors-max-len',
159        'ignore-repeated-errors' => 'errorfunc.configuration.php#ini.ignore-repeated-errors',
160        'ignore-repeated-source' => 'errorfunc.configuration.php#ini.ignore-repeated-source',
161        'report-memleaks' => 'errorfunc.configuration.php#ini.report-memleaks',
162        'track-errors' => 'errorfunc.configuration.php#ini.track-errors',
163        'xmlrpc-errors' => 'errorfunc.configuration.php#ini.xmlrpc-errors',
164        'html-errors' => 'errorfunc.configuration.php#ini.html-errors',
165        'docref-root' => 'errorfunc.configuration.php#ini.docref-root',
166        'docref-ext' => 'errorfunc.configuration.php#ini.docref-ext',
167        'error-prepend-string' => 'errorfunc.configuration.php#ini.error-prepend-string',
168        'error-append-string' => 'errorfunc.configuration.php#ini.error-append-string',
169        'error-log' => 'errorfunc.configuration.php#ini.error-log',
170        'syslog.facility' => 'errorfunc.configuration.php#ini.syslog.facility',
171        'syslog.filter' => 'errorfunc.configuration.php#ini.syslog.filter',
172        'syslog.ident' => 'errorfunc.configuration.php#ini.syslog.ident',
173        'arg-separator.output' => 'ini.core.php#ini.arg-separator.output',
174        'arg-separator.input' => 'ini.core.php#ini.arg-separator.input',
175        'variables-order' => 'ini.core.php#ini.variables-order',
176        'request-order' => 'ini.core.php#ini.request-order',
177        'register-globals' => 'ini.core.php#ini.register-globals',
178        'register-long-arrays' => 'ini.core.php#ini.register-long-arrays',
179        'register-argc-argv' => 'ini.core.php#ini.register-argc-argv',
180        'auto-globals-jit' => 'ini.core.php#ini.auto-globals-jit',
181        'post-max-size' => 'ini.core.php#ini.post-max-size',
182        'magic-quotes-gpc' => 'info.configuration.php#ini.magic-quotes-gpc',
183        'magic-quotes-runtime' => 'info.configuration.php#ini.magic-quotes-runtime',
184        'auto-prepend-file' => 'ini.core.php#ini.auto-prepend-file',
185        'auto-append-file' => 'ini.core.php#ini.auto-append-file',
186        'default-mimetype' => 'ini.core.php#ini.default-mimetype',
187        'default-charset' => 'ini.core.php#ini.default-charset',
188        'always-populate-raw-post-data' => 'ini.core.php#ini.always-populate-raw-post-data',
189        'include-path' => 'ini.core.php#ini.include-path',
190        'doc-root' => 'ini.core.php#ini.doc-root',
191        'user-dir' => 'ini.core.php#ini.user-dir',
192        'extension-dir' => 'ini.core.php#ini.extension-dir',
193        'enable-dl' => 'info.configuration.php#ini.enable-dl',
194        'cgi.force-redirect' => 'ini.core.php#ini.cgi.force-redirect',
195        'cgi.redirect-status-env' => 'ini.core.php#ini.cgi.redirect-status-env',
196        'cgi.fix-pathinfo' => 'ini.core.php#ini.cgi.fix-pathinfo',
197        'fastcgi.impersonate' => 'ini.core.php#ini.fastcgi.impersonate',
198        'cgi.rfc2616-headers' => 'ini.core.php#ini.cgi.rfc2616-headers',
199        'file-uploads' => 'ini.core.php#ini.file-uploads',
200        'upload-tmp-dir' => 'ini.core.php#ini.upload-tmp-dir',
201        'upload-max-filesize' => 'ini.core.php#ini.upload-max-filesize',
202        'allow-url-fopen' => 'filesystem.configuration.php#ini.allow-url-fopen',
203        'allow-url-include' => 'filesystem.configuration.php#ini.allow-url-include',
204        'from' => 'filesystem.configuration.php#ini.from',
205        'user-agent' => 'filesystem.configuration.php#ini.user-agent',
206        'default-socket-timeout' => 'filesystem.configuration.php#ini.default-socket-timeout',
207        'auto-detect-line-endings' => 'filesystem.configuration.php#ini.auto-detect-line-endings',
208        'date.timezone' => 'datetime.configuration.php#ini.date.timezone',
209        'date.default-latitude' => 'datetime.configuration.php#ini.date.default-latitude',
210        'date.default-longitude' => 'datetime.configuration.php#ini.date.default-longitude',
211        'date.sunrise-zenith' => 'datetime.configuration.php#ini.date.sunrise-zenith',
212        'date.sunset-zenith' => 'datetime.configuration.php#ini.date.sunset-zenith',
213        'filter.default' => 'filter.configuration.php#ini.filter.default',
214        'filter.default-flags' => 'filter.configuration.php#ini.filter.default-flags',
215        'pcre.backtrack-limit' => 'pcre.configuration.php#ini.pcre.backtrack-limit',
216        'pcre.recursion-limit' => 'pcre.configuration.php#ini.pcre.recursion-limit',
217        'pdo-odbc.connection-pooling' => 'ref.pdo-odbc.php#ini.pdo-odbc.connection-pooling',
218        'phar.readonly' => 'phar.configuration.php#ini.phar.readonly',
219        'phar.require-hash' => 'phar.configuration.php#ini.phar.require-hash',
220        'define-syslog-variables' => 'network.configuration.php#ini.define-syslog-variables',
221        'smtp' => 'mail.configuration.php#ini.smtp',
222        'smtp-port' => 'mail.configuration.php#ini.smtp-port',
223        'sendmail-from' => 'mail.configuration.php#ini.sendmail-from',
224        'sendmail-path' => 'mail.configuration.php#ini.sendmail-path',
225        'sql.safe-mode' => 'ini.core.php#ini.sql.safe-mode',
226        'odbc.default-db' => 'odbc.configuration.php#ini.uodbc.default-db',
227        'odbc.default-user' => 'odbc.configuration.php#ini.uodbc.default-user',
228        'odbc.default-pw' => 'odbc.configuration.php#ini.uodbc.default-pw',
229        'odbc.allow-persistent' => 'odbc.configuration.php#ini.uodbc.allow-persistent',
230        'odbc.check-persistent' => 'odbc.configuration.php#ini.uodbc.check-persistent',
231        'odbc.max-persistent' => 'odbc.configuration.php#ini.uodbc.max-persistent',
232        'odbc.max-links' => 'odbc.configuration.php#ini.uodbc.max-links',
233        'odbc.defaultlrl' => 'odbc.configuration.php#ini.uodbc.defaultlrl',
234        'odbc.defaultbinmode' => 'odbc.configuration.php#ini.uodbc.defaultbinmode',
235        'mysql.allow-local-infile' => 'mysql.configuration.php#ini.mysql.allow-local-infile',
236        'mysql.allow-persistent' => 'mysql.configuration.php#ini.mysql.allow-persistent',
237        'mysql.max-persistent' => 'mysql.configuration.php#ini.mysql.max-persistent',
238        'mysql.max-links' => 'mysql.configuration.php#ini.mysql.max-links',
239        'mysql.default-port' => 'mysql.configuration.php#ini.mysql.default-port',
240        'mysql.default-socket' => 'mysql.configuration.php#ini.mysql.default-socket',
241        'mysql.default-host' => 'mysql.configuration.php#ini.mysql.default-host',
242        'mysql.default-user' => 'mysql.configuration.php#ini.mysql.default-user',
243        'mysql.default-password' => 'mysql.configuration.php#ini.mysql.default-password',
244        'mysql.connect-timeout' => 'mysql.configuration.php#ini.mysql.connect-timeout',
245        'mysql.trace-mode' => 'mysql.configuration.php#ini.mysql.trace-mode',
246        'mysqli.allow-local-infile' => 'mysqli.configuration.php#ini.mysqli.allow-local-infile',
247        'mysqli.max-links' => 'mysqli.configuration.php#ini.mysqli.max-links',
248        'mysqli.allow-persistent' => 'mysqli.configuration.php#ini.mysqli.allow-persistent',
249        'mysqli.default-port' => 'mysqli.configuration.php#ini.mysqli.default-port',
250        'mysqli.default-socket' => 'mysqli.configuration.php#ini.mysqli.default-socket',
251        'mysqli.default-host' => 'mysqli.configuration.php#ini.mysqli.default-host',
252        'mysqli.default-user' => 'mysqli.configuration.php#ini.mysqli.default-user',
253        'mysqli.default-pw' => 'mysqli.configuration.php#ini.mysqli.default-pw',
254        'oci8.privileged-connect' => 'oci8.configuration.php#ini.oci8.privileged-connect',
255        'oci8.max-persistent' => 'oci8.configuration.php#ini.oci8.max-persistent',
256        'oci8.persistent-timeout' => 'oci8.configuration.php#ini.oci8.persistent-timeout',
257        'oci8.ping-interval' => 'oci8.configuration.php#ini.oci8.ping-interval',
258        'oci8.statement-cache-size' => 'oci8.configuration.php#ini.oci8.statement-cache-size',
259        'oci8.default-prefetch' => 'oci8.configuration.php#ini.oci8.default-prefetch',
260        'oci8.old-oci-close-semantics' => 'oci8.configuration.php#ini.oci8.old-oci-close-semantics',
261        'opcache.preload' => 'opcache.configuration.php#ini.opcache.preload',
262        'pgsql.allow-persistent' => 'pgsql.configuration.php#ini.pgsql.allow-persistent',
263        'pgsql.auto-reset-persistent' => 'pgsql.configuration.php#ini.pgsql.auto-reset-persistent',
264        'pgsql.max-persistent' => 'pgsql.configuration.php#ini.pgsql.max-persistent',
265        'pgsql.max-links' => 'pgsql.configuration.php#ini.pgsql.max-links',
266        'pgsql.ignore-notice' => 'pgsql.configuration.php#ini.pgsql.ignore-notice',
267        'pgsql.log-notice' => 'pgsql.configuration.php#ini.pgsql.log-notice',
268        'sqlite3.extension-dir' => 'sqlite3.configuration.php#ini.sqlite3.extension-dir',
269        'bcmath.scale' => 'bc.configuration.php#ini.bcmath.scale',
270        'browscap' => 'misc.configuration.php#ini.browscap',
271        'session.save-handler' => 'session.configuration.php#ini.session.save-handler',
272        'session.save-path' => 'session.configuration.php#ini.session.save-path',
273        'session.use-cookies' => 'session.configuration.php#ini.session.use-cookies',
274        'session.cookie-secure' => 'session.configuration.php#ini.session.cookie-secure',
275        'session.use-only-cookies' => 'session.configuration.php#ini.session.use-only-cookies',
276        'session.name' => 'session.configuration.php#ini.session.name',
277        'session.auto-start' => 'session.configuration.php#ini.session.auto-start',
278        'session.cookie-lifetime' => 'session.configuration.php#ini.session.cookie-lifetime',
279        'session.cookie-path' => 'session.configuration.php#ini.session.cookie-path',
280        'session.cookie-domain' => 'session.configuration.php#ini.session.cookie-domain',
281        'session.cookie-httponly' => 'session.configuration.php#ini.session.cookie-httponly',
282        'session.serialize-handler' => 'session.configuration.php#ini.session.serialize-handler',
283        'session.gc-probability' => 'session.configuration.php#ini.session.gc-probability',
284        'session.gc-divisor' => 'session.configuration.php#ini.session.gc-divisor',
285        'session.gc-maxlifetime' => 'session.configuration.php#ini.session.gc-maxlifetime',
286        'session.bug-compat-42' => 'session.configuration.php#ini.session.bug-compat-42',
287        'session.bug-compat-warn' => 'session.configuration.php#ini.session.bug-compat-warn',
288        'session.referer-check' => 'session.configuration.php#ini.session.referer-check',
289        'session.entropy-length' => 'session.configuration.php#ini.session.entropy-length',
290        'session.entropy-file' => 'session.configuration.php#ini.session.entropy-file',
291        'session.cache-limiter' => 'session.configuration.php#ini.session.cache-limiter',
292        'session.cache-expire' => 'session.configuration.php#ini.session.cache-expire',
293        'session.sid-length' => 'session.configuration.php#ini.session.sid-length',
294        'session.use-trans-sid' => 'session.configuration.php#ini.session.use-trans-sid',
295        'session.hash-function' => 'session.configuration.php#ini.session.hash-function',
296        'session.hash-bits-per-character' => 'session.configuration.php#ini.session.hash-bits-per-character',
297        'session.upload-progress.enabled' => 'session.configuration.php#ini.session.upload-progress.enabled',
298        'session.upload-progress.cleanup' => 'session.configuration.php#ini.session.upload-progress.cleanup',
299        'session.upload-progress.prefix' => 'session.configuration.php#ini.session.upload-progress.prefix',
300        'session.upload-progress.name' => 'session.configuration.php#ini.session.upload-progress.name',
301        'session.upload-progress.freq' => 'session.configuration.php#ini.session.upload-progress.freq',
302        'session.upload-progress.min-freq' => 'session.configuration.php#ini.session.upload-progress.min-freq',
303        'url-rewriter.tags' => 'session.configuration.php#ini.url-rewriter.tags',
304        'assert.active' => 'info.configuration.php#ini.assert.active',
305        'assert.exception' => 'info.configuration.php#ini.assert.exception',
306        'assert.warning' => 'info.configuration.php#ini.assert.warning',
307        'assert.bail' => 'info.configuration.php#ini.assert.bail',
308        'assert.callback' => 'info.configuration.php#ini.assert.callback',
309        'assert.quiet-eval' => 'info.configuration.php#ini.assert.quiet-eval',
310        'zend.enable-gc' => 'info.configuration.php#ini.zend.enable-gc',
311        'com.typelib-file' => 'com.configuration.php#ini.com.typelib-file',
312        'com.allow-dcom' => 'com.configuration.php#ini.com.allow-dcom',
313        'com.autoregister-typelib' => 'com.configuration.php#ini.com.autoregister-typelib',
314        'com.autoregister-casesensitive' => 'com.configuration.php#ini.com.autoregister-casesensitive',
315        'com.autoregister-verbose' => 'com.configuration.php#ini.com.autoregister-verbose',
316        'mbstring.language' => 'mbstring.configuration.php#ini.mbstring.language',
317        'mbstring.internal-encoding' => 'mbstring.configuration.php#ini.mbstring.internal-encoding',
318        'mbstring.http-input' => 'mbstring.configuration.php#ini.mbstring.http-input',
319        'mbstring.http-output' => 'mbstring.configuration.php#ini.mbstring.http-output',
320        'mbstring.encoding-translation' => 'mbstring.configuration.php#ini.mbstring.encoding-translation',
321        'mbstring.detect-order' => 'mbstring.configuration.php#ini.mbstring.detect-order',
322        'mbstring.substitute-character' => 'mbstring.configuration.php#ini.mbstring.substitute-character',
323        'mbstring.func-overload' => 'mbstring.configuration.php#ini.mbstring.func-overload',
324        'gd.jpeg-ignore-warning' => 'image.configuration.php#ini.image.jpeg-ignore-warning',
325        'exif.encode-unicode' => 'exif.configuration.php#ini.exif.encode-unicode',
326        'exif.decode-unicode-motorola' => 'exif.configuration.php#ini.exif.decode-unicode-motorola',
327        'exif.decode-unicode-intel' => 'exif.configuration.php#ini.exif.decode-unicode-intel',
328        'exif.encode-jis' => 'exif.configuration.php#ini.exif.encode-jis',
329        'exif.decode-jis-motorola' => 'exif.configuration.php#ini.exif.decode-jis-motorola',
330        'exif.decode-jis-intel' => 'exif.configuration.php#ini.exif.decode-jis-intel',
331        'tidy.default-config' => 'tidy.configuration.php#ini.tidy.default-config',
332        'tidy.clean-output' => 'tidy.configuration.php#ini.tidy.clean-output',
333        'soap.wsdl-cache-enabled' => 'soap.configuration.php#ini.soap.wsdl-cache-enabled',
334        'soap.wsdl-cache-dir' => 'soap.configuration.php#ini.soap.wsdl-cache-dir',
335        'soap.wsdl-cache-ttl' => 'soap.configuration.php#ini.soap.wsdl-cache-ttl',
336    ];
337
338    return $inis[$ini] ?? null;
339}
340
341function is_known_variable(string $variable): ?string {
342    $variables = [
343        // Variables
344        'globals' => 'reserved.variables.globals.php',
345        '-server' => 'reserved.variables.server.php',
346        '-get' => 'reserved.variables.get.php',
347        '-post' => 'reserved.variables.post.php',
348        '-files' => 'reserved.variables.files.php',
349        '-request' => 'reserved.variables.request.php',
350        '-session' => 'reserved.variables.session.php',
351        '-cookie' => 'reserved.variables.cookies.php',
352        '-env' => 'reserved.variables.environment.php',
353        'this' => 'language.oop5.basic.php',
354        'php-errormsg' => 'reserved.variables.phperrormsg.php',
355        'argv' => 'reserved.variables.argv.php',
356        'argc' => 'reserved.variables.argc.php',
357        'http-response-header' => 'reserved.variables.httpresponseheader.php',
358        'http-server-vars' => 'reserved.variables.server.php',
359        'http-get-vars' => 'reserved.variables.get.php',
360        'http-post-vars' => 'reserved.variables.post.php',
361        'http-session-vars' => 'reserved.variables.session.php',
362        'http-post-files' => 'reserved.variables.files.php',
363        'http-cookie-vars' => 'reserved.variables.cookies.php',
364    ];
365
366    return $variables[ltrim($variable, '$')] ?? null;
367}
368
369function is_known_term(string $term): ?string {
370    $terms = [
371        '<>' => 'language.operators.comparison.php',
372        '<=>' => 'language.operators.comparison.php',
373        'spaceship' => 'language.operators.comparison.php',
374        '==' => 'language.operators.comparison.php',
375        '===' => 'language.operators.comparison.php',
376        '@' => 'language.operators.errorcontrol.php',
377        '__halt_compiler' => 'function.halt-compiler.php',
378        '__PHP_Incomplete_Class' => 'function.unserialize.php',
379        'and' => 'language.operators.logical.php',
380        'apache' => 'install.php',
381        'array' => 'language.types.array.php',
382        'arrays' => 'language.types.array.php',
383        'as' => 'control-structures.foreach.php',
384        'case' => 'control-structures.switch.php',
385        'catch' => 'language.exceptions.php',
386        'checkbox' => 'faq.html.php',
387        'class' => 'language.oop5.basic.php',
388        'classes' => 'language.oop5.basic.php',
389        'closures' => 'functions.anonymous.php',
390        'cookie' => 'features.cookies.php',
391        'date' => 'function.date.php',
392        'default' => 'control-structures.switch.php',
393        'do' => 'control-structures.do.while.php',
394        'enddeclare' => 'control-structures.declare.php',
395        'endfor' => 'control-structures.alternative-syntax.php',
396        'endforeach' => 'control-structures.alternative-syntax.php',
397        'endif' => 'control-structures.alternative-syntax.php',
398        'endswitch' => 'control-structures.alternative-syntax.php',
399        'endwhile' => 'control-structures.alternative-syntax.php',
400        'exception' => 'language.exceptions.php',
401        'extends' => 'language.oop5.basic.php#language.oop5.basic.extends',
402        'false' => 'language.types.boolean.php',
403        'file' => 'function.file.php',
404        'final' => 'language.oop5.final.php',
405        'finally' => 'language.exceptions.php',
406        'fopen' => 'function.fopen.php',
407        'for' => 'control-structures.for.php',
408        'foreach' => 'control-structures.foreach.php',
409        'form' => 'language.variables.external.php',
410        'forms' => 'language.variables.external.php',
411        'function' => 'language.functions.php',
412        'gd' => 'book.image.php',
413        'get' => 'reserved.variables.get.php',
414        'global' => 'language.variables.scope.php',
415        'globals' => 'language.variables.scope.php',
416        'header' => 'function.header.php',
417        'heredoc' => 'language.types.string.php#language.types.string.syntax.heredoc',
418        'htaccess' => 'configuration.file.php',
419        'if' => 'control-structures.if.php',
420        'implements' => 'language.oop5.interfaces.php',
421        'include' => 'function.include.php',
422        'insteadof' => 'language.oop5.traits.php#language.oop5.traits.conflict',
423        'int' => 'language.types.integer.php',
424        'ip' => 'reserved.variables.server.php',
425        'iterable' => 'language.types.iterable.php',
426        'juggling' => 'language.types.type-juggling.php',
427        'location' => 'function.header.php',
428        'mail' => 'function.mail.php',
429        'mixed' => 'language.types.mixed.php',
430        'modulo' => 'language.operators.arithmetic.php',
431        'mysql' => 'mysql.php',
432        'never' => 'language.types.never.php',
433        'new' => 'language.oop5.basic.php#language.oop5.basic.new',
434        'nowdoc' => 'language.types.string.php#language.types.string.syntax.nowdoc',
435        'null' => 'language.types.null.php',
436        'numeric' => 'reserved.other-reserved-words.php',
437        'object' => 'language.types.object.php',
438        'operator' => 'language.operators.php',
439        'operators' => 'language.operators.php',
440        'or' => 'language.operators.logical.php',
441        'parent' => 'reserved.classes.php#reserved.classes.special',
442        'php.ini' => 'configuration.file.php',
443        'php-mysql.dll' => 'book.mysql.php',
444        'php-self' => 'reserved.variables.server.php',
445        'query-string' => 'reserved.variables.server.php',
446        'readonly' => 'language.oop5.properties.php#language.oop5.properties.readonly-properties',
447        'redirect' => 'function.header.php',
448        'reference' => 'index.php',
449        'referer' => 'reserved.variables.server.php',
450        'referrer' => 'reserved.variables.server.php',
451        'remote-addr' => 'reserved.variables.server.php',
452        'request' => 'reserved.variables.request.php',
453        'self' => 'reserved.classes.php#reserved.classes.special',
454        'session' => 'features.sessions.php',
455        'smtp' => 'book.mail.php',
456        'ssl' => 'book.openssl.php',
457        'static' => 'language.oop5.static.php',
458        'stdin' => 'wrappers.php.php',
459        'string' => 'language.types.string.php',
460        'superglobal' => 'language.variables.superglobals.php',
461        'superglobals' => 'language.variables.superglobals.php',
462        'switch' => 'control-structures.switch.php',
463        'timestamp' => 'function.time.php',
464        'true' => 'language.types.boolean.php',
465        'try' => 'language.exceptions.php',
466        'upload' => 'features.file-upload.php',
467        'use' => 'language.namespaces.php',
468        'void' => 'language.types.void.php',
469        'xor' => 'language.operators.logical.php',
470        'yield from' => 'language.generators.syntax.php#control-structures.yield.from',
471        'yield' => 'language.generators.syntax.php#control-structures.yield',
472
473        '__COMPILER_HALT_OFFSET__' => 'function.halt-compiler.php',
474        'DEFAULT_INCLUDE_PATH' => 'reserved.constants.php',
475        'E_ALL' => 'errorfunc.constants.php',
476        'E_COMPILE_ERROR' => 'errorfunc.constants.php',
477        'E_COMPILE_WARNING' => 'errorfunc.constants.php',
478        'E_CORE_ERROR' => 'errorfunc.constants.php',
479        'E_CORE_WARNING' => 'errorfunc.constants.php',
480        'E_DEPRECATED' => 'errorfunc.constants.php',
481        'E_ERROR' => 'errorfunc.constants.php',
482        'E_NOTICE' => 'errorfunc.constants.php',
483        'E_PARSE' => 'errorfunc.constants.php',
484        'E_RECOVERABLE_ERROR' => 'errorfunc.constants.php',
485        'E_STRICT' => 'errorfunc.constants.php',
486        'E_USER_DEPRECATED' => 'errorfunc.constants.php',
487        'E_USER_ERROR' => 'errorfunc.constants.php',
488        'E_USER_NOTICE' => 'errorfunc.constants.php',
489        'E_USER_WARNING' => 'errorfunc.constants.php',
490        'E_WARNING' => 'errorfunc.constants.php',
491        'PEAR_EXTENSION_DIR' => 'reserved.constants.php',
492        'PEAR_INSTALL_DIR' => 'reserved.constants.php',
493        'PHP_BINARY' => 'reserved.constants.php',
494        'PHP_BINDIR' => 'reserved.constants.php',
495        'PHP_CONFIG_FILE_PATH' => 'reserved.constants.php',
496        'PHP_CONFIG_FILE_SCAN_DIR' => 'reserved.constants.php',
497        'PHP_DATADIR' => 'reserved.constants.php',
498        'PHP_DEBUG' => 'reserved.constants.php',
499        'PHP_EOL' => 'reserved.constants.php',
500        'PHP_EXTENSION_DIR' => 'reserved.constants.php',
501        'PHP_EXTRA_VERSION' => 'reserved.constants.php',
502        'PHP_FD_SETSIZE' => 'reserved.constants.php',
503        'PHP_FLOAT_DIG' => 'reserved.constants.php',
504        'PHP_FLOAT_EPSILON' => 'reserved.constants.php',
505        'PHP_FLOAT_MAX' => 'reserved.constants.php',
506        'PHP_FLOAT_MIN' => 'reserved.constants.php',
507        'PHP_INT_MAX' => 'reserved.constants.php',
508        'PHP_INT_MIN' => 'reserved.constants.php',
509        'PHP_INT_SIZE' => 'reserved.constants.php',
510        'PHP_LIBDIR' => 'reserved.constants.php',
511        'PHP_LOCALSTATEDIR' => 'reserved.constants.php',
512        'PHP_MAJOR_VERSION' => 'reserved.constants.php',
513        'PHP_MANDIR' => 'reserved.constants.php',
514        'PHP_MAXPATHLEN' => 'reserved.constants.php',
515        'PHP_MINOR_VERSION' => 'reserved.constants.php',
516        'PHP_OS_FAMILY' => 'reserved.constants.php',
517        'PHP_OS' => 'reserved.constants.php',
518        'PHP_PREFIX' => 'reserved.constants.php',
519        'PHP_RELEASE_VERSION' => 'reserved.constants.php',
520        'PHP_SAPI' => 'reserved.constants.php',
521        'PHP_SHLIB_SUFFIX' => 'reserved.constants.php',
522        'PHP_SYSCONFDIR' => 'reserved.constants.php',
523        'PHP_VERSION_ID' => 'reserved.constants.php',
524        'PHP_VERSION' => 'reserved.constants.php',
525        'PHP_WINDOWS_EVENT_CTRL_BREAK' => 'reserved.constants.php',
526        'PHP_WINDOWS_EVENT_CTRL_C' => 'reserved.constants.php',
527        'PHP_ZTS' => 'reserved.constants.php',
528    ];
529
530    return $terms[$term] ?? null;
531}
532
533/*
534Search snippet provider: A dirty proof-of-concept:
535    This will probably live in sqlite one day, and be more intelligent (tagging?)
536    This is a 100% hack currently, and let's hope temporary does not become permanent (Hello year 2014!)
537    And this is English only today... we should add translation support via the manual, generated by PhD
538
539    This really is a proof-of-concept, where the choices below are the most popular searched terms at php.net
540    It should also take into account vague searches, such as 'global' and 'str'. The search works well enough for,
541    most terms, so something like $_SERVER isn't really needed but it's defined below anyways...
542*/
543function is_known_snippet(string $term): ?string {
544    $snippets = [
545        'global' => '
546		The <b>global</b> keyword is used to manipulate <a href="/language.variables.scope">variable scope</a>, and
547		there is also the concept of <a href="/language.variables.superglobals">super globals</a> in PHP,
548		which are special variables with a global scope.',
549        'string' => '
550		There is the <a href="/language.types.string">string type</a>, which is a scalar,
551		and also many <a href="/ref.strings">string functions.</a>',
552        'str' => '
553		Many <a href="/ref.strings">string functions</a> begin with <b>str</b>,
554		and there is also the <a href="/language.types.string">string type</a>.',
555        '_server' => '
556		<a href="/reserved.variables.server">$_SERVER</a>
557		is a <a href="/language.variables.superglobals">super global</a>,
558		and is home to many predefined variables that are typically provided by a web server',
559        'class' => '
560		A <a href="/language.oop5">class</a> is an OOP (Object Oriented Programming) concept,
561		and PHP is both a procedural and OOP friendly language.',
562        'function' => '
563		PHP contains thousands of functions. You might be interested in how a
564		<a href="/language.functions">function is defined</a>, or
565		<a href="/about.prototypes">how to read a function prototype</a>.
566		See also the list of <a href="/extensions">PHP extensions</a>',
567    ];
568
569    $term = ltrim(strtolower(trim($term)), '$');
570    return $snippets[$term] ?? null;
571}
572
573/**
574 * @param string $uri
575 * @return array<string, string>
576 */
577function get_legacy_manual_urls(string $uri): array
578{
579    $filename = $_SERVER["DOCUMENT_ROOT"] . "/manual/legacyurls.json";
580    $pages_ids = json_decode(file_get_contents($filename), true);
581    $page_id = preg_replace_callback('/^manual\/.*\/(.*?)(\.php)?$/', function (array $matches): string {
582        if (count($matches) < 2) {
583            return '';
584        }
585        return $matches[1];
586    }, $uri);
587
588    if (!isset($pages_ids[$page_id])) {
589        return [];
590    }
591
592    $legacy_urls = [];
593    foreach ($pages_ids[$page_id] as $php_version) {
594        $legacy_urls[$php_version] = convert_manual_uri_to_legacy($uri, $php_version);
595    }
596
597    return $legacy_urls;
598}
599
600/**
601 * @param array<string, string> $legacy_urls URLs to legacy manuals, indexed by major PHP version
602 */
603function fallback_to_legacy_manuals(array $legacy_urls): void
604{
605    global $MYSITE;
606    status_header(404);
607    site_header('404 Not Found', ["noindex"]);
608
609    $original_url = htmlspecialchars(substr($MYSITE, 0, -1) . $_SERVER['REQUEST_URI']);
610    $legacy_links = '';
611    foreach ($legacy_urls as $php_version => $url) {
612        $legacy_links .= '<li><a href="' . $url . '">PHP ' . $php_version . ' legacy manual</a></li>';
613    }
614
615    echo <<<HTML
616<h1>Not Found</h1>
617<p>The manual page you are looking for (<strong>$original_url</strong>) is not available.</p>
618
619<p>However, this page exists in a legacy manual for the following PHP versions.</p>
620<ul id="legacy-links">$legacy_links</ul>
621<p>Please note that legacy manuals are maintained by Zend and not the PHP Documentation Group. Any questions about their content should be reported via the "Report a Bug" links on the appropriate pages.</p>
622
623<p>If you were looking for this page in the current PHP version and believe this to be a mistake, please check back later, or if the problem persists, <a href="/contact.php">contact the webmasters</a>.</p>
624HTML;
625
626    site_footer();
627    exit;
628}
629
630function convert_manual_uri_to_legacy(string $uri, int $php_version): string
631{
632    $parts = explode('/', $uri);
633
634    return sprintf('https://php-legacy-docs.zend.com/manual/php%s/%s/%s', $php_version, $parts[1], $parts[2]);
635}
636