Created
August 11, 2010 09:14
-
-
Save jpic/518721 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Basically, it implements ArrayObject and reads yourRoot/cache/etc/*.php (lazily). | |
But when calling refresh(), it will parse all applications paths etc/*.ini, | |
taking priorities in account. | |
The section-level inheritance is trivial and implemented in parseIni(), so feel free to take that code. | |
*/ | |
class madConfiguration extends madObject { | |
public function __construct( $path, &$applicationsConfiguration = null ) { | |
$this->path = $path; | |
if ( $applicationsConfiguration ) { | |
$this['applications'] = $applicationsConfiguration; | |
} | |
} | |
public function reset() { | |
if ( isset( $this->reseted ) ) { | |
return parent::reset(); | |
} | |
$files = glob( "{$this->path}/*" ); | |
if ( !$files ) { | |
return true; | |
} | |
foreach( $files as $file ) { | |
// filename without extension | |
$name = substr( substr( $file, strrpos( $file, '/' ) + 1 ), 0, -4 ); | |
// skip applications which is already set | |
if ( isset( $this['applications'] ) && $name == 'applications' ) { | |
continue; | |
} | |
switch( substr( $file, -3 ) ) { | |
case 'php': | |
$this->parsePhp( $file ); | |
break; | |
case 'ini': | |
$this->parseIni( $file ); | |
break; | |
default: | |
continue; | |
} | |
} | |
$this->reseted = true; | |
return parent::reset(); | |
} | |
public function offsetGet( $key ) { | |
if ( parent::offsetExists( $key ) ) { | |
return parent::offsetGet($key); | |
} | |
if ( file_exists( "$this->path/$key.php" ) ) { | |
$this->parsePhp( "$this->path/$key.php" ); | |
} elseif ( file_exists( "$this->path/$key.ini" ) ) { | |
$this->parseIni( "$this->path/$key.ini" ); | |
} else { | |
trigger_error( "Cannot find configuration for $key in $this->path", E_USER_ERROR ); | |
} | |
return parent::offsetGet( $key ); | |
} | |
public function refreshApplications( $entryApplicationPath, $subdir = 'etc' ) { | |
// figure the entry application name | |
$entryApplicationName = substr( $entryApplicationPath, strrpos( $entryApplicationPath, '/' ) + 1 ); | |
// save entry app repositories and installed applications because 'applications' | |
// will be emptied | |
$repositories = $this['applications'][$entryApplicationName]['applicationRepositories']; | |
// clean applications configuration | |
$this['applications'] = new madObject(); | |
// find all configuration paths in this repository path | |
$applicationPaths = array( ); | |
if ( defined( 'RecursiveDirectoryIterator::FOLLOW_SYMLINKS' ) ) { | |
$flags = RecursiveDirectoryIterator::FOLLOW_SYMLINKS|RecursiveIteratorIterator::LEAVES_ONLY|RecursiveIteratorIterator::SELF_FIRST; | |
} else { // php 5.2.6 support | |
$flags = RecursiveIteratorIterator::LEAVES_ONLY|RecursiveIteratorIterator::SELF_FIRST; | |
} | |
// discover paths to applications in configured repositories | |
foreach( $repositories as $path ) { | |
// prepend application path and get the absolute path | |
$path = $entryApplicationPath . '/' . $path; | |
$fileIterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( | |
$path, | |
$flags | |
) ); | |
foreach( $fileIterator as $fileInfo ) { | |
$filePath = madFramework::fixPath( realpath( $fileInfo->getPath( ) ) ); | |
// searching for directories named etc | |
if ( substr( $filePath, -3 ) != $subdir ) { | |
continue; | |
} | |
$relativePath = madFramework::getRelativePath( $filePath, $path ); | |
// if the relative path contains "tests" then its in unit | |
// tests, skip. | |
if ( strpos( $relativePath, 'tests' ) !== false ) { | |
continue; | |
} | |
// if the relative path contains "cache" then its a cached | |
// configuration, skip | |
if ( strpos( $relativePath, 'cache' ) !== false ) { | |
continue; | |
} | |
// get current application path | |
$applicationPath = substr( $filePath, 0, strrpos( $filePath, '/'. $subdir ) ); | |
// get current application name | |
$applicationName = substr( $applicationPath, strrpos( $applicationPath, '/' ) + 1 ); | |
// don't add paths twice | |
if ( in_array( $applicationName, array_keys( $applicationPaths ) ) ) { | |
continue; | |
} | |
// store the path because we'll parse application config in the | |
// order of sections in applications.ini | |
$this['applications'][$applicationName] = array( | |
'path' => madFramework::getRelativePath( $applicationPath, $entryApplicationPath ) | |
); | |
} | |
} | |
} | |
public function refresh( $entryApplicationPath, $installedApplications, $subdir = 'etc' ) { | |
// applications.ini apps order is from the most specific to the less | |
// specific. Our merge overrides, so configuration must be parsed from | |
// the less specific to the most specific | |
foreach( array_reverse( $installedApplications ) as $applicationName ) { | |
$applicationPath = $this['applications'][$applicationName]['path']; | |
// entry app has its relative path empty, hence this change to | |
// avoid getting $entryApplicationPath//foo.ini | |
if ( $applicationPath ) { | |
$applicationPath .= '/'; | |
} | |
$files = glob( "$entryApplicationPath/$applicationPath{$subdir}/*.ini" ); | |
if ( !$files ) { | |
continue; | |
} | |
// fetch and override this application configuration | |
foreach( $files as $file ) { | |
$this->parseIni( madFramework::fixPath( $file ) ); | |
} | |
} | |
} | |
public function write( $cacheDirectory ) { | |
foreach( $this as $name => $object ) { | |
$body = sprintf( | |
'<?php return %s ?>', | |
var_export( $object->objectsToArray(), true ) | |
); | |
$target = $cacheDirectory . "/$name.php"; | |
file_put_contents( $target, $body ); | |
} | |
} | |
public function getSetting( $group, $section, $name, $default = null ) { | |
if ( !isset( $this[$group] ) ) { | |
$this->offsetGet( $group ); | |
} | |
if ( isset( $this[$group] ) && isset( $this[$group][$section] ) && isset( $this[$group][$section][$name] ) ) { | |
return $this[$group][$section][$name]; | |
} | |
if ( !is_null( $default ) ) { | |
return $default; | |
} | |
trigger_error( "Setting [$group][$section][$name] does not exist, and no default setting was set.", E_USER_ERROR ); | |
} | |
public function getClassApplicationName( $className ) { | |
foreach( $this['applications'] as $name => $settings ) { | |
if ( isset( $settings['classes'] ) && in_array( $className, $settings['classes'] ) ) { | |
return $name; | |
} | |
} | |
throw new Exception( "Cannot find the name of the application containing class $className, does it contain a $subdir subdir?" ); | |
} | |
public function getPathSetting( $group, $section, $name ) { | |
$path = $this->getSetting( $group, $section, $name ); | |
$realPath = ENTRY_APP_PATH . '/' . $path; | |
if ( !$realPath ) { | |
trigger_error( "Configured path deos not exists $path" ); | |
} | |
return $realPath; | |
} | |
public function parseIni( $file ) { | |
preg_match( '@/(?P<appName>[^/]+)/(?P<subdir>[^/]+)/(?P<name>[^/]+)\.ini$@', $file, $matches ); | |
$name = $matches['name']; | |
if ( isset( $this[$name] ) && !is_array( $this[$name] ) ) { | |
$settings = (array) $this[$name]->objectsToArray(); | |
} else { | |
$settings = array(); | |
} | |
$appName = $matches['appName']; | |
$parser = new ezcConfigurationIniParser( ezcConfigurationIniParser::PARSE, $file ); | |
$unsetGroups = array( ); | |
foreach( new NoRewindIterator( $parser ) as $element ) { | |
if ( $element instanceof ezcConfigurationIniItem ) { | |
switch ( $element->type ) { | |
case ezcConfigurationIniItem::GROUP_HEADER: | |
if ( !isset( $settings[$element->group] ) ) { | |
$settings[$element->group] = array(); | |
$settings[$element->group]['META'] = array( | |
'application' => substr( $element->group, 0, strpos( $element->group, '.' ) ), | |
); | |
} | |
// a little heavy on performances, but its the safest | |
if ( strpos( $element->group, '..' ) ) { | |
$groupParts = explode( '..', $element->group ); | |
$realGroup = array_shift( $groupParts ); | |
// hardlink the real group to its false name for | |
// the parsed variables to be set in both. | |
$settings[$realGroup] =& $settings[$element->group]; | |
// the configuration section with the false name | |
// will be dereferenced later. | |
$unsetGroups[] = $element->group; | |
// the order of parents should be defined from | |
// the most specific to the less specific, but our | |
// merge does override, so the order is reversed to | |
// start by the least specific. | |
foreach( array_reverse( $groupParts ) as $parentGroup ) { | |
if ( !isset( $settings[$parentGroup] ) ) { | |
trigger_error( "Can't inherit from a section that was not defined! Happenned with: " . $element->group, E_USER_ERROR ); | |
} | |
$real = new madObject( $settings[$realGroup] ); | |
$parent = new madObject( $settings[$parentGroup] ); | |
$real->merge( $parent ); | |
$settings[$realGroup] = (array) $real->objectsToArray(); | |
unset( $real ); | |
unset( $parent ); | |
} | |
} | |
break; | |
case ezcConfigurationIniItem::SETTING: | |
eval( '$settings[$element->group][$element->setting]'. $element->dimensions . ' = $element->value;' ); | |
break; | |
} | |
} | |
if ( $element instanceof ezcConfigurationValidationItem ) { | |
throw new ezcConfigurationParseErrorException( $element->file, $element->line, $element->description ); | |
} | |
} | |
foreach( $unsetGroups as $group ) { | |
unset( $settings[$group] ); | |
} | |
$this[$name] = new madObject( $settings ); | |
$this[$name]->arrayToObjects(); | |
madFramework::fixPathArray( $this[$name] ); | |
} | |
public function parsePhp( $file ) { | |
$name = substr( substr( $file, strrpos( $file, '/' ) + 1 ), 0, -4 ); | |
$this[$name] = require $file; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment