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