|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZINI class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package lib 00009 */ 00010 00011 /*! 00012 \class eZINI ezini.php 00013 \ingroup eZUtils 00014 \brief Reads and writes .ini style configuration files 00015 00016 The most common way of using it is. 00017 \code 00018 00019 $ini = eZINI::instance( "site.ini" ); 00020 00021 // get a variable from the file. 00022 $iniVar = $ini->variable( "BlockName", "Variable" ); 00023 00024 \endcode 00025 00026 The default ini file is site.ini but others can be passed to the instance() function 00027 among with some others. It will create one unique instance for each ini file and rootdir, 00028 this means that the next time instance() is used with the same parameters the same 00029 object will be returned and no new parsing is required. 00030 00031 The class will by default try to create a cache file in var/cache/ini, however to change 00032 this behaviour the static setIsCacheEnabled() function can be used, or use the $useCache 00033 parameter in instance() for setting this for one object only. 00034 00035 The class will also handle charset conversion using eZTextCodec, to turn this behaviour 00036 off use the static setIsTextCodecEnabled() function or set the $useTextCodec parameter 00037 in instance() for a per object basis setting. 00038 00039 Normally the eZINI class will not give out much information about what it's doing, 00040 it's only when errors occur that you'll see this. To enable internal debugging use 00041 the static setIsDebugEnabled() function. The class will then give information about 00042 which files are load, if cache files are used and when cache files are written. 00043 */ 00044 class eZINI 00045 { 00046 /** 00047 * Constant path to directory for configuration cache 00048 * 00049 * @var string 00050 */ 00051 const CONFIG_CACHE_DIR = 'var/cache/ini/'; 00052 00053 /** 00054 * Constant integer to check against configuration cache format revision 00055 * 00056 * @var int 00057 */ 00058 const CONFIG_CACHE_REV = 2; 00059 00060 /** 00061 * Set EZP_INI_FILEMTIME_CHECK constant to false to improve performance by 00062 * not checking modified time on ini files. You can also set it to a string, the name 00063 * of a ini file you still want to check modified time on, best example would be to 00064 * set it to 'site.ini' to make the system still check that but not the rest. 00065 * 00066 * @var null|bool 00067 */ 00068 static protected $checkFileMtime = null; 00069 00070 /** 00071 * set EZP_INI_FILE_PERMISSION constant to the permissions you want saved 00072 * ini and cache files to have. 00073 * 00074 * @var null|int 00075 */ 00076 static protected $filePermission = null; 00077 00078 /** 00079 * Array of eZINI instances 00080 * 00081 * @var array(eZINI) 00082 */ 00083 static protected $instances = array(); 00084 00085 /** 00086 * Contains whether INI cache is globally enabled. 00087 * 00088 * @var bool 00089 */ 00090 static protected $cacheEnabled = true; 00091 00092 /** 00093 * Contains whether internals debugging is enabled. 00094 * 00095 * @var bool 00096 */ 00097 static protected $debugEnabled = false; 00098 00099 /** 00100 * Contains whether textcodec conversion is enabled. 00101 * 00102 * @var bool 00103 */ 00104 static protected $textCodecEnabled = true; 00105 00106 /** 00107 * Initialization of eZINI object 00108 * 00109 * Enter description here ... 00110 * @param string $fileName 00111 * @param string $rootDir 00112 * @param null|bool $useTextCodec 00113 * @param null|bool $useCache 00114 * @param null|bool $useLocalOverrides 00115 * @param bool $directAccess 00116 * @param bool $addArrayDefinition 00117 * @param bool $load @since 4.5 Lets you disable automatic loading of ini values in 00118 * cases where changes on instance will be done first. 00119 */ 00120 function eZINI( $fileName = 'site.ini', $rootDir = '', $useTextCodec = null, $useCache = null, $useLocalOverrides = null, $directAccess = false, $addArrayDefinition = false, $load = true ) 00121 { 00122 $this->Charset = 'utf8'; 00123 if ( $fileName == '' ) 00124 $fileName = 'site.ini'; 00125 if ( $rootDir !== false && $rootDir == '' ) 00126 $rootDir = 'settings'; 00127 if ( $useCache === null ) 00128 $useCache = self::isCacheEnabled(); 00129 if ( eZINI::isNoCacheAdviced() ) 00130 { 00131 $useCache = false; 00132 } 00133 if ( $useTextCodec === null ) 00134 $useTextCodec = self::isTextCodecEnabled(); 00135 00136 $this->UseTextCodec = $useTextCodec; 00137 $this->Codec = null; 00138 $this->FileName = $fileName; 00139 $this->RootDir = $rootDir; 00140 $this->UseCache = $useCache; 00141 $this->DirectAccess = $directAccess; 00142 $this->UseLocalOverrides = $useLocalOverrides; 00143 $this->AddArrayDefinition = $addArrayDefinition; 00144 00145 if ( self::$checkFileMtime === null ) 00146 { 00147 if ( defined('EZP_INI_FILEMTIME_CHECK') ) 00148 self::$checkFileMtime = EZP_INI_FILEMTIME_CHECK; 00149 else 00150 self::$checkFileMtime = true; 00151 } 00152 00153 if ( self::$GlobalOverrideDirArray === null ) 00154 { 00155 self::$GlobalOverrideDirArray = self::defaultOverrideDirs(); 00156 } 00157 00158 if ( $this->UseLocalOverrides == true ) 00159 { 00160 $this->LocalOverrideDirArray = self::$GlobalOverrideDirArray; 00161 } 00162 00163 if ( self::$filePermission === null ) 00164 { 00165 if ( defined( 'EZP_INI_FILE_PERMISSION' ) ) 00166 self::$filePermission = EZP_INI_FILE_PERMISSION; 00167 else 00168 self::$filePermission = 0666; 00169 } 00170 00171 if ( $load ) 00172 $this->load(); 00173 } 00174 00175 /*! 00176 \return the filename. 00177 */ 00178 function filename() 00179 { 00180 return $this->FileName; 00181 } 00182 00183 /** 00184 * Returns whether INI cache is enabled globally, by default it is true. 00185 * 00186 * @see setIsCacheEnabled(). 00187 * 00188 * @return bool 00189 */ 00190 static function isCacheEnabled() 00191 { 00192 return self::$cacheEnabled; 00193 } 00194 00195 /*! 00196 \return true if cache is not adviced to be used. 00197 \note The no-cache-adviced flag might not be modified in time for site.ini and some other important files to be affected. 00198 */ 00199 static function isNoCacheAdviced() 00200 { 00201 if ( !isset( $GLOBALS['eZSiteBasics'] ) ) 00202 return false; 00203 $siteBasics = $GLOBALS['eZSiteBasics']; 00204 if ( !isset( $siteBasics['no-cache-adviced'] ) ) 00205 return false; 00206 return $siteBasics['no-cache-adviced']; 00207 } 00208 00209 /** 00210 * Sets whether caching is enabled for INI files or not. This setting is global 00211 * and can be overriden in the instance() function. 00212 * 00213 * @see isCacheEnabled(). 00214 * 00215 * @param bool $enabled 00216 */ 00217 static function setIsCacheEnabled( $enabled ) 00218 { 00219 self::$cacheEnabled = (bool)$enabled; 00220 } 00221 00222 /** 00223 * Returns whether debugging of internals is enabled. 00224 * 00225 * This will display which files are loaded an when cache files are created. 00226 * 00227 * @see setIsDebugEnabled() 00228 * 00229 * @return bool 00230 */ 00231 static function isDebugEnabled() 00232 { 00233 return self::$debugEnabled; 00234 } 00235 00236 /** 00237 * Sets whether internal debugging is enabled or not. This setting is global 00238 * and can be overriden in the instance() function. 00239 * 00240 * @see isDebugEnabled(). 00241 * 00242 * @param bool $enabled 00243 */ 00244 static function setIsDebugEnabled( $enabled ) 00245 { 00246 self::$debugEnabled = (bool)$enabled; 00247 } 00248 00249 /** 00250 * Returns whether textcodecs is to be used, this will use the eZTextCodec 00251 * class in the eZI18N library for text conversion. 00252 * 00253 * @see setIsTextCodecEnabled() 00254 * 00255 * @return bool 00256 */ 00257 static function isTextCodecEnabled() 00258 { 00259 return self::$textCodecEnabled; 00260 } 00261 00262 /** 00263 * Sets whether textcodec conversion is enabled or not. 00264 * 00265 * @see isTextCodecEnabled(). 00266 * 00267 * @param bool $enabled 00268 */ 00269 static function setIsTextCodecEnabled( $enabled ) 00270 { 00271 self::$textCodecEnabled = (bool)$enabled; 00272 } 00273 00274 /** 00275 * Check whether a specified parameter in a specified section is set in a specified file 00276 * 00277 * @deprecated Since 4.4 00278 * @param string fileName file name (optional) 00279 * @param string rootDir directory (optional) 00280 * @param string section section name 00281 * @param string parameter parameter name 00282 * @return bool True if the the parameter is set. 00283 */ 00284 static function parameterSet( $fileName = 'site.ini', $rootDir = 'settings', &$section, &$parameter ) 00285 { 00286 if ( !eZINI::exists( $fileName, $rootDir ) ) 00287 return false; 00288 00289 $iniInstance = eZINI::instance( $fileName, $rootDir, null, null, null, true ); 00290 return $iniInstance->hasVariable( $section, $parameter ); 00291 } 00292 00293 /*! 00294 \static 00295 \return true if the INI file \a $fileName exists in the root dir \a $rootDir. 00296 $fileName defaults to site.ini and rootDir to settings. 00297 */ 00298 static function exists( $fileName = "site.ini", $rootDir = "settings" ) 00299 { 00300 if ( $fileName == "" ) 00301 $fileName = "site.ini"; 00302 if ( $rootDir == "" ) 00303 $rootDir = "settings"; 00304 if ( file_exists( $rootDir . '/' . $fileName ) ) 00305 return true; 00306 else if ( file_exists( $rootDir . '/' . $fileName . '.append.php' ) ) 00307 return true; 00308 else if ( file_exists( $rootDir . '/' . $fileName . '.append' ) ) 00309 return true; 00310 return false; 00311 } 00312 00313 /** 00314 * Tries to load the ini file specified in the constructor or instance() function. 00315 * If cache files should be used and a cache file is found it loads that instead. 00316 * 00317 * @param bool $reset Reset ini values on instance 00318 */ 00319 public function load( $reset = true ) 00320 { 00321 if ( $this->UseCache ) 00322 { 00323 $this->loadCache( $reset ); 00324 } 00325 else 00326 { 00327 $this->parse( false, false, $reset ); 00328 } 00329 } 00330 00331 /** 00332 * Tries to load the ini file placement specified in the constructor or instance() function. 00333 * If cache files should be used and a cache file is found it loads that instead. 00334 * 00335 * @param bool $reset Reset ini values on instance 00336 */ 00337 public function loadPlacement( $reset = true ) 00338 { 00339 if ( $this->UseCache ) 00340 { 00341 $this->loadCache( $reset, true ); 00342 } 00343 else 00344 { 00345 $this->parse( false, false, $reset, true ); 00346 } 00347 } 00348 00349 /*! 00350 \private 00351 Looks trough all known settings and override folders to find relevant INI files. 00352 The result is a list with expanded paths to the files. 00353 \return the expanded file list. 00354 */ 00355 function findInputFiles( &$inputFiles, &$iniFile ) 00356 { 00357 if ( $this->RootDir !== false ) 00358 $iniFile = eZDir::path( array( $this->RootDir, $this->FileName ) ); 00359 else 00360 $iniFile = eZDir::path( array( $this->FileName ) ); 00361 00362 $inputFiles = array(); 00363 00364 if ( $this->FileName === 'override.ini' ) 00365 { 00366 eZExtension::prependExtensionSiteAccesses( false, $this, true, false, false ); 00367 } 00368 00369 if ( file_exists( $iniFile ) ) 00370 $inputFiles[] = $iniFile; 00371 00372 // try the same file name with '.append.php' replace with '.append' 00373 if ( strpos($iniFile, '.append.php') !== false && preg_match('#^(.+.append).php$#i', $iniFile, $matches ) && file_exists( $matches[1] ) ) 00374 $inputFiles[] = $matches[1]; 00375 00376 if ( strpos($iniFile, '.php') === false && file_exists ( $iniFile . '.php' ) ) 00377 $inputFiles[] = $iniFile . '.php'; 00378 00379 if ( $this->DirectAccess ) 00380 { 00381 if ( file_exists ( $iniFile . '.append' ) ) 00382 { 00383 // recursion eZDebug::writeStrict( "INI files with *.ini.append suffix is DEPRECATED, use *.ini or *.ini.append.php instead: $iniFile.append", __METHOD__ ); 00384 $inputFiles[] = $iniFile . '.append'; 00385 } 00386 00387 if ( file_exists ( $iniFile . '.append.php' ) ) 00388 $inputFiles[] = $iniFile . '.append.php'; 00389 } 00390 else 00391 { 00392 $overrideDirs = $this->overrideDirs(); 00393 $fileName = $this->FileName; 00394 $rootDir = $this->RootDir; 00395 foreach ( $overrideDirs as $overrideDirItem ) 00396 { 00397 $overrideDir = $overrideDirItem[0]; 00398 $isGlobal = $overrideDirItem[1]; 00399 if ( $isGlobal ) 00400 $overrideFile = eZDir::path( array( $overrideDir, $fileName ) ); 00401 else 00402 $overrideFile = eZDir::path( array( $rootDir, $overrideDir, $fileName ) ); 00403 00404 if ( file_exists( $overrideFile . '.php' ) ) 00405 { 00406 // recursion eZDebug::writeStrict( "INI files with *.ini.php suffix is DEPRECATED, use *.ini or *.ini.append.php instead: $overrideFile.php", __METHOD__ ); 00407 $inputFiles[] = $overrideFile . '.php'; 00408 } 00409 00410 if ( file_exists( $overrideFile ) ) 00411 { 00412 $inputFiles[] = $overrideFile; 00413 } 00414 00415 if ( file_exists( $overrideFile . '.append.php' ) ) 00416 { 00417 $inputFiles[] = $overrideFile . '.append.php'; 00418 } 00419 00420 if ( file_exists( $overrideFile . '.append' ) ) 00421 { 00422 // recursion eZDebug::writeStrict( "INI files with *.ini.append suffix is DEPRECATED, use *.ini or *.ini.append.php instead: $overrideFile.append", __METHOD__ ); 00423 $inputFiles[] = $overrideFile . '.append'; 00424 } 00425 } 00426 } 00427 } 00428 00429 /*! 00430 \protected 00431 Generates cache name for loadCache 00432 */ 00433 protected function cacheFileName( $placement = false ) 00434 { 00435 $cacheFileName = $this->FileName . '-' . $this->RootDir . '-' . $this->DirectAccess; 00436 00437 if ( !$this->DirectAccess ) 00438 { 00439 $cacheFileName .= '-' . serialize( $this->overrideDirs() ); 00440 } 00441 if ( $this->UseTextCodec ) 00442 { 00443 $cacheFileName .= '-' . eZTextCodec::internalCharset(); 00444 } 00445 if ( $placement ) 00446 { 00447 $cacheFileName .= '-placement:' . $placement; 00448 } 00449 $filePreFix = explode( '.', $this->FileName); 00450 return $filePreFix[0] . '-' . md5( $cacheFileName ) . '.php'; 00451 } 00452 00453 /** 00454 * Will load a cached version of the ini file if it exists, 00455 * if not it will parse the original file and create the cache file. 00456 * 00457 * @access protected 00458 * @internal Please use {@link eZINI::load()} or {@link eZINI::loadPlacement()} 00459 * @param bool $reset Reset ini values on instance 00460 * @param bool $placement Load cache for placment info, not the ini values themself. 00461 */ 00462 function loadCache( $reset = true, $placement = false ) 00463 { 00464 eZDebug::accumulatorStart( 'ini', 'Ini load', 'Load cache' ); 00465 if ( $reset ) 00466 $this->reset(); 00467 $cachedDir = self::CONFIG_CACHE_DIR; 00468 00469 $fileName = $this->cacheFileName( $placement ); 00470 $cachedFile = $cachedDir . $fileName; 00471 if ( $placement ) 00472 { 00473 $this->PlacementCacheFile = $cachedFile; 00474 } 00475 else 00476 { 00477 $this->CacheFile = $cachedFile; 00478 } 00479 00480 $data = false;// this will contain cache data if cache data is valid 00481 if ( file_exists( $cachedFile ) ) 00482 { 00483 if ( self::isDebugEnabled() ) 00484 eZDebug::writeNotice( "Loading cache '$cachedFile' for file '" . $this->FileName . "'", __METHOD__ ); 00485 00486 include( $cachedFile ); 00487 00488 if ( !isset( $data['rev'] ) || $data['rev'] != eZINI::CONFIG_CACHE_REV ) 00489 { 00490 if ( self::isDebugEnabled() ) 00491 eZDebug::writeNotice( "Old structure in cache file used, recreating '$cachedFile' to new structure", __METHOD__ ); 00492 $data = false; 00493 $this->reset(); 00494 } 00495 else if ( self::$checkFileMtime === true || self::$checkFileMtime === $this->FileName ) 00496 { 00497 eZDebug::accumulatorStart( 'ini_check_mtime', 'Ini load', 'Check MTime' ); 00498 $currentTime = time(); 00499 $cacheCreatedTime = strtotime( $data['created'] ); 00500 $iniFile = $data['file'];// used by findInputFiles further down 00501 $inputFiles = $data['files']; 00502 foreach ( $inputFiles as $inputFile ) 00503 { 00504 $fileTime = file_exists( $inputFile ) ? filemtime( $inputFile ) : false; 00505 if ( $fileTime === false )// Refresh cache & input files if file is gone 00506 { 00507 unset( $inputFiles ); 00508 $data = false; 00509 $this->reset(); 00510 break; 00511 } 00512 else if ( $fileTime > $currentTime ) 00513 { 00514 eZDebug::writeError( 'Input file "' . $inputFile . '" has a timestamp higher then current time, ignoring to avoid infinite recursion!', __METHOD__ ); 00515 } 00516 else if ( $fileTime > $cacheCreatedTime )// Refresh cache if file has been changed 00517 { 00518 $data = false; 00519 $this->reset(); 00520 break; 00521 } 00522 } 00523 eZDebug::accumulatorStop( 'ini_check_mtime' ); 00524 } 00525 } 00526 00527 if ( $data )// if we have cache data on this point, use it 00528 { 00529 $this->Charset = $data['charset']; 00530 $this->ModifiedBlockValues = array(); 00531 if ( $placement ) 00532 $this->BlockValuesPlacement = $data['val']; 00533 else 00534 $this->BlockValues = $data['val']; 00535 unset( $data ); 00536 } 00537 else 00538 { 00539 if ( !isset( $inputFiles ) )// use $inputFiles from cache if defined 00540 { 00541 eZDebug::accumulatorStart( 'ini_find_files', 'Ini load', 'Find INI Files' ); 00542 $this->findInputFiles( $inputFiles, $iniFile ); 00543 eZDebug::accumulatorStop( 'ini_find_files' ); 00544 if ( count( $inputFiles ) === 0 ) 00545 { 00546 eZDebug::accumulatorStop( 'ini' ); 00547 return false; 00548 } 00549 } 00550 00551 eZDebug::accumulatorStart( 'ini_files_parse', 'Ini load', 'Parse' ); 00552 $this->parse( $inputFiles, $iniFile, false, $placement ); 00553 eZDebug::accumulatorStop( 'ini_files_parse' ); 00554 eZDebug::accumulatorStart( 'ini_files_save', 'Ini load', 'Save Cache' ); 00555 $cacheSaved = $this->saveCache( $cachedDir, $cachedFile, $placement ? $this->BlockValuesPlacement : $this->BlockValues, $inputFiles, $iniFile ); 00556 eZDebug::accumulatorStop( 'ini_files_save' ); 00557 00558 if ( $cacheSaved ) 00559 { 00560 // Write log message to storage.log 00561 eZLog::writeStorageLog( $fileName, $cachedDir ); 00562 } 00563 } 00564 00565 eZDebug::accumulatorStop( 'ini' ); 00566 } 00567 00568 /** 00569 * Stores the content of the INI object to the cache file \a $cachedFile. 00570 * 00571 * @param string $cachedDir Cache dir, usually "var/cache/ini/" 00572 * @param string $cachedFile Name of cache file as returned by cacheFileName() 00573 * @param array $data Configuration data as an associative array structure 00574 * @param array $inputFiles List of input files used as basis for cache (for use in load cache to check mtime) 00575 * @param string $iniFile Ini file path string returned by findInputFiles() for main ini file 00576 * @return bool 00577 */ 00578 protected function saveCache( $cachedDir, $cachedFile, array $data, array $inputFiles, $iniFile ) 00579 { 00580 if ( !file_exists( $cachedDir ) ) 00581 { 00582 if ( !eZDir::mkdir( $cachedDir, 0777, true ) ) 00583 { 00584 eZDebug::writeError( "Couldn't create cache directory $cachedDir, perhaps wrong permissions", __METHOD__ ); 00585 return false; 00586 } 00587 } 00588 00589 // Save the data to a temp cached file 00590 $tmpCacheFile = $cachedFile . '_' . substr( md5( mt_rand() ), 0, 8 ); 00591 $fp = @fopen( $tmpCacheFile, "w" ); 00592 if ( $fp === false ) 00593 { 00594 eZDebug::writeError( "Couldn't create cache file '$cachedFile', perhaps wrong permissions?", __METHOD__ ); 00595 return false; 00596 } 00597 00598 // Write cache data as a php structure with some meta information for use while reading cache 00599 fwrite( $fp, "<?php\n// This is a auto generated ini cache file, time created:" . date( DATE_RFC822 ) . "\n" ); 00600 00601 fwrite( $fp, "\$data = array(\n" ); 00602 fwrite( $fp, "'rev' => " . eZINI::CONFIG_CACHE_REV . ",\n" ); 00603 fwrite( $fp, "'created' => '" . date('c') . "',\n" ); 00604 00605 if ( $this->Codec ) 00606 fwrite( $fp, "'charset' => \"".$this->Codec->RequestedOutputCharsetCode."\",\n" ); 00607 else 00608 fwrite( $fp, "'charset' => \"$this->Charset\",\n" ); 00609 00610 fwrite( $fp, "'files' => " . preg_replace( "@\n[\s]+@", '', var_export( $inputFiles, true ) ) . ",\n" ); 00611 fwrite( $fp, "'file' => '$iniFile',\n" ); 00612 00613 fwrite( $fp, "'val' => " . preg_replace( "@\n[\s]+@", '', var_export( $data, true ) ) . ");" ); 00614 fwrite( $fp, "\n?>" ); 00615 fclose( $fp ); 00616 00617 // Rename cache temp file to final desitination and set permissions 00618 if( eZFile::rename( $tmpCacheFile, $cachedFile ) ) 00619 { 00620 chmod( $cachedFile, self::$filePermission ); 00621 } 00622 00623 00624 if ( self::isDebugEnabled() ) 00625 eZDebug::writeNotice( "Wrote cache file '$cachedFile'", __METHOD__ ); 00626 00627 return true; 00628 } 00629 00630 /*! 00631 \private 00632 Parses either the override ini file or the standard file and then the append 00633 override file if it exists. 00634 */ 00635 function parse( $inputFiles = false, $iniFile = false, $reset = true, $placement = false ) 00636 { 00637 if ( $inputFiles === false or 00638 $iniFile === false ) 00639 { 00640 eZDebug::accumulatorStart( 'ini_parse_find_files', 'Ini load', 'Find INI Files2' ); 00641 $this->findInputFiles( $inputFiles, $iniFile ); 00642 eZDebug::accumulatorStop( 'ini_parse_find_files' ); 00643 } 00644 00645 if ( $reset ) 00646 $this->reset(); 00647 00648 foreach ( $inputFiles as $inputFile ) 00649 { 00650 if ( file_exists( $inputFile ) ) 00651 { 00652 $this->parseFile( $inputFile, $placement ); 00653 } 00654 } 00655 } 00656 00657 /*! 00658 \private 00659 Will parse the INI file and store the variables in the variable $this->BlockValues 00660 */ 00661 function parseFile( $file, $placement = false ) 00662 { 00663 if ( self::isDebugEnabled() ) 00664 eZDebug::writeNotice( "Parsing file '$file'", __METHOD__ ); 00665 00666 $contents = file_get_contents( $file ); 00667 if ( $contents === false ) 00668 { 00669 eZDebug::writeError( "Failed opening file '$file' for reading", __METHOD__ ); 00670 return false; 00671 } 00672 00673 $contents = str_replace( "\r", '', $contents ); 00674 $endOfLine = strpos( $contents, "\n" ); 00675 $line = substr( $contents, 0, $endOfLine ); 00676 00677 $currentBlock = ""; 00678 if ( $line ) 00679 { 00680 // check for charset 00681 if ( preg_match( "/#\?ini(.+)\?/", $line, $ini_arr ) ) 00682 { 00683 $args = explode( " ", trim( $ini_arr[1] ) ); 00684 foreach ( $args as $arg ) 00685 { 00686 $vars = explode( '=', trim( $arg ) ); 00687 if ( $vars[0] == "charset" ) 00688 { 00689 $val = $vars[1]; 00690 if ( $val[0] == '"' and 00691 strlen( $val ) > 0 and 00692 $val[strlen($val)-1] == '"' ) 00693 $val = substr( $val, 1, strlen($val) - 2 ); 00694 $this->Charset = $val; 00695 } 00696 } 00697 } 00698 } 00699 00700 unset( $this->Codec ); 00701 if ( $this->UseTextCodec ) 00702 { 00703 $this->Codec = eZTextCodec::instance( $this->Charset, false, false ); 00704 00705 if ( $this->Codec ) 00706 { 00707 eZDebug::accumulatorStart( 'ini_conversion', false, 'INI string conversion' ); 00708 $contents = $this->Codec->convertString( $contents ); 00709 eZDebug::accumulatorStop( 'ini_conversion' ); 00710 } 00711 } 00712 else 00713 $this->Codec = null; 00714 00715 foreach ( explode( "\n", $contents ) as $line ) 00716 { 00717 if ( $line == '' or $line[0] == '#' ) 00718 continue; 00719 if ( preg_match( "/^(.+)##.*/", $line, $regs ) ) 00720 $line = $regs[1]; 00721 if ( trim( $line ) == '' ) 00722 continue; 00723 // check for new block 00724 if ( preg_match("#^\[(.+)\]\s*$#", $line, $newBlockNameArray ) ) 00725 { 00726 $newBlockName = trim( $newBlockNameArray[1] ); 00727 $currentBlock = $newBlockName; 00728 continue; 00729 } 00730 00731 // check for variable 00732 if ( preg_match("#^([\w_*@-]+)\\[\\]$#", $line, $valueArray ) ) 00733 { 00734 $varName = trim( $valueArray[1] ); 00735 00736 if ( $placement ) 00737 { 00738 if ( isset( $this->BlockValuesPlacement[$currentBlock][$varName] ) && 00739 !is_array( $this->BlockValuesPlacement[$currentBlock][$varName] ) ) 00740 { 00741 eZDebug::writeError( "Wrong operation on the ini setting array '$varName'", __METHOD__ ); 00742 continue; 00743 } 00744 00745 $this->BlockValuesPlacement[$currentBlock][$varName][] = $file; 00746 } 00747 else 00748 { 00749 $this->BlockValues[$currentBlock][$varName] = array(); 00750 00751 // In direct access mode we create empty elements at the beginning of an array 00752 // in case it is redefined in this ini file. So when we will save it, definition 00753 // will be created as well. 00754 if ( $this->AddArrayDefinition ) 00755 { 00756 $this->BlockValues[$currentBlock][$varName][] = ""; 00757 } 00758 } 00759 } 00760 else if ( preg_match("#^([\w_*@-]+)(\\[([^\\]]*)\\])?=(.*)$#", $line, $valueArray ) ) 00761 { 00762 $varName = trim( $valueArray[1] ); 00763 $varValue = $valueArray[4]; 00764 00765 if ( $valueArray[2] ) 00766 { 00767 if ( $valueArray[3] ) 00768 { 00769 $keyName = $valueArray[3]; 00770 if ( $placement ) 00771 { 00772 $this->BlockValuesPlacement[$currentBlock][$varName][$keyName] = $file; 00773 } 00774 else 00775 { 00776 $this->BlockValues[$currentBlock][$varName][$keyName] = $varValue; 00777 } 00778 } 00779 else 00780 { 00781 if ( $placement ) 00782 { 00783 $this->BlockValuesPlacement[$currentBlock][$varName][] = $file; 00784 } 00785 else 00786 { 00787 $this->BlockValues[$currentBlock][$varName][] = $varValue; 00788 } 00789 } 00790 } 00791 else 00792 { 00793 if ( $placement ) 00794 { 00795 $this->BlockValuesPlacement[$currentBlock][$varName] = $file; 00796 } 00797 else 00798 { 00799 $this->BlockValues[$currentBlock][$varName] = $varValue; 00800 } 00801 } 00802 } 00803 } 00804 } 00805 00806 /*! 00807 \removes the cache file if it exists. 00808 */ 00809 function resetCache() 00810 { 00811 if ( $this->CacheFile && file_exists( $this->CacheFile ) ) 00812 unlink( $this->CacheFile ); 00813 if ( $this->PlacementCacheFile && file_exists( $this->PlacementCacheFile ) ) 00814 unlink( $this->PlacementCacheFile ); 00815 } 00816 00817 00818 /*! 00819 Saves the file to disk. 00820 If filename is given the file is saved with that name if not the current name is used. 00821 If \a $useOverride is true then the file will be placed in the override directory, 00822 if \a $useOverride is "append" it will append ".append" to the filename. 00823 */ 00824 function save( $fileName = false, $suffix = false, $useOverride = false, 00825 $onlyModified = false, $useRootDir = true, $resetArrays = false, 00826 $encapsulateInPHP = true ) 00827 { 00828 $lineSeparator = eZSys::lineSeparator(); 00829 $pathArray = array(); 00830 $dirArray = array(); 00831 if ( $fileName === false ) 00832 $fileName = $this->FileName; 00833 if ( $useRootDir === true ) 00834 { 00835 $pathArray[] = $this->RootDir; 00836 $dirArray[] = $this->RootDir; 00837 } 00838 else if ( is_string( $useRootDir ) ) 00839 { 00840 $pathArray[] = $useRootDir; 00841 $dirArray[] = $useRootDir; 00842 } 00843 if ( $useOverride ) 00844 { 00845 $pathArray[] = 'override'; 00846 $dirArray[] = 'override'; 00847 } 00848 if ( $useOverride === 'append' ) 00849 $fileName .= '.append'; 00850 if ( $suffix !== false ) 00851 $fileName .= $suffix; 00852 00853 /* Try to guess which filename would fit better: 'xxx.apend' or 'xxx.append.php'. 00854 * We choose 'xxx.append.php' in all cases except when 00855 * 'xxx.append' exists already and 'xxx.append.php' does not exist. 00856 */ 00857 if( strstr( $fileName, '.append' ) ) 00858 { 00859 $fnAppend = preg_replace( '#\.php$#', '', $fileName ); 00860 $fnAppendPhp = $fnAppend.'.php'; 00861 $fpAppend = eZDir::path( array_merge( $pathArray, array( $fnAppend ) ) ); 00862 $fpAppendPhp = eZDir::path( array_merge( $pathArray, array( $fnAppendPhp ) ) ); 00863 $fileName = ( file_exists( $fpAppend ) && !file_exists( $fpAppendPhp ) ) 00864 ? $fnAppend : $fnAppendPhp; 00865 } 00866 00867 $originalFileName = $fileName; 00868 $backupFileName = $originalFileName . eZSys::backupFilename(); 00869 $fileName .= '.tmp'; 00870 00871 $dirPath = eZDir::path( $dirArray ); 00872 if ( !file_exists( $dirPath ) ) 00873 eZDir::mkdir( $dirPath, octdec( '777' ), true ); 00874 00875 $filePath = eZDir::path( array_merge( $pathArray, array( $fileName ) ) ); 00876 $originalFilePath = eZDir::path( array_merge( $pathArray, array( $originalFileName ) ) ); 00877 $backupFilePath = eZDir::path( array_merge( $pathArray, array( $backupFileName ) ) ); 00878 00879 $fp = @fopen( $filePath, "w+"); 00880 if ( !$fp ) 00881 { 00882 eZDebug::writeError( "Failed opening file '$filePath' for writing", __METHOD__ ); 00883 return false; 00884 } 00885 $writeOK = true; 00886 $written = 0; 00887 00888 $charset = $this->Codec ? $this->Codec->RequestedOutputCharsetCode : $this->Charset; 00889 if ( $encapsulateInPHP ) 00890 { 00891 $written = fwrite( $fp, "<?php /* #?ini charset=\"$charset\"?$lineSeparator$lineSeparator" ); 00892 } 00893 else 00894 { 00895 $written = fwrite( $fp, "#?ini charset=\"$charset\"?$lineSeparator$lineSeparator" ); 00896 } 00897 00898 if ( $written === false ) 00899 $writeOK = false; 00900 $i = 0; 00901 if ( $writeOK ) 00902 { 00903 foreach( array_keys( $this->BlockValues ) as $blockName ) 00904 { 00905 if ( $onlyModified ) 00906 { 00907 $groupHasModified = false; 00908 if ( isset( $this->ModifiedBlockValues[$blockName] ) ) 00909 { 00910 foreach ( $this->ModifiedBlockValues[$blockName] as $modifiedValue ) 00911 { 00912 if ( $modifiedValue ) 00913 $groupHasModified = true; 00914 } 00915 } 00916 if ( !$groupHasModified ) 00917 continue; 00918 } 00919 $written = 0; 00920 if ( $i > 0 ) 00921 $written = fwrite( $fp, "$lineSeparator" ); 00922 if ( $written === false ) 00923 { 00924 $writeOK = false; 00925 break; 00926 } 00927 $written = fwrite( $fp, "[$blockName]$lineSeparator" ); 00928 if ( $written === false ) 00929 { 00930 $writeOK = false; 00931 break; 00932 } 00933 foreach( array_keys( $this->BlockValues[$blockName] ) as $blockVariable ) 00934 { 00935 if ( $onlyModified ) 00936 { 00937 if ( !isset( $this->ModifiedBlockValues[$blockName][$blockVariable] ) or 00938 !$this->ModifiedBlockValues[$blockName][$blockVariable] ) 00939 continue; 00940 } 00941 $varKey = $blockVariable; 00942 $varValue = $this->BlockValues[$blockName][$blockVariable]; 00943 if ( is_array( $varValue ) ) 00944 { 00945 if ( count( $varValue ) > 0 ) 00946 { 00947 $customResetArray = ( isset( $this->BlockValues[$blockName]['ResetArrays'] ) and 00948 $this->BlockValues[$blockName]['ResetArrays'] == 'false' ) 00949 ? true 00950 : false; 00951 if ( $resetArrays and !$customResetArray ) 00952 $written = fwrite( $fp, "$varKey" . "[]$lineSeparator" ); 00953 foreach ( $varValue as $varArrayKey => $varArrayValue ) 00954 { 00955 if ( is_string( $varArrayKey ) ) 00956 $written = fwrite( $fp, "$varKey" . "[$varArrayKey]=$varArrayValue$lineSeparator" ); 00957 else 00958 { 00959 if ( $varArrayValue == NULL ) 00960 $written = fwrite( $fp, "$varKey" . "[]$lineSeparator" ); 00961 else 00962 $written = fwrite( $fp, "$varKey" . "[]=$varArrayValue$lineSeparator" ); 00963 } 00964 if ( $written === false ) 00965 break; 00966 } 00967 } 00968 else 00969 $written = fwrite( $fp, "$varKey" . "[]$lineSeparator" ); 00970 } 00971 else 00972 { 00973 $written = fwrite( $fp, "$varKey=$varValue$lineSeparator" ); 00974 } 00975 if ( $written === false ) 00976 { 00977 $writeOK = false; 00978 break; 00979 } 00980 } 00981 if ( !$writeOK ) 00982 break; 00983 ++$i; 00984 } 00985 } 00986 if ( $writeOK ) 00987 { 00988 if ( $encapsulateInPHP ) 00989 { 00990 $written = fwrite( $fp, "*/ ?>" ); 00991 00992 if ( $written === false ) 00993 $writeOK = false; 00994 } 00995 } 00996 @fclose( $fp ); 00997 if ( !$writeOK ) 00998 { 00999 unlink( $filePath ); 01000 return false; 01001 } 01002 01003 chmod( $filePath, self::$filePermission ); 01004 01005 if ( file_exists( $backupFilePath ) ) 01006 unlink( $backupFilePath ); 01007 if ( file_exists( $originalFilePath ) ) 01008 { 01009 if ( !rename( $originalFilePath, $backupFilePath ) ) 01010 return false; 01011 } 01012 if ( !rename( $filePath, $originalFilePath ) ) 01013 { 01014 rename( $backupFilePath, $originalFilePath ); 01015 return false; 01016 } 01017 01018 return true; 01019 } 01020 01021 /*! 01022 Removes all read data from .ini files. 01023 */ 01024 function reset() 01025 { 01026 $this->BlockValues = array(); 01027 $this->ModifiedBlockValues = array(); 01028 } 01029 01030 /*! 01031 \return the root directory from where all .ini and override files are read. 01032 01033 This is set by the instance() or eZINI() functions. 01034 */ 01035 function rootDir() 01036 { 01037 return $this->RootDir; 01038 } 01039 01040 /** 01041 * Return the override directories witch raw override dir data, or within a scope if $scope is set, 01042 * see {@link eZINI::defaultOverrideDirs()} for how the raw data looks like. 01043 * 01044 * @param string|null|false $scope See {@link eZINI::defaultOverrideDirs()} for possible scope values 01045 * If false then you'll get raw override dir structure, null (default) is a simplified 01046 * variant withouth scopes that is easy to iterate over. 01047 * @return array 01048 */ 01049 function overrideDirs( $scope = null ) 01050 { 01051 if ( $this->UseLocalOverrides == true ) 01052 $dirs =& $this->LocalOverrideDirArray; 01053 else 01054 $dirs =& self::$GlobalOverrideDirArray; 01055 01056 return self::overrideDirsByScope( $dirs, $scope ); 01057 } 01058 01059 /** 01060 * Return the global override directories witch raw override dir data, or within a scope if $scope is set, 01061 * see {@link eZINI::defaultOverrideDirs()} for how the raw data looks like. 01062 * 01063 * @param string|false|null $scope See {@link eZINI::defaultOverrideDirs()} for possible scope values 01064 * If false then you'll get raw override dir structure, null (default) is a simplified 01065 * variant withouth scopes that is easy to iterate over. 01066 * @return array 01067 */ 01068 public static function globalOverrideDirs( $scope = null ) 01069 { 01070 return self::overrideDirsByScope( self::$GlobalOverrideDirArray, $scope ); 01071 } 01072 01073 /** 01074 * Return the override directories witch raw override dir data, or within a scope if $scope is set, 01075 * see {@link eZINI::defaultOverrideDirs()} for how the raw data looks like. 01076 * 01077 * @param array $dirs Directories directly from internal raw structure (see above). 01078 * @param string|null|false $scope See {@link eZINI::defaultOverrideDirs()} for possible scope values 01079 * If false then you'll get raw override dir structure, null (default) is a simplified 01080 * variant withouth scopes that is easy to iterate over. 01081 * @return array 01082 */ 01083 protected static function overrideDirsByScope( array $dirs, $scope = null ) 01084 { 01085 if ( $scope !== null ) 01086 { 01087 if ( $scope === false ) 01088 return $dirs; 01089 if ( isset( $dirs[$scope] ) ) 01090 return $dirs[$scope]; 01091 eZDebug::writeWarning( "Undefined override dir scope: '$scope'", __METHOD__ ); 01092 } 01093 01094 return array_merge( $dirs['sa-extension'], $dirs['siteaccess'], $dirs['extension'], $dirs['override'] ); 01095 } 01096 01097 /** 01098 * Default override directories as raw array data 01099 * 01100 * @return array An associated array of associated arrays of arrays.. 01101 * First level keys are the scope and values are arrays 01102 * Second level keys are identifier (numberic if not defined by caller) and value is arrays 01103 * Third level contains (string) override dir, (bool) global flag if false then 01104 * relative to {@link $RootDir} and (string|false) optional identifier as used by 01105 * {@link eZINI::prependOverrideDir()} to match and replace values on. 01106 */ 01107 static public function defaultOverrideDirs() 01108 { 01109 static $def = array( 01110 'sa-extension' => array(), 01111 'siteaccess' => array(), 01112 'extension' => array(), 01113 'override' => array( array( 'override', false ) ) 01114 ); 01115 return $def; 01116 } 01117 01118 /** 01119 * Set the override directories witch raw override dir data, or within a scope if $scope is set, 01120 * see {@link eZINI::defaultOverrideDirs()} for how the raw data looks like. 01121 * 01122 * @param array $newDirs 01123 * @param string|false $scope See {@link eZINI::defaultOverrideDirs()} for possible scope values 01124 */ 01125 function setOverrideDirs( array $newDirs, $scope = false ) 01126 { 01127 if ( $this->UseLocalOverrides == true ) 01128 $dirs =& $this->LocalOverrideDirArray; 01129 else 01130 $dirs =& self::$GlobalOverrideDirArray; 01131 01132 if ( $scope === false ) 01133 $dirs = $newDirs; 01134 else if ( isset( $dirs[$scope] ) ) 01135 $dirs[$scope] = $newDirs; 01136 else 01137 eZDebug::writeWarning( "Undefined override dir scope: '$scope'", __METHOD__ ); 01138 01139 $this->CacheFile = false; 01140 } 01141 01142 /** 01143 * Reset the global override directories with data from {@link eZINI::defaultOverrideDirs()} 01144 */ 01145 static public function resetGlobalOverrideDirs() 01146 { 01147 self::$GlobalOverrideDirArray = self::defaultOverrideDirs(); 01148 } 01149 01150 /** 01151 * Reset the override directories with data from {@link eZINI::defaultOverrideDirs()} 01152 */ 01153 public function resetOverrideDirs() 01154 { 01155 $this->setOverrideDirs( self::defaultOverrideDirs() ); 01156 } 01157 01158 /** 01159 * Removes an override dir by identifier 01160 * See {@link eZINI::defaultOverrideDirs()} for how these parameters are used. 01161 * 01162 * @param string $identifier Will remove existing directory with identifier it it exists 01163 * @param string $scope By default 'extension' 01164 * @return bool True if new dir was appended, false if there was a $identifier match and a overwrite 01165 */ 01166 function removeOverrideDir( $identifier, $scope = 'extension' ) 01167 { 01168 if ( $this->UseLocalOverrides == true ) 01169 $dirs =& $this->LocalOverrideDirArray; 01170 else 01171 $dirs =& self::$GlobalOverrideDirArray; 01172 01173 if ( !$identifier || !is_string( $identifier ) ) 01174 { 01175 eZDebug::writeError( "\$identifier must be a string", __METHOD__ ); 01176 return false; 01177 } 01178 01179 if ( !isset( $dirs[$scope] ) ) 01180 { 01181 eZDebug::writeWarning( "Undefined override dir scope: '$scope'", __METHOD__ ); 01182 $scope = 'extension'; 01183 } 01184 01185 $overrideRemoved = false; 01186 if ( isset( $dirs[$scope][$identifier] ) ) 01187 { 01188 unset( $dirs[$scope][$identifier] ); 01189 $overrideRemoved = true; 01190 $this->CacheFile = false; 01191 } 01192 01193 return $overrideRemoved; 01194 } 01195 01196 /** 01197 * Prepends the override directory $dir to the override directory list. 01198 * Prepends override dir to 'extension' scope by default, bellow siteaccess and override settings. 01199 * See {@link eZINI::defaultOverrideDirs()} for how these parameters are used. 01200 * 01201 * @param string $dir 01202 * @param bool $globalDir 01203 * @param string|false $identifier Will overwrite existing directory with same identifier if set 01204 * @param string|null $scope 01205 * @return bool True if new dir was prepended, false if there was a $identifier match and a overwrite 01206 */ 01207 function prependOverrideDir( $dir, $globalDir = false, $identifier = false, $scope = null ) 01208 { 01209 if ( self::isDebugEnabled() ) 01210 eZDebug::writeNotice( "Prepending override dir '$dir'", "eZINI" ); 01211 01212 if ( $this->UseLocalOverrides == true ) 01213 $dirs =& $this->LocalOverrideDirArray; 01214 else 01215 $dirs =& self::$GlobalOverrideDirArray; 01216 01217 $scope = self::selectOverrideScope( $scope, $identifier, $dir, 'extension' ); 01218 01219 // Check if the override with the current identifier already exists 01220 $overrideOverwritten = false; 01221 if ( $identifier && isset( $dirs[$scope][$identifier] ) ) 01222 { 01223 $dirs[$scope][$identifier] = array( $dir, $globalDir ); 01224 $overrideOverwritten = true; 01225 } 01226 else 01227 { 01228 // array_merge() is to be used below instead of the "+" operator because the 01229 // position of elements in this mixed hash/list is important! 01230 if ( $identifier ) 01231 $dirs[$scope] = array_merge( array( $identifier => array( $dir, $globalDir ) ), $dirs[$scope] ); 01232 else 01233 $dirs[$scope] = array_merge( array( array( $dir, $globalDir ) ), $dirs[$scope] ); 01234 } 01235 01236 $this->CacheFile = false; 01237 return $overrideOverwritten === false; 01238 } 01239 01240 /** 01241 * Appends the override directory $dir to the override directory list. 01242 * Appends override dir to 'override' scope if scope is not defined, meaning above anything else. 01243 * See {@link eZINI::defaultOverrideDirs()} for how these parameters are used. 01244 * 01245 * @param string $dir 01246 * @param bool $globalDir 01247 * @param string|false $identifier Will overwrite existing directory with same identifier if set 01248 * @param string|null $scope 01249 * @return bool True if new dir was appended, false if there was a $identifier match and a overwrite 01250 */ 01251 function appendOverrideDir( $dir, $globalDir = false, $identifier = false, $scope = null ) 01252 { 01253 if ( self::isDebugEnabled() ) 01254 eZDebug::writeNotice( "Appending override dir '$dir'", __METHOD__ ); 01255 01256 if ( $this->UseLocalOverrides == true ) 01257 $dirs =& $this->LocalOverrideDirArray; 01258 else 01259 $dirs =& self::$GlobalOverrideDirArray; 01260 01261 $scope = self::selectOverrideScope( $scope, $identifier, $dir, 'override' ); 01262 01263 // Check if the override with the current identifier already exists 01264 $overrideOverwritten = false; 01265 if ( $identifier && isset( $dirs[$scope][$identifier] ) ) 01266 { 01267 $dirs[$scope][$identifier] = array( $dir, $globalDir ); 01268 $overrideOverwritten = true; 01269 } 01270 else 01271 { 01272 if ( $identifier ) 01273 $dirs[$scope][$identifier] = array( $dir, $globalDir ); 01274 else 01275 $dirs[$scope][] = array( $dir, $globalDir ); 01276 } 01277 01278 $this->CacheFile = false; 01279 return $overrideOverwritten === false; 01280 } 01281 01282 /** 01283 * Function to handle bc with code from pre 4.4 that does not know about scopes 01284 * 01285 * @since 4.4 01286 * @param string|null $scope 01287 * @param string $identifier 01288 * @param string $dir 01289 * @param string $default 01290 * @return string 01291 */ 01292 protected static function selectOverrideScope( $scope, $identifier, $dir, $default ) 01293 { 01294 if ( $scope !== null ) 01295 { 01296 $def = self::defaultOverrideDirs(); 01297 if ( isset( $def[$scope] ) ) 01298 return $scope; 01299 eZDebug::writeWarning( "Undefined override dir scope: '$scope' with dir: '$dir'", __METHOD__ ); 01300 } 01301 if ( $identifier === 'siteaccess' ) 01302 return 'siteaccess'; 01303 else if ( $identifier && strpos($identifier, 'extension:') === 0 ) 01304 return 'extension'; 01305 else if ( strpos($dir, 'siteaccess') !== false ) 01306 return 'siteaccess'; 01307 else if ( strpos($dir, 'extension') !== false ) 01308 return 'extension'; 01309 01310 eZDebug::writeStrict( "Could not figgure out INI scope for \$identifier: '$identifier' with \$dir: '$dir', falling back to '$default'", __METHOD__ ); 01311 return $default; 01312 } 01313 01314 /*! 01315 Reads a variable from the ini file and puts it in the parameter \a $variable. 01316 \note \a $variable is not modified if the variable does not exist 01317 */ 01318 function assign( $blockName, $varName, &$variable ) 01319 { 01320 if ( isset( $this->BlockValues[$blockName][$varName] ) ) 01321 $variable = $this->BlockValues[$blockName][$varName]; 01322 else 01323 return false; 01324 return true; 01325 } 01326 01327 /*! 01328 Reads a variable from the ini file. 01329 false is returned if the variable was not found. 01330 */ 01331 function variable( $blockName, $varName ) 01332 { 01333 if ( isset( $this->BlockValues[$blockName][$varName] ) ) 01334 return $this->BlockValues[$blockName][$varName]; 01335 else if ( !isset( $this->BlockValues[$blockName] ) ) 01336 eZDebug::writeError( "Undefined group: '$blockName' in " . $this->FileName, __METHOD__ ); 01337 else 01338 eZDebug::writeError( "Undefined variable: '$varName' in group '$blockName' in " . $this->FileName, __METHOD__ ); 01339 return false; 01340 } 01341 01342 /*! 01343 Reads multiple variables from the ini file. 01344 false is returned if the variable was not found. 01345 */ 01346 function variableMulti( $blockName, $varNames, $signatures = array() ) 01347 { 01348 $ret = array(); 01349 01350 if ( !isset( $this->BlockValues[$blockName] ) ) 01351 { 01352 eZDebug::writeError( "Undefined group: '$blockName' in " . $this->FileName, "eZINI" ); 01353 return false; 01354 } 01355 foreach ( $varNames as $key => $varName ) 01356 { 01357 if ( isset( $this->BlockValues[$blockName][$varName] ) ) 01358 { 01359 $ret[$key] = $this->BlockValues[$blockName][$varName]; 01360 01361 if ( isset( $signatures[$key] ) ) 01362 { 01363 switch ( $signatures[$key] ) 01364 { 01365 case 'enabled': 01366 $ret[$key] = $this->BlockValues[$blockName][$varName] == 'enabled'; 01367 break; 01368 } 01369 } 01370 } 01371 else 01372 { 01373 $ret[] = null; 01374 } 01375 } 01376 01377 return $ret; 01378 } 01379 01380 /*! 01381 Checks if a variable is set. Returns true if the variable exists, false if not. 01382 */ 01383 function hasVariable( $blockName, $varName ) 01384 { 01385 return isset( $this->BlockValues[$blockName][$varName] ); 01386 } 01387 01388 /*! 01389 Check if a block/section is set. Returns true if the section/block is set, false if not 01390 */ 01391 function hasSection( $sectionName ) 01392 { 01393 return isset( $this->BlockValues[$sectionName] ); 01394 } 01395 01396 /*! 01397 \return true if the variable \a $varName in group \a $blockName has been modified. 01398 */ 01399 function isVariableModified( $blockName, $varName ) 01400 { 01401 return ( isset( $this->ModifiedBlockValues[$blockName][$varName] ) and 01402 $this->ModifiedBlockValues[$blockName][$varName] ); 01403 } 01404 01405 /*! 01406 Reads a variable from the ini file. The variable 01407 will be returned as an array. ; is used as delimiter. 01408 */ 01409 function variableArray( $blockName, $varName ) 01410 { 01411 if ( isset( $this->BlockValues[$blockName][$varName] ) ) 01412 $ret = $this->BlockValues[$blockName][$varName]; 01413 else 01414 return false; 01415 01416 if ( is_array( $ret ) ) 01417 { 01418 $arr = array(); 01419 foreach ( $ret as $key => $retItem ) 01420 { 01421 $arr[$key] = explode( ';', $retItem ); 01422 } 01423 $ret = $arr; 01424 } 01425 else if ( $ret !== false ) 01426 { 01427 $ret = trim( $ret ) === '' ? array() : explode( ';', $ret ); 01428 } 01429 01430 return $ret; 01431 } 01432 01433 /*! 01434 Checks if group $blockName is set. Returns true if the group exists, false if not. 01435 */ 01436 function hasGroup( $blockName ) 01437 { 01438 return isSet( $this->BlockValues[$blockName] ); 01439 } 01440 01441 /*! 01442 Fetches a variable group and returns it as an associative array. 01443 */ 01444 function &group( $blockName ) 01445 { 01446 if ( !isset( $this->BlockValues[$blockName] ) ) 01447 { 01448 eZDebug::writeError( "Unknown group: '$blockName'", __METHOD__ ); 01449 $ret = null; 01450 return $ret; 01451 } 01452 $ret = $this->BlockValues[$blockName]; 01453 01454 return $ret; 01455 } 01456 01457 function isSettingReadOnly( $fileName = false, $blockName = false, $settingName = false ) 01458 { 01459 if ( !$this->readOnlySettingsCheck() ) 01460 return true; 01461 01462 $ini = eZINI::instance(); 01463 if ( !$ini->hasVariable( 'eZINISettings', 'ReadonlySettingList' ) ) 01464 return true; 01465 01466 $fileName = $fileName === false ? $ini->FileName : $fileName; 01467 $fileNameExploded = explode( '.', $fileName ); 01468 $realFileName = $fileNameExploded[0] . '.' . $fileNameExploded[1]; 01469 $blockName = $blockName === false ? '*' : $blockName; 01470 $settingName = $settingName === false ? '*' : $settingName; 01471 $currentSetting = $realFileName . '/' . $blockName . '/' . $settingName; 01472 01473 $settingList = $ini->variable( 'eZINISettings', 'ReadonlySettingList' ); 01474 $settingList[] = 'site.ini/eZINISettings/*'; 01475 01476 $result = !( in_array( $realFileName . '/*' , $settingList ) or 01477 in_array( $realFileName . '/' . $blockName . '/*' , $settingList ) or 01478 in_array( $realFileName . '/' . $blockName . '/' . $settingName , $settingList ) ); 01479 01480 return $result; 01481 } 01482 /*! 01483 Removes the group and all it's settings from the .ini file 01484 */ 01485 function removeGroup( $blockName ) 01486 { 01487 unset( $this->BlockValues[$blockName] ); 01488 unset( $this->BlockValuesPlacement[$blockName] ); 01489 } 01490 01491 function removeSetting( $blockName, $settingName ) 01492 { 01493 unset( $this->BlockValues[$blockName][$settingName] ); 01494 unset( $this->BlockValuesPlacement[$blockName][$settingName] ); 01495 if ( $this->BlockValues[$blockName] == null ) 01496 $this->removeGroup( $blockName ); 01497 } 01498 01499 /*! 01500 Fetches all defined groups and returns them as an associative array 01501 */ 01502 function &groups() 01503 { 01504 return $this->BlockValues; 01505 } 01506 01507 /*! 01508 Fetches all defined placements for every setting and returns them as an associative array 01509 */ 01510 function &groupPlacements() 01511 { 01512 if ( !$this->BlockValuesPlacement ) 01513 { 01514 $this->loadPlacement(); 01515 } 01516 return $this->BlockValuesPlacement; 01517 } 01518 01519 /** 01520 * Gives you the location of a ini file based on it's path, format is same as used internally 01521 * for $identifer for override dirs- 01522 * Eg: default / ext-siteaccess:<ext> / siteaccess / extension:<ext> / override 01523 * 01524 * @param string $path 01525 * @return string 01526 */ 01527 function findSettingPlacement( $path ) 01528 { 01529 if ( is_array( $path ) && isset( $path[0] ) ) 01530 $path = $path[0]; 01531 $exploded = explode( '/', $path ); 01532 $directoryCount = count( $exploded ); 01533 switch ( $directoryCount ) 01534 { 01535 case 2: 01536 $placement = 'default'; 01537 break; 01538 case 3: 01539 $placement = 'override'; 01540 break; 01541 case 4: 01542 { 01543 if ( $exploded[0] === 'extension' ) 01544 $placement = 'extension:' . $exploded[1]; 01545 else 01546 $placement = 'siteaccess'; 01547 } 01548 break; 01549 case 6: 01550 { 01551 $placement = 'ext-siteaccess:' . $exploded[1]; 01552 } 01553 break; 01554 default: 01555 $placement = 'undefined'; 01556 break; 01557 } 01558 return $placement; 01559 } 01560 01561 function settingType( $settingValue ) 01562 { 01563 if ( is_array( $settingValue ) ) 01564 return 'array'; 01565 01566 if ( is_numeric( $settingValue ) ) 01567 return 'numeric'; 01568 01569 if ( $settingValue == 'true' or $settingValue == 'false' ) 01570 { 01571 return 'true/false'; 01572 } 01573 if ( $settingValue == 'enabled' or $settingValue == 'disabled' ) 01574 { 01575 return 'enable/disable'; 01576 } 01577 01578 return 'string'; 01579 } 01580 01581 /*! 01582 Sets all groups overwriting the current values 01583 */ 01584 function setGroups( $groupArray ) 01585 { 01586 $resultArray = array(); 01587 // Check for readOnly 01588 foreach ( $groupArray as $blockName => $blockVariables ) 01589 { 01590 foreach ( $blockVariables as $variableName => $variableValue ) 01591 { 01592 if ( !$this->isSettingReadOnly( $this->FileName, $blockName, $variableName ) ) 01593 continue; 01594 $resultArray[$blockName][$variableName] = $variableValue; 01595 } 01596 } 01597 $this->BlockValues = $resultArray; 01598 } 01599 01600 /*! 01601 Sets multiple variables from the array \a $variables. 01602 \param $variables Contains an associative array with groups as first key, 01603 variable names as second key and variable values as values. 01604 \code 01605 $ini->setVariables( array( 'SiteSettings' => array( 'SiteName' => 'mysite', 01606 'SiteURL' => 'mysite.com' ) ) ); 01607 \endcode 01608 \sa setVariable 01609 */ 01610 function setVariables( $variables ) 01611 { 01612 foreach ( $variables as $blockName => $blockVariables ) 01613 { 01614 foreach ( $blockVariables as $variableName => $variableValue ) 01615 { 01616 $this->setVariable( $blockName, $variableName, $variableValue ); 01617 } 01618 } 01619 } 01620 01621 /*! 01622 Sets an INI file variable. 01623 \code 01624 $ini->setVariable( 'SiteSettings', 'SiteName', 'mysite' ); 01625 \endcode 01626 \sa setVariables 01627 */ 01628 function setVariable( $blockName, $variableName, $variableValue ) 01629 { 01630 if ( !$this->isSettingReadOnly( $this->filename(), $blockName, $variableName ) ) 01631 return false; 01632 01633 $this->BlockValues[$blockName][$variableName] = $variableValue; 01634 $this->ModifiedBlockValues[$blockName][$variableName] = true; 01635 } 01636 01637 /*! 01638 Returns BlockValues, which is a nicely named Array 01639 */ 01640 function getNamedArray() 01641 { 01642 return $this->BlockValues; 01643 } 01644 01645 /** 01646 * Returns whether the mentioned ini file has been loaded. 01647 * 01648 * @param string $fileName 01649 * @param string $rootDir 01650 * @param null|bool $useLocalOverrides default system setting if null 01651 * 01652 * @return bool 01653 */ 01654 static function isLoaded( $fileName = 'site.ini', $rootDir = 'settings', $useLocalOverrides = null ) 01655 { 01656 return isset( self::$instances["$rootDir-$fileName-$useLocalOverrides"] ); 01657 } 01658 01659 /** 01660 * Returns a shared instance of the eZINI class pr $fileName, $rootDir and $useLocalOverrides 01661 * param combinations. 01662 * If $useLocalOverrides is set to true you will get a copy of the current overrides, 01663 * but changes to the override settings will not be global. 01664 * Direct access is for accessing the filename directly in the specified path. .append and .append.php is automaticly added to filename 01665 * note: Use create() if you need to get a unique copy which you can alter. 01666 * 01667 * @param string $fileName 01668 * @param string $rootDir 01669 * @param null|bool $useTextCodec Default system setting if null (instance not used if not null!) 01670 * @param null|bool $useCache Default system setting if null (instance not used if not null!) 01671 * @param null|bool $useLocalOverrides Default system setting if null 01672 * @param bool $directAccess Direct access to specific file instead of values from several (instance not used if true!) 01673 * @param bool $addArrayDefinition @deprecated since version 4.5, use "new eZINI()" (instance not used if true!) 01674 * @return eZINI 01675 */ 01676 static function instance( $fileName = 'site.ini', $rootDir = 'settings', $useTextCodec = null, $useCache = null, $useLocalOverrides = null, $directAccess = false, $addArrayDefinition = false ) 01677 { 01678 if ( $addArrayDefinition !== false || $directAccess !== false || $useTextCodec !== null || $useCache !== null ) 01679 { 01680 // Could have trown strict error here but will cause issues if ini system has not been setup yet.. 01681 return new eZINI( $fileName, $rootDir, $useTextCodec, $useCache, $useLocalOverrides, $directAccess, $addArrayDefinition ); 01682 } 01683 01684 $key = "$rootDir-$fileName-$useLocalOverrides"; 01685 if ( !isset( self::$instances[$key] ) ) 01686 { 01687 self::$instances[$key] = new eZINI( $fileName, $rootDir, $useTextCodec, $useCache, $useLocalOverrides, $directAccess, $addArrayDefinition ); 01688 } 01689 return self::$instances[$key]; 01690 } 01691 01692 /*! 01693 Fetches the ini file \a $fileName and returns the INI object for it. 01694 \note This will not use the override system or read cache files, this is a direct fetch from one file. 01695 */ 01696 static function fetchFromFile( $fileName, $useTextCodec = null ) 01697 { 01698 return new eZINI( $fileName, false, $useTextCodec, false, false, true ); 01699 } 01700 01701 /** 01702 * Get ini file for a specific siteaccess (not incl extesnions or overrides) 01703 * use {@link eZSiteAccess::getIni()} instead if you want to have full ini env. 01704 * 01705 * @param string $siteAccess 01706 * @param string $iniFile 01707 * @return eZINI 01708 */ 01709 static function getSiteAccessIni( $siteAccess, $iniFile ) 01710 { 01711 $saPath = eZSiteAccess::findPathToSiteAccess( $siteAccess ); 01712 return self::fetchFromFile( "$saPath/$iniFile" ); 01713 } 01714 01715 /*! 01716 \static 01717 Similar to instance() but will always create a new copy. 01718 */ 01719 static function create( $fileName = 'site.ini', $rootDir = 'settings', $useTextCodec = null, $useCache = null, $useLocalOverrides = null ) 01720 { 01721 $impl = new eZINI( $fileName, $rootDir, $useTextCodec, $useCache, $useLocalOverrides ); 01722 return $impl; 01723 } 01724 01725 /*! 01726 Sets ReadonlySettingsCheck variable. 01727 */ 01728 function setReadOnlySettingsCheck( $readOnly = true ) 01729 { 01730 $this->ReadOnlySettingsCheck = $readOnly; 01731 } 01732 01733 /*! 01734 \return ReadonlySettingsCheck variable. 01735 */ 01736 function readOnlySettingsCheck() 01737 { 01738 return $this->ReadOnlySettingsCheck; 01739 } 01740 01741 /** 01742 * Resets a specific instance of eZINI. 01743 * 01744 * @deprecated since 4.5, use resetInstance() instead 01745 * 01746 * @param string $fileName 01747 * @param string $rootDir 01748 * @param null|bool $useLocalOverrides default system setting if null 01749 * 01750 * @see resetInstance() 01751 */ 01752 static function resetGlobals( $fileName = 'site.ini', $rootDir = 'settings', $useLocalOverrides = null ) 01753 { 01754 self::resetInstance( $fileName, $rootDir, $useLocalOverrides ); 01755 } 01756 01757 /** 01758 * Resets a specific instance of eZINI. 01759 * 01760 * @since 4.5 01761 * 01762 * @param string $fileName 01763 * @param string $rootDir 01764 * @param null|bool $useLocalOverrides default system setting if null 01765 */ 01766 static function resetInstance( $fileName = 'site.ini', $rootDir = 'settings', $useLocalOverrides = null ) 01767 { 01768 unset( self::$instances["$rootDir-$fileName-$useLocalOverrides"] ); 01769 } 01770 01771 /** 01772 * Reset all eZINI instances as well override dirs ( optional ) 01773 * 01774 * @deprecated since 4.5, use resetAllInstances() instead 01775 * 01776 * @param bool $resetOverrideDirs Specify if you don't want to clear override dirs 01777 * 01778 * @see resetAllInstances() 01779 */ 01780 static function resetAllGlobals( $resetGlobalOverrideDirs = true ) 01781 { 01782 self::resetAllInstances( $resetGlobalOverrideDirs ); 01783 } 01784 01785 /** 01786 * Reset all eZINI instances as well override dirs ( optional ) 01787 * 01788 * @since 4.5 01789 * 01790 * @param bool $resetOverrideDirs Specify if you don't want to clear override dirs 01791 */ 01792 static function resetAllInstances( $resetGlobalOverrideDirs = true ) 01793 { 01794 self::$instances = array(); 01795 01796 if ( $resetGlobalOverrideDirs ) 01797 self::resetGlobalOverrideDirs(); 01798 } 01799 01800 /// \privatesection 01801 /// The charset of the ini file 01802 public $Charset; 01803 01804 /// Variable to store the textcodec. 01805 public $Codec; 01806 01807 /// Variable to store the ini file values. 01808 public $BlockValues; 01809 01810 /// Variable to store the setting placement (which file is the setting in). 01811 public $BlockValuesPlacement; 01812 01813 /// Variable to store whether variables are modified or not 01814 public $ModifiedBlockValues; 01815 01816 /// Stores the filename 01817 public $FileName; 01818 01819 /// The root of all ini files 01820 public $RootDir; 01821 01822 /// Whether to use the text codec when reading the ini file or not 01823 public $UseTextCodec; 01824 01825 /// Stores the path and filename of the value cache file 01826 public $CacheFile; 01827 01828 /// Stores the path and filename of the placement cache file 01829 public $PlacementCacheFile; 01830 01831 /// true if cache should be used 01832 public $UseCache; 01833 01834 /// true if the overrides should only be changed locally 01835 public $UseLocalOverrides; 01836 01837 /// Contains the override dirs, if in local mode 01838 public $LocalOverrideDirArray; 01839 01840 /// Contains global override dirs 01841 static protected $GlobalOverrideDirArray = null; 01842 01843 /// If \c true then all file loads are done directly on the filename. 01844 public $DirectAccess; 01845 01846 /// If \c true empty element will be created in the beginning of array if it is defined in this ini file. 01847 public $AddArrayDefinition; 01848 01849 /// If \c true eZINI will check each setting (before saving) for correspondence of settings in site.ini[eZINISetting].ReadonlySettingList 01850 public $ReadOnlySettingsCheck = true; 01851 01852 } 01853 01854 ?>