source: trunk/lib/Utilities.inc.php @ 534

Last change on this file since 534 was 534, checked in by anonymous, 9 years ago

Improved module maker validation output. Allow disabling cache at run time for ACL. Added ACL getList() method. Improved ACL CLI listing. Fixed app boomerang array initialization. Now retaining identical boomerang URLs if the key is different. Added a maximum boomerang time. Added a way to disable cache per request through a query string. Added validator isDecimal() method. Added disableSelectOptions() HTML method. Added getGravatarURL() method. Change how navigation page array is managed. Updated navigation currentPage() method to test an array of URLs.

File size: 46.6 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Utilities.inc.php
25 */
26
27
28/**
29 * Print variable dump.
30 *
31 * @param  mixed    $var        The variable to dump.
32 * @param  bool     $display    Print the dump in <pre> tags or hide it in html comments (non-CLI only).
33 * @param  bool     $var_dump   Use var_dump instead of print_r.
34 * @param  string   $file       Value of __FILE__.
35 * @param  string   $line       Value of __LINE__
36 */
37function dump($var, $display=false, $var_dump=false, $file='', $line='')
38{
39    if (defined('_CLI')) {
40        echo "DUMP FROM: $file $line\n";
41    } else {
42        echo $display ? "\n<br />DUMP <strong>$file $line</strong><br /><pre>\n" : "\n<!-- DUMP $file $line\n";
43    }
44    if ($var_dump) {
45        var_dump($var);
46    } else {
47        // Print human-readable descriptions of invisible types.
48        if (null === $var) {
49            echo '(null)';
50        } else if (true === $var) {
51            echo '(bool: true)';
52        } else if (false === $var) {
53            echo '(bool: false)';
54        } else if (is_scalar($var) && '' === $var) {
55            echo '(empty string)';
56        } else if (is_scalar($var) && preg_match('/^\s+$/', $var)) {
57            echo '(only white space)';
58        } else {
59            print_r($var);
60        }
61    }
62    if (defined('_CLI')) {
63        echo "\n";
64    } else {
65        echo $display ? "\n</pre><br />\n" : "\n-->\n";
66    }
67}
68
69/*
70* Log a PHP variable to javascript console. Relies on getDump(), below.
71*
72* @access   public
73* @param    mixed   $var      The variable to dump.
74* @param    string  $prefix   A short note to print before the output to make identifying output easier.
75* @param    string  $file     The value of __FILE__.
76* @param    string  $line     The value of __LINE__.
77* @return   null
78* @author   Quinn Comendant <quinn@strangecode.com>
79*/
80function jsDump($var, $prefix='jsDump', $file='-', $line='-')
81{
82    if (!empty($var)) {
83        ?>
84        <script type="text/javascript">
85        /* <![CDATA[ */
86        console.log('<?php printf('%s: %s (on line %s of %s)', $prefix, str_replace("'", "\\'", getDump($var, true)), $line, $file); ?>');
87        /* ]]> */
88        </script>
89        <?php
90    }
91}
92
93/*
94* Return a string version of any variable, optionally serialized on one line.
95*
96* @access   public
97* @param    mixed   $var        The variable to dump.
98* @param    bool    $serialize  If true, remove line-endings. Useful for logging variables.
99* @return   string              The dumped variable.
100* @author   Quinn Comendant <quinn@strangecode.com>
101*/
102function getDump($var, $serialize=false)
103{
104    ob_start();
105    print_r($var);
106    $d = ob_get_contents();
107    ob_end_clean();
108    return $serialize ? preg_replace('/\s+/m', ' ', $d) : $d;
109}
110
111/**
112 * Return dump as cleaned text. Useful for dumping data into emails.
113 *
114 * @param  array    $var        Variable to dump.
115 * @param  strong   $indent     A string to prepend indented lines (tab for example).
116 * @return string Dump of var.
117 */
118function fancyDump($var, $indent='')
119{
120    $output = '';
121    if (is_array($var)) {
122        foreach ($var as $k=>$v) {
123            $k = ucfirst(mb_strtolower(str_replace(array('_', '  '), ' ', $k)));
124            if (is_array($v)) {
125                $output .= sprintf("\n%s%s: %s\n", $indent, $k, fancyDump($v, $indent . $indent));
126            } else {
127                $output .= sprintf("%s%s: %s\n", $indent, $k, $v);
128            }
129        }
130    } else {
131        $output .= sprintf("%s%s\n", $indent, $var);
132    }
133    return $output;
134}
135
136/**
137 * Returns text with appropriate html translations (a smart wrapper for htmlspecialchars()).
138 *
139 * @param  string $text             Text to clean.
140 * @param  bool   $preserve_html    If set to true, oTxt will not translate <, >, ", or '
141 *                                  characters into HTML entities. This allows HTML to pass through undisturbed.
142 * @return string                   HTML-safe text.
143 */
144function oTxt($text, $preserve_html=false)
145{
146    $app =& App::getInstance();
147
148    $search = array();
149    $replace = array();
150
151    // Make converted ampersand entities into normal ampersands (they will be done manually later) to retain HTML entities.
152    $search['retain_ampersand']     = '/&amp;/';
153    $replace['retain_ampersand']    = '&';
154
155    if ($preserve_html) {
156        // Convert characters that must remain non-entities for displaying HTML.
157        $search['retain_left_angle']       = '/&lt;/';
158        $replace['retain_left_angle']      = '<';
159
160        $search['retain_right_angle']      = '/&gt;/';
161        $replace['retain_right_angle']     = '>';
162
163        $search['retain_single_quote']     = '/&#039;/';
164        $replace['retain_single_quote']    = "'";
165
166        $search['retain_double_quote']     = '/&quot;/';
167        $replace['retain_double_quote']    = '"';
168    }
169
170    // & becomes &amp;. Exclude any occurrence where the & is followed by a alphanum or unicode character.
171    $search['ampersand']        = '/&(?![\w\d#]{1,10};)/';
172    $replace['ampersand']       = '&amp;';
173
174    return preg_replace($search, $replace, htmlspecialchars($text, ENT_QUOTES, $app->getParam('character_set')));
175}
176
177/**
178 * Returns text with stylistic modifications. Warning: this will break some HTML attributes!
179 * TODO: Allow a string such as this to be passed: <a href="javascript:openPopup('/foo/bar.php')">Click here</a>
180 *
181 * @param  string   $text Text to clean.
182 * @return string         Cleaned text.
183 */
184function fancyTxt($text)
185{
186    $search = array();
187    $replace = array();
188
189    // "double quoted text"  becomes  &ldquo;double quoted text&rdquo;
190    $search['double_quotes']    = '/(^|[^\w=])(?:"|&quot;|&#34;|&#x22;|&ldquo;)([^"]+?)(?:"|&quot;|&#34;|&#x22;|&rdquo;)([^\w]|$)/ms'; // " is the same as &quot; and &#34; and &#x22;
191    $replace['double_quotes']   = '$1&ldquo;$2&rdquo;$3';
192
193    // text's apostrophes  become  text&rsquo;s apostrophes
194    $search['apostrophe']       = '/(\w)(?:\'|&#39;|&#039;)(\w)/ms';
195    $replace['apostrophe']      = '$1&rsquo;$2';
196
197    // 'single quoted text'  becomes  &lsquo;single quoted text&rsquo;
198    $search['single_quotes']    = '/(^|[^\w=])(?:\'|&#39;|&lsquo;)([^\']+?)(?:\'|&#39;|&rsquo;)([^\w]|$)/ms';
199    $replace['single_quotes']   = '$1&lsquo;$2&rsquo;$3';
200
201    // plural posessives' apostrophes become posessives&rsquo;
202    $search['apostrophes']      = '/(s)(?:\'|&#39;|&#039;)(\s)/ms';
203    $replace['apostrophes']     = '$1&rsquo;$2';
204
205    // em--dashes  become em&mdash;dashes
206    $search['em_dash']          = '/(\s*[^!<-])--([^>-]\s*)/';
207    $replace['em_dash']         = '$1&mdash;$2';
208
209    return preg_replace($search, $replace, $text);
210}
211
212/*
213* Finds all URLs in text and hyperlinks them.
214*
215* @access   public
216* @param    string  $text   Text to search for URLs.
217* @param    mixed   $length Number of characters to truncate URL, or NULL to disable truncating.
218* @param    string  $delim  Delimiter to append, indicate truncation.
219* @return   string          Same input text, but URLs hyperlinked.
220* @author   Quinn Comendant <quinn@strangecode.com>
221* @version  1.0
222* @since    22 Mar 2015 23:29:04
223*/
224function hyperlinkTxt($text, $length=null, $delim='
')
225{
226    return preg_replace_callback(
227        // Inspired by @stephenhay's regex from https://mathiasbynens.be/demo/url-regex
228        // Here we capture the full URL into the first match and only the first X characters into the second match.
229        sprintf('@\b(?<!")(?<!\')(?<!=)(((?:https?|s?ftps?)://[^\s/$.?#].[^\s]{0,%s})[^\s]*)@iS', $length),
230        // Use an anonymous function to decide when to append the delim.
231        // Also encode special chars with oTxt().
232        function ($m) use ($length, $delim) {
233            if (is_null($length) || $m[1] == $m[2]) {
234                // If not truncating, or URL was not truncated.
235                return sprintf('<a href="%s">%s</a>', oTxt($m[1]), oTxt($m[1]));
236            } else {
237                // Truncated URL.
238                return sprintf('<a href="%s">%s%s</a>', oTxt($m[1]), oTxt(trim($m[2])), $delim);
239            }
240        },
241        $text
242    );
243}
244
245/**
246 * Applies a class to search terms to highlight them ala google results.
247 *
248 * @param  string   $text   Input text to search.
249 * @param  string   $search String of word(s) that will be highlighted.
250 * @param  string   $class  CSS class to apply.
251 * @return string           Text with searched words wrapped in <span>.
252 */
253function highlightWords($text, $search, $class='sc-highlightwords')
254{
255    $words = preg_split('/[^\w]/', $search, -1, PREG_SPLIT_NO_EMPTY);
256
257    $search = array();
258    $replace = array();
259
260    foreach ($words as $w) {
261        if ('' != trim($w)) {
262            $search[] = '/\b(' . preg_quote($w) . ')\b/i';
263            $replace[] = '<span class="' . $class . '">$1</span>';
264        }
265    }
266
267    return empty($replace) ? $text : preg_replace($search, $replace, $text);
268}
269
270/**
271 * Generates a hexadecimal html color based on provided word.
272 *
273 * @access public
274 * @param  string $text  A string for which to convert to color.
275 * @return string  A hexadecimal html color.
276 */
277function getTextColor($text, $method=1, $n=0.87)
278{
279    $hash = md5($text);
280    $rgb = array(
281        mb_substr($hash, 0, 1),
282        mb_substr($hash, 1, 1),
283        mb_substr($hash, 2, 1),
284        mb_substr($hash, 3, 1),
285        mb_substr($hash, 4, 1),
286        mb_substr($hash, 5, 1),
287    );
288
289    switch ($method) {
290    case 1 :
291    default :
292        // Reduce all hex values slightly to avoid all white.
293        array_walk($rgb, create_function('&$v', "\$v = dechex(round(hexdec(\$v) * $n));"));
294        break;
295    case 2 :
296        foreach ($rgb as $i => $v) {
297            if (hexdec($v) > hexdec('c')) {
298                $rgb[$i] = dechex(hexdec('f') - hexdec($v));
299            }
300        }
301        break;
302    }
303
304    return join('', $rgb);
305}
306
307/**
308 * Encodes a string into unicode values 128-255.
309 * Useful for hiding an email address from spambots.
310 *
311 * @access  public
312 * @param   string   $text   A line of text to encode.
313 * @return  string   Encoded text.
314 */
315function encodeAscii($text)
316{
317    $output = '';
318    $num = mb_strlen($text);
319    for ($i=0; $i<$num; $i++) {
320        $output .= sprintf('&#%03s', ord($text{$i}));
321    }
322    return $output;
323}
324
325/**
326 * Encodes an email into a "user at domain dot com" format.
327 *
328 * @access  public
329 * @param   string   $email   An email to encode.
330 * @param   string   $at      Replaces the @.
331 * @param   string   $dot     Replaces the ..
332 * @return  string   Encoded email.
333 */
334function encodeEmail($email, $at=' at ', $dot=' dot ')
335{
336    $search = array('/@/', '/\./');
337    $replace = array($at, $dot);
338    return preg_replace($search, $replace, $email);
339}
340
341/**
342 * Truncates "a really long string" into a string of specified length
343 * at the beginning: "
long string"
344 * at the middle: "a rea
string"
345 * or at the end: "a really
".
346 *
347 * The regular expressions below first match and replace the string to the specified length and position,
348 * and secondly they remove any whitespace from around the delimiter (to avoid "this 
 " from happening).
349 *
350 * @access  public
351 * @param   string  $str    Input string
352 * @param   int     $len    Maximum string length.
353 * @param   string  $where  Where to cut the string. One of: 'start', 'middle', or 'end'.
354 * @return  string          Truncated output string.
355 * @author  Quinn Comendant <quinn@strangecode.com>
356 * @since   29 Mar 2006 13:48:49
357 */
358function truncate($str, $len=50, $where='end', $delim='
')
359{
360    $dlen = mb_strlen($delim);
361    if ($len <= $dlen || mb_strlen($str) <= $dlen) {
362        return substr($str, 0, $len);
363    }
364    $part1 = floor(($len - $dlen) / 2);
365    $part2 = ceil(($len - $dlen) / 2);
366
367    if ($len > ini_get('pcre.backtrack_limit')) {
368        $app =& App::getInstance();
369        $app->logMsg(sprintf('Asked to truncate string len of %s > pcre.backtrack_limit of %s', $len, ini_get('pcre.backtrack_limit')), LOG_DEBUG, __FILE__, __LINE__);
370        ini_set('pcre.backtrack_limit', $len);
371    }
372
373    switch ($where) {
374    case 'start' :
375        return preg_replace(array(sprintf('/^.{%s,}(.{%s})$/su', $dlen + 1, $part1 + $part2), sprintf('/\s*%s{%s,}\s*/su', preg_quote($delim), $dlen)), array($delim . '$1', $delim), $str);
376
377    case 'middle' :
378        return preg_replace(array(sprintf('/^(.{%s}).{%s,}(.{%s})$/su', $part1, $dlen + 1, $part2), sprintf('/\s*%s{%s,}\s*/su', preg_quote($delim), $dlen)), array('$1' . $delim . '$2', $delim), $str);
379
380    case 'end' :
381    default :
382        return preg_replace(array(sprintf('/^(.{%s}).{%s,}$/su', $part1 + $part2, $dlen + 1), sprintf('/\s*%s{%s,}\s*/su', preg_quote($delim), $dlen)), array('$1' . $delim, $delim), $str);
383    }
384}
385
386/*
387* A substitution for the missing mb_ucfirst function.
388*
389* @access   public
390* @param    string  $string The string
391* @return   string          String with upper-cased first character.
392* @author   Quinn Comendant <quinn@strangecode.com>
393* @version  1.0
394* @since    06 Dec 2008 17:04:01
395*/
396if (!function_exists('mb_ucfirst')) {
397    function mb_ucfirst($string)
398    {
399        return mb_strtoupper(mb_substr($string, 0, 1)) . mb_substr($string, 1, mb_strlen($string));
400    }
401}
402
403/*
404* A substitution for the missing mb_strtr function.
405*
406* @access   public
407* @param    string  $string The string
408* @param    string  $from   String of characters to translate from
409* @param    string  $to     String of characters to translate to
410* @return   string          String with translated characters.
411* @author   Quinn Comendant <quinn@strangecode.com>
412* @version  1.0
413* @since    20 Jan 2013 12:33:26
414*/
415if (!function_exists('mb_strtr')) {
416    function mb_strtr($string, $from, $to)
417    {
418        return str_replace(mb_split('.', $from), mb_split('.', $to), $string);
419    }
420}
421
422/*
423* A substitution for the missing mb_str_pad function.
424*
425* @access   public
426* @param    string  $input      The string that receives padding.
427* @param    string  $pad_length Total length of resultant string.
428* @param    string  $pad_string The string to use for padding
429* @param    string  $pad_type   Flags STR_PAD_RIGHT or STR_PAD_LEFT or STR_PAD_BOTH
430* @return   string          String with translated characters.
431* @author   Quinn Comendant <quinn@strangecode.com>
432* @version  1.0
433* @since    20 Jan 2013 12:33:26
434*/
435if (!function_exists('mb_str_pad')) {
436    function mb_str_pad($input, $pad_length, $pad_string=' ', $pad_type=STR_PAD_RIGHT) {
437        $diff = strlen($input) - mb_strlen($input);
438        return str_pad($input, $pad_length + $diff, $pad_string, $pad_type);
439    }
440}
441
442/*
443* Converts a string into a URL-safe slug, removing spaces and non word characters.
444*
445* @access   public
446* @param    string  $str    String to convert.
447* @return   string          URL-safe slug.
448* @author   Quinn Comendant <quinn@strangecode.com>
449* @version  1.0
450* @since    18 Aug 2014 12:54:29
451*/
452function URLSlug($str)
453{
454    $slug = preg_replace(array('/[^\w]+/', '/^-+|-+$/'), array('-', ''), $str);
455    $slug = strtolower($slug);
456    return $slug;
457}
458
459/**
460 * Return a human readable disk space measurement. Input value measured in bytes.
461 *
462 * @param       int    $size        Size in bytes.
463 * @param       int    $unit        The maximum unit
464 * @param       int    $format      The return string format
465 * @author      Aidan Lister <aidan@php.net>
466 * @author      Quinn Comendant <quinn@strangecode.com>
467 * @version     1.2.0
468 */
469function humanFileSize($size, $format='%01.2f %s', $max_unit=null, $multiplier=1024)
470{
471    // Units
472    $units = array('B', 'KB', 'MB', 'GB', 'TB');
473    $ii = count($units) - 1;
474
475    // Max unit
476    $max_unit = array_search((string) $max_unit, $units);
477    if ($max_unit === null || $max_unit === false) {
478        $max_unit = $ii;
479    }
480
481    // Loop
482    $i = 0;
483    while ($max_unit != $i && $size >= $multiplier && $i < $ii) {
484        $size /= $multiplier;
485        $i++;
486    }
487
488    return sprintf($format, $size, $units[$i]);
489}
490
491/*
492* Returns a human readable amount of time for the given amount of seconds.
493*
494* 45 seconds
495* 12 minutes
496* 3.5 hours
497* 2 days
498* 1 week
499* 4 months
500*
501* Months are calculated using the real number of days in a year: 365.2422 / 12.
502*
503* @access   public
504* @param    int $seconds Seconds of time.
505* @param    string $max_unit Key value from the $units array.
506* @param    string $format Sprintf formatting string.
507* @return   string Value of units elapsed.
508* @author   Quinn Comendant <quinn@strangecode.com>
509* @version  1.0
510* @since    23 Jun 2006 12:15:19
511*/
512function humanTime($seconds, $max_unit=null, $format='%01.1f')
513{
514    // Units: array of seconds in the unit, singular and plural unit names.
515    $units = array(
516        'second' => array(1, _("second"), _("seconds")),
517        'minute' => array(60, _("minute"), _("minutes")),
518        'hour' => array(3600, _("hour"), _("hours")),
519        'day' => array(86400, _("day"), _("days")),
520        'week' => array(604800, _("week"), _("weeks")),
521        'month' => array(2629743.84, _("month"), _("months")),
522        'year' => array(31556926.08, _("year"), _("years")),
523        'decade' => array(315569260.8, _("decade"), _("decades")),
524        'century' => array(3155692608, _("century"), _("centuries")),
525    );
526
527    // Max unit to calculate.
528    $max_unit = isset($units[$max_unit]) ? $max_unit : 'year';
529
530    $final_time = $seconds;
531    $final_unit = 'second';
532    foreach ($units as $k => $v) {
533        if ($seconds >= $v[0]) {
534            $final_time = $seconds / $v[0];
535            $final_unit = $k;
536        }
537        if ($max_unit == $final_unit) {
538            break;
539        }
540    }
541    $final_time = sprintf($format, $final_time);
542    return sprintf('%s %s', $final_time, (1 == $final_time ? $units[$final_unit][1] : $units[$final_unit][2]));
543}
544
545/**
546 * Removes non-latin characters from file name, using htmlentities to convert known weirdos into regular squares.
547 *
548 * @access  public
549 * @param   string  $file_name  A name of a file.
550 * @return  string              The same name, but cleaned.
551 */
552function cleanFileName($file_name)
553{
554    $app =& App::getInstance();
555
556    $file_name = preg_replace(array(
557        '/&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml|caron);/ui',
558        '/&(?:amp);/ui',
559        '/[&;]+/u',
560        '/[^a-zA-Z0-9()@._=+-]+/u',
561        '/^_+|_+$/u'
562    ), array(
563        '$1',
564        'and',
565        '',
566        '_',
567        ''
568    ), htmlentities($file_name, ENT_NOQUOTES | ENT_IGNORE, $app->getParam('character_set')));
569    return mb_substr($file_name, 0, 250);
570}
571
572/**
573 * Returns the extension of a file name, or an empty string if none exists.
574 *
575 * @access  public
576 * @param   string  $file_name  A name of a file, with extension after a dot.
577 * @return  string              The value found after the dot
578 */
579function getFilenameExtension($file_name)
580{
581    preg_match('/.*?\.(\w+)$/i', trim($file_name), $ext);
582    return isset($ext[1]) ? $ext[1] : '';
583}
584
585/*
586* Convert a php.ini value (8M, 512K, etc), into integer value of bytes.
587*
588* @access   public
589* @param    string  $val    Value from php config, e.g., upload_max_filesize.
590* @return   int             Value converted to bytes as an integer.
591* @author   Quinn Comendant <quinn@strangecode.com>
592* @version  1.0
593* @since    20 Aug 2014 14:32:41
594*/
595function phpIniGetBytes($val)
596{
597    $val = trim(ini_get($val));
598    if ($val != '') {
599        $last = strtolower($val{strlen($val) - 1});
600    } else {
601        $last = '';
602    }
603    switch ($last) {
604        // The 'G' modifier is available since PHP 5.1.0
605        case 'g':
606            $val *= 1024;
607        case 'm':
608            $val *= 1024;
609        case 'k':
610            $val *= 1024;
611    }
612
613    return (int)$val;
614}
615
616/**
617 * Tests the existence of a file anywhere in the include path.
618 * Replaced by stream_resolve_include_path() in PHP 5 >= 5.3.2
619 *
620 * @param   string  $file   File in include path.
621 * @return  mixed           False if file not found, the path of the file if it is found.
622 * @author  Quinn Comendant <quinn@strangecode.com>
623 * @since   03 Dec 2005 14:23:26
624 */
625function fileExistsIncludePath($file)
626{
627    $app =& App::getInstance();
628
629    foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) {
630        $fullpath = $path . DIRECTORY_SEPARATOR . $file;
631        if (file_exists($fullpath)) {
632            $app->logMsg(sprintf('Found file "%s" at path: %s', $file, $fullpath), LOG_DEBUG, __FILE__, __LINE__);
633            return $fullpath;
634        } else {
635            $app->logMsg(sprintf('File "%s" not found in include_path: %s', $file, get_include_path()), LOG_DEBUG, __FILE__, __LINE__);
636            return false;
637        }
638    }
639}
640
641/**
642 * Returns stats of a file from the include path.
643 *
644 * @param   string  $file   File in include path.
645 * @param   mixed   $stat   Which statistic to return (or null to return all).
646 * @return  mixed           Value of requested key from fstat(), or false on error.
647 * @author  Quinn Comendant <quinn@strangecode.com>
648 * @since   03 Dec 2005 14:23:26
649 */
650function statIncludePath($file, $stat=null)
651{
652    // Open file pointer read-only using include path.
653    if ($fp = fopen($file, 'r', true)) {
654        // File opened successfully, get stats.
655        $stats = fstat($fp);
656        fclose($fp);
657        // Return specified stats.
658        return is_null($stat) ? $stats : $stats[$stat];
659    } else {
660        return false;
661    }
662}
663
664/*
665* Writes content to the specified file. This function emulates the functionality of file_put_contents from PHP 5.
666* It makes an exclusive lock on the file while writing.
667*
668* @access   public
669* @param    string  $filename   Path to file.
670* @param    string  $content    Data to write into file.
671* @return   bool                Success or failure.
672* @author   Quinn Comendant <quinn@strangecode.com>
673* @since    11 Apr 2006 22:48:30
674*/
675function filePutContents($filename, $content)
676{
677    $app =& App::getInstance();
678
679    // Open file for writing and truncate to zero length.
680    if ($fp = fopen($filename, 'w')) {
681        if (flock($fp, LOCK_EX)) {
682            if (!fwrite($fp, $content, mb_strlen($content))) {
683                $app->logMsg(sprintf('Failed writing to file: %s', $filename), LOG_ERR, __FILE__, __LINE__);
684                fclose($fp);
685                return false;
686            }
687            flock($fp, LOCK_UN);
688        } else {
689            $app->logMsg(sprintf('Could not lock file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__);
690            fclose($fp);
691            return false;
692        }
693        fclose($fp);
694        // Success!
695        $app->logMsg(sprintf('Wrote to file: %s', $filename), LOG_DEBUG, __FILE__, __LINE__);
696        return true;
697    } else {
698        $app->logMsg(sprintf('Could not open file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__);
699        return false;
700    }
701}
702
703/**
704 * If $var is net set or null, set it to $default. Otherwise leave it alone.
705 * Returns the final value of $var. Use to find a default value of one is not available.
706 *
707 * @param  mixed $var       The variable that is being set.
708 * @param  mixed $default   What to set it to if $val is not currently set.
709 * @return mixed            The resulting value of $var.
710 */
711function setDefault(&$var, $default='')
712{
713    if (!isset($var)) {
714        $var = $default;
715    }
716    return $var;
717}
718
719/**
720 * Like preg_quote() except for arrays, it takes an array of strings and puts
721 * a backslash in front of every character that is part of the regular
722 * expression syntax.
723 *
724 * @param  array $array    input array
725 * @param  array $delim    optional character that will also be escaped.
726 * @return array    an array with the same values as $array1 but shuffled
727 */
728function pregQuoteArray($array, $delim='/')
729{
730    if (!empty($array)) {
731        if (is_array($array)) {
732            foreach ($array as $key=>$val) {
733                $quoted_array[$key] = preg_quote($val, $delim);
734            }
735            return $quoted_array;
736        } else {
737            return preg_quote($array, $delim);
738        }
739    }
740}
741
742/**
743 * Converts a PHP Array into encoded URL arguments and return them as an array.
744 *
745 * @param  mixed $data        An array to transverse recursively, or a string
746 *                            to use directly to create url arguments.
747 * @param  string $prefix     The name of the first dimension of the array.
748 *                            If not specified, the first keys of the array will be used.
749 * @return array              URL with array elements as URL key=value arguments.
750 */
751function urlEncodeArray($data, $prefix='', $_return=true)
752{
753    // Data is stored in static variable.
754    static $args;
755
756    if (is_array($data)) {
757        foreach ($data as $key => $val) {
758            // If the prefix is empty, use the $key as the name of the first dimension of the "array".
759            // ...otherwise, append the key as a new dimension of the "array".
760            $new_prefix = ('' == $prefix) ? urlencode($key) : $prefix . '[' . urlencode($key) . ']';
761            // Enter recursion.
762            urlEncodeArray($val, $new_prefix, false);
763        }
764    } else {
765        // We've come to the last dimension of the array, save the "array" and its value.
766        $args[$prefix] = urlencode($data);
767    }
768
769    if ($_return) {
770        // This is not a recursive execution. All recursion is complete.
771        // Reset static var and return the result.
772        $ret = $args;
773        $args = array();
774        return is_array($ret) ? $ret : array();
775    }
776}
777
778/**
779 * Converts a PHP Array into encoded URL arguments and return them in a string.
780 *
781 * @param  mixed $data        An array to transverse recursively, or a string
782 *                            to use directly to create url arguments.
783 * @param  string $prefix     The name of the first dimension of the array.
784 *                            If not specified, the first keys of the array will be used.
785 * @return string url         A string ready to append to a url.
786 */
787function urlEncodeArrayToString($data, $prefix='')
788{
789
790    $array_args = urlEncodeArray($data, $prefix);
791    $url_args = '';
792    $delim = '';
793    foreach ($array_args as $key=>$val) {
794        $url_args .= $delim . $key . '=' . $val;
795        $delim = ini_get('arg_separator.output');
796    }
797    return $url_args;
798}
799
800/**
801 * Fills an array with the result from a multiple ereg search.
802 * Courtesy of Bruno - rbronosky@mac.com - 10-May-2001
803 *
804 * @param  mixed $pattern   regular expression needle
805 * @param  mixed $string   haystack
806 * @return array    populated with each found result
807 */
808function eregAll($pattern, $string)
809{
810    do {
811        if (!mb_ereg($pattern, $string, $temp)) {
812             continue;
813        }
814        $string = str_replace($temp[0], '', $string);
815        $results[] = $temp;
816    } while (mb_ereg($pattern, $string, $temp));
817    return $results;
818}
819
820/**
821 * Prints the word "checked" if a variable is set, and optionally matches
822 * the desired value, otherwise prints nothing,
823 * used for printing the word "checked" in a checkbox form input.
824 *
825 * @param  mixed $var     the variable to compare
826 * @param  mixed $value   optional, what to compare with if a specific value is required.
827 */
828function frmChecked($var, $value=null)
829{
830    if (func_num_args() == 1 && $var) {
831        // 'Checked' if var is true.
832        echo ' checked="checked" ';
833    } else if (func_num_args() == 2 && $var == $value) {
834        // 'Checked' if var and value match.
835        echo ' checked="checked" ';
836    } else if (func_num_args() == 2 && is_array($var)) {
837        // 'Checked' if the value is in the key or the value of an array.
838        if (isset($var[$value])) {
839            echo ' checked="checked" ';
840        } else if (in_array($value, $var)) {
841            echo ' checked="checked" ';
842        }
843    }
844}
845
846/**
847 * prints the word "selected" if a variable is set, and optionally matches
848 * the desired value, otherwise prints nothing,
849 * otherwise prints nothing, used for printing the word "checked" in a
850 * select form input
851 *
852 * @param  mixed $var     the variable to compare
853 * @param  mixed $value   optional, what to compare with if a specific value is required.
854 */
855function frmSelected($var, $value=null)
856{
857    if (func_num_args() == 1 && $var) {
858        // 'selected' if var is true.
859        echo ' selected="selected" ';
860    } else if (func_num_args() == 2 && $var == $value) {
861        // 'selected' if var and value match.
862        echo ' selected="selected" ';
863    } else if (func_num_args() == 2 && is_array($var)) {
864        // 'selected' if the value is in the key or the value of an array.
865        if (isset($var[$value])) {
866            echo ' selected="selected" ';
867        } else if (in_array($value, $var)) {
868            echo ' selected="selected" ';
869        }
870    }
871}
872
873/**
874 * Adds slashes to values of an array and converts the array to a comma
875 * delimited list. If value provided is a string return the string
876 * escaped.  This is useful for putting values coming in from posted
877 * checkboxes into a SET column of a database.
878 *
879 *
880 * @param  array $in      Array to convert.
881 * @return string         Comma list of array values.
882 */
883function escapedList($in, $separator="', '")
884{
885    $db =& DB::getInstance();
886
887    if (is_array($in) && !empty($in)) {
888        return join($separator, array_map(array($db, 'escapeString'), $in));
889    } else {
890        return $db->escapeString($in);
891    }
892}
893
894/**
895 * Converts a human string date into a SQL-safe date.  Dates nearing
896 * infinity use the date 2038-01-01 so conversion to unix time format
897 * remain within valid range.
898 *
899 * @param  array $date     String date to convert.
900 * @param  array $format   Date format to pass to date().
901 *                         Default produces MySQL datetime: 0000-00-00 00:00:00.
902 * @return string          SQL-safe date.
903 */
904function strToSQLDate($date, $format='Y-m-d H:i:s')
905{
906    // Translate the human string date into SQL-safe date format.
907    if (empty($date) || mb_strpos($date, '0000-00-00') !== false || strtotime($date) === -1 || strtotime($date) === false || strtotime($date) === null) {
908        // Return a string of zero time, formatted the same as $format.
909        return strtr($format, array(
910            'Y' => '0000',
911            'm' => '00',
912            'd' => '00',
913            'H' => '00',
914            'i' => '00',
915            's' => '00',
916        ));
917    } else {
918        return date($format, strtotime($date));
919    }
920}
921
922/**
923 * If magic_quotes_gpc is in use, run stripslashes() on $var. If $var is an
924 * array, stripslashes is run on each value, recursively, and the stripped
925 * array is returned.
926 *
927 * @param  mixed $var   The string or array to un-quote, if necessary.
928 * @return mixed        $var, minus any magic quotes.
929 */
930function dispelMagicQuotes($var, $always=false)
931{
932    static $magic_quotes_gpc;
933
934    if (!isset($magic_quotes_gpc)) {
935        $magic_quotes_gpc = get_magic_quotes_gpc();
936    }
937
938    if ($always || $magic_quotes_gpc) {
939        if (!is_array($var)) {
940            $var = stripslashes($var);
941        } else {
942            foreach ($var as $key=>$val) {
943                if (is_array($val)) {
944                    $var[$key] = dispelMagicQuotes($val, $always);
945                } else {
946                    $var[$key] = stripslashes($val);
947                }
948            }
949        }
950    }
951    return $var;
952}
953
954/**
955 * Get a form variable from GET or POST data, stripped of magic
956 * quotes if necessary.
957 *
958 * @param string $var (optional) The name of the form variable to look for.
959 * @param string $default (optional) The value to return if the
960 *                                   variable is not there.
961 * @return mixed      A cleaned GET or POST if no $var specified.
962 * @return string     A cleaned form $var if found, or $default.
963 */
964function getFormData($var=null, $default=null)
965{
966    $app =& App::getInstance();
967
968    if ('POST' == getenv('REQUEST_METHOD') && is_null($var)) {
969        return dispelMagicQuotes($_POST, $app->getParam('always_dispel_magicquotes'));
970    } else if ('GET' == getenv('REQUEST_METHOD') && is_null($var)) {
971        return dispelMagicQuotes($_GET, $app->getParam('always_dispel_magicquotes'));
972    }
973    if (isset($_POST[$var])) {
974        return dispelMagicQuotes($_POST[$var], $app->getParam('always_dispel_magicquotes'));
975    } else if (isset($_GET[$var])) {
976        return dispelMagicQuotes($_GET[$var], $app->getParam('always_dispel_magicquotes'));
977    } else {
978        return $default;
979    }
980}
981
982function getPost($var=null, $default=null)
983{
984    $app =& App::getInstance();
985
986    if (is_null($var)) {
987        return dispelMagicQuotes($_POST, $app->getParam('always_dispel_magicquotes'));
988    }
989    if (isset($_POST[$var])) {
990        return dispelMagicQuotes($_POST[$var], $app->getParam('always_dispel_magicquotes'));
991    } else {
992        return $default;
993    }
994}
995
996function getGet($var=null, $default=null)
997{
998    $app =& App::getInstance();
999    if (is_null($var)) {
1000        return dispelMagicQuotes($_GET, $app->getParam('always_dispel_magicquotes'));
1001    }
1002    if (isset($_GET[$var])) {
1003        return dispelMagicQuotes($_GET[$var], $app->getParam('always_dispel_magicquotes'));
1004    } else {
1005        return $default;
1006    }
1007}
1008
1009/*
1010* Sets a $_GET or $_POST variable.
1011*
1012* @access   public
1013* @param    string  $key    The key of the request array to set.
1014* @param    mixed   $val    The value to save in the request array.
1015* @return   void
1016* @author   Quinn Comendant <quinn@strangecode.com>
1017* @version  1.0
1018* @since    01 Nov 2009 12:25:29
1019*/
1020function putFormData($key, $val)
1021{
1022    if ('POST' == getenv('REQUEST_METHOD')) {
1023        $_POST[$key] = $val;
1024    } else if ('GET' == getenv('REQUEST_METHOD')) {
1025        $_GET[$key] = $val;
1026    }
1027}
1028
1029/**
1030 * Signs a value using md5 and a simple text key. In order for this
1031 * function to be useful (i.e. secure) the salt must be kept secret, which
1032 * means keeping it as safe as database credentials. Putting it into an
1033 * environment variable set in httpd.conf is a good place.
1034 *
1035 * @access  public
1036 * @param   string  $val    The string to sign.
1037 * @param   string  $salt   (Optional) A text key to use for computing the signature.
1038 * @param   string  $length (Optional) The length of the added signature. Longer signatures are safer. Must match the length passed to verifySignature() for the signatures to match.
1039 * @return  string  The original value with a signature appended.
1040 */
1041function addSignature($val, $salt=null, $length=18)
1042{
1043    $app =& App::getInstance();
1044
1045    if ('' == trim($val)) {
1046        $app->logMsg(sprintf('Cannot add signature to an empty string.', null), LOG_INFO, __FILE__, __LINE__);
1047        return '';
1048    }
1049
1050    if (!isset($salt)) {
1051        $salt = $app->getParam('signing_key');
1052    }
1053
1054    switch ($app->getParam('signing_method')) {
1055    case 'sha512+base64':
1056        return $val . '-' . mb_substr(preg_replace('/[^\w]/', '', base64_encode(hash('sha512', $val . $salt, true))), 0, $length);
1057
1058    case 'md5':
1059    default:
1060        return $val . '-' . mb_strtolower(mb_substr(md5($salt . md5($val . $salt)), 0, $length));
1061    }
1062}
1063
1064/**
1065 * Strips off the signature appended by addSignature().
1066 *
1067 * @access  public
1068 * @param   string  $signed_val     The string to sign.
1069 * @return  string  The original value with a signature removed.
1070 */
1071function removeSignature($signed_val)
1072{
1073    if (empty($signed_val) || mb_strpos($signed_val, '-') === false) {
1074        return '';
1075    }
1076    return mb_substr($signed_val, 0, mb_strrpos($signed_val, '-'));
1077}
1078
1079/**
1080 * Verifies a signature appended to a value by addSignature().
1081 *
1082 * @access  public
1083 * @param   string  $signed_val A value with appended signature.
1084 * @param   string  $salt       (Optional) A text key to use for computing the signature.
1085 * @param   string  $length (Optional) The length of the added signature.
1086 * @return  bool    True if the signature matches the var.
1087 */
1088function verifySignature($signed_val, $salt=null, $length=18)
1089{
1090    // Strip the value from the signed value.
1091    $val = removeSignature($signed_val);
1092    // If the signed value matches the original signed value we consider the value safe.
1093    if ('' != $signed_val && $signed_val == addSignature($val, $salt, $length)) {
1094        // Signature verified.
1095        return true;
1096    } else {
1097        $app =& App::getInstance();
1098        $app->logMsg(sprintf('Failed signature (%s should be %s)', $signed_val, addSignature($val, $salt, $length)), LOG_DEBUG, __FILE__, __LINE__);
1099        return false;
1100    }
1101}
1102
1103/**
1104 * Sends empty output to the browser and flushes the php buffer so the client
1105 * will see data before the page is finished processing.
1106 */
1107function flushBuffer()
1108{
1109    echo str_repeat('          ', 205);
1110    flush();
1111}
1112
1113/**
1114 * Adds email address to mailman mailing list. Requires /etc/sudoers entry for apache to sudo execute add_members.
1115 * Don't forget to allow php_admin_value open_basedir access to "/var/mailman/bin".
1116 *
1117 * @access  public
1118 * @param   string  $email     Email address to add.
1119 * @param   string  $list      Name of list to add to.
1120 * @param   bool    $send_welcome_message   True to send welcome message to subscriber.
1121 * @return  bool    True on success, false on failure.
1122 */
1123function mailmanAddMember($email, $list, $send_welcome_message=false)
1124{
1125    $app =& App::getInstance();
1126
1127    $add_members = '/usr/lib/mailman/bin/add_members';
1128    // FIXME: checking of executable is disabled.
1129    if (true || is_executable($add_members) && is_readable($add_members)) {
1130        $welcome_msg = $send_welcome_message ? 'y' : 'n';
1131        exec(sprintf("/bin/echo '%s' | /usr/bin/sudo %s -r - --welcome-msg=%s --admin-notify=n '%s'", escapeshellarg($email), escapeshellarg($add_members), $welcome_msg, escapeshellarg($list)), $stdout, $return_code);
1132        if (0 == $return_code) {
1133            $app->logMsg(sprintf('Mailman add member success for list: %s, user: %s', $list, $email), LOG_INFO, __FILE__, __LINE__);
1134            return true;
1135        } else {
1136            $app->logMsg(sprintf('Mailman add member failed for list: %s, user: %s, with message: %s', $list, $email, getDump($stdout)), LOG_WARNING, __FILE__, __LINE__);
1137            return false;
1138        }
1139    } else {
1140        $app->logMsg(sprintf('Mailman add member program not executable: %s', $add_members), LOG_ALERT, __FILE__, __LINE__);
1141        return false;
1142    }
1143}
1144
1145/**
1146 * Removes email address from mailman mailing list. Requires /etc/sudoers entry for apache to sudo execute add_members.
1147 * Don't forget to allow php_admin_value open_basedir access to "/var/mailman/bin".
1148 *
1149 * @access  public
1150 * @param   string  $email     Email address to add.
1151 * @param   string  $list      Name of list to add to.
1152 * @param   bool    $send_user_ack   True to send goodbye message to subscriber.
1153 * @return  bool    True on success, false on failure.
1154 */
1155function mailmanRemoveMember($email, $list, $send_user_ack=false)
1156{
1157    $app =& App::getInstance();
1158
1159    $remove_members = '/usr/lib/mailman/bin/remove_members';
1160    // FIXME: checking of executable is disabled.
1161    if (true || is_executable($remove_members) && is_readable($remove_members)) {
1162        $userack = $send_user_ack ? '' : '--nouserack';
1163        exec(sprintf("/usr/bin/sudo %s %s --noadminack '%s' '%s'", escapeshellarg($remove_members), $userack, escapeshellarg($list), escapeshellarg($email)), $stdout, $return_code);
1164        if (0 == $return_code) {
1165            $app->logMsg(sprintf('Mailman remove member success for list: %s, user: %s', $list, $email), LOG_INFO, __FILE__, __LINE__);
1166            return true;
1167        } else {
1168            $app->logMsg(sprintf('Mailman remove member failed for list: %s, user: %s, with message: %s', $list, $email, $stdout), LOG_WARNING, __FILE__, __LINE__);
1169            return false;
1170        }
1171    } else {
1172        $app->logMsg(sprintf('Mailman remove member program not executable: %s', $remove_members), LOG_ALERT, __FILE__, __LINE__);
1173        return false;
1174    }
1175}
1176
1177/*
1178* Returns the remote IP address, taking into consideration proxy servers.
1179*
1180* If strict checking is enabled, we will only trust REMOTE_ADDR or an HTTP header
1181* value if REMOTE_ADDR is a trusted proxy (configured as an array in $cfg['trusted_proxies']).
1182*
1183* @access   public
1184* @param    bool $dolookup            Resolve to IP to a hostname?
1185* @param    bool $trust_all_proxies   Should we trust any IP address set in HTTP_* variables? Set to FALSE for secure usage.
1186* @return   mixed Canonicalized IP address (or a corresponding hostname if $dolookup is true), or false if no IP was found.
1187* @author   Alix Axel <http://stackoverflow.com/a/2031935/277303>
1188* @author   Corey Ballou <http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/>
1189* @author   Quinn Comendant <quinn@strangecode.com>
1190* @version  1.0
1191* @since    12 Sep 2014 19:07:46
1192*/
1193function getRemoteAddr($dolookup=false, $trust_all_proxies=true)
1194{
1195    global $cfg;
1196
1197    if (!isset($_SERVER['REMOTE_ADDR'])) {
1198        // In some cases this won't be set, e.g., CLI scripts.
1199        return null;
1200    }
1201
1202    // Use an HTTP header value only if $trust_all_proxies is true or when REMOTE_ADDR is in our $cfg['trusted_proxies'] array.
1203    // $cfg['trusted_proxies'] is an array of proxy server addresses we expect to see in REMOTE_ADDR.
1204    if ($trust_all_proxies || isset($cfg['trusted_proxies']) && is_array($cfg['trusted_proxies']) && in_array($_SERVER['REMOTE_ADDR'], $cfg['trusted_proxies'], true)) {
1205        // Then it's probably safe to use an IP address value set in an HTTP header.
1206        // Loop through possible IP address headers.
1207        foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED') as $key) {
1208            // Loop through and if
1209            if (array_key_exists($key, $_SERVER)) {
1210                foreach (explode(',', $_SERVER[$key]) as $addr) {
1211                    $addr = canonicalIPAddr(trim($addr));
1212                    if (false !== filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
1213                        return $dolookup && '' != $addr ? gethostbyaddr($addr) : $addr;
1214                    }
1215                }
1216            }
1217        }
1218    }
1219
1220    $addr = canonicalIPAddr(trim($_SERVER['REMOTE_ADDR']));
1221    return $dolookup && $addr ? gethostbyaddr($addr) : $addr;
1222}
1223
1224/*
1225* Converts an ipv4 IP address in hexadecimal form into canonical form (i.e., it removes the prefix).
1226*
1227* @access   public
1228* @param    string  $addr   IP address.
1229* @return   string          Canonical IP address.
1230* @author   Sander Steffann <http://stackoverflow.com/a/12436099/277303>
1231* @author   Quinn Comendant <quinn@strangecode.com>
1232* @version  1.0
1233* @since    15 Sep 2012
1234*/
1235function canonicalIPAddr($addr)
1236{
1237    // Known prefix
1238    $v4mapped_prefix_bin = pack('H*', '00000000000000000000ffff');
1239
1240    // Parse
1241    $addr_bin = inet_pton($addr);
1242
1243    // Check prefix
1244    if (substr($addr_bin, 0, strlen($v4mapped_prefix_bin)) == $v4mapped_prefix_bin) {
1245        // Strip prefix
1246        $addr_bin = substr($addr_bin, strlen($v4mapped_prefix_bin));
1247    }
1248
1249    // Convert back to printable address in canonical form
1250    return inet_ntop($addr_bin);
1251}
1252
1253/**
1254 * Tests whether a given IP address can be found in an array of IP address networks.
1255 * Elements of networks array can be single IP addresses or an IP address range in CIDR notation
1256 * See: http://en.wikipedia.org/wiki/Classless_inter-domain_routing
1257 *
1258 * @access  public
1259 * @param   string  IP address to search for.
1260 * @param   array   Array of networks to search within.
1261 * @return  mixed   Returns the network that matched on success, false on failure.
1262 */
1263function ipInRange($addr, $networks)
1264{
1265    if (!is_array($networks)) {
1266        $networks = array($networks);
1267    }
1268
1269    $addr_binary = sprintf('%032b', ip2long($addr));
1270    foreach ($networks as $network) {
1271        if (preg_match('![\d\.]{7,15}/\d{1,2}!', $network)) {
1272            // IP is in CIDR notation.
1273            list($cidr_ip, $cidr_bitmask) = explode('/', $network);
1274            $cidr_ip_binary = sprintf('%032b', ip2long($cidr_ip));
1275            if (mb_substr($addr_binary, 0, $cidr_bitmask) === mb_substr($cidr_ip_binary, 0, $cidr_bitmask)) {
1276               // IP address is within the specified IP range.
1277               return $network;
1278            }
1279        } else {
1280            if ($addr === $network) {
1281               // IP address exactly matches.
1282               return $network;
1283            }
1284        }
1285    }
1286
1287    return false;
1288}
1289
1290/**
1291 * If the given $url is on the same web site, return true. This can be used to
1292 * prevent from sending sensitive info in a get query (like the SID) to another
1293 * domain.
1294 *
1295 * @param  string $url    the URI to test.
1296 * @return bool True if given $url is our domain or has no domain (is a relative url), false if it's another.
1297 */
1298function isMyDomain($url)
1299{
1300    static $urls = array();
1301
1302    if (!isset($urls[$url])) {
1303        if (!preg_match('|https?://[\w.]+/|', $url)) {
1304            // If we can't find a domain we assume the URL is local (i.e. "/my/url/path/" or "../img/file.jpg").
1305            $urls[$url] = true;
1306        } else {
1307            $urls[$url] = preg_match('|https?://[\w.]*' . preg_quote(getenv('HTTP_HOST'), '|') . '|i', $url);
1308        }
1309    }
1310    return $urls[$url];
1311}
1312
1313/**
1314 * Takes a URL and returns it without the query or anchor portion
1315 *
1316 * @param  string $url   any kind of URI
1317 * @return string        the URI with ? or # and everything after removed
1318 */
1319function stripQuery($url)
1320{
1321    return preg_replace('/[?#].*$/', '', $url);
1322}
1323
1324/**
1325 * Returns a fully qualified URL to the current script, including the query.
1326 *
1327 * @return string    a full url to the current script
1328 */
1329function absoluteMe()
1330{
1331    $protocol = ('on' == getenv('HTTPS')) ? 'https://' : 'http://';
1332    return $protocol . getenv('HTTP_HOST') . getenv('REQUEST_URI');
1333}
1334
1335/**
1336 * Compares the current url with the referring url.
1337 *
1338 * @param  bool $exclude_query  Remove the query string first before comparing.
1339 * @return bool                 True if the current URL is the same as the referring URL, false otherwise.
1340 */
1341function refererIsMe($exclude_query=false)
1342{
1343    if ($exclude_query) {
1344        return (stripQuery(absoluteMe()) == stripQuery(getenv('HTTP_REFERER')));
1345    } else {
1346        return (absoluteMe() == getenv('HTTP_REFERER'));
1347    }
1348}
1349
1350/*
1351* Returns true if the given URL resolves to a resource with a HTTP 200 header response.
1352*
1353* @access   public
1354* @param    string  $url    URL to a file.
1355* @return   bool            True if the resource exists, false otherwise.
1356* @author   Quinn Comendant <quinn@strangecode.com>
1357* @version  1.0
1358* @since    02 May 2015 15:10:09
1359*/
1360function httpExists($url)
1361{
1362    $ch = curl_init($url);
1363    curl_setopt($ch, CURLOPT_NOBODY, true);
1364    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
1365    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1366    curl_exec($ch);
1367    return '200' == curl_getinfo($ch, CURLINFO_HTTP_CODE);
1368}
Note: See TracBrowser for help on using the repository browser.