source: trunk/common/common.php @ 1164

Revision 1164, 11.8 KB checked in by btataroiu@…, 3 weeks ago (diff)

Make infoarena compatible with HPHP.

  • Implemented APC caching support since eaccelerator is not compatible with HPHP and although we support memcached, we do not use it on live.
  • Replaced instances of create_function_cached with php anonymous functions. This bumps the PHP version requirement to 5.3, but it allows the code to work with HPHP, since dynamic function generation is replaced with staticly analyzable functions. This also removes a horrible, terrible hack in Textile.php which was storing the current instance of the Textile class being processed as a static variable so that it could be accessed by anonymous functions.
  • Replaced occurrences of assert with log_assert.
  • When running under HPHP, IA_HPHP_ENV is defined and is used to bypass some incompatible code (redundant ini configuration checks).
  • Fixed some includes that were dependent on the working directory being www/
  • Included a sample HPHP configuration file and a Makefile with compilation commands.

REVIEW URL:  http://reviewboard.infoarena.ro/r/182/

  • Property svn:eol-style set to native
Line 
1<?php
2
3// Changes all special characters ș, ț, Ș, Ț to ş, ţ, Ş, Ţ
4// commabelow to cedille and returns the modified text
5// FIXME: reverse characters in to_change when windows XP dies
6function text_change_special_chars($text) {
7    $to_change = array("ș"=>"ş", "ț"=>"ţ", "Ș"=>"Ş", "Ț"=>"Ţ");
8
9    foreach ($to_change as $bad=>$good) {
10        $text = mb_ereg_replace($bad, $good, $text);
11    }
12
13    return $text;
14}
15
16// Nicer way to get an element from an array. It returns a default value
17// (defaulting to null) instead of throwing an error.
18function getattr($dict, $attribute, $default = null) {
19    if (isset($dict[$attribute])) {
20        return $dict[$attribute];
21    } else {
22        return $default;
23    }
24}
25
26// This is a bunch of common regular expressions.
27// NOTE: centralize all regular expressions here, don't copy paste
28// This way we can avoid issues with one regexp allowing a dot in username
29// and another not allowing it.
30//
31// Do not include // stuff, we should be able to nest them.
32// Do not capture. use (?: lalala ) instead of ( lalala )
33// Case insensitive unless specially mentioned. Assume /xi
34// Prefix with IA_RE
35
36// Valid page names. This allows //SanNdBox/ stuff
37define("IA_RE_PAGE_NAME", '[a-z0-9][a-z0-9_\-\.\@\/]*');
38
39// Valid normal page names. This only allows sand/box stuff
40// Lowercase, words are separated by only one /(no trailing).
41// CASE sensitive!!
42define("IA_RE_NORMAL_PAGE_NAME", '
43        (?: [a-z0-9_\-\.\@\+\*\[\]]* )
44        (?: \/ [a-z0-9_\-\.\@]* )*');
45
46// Short identifiers. FIXME: limit length here too?
47define("IA_RE_ROUND_ID", '[a-z0-9][a-z0-9_\-\.]*');
48define("IA_RE_TASK_ID", '(?-i:[a-z0-9][a-z0-9_\-\.]*)');
49define("IA_RE_TAG_NAME", '[a-z0-9\-\.\ \@\(\)]+');
50define("IA_RE_SCORE_NAME", '[a-z0-9][a-z0-9_\-\.]*');
51define("IA_RE_USER_NAME", '[_@a-z0-9][a-z0-9_\-\.\@]*');
52
53// IPv4 address, doesn't check for values greater than 255.
54define("IA_RE_IPV4", '(?:\d{1,3}\.){3}\d{1,3}');
55// Full IPv6 address, doesn't match compressed IPv6 formats.
56define("IA_RE_IPV6_NO_COMPRESS", '(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}');
57// IP address, matches IPv4 and non-compressed IPv6.
58define("IA_RE_IP_ADDRESS", IA_RE_IPV4."|".IA_RE_IPV6_NO_COMPRESS);
59
60// Valid email. A complete check is not possible, see
61// http://www.regular-expressions.info/email.html
62define("IA_RE_EMAIL", '[^@]+@.+\..+');
63
64// User full name. Your name can't be %$!
65define("IA_RE_USER_FULL_NAME", '[a-z0-9\-\.\ \@]+');
66
67// Attachment names.
68// Starts with letter or number, can also contain .-_
69// No funky stuff.
70define("IA_RE_ATTACHMENT_NAME", '[a-z0-9][a-z0-9\.\-_]*');
71
72// External urls. Used by textile.
73define("IA_RE_EXTERNAL_URL", '[a-z]+:\/\/|mailto:[^@]+@[^@]+|[^@]+@[^@]');
74
75// Constants used by the task list filter
76define("IA_TLF_ALL", '');
77define("IA_TLF_UNSOLVED", '1');
78define("IA_TLF_TRIED", '2');
79define("IA_TLF_SOLVED", '3');
80
81// Constants for IA user-defined rounds
82// Users can't create rounds lasting 123141231223 hours
83define("IA_USER_DEFINED_ROUND_DURATION_LIMIT", '48');
84// Users can't create rounds 5412312421 days before the round starts
85define("IA_USER_DEFINED_ROUND_DAYSBEFORE_LIMIT", '30');
86// Users can't create user defined rounds with too many problems
87define("IA_USER_DEFINED_ROUND_TASK_LIMIT", '25');
88
89// Windows hack for checkdnsrr function.
90// FIXME: Not fully tested!
91if (!function_exists('checkdnsrr')) {
92    log_warn("Function checkdnsrr does not exist. ".
93             "Presuming Windows NT enviroment and reimplementing it.");
94
95    function checkdnsrr($hostName, $recType = "MX")
96    {
97        if (empty($hostName)) {
98            return false;
99        }
100
101        exec("nslookup -type=$recType $hostName", $result);
102        // Check each line to find the one that starts with the host name.
103        // If it exists then the function succeeded.
104        foreach ($result as $line) {
105            if (eregi("^$hostName" ,$line)) {
106                return true;
107            }
108        }
109
110        // Otherwise there was no mail handler for the domain
111        return false;
112    }
113}
114
115// Check if a a variable is a whole number.
116function is_whole_number($x) {
117    return is_numeric($x) && $x == intval($x);
118}
119
120// tell if email address seems to be valid
121function is_valid_email($email) {
122    if (!preg_match('/^'.IA_RE_EMAIL.'$/xi', $email)) {
123        return false;
124    }
125    $domain = explode("@", $email);
126    $domain = array_pop($domain).'.';
127    if (!checkdnsrr($domain, "MX")) {
128        return false;
129    }
130    return true;
131}
132
133// Check for (apparently) valid IP address.
134// It will match IPv4 and only non-compressed IPv6. Doesn't check
135// for values greater than 255.
136function is_valid_ip_address($ip_address) {
137    return preg_match('/^'.IA_RE_IP_ADDRESS.'$/xi', $ip_address);
138}
139
140// Normalize a page name. Removes extra slashes and lowercases.
141// This should always be done before entering the database.
142function normalize_page_name($page_name) {
143    $path = preg_split('/\//', $page_name, -1, PREG_SPLIT_NO_EMPTY);
144    return strtolower(implode('/', $path));
145}
146
147// Checks if textblock name is normalized.
148// no double slashes, no slashes at the end, no capitalizations.
149function is_normal_page_name($page_name) {
150    return preg_match('/^'.IA_RE_NORMAL_PAGE_NAME.'$/xi', $page_name);
151}
152
153// Validates page name
154function is_page_name($page_name) {
155    return preg_match('/^'.IA_RE_PAGE_NAME.'$/xi', $page_name);
156}
157
158/**
159 * Validates user page name
160 *
161 * @param  string  $page_name
162 * @return array                returns an array containing the matched user
163 */
164function get_page_user_name($page_name) {
165    preg_match("/^ ".
166                preg_quote(IA_USER_TEXTBLOCK_PREFIX, '/').
167                '('.IA_RE_USER_NAME.") (\/?.*) $/xi",
168                $page_name, $matches);
169    return $matches;
170}
171
172// returns boolean whether specified attach name is valid
173// NOTE: We hereby limit file names. No spaces, please. Not that we have
174// a problem with spaces inside URLs. Everything should be (and hopefully is)
175// urlencode()-ed. However, practical experience shows it is hard to work with
176// such file names, mostly due to URLs word-wrapping when inserted in texts,
177// unless, of course, one knows how to properly escape spaces with %20 or +
178function is_attachment_name($attach_name) {
179    return preg_match('/^'.IA_RE_ATTACHMENT_NAME.'$/xi', $attach_name);
180}
181
182// FIXME: crappy check
183function is_user_id($user_id) {
184    return is_whole_number($user_id);
185}
186
187// FIXME: crappy check
188function is_attachment_id($id) {
189    return is_whole_number($id);
190}
191
192// tells whether $round_id is a valid round identifier
193// Does not check existence.
194function is_round_id($round_id) {
195    return preg_match('/^'.IA_RE_ROUND_ID.'$/xi', $round_id) &&
196        strlen($round_id) < 64;
197}
198
199// Check valid score names.
200// Does not check existence.
201function is_score_name($score_name) {
202    return preg_match('/^'.IA_RE_SCORE_NAME.'$/xi', $score_name) &&
203        strlen($score_name) < 64;
204}
205
206// Tells whether $task_id is a valid task identifier
207// Does not check existence.
208function is_task_id($task_id) {
209    return preg_match('/^'.IA_RE_TASK_ID.'$/xi', $task_id) &&
210           strlen($task_id) < 64;
211}
212
213function is_blog_post($blog_post) {
214    return is_page_name($blog_post) && substr($blog_post, 0, 5) == 'blog/';
215}
216
217// Check job id
218function is_job_id($job_id) {
219    return is_whole_number($job_id);
220}
221
222// Check user name
223function is_user_name($user_name) {
224    return preg_match('/^'.IA_RE_USER_NAME.'$/xi', $user_name) &&
225           strlen($user_name) < 64;
226}
227
228// Check user full name (John Smith sr.)
229function is_user_full_name($user_full_name) {
230    return preg_match('/^'.IA_RE_USER_FULL_NAME.'$/xi', $user_full_name);
231}
232
233// Check tag id
234function is_tag_id($tag_id) {
235    return is_whole_number($tag_id);
236}
237
238// Check tag name
239function is_tag_name($tag_name) {
240    return preg_match('/^'.IA_RE_TAG_NAME.'$/xi', $tag_name) &&
241           strlen($tag_name) < 64;
242}
243
244// Check tag type
245function is_tag_type($tag_type) {
246    return in_array($tag_type, array(
247        'author', 'contest', 'year', 'round', 'age_group',
248        'method', 'algorithm', 'tag'
249    ));
250}
251
252// Check tag
253function is_tag($tag) {
254    if (!is_array($tag)) {
255        return false;
256    }
257    if (!array_key_exists("name", $tag)) {
258        return false;
259    }
260    if (!array_key_exists("type", $tag)) {
261        return false;
262    }
263    if (!array_key_exists("parent", $tag)) {
264        return false;
265    }
266    return (
267        is_tag_name($tag["name"]) &&
268        is_tag_type($tag["type"]) &&
269        is_tag_id($tag["parent"])
270    );
271}
272
273// Taggable objects
274function is_taggable($obj) {
275    return $obj == 'user' || $obj == 'task' || $obj == 'round' || $obj == 'textblock';
276}
277
278/**
279 * Returns whether or not the size type given is an existent one on the site
280 *
281 * @param  string  $size_type
282 * @return bool
283 */
284function is_valid_size_type($size_type) {
285    $size_types = array("full", "tiny", "small", "normal", "forum", "big");
286    return in_array($size_type, $size_types);
287}
288
289// Cached version of create_function_cached.
290// Never use create_function_cached directly because it's a memory leak.
291// It's better to try avoid create_function_cached anyway, but if you
292// have to use it it's better to use this cached version.
293function create_function_cached($args, $code) {
294    if (!IA_ENABLE_CREATE_FUNCTION_CACHE) {
295        return create_function($args, $code);
296    }
297    static $_cache = array();
298    $key = str_replace('|', '<|>', $args) . '|' . str_replace('|', '<|>', $code);
299    if (!array_key_exists($key, $_cache)) {
300        $_cache[$key] = create_function($args, $code);
301    }
302    return $_cache[$key];
303}
304
305// Checks system requirements.
306// This will fail early if something is missing.
307function check_requirements()
308{
309    $extensions = get_loaded_extensions();
310
311    if (version_compare(phpversion(), '5.0', '<')) {
312        log_error("PHP 5.0 required.");
313    }
314    if (array_search('mysql', $extensions) === false) {
315        log_error("mysql extension required.");
316    }
317    if (array_search('gd', $extensions) === false) {
318        log_warn("gd extension missing.");
319    }
320    if (array_search('zip', $extensions) === false) {
321        log_warn("zip extension missing.");
322    }
323    if (array_search('mbstring', $extensions) === false) {
324        log_warn("mbstring extension missing. inline diff will not be enabled");
325    }
326    if (!function_exists("finfo_open")) {
327        log_warn("finfo_open missing, falling back to mime_content_type.");
328        if (!function_exists("mime_content_type")) {
329            log_warn("mime_content_type missing, mime-types will default to application/octet-stream.");
330        }
331    }
332
333    // Check for retarded php.ini settings.
334    if (IA_HTTP_ENV && !defined('IA_HPHP_ENV')) {
335        log_assert(!ini_get("session.auto_start"),
336                   "Please disable session.auto_start. It kills babies!");
337        log_assert(ini_get("session.use_cookies"),
338                   "Please enable session.use_cookies.");
339        log_assert(ini_get("session.use_only_cookies"),
340                   "Please enable session.use_only_cookies.");
341        log_assert(!ini_get("register_globals"),
342                   "Please disable register_globals. It makes baby Jesus cry!");
343        log_assert(!ini_get("magic_quotes_gpc"),
344                   "Please disable magic_quotes_gpc. Magic is for wussies!");
345        log_assert(!ini_get("magic_quotes_runtime"),
346                   "Please disable magic_quotes_runtime.");
347    }
348}
349
350// Various initialization
351// FIXME: it's WRONG for an include or require to execute code.
352// FIXME: I have no idea on where to move these things.
353
354// Force max error reporting.
355error_reporting(0xFFFF);
356
357// Initialize execution stats.
358if (IA_DEVELOPMENT_MODE) {
359    $execution_stats = array(
360        'timestamp' => microtime(true),
361        'queries' => 0,
362        'log_copy' => '',
363    );
364}
365
366// All timing our logic is done in UTC, the sensible way.
367// FORCE default timezone.
368if (function_exists("date_default_timezone_set")) {
369    date_default_timezone_set("UTC");
370}
371
372?>
Note: See TracBrowser for help on using the repository browser.