InifileConfiguration.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2017 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
11 namespace wcmf\lib\config\impl;
12 
23 
24 /**
25  * InifileConfiguration reads the application configuration from ini files.
26  * @note This class only supports ini files with sections.
27  *
28  * @author ingo herwig <ingo@wemove.com>
29  */
31 
32  private $configArray = []; // an assoziate array that holds sections with keys with values
33  private $comments = []; // an assoziate array that holds the comments/blank lines in the file
34  // (each comment is attached to the following section/key)
35  // the key ';' holds the comments at the end of the file
36  private $lookupTable = []; // an assoziate array that has lowercased section or section:key
37  // keys and [section, key] values for fast lookup
38 
39  private $isModified = false;
40  private $addedFiles = []; // files added to the configuration
41  private $containedFiles = []; // all included files (also by config include)
42 
43  private $configPath = null;
44  private $configExtension = 'ini';
45 
46  private $fileUtil = null;
47 
48  private static $logger = null;
49 
50  /**
51  * Constructor.
52  * @param $configPath The path, either absolute or relative to the executed script
53  */
54  public function __construct($configPath) {
55  $this->configPath = $configPath;
56  $this->fileUtil = new FileUtil();
57  if (self::$logger == null) {
58  self::$logger = LogManager::getLogger(__CLASS__);
59  }
60  }
61 
62  /**
63  * Get the file system path to the configuration files.
64  * @return The path, either absolute or relative to the executed script
65  */
66  public function getConfigPath() {
67  return $this->configPath;
68  }
69 
70  /**
71  * Configuration interface
72  */
73 
74  /**
75  * @see Configuration::getConfigurations()
76  */
77  public function getConfigurations() {
78  return $this->fileUtil->getFiles($this->configPath, '/\.'.$this->configExtension.'$/', true);
79  }
80 
81  /**
82  * @see Configuration::addConfiguration()
83  * Name is the ini file to be parsed (relative to configPath)
84  * @note ini files referenced in section 'config' key 'include' are parsed afterwards
85  */
86  public function addConfiguration($name, $processValues=true) {
87  if (self::$logger->isDebugEnabled()) {
88  self::$logger->debug("Add configuration: ".$name);
89  }
90  $filename = $this->configPath.$name;
91 
92  // do nothing, if the requested file was the last parsed file
93  // we don't only check if it's parsed, because order matters
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);
98  foreach($this->addedFiles as $addedFile) {
99  self::$logger->debug("File date ".$addedFile.": ".@filemtime($addedFile));
100  }
101  $cachedFile = $this->getSerializeFilename($this->addedFiles);
102  self::$logger->debug("File date ".$cachedFile.": ".@filemtime($cachedFile));
103  }
104  if ($numParsedFiles > 0 && $lastFile == $filename &&
105  !$this->checkFileDate($this->addedFiles, $this->getSerializeFilename($this->addedFiles))) {
106  if (self::$logger->isDebugEnabled()) {
107  self::$logger->debug("Skipping");
108  }
109  return;
110  }
111 
112  if (!file_exists($filename)) {
113  throw new ConfigurationException('Configuration file '.$filename.' not found!');
114  }
115 
116  if (self::$logger->isDebugEnabled()) {
117  self::$logger->debug("Adding...");
118  }
119  // try to unserialize an already parsed ini file sequence
120  $this->addedFiles[] = $filename;
121  if (!$this->unserialize($this->addedFiles)) {
122  if (self::$logger->isDebugEnabled()) {
123  self::$logger->debug("Parse first time");
124  }
125  $result = $this->processFile($filename, $this->configArray, $this->containedFiles);
126  $this->configArray = $result['config'];
127  $this->containedFiles = array_unique($result['files']);
128 
129  if ($processValues) {
130  $this->processValues();
131  }
132 
133  // re-build lookup table
134  $this->buildLookupTable();
135 
136  // serialize the parsed ini file sequence
137  $this->serialize();
138 
139  // notify configuration change listeners
140  $this->configChanged();
141  }
142  else {
143  if (self::$logger->isDebugEnabled()) {
144  self::$logger->debug("Reuse from cache");
145  }
146  }
147  }
148 
149  /**
150  * Process the given file recursively
151  * @param $filename The filename
152  * @param $configArray Configuration array
153  * @param $parsedFiles Parsed files
154  * @return Associative array with keys 'config' (configuration array) and 'files'
155  * (array of parsed files)
156  */
157  protected function processFile($filename, $configArray=[], $parsedFiles=[]) {
158  // avoid circular includes
159  if (!in_array($filename, $parsedFiles)) {
160  $parsedFiles[] = $filename;
161 
162  $content = $this->parseIniFile($filename);
163 
164  // process includes
165  $includes = $this->getConfigIncludes($content);
166  if ($includes) {
167  $this->processValue($includes);
168  foreach ($includes as $include) {
169  $result = $this->processFile($this->configPath.$include, $configArray, $parsedFiles);
170  $configArray = $this->configMerge($configArray, $result['config'], true);
171  $parsedFiles = $result['files'];
172  }
173  }
174 
175  // process self
176  $configArray = $this->configMerge($configArray, $content, true);
177  }
178  return ['config' => $configArray, 'files' => $parsedFiles];
179  }
180 
181  /**
182  * @see Configuration::getSections()
183  */
184  public function getSections() {
185  return array_keys($this->configArray);
186  }
187 
188  /**
189  * @see Configuration::hasSection()
190  */
191  public function hasSection($section) {
192  return ($this->lookup($section) != null);
193  }
194 
195  /**
196  * @see Configuration::getSection()
197  */
198  public function getSection($section, $includeMeta=false) {
199  $lookupEntry = $this->lookup($section);
200  if ($lookupEntry == null) {
201  throw new ConfigurationException('Section \''.$section.'\' not found!');
202  }
203  else {
204  if ($includeMeta) {
205  return $this->configArray[$lookupEntry[0]];
206  }
207  else {
208  return array_filter($this->configArray[$lookupEntry[0]], function($k) {
209  return !preg_match('/^__/', $k);
210  }, \ARRAY_FILTER_USE_KEY);
211  }
212  }
213  }
214 
215  /**
216  * @see Configuration::hasValue()
217  */
218  public function hasValue($key, $section) {
219  return ($this->lookup($section, $key) != null);
220  }
221 
222  /**
223  * @see Configuration::getValue()
224  */
225  public function getValue($key, $section) {
226  $lookupEntry = $this->lookup($section, $key);
227  if ($lookupEntry == null || sizeof($lookupEntry) == 1) {
228  throw new ConfigurationException('Key \''.$key.'\' not found in section \''.$section.'\'!');
229  }
230  else {
231  return $this->configArray[$lookupEntry[0]][$lookupEntry[1]];
232  }
233  }
234 
235  /**
236  * @see Configuration::getBooleanValue()
237  */
238  public function getBooleanValue($key, $section) {
239  $value = $this->getValue($key, $section);
240  return StringUtil::getBoolean($value);
241  }
242 
243  /**
244  * @see Configuration::getDirectoryValue()
245  */
246  public function getDirectoryValue($key, $section) {
247  $value = $this->getValue($key, $section);
248  $isArray = is_array($value);
249  $values = !$isArray ? [$value] : $value;
250 
251  $result = [];
252  foreach ($values as $path) {
253  $absPath = WCMF_BASE.$path;
254  $result[] = $this->fileUtil->realpath($absPath).'/';
255  }
256 
257  return $isArray ? $result : (sizeof($result) > 0 ? $result[0] : null);
258  }
259 
260  /**
261  * @see Configuration::getFileValue()
262  */
263  public function getFileValue($key, $section) {
264  $value = $this->getValue($key, $section);
265  $isArray = is_array($value);
266  $values = !$isArray ? [$value] : $value;
267 
268  $result = [];
269  foreach ($values as $path) {
270  $absPath = WCMF_BASE.$path;
271  $result[] = $this->fileUtil->realpath(dirname($absPath)).'/'.basename($absPath);
272  }
273 
274  return $isArray ? $result : (sizeof($result) > 0 ? $result[0] : null);
275  }
276 
277  /**
278  * @see Configuration::getKey()
279  */
280  public function getKey($value, $section) {
281  $map = array_flip($this->getSection($section));
282  if (!isset($map[$value])) {
283  throw new ConfigurationException('Value \''.$value.'\' not found in section \''.$section.'\'!');
284  }
285  return $map[$value];
286  }
287 
288  /**
289  * WritableConfiguration interface
290  */
291 
292  /**
293  * @see WritableConfiguration::isEditable()
294  */
295  public function isEditable($section) {
296  if ($this->hasValue('readonlySections', 'config')) {
297  $readonlySections = $this->getValue('readonlySections', 'config');
298  $sectionLower = strtolower($section);
299  if (is_array($readonlySections)) {
300  foreach($readonlySections as $readonlySection) {
301  if ($sectionLower == strtolower($readonlySection)) {
302  return false;
303  }
304  }
305  }
306  }
307  return true;
308  }
309 
310  /**
311  * @see WritableConfiguration::isModified()
312  */
313  public function isModified() {
314  return $this->isModified;
315  }
316 
317  /**
318  * @see WritableConfiguration::createSection()
319  */
320  public function createSection($section) {
321  $section = trim($section);
322  if (strlen($section) == 0) {
323  throw new IllegalArgumentException('Empty section names are not allowed!');
324  }
325  if ($this->hasSection($section)) {
326  throw new IllegalArgumentException('Section \''.$section.'\' already exists!');
327  }
328  $this->configArray[$section] = '';
329  $this->buildLookupTable();
330  $this->isModified = true;
331  return true;
332  }
333 
334  /**
335  * @see WritableConfiguration::removeSection()
336  */
337  public function removeSection($section) {
338  if (!$this->isEditable($section)) {
339  throw new IllegalArgumentException('Section \''.$section.'\' is not editable!');
340  }
341  $lookupEntry = $this->lookup($section);
342  if ($lookupEntry != null) {
343  unset($this->configArray[$lookupEntry[0]]);
344  $this->buildLookupTable();
345  $this->isModified = true;
346  }
347  }
348 
349  /**
350  * @see WritableConfiguration::renameSection()
351  */
352  public function renameSection($oldname, $newname) {
353  $newname = trim($newname);
354  if (strlen($newname) == 0) {
355  throw new IllegalArgumentException('Empty section names are not allowed!');
356  }
357  $lookupEntryOld = $this->lookup($oldname);
358  if ($lookupEntryOld == null) {
359  throw new IllegalArgumentException('Section \''.$oldname.'\' does not exist!');
360  }
361  if (!$this->isEditable($oldname)) {
362  throw new IllegalArgumentException('Section \''.$oldname.'\' is not editable!');
363  }
364  $lookupEntryNew = $this->lookup($newname);
365  if ($lookupEntryNew != null) {
366  throw new IllegalArgumentException('Section \''.$newname.'\' already exists!');
367  }
368  // do rename
369  $value = $this->configArray[$lookupEntryOld[0]];
370  $this->configArray[$newname] = $value;
371  unset($this->configArray[$lookupEntryOld[0]]);
372  $this->buildLookupTable();
373  $this->isModified = true;
374  }
375 
376  /**
377  * @see WritableConfiguration::setValue()
378  */
379  public function setValue($key, $value, $section, $createSection=true) {
380  $key = trim($key);
381  if (strlen($key) == 0) {
382  throw new IllegalArgumentException('Empty key names are not allowed!');
383  }
384  $lookupEntrySection = $this->lookup($section);
385  if ($lookupEntrySection == null && !$createSection) {
386  throw new IllegalArgumentException('Section \''.$section.'\' does not exist!');
387  }
388  if ($lookupEntrySection != null && !$this->isEditable($section)) {
389  throw new IllegalArgumentException('Section \''.$section.'\' is not editable!');
390  }
391 
392  // create section if requested and determine section name
393  if ($lookupEntrySection == null && $createSection) {
394  $section = trim($section);
395  $this->configArray[$section] = [];
396  $finalSectionName = $section;
397  }
398  else {
399  $finalSectionName = $lookupEntrySection[0];
400  }
401  // determine key name
402  if ($lookupEntrySection != null) {
403  $lookupEntryKey = $this->lookup($section, $key);
404  if ($lookupEntryKey == null) {
405  // key does not exist yet
406  $finalKeyName = $key;
407  }
408  else {
409  $finalKeyName = $lookupEntryKey[1];
410  }
411  }
412  else {
413  $finalKeyName = $key;
414  }
415  $this->configArray[$finalSectionName][$finalKeyName] = $value;
416  $this->buildLookupTable();
417  $this->isModified = true;
418  }
419 
420  /**
421  * @see WritableConfiguration::removeKey()
422  */
423  public function removeKey($key, $section) {
424  if (!$this->isEditable($section)) {
425  throw new IllegalArgumentException('Section \''.$section.'\' is not editable!');
426  }
427  $lookupEntry = $this->lookup($section, $key);
428  if ($lookupEntry != null) {
429  unset($this->configArray[$lookupEntry[0]][$lookupEntry[1]]);
430  $this->buildLookupTable();
431  $this->isModified = true;
432  }
433  }
434 
435  /**
436  * @see WritableConfiguration::renameKey()
437  */
438  public function renameKey($oldname, $newname, $section) {
439  $newname = trim($newname);
440  if (strlen($newname) == 0) {
441  throw new IllegalArgumentException('Empty key names are not allowed!');
442  }
443  if (!$this->hasSection($section)) {
444  throw new IllegalArgumentException('Section \''.$section.'\' does not exist!');
445  }
446  if (!$this->isEditable($section)) {
447  throw new IllegalArgumentException('Section \''.$section.'\' is not editable!');
448  }
449  $lookupEntryOld = $this->lookup($section, $oldname);
450  if ($lookupEntryOld == null) {
451  throw new IllegalArgumentException('Key \''.$oldname.'\' does not exist in section \''.$section.'\'!');
452  }
453  $lookupEntryNew = $this->lookup($section, $newname);
454  if ($lookupEntryNew != null) {
455  throw new IllegalArgumentException('Key \''.$newname.'\' already exists in section \''.$section.'\'!');
456  }
457  // do rename
458  $value = $this->configArray[$lookupEntryOld[0]][$lookupEntryOld[1]];
459  $this->configArray[$lookupEntryOld[0]][$newname] = $value;
460  unset($this->configArray[$lookupEntryOld[0]][$lookupEntryOld[1]]);
461  $this->buildLookupTable();
462  $this->isModified = true;
463  }
464 
465  /**
466  * @see WritableConfiguration::writeConfiguration()
467  */
468  public function writeConfiguration($name) {
469  $filename = $name;
470  $content = "";
471  foreach($this->configArray as $section => $values) {
472  $sectionString = "[".$section."]";
473  $content .= $this->comments[$sectionString];
474  $content .= $sectionString."\n";
475  if (is_array($values)) {
476  foreach($values as $key => $value) {
477  if (is_array($value)) {
478  $value = "{".join(", ", $value)."}";
479  }
480  // unescape double quotes
481  $value = str_replace("\\\"", "\"", $value);
482  $content .= $this->comments[$section][$key];
483  $content .= $key." = ".$value."\n";
484  }
485  }
486  }
487  $content .= $this->comments[';'];
488 
489  if (!$fh = fopen($filename, 'w')) {
490  throw new IOException('Can\'t open ini file \''.$filename.'\'!');
491  }
492 
493  if (!fwrite($fh, $content)) {
494  throw new IOException('Can\'t write ini file \''.$filename.'\'!');
495  }
496  fclose($fh);
497 
498  // notify configuration change listeners
499  $this->configChanged();
500  $this->isModified = false;
501  }
502 
503  /**
504  * Private interface
505  */
506 
507  /**
508  * Load in the ini file specified in filename, and return
509  * the settings in a multidimensional array, with the section names and
510  * settings included. All section names and keys are lowercased.
511  * @param $filename The filename of the ini file to parse
512  * @return An associative array containing the data
513  *
514  * @author: Sebastien Cevey <seb@cine7.net>
515  * Original Code base: <info@megaman.nl>
516  * Added comment handling/Removed process sections flag: Ingo Herwig
517  */
518  protected function parseIniFile($filename) {
519  if (!file_exists($filename)) {
520  throw new ConfigurationException('The config file '.$filename.' does not exist.');
521  }
522  $configArray = [];
523  $sectionName = '';
524  $lines = file($filename);
525  $commentsPending = '';
526  foreach($lines as $line) {
527  $line = trim($line);
528  // comments/blank lines
529  if($line == '' || $line[0] == ';') {
530  $commentsPending .= $line."\n";
531  continue;
532  }
533 
534  if($line[0] == '[' && $line[strlen($line)-1] == ']') {
535  $sectionName = substr($line, 1, strlen($line)-2);
536  $configArray[$sectionName] = [];
537 
538  // store comments/blank lines for section
539  $this->comments[$line] = $commentsPending;
540  $commentsPending = '';
541  }
542  else {
543  $parts = explode('=', $line, 2);
544  $key = trim($parts[0]);
545  $value = trim($parts[1]);
546  $configArray[$sectionName][$key] = $value;
547 
548  // store comments/blank lines for key
549  $this->comments[$sectionName][$key] = $commentsPending;
550  $commentsPending = "";
551  }
552  }
553  // store comments/blank lines from the end of the file
554  $this->comments[';'] = substr($commentsPending, 0, -1);
555 
556  return $configArray;
557  }
558 
559  /**
560  * Process the values in the ini array.
561  * This method turns string values that hold array definitions
562  * (comma separated values enclosed by curly brackets) into array values.
563  */
564  protected function processValues() {
565  array_walk_recursive($this->configArray, [$this, 'processValue']);
566  }
567 
568  /**
569  * Process the values in the ini array.
570  * This method turns string values that hold array definitions
571  * (comma separated values enclosed by curly brackets) into array values.
572  * @param $value A reference to the value
573  */
574  protected function processValue(&$value) {
575  if (!is_array($value)) {
576  // decode encoded (%##) values
577  if (preg_match ("/%/", $value)) {
578  $value = urldecode($value);
579  }
580  // make arrays
581  if(preg_match("/^{.*}$/", $value)) {
582  $arrayValues = StringUtil::quotesplit(substr($value, 1, -1));
583  $value = [];
584  foreach ($arrayValues as $arrayValue) {
585  $value[] = trim($arrayValue);
586  }
587  }
588  }
589  }
590 
591  /**
592  * Merge the second array into the first, preserving entries of the first array
593  * unless the second array contains the special key '__inherit' set to false
594  * or they are re-defined in the second array.
595  * @param $array1 First array.
596  * @param $array2 Second array.
597  * @param $override Boolean whether values defined in array1 should be overridden by values defined in array2.
598  * @return The merged array.
599  */
600  protected function configMerge($array1, $array2, $override) {
601  $result = $array1;
602  foreach(array_keys($array2) as $key) {
603  if (!array_key_exists($key, $result)) {
604  // copy complete section, if new
605  $result[$key] = $array2[$key];
606  }
607  else {
608  // process existing section
609  // remove old keys, if inheritence is disabled
610  $inherit = !isset($array2[$key]['__inherit']) || $array2[$key]['__inherit'] == false;
611  if (!$inherit) {
612  foreach(array_keys($result[$key]) as $subkey) {
613  unset($result[$key][$subkey]);
614  }
615  }
616  // merge in new keys
617  foreach(array_keys($array2[$key]) as $subkey) {
618  if ((array_key_exists($subkey, $result[$key]) && $override) || !isset($result[$key][$subkey])) {
619  $result[$key][$subkey] = $array2[$key][$subkey];
620  }
621  }
622  }
623  }
624  return $result;
625  }
626 
627  /**
628  * Search the given value for a 'include' key in a section named 'config' (case-insensivite)
629  * @param $array The array to search in
630  * @return Mixed
631  */
632  protected function getConfigIncludes($array) {
633  $sectionMatches = null;
634  if (preg_match('/(?:^|,)(config)(?:,|$)/i', join(',', array_keys($array)), $sectionMatches)) {
635  $sectionKey = sizeof($sectionMatches) > 0 ? $sectionMatches[1] : null;
636  if ($sectionKey) {
637  $keyMatches = null;
638  if (preg_match('/(?:^|,)(include)(?:,|$)/i', join(',', array_keys($array[$sectionKey])), $keyMatches)) {
639  return sizeof($keyMatches) > 0 ? $array[$sectionKey][$keyMatches[1]] : null;
640  }
641  }
642  }
643  return null;
644  }
645 
646  /**
647  * Store the instance in the filesystem. If the instance is modified, this call is ignored.
648  */
649  protected function serialize() {
650  if (!$this->isModified()) {
651  $cacheFile = $this->getSerializeFilename($this->addedFiles);
652  if (self::$logger->isDebugEnabled()) {
653  self::$logger->debug("Serialize configuration: ".join(',', $this->addedFiles)." to file: ".$cacheFile);
654  }
655  if ($fh = @fopen($cacheFile, "w")) {
656  if (@fwrite($fh, serialize(get_object_vars($this)))) {
657  @fclose($fh);
658  }
659  }
660  }
661  }
662 
663  /**
664  * Retrieve parsed ini data from the file system and update the current instance.
665  * If the current instance is modified or any file given in parsedFiles
666  * is newer than the serialized data, this call is ignored.
667  * If InifileConfiguration class changed, the call will be ignored as well.
668  * @param $parsedFiles An array of ini filenames that must be contained in the data.
669  * @return Boolean whether the data could be retrieved or not
670  */
671  protected function unserialize($parsedFiles) {
672  if (!$this->isModified()) {
673  $cacheFile = $this->getSerializeFilename($parsedFiles);
674  if (file_exists($cacheFile)) {
675  $parsedFiles[] = __FILE__;
676  if (!$this->checkFileDate($parsedFiles, $cacheFile)) {
677  $vars = unserialize(file_get_contents($cacheFile));
678 
679  // check if included ini files were updated since last cache time
680  $includes = $vars['containedFiles'];
681  if (is_array($includes)) {
682  if ($this->checkFileDate($includes, $cacheFile)) {
683  return false;
684  }
685  }
686 
687  // everything is up-to-date
688  foreach($vars as $key => $val) {
689  $this->$key = $val;
690  }
691  return true;
692  }
693  }
694  }
695  return false;
696  }
697 
698  /**
699  * Get the filename for the serialized data that correspond to the the given ini file sequence.
700  * @param $parsedFiles An array of parsed filenames
701  * @return Filename
702  */
703  protected function getSerializeFilename($parsedFiles) {
704  $path = session_save_path().DIRECTORY_SEPARATOR;
705  $filename = $path.'wcmf_config_'.md5(realpath($this->configPath).'/'.join('_', $parsedFiles));
706  return $filename;
707  }
708 
709  /**
710  * Check if one file in fileList is newer than the referenceFile.
711  * @param $fileList An array of files
712  * @param $referenceFile The file to check against
713  * @return True, if one of the files is newer, false else
714  */
715  protected function checkFileDate($fileList, $referenceFile) {
716  foreach ($fileList as $file) {
717  if (@filemtime($file) > @filemtime($referenceFile)) {
718  return true;
719  }
720  }
721  return false;
722  }
723 
724  /**
725  * Notify configuration change listeners
726  */
727  protected function configChanged() {
728  if (self::$logger->isDebugEnabled()) {
729  self::$logger->debug("Configuration is changed");
730  }
731  if (ObjectFactory::isConfigured()) {
732  if (self::$logger->isDebugEnabled()) {
733  self::$logger->debug("Emitting change event");
734  }
735  ObjectFactory::getInstance('eventManager')->dispatch(ConfigChangeEvent::NAME,
736  new ConfigChangeEvent());
737  }
738  }
739 
740  /**
741  * Build the internal lookup table
742  */
743  protected function buildLookupTable() {
744  $this->lookupTable = [];
745  foreach ($this->configArray as $section => $entry) {
746  // create section entry
747  $lookupSectionKey = strtolower($section.':');
748  $this->lookupTable[$lookupSectionKey] = [$section];
749  // create key entries
750  foreach ($entry as $key => $value) {
751  $lookupKey = strtolower($lookupSectionKey.$key);
752  $this->lookupTable[$lookupKey] = [$section, $key];
753  }
754  }
755  }
756 
757  /**
758  * Lookup section and key.
759  * @param $section The section to lookup
760  * @param $key The key to lookup (optional)
761  * @return Array with section as first entry and key as second or null if not found
762  */
763  protected function lookup($section, $key=null) {
764  $lookupKey = strtolower($section).':'.strtolower($key);
765  if (isset($this->lookupTable[$lookupKey])) {
766  return $this->lookupTable[$lookupKey];
767  }
768  return null;
769  }
770 }
771 ?>
Implementations of WritableConfiguration allow to change the whole or parts of the configuration and ...
configChanged()
Notify configuration change listeners.
configMerge($array1, $array2, $override)
Merge the second array into the first, preserving entries of the first array unless the second array ...
processFile($filename, $configArray=[], $parsedFiles=[])
Process the given file recursively.
processValue(&$value)
Process the values in the ini array.
static getLogger($name)
Get the logger with the given name.
Definition: LogManager.php:37
lookup($section, $key=null)
Lookup section and key.
getConfigPath()
Get the file system path to the configuration files.
getConfigIncludes($array)
Search the given value for a 'include' key in a section named 'config' (case-insensivite) ...
getSerializeFilename($parsedFiles)
Get the filename for the serialized data that correspond to the the given ini file sequence...
processValues()
Process the values in the ini array.
Implementations of Configuration give access to the application configuration.
buildLookupTable()
Build the internal lookup table.
InifileConfiguration reads the application configuration from ini files.
FileUtil provides basic support for file functionality like HTTP file upload.
Definition: FileUtil.php:22
serialize()
Store the instance in the filesystem.
ConfigurationException signals an exception in the configuration.
checkFileDate($fileList, $referenceFile)
Check if one file in fileList is newer than the referenceFile.
unserialize($parsedFiles)
Retrieve parsed ini data from the file system and update the current instance.