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