31 private $_configArray = array();
32 private $_comments = array();
35 private $_lookupTable = array();
38 private $_isModified =
false;
39 private $_addedFiles = array();
40 private $_containedFiles = array();
41 private $_useCache =
true;
43 private $_configPath = null;
44 private $_configExtension =
'ini';
46 private $_fileUtil = null;
48 private static $_logger = null;
55 $this->_configPath = $configPath;
57 if (self::$_logger == null) {
67 return $this->_configPath;
78 return $this->_fileUtil->getFiles($this->_configPath,
'/\.'.$this->_configExtension.
'$/',
true);
87 if (self::$_logger->isDebugEnabled()) {
88 self::$_logger->debug(
"Add configuration: ".$name);
90 $filename = $this->_configPath.$name;
94 $numParsedFiles =
sizeof($this->_addedFiles);
95 $lastFile = $numParsedFiles > 0 ? $this->_addedFiles[$numParsedFiles-1] :
'';
96 if (self::$_logger->isDebugEnabled()) {
97 self::$_logger->debug(
"Parsed files: ".$numParsedFiles.
", last file: ".$lastFile);
99 if ($numParsedFiles > 0 && $lastFile == $filename) {
100 if (self::$_logger->isDebugEnabled()) {
101 self::$_logger->debug(
"Skipping");
106 if (file_exists($filename)) {
107 if (self::$_logger->isDebugEnabled()) {
108 self::$_logger->debug(
"Adding...");
111 $this->_addedFiles[] = $filename;
113 if (self::$_logger->isDebugEnabled()) {
114 self::$_logger->debug(
"Parse first time");
116 $result = $this->
processFile($filename, $this->_configArray, $this->_containedFiles);
117 $this->_configArray = $result[
'config'];
118 $this->_containedFiles = array_unique($result[
'files']);
120 if ($processValues) {
131 if (self::$_logger->isDebugEnabled()) {
132 self::$_logger->debug(
"Reuse from cache");
149 protected function processFile($filename, $configArray=array(), $parsedFiles=array()) {
151 if (!in_array($filename, $parsedFiles)) {
152 $parsedFiles[] = $filename;
160 foreach ($includes as $include) {
161 $result = $this->
processFile($this->_configPath.$include, $configArray, $parsedFiles);
162 $configArray = $this->
configMerge($configArray, $result[
'config'],
true);
163 $parsedFiles = $result[
'files'];
168 $configArray = $this->
configMerge($configArray, $content,
true);
170 return array(
'config' => $configArray,
'files' => $parsedFiles);
177 return array_keys($this->_configArray);
184 return ($this->
lookup($section) != null);
191 $lookupEntry = $this->
lookup($section);
192 if ($lookupEntry == null) {
196 return $this->_configArray[$lookupEntry[0]];
203 public function hasValue($key, $section) {
204 return ($this->lookup($section, $key) != null);
210 public function getValue($key, $section) {
211 $lookupEntry = $this->lookup($section, $key);
212 if ($lookupEntry == null || sizeof($lookupEntry) == 1) {
213 throw new ConfigurationException('Key \
''.$key.
'\' not found in section \
''.$section.
'\'!
');
216 return $this->_configArray[$lookupEntry[0]][$lookupEntry[1]];
223 public function getBooleanValue($key, $section) {
224 $value = $this->getValue($key, $section);
225 return StringUtil::getBoolean($value);
231 public function getDirectoryValue($key, $section) {
232 $value = $this->getValue($key, $section);
233 $isArray = is_array($value);
234 $values = !$isArray ? array($value) : $value;
237 foreach ($values as $path) {
238 $absPath = WCMF_BASE.$path;
239 $result[] = $this->_fileUtil->realpath($absPath).'/
';
242 return $isArray ? $result : (sizeof($result) > 0 ? $result[0] : null);
248 public function getFileValue($key, $section) {
249 $value = $this->getValue($key, $section);
250 $isArray = is_array($value);
251 $values = !$isArray ? array($value) : $value;
254 foreach ($values as $path) {
255 $absPath = WCMF_BASE.$path;
256 $result[] = $this->_fileUtil->realpath(dirname($absPath)).'/
'.basename($absPath);
259 return $isArray ? $result : (sizeof($result) > 0 ? $result[0] : null);
265 public function getKey($value, $section) {
266 $map = array_flip($this->getSection($section));
267 if (!isset($map[$value])) {
268 throw new ConfigurationException('Value \
''.$value.
'\' not found in section \
''.$section.
'\'!
');
280 public function isEditable($section) {
281 if ($this->hasValue('readonlySections
', 'config
')) {
282 $readonlySections = $this->getValue('readonlySections
', 'config
');
283 $sectionLower = strtolower($section);
284 if (is_array($readonlySections)) {
285 foreach($readonlySections as $readonlySection) {
286 if ($sectionLower == strtolower($readonlySection)) {
298 public function isModified() {
299 return $this->_isModified;
305 public function createSection($section) {
306 $section = trim($section);
307 if (strlen($section) == 0) {
308 throw new IllegalArgumentException('Empty section names are not allowed!
');
310 if ($this->hasSection($section)) {
311 throw new IllegalArgumentException('Section \
''.$section.
'\' already exists!
');
313 $this->_configArray[$section] = '';
314 $this->buildLookupTable();
315 $this->_isModified = true;
322 public function removeSection($section) {
323 if (!$this->isEditable($section)) {
324 throw new IllegalArgumentException('Section \
''.$section.
'\' is not editable!
');
326 $lookupEntry = $this->lookup($section);
327 if ($lookupEntry != null) {
328 unset($this->_configArray[$lookupEntry[0]]);
329 $this->buildLookupTable();
330 $this->_isModified = true;
337 public function renameSection($oldname, $newname) {
338 $newname = trim($newname);
339 if (strlen($newname) == 0) {
340 throw new IllegalArgumentException('Empty section names are not allowed!
');
342 $lookupEntryOld = $this->lookup($oldname);
343 if ($lookupEntryOld == null) {
344 throw new IllegalArgumentException('Section \
''.$oldname.
'\' does not exist!
');
346 if (!$this->isEditable($oldname)) {
347 throw new IllegalArgumentException('Section \
''.$oldname.
'\' is not editable!
');
349 $lookupEntryNew = $this->lookup($newname);
350 if ($lookupEntryNew != null) {
351 throw new IllegalArgumentException('Section \
''.$newname.
'\' already exists!
');
354 $value = $this->_configArray[$lookupEntryOld[0]];
355 $this->_configArray[$newname] = $value;
356 unset($this->_configArray[$lookupEntryOld[0]]);
357 $this->buildLookupTable();
358 $this->_isModified = true;
364 public function setValue($key, $value, $section, $createSection=true) {
366 if (strlen($key) == 0) {
367 throw new IllegalArgumentException('Empty key names are not allowed!
');
369 $lookupEntrySection = $this->lookup($section);
370 if ($lookupEntrySection == null && !$createSection) {
371 throw new IllegalArgumentException('Section \
''.$section.
'\' does not exist!
');
373 if ($lookupEntrySection != null && !$this->isEditable($section)) {
374 throw new IllegalArgumentException('Section \
''.$section.
'\' is not editable!
');
377 // create section if requested and determine section name
378 if ($lookupEntrySection == null && $createSection) {
379 $section = trim($section);
380 $this->_configArray[$section] = array();
381 $finalSectionName = $section;
384 $finalSectionName = $lookupEntrySection[0];
386 // determine key name
387 if ($lookupEntrySection != null) {
388 $lookupEntryKey = $this->lookup($section, $key);
389 if ($lookupEntryKey == null) {
390 // key does not exist yet
391 $finalKeyName = $key;
394 $finalKeyName = $lookupEntryKey[1];
398 $finalKeyName = $key;
400 $this->_configArray[$finalSectionName][$finalKeyName] = $value;
401 $this->buildLookupTable();
402 $this->_isModified = true;
408 public function removeKey($key, $section) {
409 if (!$this->isEditable($section)) {
410 throw new IllegalArgumentException('Section \
''.$section.
'\' is not editable!
');
412 $lookupEntry = $this->lookup($section, $key);
413 if ($lookupEntry != null) {
414 unset($this->_configArray[$lookupEntry[0]][$lookupEntry[1]]);
415 $this->buildLookupTable();
416 $this->_isModified = true;
423 public function renameKey($oldname, $newname, $section) {
424 $newname = trim($newname);
425 if (strlen($newname) == 0) {
426 throw new IllegalArgumentException('Empty key names are not allowed!
');
428 if (!$this->hasSection($section)) {
429 throw new IllegalArgumentException('Section \
''.$section.
'\' does not exist!
');
431 if (!$this->isEditable($section)) {
432 throw new IllegalArgumentException('Section \
''.$section.
'\' is not editable!
');
434 $lookupEntryOld = $this->lookup($section, $oldname);
435 if ($lookupEntryOld == null) {
436 throw new IllegalArgumentException('Key \
''.$oldname.
'\' does not exist in section \
''.$section.
'\'!
');
438 $lookupEntryNew = $this->lookup($section, $newname);
439 if ($lookupEntryNew != null) {
440 throw new IllegalArgumentException('Key \
''.$newname.
'\' already exists in section \
''.$section.
'\'!
');
443 $value = $this->_configArray[$lookupEntryOld[0]][$lookupEntryOld[1]];
444 $this->_configArray[$lookupEntryOld[0]][$newname] = $value;
445 unset($this->_configArray[$lookupEntryOld[0]][$lookupEntryOld[1]]);
446 $this->buildLookupTable();
447 $this->_isModified = true;
453 public function writeConfiguration($name) {
456 foreach($this->_configArray as $section => $values) {
457 $sectionString = "[".$section."]";
458 $content .= $this->_comments[$sectionString];
459 $content .= $sectionString."\n";
460 if (is_array($values)) {
461 foreach($values as $key => $value) {
462 if (is_array($value)) {
463 $value = "{".join(", ", $value)."}";
465 // unescape double quotes
466 $value = str_replace("\\\"", "\"", $value);
467 $content .= $this->_comments[$section][$key];
468 $content .= $key." = ".$value."\n";
472 $content .= $this->_comments[';
'];
474 if (!$fh = fopen($filename, 'w
')) {
475 throw new IOException('Can\
't open ini file \''.$filename.
'\'!
');
478 if (!fwrite($fh, $content)) {
479 throw new IOException('Can\
't write ini file \''.$filename.
'\'!
');
482 // clear the application cache, because it may become invalid
483 $this->clearAllCache();
484 $this->_isModified = false;
502 protected function _parse_ini_file($filename) {
503 if (!file_exists($filename)) {
504 throw new ConfigurationException('The config file
'.$filename.' does not exist.
');
506 $configArray = array();
508 $lines = file($filename);
509 $commentsPending = '';
510 foreach($lines as $line) {
512 // comments/blank lines
513 if($line == '' || $line[0] == ';
') {
514 $commentsPending .= $line."\n";
518 if($line[0] == '[
' && $line[strlen($line)-1] == ']
') {
519 $sectionName = substr($line, 1, strlen($line)-2);
520 $configArray[$sectionName] = array();
522 // store comments/blank lines for section
523 $this->_comments[$line] = $commentsPending;
524 $commentsPending = '';
527 $parts = explode('=
', $line, 2);
528 $key = trim($parts[0]);
529 $value = trim($parts[1]);
530 $configArray[$sectionName][$key] = $value;
532 // store comments/blank lines for key
533 $this->_comments[$sectionName][$key] = $commentsPending;
534 $commentsPending = "";
537 // store comments/blank lines from the end of the file
538 $this->_comments[';
'] = substr($commentsPending, 0, -1);
548 protected function processValues() {
549 array_walk_recursive($this->_configArray, array($this, '
processValue'));
558 protected function processValue(&$value) {
559 if (!is_array($value)) {
560 // decode encoded (%##) values
561 if (preg_match ("/%/", $value)) {
562 $value = urldecode($value);
565 if(preg_match("/^{.*}$/", $value)) {
566 $arrayValues = StringUtil::quotesplit(substr($value, 1, -1));
568 foreach ($arrayValues as $arrayValue) {
569 $value[] = trim($arrayValue);
583 protected function configMerge($array1, $array2, $override) {
585 foreach(array_keys($array2) as $key) {
586 if (!array_key_exists($key, $result)) {
587 $result[$key] = $array2[$key];
590 foreach(array_keys($array2[$key]) as $subkey) {
591 if ((array_key_exists($subkey, $result[$key]) && $override) || !isset($result[$key][$subkey])) {
592 $result[$key][$subkey] = $array2[$key][$subkey];
605 protected function getConfigIncludes($array) {
606 $sectionMatches = null;
607 if (preg_match('/(?:^|,)(config)(?:,|$)/i
', join(',
', array_keys($array)), $sectionMatches)) {
608 $sectionKey = sizeof($sectionMatches) > 0 ? $sectionMatches[1] : null;
611 if (preg_match('/(?:^|,)(include)(?:,|$)/i
', join(',
', array_keys($array[$sectionKey])), $keyMatches)) {
612 return sizeof($keyMatches) > 0 ? $array[$sectionKey][$keyMatches[1]] : null;
622 protected function serialize() {
623 if ($this->_useCache && !$this->isModified()) {
624 $cacheFile = $this->getSerializeFilename($this->_addedFiles);
625 if (self::$_logger->isDebugEnabled()) {
626 self::$_logger->debug("Serialize configuration: ".join(',
', $this->_addedFiles)." to file: ".$cacheFile);
628 if ($fh = @fopen($cacheFile, "w")) {
629 if (@fwrite($fh, serialize(get_object_vars($this)))) {
632 // clear the application cache, because it may become invalid
633 $this->clearAllCache();
646 protected function unserialize($parsedFiles) {
647 if ($this->_useCache && !$this->isModified()) {
648 $cacheFile = $this->getSerializeFilename($parsedFiles);
649 if (file_exists($cacheFile)) {
650 $parsedFiles[] = __FILE__;
651 if (!$this->checkFileDate($parsedFiles, $cacheFile)) {
652 $vars = unserialize(file_get_contents($cacheFile));
654 // check if included ini files were updated since last cache time
655 $includes = $vars['_containedFiles
'];
656 if (is_array($includes)) {
657 if ($this->checkFileDate($includes, $cacheFile)) {
662 // everything is up-to-date
663 foreach($vars as $key => $val) {
678 protected function getSerializeFilename($parsedFiles) {
679 $path = session_save_path().DIRECTORY_SEPARATOR;
680 $filename = $path.'wcmf_config_
'.md5(realpath($this->_configPath).'/
'.join('_
', $parsedFiles));
690 protected function checkFileDate($fileList, $referenceFile) {
691 foreach ($fileList as $file) {
692 if (filemtime($file) > filemtime($referenceFile)) {
702 protected function clearAllCache() {
703 if (self::$_logger->isDebugEnabled()) {
704 self::$_logger->debug("Clear all caches");
707 $cache = ObjectFactory::getInstance('cache
');
710 catch (\Exception $e) {}
716 protected function buildLookupTable() {
717 $this->_lookupTable = array();
718 foreach ($this->_configArray as $section => $entry) {
719 // create section entry
720 $lookupSectionKey = strtolower($section.':
');
721 $this->_lookupTable[$lookupSectionKey] = array($section);
722 // create key entries
723 foreach ($entry as $key => $value) {
724 $lookupKey = strtolower($lookupSectionKey.$key);
725 $this->_lookupTable[$lookupKey] = array($section, $key);
736 protected function lookup($section, $key=null) {
737 $lookupKey = strtolower($section).':
'.strtolower($key);
738 if (isset($this->_lookupTable[$lookupKey])) {
739 return $this->_lookupTable[$lookupKey];
Implementations of WritableConfiguration allow to change the whole or parts of the configuration and ...
configMerge($array1, $array2, $override)
Merge two arrays, preserving entries in first one unless they are overridden by ones in the second...
processValue(&$value)
Process the values in the ini array.
processFile($filename, $configArray=array(), $parsedFiles=array())
Process the given file recursivly.
static getLogger($name)
Get the logger with the given name.
__construct($configPath)
Constructor.
lookup($section, $key=null)
Lookup section and key.
getConfigPath()
Get the filesystem path to the configuration files.
getConfigIncludes($array)
Search the given value for a 'include' key in a section named 'config' (case-insensivite) ...
processValues()
Process the values in the ini array.
Implementations of Configuration give access to the application configuration.
getConfigurations()
Configuration interface.
_parse_ini_file($filename)
Private interface.
buildLookupTable()
Build the internal lookup table.
InifileConfiguration reads the application configuraiton from ini files.
FileUtil provides basic support for file functionality like HTTP file upload.
addConfiguration($name, $processValues=true)
serialize()
Store the instance in the filesystem.
ConfigurationException signals an exception in the configuration.
unserialize($parsedFiles)
Retrieve parsed ini data from the filesystem and update the current instance.