Continued:
[core.git] / framework / config / class_FrameworkConfiguration.php
1 <?php
2
3 // Own namespace
4
5 namespace CoreFramework\Configuration;
6
7 // Import framework stuff
8 use CoreFramework\Console\Tools\ConsoleTools;
9 use CoreFramework\Dns\UnknownHostnameException;
10 use CoreFramework\Generic\FrameworkInterface;
11 use CoreFramework\Generic\NullPointerException;
12 use CoreFramework\Generic\UnsupportedOperationException;
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          * Protected constructor
70          *
71          * @return      void
72          */
73         protected 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, self::EXCEPTION_IS_NULL_POINTER);
115                 } elseif (empty($str)) {
116                         // Entry is empty
117                         throw new InvalidArgumentException('str is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
118                 }
119
120                 // Convert them all
121                 $str = str_replace('-', '_', $str);
122
123                 // Return converted string
124                 return $str;
125         }
126
127         /**
128          * Setter for default time zone (must be correct!)
129          *
130          * @param       $zone   The time-zone string (e.g. Europe/Berlin)
131          * @return      void
132          * @throws      NullPointerException    If $zone is NULL
133          * @throws      InvalidArgumentException        If $zone is empty
134          */
135         public final function setDefaultTimezone ($zone) {
136                 // Is it null?
137                 if (is_null($zone)) {
138                         // Throw NPE
139                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
140                 } elseif (empty($zone)) {
141                         // Entry is empty
142                         throw new InvalidArgumentException('zone is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
143                 }
144
145                 // Is PHP version 5.1.0 or higher? Older versions are being ignored
146                 if (version_compare(phpversion(), '5.1.0', '>=')) {
147                         /*
148                          * Set desired time zone to prevent date() and related functions to
149                          * issue a E_WARNING.
150                          */
151                         date_default_timezone_set($zone);
152                 } // END - if
153         }
154
155         /**
156          * Checks whether the given configuration key is set
157          *
158          * @param       $configKey      The configuration key we shall check
159          * @return      $isset  Whether the given configuration key is set
160          * @throws      NullPointerException    If $configKey is NULL
161          * @throws      InvalidArgumentException        If $configKey is empty
162          */
163         public function isConfigurationEntrySet ($configKey) {
164                 // Is it null?
165                 if (is_null($configKey)) {
166                         // Throw NPE
167                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
168                 } elseif (empty($configKey)) {
169                         // Entry is empty
170                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
171                 }
172
173                 // Is it set?
174                 $isset = isset($this->config[$configKey]);
175
176                 // Return the result
177                 return $isset;
178         }
179
180         /**
181          * Read a configuration element.
182          *
183          * @param       $configKey              The configuration element
184          * @return      $configValue    The fetched configuration value
185          * @throws      NullPointerException    If $configKey is NULL
186          * @throws      InvalidArgumentException        If $configKey is empty
187          * @throws      NoConfigEntryException          If a configuration element was not found
188          */
189         public function getConfigEntry ($configKey) {
190                 // Is it null?
191                 if (is_null($configKey)) {
192                         // Throw NPE
193                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
194                 } elseif (empty($configKey)) {
195                         // Entry is empty
196                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
197                 }
198
199                 // Convert dashes to underscore
200                 $configKey = self::convertDashesToUnderscores($configKey);
201
202                 // Is a valid configuration key provided?
203                 if (!$this->isConfigurationEntrySet($configKey)) {
204                         // Entry was not found!
205                         throw new NoConfigEntryException(array(__CLASS__, $configKey), self::EXCEPTION_CONFIG_KEY_WAS_NOT_FOUND);
206                 } // END - if
207
208                 // Return the requested value
209                 return $this->config[$configKey];
210         }
211
212         /**
213          * Set a configuration key
214          *
215          * @param       $configKey      The configuration key we want to add/change
216          * @param       $configValue    The configuration value we want to set
217          * @return      void
218          * @throws      NullPointerException    If $configKey is NULL
219          * @throws      InvalidArgumentException        If $configKey is empty
220          * @throws      ConfigValueTypeUnsupportedException     If $configValue has an unsupported variable type
221          */
222         public final function setConfigEntry ($configKey, $configValue) {
223                 // Is a valid configuration key key provided?
224                 if (is_null($configKey)) {
225                         // Configuration key is null
226                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
227                 } elseif ((empty($configKey)) || (!is_string($configKey))) {
228                         // Entry is empty
229                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
230                 } elseif ((is_null($configValue)) || (is_array($configValue)) || (is_object($configValue)) || (is_resource($configValue))) {
231                         // These cannot be set as this is not intended for configuration values, please use FrameworkArrayObject instead.
232                         throw new ConfigValueTypeUnsupportedException(array($this, $configKey, $configValue), self::EXCEPTION_CONFIG_VALUE_TYPE_UNSUPPORTED);
233                 }
234
235                 // Cast to string
236                 $configKey = self::convertDashesToUnderscores($configKey);
237
238                 // Set the configuration value
239                 //* NOISY-DEBUG: */ print(__METHOD__ . ':configEntry=' . $configKey . ',configValue[' . gettype($configValue) . ']=' . $configValue . PHP_EOL);
240                 $this->config[$configKey] = $configValue;
241
242                 // Resort the array
243                 ksort($this->config);
244         }
245
246         /**
247          * Getter for whole configuration array
248          *
249          * @return      $config         Configuration array
250          */
251         public final function getConfigurationArray () {
252                 // Return it
253                 return $this->config;
254         }
255
256         /**
257          * Unset a configuration key, the entry must be there or else an
258          * exception is thrown.
259          *
260          * @param       $configKey      Configuration key to unset
261          * @return      void
262          * @throws      NullPointerException    If $configKey is NULL
263          * @throws      InvalidArgumentException        If $configKey is empty
264          * @throws      NoConfigEntryException  If a configuration element was not found
265          */
266         public final function unsetConfigEntry ($configKey) {
267                 if (is_null($configKey)) {
268                         // Configuration key is null
269                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
270                 } elseif ((empty($configKey)) || (!is_string($configKey))) {
271                         // Entry is empty
272                         throw new InvalidArgumentException('configKey is empty', self::EXCEPTION_CONFIG_KEY_IS_EMPTY);
273                 }
274
275                 // Convert dashes to underscore
276                 $configKey = self::convertDashesToUnderscores($configKey);
277
278                 // Is the configuration key there?
279                 if (!$this->isConfigurationEntrySet($configKey)) {
280                         // Entry was not found!
281                         throw new NoConfigEntryException(array(__CLASS__, $configKey), self::EXCEPTION_CONFIG_KEY_WAS_NOT_FOUND);
282                 } // END - if
283
284                 // Unset it
285                 unset($this->config[$configKey]);
286         }
287
288         /**
289          * Detects the server address (SERVER_ADDR) and set it in configuration
290          *
291          * @return      $serverAddress  The detected server address
292          * @throws      UnknownHostnameException        If SERVER_NAME cannot be resolved to an IP address
293          * @todo        Have to check some more entries from $_SERVER here
294          */
295         public function detectServerAddress () {
296                 // Is the entry set?
297                 if (!$this->isConfigurationEntrySet('server_addr')) {
298                         // Is it set in $_SERVER?
299                         if (isset($_SERVER['SERVER_ADDR'])) {
300                                 // Set it from $_SERVER
301                                 $this->setServerAddress($_SERVER['SERVER_ADDR']);
302                         } elseif (isset($_SERVER['SERVER_NAME'])) {
303                                 // Resolve IP address
304                                 $serverIp = ConsoleTools::resolveIpAddress($_SERVER['SERVER_NAME']);
305
306                                 // Is it valid?
307                                 if ($serverIp === false) {
308                                         /*
309                                          * Why is gethostbyname() returning the host name and not
310                                          * false as many other PHP functions are doing? ;-(
311                                          */
312                                         throw new UnknownHostnameException(sprintf('Cannot resolve "%s" to an IP address. Please fix your setup.', $_SERVER['SERVER_NAME']));
313                                 } // END - if
314
315                                 // Al fine, set it
316                                 $this->setServerAddress($serverIp);
317                         } else {
318                                 // Run auto-detecting through console tools lib
319                                 ConsoleTools::acquireSelfIPAddress();
320                         }
321                 } // END - if
322
323                 // Now get it from configuration
324                 $serverAddress = $this->getServerAddress();
325
326                 // Return it
327                 return $serverAddress;
328         }
329
330         /**
331          * Setter for SERVER_ADDR
332          *
333          * @param       $serverAddress  New SERVER_ADDR value to set
334          * @return      void
335          */
336         public function setServerAddress ($serverAddress) {
337                 $this->setConfigEntry('server_addr', (string) $serverAddress);
338         }
339
340         /**
341          * Getter for SERVER_ADDR
342          *
343          * @return      $serverAddress  New SERVER_ADDR value to set
344          */
345         public function getServerAddress () {
346                 return $this->getConfigEntry('server_addr');
347         }
348
349         /**
350          * Detects the HTTPS flag
351          *
352          * @return      $isSecured      The detected HTTPS flag or null if failed
353          */
354         public function detectHttpSecured () {
355                 // Default is null
356                 $isSecured = NULL;
357
358                 // Is HTTPS set?
359                 if ($this->isHttpSecured()) {
360                         // Then use it
361                         $isSecured = $_SERVER['HTTPS'];
362                 } // END - if
363
364                 // Return it
365                 return $isSecured;
366         }
367
368         /**
369          * Checks whether HTTPS is set in $_SERVER
370          *
371          * @return $isset       Whether HTTPS is set
372          */
373         public function isHttpSecured () {
374                 return (isset($_SERVER['HTTPS']));
375         }
376
377         /**
378          * Dectect and return the base URL for all URLs and forms
379          *
380          * @return      $baseUrl        Detected base URL
381          */
382         public function detectBaseUrl () {
383                 // Initialize the URL
384                 $baseUrl = 'http';
385
386                 // Do we have HTTPS?
387                 if ($this->isHttpSecured()) {
388                         // Add the >s< for HTTPS
389                         $baseUrl .= 's';
390                 } // END - if
391
392                 // Construct the full URL and secure it against CSRF attacks
393                 $baseUrl = $baseUrl . '://' . $this->detectDomain() . $this->detectScriptPath();
394
395                 // Return the URL
396                 return $baseUrl;
397         }
398
399         /**
400          * Detect safely and return the full domain where this script is installed
401          *
402          * @return      $fullDomain             The detected full domain
403          */
404         public function detectDomain () {
405                 // Full domain is localnet.invalid by default
406                 $fullDomain = 'localnet.invalid';
407
408                 // Is the server name there?
409                 if (isset($_SERVER['SERVER_NAME'])) {
410                         // Detect the full domain
411                         $fullDomain = htmlentities(strip_tags($_SERVER['SERVER_NAME']), ENT_QUOTES);
412                 } // END - if
413
414                 // Return it
415                 return $fullDomain;
416         }
417
418         /**
419          * Detect safely the script path without trailing slash which is the glue
420          * between "http://your-domain.invalid/" and "script-name.php"
421          *
422          * @return      $scriptPath             The script path extracted from $_SERVER['SCRIPT_NAME']
423          */
424         public function detectScriptPath () {
425                 // Default is empty
426                 $scriptPath = '';
427
428                 // Is the scriptname set?
429                 if (isset($_SERVER['SCRIPT_NAME'])) {
430                         // Get dirname from it and replace back-slashes with slashes for lame OSes...
431                         $scriptPath = str_replace("\\", '/', dirname($_SERVER['SCRIPT_NAME']));
432                 } // END - if
433
434                 // Return it
435                 return $scriptPath;
436         }
437
438         /**
439          * Getter for field name
440          *
441          * @param       $fieldName              Field name which we shall get
442          * @return      $fieldValue             Field value from the user
443          * @throws      NullPointerException    If the result instance is null
444          */
445         public final function getField ($fieldName) {
446                 // The super interface "FrameworkInterface" requires this
447                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
448         }
449
450         /**
451          * Checks if given field is set
452          *
453          * @param       $fieldName      Field name to check
454          * @return      $isSet          Whether the given field name is set
455          * @throws      NullPointerException    If the result instance is null
456          */
457         public function isFieldSet ($fieldName) {
458                 // The super interface "FrameworkInterface" requires this
459                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
460         }
461
462         /**
463          * Generates a code for hashes from this class
464          *
465          * @return      $hashCode       The hash code respresenting this class
466          */
467         public function hashCode () {
468                 return crc32($this->__toString());
469         }
470
471         /**
472          * Checks whether an object equals this object. You should overwrite this
473          * method to implement own equality checks
474          *
475          * @param       $objectInstance         An instance of a FrameworkInterface object
476          * @return      $equals                         Whether both objects equals
477          */
478         public function equals (FrameworkInterface $objectInstance) {
479                 // Now test it
480                 $equals = ((
481                                 $this->__toString() === $objectInstance->__toString()
482                                 ) && (
483                                 $this->hashCode() === $objectInstance->hashCode()
484                                 ));
485
486                 // Return the result
487                 return $equals;
488         }
489
490         /**
491          * Setter for call-back instance
492          *
493          * @param       $callbackInstance       An instance of a FrameworkInterface class
494          * @return      void
495          */
496         public function setCallbackInstance (FrameworkInterface $callbackInstance) {
497                 $this->callbackInstance = $callbackInstance;
498         }
499
500 }