Continued:
[core.git] / framework / config / class_FrameworkConfiguration.php
1 <?php
2
3 // Own namespace
4 namespace CoreFramework\Configuration;
5
6 // Import framework stuff
7 use CoreFramework\Console\Tools\ConsoleTools;
8 use CoreFramework\Dns\UnknownHostnameException;
9 use CoreFramework\Generic\FrameworkInterface;
10 use CoreFramework\Generic\NullPointerException;
11 use CoreFramework\Generic\UnsupportedOperationException;
12 use CoreFramework\Object\BaseFrameworkSystem;
13 use CoreFramework\Registry\Registerable;
14
15 // Import SPL stuff
16 use \InvalidArgumentException;
17
18 /**
19  * A class for the configuration stuff implemented in a singleton design paddern
20  *
21  * NOTE: We cannot put this in framework/main/ because it would be loaded (again) in
22  * class loader. See framework/loader/class_ClassLoader.php for instance
23  *
24  * @see                 ClassLoader
25  * @author              Roland Haeder <webmaster@shipsimu.org>
26  * @version             1.0.1
27  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
28  * @license             GNU GPL 3.0 or any newer version
29  * @link                http://www.shipsimu.org
30  *
31  * This program is free software: you can redistribute it and/or modify
32  * it under the terms of the GNU General Public License as published by
33  * the Free Software Foundation, either version 3 of the License, or
34  * (at your option) any later version.
35  *
36  * This program is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
39  * GNU General Public License for more details.
40  *
41  * You should have received a copy of the GNU General Public License
42  * along with this program. If not, see <http://www.gnu.org/licenses/>.
43  */
44 class FrameworkConfiguration implements Registerable {
45
46         /**
47          * The framework's main configuration array which will be initialized with
48          * hard-coded configuration data and might be overwritten/extended by
49          * config data from the database.
50          */
51         private $config = array();
52
53         /**
54          * The configuration instance itself
55          */
56         private static $configInstance = NULL;
57
58         /**
59          * Call-back instance (unused)
60          */
61         private $callbackInstance = NULL;
62
63         // Some constants for the configuration system
64         const EXCEPTION_CONFIG_KEY_IS_EMPTY = 0x130;
65         const EXCEPTION_CONFIG_KEY_WAS_NOT_FOUND = 0x131;
66         const EXCEPTION_CONFIG_VALUE_TYPE_UNSUPPORTED = 0x132;
67
68         /**
69          * Private constructor
70          *
71          * @return      void
72          */
73         private function __construct () {
74                 // Empty for now
75         }
76
77         /**
78          * Compatiblity method to return this class' name
79          *
80          * @return      __CLASS__       This class' name
81          */
82         public function __toString () {
83                 return get_class($this);
84         }
85
86         /**
87          * Getter for a singleton instance of this class
88          *
89          * @return      $configInstance         A singleton instance of this class
90          */
91         public static final function getSelfInstance () {
92                 // is the instance there?
93                 if (is_null(self::$configInstance)) {
94                         // Create a config instance
95                         self::$configInstance = new FrameworkConfiguration();
96                 } // END - if
97
98                 // Return singleton instance
99                 return self::$configInstance;
100         }
101
102         /**
103          * Converts dashes to underscores, e.g. useable for configuration entries
104          *
105          * @param       $str    The string with maybe dashes inside
106          * @return      $str    The converted string with no dashed, but underscores
107          * @throws      NullPointerException    If $str is null
108          * @throws      InvalidArgumentException        If $str is empty
109          */
110         private final function convertDashesToUnderscores ($str) {
111                 // Is it null?
112                 if (is_null($str)) {
113                         // Throw NPE
114                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
115                 } elseif (!is_string($str)) {
116                         // Entry is empty
117                         throw new InvalidArgumentException(sprintf('str[]=%s is not a string', gettype($str)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
118                 } elseif ((is_string($str)) && (empty($str))) {
119                         // Entry is empty
120                         throw new InvalidArgumentException('str is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
121                 }
122
123                 // Convert them all
124                 $str = str_replace('-', '_', $str);
125
126                 // Return converted string
127                 return $str;
128         }
129
130         /**
131          * Setter for default time zone (must be correct!)
132          *
133          * @param       $timezone       The timezone string (e.g. Europe/Berlin)
134          * @return      $success        If timezone was accepted
135          * @throws      NullPointerException    If $timezone is NULL
136          * @throws      InvalidArgumentException        If $timezone is empty
137          */
138         public final function setDefaultTimezone ($timezone) {
139                 // Is it null?
140                 if (is_null($timezone)) {
141                         // Throw NPE
142                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
143                 } elseif (!is_string($timezone)) {
144                         // Is not a string
145                         throw new InvalidArgumentException(sprintf('timezone[]=%s is not a string', gettype($timezone)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
146                 } elseif ((is_string($timezone)) && (empty($timezone))) {
147                         // Entry is empty
148                         throw new InvalidArgumentException('timezone is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
149                 }
150
151                 // Default success
152                 $success = FALSE;
153
154                 /*
155                  * Set desired time zone to prevent date() and related functions to
156                  * issue an E_WARNING.
157                  */
158                 $success = date_default_timezone_set($timezone);
159
160                 // Return status
161                 return $success;
162         }
163
164         /**
165          * Checks whether the given configuration key is set
166          *
167          * @param       $configKey      The configuration key we shall check
168          * @return      $isset  Whether the given configuration key is set
169          * @throws      NullPointerException    If $configKey is NULL
170          * @throws      InvalidArgumentException        If $configKey is empty
171          */
172         public function isConfigurationEntrySet ($configKey) {
173                 // Is it null?
174                 if (is_null($configKey)) {
175                         // Throw NPE
176                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
177                 } elseif (!is_string($configKey)) {
178                         // Is not a string
179                         throw new InvalidArgumentException(sprintf('configKey[]=%s is not a string', gettype($configKey)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
180                 } elseif ((is_string($configKey)) && (empty($configKey))) {
181                         // Entry is empty
182                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
183                 }
184
185                 // Is it set?
186                 $isset = ((isset($this->config[$configKey])) || (array_key_exists($configKey, $this->config)));
187
188                 // Return the result
189                 return $isset;
190         }
191
192         /**
193          * Read a configuration element.
194          *
195          * @param       $configKey              The configuration element
196          * @return      $configValue    The fetched configuration value
197          * @throws      NullPointerException    If $configKey is NULL
198          * @throws      InvalidArgumentException        If $configKey is empty
199          * @throws      NoConfigEntryException          If a configuration element was not found
200          */
201         public function getConfigEntry ($configKey) {
202                 // Is it null?
203                 if (is_null($configKey)) {
204                         // Throw NPE
205                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
206                 } elseif (!is_string($configKey)) {
207                         // Is not a string
208                         throw new InvalidArgumentException(sprintf('configKey[]=%s is not a string', gettype($configKey)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
209                 } elseif ((is_string($configKey)) && (empty($configKey))) {
210                         // Entry is empty
211                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
212                 }
213
214                 // Convert dashes to underscore
215                 $configKey = self::convertDashesToUnderscores($configKey);
216
217                 // Is a valid configuration key provided?
218                 if (!$this->isConfigurationEntrySet($configKey)) {
219                         // Entry was not found!
220                         throw new NoConfigEntryException(array(__CLASS__, $configKey), self::EXCEPTION_CONFIG_KEY_WAS_NOT_FOUND);
221                 } // END - if
222
223                 // Return the requested value
224                 return $this->config[$configKey];
225         }
226
227         /**
228          * Set a configuration key
229          *
230          * @param       $configKey      The configuration key we want to add/change
231          * @param       $configValue    The configuration value we want to set
232          * @return      void
233          * @throws      NullPointerException    If $configKey is NULL
234          * @throws      InvalidArgumentException        If $configKey is empty
235          * @throws      InvalidArgumentException        If $configValue has an unsupported variable type
236          */
237         public final function setConfigEntry ($configKey, $configValue) {
238                 // Is a valid configuration key key provided?
239                 if (is_null($configKey)) {
240                         // Configuration key is null
241                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
242                 } elseif (!is_string($configKey)) {
243                         // Is not a string
244                         throw new InvalidArgumentException(sprintf('configKey[]=%s is not a string', gettype($configKey)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
245                 } elseif ((is_string($configKey)) && (empty($configKey))) {
246                         // Entry is empty
247                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
248                 } elseif ((is_array($configValue)) || (is_object($configValue)) || (is_resource($configValue))) {
249                         // These cannot be set as this is not intended for configuration values, please use FrameworkArrayObject instead.
250                         throw new InvalidArgumentException(sprintf('configValue[]=%s for configKey=%s is not supported.', gettype($configValue), $configKey), self::EXCEPTION_CONFIG_VALUE_TYPE_UNSUPPORTED);
251                 }
252
253                 // Cast to string
254                 $configKey = self::convertDashesToUnderscores($configKey);
255
256                 // Set the configuration value
257                 //* NOISY-DEBUG: */ print(__METHOD__ . ':configEntry=' . $configKey . ',configValue[' . gettype($configValue) . ']=' . $configValue . PHP_EOL);
258                 $this->config[$configKey] = $configValue;
259
260                 // Resort the array
261                 ksort($this->config);
262         }
263
264         /**
265          * Getter for whole configuration array
266          *
267          * @return      $config         Configuration array
268          */
269         public final function getConfigurationArray () {
270                 // Return it
271                 return $this->config;
272         }
273
274         /**
275          * Unset a configuration key, the entry must be there or else an
276          * exception is thrown.
277          *
278          * @param       $configKey      Configuration key to unset
279          * @return      void
280          * @throws      NullPointerException    If $configKey is NULL
281          * @throws      InvalidArgumentException        If $configKey is empty
282          * @throws      NoConfigEntryException  If a configuration element was not found
283          */
284         public final function unsetConfigEntry ($configKey) {
285                 // Validate parameters
286                 if (is_null($configKey)) {
287                         // Configuration key is null
288                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
289                 } elseif (!is_string($configKey)) {
290                         // Entry is empty
291                         throw new InvalidArgumentException(sprintf('configKey[]=%s is not a string', gettype($configKey)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
292                 } elseif ((is_string($configKey)) && (empty($configKey))) {
293                         // Entry is empty
294                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
295                 }
296
297                 // Convert dashes to underscore
298                 $configKey = self::convertDashesToUnderscores($configKey);
299
300                 // Is the configuration key there?
301                 if (!$this->isConfigurationEntrySet($configKey)) {
302                         // Entry was not found!
303                         throw new NoConfigEntryException(array(__CLASS__, $configKey), self::EXCEPTION_CONFIG_KEY_WAS_NOT_FOUND);
304                 } // END - if
305
306                 // Unset it
307                 unset($this->config[$configKey]);
308         }
309
310         /**
311          * Detects the server address (SERVER_ADDR) and set it in configuration
312          *
313          * @return      $serverAddress  The detected server address
314          * @throws      UnknownHostnameException        If SERVER_NAME cannot be resolved to an IP address
315          * @todo        Have to check some more entries from $_SERVER here
316          */
317         public function detectServerAddress () {
318                 // Is the entry set?
319                 if (!$this->isConfigurationEntrySet('server_addr')) {
320                         // Is it set in $_SERVER?
321                         if (isset($_SERVER['SERVER_ADDR'])) {
322                                 // Set it from $_SERVER
323                                 $this->setServerAddress($_SERVER['SERVER_ADDR']);
324                         } elseif (isset($_SERVER['SERVER_NAME'])) {
325                                 // Resolve IP address
326                                 $serverIp = ConsoleTools::resolveIpAddress($_SERVER['SERVER_NAME']);
327
328                                 // Is it valid?
329                                 if ($serverIp === false) {
330                                         /*
331                                          * Why is gethostbyname() returning the host name and not
332                                          * false as many other PHP functions are doing? ;-(
333                                          */
334                                         throw new UnknownHostnameException(sprintf('Cannot resolve "%s" to an IP address. Please fix your setup.', $_SERVER['SERVER_NAME']));
335                                 } // END - if
336
337                                 // Al fine, set it
338                                 $this->setServerAddress($serverIp);
339                         } else {
340                                 // Run auto-detecting through console tools lib
341                                 ConsoleTools::acquireSelfIpAddress();
342                         }
343                 } // END - if
344
345                 // Now get it from configuration
346                 $serverAddress = $this->getServerAddress();
347
348                 // Return it
349                 return $serverAddress;
350         }
351
352         /**
353          * Setter for SERVER_ADDR
354          *
355          * @param       $serverAddress  New SERVER_ADDR value to set
356          * @return      void
357          */
358         public function setServerAddress ($serverAddress) {
359                 // Is a valid configuration key key provided?
360                 if (is_null($serverAddress)) {
361                         // Configuration key is null
362                         throw new NullPointerException($this, BaseFrameworkSystem::EXCEPTION_IS_NULL_POINTER);
363                 } elseif (!is_string($serverAddress)) {
364                         // Is not a string
365                         throw new InvalidArgumentException(sprintf('serverAddress[]=%s is not a string', gettype($serverAddress)), self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
366                 } elseif ((is_string($serverAddress)) && (empty($serverAddress))) {
367                         // Entry is empty
368                         throw new InvalidArgumentException('serverAddress is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
369                 }
370
371                 // Set it, please don't do it yourself here
372                 $this->setConfigEntry('server_addr', (string) $serverAddress);
373         }
374
375         /**
376          * Getter for SERVER_ADDR
377          *
378          * @return      $serverAddress  New SERVER_ADDR value to set
379          */
380         public function getServerAddress () {
381                 return $this->getConfigEntry('server_addr');
382         }
383
384         /**
385          * Detects the HTTPS flag
386          *
387          * @return      $https  The detected HTTPS flag or null if failed
388          */
389         public function detectHttpSecured () {
390                 // Default is null
391                 $https = NULL;
392
393                 // Is HTTPS set?
394                 if ($this->isHttpSecured()) {
395                         // Then use it
396                         $https = $_SERVER['HTTPS'];
397                 } // END - if
398
399                 // Return it
400                 return $https;
401         }
402
403         /**
404          * Checks whether HTTPS is set in $_SERVER
405          *
406          * @return      $isset  Whether HTTPS is set
407          * @todo        Test more fields
408          */
409         public function isHttpSecured () {
410                 return (isset($_SERVER['HTTPS']));
411         }
412
413         /**
414          * Dectect and return the base URL for all URLs and forms
415          *
416          * @return      $baseUrl        Detected base URL
417          */
418         public function detectBaseUrl () {
419                 // Initialize the URL
420                 $protocol = 'http';
421
422                 // Do we have HTTPS?
423                 if ($this->isHttpSecured()) {
424                         // Add the >s< for HTTPS
425                         $protocol = 's';
426                 } // END - if
427
428                 // Construct the full URL and secure it against CSRF attacks
429                 $baseUrl = $protocol . '://' . $this->detectDomain() . $this->detectScriptPath();
430
431                 // Return the URL
432                 return $baseUrl;
433         }
434
435         /**
436          * Detect safely and return the full domain where this script is installed
437          *
438          * @return      $fullDomain             The detected full domain
439          */
440         public function detectDomain () {
441                 // Full domain is localnet.invalid by default
442                 $fullDomain = 'localnet.invalid';
443
444                 // Is the server name there?
445                 if (isset($_SERVER['SERVER_NAME'])) {
446                         // Detect the full domain
447                         $fullDomain = htmlentities(strip_tags($_SERVER['SERVER_NAME']), ENT_QUOTES);
448                 } // END - if
449
450                 // Return it
451                 return $fullDomain;
452         }
453
454         /**
455          * Detect safely the script path without trailing slash which is the glue
456          * between "http://your-domain.invalid/" and "script-name.php"
457          *
458          * @return      $scriptPath             The script path extracted from $_SERVER['SCRIPT_NAME']
459          */
460         public function detectScriptPath () {
461                 // Default is empty
462                 $scriptPath = '';
463
464                 // Is the scriptname set?
465                 if (isset($_SERVER['SCRIPT_NAME'])) {
466                         // Get dirname from it and replace back-slashes with slashes for lame OSes...
467                         $scriptPath = str_replace("\\", '/', dirname($_SERVER['SCRIPT_NAME']));
468                 } // END - if
469
470                 // Return it
471                 return $scriptPath;
472         }
473
474         /**
475          * Getter for field name
476          *
477          * @param       $fieldName              Field name which we shall get
478          * @return      $fieldValue             Field value from the user
479          * @throws      NullPointerException    If the result instance is null
480          */
481         public final function getField ($fieldName) {
482                 // The super interface "FrameworkInterface" requires this
483                 throw new UnsupportedOperationException(array($this, __FUNCTION__), BaseFrameworkSystem::EXCEPTION_UNSPPORTED_OPERATION);
484         }
485
486         /**
487          * Checks if given field is set
488          *
489          * @param       $fieldName      Field name to check
490          * @return      $isSet          Whether the given field name is set
491          * @throws      NullPointerException    If the result instance is null
492          */
493         public function isFieldSet ($fieldName) {
494                 // The super interface "FrameworkInterface" requires this
495                 throw new UnsupportedOperationException(array($this, __FUNCTION__), BaseFrameworkSystem::EXCEPTION_UNSPPORTED_OPERATION);
496         }
497
498         /**
499          * Generates a code for hashes from this class
500          *
501          * @return      $hashCode       The hash code respresenting this class
502          */
503         public function hashCode () {
504                 return crc32($this->__toString());
505         }
506
507         /**
508          * Checks whether an object equals this object. You should overwrite this
509          * method to implement own equality checks
510          *
511          * @param       $objectInstance         An instance of a FrameworkInterface object
512          * @return      $equals                         Whether both objects equals
513          */
514         public function equals (FrameworkInterface $objectInstance) {
515                 // Now test it
516                 $equals = ((
517                                 $this->__toString() === $objectInstance->__toString()
518                                 ) && (
519                                 $this->hashCode() === $objectInstance->hashCode()
520                                 ));
521
522                 // Return the result
523                 return $equals;
524         }
525
526         /**
527          * Setter for call-back instance
528          *
529          * @param       $callbackInstance       An instance of a FrameworkInterface class
530          * @return      void
531          */
532         public function setCallbackInstance (FrameworkInterface $callbackInstance) {
533                 $this->callbackInstance = $callbackInstance;
534         }
535
536 }