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