eZ Publish  [trunk]
ezsys.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZSys class.
00004  *
00005  * Portions are modifications of patches by Andreas Böckler and Francis Nart
00006  *
00007  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00008  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00009  * @version //autogentag//
00010  * @package lib
00011  */
00012 
00013 /**
00014  * Easy access to various system settings
00015  *
00016  * The system is checked to see whether a virtualhost-less setup is used and
00017  * sets the appropriate variables which can be fetched with siteDir(), wwwDir()
00018  * and indexFile().
00019  * It also detects file and environment separators, fetch them with
00020  * fileSeparator() and envSeparator().
00021  *
00022  * <code>
00023  * // Run the init in the index file
00024  * eZSys::init( 'index.php', $ini->variable( 'SiteAccessSettings', 'ForceVirtualHost' ) === 'true' );
00025  * echo eZSys::indexFile();
00026  * echo eZSys::wwwDir();
00027  * </code>
00028  *
00029  * @package lib
00030  * @subpackage ezutils
00031  */
00032 class eZSys
00033 {
00034     /**
00035      * The line separator used in files, "\n" / "\n\r" / "\r"
00036      *
00037      * @var string
00038      */
00039     public $LineSeparator;
00040 
00041     /**
00042      * The directory separator used for files, '/' or '\'
00043      *
00044      * @var string
00045      */
00046     public $FileSeparator;
00047 
00048     /**
00049      * The list separator used for env variables (':' or ';')
00050      *
00051      * @var string
00052      */
00053     public $EnvSeparator;
00054 
00055     /**
00056      * The absolute path to the root directory.
00057      *
00058      * @var string
00059      */
00060     public $RootDir;
00061 
00062     /**
00063      * The system path to where all the code resides
00064      *
00065      * @var string
00066      */
00067     public $SiteDir;
00068 
00069     /**
00070      * The access path of the current site view, associated array of associated arrays.
00071      *
00072      * On first level key is 'siteaccess' and 'path' to distinguish between siteaccess
00073      * and general path. On second level you have (string)'name' and (array)'url',
00074      * where url is the path and name is the name of the source (used to match siteaccess
00075      * in {@link eZSys::indexFile()} for RemoveSiteAccessIfDefaultAccess matching) .
00076      *
00077      * @var array
00078      */
00079     protected $AccessPath;
00080 
00081     /**
00082      * The relative directory path of the vhless setup
00083      *
00084      * @var string
00085      */
00086     public $WWWDir;
00087 
00088     /**
00089      * The index file name (eg: 'index.php')
00090      *
00091      * @var string
00092      */
00093     public $IndexFile;
00094 
00095     /**
00096      * The uri which is used for parsing module/view information from, may differ from $_SERVER['REQUEST_URI']
00097      *
00098      * @var string
00099      */
00100     public $RequestURI;
00101 
00102     /**
00103      * The type of filesystem, is either win32 or unix. This often used to determine OS specific paths.
00104      *
00105      * @var string
00106      */
00107     public $FileSystemType;
00108 
00109     /**
00110      * The character to be used in shell escaping, this character is OS specific
00111      *
00112      * @var stringt
00113      */
00114     public $ShellEscapeCharacter;
00115 
00116     /**
00117      * The type of OS, is either win32, mac or unix.
00118      *
00119      * @var string
00120      */
00121     public $OSType;
00122 
00123     /**
00124      * Holds server variables as read automatically or provided by unit tests
00125      * Only used by init functionality as other calls will need to use live data direclty from globals.
00126      *
00127      * @var array
00128      */
00129     protected $Params;
00130 
00131     /**
00132      * Holds eZSys instance
00133      *
00134      * @var eZSys|null
00135      */
00136     protected static $instance = null;
00137 
00138     /**
00139      * Query string for the current request
00140      * In the form of "?param1=value1&param2=value2
00141      *
00142      * @var string
00143      */
00144     protected $QueryString;
00145 
00146     /**
00147      * Initialize the object with settings taken from the current script run.
00148      *
00149      * @param array $serverParams For unit testing use, see first few lines for content
00150      */
00151     public function __construct( array $serverParams = array() )
00152     {
00153         $this->Params = $serverParams + array(
00154             'PHP_OS' => PHP_OS,
00155             'DIRECTORY_SEPARATOR' => DIRECTORY_SEPARATOR,
00156             'PATH_SEPARATOR' => PATH_SEPARATOR,
00157             '_SERVER' => $_SERVER,
00158         );
00159 
00160         if ( isset( $this->Params['_SERVER']['REQUEST_TIME'] ) )
00161         {
00162             // REQUEST_TIME is a float and includes microseconds in PHP > 5.4.0
00163             // It should be casted to int in order to keep BC
00164             $this->Params['_SERVER']['REQUEST_TIME'] = (int)$this->Params['_SERVER']['REQUEST_TIME'];
00165         }
00166 
00167         $this->Attributes = array( 'magickQuotes' => true,
00168                                    'hostname'     => true );
00169         $this->FileSeparator = $this->Params['DIRECTORY_SEPARATOR'];
00170         $this->EnvSeparator  = $this->Params['PATH_SEPARATOR'];
00171 
00172         // Determine OS specific settings
00173         if ( $this->Params['PHP_OS'] === 'WINNT' )
00174         {
00175             $this->OSType = "win32";
00176             $this->OS = "windows";
00177             $this->FileSystemType = "win32";
00178             $this->LineSeparator = "\r\n";
00179             $this->ShellEscapeCharacter = '"';
00180             $this->BackupFilename = '.bak';
00181         }
00182         else
00183         {
00184             $this->OSType = 'unix';
00185             if ( $this->Params['PHP_OS'] === 'Linux' )
00186             {
00187                 $this->OS = 'linux';
00188             }
00189             else if ( $this->Params['PHP_OS'] === 'FreeBSD' )
00190             {
00191                 $this->OS = 'freebsd';
00192             }
00193             else if ( $this->Params['PHP_OS'] === 'Darwin' )
00194             {
00195                 $this->OS = 'darwin';
00196             }
00197             else
00198             {
00199                 $this->OS = false;
00200             }
00201             $this->FileSystemType = "unix";
00202             $this->LineSeparator = "\n";
00203             $this->ShellEscapeCharacter = "'";
00204             $this->BackupFilename = '~';
00205         }
00206 
00207         if ( get_magic_quotes_gpc() == 1 )
00208         {
00209             self::removeMagicQuotes();
00210         }
00211 
00212         $this->AccessPath = array( 'siteaccess' => array( 'name' => '', 'url' => array() ),
00213                                    'path'       => array( 'name' => '', 'url' => array() ) );
00214     }
00215 
00216     /**
00217      * Removes magic quotes
00218      *
00219      * @deprecated Since 4.5, magic quotes setting has been deprecated in PHP 5.3
00220      *
00221      * @return void
00222      */
00223     public static function removeMagicQuotes()
00224     {
00225         $globalVariables = array( '_SERVER', '_ENV' );
00226         foreach ( $globalVariables as $globalVariable )
00227         {
00228             foreach ( array_keys( $GLOBALS[$globalVariable] ) as $key )
00229             {
00230                 if ( !is_array( $GLOBALS[$globalVariable][$key] ) )
00231                 {
00232                     $GLOBALS[$globalVariable][$key] = stripslashes( $GLOBALS[$globalVariable][$key] );
00233                 }
00234             }
00235         }
00236     }
00237 
00238     /**
00239      * Returns the OS type
00240      *
00241      * Possible values: win32, unix
00242      *
00243      * @return string
00244      */
00245     public static function osType()
00246     {
00247         return self::instance()->OSType;
00248     }
00249 
00250     /**
00251      * Returns the current OS name or false if it can not be determined.
00252      *
00253      * Possible values: windows, linux, freebsd, darwin
00254      *
00255      * @return string|bool
00256      */
00257     public static function osName()
00258     {
00259         return self::instance()->OS;
00260     }
00261 
00262     /**
00263      * Returns the filesystem type
00264      *
00265      * Possible values: win32, unix
00266      *
00267      * @return string
00268      */
00269     public static function filesystemType()
00270     {
00271         return self::instance()->FileSystemType;
00272     }
00273 
00274     /**
00275      * Returns the string used as the file separator on the current system
00276      *
00277      * @return string
00278      */
00279     public static function fileSeparator()
00280     {
00281         return self::instance()->FileSeparator;
00282     }
00283 
00284     /**
00285      * The PHP version as text.
00286      *
00287      * @deprecated Since 4.5, use PHP_VERSION
00288      *
00289      * @return string
00290      */
00291     public static function phpVersionText()
00292     {
00293         return PHP_VERSION;
00294     }
00295 
00296     /**
00297      * Returns the PHP version as an array with the version elements.
00298      *
00299      * @deprecated Since 4.5
00300      *
00301      * @return array
00302      */
00303     public static function phpVersion()
00304     {
00305         $elements = explode( '.', PHP_VERSION );
00306         return $elements;
00307     }
00308 
00309     /**
00310      * Checks if the given version is greater than or equal to the current PHP version
00311      *
00312      * Usage:
00313      * <code>
00314      * eZSys::isPHPVersionSufficient( array( 4, 1, 0 ) );
00315      * </code>
00316      *
00317      * @deprecated Since 4.5
00318      * @param array $requiredVersion Must be an array with version number
00319      * @return bool
00320     */
00321     static function isPHPVersionSufficient( $requiredVersion )
00322     {
00323         if ( !is_array( $requiredVersion ) )
00324             return false;
00325         $phpVersion = self::phpVersion();
00326         $len = min( count( $phpVersion ), count( $requiredVersion ) );
00327 
00328         for ( $i = 0; $i < $len; ++$i )
00329         {
00330             if ( (int) $phpVersion[$i] > (int) $requiredVersion[$i] )
00331                 return true;
00332             if ( (int) $phpVersion[$i] < (int) $requiredVersion[$i] )
00333                 return false;
00334         }
00335 
00336         return true;
00337     }
00338 
00339     /**
00340      * Determines if the current process has been started from the web or the shell
00341      *
00342      * @return bool
00343      */
00344     public static function isShellExecution()
00345     {
00346         $sapiType = php_sapi_name();
00347 
00348         if ( $sapiType == 'cli' )
00349             return true;
00350 
00351         // For CGI we have to check, if the script has been executed over shell.
00352         // Currently it looks like the HTTP_HOST variable is the most reasonable to check.
00353         if ( substr( $sapiType, 0, 3 ) == 'cgi' )
00354         {
00355             if ( !self::serverVariable( 'HTTP_HOST', true ) )
00356                 return true;
00357             else
00358                 return false;
00359         }
00360         return false;
00361     }
00362 
00363     /**
00364      * Returns an escaped string to be used as a shell argument
00365      *
00366      * @param string $argument
00367      * @return string
00368      */
00369     public static function escapeShellArgument( $argument )
00370     {
00371         $escapeChar = self::instance()->ShellEscapeCharacter;
00372         $argument = str_replace( "\\", "\\\\", $argument );
00373         if ( $escapeChar == "'" )
00374         {
00375             $argument = str_replace( $escapeChar, $escapeChar . "\\" . $escapeChar . $escapeChar, $argument );
00376         }
00377         else
00378         {
00379             $argument = str_replace( $escapeChar, "\\" . $escapeChar, $argument );
00380         }
00381         $argument = $escapeChar . $argument . $escapeChar;
00382         return $argument;
00383     }
00384 
00385     /**
00386      * Replaces % elements in $argumentText using $replaceList, and also
00387      * properly escape the argument
00388      *
00389      * @param string $argumentText
00390      * @param array $replaceList
00391      * @return string
00392      */
00393     public static function createShellArgument( $argumentText, array $replaceList )
00394     {
00395         $instance = self::instance();
00396         $elements = $instance->splitArgumentIntoElements( $argumentText );
00397         $replacedElements = array();
00398         foreach ( $elements as $element )
00399         {
00400             if ( is_string( $element ) )
00401             {
00402                 $replacedElements[] = strtr( $element, $replaceList );
00403                 continue;
00404             }
00405             $replacedElements[] = $element;
00406         }
00407         $text = $instance->mergeArgumentElements( $replacedElements );
00408         return $text;
00409     }
00410 
00411     /**
00412      * Splits $argumentText on boundaries formed by one or more spaces and save
00413      * them into an array of separate arguments.
00414      *
00415      * The number of spaces between to arguments is inserted as an integer value
00416      * between two argument values.
00417      *
00418      * Example:
00419      * <code>
00420      * $list = splitArgumentIntoElements( "-geometry 100x100" );
00421      * var_dump( $list ); // Output: array( "-geometry", 1, "100x100" );
00422      * </code>
00423      *
00424      * You can then easily modify the elements separately and create the argument
00425      * text with eZSys::mergeArgumentElements()
00426      *
00427      * @param string $argumentText
00428      * @return array
00429      */
00430     public static function splitArgumentIntoElements( $argumentText )
00431     {
00432         $argumentElements = array();
00433         $pos = 0;
00434 
00435         while ( $pos < strlen( $argumentText ) )
00436         {
00437             if ( $argumentText[$pos] == '"' || $argumentText[$pos] == "'" )
00438             {
00439                 $quoteStartPos = $pos + 1;
00440                 $quoteEndPos = $pos + 1;
00441 
00442                 while ( $quoteEndPos < strlen( $argumentText ) )
00443                 {
00444                     $tmpPos = strpos( $argumentText, $argumentText[$pos], $quoteEndPos );
00445 
00446                     if ( $tmpPos !== false && $argumentText[$tmpPos - 1] != "\\" )
00447                     {
00448                         $quoteEndPos = $tmpPos;
00449                         break;
00450                     }
00451 
00452                     if ( $tmpPos === false )
00453                     {
00454                         $quoteEndPos = strlen( $argumentText );
00455                         break;
00456                     }
00457                     $quoteEndPos = $tmpPos + 1;
00458                 }
00459 
00460                 $argumentElements[] = substr( $argumentText, $quoteStartPos, $quoteEndPos - $quoteStartPos );
00461                 $pos = $quoteEndPos + 1;
00462             }
00463             else if ( $argumentText[$pos] == ' ' )
00464             {
00465                 $spacePos = $pos;
00466                 $spaceEndPos = $pos;
00467                 while ( $spaceEndPos < strlen( $argumentText ) )
00468                 {
00469                     if ( $argumentText[$spaceEndPos] != ' ' )
00470                     {
00471                         break;
00472                     }
00473                     ++$spaceEndPos;
00474                 }
00475                 $spaceText = substr( $argumentText, $spacePos, $spaceEndPos - $spacePos );
00476                 $spaceCount = strlen( $spaceText );
00477                 if ( $spaceCount > 0 )
00478                 {
00479                     $argumentElements[] = $spaceCount;
00480                 }
00481 
00482                 $pos = $spaceEndPos;
00483             }
00484             else
00485             {
00486                 $spacePos = strpos( $argumentText, ' ', $pos );
00487 
00488                 if ( $spacePos !== false )
00489                 {
00490                     $argumentElements[] = substr( $argumentText, $pos, $spacePos - $pos );
00491                     $spaceEndPos = $spacePos + 1;
00492 
00493                     while ( $spaceEndPos < strlen( $argumentText ) )
00494                     {
00495                         if ( $argumentText[$spaceEndPos] != ' ' )
00496                         {
00497                             break;
00498                         }
00499                         ++$spaceEndPos;
00500                     }
00501 
00502                     $spaceText = substr( $argumentText, $spacePos, $spaceEndPos - $spacePos );
00503                     $spaceCount = strlen( $spaceText );
00504 
00505                     if ( $spaceCount > 0 )
00506                     {
00507                         $argumentElements[] = $spaceCount;
00508                     }
00509                     $pos = $spaceEndPos;
00510                 }
00511                 else
00512                 {
00513                     $argumentElements[] = substr( $argumentText, $pos );
00514                     $pos = strlen( $argumentText );
00515                 }
00516             }
00517         }
00518 
00519         return $argumentElements;
00520     }
00521 
00522     /**
00523      * Merges an argument list created by eZSys::splitArgumentIntoElements()
00524      * back into a text string
00525      *
00526      * @param array $argumentElements
00527      * @return string
00528      */
00529     public static function mergeArgumentElements( array $argumentElements )
00530     {
00531         $instance = self::instance();
00532         $argumentText = '';
00533         foreach ( $argumentElements as $element )
00534         {
00535             if ( is_int( $element ) )
00536             {
00537                 $argumentText .= str_repeat( ' ', $element );
00538             }
00539             else if ( is_string( $element ) )
00540             {
00541                 $argumentText .= $instance->escapeShellArgument( $element );
00542             }
00543         }
00544         return $argumentText;
00545     }
00546 
00547     /**
00548      * Returns the backup filename for this platform
00549      *
00550      * Possible values: .bak (win32), ~ (unix, mac)
00551      *
00552      * @return string
00553      */
00554     public static function backupFilename()
00555     {
00556         return self::instance()->BackupFilename;
00557     }
00558 
00559     /**
00560      * Returns the string used as line separator on the current system
00561      *
00562      * @return string
00563      */
00564     public static function lineSeparator()
00565     {
00566         return self::instance()->LineSeparator;
00567     }
00568 
00569     /**
00570      * Returns the string used as environment separator on the current system
00571      *
00572      * @return string
00573      */
00574     public static function envSeparator()
00575     {
00576         return self::instance()->EnvSeparator;
00577     }
00578 
00579     /**
00580      * Returns the path of the current var directory
00581      *
00582      * @return string
00583      */
00584     public static function varDirectory()
00585     {
00586         $ini = eZINI::instance();
00587         return eZDir::path( array( $ini->variable( 'FileSettings', 'VarDir' ) ) );
00588     }
00589 
00590     /**
00591      * Returns the current storage directory
00592      *
00593      * @return string
00594      */
00595     public static function storageDirectory()
00596     {
00597         $ini = eZINI::instance();
00598         $varDir = self::varDirectory();
00599         $storageDir = $ini->variable( 'FileSettings', 'StorageDir' );
00600         return eZDir::path( array( $varDir, $storageDir ) );
00601     }
00602 
00603     /**
00604      * Returns the current cache directory.
00605      *
00606      * @return string
00607      */
00608     public static function cacheDirectory()
00609     {
00610         $ini = eZINI::instance();
00611         $cacheDir = $ini->variable( 'FileSettings', 'CacheDir' );
00612 
00613         if ( $cacheDir[0] == "/" )
00614         {
00615             return eZDir::path( array( $cacheDir ) );
00616         }
00617         else
00618         {
00619             return eZDir::path( array( self::varDirectory(), $cacheDir ) );
00620         }
00621     }
00622 
00623     /**
00624      * Returns the absolute path to the eZ Publish root directory
00625      *
00626      * @return string|null
00627      */
00628     public static function rootDir()
00629     {
00630         $instance = self::instance();
00631         if ( !$instance->RootDir )
00632         {
00633             $cwd  = getcwd();
00634             $self  = $instance->serverVariable( 'PHP_SELF' );
00635             if ( file_exists( $cwd . $instance->FileSeparator . $self ) or
00636                  file_exists( $cwd . $instance->FileSeparator . $instance->IndexFile ) )
00637             {
00638                 $instance->RootDir = $cwd;
00639             }
00640             else
00641             {
00642                 $instance->RootDir = null;
00643             }
00644         }
00645         return $instance->RootDir;
00646     }
00647 
00648     /**
00649      * Returns the path to where all the code resides.
00650      *
00651      * @return string
00652      */
00653     public static function siteDir()
00654     {
00655         return self::instance()->SiteDir;
00656     }
00657 
00658     /**
00659      * Returns the relative directory path of the vhless setup.
00660      *
00661      * @return string
00662      */
00663     public static function wwwDir()
00664     {
00665         return self::instance()->WWWDir;
00666     }
00667 
00668     /**
00669      * Returns the filepath for the index file.
00670      *
00671      * @param bool $withAccessList
00672      * @return string
00673      */
00674     public static function indexDir( $withAccessList = true )
00675     {
00676         return self::wwwDir() . self::indexFile( $withAccessList );
00677     }
00678 
00679     /**
00680      * Returns the query string for the current request.
00681      *
00682      * <code>
00683      * ?param1=value1&param2=value2
00684      * </code>
00685      *
00686      * @return string
00687      */
00688     public static function queryString()
00689     {
00690         return self::instance()->QueryString;
00691     }
00692 
00693     /**
00694      * Returns the filepath for the index file with the access path appended
00695      *
00696      * @param bool $withAccessPath
00697      * @return string
00698      */
00699     public static function indexFile( $withAccessPath = true )
00700     {
00701         $sys  = self::instance();
00702         $text = $sys->IndexFile;
00703 
00704         if ( $withAccessPath && ( isset( $sys->AccessPath['siteaccess']['url'][0] ) || isset( $sys->AccessPath['path']['url'][0] ) ) )
00705         {
00706             $ini = eZINI::instance();
00707             if ( isset( $sys->AccessPath['siteaccess']['url'][0] ) &&
00708                  $ini->variable( 'SiteAccessSettings', 'RemoveSiteAccessIfDefaultAccess' ) === 'enabled' )
00709             {
00710                 $defaultAccess = $ini->variable( 'SiteSettings', 'DefaultAccess' );
00711                 // 1st is proper match where code has used updated api as of 4.4, do not use siteaccess
00712                 if ( $sys->AccessPath['siteaccess']['name'] === $defaultAccess )
00713                     $accessPath = implode( '/', $sys->AccessPath['path']['url'] );
00714                 // 2nd is for compatability with older code that used eZSys api withouth defining scopes, shift default siteaccess path
00715                 elseif ( $sys->AccessPath['siteaccess']['name'] === 'undefined' && $sys->AccessPath['siteaccess']['url'][0] === $defaultAccess )
00716                 {
00717                     $accessPathArray = $sys->AccessPath;
00718                     array_shift( $accessPathArray['siteaccess']['url'] ); //remove default siteaccess
00719                     $accessPath = implode( '/', array_merge( $accessPathArray['siteaccess']['url'], $accessPathArray['path']['url'] ) );
00720                 }
00721                 // In case there is no default siteaccess match use full url
00722                 else
00723                     $accessPath = implode( '/', array_merge( $sys->AccessPath['siteaccess']['url'], $sys->AccessPath['path']['url'] ) );
00724             }
00725             else
00726             {
00727                 $accessPath = implode( '/', array_merge( $sys->AccessPath['siteaccess']['url'], $sys->AccessPath['path']['url'] ) );
00728             }
00729 
00730             $text .= '/' . $accessPath;
00731 
00732             // Make sure we never return just a single '/' in case where siteaccess was shifted
00733             if ( $text === '/' )
00734                 $text = '';
00735         }
00736         return $text;
00737     }
00738 
00739     /**
00740      * Returns the filepath for the index file
00741      *
00742      * @return string
00743      */
00744     public static function indexFileName()
00745     {
00746         return self::instance()->IndexFile;
00747     }
00748 
00749     /**
00750      * Returns the current hostname.
00751      *
00752      * First tries to use X-Forward-Host before it goes on to use host in header, if none of them
00753      * exists fallback to use host part of site.ini\[SiteSettings]|SiteURL setting.
00754      *
00755      * @return string
00756     */
00757     public static function hostname()
00758     {
00759         $hostName = null;
00760         $forwardedHostsString = self::serverVariable( 'HTTP_X_FORWARDED_HOST', true );
00761         if ( $forwardedHostsString )
00762         {
00763             $forwardedHosts = explode( ',', $forwardedHostsString );
00764             $hostName = trim( $forwardedHosts[0] );
00765         }
00766 
00767         if ( !$hostName && self::serverVariable( 'HTTP_HOST', true ) )
00768         {
00769             $hostName = self::serverVariable( 'HTTP_HOST' );
00770         }
00771 
00772         if ( !$hostName )
00773         {
00774             $siteUrl = eZINI::instance()->variable( 'SiteSettings', 'SiteURL' );
00775             $hostName = parse_url( "http://{$siteUrl}", PHP_URL_HOST );
00776         }
00777 
00778         return $hostName;
00779     }
00780 
00781     /**
00782      * Returns the client IP whether he's behind a proxy or not
00783      *
00784      * Use [HTTPHeaderSettings].ClientIpByCustomHTTPHeader in site.ini if you want
00785      * to use a custom http header such as X-Forwarded-For
00786      *
00787      * Note: X-Forwarded-For is transformed by PHP
00788      *       into $_SERVER['HTTP_X_FORWARDED_FOR]
00789      *
00790      * @return string
00791      */
00792     public static function clientIP()
00793     {
00794         $customHTTPHeader = eZINI::instance()->variable( 'HTTPHeaderSettings', 'ClientIpByCustomHTTPHeader' );
00795         if( $customHTTPHeader && $customHTTPHeader != 'false' )
00796         {
00797             // Transforms for instance, X-Forwarded-For into X_FORWARDED_FOR
00798             $phpHeader = 'HTTP_' . str_replace( '-', '_', strtoupper( $customHTTPHeader ) );
00799             $forwardedClientsString = eZSys::serverVariable( $phpHeader, true );
00800 
00801             if ( $forwardedClientsString )
00802             {
00803                 // $forwardedClientsString (usually) contains a comma+space separated list of IPs
00804                 // where the left-most being the farthest downstream client. All the others are proxy servers.
00805                 // As X-Forwarded-For is not a standard header yet, we prefer to use a simple comma as the explode delimiter
00806                 $forwardedClients = explode( ',', $forwardedClientsString );
00807                 if( !empty( $forwardedClients ) )
00808                 {
00809                     return trim( $forwardedClients[0] );
00810                 }
00811             }
00812 
00813             // Fallback on $_SERVER['REMOTE_ADDR']
00814             eZDebug::writeWarning( "Could not get ip with ClientIpByCustomHTTPHeader={$customHTTPHeader}, fallback to using REMOTE_ADDR",
00815                                    __METHOD__ );
00816         }
00817 
00818         return self::serverVariable( 'REMOTE_ADDR', true );
00819     }
00820 
00821     /**
00822      * Determines if SSL is enabled and protocol HTTPS is used.
00823      *
00824      * @return bool
00825      */
00826     public static function isSSLNow()
00827     {
00828         $ini = eZINI::instance();
00829         $sslPort = $ini->variable( 'SiteSettings', 'SSLPort' );
00830         if ( !$sslPort )
00831             $sslPort = eZSSLZone::DEFAULT_SSL_PORT;
00832         // $nowSSl is true if current access mode is HTTPS.
00833         $nowSSL = ( self::serverPort() == $sslPort );
00834 
00835         if ( !$nowSSL )
00836         {
00837             // Check if this request might be driven through a ssl proxy
00838             if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) )
00839             {
00840                 $nowSSL = ( $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' );
00841             }
00842             else if ( isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) )
00843             {
00844                 $sslPort = $ini->variable( 'SiteSettings', 'SSLPort' );
00845                 $nowSSL = ( $_SERVER['HTTP_X_FORWARDED_PORT'] == $sslPort );
00846             }
00847             else if ( isset( $_SERVER['HTTP_X_FORWARDED_SERVER'] ) )
00848             {
00849                 $sslProxyServerName = $ini->variable( 'SiteSettings', 'SSLProxyServerName' );
00850                 $nowSSL = ( $sslProxyServerName == $_SERVER['HTTP_X_FORWARDED_SERVER'] );
00851             }
00852         }
00853         return $nowSSL;
00854     }
00855 
00856     /**
00857      * Returns the current server protocol depending on if SSL is enabled or not.
00858      *
00859      * @return string
00860      */
00861     public static function serverProtocol()
00862     {
00863         if ( self::isSSLNow() )
00864             return 'https';
00865         else
00866             return 'http';
00867     }
00868 
00869     /**
00870      * Returns the server URL (protocol and hostname and port)
00871      *
00872      * @return string
00873      */
00874     public static function serverURL()
00875     {
00876         $host = self::hostname();
00877         $url = '';
00878         if ( $host )
00879         {
00880             if ( self::isSSLNow() )
00881             {
00882                 // https case
00883                 $host = preg_replace( '/:\d+$/', '', $host );
00884 
00885                 $ini = eZINI::instance();
00886                 $sslPort = $ini->variable( 'SiteSettings', 'SSLPort' );
00887 
00888                 $sslPortString = ( $sslPort == eZSSLZone::DEFAULT_SSL_PORT ) ? '' : ":$sslPort";
00889                 $url = "https://" . $host  . $sslPortString;
00890             }
00891             else
00892             {
00893                 $url = "http://" . $host;
00894             }
00895         }
00896         return $url;
00897     }
00898 
00899     /**
00900      * Returns the server port or 80 as default if the server port can not
00901      * be retrieved from the hostname or the server variable 'SERVER_PORT'
00902      *
00903      * @return int
00904      */
00905     public static function serverPort()
00906     {
00907         if ( empty( $GLOBALS['eZSysServerPort'] ) )
00908         {
00909             $hostname = self::hostname();
00910             if ( preg_match( "/.*:([0-9]+)/", $hostname, $regs ) )
00911             {
00912                 $port = (int) $regs[1];
00913             }
00914             else
00915             {
00916                 $port = (int) self::serverVariable( 'SERVER_PORT', true );
00917             }
00918 
00919             if ( !$port )
00920             {
00921                 $port = 80;
00922             }
00923 
00924             $GLOBALS['eZSysServerPort'] = $port;
00925         }
00926         return $GLOBALS['eZSysServerPort'];
00927     }
00928 
00929     /**
00930      * Should return true when magick quotes are enabled, but instead return null.
00931      *
00932      * @deprecated since 4.5
00933      *
00934      * @return null
00935      */
00936     public static function magickQuotes()
00937     {
00938         return null;
00939     }
00940 
00941     /**
00942      * Returns the value of $_SERVER[$variableName] if it is set.
00943      *
00944      * If it isn't set, trigger an error message if $quiet is false
00945      *
00946      * @param string $variableName
00947      * @param bool $quiet
00948      * @return mixed|null
00949      */
00950     public static function serverVariable( $variableName, $quiet = false )
00951     {
00952         if ( !isset( $_SERVER[$variableName] ) )
00953         {
00954             if ( !$quiet )
00955             {
00956                 eZDebug::writeError( "Server variable '$variableName' does not exist", __METHOD__ );
00957             }
00958 
00959             return null;
00960         }
00961         return $_SERVER[$variableName];
00962     }
00963 
00964     /**
00965      * Sets a server variable in the global array $_SERVER
00966      *
00967      * Note: Variables are only set for the current process/page view
00968      * @param string $variableName
00969      * @param mixed $variableValue
00970      * @return void
00971      */
00972     public static function setServerVariable( $variableName, $variableValue )
00973     {
00974         $_SERVER[$variableName] = $variableValue;
00975     }
00976 
00977     /**
00978      * Returns the server's path string
00979      *
00980      * @param bool $quiet
00981      * @return mixed|null
00982      */
00983     public static function path( $quiet = false )
00984     {
00985         return self::serverVariable( 'PATH', $quiet );
00986     }
00987 
00988     /**
00989      * Returns an environment variable or null if it is not available
00990      *
00991      * If the variable is not available, trigger an error message
00992      *
00993      * @param string $variableName
00994      * @param bool $quiet
00995      * @return null|string
00996      */
00997     public static function environmentVariable( $variableName, $quiet = false )
00998     {
00999         if ( getenv($variableName) === false )
01000         {
01001             if ( !$quiet )
01002             {
01003                 eZDebug::writeError( "Environment variable '$variableName' does not exist", __METHOD__ );
01004             }
01005             return null;
01006         }
01007         return getenv($variableName);
01008     }
01009 
01010     /**
01011      * Checks if an environment variable is available
01012      *
01013      * @param string $variableName
01014      * @return bool
01015      */
01016     public static function hasEnvironmentVariable( $variableName )
01017     {
01018         return getenv($variableName) !== false;
01019     }
01020 
01021     /**
01022      * Sets an environment variable for the current process/page view
01023      *
01024      * @param string $variableName
01025      * @param mixed $variableValue
01026      * @return void
01027      */
01028     public static function setEnvironmentVariable( $variableName, $variableValue )
01029     {
01030         putenv( "$variableName=$variableValue" );
01031     }
01032 
01033     /**
01034      * Make sure that certain attribute keys are available in $this->Attributes
01035      *
01036      * @return array
01037      */
01038     function attributes()
01039     {
01040         return array_merge( array( 'wwwdir',
01041                                    'sitedir',
01042                                    'indexfile',
01043                                    'indexdir',
01044                                    'querystring' ),
01045                             array_keys( $this->Attributes ) );
01046 
01047     }
01048 
01049     /**
01050      * Checks if the attribute $attr is set.
01051      *
01052      * @param string $attr
01053      * @return bool
01054      */
01055     function hasAttribute( $attr )
01056     {
01057         return in_array( $attr, $this->attributes() );
01058     }
01059 
01060     /**
01061      * Returns the attribute value for $attr or null if the attribute does not exist
01062      *
01063      * @param string $attr
01064      * @return null|string
01065      */
01066     function attribute( $attr )
01067     {
01068         if ( isset( $this->Attributes[$attr] ) )
01069         {
01070             return $this->$attr();
01071         }
01072         else if ( $attr == 'wwwdir' )
01073         {
01074             return $this->wwwDir();
01075         }
01076         else if ( $attr == 'sitedir' )
01077         {
01078             return $this->siteDir();
01079         }
01080         else if ( $attr == 'indexfile' )
01081         {
01082             return $this->indexFile();
01083         }
01084         else if ( $attr == 'indexdir' )
01085         {
01086             return $this->indexDir();
01087         }
01088         else if ( $attr = 'querystring' )
01089         {
01090             return $this->queryString();
01091         }
01092 
01093         eZDebug::writeError( "Attribute '$attr' does not exist", __METHOD__ );
01094         return null;
01095     }
01096 
01097     /**
01098      * Appends the access path (parts of url that identifies siteaccess), used by {@link eZSys::indexFile()}
01099      * NOTE: Does not make sense to use for siteaccess, as you would want to clear current path and set new one
01100      *       normally, so preferably use {@link eZSys::setAccessPath()} in this case.
01101      *
01102      * @param array|string $path
01103      * @param string $name An identifer of the name of the path provided {@link $AccessPath}
01104      * @param bool $siteaccess Hints if path is siteaccess related or not, needed in case subsequesnt code suddenly
01105      *                         changes siteaccess and needs to clear siteaccess scope
01106      */
01107     static function addAccessPath( $path, $name = 'undefined', $siteaccess = true )
01108     {
01109         $instance = self::instance();
01110         if ( !is_array( $path ) )
01111             $path = array( $path );
01112 
01113         if ( $siteaccess )
01114         {
01115             $instance->AccessPath['siteaccess']['name'] = $name;
01116             if ( isset($instance->AccessPath['siteaccess']['url'][0]) )
01117                 $instance->AccessPath['siteaccess']['url'] = array_merge( $instance->AccessPath['siteaccess']['url'], $path );
01118             else
01119                 $instance->AccessPath['siteaccess']['url'] = $path;
01120         }
01121         else
01122         {
01123             $instance->AccessPath['path']['name'] = $name;
01124             if ( isset($instance->AccessPath['path']['url'][0]) )
01125                 $instance->AccessPath['path']['url'] = array_merge( $instance->AccessPath['path']['url'], $path );
01126             else
01127                 $instance->AccessPath['path']['url'] = $path;
01128         }
01129     }
01130 
01131     /**
01132      * Set access path (parts of url that identifies siteaccess), used by {@link eZSys::indexFile()}
01133      *
01134      * @param array $path
01135      * @param string $name An identifer of the name of the path provided {@link $AccessPath}
01136      * @param bool $siteaccess Hints if path is siteaccess related or not, needed in case subsequesnt code suddenly
01137      *                         changes siteaccess and needs to clear siteaccess scope
01138      */
01139     static function setAccessPath( array $path = array(), $name = 'undefined', $siteaccess = true  )
01140     {
01141         if ( $siteaccess )
01142             self::instance()->AccessPath['siteaccess'] = array( 'name' => $name, 'url' => $path );
01143         else
01144             self::instance()->AccessPath['path'] = array( 'name' => $name, 'url' => $path );
01145     }
01146 
01147     /**
01148      * Clears the access path, used by {@link eZSys::indexFile()}
01149      *
01150      * @param bool $siteaccess
01151      * @return void
01152      */
01153     static function clearAccessPath( $siteaccess = true )
01154     {
01155         if ( $siteaccess )
01156             self::instance()->AccessPath['siteaccess'] = array( 'name' => '', 'url' => array() );
01157         else
01158             self::instance()->AccessPath['path'] = array( 'name' => '', 'url' => array() );
01159     }
01160 
01161     /**
01162      * Magic function to get access readonly properties (protected)
01163      *
01164      * @param string $propertyName
01165      * @return mixed
01166      * @throws ezcBasePropertyNotFoundException
01167      */
01168     public function __get( $propertyName )
01169     {
01170         if ( $propertyName === 'AccessPath' )
01171             return $this->AccessPath;
01172 
01173         throw new ezcBasePropertyNotFoundException( $propertyName );
01174     }
01175 
01176     /**
01177      * Magic function to see if readonly properties (protected) exists
01178      *
01179      * @param string $propertyName Option name to check for.
01180      * @return bool Whether the option exists.
01181      * @ignore
01182      */
01183     public function __isset( $propertyName )
01184     {
01185         return $propertyName === 'AccessPath';
01186     }
01187 
01188     /**
01189      * Returns true if debugging of internals is enabled, this will display
01190      * which server variables are read.
01191      * Set the option with setIsDebugEnabled().
01192      *
01193      * @deprecated Since 4.5, not used
01194      * @return bool
01195      */
01196     public static function isDebugEnabled()
01197     {
01198     }
01199 
01200     /**
01201      * Sets whether internal debugging is enabled or not.
01202      *
01203      * @deprecated Since 4.5, has not effect anymore
01204      * @param bool $debug
01205      */
01206     public static function setIsDebugEnabled( $debug )
01207     {
01208     }
01209 
01210     /**
01211      * Initializes some variables according to some global PHP values.
01212      * This function should be called once in the index file with the parameters
01213      * stated in the parameter list.
01214      *
01215      * @param string $index The current index file, needed for virtual host mode detection.
01216      * @param bool $forceVirtualHost Virtual host mode is normally autodetected, but if not this can be forced
01217      *                               by setting this to true.
01218      */
01219     public static function init( $index = 'index.php', $forceVirtualHost = null )
01220     {
01221         $instance       = self::instance();
01222         $server         = $instance->Params['_SERVER'];
01223         $phpSelf        = $server['PHP_SELF'];
01224         $requestUri     = $server['REQUEST_URI'];
01225         $scriptFileName = $server['SCRIPT_FILENAME'];
01226         $siteDir        = rtrim( str_replace( $index, '', $scriptFileName ), '\/' ) . '/';
01227         $wwwDir         = '';
01228         $IndexFile      = '';
01229         $queryString    = '';
01230 
01231         // see if we can use phpSelf to determin wwwdir
01232         $tempwwwDir = self::getValidwwwDir( $phpSelf, $scriptFileName, $index );
01233         if ( $tempwwwDir !== null && $tempwwwDir !== false )
01234         {
01235             // Force virual host or Auto detect IIS vh mode & Apache .htaccess mode
01236             if ( $forceVirtualHost
01237               || ( isset( $server['IIS_WasUrlRewritten'] ) && $server['IIS_WasUrlRewritten'] )
01238               || ( isset( $server['REDIRECT_URL'] ) && isset( $server['REDIRECT_STATUS'] ) && $server['REDIRECT_STATUS'] == '200' ) )
01239             {
01240                 if ( $tempwwwDir )
01241                 {
01242                     $wwwDir = '/' . $tempwwwDir;
01243                     $wwwDirPos = strpos( $requestUri, $wwwDir );
01244                     if ( $wwwDirPos !== false )
01245                     {
01246                         $requestUri = substr( $requestUri, $wwwDirPos + strlen($wwwDir) );
01247                     }
01248                 }
01249             }
01250             else // Non virtual host mode, use $tempwwwDir to figgure out paths
01251             {
01252                 $indexDir = $index;
01253                 if ( $tempwwwDir )
01254                 {
01255                     $wwwDir  = '/' . $tempwwwDir;
01256                     $indexDir = $wwwDir . '/' . $indexDir;
01257                 }
01258                 $IndexFile = '/' . $index;
01259 
01260                 // remove sub path from requestUri
01261                 $indexDirPos = strpos( $requestUri, $indexDir );
01262                 if ( $indexDirPos !== false )
01263                 {
01264                     $requestUri = substr( $requestUri, $indexDirPos + strlen($indexDir) );
01265                 }
01266                 elseif ( $wwwDir )
01267                 {
01268                     $wwwDirPos = strpos( $requestUri, $wwwDir );
01269                     if ( $wwwDirPos !== false )
01270                     {
01271                         $requestUri = substr( $requestUri, $wwwDirPos + strlen($wwwDir) );
01272                     }
01273                 }
01274             }
01275         }
01276 
01277         // remove url and hash parameters
01278         if ( isset( $requestUri[1] ) && $requestUri !== '/'  )
01279         {
01280             $uriGetPos = strpos( $requestUri, '?' );
01281             if ( $uriGetPos !== false )
01282             {
01283                 $queryString = substr( $requestUri, $uriGetPos );
01284                 if ( $uriGetPos === 0 )
01285                     $requestUri = '';
01286                 else
01287                     $requestUri = substr( $requestUri, 0, $uriGetPos );
01288             }
01289 
01290             $uriHashPos = strpos( $requestUri, '#' );
01291             if ( $uriHashPos === 0 )
01292                 $requestUri = '';
01293             elseif ( $uriHashPos !== false )
01294                 $requestUri = substr( $requestUri, 0, $uriHashPos );
01295         }
01296 
01297         // normalize slash use and url decode url if needed
01298         if ( $requestUri === '/' || $requestUri === '' )
01299         {
01300             $requestUri = '';
01301         }
01302         else
01303         {
01304             $requestUri = '/' . urldecode( trim( $requestUri, '/ ' ) );
01305         }
01306 
01307         $instance->AccessPath = array( 'siteaccess' => array( 'name' => '', 'url' => array() ),
01308                                        'path'       => array( 'name' => '', 'url' => array() ) );
01309 
01310         $instance->SiteDir    = $siteDir;
01311         $instance->WWWDir     = $wwwDir;
01312         $instance->IndexFile  = $IndexFile;
01313         $instance->RequestURI = $requestUri;
01314         $instance->QueryString = $queryString;
01315     }
01316 
01317     /**
01318      * Generate wwwdir from phpSelf if valid accoring to scriptFileName
01319      * and return null if invalid and false if there is no index in phpSelf
01320      *
01321      * @param string $phpSelf
01322      * @param string $scriptFileName
01323      * @param string $index
01324      * @return string|null|false String in form 'path/path2' if valid, null if not
01325      *                           and false if $index is not  part of phpself
01326      */
01327     protected static function getValidwwwDir( $phpSelf, $scriptFileName, $index )
01328     {
01329         if ( !isset( $phpSelf[1] ) || strpos( $phpSelf, $index ) === false )
01330             return false;
01331 
01332         // validate $index straight away
01333         if ( strpos( $scriptFileName, $index ) === false )
01334             return null;
01335 
01336         // optimize '/index.php' pattern
01337         if ( $phpSelf === "/{$index}" )
01338             return '';
01339 
01340         $phpSelfParts = explode( $index, $phpSelf );
01341         $validateDir = $phpSelfParts[0];
01342         // remove first path if home dir
01343         if ( $phpSelf[1] === '~' )
01344         {
01345             $uri = explode( '/', ltrim( $validateDir, '/' ) );
01346             array_shift( $uri );
01347             $validateDir = '/' . implode( '/', $uri );
01348         }
01349 
01350         // validate direclty with phpself part
01351         if ( strpos( $scriptFileName, $validateDir ) !== false )
01352             return trim( $phpSelfParts[0], '/' );
01353 
01354         // validate with windows path
01355         if ( strpos( $scriptFileName, str_replace( '/', '\\', $validateDir ) ) !== false )
01356             return trim( $phpSelfParts[0], '/' );
01357 
01358         return null;
01359     }
01360 
01361     /**
01362      * Returns the URI used for parsing modules, views and parameters
01363      *
01364      * May differ from $_SERVER['REQUEST_URI'].
01365      *
01366      * @return string
01367      */
01368     public static function requestURI()
01369     {
01370         return self::instance()->RequestURI;
01371     }
01372 
01373     /**
01374      * Returns a shared instance of the eZSys class
01375      *
01376      * @return eZSys
01377      */
01378     public static function instance()
01379     {
01380         if ( !self::$instance instanceof eZSys )
01381         {
01382             self::$instance = new eZSys();
01383         }
01384         return self::$instance;
01385     }
01386 
01387     /**
01388      * Sets eZSys instance or clears it if left undefined.
01389      *
01390      * @param eZSys $instance
01391      */
01392     public static function setInstance( eZSys $instance = null )
01393     {
01394         self::$instance = $instance;
01395     }
01396 
01397     /**
01398      * A wrapper for PHP's crc32 function. Returns the crc32 polynomial as unsigned int
01399      *
01400      * @param $string
01401      * @return int|string
01402      */
01403     public static function ezcrc32( $string )
01404     {
01405         $ini = eZINI::instance();
01406 
01407         if ( $ini->variable( 'SiteSettings', '64bitCompatibilityMode' ) === 'enabled' )
01408             $checksum = sprintf( '%u', crc32( $string ) );
01409         else
01410             $checksum = crc32( $string );
01411 
01412         return $checksum;
01413     }
01414 
01415     /**
01416      * Returns the schema of the request.
01417      *
01418      * @return string
01419      */
01420     public static function protocolSchema()
01421     {
01422         $schema = '';
01423         if( preg_match( "#^([a-zA-Z]+)/.*$#", self::serverVariable( 'SERVER_PROTOCOL' ), $schemaMatches ) )
01424         {
01425             $schema = strtolower( $schemaMatches[1] ) . '://';
01426         }
01427 
01428         return $schema;
01429     }
01430 
01431     /**
01432      * Wraps around the built-in glob() function to provide same functionality
01433      * for systems (e.g Solaris) that does not support GLOB_BRACE.
01434      *
01435      * @param string $pattern
01436      * @param int $flags
01437      * @return array
01438      */
01439     public static function globBrace( $pattern, $flags = 0 )
01440     {
01441         if ( defined( 'GLOB_BRACE' ) )
01442         {
01443             $flags = $flags | GLOB_BRACE;
01444             return glob( $pattern, $flags );
01445         }
01446         else
01447         {
01448             $result = array();
01449             $files = self::simulateGlobBrace( array( $pattern ) );
01450             foreach( $files as $file )
01451             {
01452                 $globList = glob( $file, $flags );
01453                 if ( is_array( $globList ) )
01454                 {
01455                     $result = array_merge( $result, $globList );
01456                 }
01457             }
01458             return $result;
01459         }
01460     }
01461 
01462     /**
01463      * Expands a list of filenames like GLOB_BRACE does.
01464      *
01465      * GLOB_BRACE is non POSIX and only available in GNU glibc. This is needed to
01466      * support operating systems like Solars.
01467      *
01468      * @param $filenames
01469      * @return array
01470      */
01471     protected static function simulateGlobBrace( $filenames )
01472     {
01473        $result = array();
01474 
01475        foreach ( $filenames as $filename )
01476        {
01477            if ( strpos( $filename, '{' ) === false )
01478            {
01479                $result[] = $filename;
01480                continue;
01481            }
01482 
01483            if ( preg_match( '/^(.*)\{(.*?)(?<!\\\\)\}(.*)$/', $filename, $match ) )
01484            {
01485                $variants = preg_split( '/(?<!\\\\),/', $match[2] );
01486 
01487                $newFilenames = array();
01488                foreach ( $variants as $variant )
01489                {
01490                    $newFilenames[] = $match[1] . $variant . $match[3];
01491                }
01492 
01493                $newFilenames = self::simulateGlobBrace( $newFilenames );
01494                $result = array_merge( $result, $newFilenames );
01495            }
01496            else
01497            {
01498                $result[] = $filename;
01499            }
01500        }
01501 
01502        return $result;
01503     }
01504 }
01505 
01506 ?>