Overview

Packages

  • Util

Classes

  • Horde_Array
  • Horde_Array_Sort_Helper
  • Horde_Domhtml
  • Horde_String
  • Horde_Util
  • Horde_Variables
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Util:: class provides generally useful methods.
  4:  *
  5:  * Copyright 1999-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author   Chuck Hagenbuch <chuck@horde.org>
 11:  * @author   Jon Parise <jon@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 14:  * @package  Util
 15:  */
 16: class Horde_Util
 17: {
 18:     /**
 19:      * A list of random patterns to use for overwriting purposes.
 20:      * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
 21:      * We save the random overwrites for efficiency reasons.
 22:      *
 23:      * @var array
 24:      */
 25:     static public $patterns = array(
 26:         "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
 27:         "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
 28:         "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
 29:         "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
 30:         "\xb6\xdb\x6d", "\xdb\x6d\xb6"
 31:     );
 32: 
 33:     /**
 34:      * Temp directory locations.
 35:      *
 36:      * @var array
 37:      */
 38:     static public $tmpLocations = array(
 39:         '/tmp/', '/var/tmp/', 'c:\WUTemp\\', 'c:\temp\\', 'c:\windows\temp\\',
 40:         'c:\winnt\temp\\'
 41:     );
 42: 
 43:     /**
 44:      * Are magic quotes in use?
 45:      *
 46:      * @var boolean
 47:      */
 48:     static protected $_magicquotes = null;
 49: 
 50:     /**
 51:      * TODO
 52:      *
 53:      * @var array
 54:      */
 55:     static protected $_shutdowndata = array(
 56:         'dirs' => array(),
 57:         'files' => array(),
 58:         'secure' => array()
 59:     );
 60: 
 61:     /**
 62:      * Has the shutdown method been registered?
 63:      *
 64:      * @var boolean
 65:      */
 66:     static protected $_shutdownreg = false;
 67: 
 68:     /**
 69:      * Cache for extensionExists().
 70:      *
 71:      * @var array
 72:      */
 73:     static protected $_cache = array();
 74: 
 75:     /**
 76:      * Returns an object's clone.
 77:      *
 78:      * @param object &$obj  The object to clone.
 79:      *
 80:      * @return object  The cloned object.
 81:      */
 82:     static public function &cloneObject(&$obj)
 83:     {
 84:         if (!is_object($obj)) {
 85:             $bt = debug_backtrace();
 86:             if (isset($bt[1])) {
 87:                 $caller = $bt[1]['function'];
 88:                 if (isset($bt[1]['class'])) {
 89:                     $caller = $bt[1]['class'].$bt[1]['type'].$caller;
 90:                 }
 91:             } else {
 92:                 $caller = 'main';
 93:             }
 94: 
 95:             $caller .= ' on line ' . $bt[0]['line'] . ' of ' . $bt[0]['file'];
 96: 
 97:             $ret = $obj;
 98:             return $ret;
 99:         }
100: 
101:         $ret = clone($obj);
102:         return $ret;
103:     }
104: 
105:     /**
106:      * Checks to see if a value has been set by the script and not by GET,
107:      * POST, or cookie input. The value being checked MUST be in the global
108:      * scope.
109:      *
110:      * @param string $varname  The variable name to check.
111:      * @param mixed $default   Default value if the variable isn't present
112:      *                         or was specified by the user. Defaults to null.
113:      *
114:      * @return mixed  $default if the var is in user input or not present,
115:      *                the variable value otherwise.
116:      */
117:     static public function nonInputVar($varname, $default = null)
118:     {
119:         return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
120:             ? $default
121:             : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
122:     }
123: 
124:     /**
125:      * Adds a name=value pair to the end of an URL, taking care of whether
126:      * there are existing parameters and whether to use ?, & or &amp; as the
127:      * glue.  All data will be urlencoded.
128:      *
129:      * @deprecated
130:      *
131:      * @param Horde_Url|string $url  The URL to modify.
132:      * @param mixed $parameter       Either the name value -or- an array of
133:      *                               name/value pairs.
134:      * @param string $value          If specified, the value part ($parameter
135:      *                               is then assumed to just be the parameter
136:      *                               name).
137:      * @param boolean $encode        Encode the argument separator?
138:      *
139:      * @return Horde_Url  The modified URL.
140:      */
141:     static public function addParameter($url, $parameter, $value = null,
142:                                         $encode = true)
143:     {
144:         if (empty($parameter)) {
145:             return $url;
146:         }
147: 
148:         if ($url instanceof Horde_Url) {
149:             $url = $url->copy()->add($parameter, $value);
150:             if (is_null($url->raw)) {
151:                 $url->setRaw(!$encode);
152:             }
153:             return $url;
154:         }
155: 
156:         $url = new Horde_Url($url);
157:         if (is_null($url->raw) && count($url->parameters)) {
158:             $url->setRaw(!$encode);
159:         }
160:         return $url->add($parameter, $value);
161:     }
162: 
163:     /**
164:      * Removes name=value pairs from a URL.
165:      *
166:      * @deprecated
167:      *
168:      * @param Horde_Url|string $url  The URL to modify.
169:      * @param mixed $remove          Either a single parameter to remove or an
170:      *                               array of parameters to remove.
171:      *
172:      * @return Horde_Url  The modified URL.
173:      */
174:     static public function removeParameter($url, $remove)
175:     {
176:         if ($url instanceof Horde_Url) {
177:             return $url->copy()->remove($remove);
178:         }
179: 
180:         $horde_url = new Horde_Url($url);
181:         return $horde_url->remove($remove);
182:     }
183: 
184:     /**
185:      * Returns a hidden form input containing the session name and id.
186:      *
187:      * @param boolean $append_session  0 = only if needed, 1 = always.
188:      *
189:      * @return string  The hidden form input, if needed/requested.
190:      */
191:     static public function formInput($append_session = 0)
192:     {
193:         return (($append_session == 1) || !isset($_COOKIE[session_name()]))
194:             ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
195:             : '';
196:     }
197: 
198:     /**
199:      * Prints a hidden form input containing the session name and id.
200:      *
201:      * @param boolean $append_session  0 = only if needed, 1 = always.
202:      */
203:     static public function pformInput($append_session = 0)
204:     {
205:         echo self::formInput($append_session);
206:     }
207: 
208:     /**
209:      * If magic_quotes_gpc is in use, run stripslashes() on $var.
210:      *
211:      * @param mixed $var  The string, or an array of strings, to un-quote.
212:      *
213:      * @return mixed  $var, minus any magic quotes.
214:      */
215:     static public function dispelMagicQuotes($var)
216:     {
217:         if (is_null(self::$_magicquotes)) {
218:             self::$_magicquotes = get_magic_quotes_gpc();
219:         }
220: 
221:         if (self::$_magicquotes) {
222:             $var = is_array($var)
223:                 ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var)
224:                 : stripslashes($var);
225:         }
226: 
227:         return $var;
228:     }
229: 
230:     /**
231:      * Gets a form variable from GET or POST data, stripped of magic quotes if
232:      * necessary. If the variable is somehow set in both the GET data and the
233:      * POST data, the value from the POST data will be returned and the GET
234:      * value will be ignored.
235:      *
236:      * @param string $var      The name of the form variable to look for.
237:      * @param string $default  The value to return if the variable is not
238:      *                         there.
239:      *
240:      * @return string  The cleaned form variable, or $default.
241:      */
242:     static public function getFormData($var, $default = null)
243:     {
244:         return (($val = self::getPost($var)) !== null)
245:             ? $val
246:             : self::getGet($var, $default);
247:     }
248: 
249:     /**
250:      * Gets a form variable from GET data, stripped of magic quotes if
251:      * necessary. This function will NOT return a POST variable.
252:      *
253:      * @param string $var      The name of the form variable to look for.
254:      * @param string $default  The value to return if the variable is not
255:      *                         there.
256:      *
257:      * @return string  The cleaned form variable, or $default.
258:      */
259:     static public function getGet($var, $default = null)
260:     {
261:         return (isset($_GET[$var]))
262:             ? self::dispelMagicQuotes($_GET[$var])
263:             : $default;
264:     }
265: 
266:     /**
267:      * Gets a form variable from POST data, stripped of magic quotes if
268:      * necessary. This function will NOT return a GET variable.
269:      *
270:      * @param string $var      The name of the form variable to look for.
271:      * @param string $default  The value to return if the variable is not
272:      *                         there.
273:      *
274:      * @return string  The cleaned form variable, or $default.
275:      */
276:     static public function getPost($var, $default = null)
277:     {
278:         return (isset($_POST[$var]))
279:             ? self::dispelMagicQuotes($_POST[$var])
280:             : $default;
281:     }
282: 
283:     /**
284:      * Determines the location of the system temporary directory.
285:      *
286:      * @return string  A directory name which can be used for temp files.
287:      *                 Returns false if one could not be found.
288:      */
289:     static public function getTempDir()
290:     {
291:         $tmp = false;
292: 
293:         // Try sys_get_temp_dir() - only available in PHP 5.2.1+.
294:         if (function_exists('sys_get_temp_dir')) {
295:             $tmp = sys_get_temp_dir();
296:         }
297: 
298:         // First, try PHP's upload_tmp_dir directive.
299:         if (!$tmp) {
300:             $tmp = ini_get('upload_tmp_dir');
301: 
302:             // Otherwise, try to determine the TMPDIR environment
303:             // variable.
304:             if (!$tmp) {
305:                 $tmp = getenv('TMPDIR');
306: 
307:                 // If we still cannot determine a value, then cycle through a
308:                 // list of preset possibilities.
309:                 if (!$tmp) {
310:                     foreach (self::$tmpLocations as $tmp_check) {
311:                         if (@is_dir($tmp_check)) {
312:                             return $tmp_check;
313:                         }
314:                     }
315:                 }
316:             }
317:         }
318: 
319:         return $tmp ? $tmp : false;
320:     }
321: 
322:     /**
323:      * Creates a temporary filename for the lifetime of the script, and
324:      * (optionally) registers it to be deleted at request shutdown.
325:      *
326:      * @param string $prefix   Prefix to make the temporary name more
327:      *                         recognizable.
328:      * @param boolean $delete  Delete the file at the end of the request?
329:      * @param string $dir      Directory to create the temporary file in.
330:      * @param boolean $secure  If deleting the file, should we securely delete
331:      *                         the file by overwriting it with random data?
332:      *
333:      * @return string   Returns the full path-name to the temporary file.
334:      *                  Returns false if a temp file could not be created.
335:      */
336:     static public function getTempFile($prefix = '', $delete = true, $dir = '',
337:                                        $secure = false)
338:     {
339:         $tempDir = (empty($dir) || !is_dir($dir))
340:             ? self::getTempDir()
341:             : $dir;
342: 
343:         $tempFile = tempnam($tempDir, $prefix);
344: 
345:         // If the file was created, then register it for deletion and return.
346:         if (empty($tempFile)) {
347:             return false;
348:         }
349: 
350:         if ($delete) {
351:             self::deleteAtShutdown($tempFile, true, $secure);
352:         }
353: 
354:         return $tempFile;
355:     }
356: 
357:     /**
358:      * Creates a temporary filename with a specific extension for the lifetime
359:      * of the script, and (optionally) registers it to be deleted at request
360:      * shutdown.
361:      *
362:      * @param string $extension  The file extension to use.
363:      * @param string $prefix     Prefix to make the temporary name more
364:      *                           recognizable.
365:      * @param boolean $delete    Delete the file at the end of the request?
366:      * @param string $dir        Directory to create the temporary file in.
367:      * @param boolean $secure    If deleting file, should we securely delete
368:      *                           the file by overwriting it with random data?
369:      *
370:      * @return string   Returns the full path-name to the temporary file.
371:      *                  Returns false if a temporary file could not be created.
372:      */
373:     static public function getTempFileWithExtension($extension = '.tmp',
374:                                                     $prefix = '',
375:                                                     $delete = true, $dir = '',
376:                                                     $secure = false)
377:     {
378:         $tempDir = (empty($dir) || !is_dir($dir))
379:             ? self::getTempDir()
380:             : $dir;
381: 
382:         if (empty($tempDir)) {
383:             return false;
384:         }
385: 
386:         $windows = substr(PHP_OS, 0, 3) == 'WIN';
387:         $tries = 1;
388:         do {
389:             // Get a known, unique temporary file name.
390:             $sysFileName = tempnam($tempDir, $prefix);
391:             if ($sysFileName === false) {
392:                 return false;
393:             }
394: 
395:             // tack on the extension
396:             $tmpFileName = $sysFileName . $extension;
397:             if ($sysFileName == $tmpFileName) {
398:                 return $sysFileName;
399:             }
400: 
401:             // Move or point the created temporary file to the full filename
402:             // with extension. These calls fail if the new name already
403:             // exists.
404:             $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName));
405:             if ($fileCreated) {
406:                 if (!$windows) {
407:                     unlink($sysFileName);
408:                 }
409: 
410:                 if ($delete) {
411:                     self::deleteAtShutdown($tmpFileName, true, $secure);
412:                 }
413: 
414:                 return $tmpFileName;
415:             }
416: 
417:             unlink($sysFileName);
418:         } while (++$tries <= 5);
419: 
420:         return false;
421:     }
422: 
423:     /**
424:      * Creates a temporary directory in the system's temporary directory.
425:      *
426:      * @param boolean $delete   Delete the temporary directory at the end of
427:      *                          the request?
428:      * @param string $temp_dir  Use this temporary directory as the directory
429:      *                          where the temporary directory will be created.
430:      *
431:      * @return string  The pathname to the new temporary directory.
432:      *                 Returns false if directory not created.
433:      */
434:     static public function createTempDir($delete = true, $temp_dir = null)
435:     {
436:         if (is_null($temp_dir)) {
437:             $temp_dir = self::getTempDir();
438:         }
439: 
440:         if (empty($temp_dir)) {
441:             return false;
442:         }
443: 
444:         /* Get the first 8 characters of a random string to use as a temporary
445:            directory name. */
446:         do {
447:             $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
448:         } while (file_exists($new_dir));
449: 
450:         $old_umask = umask(0000);
451:         if (!mkdir($new_dir, 0700)) {
452:             $new_dir = false;
453:         } elseif ($delete) {
454:             self::deleteAtShutdown($new_dir);
455:         }
456:         umask($old_umask);
457: 
458:         return $new_dir;
459:     }
460: 
461:     /**
462:      * Wrapper around fgetcsv().
463:      *
464:      * Empty lines will be skipped. If the 'length' parameter is provided, all
465:      * rows are filled up with empty strings up to this length, or stripped
466:      * down to this length.
467:      *
468:      * @param resource $file  A file pointer.
469:      * @param array $params   Optional parameters. Possible values:
470:      *                        - 'separator': The field delimiter.
471:      *                        - 'quote': The quote character.
472:      *                        - 'escape': The escape character.
473:      *                        - 'length': The expected number of fields.
474:      *
475:      * @return array|boolean  A row from the CSV file or false on error or end
476:      *                        of file.
477:      */
478:     static public function getCsv($file, $params = array())
479:     {
480:         $params += array('separator' => ',', 'quote' => '"', 'escape' => '\\');
481: 
482:         // Detect Mac line endings.
483:         $old = ini_get('auto_detect_line_endings');
484:         ini_set('auto_detect_line_endings', 1);
485: 
486:         do {
487:             // fgetcsv() throws a warning if the quote character is empty.
488:             if (!strlen($params['quote']) && $params['escape'] != '\\') {
489:                 $params['quote'] = '"';
490:             }
491:             if (!strlen($params['quote'])) {
492:                 $row = fgetcsv($file, 0, $params['separator']);
493:             } elseif (version_compare(PHP_VERSION, '5.3.0', '<')) {
494:                 $row = fgetcsv($file, 0, $params['separator'], $params['quote']);
495:             } else {
496:                 $row = fgetcsv($file, 0, $params['separator'], $params['quote'], $params['escape']);
497:             }
498:         } while ($row && $row[0] === null);
499: 
500:         ini_set('auto_detect_line_endings', $old);
501: 
502:         if ($row) {
503:             if (strlen($params['quote']) && strlen($params['escape'])) {
504:                 $row = array_map(create_function('$a', 'return str_replace(\'' . str_replace('\'', '\\\'', $params['escape'] . $params['quote']) . '\', \'' . str_replace('\'', '\\\'', $params['quote']) . '\', $a);'), $row);
505:             } else {
506:                 $row = array_map('trim', $row);
507:             }
508:             if (!empty($params['length'])) {
509:                 $length = count($row);
510:                 if ($length < $params['length']) {
511:                     $row += array_fill($length, $params['length'] - $length, '');
512:                 } elseif ($length > $params['length']) {
513:                     array_splice($row, $params['length']);
514:                 }
515:             }
516:         }
517: 
518:         return $row;
519:     }
520: 
521:     /**
522:      * Returns the canonical path of the string.  Like PHP's built-in
523:      * realpath() except the directory need not exist on the local server.
524:      *
525:      * Algorithim loosely based on code from the Perl File::Spec::Unix module
526:      * (version 1.5).
527:      *
528:      * @param string $path  A file path.
529:      *
530:      * @return string  The canonicalized file path.
531:      */
532:     static public function realPath($path)
533:     {
534:         /* Standardize on UNIX directory separators. */
535:         if (!strncasecmp(PHP_OS, 'WIN', 3)) {
536:             $path = str_replace('\\', '/', $path);
537:         }
538: 
539:         /* xx////xx -> xx/xx
540:          * xx/././xx -> xx/xx */
541:         $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path);
542: 
543:         /* ./xx -> xx */
544:         if ($path != './') {
545:             $path = preg_replace("|^(\./)+|", '', $path);
546:         }
547: 
548:         /* /../../xx -> xx */
549:         $path = preg_replace("|^/(\.\./?)+|", '/', $path);
550: 
551:         /* xx/ -> xx */
552:         if ($path != '/') {
553:             $path = preg_replace("|/\Z(?!\n)|", '', $path);
554:         }
555: 
556:         /* /xx/.. -> / */
557:         while (strpos($path, '/..') !== false) {
558:             $path = preg_replace("|/[^/]+/\.\.|", '', $path);
559:         }
560: 
561:         return empty($path) ? '/' : $path;
562:     }
563: 
564:     /**
565:      * Removes given elements at request shutdown.
566:      *
567:      * If called with a filename will delete that file at request shutdown; if
568:      * called with a directory will remove that directory and all files in that
569:      * directory at request shutdown.
570:      *
571:      * If called with no arguments, return all elements to be deleted (this
572:      * should only be done by Horde_Util::_deleteAtShutdown()).
573:      *
574:      * The first time it is called, it initializes the array and registers
575:      * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do
576:      * so manually.
577:      *
578:      * The second parameter allows the unregistering of previously registered
579:      * elements.
580:      *
581:      * @param string $filename   The filename to be deleted at the end of the
582:      *                           request.
583:      * @param boolean $register  If true, then register the element for
584:      *                           deletion, otherwise, unregister it.
585:      * @param boolean $secure    If deleting file, should we securely delete
586:      *                           the file?
587:      */
588:     static public function deleteAtShutdown($filename, $register = true,
589:                                             $secure = false)
590:     {
591:         /* Initialization of variables and shutdown functions. */
592:         if (!self::$_shutdownreg) {
593:             register_shutdown_function(array(__CLASS__, 'shutdown'));
594:             self::$_shutdownreg = true;
595:         }
596: 
597:         $ptr = &self::$_shutdowndata;
598:         if ($register) {
599:             if (@is_dir($filename)) {
600:                 $ptr['dirs'][$filename] = true;
601:             } else {
602:                 $ptr['files'][$filename] = true;
603:             }
604: 
605:             if ($secure) {
606:                 $ptr['secure'][$filename] = true;
607:             }
608:         } else {
609:             unset($ptr['dirs'][$filename], $ptr['files'][$filename], $ptr['secure'][$filename]);
610:         }
611:     }
612: 
613:     /**
614:      * Deletes registered files at request shutdown.
615:      *
616:      * This function should never be called manually; it is registered as a
617:      * shutdown function by Horde_Util::deleteAtShutdown() and called
618:      * automatically at the end of the request.
619:      *
620:      * Contains code from gpg_functions.php.
621:      * Copyright 2002-2003 Braverock Ventures
622:      */
623:     static public function shutdown()
624:     {
625:         $ptr = &self::$_shutdowndata;
626: 
627:         foreach ($ptr['files'] as $file => $val) {
628:             /* Delete files */
629:             if ($val && file_exists($file)) {
630:                 /* Should we securely delete the file by overwriting the data
631:                    with a random string? */
632:                 if (isset($ptr['secure'][$file])) {
633:                     $filesize = filesize($file);
634:                     $fp = fopen($file, 'r+');
635:                     foreach (self::$patterns as $pattern) {
636:                         $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize);
637:                         fwrite($fp, $pattern);
638:                         fseek($fp, 0);
639:                     }
640:                     fclose($fp);
641:                 }
642:                 @unlink($file);
643:             }
644:         }
645: 
646:         foreach ($ptr['dirs'] as $dir => $val) {
647:             /* Delete directories */
648:             if ($val && file_exists($dir)) {
649:                 /* Make sure directory is empty. */
650:                 $dir_class = dir($dir);
651:                 while (false !== ($entry = $dir_class->read())) {
652:                     if ($entry != '.' && $entry != '..') {
653:                         @unlink($dir . '/' . $entry);
654:                     }
655:                 }
656:                 $dir_class->close();
657:                 @rmdir($dir);
658:             }
659:         }
660:     }
661: 
662:     /**
663:      * Caches the result of extension_loaded() calls.
664:      *
665:      * @param string $ext  The extension name.
666:      *
667:      * @return boolean  Is the extension loaded?
668:      */
669:     static public function extensionExists($ext)
670:     {
671:         if (!isset(self::$_cache[$ext])) {
672:             self::$_cache[$ext] = extension_loaded($ext);
673:         }
674: 
675:         return self::$_cache[$ext];
676:     }
677: 
678:     /**
679:      * Tries to load a PHP extension, behaving correctly for all operating
680:      * systems.
681:      *
682:      * @param string $ext  The extension to load.
683:      *
684:      * @return boolean  True if the extension is now loaded, false if not.
685:      *                  True can mean that the extension was already loaded,
686:      *                  OR was loaded dynamically.
687:      */
688:     static public function loadExtension($ext)
689:     {
690:         /* If $ext is already loaded, our work is done. */
691:         if (self::extensionExists($ext)) {
692:             return true;
693:         }
694: 
695:         /* See if we can call dl() at all, by the current ini settings.
696:          * dl() has been removed in some PHP 5.3 SAPIs. */
697:         if ((ini_get('enable_dl') != 1) ||
698:             (ini_get('safe_mode') == 1) ||
699:             !function_exists('dl')) {
700:             return false;
701:         }
702: 
703:         if (!strncasecmp(PHP_OS, 'WIN', 3)) {
704:             $suffix = 'dll';
705:         } else {
706:             switch (PHP_OS) {
707:             case 'HP-UX':
708:                 $suffix = 'sl';
709:                 break;
710: 
711:             case 'AIX':
712:                 $suffix = 'a';
713:                 break;
714: 
715:             case 'OSX':
716:                 $suffix = 'bundle';
717:                 break;
718: 
719:             default:
720:                 $suffix = 'so';
721:             }
722:         }
723: 
724:         return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix);
725:     }
726: 
727:     /**
728:      * Utility function to obtain PATH_INFO information.
729:      *
730:      * @return string  The PATH_INFO string.
731:      */
732:     static public function getPathInfo()
733:     {
734:         if (isset($_SERVER['PATH_INFO']) &&
735:             (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) {
736:             return $_SERVER['PATH_INFO'];
737:         } elseif (isset($_SERVER['REQUEST_URI']) &&
738:                   isset($_SERVER['SCRIPT_NAME'])) {
739:             $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']);
740:             if (substr($search, -1) == '/') {
741:                 $search = substr($search, 0, -1);
742:             }
743:             $search = array($search);
744:             if (!empty($_SERVER['QUERY_STRING'])) {
745:                 // We can't use QUERY_STRING directly because URL rewriting
746:                 // might add more parameters to the query string than those
747:                 // from the request URI.
748:                 $url = parse_url($_SERVER['REQUEST_URI']);
749:                 if (!empty($url['query'])) {
750:                     $search[] = '?' . $url['query'];
751:                 }
752:             }
753:             $path = str_replace($search, '', $_SERVER['REQUEST_URI']);
754:             if ($path == '/') {
755:                 $path = '';
756:             }
757:             return $path;
758:         }
759: 
760:         return '';
761:     }
762: 
763: }
764: 
API documentation generated by ApiGen