]> git.mxchange.org Git - core.git/blob - framework/main/classes/class_BaseFrameworkSystem.php
Continued:
[core.git] / framework / main / classes / class_BaseFrameworkSystem.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Object;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Criteria\Criteria;
8 use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint;
9 use Org\Mxchange\CoreFramework\Factory\ObjectFactory;
10 use Org\Mxchange\CoreFramework\Filesystem\PathWriteProtectedException;
11 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
12 use Org\Mxchange\CoreFramework\Generic\NullPointerException;
13 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
14 use Org\Mxchange\CoreFramework\Loader\ClassLoader;
15 use Org\Mxchange\CoreFramework\Manager\ManageableApplication;
16 use Org\Mxchange\CoreFramework\Middleware\Debug\DebugMiddleware;
17 use Org\Mxchange\CoreFramework\Registry\GenericRegistry;
18 use Org\Mxchange\CoreFramework\Result\Database\CachedDatabaseResult;
19 use Org\Mxchange\CoreFramework\State\Stateable;
20 use Org\Mxchange\CoreFramework\Stream\Output\OutputStreamer;
21 use Org\Mxchange\CoreFramework\Utils\String\StringUtils;
22
23 // Import SPL stuff
24 use \stdClass;
25 use \InvalidArgumentException;
26 use \ReflectionClass;
27 use \SplFileInfo;
28
29 /**
30  * The simulator system class is the super class of all other classes. This
31  * class handles saving of games etc.
32  *
33  * @author              Roland Haeder <webmaster@shipsimu.org>
34  * @version             0.0.0
35  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
36  * @license             GNU GPL 3.0 or any newer version
37  * @link                http://www.shipsimu.org
38  *
39  * This program is free software: you can redistribute it and/or modify
40  * it under the terms of the GNU General Public License as published by
41  * the Free Software Foundation, either version 3 of the License, or
42  * (at your option) any later version.
43  *
44  * This program is distributed in the hope that it will be useful,
45  * but WITHOUT ANY WARRANTY; without even the implied warranty of
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47  * GNU General Public License for more details.
48  *
49  * You should have received a copy of the GNU General Public License
50  * along with this program. If not, see <http://www.gnu.org/licenses/>.
51  */
52 abstract class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
53         /**
54          * Length of output from hash()
55          */
56         private static $hashLength = NULL;
57
58         /**
59          * Self-referencing instance
60          */
61         private static $selfInstance = NULL;
62
63         /**
64          * The real class name
65          */
66         private $realClass = __CLASS__;
67
68         /**
69          * State instance
70          */
71         private $stateInstance = NULL;
72
73         /**
74          * Call-back instance
75          */
76         private $callbackInstance = NULL;
77
78         /**
79          * Generic array
80          */
81         private $genericArray = [];
82
83         /***********************
84          * Exception codes.... *
85          ***********************/
86
87         // @todo Try to clean these constants up
88         const EXCEPTION_IS_NULL_POINTER              = 0x001;
89         const EXCEPTION_IS_NO_OBJECT                 = 0x002;
90         const EXCEPTION_IS_NO_ARRAY                  = 0x003;
91         const EXCEPTION_MISSING_METHOD               = 0x004;
92         const EXCEPTION_CLASSES_NOT_MATCHING         = 0x005;
93         const EXCEPTION_INDEX_OUT_OF_BOUNDS          = 0x006;
94         const EXCEPTION_DIMENSION_ARRAY_INVALID      = 0x007;
95         const EXCEPTION_ITEM_NOT_TRADEABLE           = 0x008;
96         const EXCEPTION_ITEM_NOT_IN_PRICE_LIST       = 0x009;
97         const EXCEPTION_GENDER_IS_WRONG              = 0x00a;
98         const EXCEPTION_BIRTH_DATE_IS_INVALID        = 0x00b;
99         const EXCEPTION_EMPTY_STRUCTURES_ARRAY       = 0x00c;
100         const EXCEPTION_HAS_ALREADY_PERSONELL_LIST   = 0x00d;
101         const EXCEPTION_NOT_ENOUGTH_UNEMPLOYEES      = 0x00e;
102         const EXCEPTION_TOTAL_PRICE_NOT_CALCULATED   = 0x00f;
103         const EXCEPTION_HARBOR_HAS_NO_SHIPYARDS      = 0x010;
104         const EXCEPTION_CONTRACT_PARTNER_INVALID     = 0x011;
105         const EXCEPTION_CONTRACT_PARTNER_MISMATCH    = 0x012;
106         const EXCEPTION_CONTRACT_ALREADY_SIGNED      = 0x013;
107         const EXCEPTION_UNEXPECTED_EMPTY_STRING      = 0x014;
108         const EXCEPTION_PATH_NOT_FOUND               = 0x015;
109         const EXCEPTION_INVALID_PATH_NAME            = 0x016;
110         const EXCEPTION_READ_PROTECED_PATH           = 0x017;
111         const EXCEPTION_WRITE_PROTECED_PATH          = 0x018;
112         const EXCEPTION_DIR_POINTER_INVALID          = 0x019;
113         const EXCEPTION_FILE_POINTER_INVALID         = 0x01a;
114         const EXCEPTION_INVALID_RESOURCE             = 0x01b;
115         const EXCEPTION_UNEXPECTED_OBJECT            = 0x01c;
116         const EXCEPTION_LIMIT_ELEMENT_IS_UNSUPPORTED = 0x01d;
117         const EXCEPTION_GETTER_IS_MISSING            = 0x01e;
118         const EXCEPTION_ARRAY_EXPECTED               = 0x01f;
119         const EXCEPTION_ARRAY_HAS_INVALID_COUNT      = 0x020;
120         const EXCEPTION_ID_IS_INVALID_FORMAT         = 0x021;
121         const EXCEPTION_MD5_CHECKSUMS_MISMATCH       = 0x022;
122         const EXCEPTION_UNEXPECTED_STRING_SIZE       = 0x023;
123         const EXCEPTION_SIMULATOR_ID_INVALID         = 0x024;
124         const EXCEPTION_MISMATCHING_COMPRESSORS      = 0x025;
125         const EXCEPTION_CONTAINER_ITEM_IS_NULL       = 0x026;
126         const EXCEPTION_ITEM_IS_NO_ARRAY             = 0x027;
127         const EXCEPTION_CONTAINER_MAYBE_DAMAGED      = 0x028;
128         const EXCEPTION_INVALID_STRING               = 0x029;
129         const EXCEPTION_VARIABLE_NOT_SET             = 0x02a;
130         const EXCEPTION_ATTRIBUTES_ARE_MISSING       = 0x02b;
131         const EXCEPTION_ARRAY_ELEMENTS_MISSING       = 0x02c;
132         const EXCEPTION_TEMPLATE_ENGINE_UNSUPPORTED  = 0x02d;
133         const EXCEPTION_UNSPPORTED_OPERATION         = 0x02e;
134         const EXCEPTION_FACTORY_REQUIRE_PARAMETER    = 0x02f;
135         const EXCEPTION_MISSING_ELEMENT              = 0x030;
136         const EXCEPTION_HEADERS_ALREADY_SENT         = 0x031;
137         const EXCEPTION_DEFAULT_CONTROLLER_GONE      = 0x032;
138         const EXCEPTION_CLASS_NOT_FOUND              = 0x033;
139         const EXCEPTION_REQUIRED_INTERFACE_MISSING   = 0x034;
140         const EXCEPTION_FATAL_ERROR                  = 0x035;
141         const EXCEPTION_FILE_NOT_FOUND               = 0x036;
142         const EXCEPTION_ASSERTION_FAILED             = 0x037;
143         const EXCEPTION_FILE_NOT_REACHABLE           = 0x038;
144         const EXCEPTION_FILE_CANNOT_BE_READ          = 0x039;
145         const EXCEPTION_FILE_CANNOT_BE_WRITTEN       = 0x03a;
146         const EXCEPTION_PATH_CANNOT_BE_WRITTEN       = 0x03b;
147         const EXCEPTION_DATABASE_UPDATED_NOT_ALLOWED = 0x03c;
148         const EXCEPTION_FILTER_CHAIN_INTERCEPTED     = 0x03d;
149         const EXCEPTION_INVALID_SOCKET               = 0x03e;
150         const EXCEPTION_SELF_INSTANCE                = 0x03f;
151
152         /**
153          * Startup time in miliseconds
154          */
155         private static $startupTime = 0;
156
157         /**
158          * Protected super constructor
159          *
160          * @param       $className      Name of the class
161          * @return      void
162          */
163         protected function __construct (string $className) {
164                 // Set real class
165                 $this->setRealClass($className);
166
167                 // Is the startup time set? (0 cannot be true anymore)
168                 if (self::$startupTime == 0) {
169                         // Then set it
170                         self::$startupTime = microtime(true);
171                 }
172         }
173
174         /**
175          * Destructor for all classes. You should not call this method on your own.
176          *
177          * @return      void
178          */
179         public function __destruct () {
180                 // Is this object already destroyed?
181                 if ($this->__toString() != 'DestructedObject') {
182                         // Destroy all informations about this class but keep some text about it alive
183                         $this->setRealClass('DestructedObject');
184                 } elseif ((defined('DEBUG_DESTRUCTOR')) && (is_object($this->getDebugInstance()))) {
185                         // Already destructed object
186                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('The object <span class="object_name">%s</span> is already destroyed.',
187                                 $this->__toString()
188                         ));
189                 } else {
190                         // Do not call this twice
191                         trigger_error(__METHOD__ . ': Called twice.');
192                         exit;
193                 }
194         }
195
196         /**
197          * The __call() method where all non-implemented methods end up
198          *
199          * @param       $methodName             Name of the missing method
200          * @args        $args                   Arguments passed to the method
201          * @return      void
202          */
203         public final function __call (string $methodName, array $args = NULL) {
204                 // Set self-instance
205                 self::$selfInstance = $this;
206
207                 // Call static method
208                 self::__callStatic($methodName, $args);
209
210                 // Clear self-instance
211                 self::$selfInstance = NULL;
212         }
213
214         /**
215          * The __callStatic() method where all non-implemented static methods end up
216          *
217          * @param       $methodName             Name of the missing method
218          * @param       $args                   Arguments passed to the method
219          * @return      void
220          * @throws      InvalidArgumentException If self::$selfInstance is not a framework's own object
221          */
222         public static final function __callStatic (string $methodName, array $args = NULL) {
223                 // Init argument string and class name
224                 //* PRINT-DEBUG: */ printf('[%s:%d]: methodName=%s,args[]=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $methodName, gettype($args));
225                 $argsString = '';
226                 $className = 'unknown';
227
228                 // Is self-instance set?
229                 if (self::$selfInstance instanceof FrameworkInterface) {
230                         // Framework's own instance
231                         $className = self::$selfInstance->__toString();
232                 } elseif (!is_null(self::$selfInstance)) {
233                         // Invalid argument!
234                         throw new InvalidArgumentException(sprintf('self::instance[%s] is not expected.', gettype(self::$selfInstance)), self::EXCEPTION_SELF_INSTANCE);
235                 }
236
237                 // Is it NULL, empty or an array?
238                 if (is_null($args)) {
239                         // No arguments
240                         $argsString = 'NULL';
241                 } elseif (is_array($args)) {
242                         // Start braces
243                         $argsString = '(';
244
245                         // Some arguments are there
246                         foreach ($args as $arg) {
247                                 // Add data about the argument
248                                 $argsString .= gettype($arg) . ':';
249
250                                 if (is_null($arg)) {
251                                         // Found a NULL argument
252                                         $argsString .= 'NULL';
253                                 } elseif (is_string($arg)) {
254                                         // Add length for strings
255                                         $argsString .= strlen($arg);
256                                 } elseif ((is_int($arg)) || (is_float($arg))) {
257                                         // ... integer/float
258                                         $argsString .= $arg;
259                                 } elseif (is_array($arg)) {
260                                         // .. or size if array
261                                         $argsString .= count($arg);
262                                 } elseif (is_object($arg)) {
263                                         // Get reflection
264                                         $reflection = new ReflectionClass($arg);
265
266                                         // Is an other object, maybe no __toString() available
267                                         $argsString .= $reflection->getName();
268                                 } elseif ($arg === true) {
269                                         // ... is boolean 'true'
270                                         $argsString .= 'true';
271                                 } elseif ($arg === false) {
272                                         // ... is boolean 'false'
273                                         $argsString .= 'false';
274                                 }
275
276                                 // Comma for next one
277                                 $argsString .= ', ';
278                         }
279
280                         // Last comma found?
281                         if (substr($argsString, -2, 1) == ',') {
282                                 // Remove last comma
283                                 $argsString = substr($argsString, 0, -2);
284                         }
285
286                         // Close braces
287                         $argsString .= ')';
288                 }
289
290                 // Output stub message
291                 // @TODO __CLASS__ does always return BaseFrameworkSystem but not the extending (=child) class
292                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s::%s]: Stub! Args: %s',
293                         $className,
294                         $methodName,
295                         $argsString
296                 ));
297
298                 // Return nothing
299                 return NULL;
300         }
301
302         /**
303          * Getter for $realClass
304          *
305          * @return      $realClass The name of the real class (not BaseFrameworkSystem)
306          */
307         public function __toString () {
308                 return $this->realClass;
309         }
310
311         /**
312          * Magic method to catch setting of missing but set class fields/attributes
313          *
314          * @param       $name   Name of the field/attribute
315          * @param       $value  Value to store
316          * @return      void
317          */
318         public final function __set (string $name, $value) {
319                 $this->debugBackTrace(sprintf('Tried to set a missing field. name=%s, value[%s]=%s',
320                         $name,
321                         gettype($value),
322                         print_r($value, true)
323                 ));
324         }
325
326         /**
327          * Magic method to catch getting of missing fields/attributes
328          *
329          * @param       $name   Name of the field/attribute
330          * @return      void
331          */
332         public final function __get (string $name) {
333                 $this->debugBackTrace(sprintf('Tried to get a missing field. name=%s',
334                         $name
335                 ));
336         }
337
338         /**
339          * Magic method to catch unsetting of missing fields/attributes
340          *
341          * @param       $name   Name of the field/attribute
342          * @return      void
343          */
344         public final function __unset (string $name) {
345                 $this->debugBackTrace(sprintf('Tried to unset a missing field. name=%s',
346                         $name
347                 ));
348         }
349
350         /**
351          * Magic method to catch object serialization
352          *
353          * @return      $unsupported    Unsupported method
354          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
355          */
356         public final function __sleep () {
357                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
358         }
359
360         /**
361          * Magic method to catch object deserialization
362          *
363          * @return      $unsupported    Unsupported method
364          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
365          */
366         public final function __wakeup () {
367                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
368         }
369
370         /**
371          * Magic method to catch calls when an object instance is called
372          *
373          * @return      $unsupported    Unsupported method
374          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
375          */
376         public final function __invoke () {
377                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
378         }
379
380         /**
381          * Setter for the real class name
382          *
383          * @param       $realClass      Class name (string)
384          * @return      void
385          */
386         public final function setRealClass (string $realClass) {
387                 // Set real class
388                 $this->realClass = $realClass;
389         }
390
391         /**
392          * Setter for debug instance
393          *
394          * @param       $debugInstance  The instance for debug output class
395          * @return      void
396          */
397         public final function setDebugInstance (DebugMiddleware $debugInstance) {
398                 GenericRegistry::getRegistry()->addInstance('debug', $debugInstance);
399         }
400
401         /**
402          * Getter for debug instance
403          *
404          * @return      $debugInstance  Instance to class DebugConsoleOutput or DebugWebOutput
405          */
406         public final function getDebugInstance () {
407                 // Get debug instance
408                 $debugInstance = GenericRegistry::getRegistry()->getInstance('debug');
409
410                 // Return it
411                 return $debugInstance;
412         }
413
414         /**
415          * Setter for web output instance
416          *
417          * @param       $webInstance    The instance for web output class
418          * @return      void
419          */
420         public final function setWebOutputInstance (OutputStreamer $webInstance) {
421                 GenericRegistry::getRegistry()->addInstance('web_output', $webInstance);
422         }
423
424         /**
425          * Getter for web output instance
426          *
427          * @return      $webOutputInstance - Instance to class WebOutput
428          */
429         public final function getWebOutputInstance () {
430                 $webOutputInstance = GenericRegistry::getRegistry()->getInstance('web_output');
431                 return $webOutputInstance;
432         }
433
434         /**
435          * Setter for state instance
436          *
437          * @param       $stateInstance  A Stateable instance
438          * @return      void
439          */
440         public final function setStateInstance (Stateable $stateInstance) {
441                 $this->stateInstance = $stateInstance;
442         }
443
444         /**
445          * Getter for state instance
446          *
447          * @return      $stateInstance  A Stateable instance
448          */
449         public final function getStateInstance () {
450                 return $this->stateInstance;
451         }
452
453         /**
454          * Setter for call-back instance
455          *
456          * @param       $callbackInstance       An instance of a FrameworkInterface class
457          * @return      void
458          */
459         public final function setCallbackInstance (FrameworkInterface $callbackInstance) {
460                 $this->callbackInstance = $callbackInstance;
461         }
462
463         /**
464          * Getter for call-back instance
465          *
466          * @return      $callbackInstance       An instance of a FrameworkInterface class
467          */
468         protected final function getCallbackInstance () {
469                 return $this->callbackInstance;
470         }
471
472         /**
473          * Checks whether an object equals this object. You should overwrite this
474          * method to implement own equality checks
475          *
476          * @param       $objectInstance         An instance of a FrameworkInterface object
477          * @return      $equals                         Whether both objects equals
478          */
479         public function equals (FrameworkInterface $objectInstance) {
480                 // Now test it
481                 $equals = ((
482                         $this->__toString() == $objectInstance->__toString()
483                 ) && (
484                         $this->hashCode() == $objectInstance->hashCode()
485                 ));
486
487                 // Return the result
488                 return $equals;
489         }
490
491         /**
492          * Generates a generic hash code of this class. You should really overwrite
493          * this method with your own hash code generator code. But keep KISS in mind.
494          *
495          * @return      $hashCode       A generic hash code respresenting this whole class
496          */
497         public function hashCode () {
498                 // Simple hash code
499                 return crc32($this->__toString());
500         }
501
502         /**
503          * Appends a trailing slash to a string
504          *
505          * @param       $str    A string (maybe) without trailing slash
506          * @return      $str    A string with an auto-appended trailing slash
507          */
508         public final function addMissingTrailingSlash ($str) {
509                 // Is there a trailing slash?
510                 if (substr($str, -1, 1) != '/') {
511                         $str .= '/';
512                 }
513
514                 // Return string with trailing slash
515                 return $str;
516         }
517
518         /**
519          * Debugs this instance by putting out it's full content
520          *
521          * @param       $message        Optional message to show in debug output
522          * @return      void
523          */
524         public final function debugInstance ($message = '') {
525                 // Restore the error handler to avoid trouble with missing array elements or undeclared variables
526                 restore_error_handler();
527
528                 // Init content
529                 $content = '';
530
531                 // Is a message set?
532                 if (!empty($message)) {
533                         // Construct message
534                         $content = sprintf('<div class="debug_message">
535         Message: %s
536 </div>' . PHP_EOL, $message);
537                 }
538
539                 // Generate the output
540                 $content .= sprintf('<pre>%s</pre>',
541                         trim(
542                                 htmlentities(
543                                         print_r($this, true)
544                                 )
545                         )
546                 );
547
548                 // Output it
549                 ApplicationEntryPoint::exitApplication(sprintf('<div class="debug_header">
550         %s debug output:
551 </div>
552 <div class="debug_content">
553         %s
554 </div>
555 Loaded includes:
556 <div class="debug_include_list">
557         %s
558 </div>',
559                         $this->__toString(),
560                         $content,
561                         ClassLoader::getSelfInstance()->getPrintableIncludeList()
562                 ));
563         }
564
565         /**
566          * Replaces control characters with printable output
567          *
568          * @param       $str    String with control characters
569          * @return      $str    Replaced string
570          */
571         protected function replaceControlCharacters ($str) {
572                 // Replace them
573                 $str = str_replace(
574                         chr(13), '[r]', str_replace(
575                         chr(10), '[n]', str_replace(
576                         chr(9) , '[t]',
577                         $str
578                 )));
579
580                 // Return it
581                 return $str;
582         }
583
584         /**
585          * Output a partial stub message for the caller method
586          *
587          * @param       $message        An optional message to display
588          * @return      void
589          */
590         protected function partialStub ($message = '') {
591                 // Init variable
592                 $stubMessage = 'Partial stub!';
593
594                 // Is an extra message given?
595                 if (!empty($message)) {
596                         // Then add it as well
597                         $stubMessage .= ' Message: ' . $message;
598                 }
599
600                 // Debug instance is there?
601                 if (!is_null($this->getDebugInstance())) {
602                         // Output stub message
603                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput($stubMessage);
604                 } else {
605                         // Trigger an error
606                         trigger_error($stubMessage);
607                         exit;
608                 }
609         }
610
611         /**
612          * Outputs a debug backtrace and stops further script execution
613          *
614          * @param       $message        An optional message to output
615          * @param       $doExit         Whether exit the program (true is default)
616          * @return      void
617          */
618         public function debugBackTrace ($message = '', $doExit = true) {
619                 // Sorry, there is no other way getting this nice backtrace
620                 if (!empty($message)) {
621                         // Output message
622                         printf('Message: %s<br />' . PHP_EOL, $message);
623                 }
624
625                 print('<pre>');
626                 debug_print_backtrace();
627                 print('</pre>');
628
629                 // Exit program?
630                 if ($doExit === true) {
631                         exit();
632                 }
633         }
634
635         /**
636          * Creates an instance of a debugger instance
637          *
638          * @param       $className              Name of the class (currently unsupported)
639          * @param       $lineNumber             Line number where the call was made
640          * @return      $debugInstance  An instance of a debugger class
641          * @deprecated  Not fully, as the new Logger facilities are not finished yet.
642          */
643         public final static function createDebugInstance ($className, $lineNumber = NULL) {
644                 // Is the instance set?
645                 if (!GenericRegistry::getRegistry()->instanceExists('debug')) {
646                         // Init debug instance
647                         $debugInstance = NULL;
648
649                         // Try it
650                         try {
651                                 // Get a debugger instance
652                                 $debugInstance = DebugMiddleware::createDebugMiddleware(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('debug_' . FrameworkBootstrap::getRequestTypeFromSystem() . '_class'), $className);
653                         } catch (NullPointerException $e) {
654                                 // Didn't work, no instance there
655                                 exit(sprintf('Cannot create debugInstance! Exception=%s,message=%s,className=%s,lineNumber=%d' . PHP_EOL, $e->__toString(), $e->getMessage(), $className, $lineNumber));
656                         }
657
658                         // Empty string should be ignored and used for testing the middleware
659                         DebugMiddleware::getSelfInstance()->output('');
660
661                         // Set it in registry
662                         GenericRegistry::getRegistry()->addInstance('debug', $debugInstance);
663                 } else {
664                         // Get instance from registry
665                         $debugInstance = GenericRegistry::getRegistry()->getDebugInstance();
666                 }
667
668                 // Return it
669                 return $debugInstance;
670         }
671
672         /**
673          * Simple output of a message with line-break
674          *
675          * @param       $message        Message to output
676          * @return      void
677          */
678         public function outputLine ($message) {
679                 // Simply output it
680                 print($message . PHP_EOL);
681         }
682
683         /**
684          * Outputs a debug message whether to debug instance (should be set!) or
685          * dies with or ptints the message. Do NEVER EVER rewrite the exit() call to
686          * ApplicationEntryPoint::app_exit(), this would cause an endless loop.
687          *
688          * @param       $message        Message we shall send out...
689          * @param       $doPrint        Whether print or die here (default: print)
690          * @paran       $stripTags      Whether to strip tags (default: false)
691          * @return      void
692          */
693         public function debugOutput ($message, $doPrint = true, $stripTags = false) {
694                 // Set debug instance to NULL
695                 $debugInstance = NULL;
696
697                 // Get backtrace
698                 $backtrace = debug_backtrace(!DEBUG_BACKTRACE_PROVIDE_OBJECT);
699
700                 // Is function partialStub/__callStatic ?
701                 if (in_array($backtrace[1]['function'], array('partialStub', '__call', '__callStatic'))) {
702                         // Prepend class::function:line from 3rd element
703                         $message = sprintf('[%s::%s:%d]: %s',
704                                 $backtrace[2]['class'],
705                                 $backtrace[2]['function'],
706                                 (isset($backtrace[2]['line']) ? $backtrace[2]['line'] : '0'),
707                                 $message
708                         );
709                 } else {
710                         // Prepend class::function:line from 2nd element
711                         $message = sprintf('[%s::%s:%d]: %s',
712                                 $backtrace[1]['class'],
713                                 $backtrace[1]['function'],
714                                 (isset($backtrace[1]['line']) ? $backtrace[1]['line'] : '0'),
715                                 $message
716                         );
717                 }
718
719                 // Try it:
720                 try {
721                         // Get debug instance
722                         $debugInstance = $this->getDebugInstance();
723                 } catch (NullPointerException $e) {
724                         // The debug instance is not set (yet)
725                 }
726
727                 // Is the debug instance there?
728                 if (is_object($debugInstance)) {
729                         // Use debug output handler
730                         $debugInstance->output($message, $stripTags);
731
732                         if ($doPrint === false) {
733                                 // Die here if not printed
734                                 exit();
735                         }
736                 } else {
737                         // Are debug times enabled?
738                         if (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('debug_' . FrameworkBootstrap::getRequestTypeFromSystem() . '_output_timings') == 'Y') {
739                                 // Prepent it
740                                 $message = $this->getPrintableExecutionTime() . $message;
741                         }
742
743                         // Put directly out
744                         if ($doPrint === true) {
745                                 // Print message
746                                 $this->outputLine($message);
747                         } else {
748                                 // Die here
749                                 exit($message);
750                         }
751                 }
752         }
753
754         /**
755          * Marks up the code by adding e.g. line numbers
756          *
757          * @param       $phpCode                Unmarked PHP code
758          * @return      $markedCode             Marked PHP code
759          */
760         public function markupCode ($phpCode) {
761                 // Init marked code
762                 $markedCode = '';
763
764                 // Get last error
765                 $errorArray = error_get_last();
766
767                 // Init the code with error message
768                 if (is_array($errorArray)) {
769                         // Get error infos
770                         $markedCode = sprintf('<div id="error_header">File: <span id="error_data">%s</span>, Line: <span id="error_data">%s</span>, Message: <span id="error_data">%s</span>, Type: <span id="error_data">%s</span></div>',
771                                 basename($errorArray['file']),
772                                 $errorArray['line'],
773                                 $errorArray['message'],
774                                 $errorArray['type']
775                         );
776                 }
777
778                 // Add line number to the code
779                 foreach (explode(chr(10), $phpCode) as $lineNo => $code) {
780                         // Add line numbers
781                         $markedCode .= sprintf('<span id="code_line">%s</span>: %s' . PHP_EOL,
782                                 ($lineNo + 1),
783                                 htmlentities($code, ENT_QUOTES)
784                         );
785                 }
786
787                 // Return the code
788                 return $markedCode;
789         }
790
791         /**
792          * "Getter" for databse entry
793          *
794          * @return      $entry  An array with database entries
795          * @throws      NullPointerException    If the database result is not found
796          * @throws      InvalidDatabaseResultException  If the database result is invalid
797          * @todo        Monolithic method, should be moved to proper classes
798          */
799         protected final function getDatabaseEntry () {
800                 // Is there an instance?
801                 if (!$this->getResultInstance() instanceof SearchableResult) {
802                         // Throw an exception here
803                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
804                 }
805
806                 // Rewind it
807                 $this->getResultInstance()->rewind();
808
809                 // Do we have an entry?
810                 if ($this->getResultInstance()->valid() === false) {
811                         // @TODO Move the constant to e.g. BaseDatabaseResult when there is a non-cached database result available
812                         throw new InvalidDatabaseResultException(array($this, $this->getResultInstance()), CachedDatabaseResult::EXCEPTION_INVALID_DATABASE_RESULT);
813                 }
814
815                 // Get next entry
816                 $this->getResultInstance()->next();
817
818                 // Fetch it
819                 $entry = $this->getResultInstance()->current();
820
821                 // And return it
822                 return $entry;
823         }
824
825         /**
826          * Getter for field name
827          *
828          * @param       $fieldName              Field name which we shall get
829          * @return      $fieldValue             Field value from the user
830          * @throws      NullPointerException    If the result instance is null
831          */
832         public final function getField (string $fieldName) {
833                 // Default field value
834                 $fieldValue = NULL;
835
836                 // Get result instance
837                 $resultInstance = $this->getResultInstance();
838
839                 // Is this instance null?
840                 if (is_null($resultInstance)) {
841                         // Then the user instance is no longer valid (expired cookies?)
842                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
843                 }
844
845                 // Get current array
846                 $fieldArray = $resultInstance->current();
847                 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput($fieldName.':<pre>'.print_r($fieldArray, true).'</pre>');
848
849                 // Convert dashes to underscore
850                 $fieldName2 = StringUtils::convertDashesToUnderscores($fieldName);
851
852                 // Does the field exist?
853                 if ($this->isFieldSet($fieldName)) {
854                         // Get it
855                         $fieldValue = $fieldArray[$fieldName2];
856                 } elseif (defined('DEVELOPER')) {
857                         // Missing field entry, may require debugging
858                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']:fieldArray<pre>=' . print_r($fieldArray, true) . '</pre>,fieldName=' . $fieldName . ' not found!');
859                 } else {
860                         // Missing field entry, may require debugging
861                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']:fieldName=' . $fieldName . ' not found!');
862                 }
863
864                 // Return it
865                 return $fieldValue;
866         }
867
868         /**
869          * Checks if given field is set
870          *
871          * @param       $fieldName      Field name to check
872          * @return      $isSet          Whether the given field name is set
873          * @throws      NullPointerException    If the result instance is null
874          */
875         public function isFieldSet (string $fieldName) {
876                 // Get result instance
877                 $resultInstance = $this->getResultInstance();
878
879                 // Is this instance null?
880                 if (is_null($resultInstance)) {
881                         // Then the user instance is no longer valid (expired cookies?)
882                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
883                 }
884
885                 // Get current array
886                 $fieldArray = $resultInstance->current();
887                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . $this->__toString() . ':' . __LINE__ . '] fieldName=' . $fieldName . ',fieldArray=<pre>'.print_r($fieldArray, true).'</pre>');
888
889                 // Convert dashes to underscore
890                 $fieldName = StringUtils::convertDashesToUnderscores($fieldName);
891
892                 // Determine it
893                 $isSet = isset($fieldArray[$fieldName]);
894
895                 // Return result
896                 return $isSet;
897         }
898
899         /**
900          * Outputs a deprecation warning to the developer.
901          *
902          * @param       $message        The message we shall output to the developer
903          * @return      void
904          * @todo        Write a logging mechanism for productive mode
905          */
906         public function deprecationWarning ($message) {
907                 // Is developer mode active?
908                 if (defined('DEVELOPER')) {
909                         // Debug instance is there?
910                         if (!is_null($this->getDebugInstance())) {
911                                 // Output stub message
912                                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput($message);
913                         } else {
914                                 // Trigger an error
915                                 trigger_error($message . "<br />\n");
916                                 exit;
917                         }
918                 } else {
919                         // @TODO Finish this part!
920                         $this->partialStub('Developer mode inactive. Message:' . $message);
921                 }
922         }
923
924         /**
925          * Checks whether the given PHP extension is loaded
926          *
927          * @param       $phpExtension   The PHP extension we shall check
928          * @return      $isLoaded       Whether the PHP extension is loaded
929          */
930         public final function isPhpExtensionLoaded ($phpExtension) {
931                 // Is it loaded?
932                 $isLoaded = in_array($phpExtension, get_loaded_extensions());
933
934                 // Return result
935                 return $isLoaded;
936         }
937
938         /**
939          * "Getter" as a time() replacement but with milliseconds. You should use this
940          * method instead of the encapsulated getimeofday() function.
941          *
942          * @return      $milliTime      Timestamp with milliseconds
943          */
944         public function getMilliTime () {
945                 // Get the time of day as float
946                 $milliTime = gettimeofday(true);
947
948                 // Return it
949                 return $milliTime;
950         }
951
952         /**
953          * Idles (sleeps) for given milliseconds
954          *
955          * @return      $hasSlept       Whether it goes fine
956          */
957         public function idle ($milliSeconds) {
958                 // Sleep is fine by default
959                 $hasSlept = true;
960
961                 // Idle so long with found function
962                 if (function_exists('time_sleep_until')) {
963                         // Get current time and add idle time
964                         $sleepUntil = $this->getMilliTime() + abs($milliSeconds) / 1000;
965
966                         // New PHP 5.1.0 function found, ignore errors
967                         $hasSlept = @time_sleep_until($sleepUntil);
968                 } else {
969                         /*
970                          * My Sun station doesn't have that function even with latest PHP
971                          * package. :(
972                          */
973                         usleep($milliSeconds * 1000);
974                 }
975
976                 // Return result
977                 return $hasSlept;
978         }
979
980         /**
981          * Checks whether the given encoded data was encoded with Base64
982          *
983          * @param       $encodedData    Encoded data we shall check
984          * @return      $isBase64               Whether the encoded data is Base64
985          */
986         protected function isBase64Encoded ($encodedData) {
987                 // Determine it
988                 $isBase64 = (@base64_decode($encodedData, true) !== false);
989
990                 // Return it
991                 return $isBase64;
992         }
993
994         /**
995          * Gets a cache key from Criteria instance
996          *
997          * @param       $criteriaInstance       An instance of a Criteria class
998          * @param       $onlyKeys                       Only use these keys for a cache key
999          * @return      $cacheKey                       A cache key suitable for lookup/storage purposes
1000          */
1001         protected function getCacheKeyByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
1002                 // Generate it
1003                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: criteriaInstance=' . $criteriaInstance->__toString() . ',onlyKeys()=' . count($onlyKeys) . ' - CALLED!');
1004                 $cacheKey = sprintf('%s@%s',
1005                         $this->__toString(),
1006                         $criteriaInstance->getCacheKey($onlyKeys)
1007                 );
1008
1009                 // And return it
1010                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: cacheKey=' . $cacheKey . ' - EXIT!');
1011                 return $cacheKey;
1012         }
1013
1014         /**
1015          * Getter for startup time in miliseconds
1016          *
1017          * @return      $startupTime    Startup time in miliseconds
1018          */
1019         protected function getStartupTime () {
1020                 return self::$startupTime;
1021         }
1022
1023         /**
1024          * "Getter" for a printable currently execution time in nice braces
1025          *
1026          * @return      $executionTime  Current execution time in nice braces
1027          */
1028         protected function getPrintableExecutionTime () {
1029                 // Caculate the execution time
1030                 $executionTime = microtime(true) - $this->getStartupTime();
1031
1032                 // Pack it in nice braces
1033                 $executionTime = sprintf('[ %01.5f ] ', $executionTime);
1034
1035                 // And return it
1036                 return $executionTime;
1037         }
1038
1039         /**
1040          * Hashes a given string with a simple but stronger hash function (no salt)
1041          * and hex-encode it.
1042          *
1043          * @param       $str    The string to be hashed
1044          * @return      $hash   The hash from string $str
1045          */
1046         public static final function hash ($str) {
1047                 // Hash given string with (better secure) hasher
1048                 $hash = bin2hex(mhash(MHASH_SHA256, $str));
1049
1050                 // Return it
1051                 return $hash;
1052         }
1053
1054         /**
1055          * "Getter" for length of hash() output. This will be "cached" to speed up
1056          * things.
1057          *
1058          * @return      $length         Length of hash() output
1059          */
1060         public static final function getHashLength () {
1061                 // Is it cashed?
1062                 if (is_null(self::$hashLength)) {
1063                         // No, then hash a string and save its length.
1064                         self::$hashLength = strlen(self::hash('abc123'));
1065                 }
1066
1067                 // Return it
1068                 return self::$hashLength;
1069         }
1070
1071         /**
1072          * Checks whether the given number is really a number (only chars 0-9).
1073          *
1074          * @param       $num            A string consisting only chars between 0 and 9
1075          * @param       $castValue      Whether to cast the value to double. Do only use this to secure numbers from Requestable classes.
1076          * @param       $assertMismatch         Whether to assert mismatches
1077          * @return      $ret            The (hopefully) secured numbered value
1078          */
1079         public function bigintval ($num, $castValue = true, $assertMismatch = false) {
1080                 // Filter all numbers out
1081                 $ret = preg_replace('/[^0123456789]/', '', $num);
1082
1083                 // Shall we cast?
1084                 if ($castValue === true) {
1085                         // Cast to biggest numeric type
1086                         $ret = (double) $ret;
1087                 }
1088
1089                 // Assert only if requested
1090                 if ($assertMismatch === true) {
1091                         // Has the whole value changed?
1092                         assert(('' . $ret . '' != '' . $num . '') && (!is_null($num)));
1093                 }
1094
1095                 // Return result
1096                 return $ret;
1097         }
1098
1099         /**
1100          * Checks whether the given hexadecimal number is really a hex-number (only chars 0-9,a-f).
1101          *
1102          * @param       $num    A string consisting only chars between 0 and 9
1103          * @param       $assertMismatch         Whether to assert mismatches
1104          * @return      $ret    The (hopefully) secured hext-numbered value
1105          */
1106         public function hexval ($num, $assertMismatch = false) {
1107                 // Filter all numbers out
1108                 $ret = preg_replace('/[^0123456789abcdefABCDEF]/', '', $num);
1109
1110                 // Assert only if requested
1111                 if ($assertMismatch === true) {
1112                         // Has the whole value changed?
1113                         assert(('' . $ret . '' != '' . $num . '') && (!is_null($num)));
1114                 }
1115
1116                 // Return result
1117                 return $ret;
1118         }
1119
1120         /**
1121          * Determines if an element is set in the generic array
1122          *
1123          * @param       $keyGroup       Main group for the key
1124          * @param       $subGroup       Sub group for the key
1125          * @param       $key            Key to check
1126          * @param       $element        Element to check
1127          * @return      $isset          Whether the given key is set
1128          */
1129         protected final function isGenericArrayElementSet ($keyGroup, $subGroup, $key, $element) {
1130                 // Debug message
1131                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1132
1133                 // Is it there?
1134                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1135
1136                 // Return it
1137                 return $isset;
1138         }
1139         /**
1140          * Determines if a key is set in the generic array
1141          *
1142          * @param       $keyGroup       Main group for the key
1143          * @param       $subGroup       Sub group for the key
1144          * @param       $key            Key to check
1145          * @return      $isset          Whether the given key is set
1146          */
1147         protected final function isGenericArrayKeySet ($keyGroup, $subGroup, $key) {
1148                 // Debug message
1149                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1150
1151                 // Is it there?
1152                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key]);
1153
1154                 // Return it
1155                 return $isset;
1156         }
1157
1158
1159         /**
1160          * Determines if a group is set in the generic array
1161          *
1162          * @param       $keyGroup       Main group
1163          * @param       $subGroup       Sub group
1164          * @return      $isset          Whether the given group is set
1165          */
1166         protected final function isGenericArrayGroupSet ($keyGroup, $subGroup) {
1167                 // Debug message
1168                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1169
1170                 // Is it there?
1171                 $isset = isset($this->genericArray[$keyGroup][$subGroup]);
1172
1173                 // Return it
1174                 return $isset;
1175         }
1176
1177         /**
1178          * Getter for sub key group
1179          *
1180          * @param       $keyGroup       Main key group
1181          * @param       $subGroup       Sub key group
1182          * @return      $array          An array with all array elements
1183          */
1184         protected final function getGenericSubArray ($keyGroup, $subGroup) {
1185                 // Is it there?
1186                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1187                         // No, then abort here
1188                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1189                         exit;
1190                 }
1191
1192                 // Debug message
1193                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',value=' . print_r($this->genericArray[$keyGroup][$subGroup], true));
1194
1195                 // Return it
1196                 return $this->genericArray[$keyGroup][$subGroup];
1197         }
1198
1199         /**
1200          * Unsets a given key in generic array
1201          *
1202          * @param       $keyGroup       Main group for the key
1203          * @param       $subGroup       Sub group for the key
1204          * @param       $key            Key to unset
1205          * @return      void
1206          */
1207         protected final function unsetGenericArrayKey ($keyGroup, $subGroup, $key) {
1208                 // Debug message
1209                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1210
1211                 // Remove it
1212                 unset($this->genericArray[$keyGroup][$subGroup][$key]);
1213         }
1214
1215         /**
1216          * Unsets a given element in generic array
1217          *
1218          * @param       $keyGroup       Main group for the key
1219          * @param       $subGroup       Sub group for the key
1220          * @param       $key            Key to unset
1221          * @param       $element        Element to unset
1222          * @return      void
1223          */
1224         protected final function unsetGenericArrayElement ($keyGroup, $subGroup, $key, $element) {
1225                 // Debug message
1226                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1227
1228                 // Remove it
1229                 unset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1230         }
1231
1232         /**
1233          * Append a string to a given generic array key
1234          *
1235          * @param       $keyGroup       Main group for the key
1236          * @param       $subGroup       Sub group for the key
1237          * @param       $key            Key to unset
1238          * @param       $value          Value to add/append
1239          * @return      void
1240          */
1241         protected final function appendStringToGenericArrayKey ($keyGroup, $subGroup, $key, $value, $appendGlue = '') {
1242                 // Debug message
1243                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true) . ',appendGlue=' . $appendGlue);
1244
1245                 // Is it already there?
1246                 if ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1247                         // Append it
1248                         $this->genericArray[$keyGroup][$subGroup][$key] .= $appendGlue . (string) $value;
1249                 } else {
1250                         // Add it
1251                         $this->genericArray[$keyGroup][$subGroup][$key] = (string) $value;
1252                 }
1253         }
1254
1255         /**
1256          * Append a string to a given generic array element
1257          *
1258          * @param       $keyGroup       Main group for the key
1259          * @param       $subGroup       Sub group for the key
1260          * @param       $key            Key to unset
1261          * @param       $element        Element to check
1262          * @param       $value          Value to add/append
1263          * @return      void
1264          */
1265         protected final function appendStringToGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value, $appendGlue = '') {
1266                 // Debug message
1267                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true) . ',appendGlue=' . $appendGlue);
1268
1269                 // Is it already there?
1270                 if ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1271                         // Append it
1272                         $this->genericArray[$keyGroup][$subGroup][$key][$element] .= $appendGlue . (string) $value;
1273                 } else {
1274                         // Add it
1275                         $this->setStringGenericArrayElement($keyGroup, $subGroup, $key, $element, $value);
1276                 }
1277         }
1278
1279         /**
1280          * Sets a string in a given generic array element
1281          *
1282          * @param       $keyGroup       Main group for the key
1283          * @param       $subGroup       Sub group for the key
1284          * @param       $key            Key to unset
1285          * @param       $element        Element to check
1286          * @param       $value          Value to add/append
1287          * @return      void
1288          */
1289         protected final function setStringGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value, $appendGlue = '') {
1290                 // Debug message
1291                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true) . ',appendGlue=' . $appendGlue);
1292
1293                 // Set it
1294                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = (string) $value;
1295         }
1296
1297         /**
1298          * Initializes given generic array group
1299          *
1300          * @param       $keyGroup       Main group for the key
1301          * @param       $subGroup       Sub group for the key
1302          * @param       $key            Key to use
1303          * @param       $forceInit      Optionally force initialization
1304          * @return      void
1305          */
1306         protected final function initGenericArrayGroup ($keyGroup, $subGroup, $forceInit = false) {
1307                 // Debug message
1308                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',forceInit=' . intval($forceInit));
1309
1310                 // Is it already set?
1311                 if (($forceInit === false) && ($this->isGenericArrayGroupSet($keyGroup, $subGroup))) {
1312                         // Already initialized
1313                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' already initialized.');
1314                         exit;
1315                 }
1316
1317                 // Initialize it
1318                 $this->genericArray[$keyGroup][$subGroup] = [];
1319         }
1320
1321         /**
1322          * Initializes given generic array key
1323          *
1324          * @param       $keyGroup       Main group for the key
1325          * @param       $subGroup       Sub group for the key
1326          * @param       $key            Key to use
1327          * @param       $forceInit      Optionally force initialization
1328          * @return      void
1329          */
1330         protected final function initGenericArrayKey ($keyGroup, $subGroup, $key, $forceInit = false) {
1331                 // Debug message
1332                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',forceInit=' . intval($forceInit));
1333
1334                 // Is it already set?
1335                 if (($forceInit === false) && ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key))) {
1336                         // Already initialized
1337                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' already initialized.');
1338                         exit;
1339                 }
1340
1341                 // Initialize it
1342                 $this->genericArray[$keyGroup][$subGroup][$key] = [];
1343         }
1344
1345         /**
1346          * Initializes given generic array element
1347          *
1348          * @param       $keyGroup       Main group for the key
1349          * @param       $subGroup       Sub group for the key
1350          * @param       $key            Key to use
1351          * @param       $element        Element to use
1352          * @param       $forceInit      Optionally force initialization
1353          * @return      void
1354          */
1355         protected final function initGenericArrayElement ($keyGroup, $subGroup, $key, $element, $forceInit = false) {
1356                 // Debug message
1357                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',forceInit=' . intval($forceInit));
1358
1359                 // Is it already set?
1360                 if (($forceInit === false) && ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element))) {
1361                         // Already initialized
1362                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' already initialized.');
1363                         exit;
1364                 }
1365
1366                 // Initialize it
1367                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = [];
1368         }
1369
1370         /**
1371          * Pushes an element to a generic key
1372          *
1373          * @param       $keyGroup       Main group for the key
1374          * @param       $subGroup       Sub group for the key
1375          * @param       $key            Key to use
1376          * @param       $value          Value to add/append
1377          * @return      $count          Number of array elements
1378          */
1379         protected final function pushValueToGenericArrayKey ($keyGroup, $subGroup, $key, $value) {
1380                 // Debug message
1381                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1382
1383                 // Is it set?
1384                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1385                         // Initialize array
1386                         $this->initGenericArrayKey($keyGroup, $subGroup, $key);
1387                 }
1388
1389                 // Then push it
1390                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key], $value);
1391
1392                 // Return count
1393                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1394                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1395                 return $count;
1396         }
1397
1398         /**
1399          * Pushes an element to a generic array element
1400          *
1401          * @param       $keyGroup       Main group for the key
1402          * @param       $subGroup       Sub group for the key
1403          * @param       $key            Key to use
1404          * @param       $element        Element to check
1405          * @param       $value          Value to add/append
1406          * @return      $count          Number of array elements
1407          */
1408         protected final function pushValueToGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value) {
1409                 // Debug message
1410                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true));
1411
1412                 // Is it set?
1413                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1414                         // Initialize array
1415                         $this->initGenericArrayElement($keyGroup, $subGroup, $key, $element);
1416                 }
1417
1418                 // Then push it
1419                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key][$element], $value);
1420
1421                 // Return count
1422                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1423                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1424                 return $count;
1425         }
1426
1427         /**
1428          * Pops an element from  a generic group
1429          *
1430          * @param       $keyGroup       Main group for the key
1431          * @param       $subGroup       Sub group for the key
1432          * @param       $key            Key to unset
1433          * @return      $value          Last "popped" value
1434          */
1435         protected final function popGenericArrayElement ($keyGroup, $subGroup, $key) {
1436                 // Debug message
1437                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1438
1439                 // Is it set?
1440                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1441                         // Not found
1442                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1443                         exit;
1444                 }
1445
1446                 // Then "pop" it
1447                 $value = array_pop($this->genericArray[$keyGroup][$subGroup][$key]);
1448
1449                 // Return value
1450                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1451                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1452                 return $value;
1453         }
1454
1455         /**
1456          * Shifts an element from  a generic group
1457          *
1458          * @param       $keyGroup       Main group for the key
1459          * @param       $subGroup       Sub group for the key
1460          * @param       $key            Key to unset
1461          * @return      $value          Last "popped" value
1462          */
1463         protected final function shiftGenericArrayElement ($keyGroup, $subGroup, $key) {
1464                 // Debug message
1465                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1466
1467                 // Is it set?
1468                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1469                         // Not found
1470                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1471                         exit;
1472                 }
1473
1474                 // Then "shift" it
1475                 $value = array_shift($this->genericArray[$keyGroup][$subGroup][$key]);
1476
1477                 // Return value
1478                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1479                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1480                 return $value;
1481         }
1482
1483         /**
1484          * Count generic array group
1485          *
1486          * @param       $keyGroup       Main group for the key
1487          * @return      $count          Count of given group
1488          */
1489         protected final function countGenericArray ($keyGroup) {
1490                 // Debug message
1491                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1492
1493                 // Is it there?
1494                 if (!isset($this->genericArray[$keyGroup])) {
1495                         // Abort here
1496                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' not found.');
1497                         exit;
1498                 }
1499
1500                 // Then count it
1501                 $count = count($this->genericArray[$keyGroup]);
1502
1503                 // Debug message
1504                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',count=' . $count);
1505
1506                 // Return it
1507                 return $count;
1508         }
1509
1510         /**
1511          * Count generic array sub group
1512          *
1513          * @param       $keyGroup       Main group for the key
1514          * @param       $subGroup       Sub group for the key
1515          * @return      $count          Count of given group
1516          */
1517         protected final function countGenericArrayGroup ($keyGroup, $subGroup) {
1518                 // Debug message
1519                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1520
1521                 // Is it there?
1522                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1523                         // Abort here
1524                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1525                         exit;
1526                 }
1527
1528                 // Then count it
1529                 $count = count($this->genericArray[$keyGroup][$subGroup]);
1530
1531                 // Debug message
1532                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',count=' . $count);
1533
1534                 // Return it
1535                 return $count;
1536         }
1537
1538         /**
1539          * Count generic array elements
1540          *
1541          * @param       $keyGroup       Main group for the key
1542          * @param       $subGroup       Sub group for the key
1543          * @para        $key            Key to count
1544          * @return      $count          Count of given key
1545          */
1546         protected final function countGenericArrayElements ($keyGroup, $subGroup, $key) {
1547                 // Debug message
1548                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1549
1550                 // Is it there?
1551                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1552                         // Abort here
1553                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1554                         exit;
1555                 } elseif (!$this->isValidGenericArrayGroup($keyGroup, $subGroup)) {
1556                         // Not valid
1557                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' is not an array.');
1558                         exit;
1559                 }
1560
1561                 // Then count it
1562                 $count = count($this->genericArray[$keyGroup][$subGroup][$key]);
1563
1564                 // Debug message
1565                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',count=' . $count);
1566
1567                 // Return it
1568                 return $count;
1569         }
1570
1571         /**
1572          * Getter for whole generic group array
1573          *
1574          * @param       $keyGroup       Key group to get
1575          * @return      $array          Whole generic array group
1576          */
1577         protected final function getGenericArray ($keyGroup) {
1578                 // Debug message
1579                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1580
1581                 // Is it there?
1582                 if (!isset($this->genericArray[$keyGroup])) {
1583                         // Then abort here
1584                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' does not exist.');
1585                         exit;
1586                 }
1587
1588                 // Return it
1589                 return $this->genericArray[$keyGroup];
1590         }
1591
1592         /**
1593          * Setter for generic array key
1594          *
1595          * @param       $keyGroup       Key group to get
1596          * @param       $subGroup       Sub group for the key
1597          * @param       $key            Key to unset
1598          * @param       $value          Mixed value from generic array element
1599          * @return      void
1600          */
1601         protected final function setGenericArrayKey ($keyGroup, $subGroup, $key, $value) {
1602                 // Debug message
1603                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1604
1605                 // Set value here
1606                 $this->genericArray[$keyGroup][$subGroup][$key] = $value;
1607         }
1608
1609         /**
1610          * Getter for generic array key
1611          *
1612          * @param       $keyGroup       Key group to get
1613          * @param       $subGroup       Sub group for the key
1614          * @param       $key            Key to unset
1615          * @return      $value          Mixed value from generic array element
1616          */
1617         protected final function getGenericArrayKey ($keyGroup, $subGroup, $key) {
1618                 // Debug message
1619                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1620
1621                 // Is it there?
1622                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1623                         // Then abort here
1624                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' does not exist.');
1625                         exit;
1626                 }
1627
1628                 // Return it
1629                 return $this->genericArray[$keyGroup][$subGroup][$key];
1630         }
1631
1632         /**
1633          * Sets a value in given generic array key/element
1634          *
1635          * @param       $keyGroup       Main group for the key
1636          * @param       $subGroup       Sub group for the key
1637          * @param       $key            Key to set
1638          * @param       $element        Element to set
1639          * @param       $value          Value to set
1640          * @return      void
1641          */
1642         protected final function setGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value) {
1643                 // Debug message
1644                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true));
1645
1646                 // Then set it
1647                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = $value;
1648         }
1649
1650         /**
1651          * Getter for generic array element
1652          *
1653          * @param       $keyGroup       Key group to get
1654          * @param       $subGroup       Sub group for the key
1655          * @param       $key            Key to look for
1656          * @param       $element        Element to look for
1657          * @return      $value          Mixed value from generic array element
1658          */
1659         protected final function getGenericArrayElement ($keyGroup, $subGroup, $key, $element) {
1660                 // Debug message
1661                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1662
1663                 // Is it there?
1664                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1665                         // Then abort here
1666                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' does not exist.');
1667                         exit;
1668                 }
1669
1670                 // Return it
1671                 return $this->genericArray[$keyGroup][$subGroup][$key][$element];
1672         }
1673
1674         /**
1675          * Checks if a given sub group is valid (array)
1676          *
1677          * @param       $keyGroup       Key group to get
1678          * @param       $subGroup       Sub group for the key
1679          * @return      $isValid        Whether given sub group is valid
1680          */
1681         protected final function isValidGenericArrayGroup ($keyGroup, $subGroup) {
1682                 // Debug message
1683                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1684
1685                 // Determine it
1686                 $isValid = (($this->isGenericArrayGroupSet($keyGroup, $subGroup)) && (is_array($this->getGenericSubArray($keyGroup, $subGroup))));
1687
1688                 // Return it
1689                 return $isValid;
1690         }
1691
1692         /**
1693          * Checks if a given key is valid (array)
1694          *
1695          * @param       $keyGroup       Key group to get
1696          * @param       $subGroup       Sub group for the key
1697          * @param       $key            Key to check
1698          * @return      $isValid        Whether given sub group is valid
1699          */
1700         protected final function isValidGenericArrayKey ($keyGroup, $subGroup, $key) {
1701                 // Debug message
1702                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1703
1704                 // Determine it
1705                 $isValid = (($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) && (is_array($this->getGenericArrayKey($keyGroup, $subGroup, $key))));
1706
1707                 // Return it
1708                 return $isValid;
1709         }
1710
1711         /**
1712          * Initializes the web output instance
1713          *
1714          * @return      void
1715          */
1716         protected function initWebOutputInstance () {
1717                 // Get application instance
1718                 $applicationInstance = GenericRegistry::getRegistry()->getInstance('application');
1719
1720                 // Init web output instance
1721                 $outputInstance = ObjectFactory::createObjectByConfiguredName('output_class', array($applicationInstance));
1722
1723                 // Set it locally
1724                 $this->setWebOutputInstance($outputInstance);
1725         }
1726
1727         /**
1728          * Translates boolean true to 'Y' and false to 'N'
1729          *
1730          * @param       $boolean                Boolean value
1731          * @return      $translated             Translated boolean value
1732          */
1733         public static final function translateBooleanToYesNo (bool $boolean) {
1734                 // Make sure it is really boolean
1735                 assert(is_bool($boolean));
1736
1737                 // "Translate" it
1738                 $translated = ($boolean === true) ? 'Y' : 'N';
1739
1740                 // ... and return it
1741                 return $translated;
1742         }
1743
1744         /**
1745          * Creates a full-qualified file name (FQFN) for given file name by adding
1746          * a configured temporary file path to it.
1747          *
1748          * @param       $infoInstance   An instance of a SplFileInfo class
1749          * @return      $tempInstance   An instance of a SplFileInfo class (temporary file)
1750          * @throw       PathWriteProtectedException If the path in 'temp_file_path' is write-protected
1751          * @throws      FileIoException If the file cannot be written
1752          */
1753          protected static function createTempPathForFile (SplFileInfo $infoInstance) {
1754                 // Get config entry
1755                 $basePath = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('temp_file_path');
1756
1757                 // Is the path writeable?
1758                 if (!is_writable($basePath)) {
1759                         // Path is write-protected
1760                         throw new PathWriteProtectedException($infoInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
1761                 }
1762
1763                 // Add it
1764                 $tempInstance = new SplFileInfo($basePath . DIRECTORY_SEPARATOR . $infoInstance->getBasename());
1765
1766                 // Is it reachable?
1767                 if (!FrameworkBootstrap::isReachableFilePath($tempInstance)) {
1768                         // Not reachable
1769                         throw new FileIoException($tempInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
1770                 }
1771
1772                 // Return it
1773                 return $tempInstance;
1774          }
1775
1776         /**
1777          * "Getter" for a printable state name
1778          *
1779          * @return      $stateName      Name of the node's state in a printable format
1780          */
1781         public final function getPrintableState () {
1782                 // Default is 'null'
1783                 $stateName = 'null';
1784
1785                 // Get the state instance
1786                 $stateInstance = $this->getStateInstance();
1787
1788                 // Is it an instance of Stateable?
1789                 if ($stateInstance instanceof Stateable) {
1790                         // Then use that state name
1791                         $stateName = $stateInstance->getStateName();
1792                 }
1793
1794                 // Return result
1795                 return $stateName;
1796         }
1797
1798 }