]> 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 ($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 ($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 ($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 ($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 ($message = '', $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 ($className, $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 ($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 ($message, $doPrint = true, $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 ($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 (defined('DEVELOPER')) {
833                         // Missing field entry, may require debugging
834                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: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('[' . __METHOD__ . ':' . __LINE__ . ']: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 ($message) {
883                 // Is developer mode active?
884                 if (defined('DEVELOPER')) {
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 ($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 ($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 ($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 ($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          * Checks whether the given number is really a number (only chars 0-9).
1049          *
1050          * @param       $num            A string consisting only chars between 0 and 9
1051          * @param       $castValue      Whether to cast the value to double. Do only use this to secure numbers from Requestable classes.
1052          * @param       $assertMismatch         Whether to assert mismatches
1053          * @return      $ret            The (hopefully) secured numbered value
1054          */
1055         public function bigintval ($num, $castValue = true, $assertMismatch = false) {
1056                 // Filter all numbers out
1057                 $ret = preg_replace('/[^0123456789]/', '', $num);
1058
1059                 // Shall we cast?
1060                 if ($castValue === true) {
1061                         // Cast to biggest numeric type
1062                         $ret = (double) $ret;
1063                 }
1064
1065                 // Assert only if requested
1066                 if ($assertMismatch === true) {
1067                         // Has the whole value changed?
1068                         assert(('' . $ret . '' != '' . $num . '') && (!is_null($num)));
1069                 }
1070
1071                 // Return result
1072                 return $ret;
1073         }
1074
1075         /**
1076          * Checks whether the given hexadecimal number is really a hex-number (only chars 0-9,a-f).
1077          *
1078          * @param       $num    A string consisting only chars between 0 and 9
1079          * @param       $assertMismatch         Whether to assert mismatches
1080          * @return      $ret    The (hopefully) secured hext-numbered value
1081          */
1082         public function hexval ($num, $assertMismatch = false) {
1083                 // Filter all numbers out
1084                 $ret = preg_replace('/[^0123456789abcdefABCDEF]/', '', $num);
1085
1086                 // Assert only if requested
1087                 if ($assertMismatch === true) {
1088                         // Has the whole value changed?
1089                         assert(('' . $ret . '' != '' . $num . '') && (!is_null($num)));
1090                 }
1091
1092                 // Return result
1093                 return $ret;
1094         }
1095
1096         /**
1097          * Determines if an element is set in the generic array
1098          *
1099          * @param       $keyGroup       Main group for the key
1100          * @param       $subGroup       Sub group for the key
1101          * @param       $key            Key to check
1102          * @param       $element        Element to check
1103          * @return      $isset          Whether the given key is set
1104          */
1105         protected final function isGenericArrayElementSet ($keyGroup, $subGroup, $key, $element) {
1106                 // Debug message
1107                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1108
1109                 // Is it there?
1110                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1111
1112                 // Return it
1113                 return $isset;
1114         }
1115         /**
1116          * Determines if a key is set in the generic array
1117          *
1118          * @param       $keyGroup       Main group for the key
1119          * @param       $subGroup       Sub group for the key
1120          * @param       $key            Key to check
1121          * @return      $isset          Whether the given key is set
1122          */
1123         protected final function isGenericArrayKeySet ($keyGroup, $subGroup, $key) {
1124                 // Debug message
1125                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1126
1127                 // Is it there?
1128                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key]);
1129
1130                 // Return it
1131                 return $isset;
1132         }
1133
1134
1135         /**
1136          * Determines if a group is set in the generic array
1137          *
1138          * @param       $keyGroup       Main group
1139          * @param       $subGroup       Sub group
1140          * @return      $isset          Whether the given group is set
1141          */
1142         protected final function isGenericArrayGroupSet ($keyGroup, $subGroup) {
1143                 // Debug message
1144                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1145
1146                 // Is it there?
1147                 $isset = isset($this->genericArray[$keyGroup][$subGroup]);
1148
1149                 // Return it
1150                 return $isset;
1151         }
1152
1153         /**
1154          * Getter for sub key group
1155          *
1156          * @param       $keyGroup       Main key group
1157          * @param       $subGroup       Sub key group
1158          * @return      $array          An array with all array elements
1159          */
1160         protected final function getGenericSubArray ($keyGroup, $subGroup) {
1161                 // Is it there?
1162                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1163                         // No, then abort here
1164                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1165                         exit;
1166                 }
1167
1168                 // Debug message
1169                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',value=' . print_r($this->genericArray[$keyGroup][$subGroup], true));
1170
1171                 // Return it
1172                 return $this->genericArray[$keyGroup][$subGroup];
1173         }
1174
1175         /**
1176          * Unsets a given key in generic array
1177          *
1178          * @param       $keyGroup       Main group for the key
1179          * @param       $subGroup       Sub group for the key
1180          * @param       $key            Key to unset
1181          * @return      void
1182          */
1183         protected final function unsetGenericArrayKey ($keyGroup, $subGroup, $key) {
1184                 // Debug message
1185                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1186
1187                 // Remove it
1188                 unset($this->genericArray[$keyGroup][$subGroup][$key]);
1189         }
1190
1191         /**
1192          * Unsets a given element in generic array
1193          *
1194          * @param       $keyGroup       Main group for the key
1195          * @param       $subGroup       Sub group for the key
1196          * @param       $key            Key to unset
1197          * @param       $element        Element to unset
1198          * @return      void
1199          */
1200         protected final function unsetGenericArrayElement ($keyGroup, $subGroup, $key, $element) {
1201                 // Debug message
1202                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1203
1204                 // Remove it
1205                 unset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1206         }
1207
1208         /**
1209          * Append a string to a given generic array key
1210          *
1211          * @param       $keyGroup       Main group for the key
1212          * @param       $subGroup       Sub group for the key
1213          * @param       $key            Key to unset
1214          * @param       $value          Value to add/append
1215          * @return      void
1216          */
1217         protected final function appendStringToGenericArrayKey ($keyGroup, $subGroup, $key, $value, $appendGlue = '') {
1218                 // Debug message
1219                 //* 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);
1220
1221                 // Is it already there?
1222                 if ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1223                         // Append it
1224                         $this->genericArray[$keyGroup][$subGroup][$key] .= $appendGlue . (string) $value;
1225                 } else {
1226                         // Add it
1227                         $this->genericArray[$keyGroup][$subGroup][$key] = (string) $value;
1228                 }
1229         }
1230
1231         /**
1232          * Append a string to a given generic array element
1233          *
1234          * @param       $keyGroup       Main group for the key
1235          * @param       $subGroup       Sub group for the key
1236          * @param       $key            Key to unset
1237          * @param       $element        Element to check
1238          * @param       $value          Value to add/append
1239          * @return      void
1240          */
1241         protected final function appendStringToGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value, $appendGlue = '') {
1242                 // Debug message
1243                 //* 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);
1244
1245                 // Is it already there?
1246                 if ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1247                         // Append it
1248                         $this->genericArray[$keyGroup][$subGroup][$key][$element] .= $appendGlue . (string) $value;
1249                 } else {
1250                         // Add it
1251                         $this->setStringGenericArrayElement($keyGroup, $subGroup, $key, $element, $value);
1252                 }
1253         }
1254
1255         /**
1256          * Sets a string in 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 setStringGenericArrayElement ($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                 // Set it
1270                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = (string) $value;
1271         }
1272
1273         /**
1274          * Initializes given generic array group
1275          *
1276          * @param       $keyGroup       Main group for the key
1277          * @param       $subGroup       Sub group for the key
1278          * @param       $key            Key to use
1279          * @param       $forceInit      Optionally force initialization
1280          * @return      void
1281          */
1282         protected final function initGenericArrayGroup ($keyGroup, $subGroup, $forceInit = false) {
1283                 // Debug message
1284                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',forceInit=' . intval($forceInit));
1285
1286                 // Is it already set?
1287                 if (($forceInit === false) && ($this->isGenericArrayGroupSet($keyGroup, $subGroup))) {
1288                         // Already initialized
1289                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' already initialized.');
1290                         exit;
1291                 }
1292
1293                 // Initialize it
1294                 $this->genericArray[$keyGroup][$subGroup] = [];
1295         }
1296
1297         /**
1298          * Initializes given generic array 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       $forceInit      Optionally force initialization
1304          * @return      void
1305          */
1306         protected final function initGenericArrayKey ($keyGroup, $subGroup, $key, $forceInit = false) {
1307                 // Debug message
1308                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',forceInit=' . intval($forceInit));
1309
1310                 // Is it already set?
1311                 if (($forceInit === false) && ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key))) {
1312                         // Already initialized
1313                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' already initialized.');
1314                         exit;
1315                 }
1316
1317                 // Initialize it
1318                 $this->genericArray[$keyGroup][$subGroup][$key] = [];
1319         }
1320
1321         /**
1322          * Initializes given generic array element
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       $element        Element to use
1328          * @param       $forceInit      Optionally force initialization
1329          * @return      void
1330          */
1331         protected final function initGenericArrayElement ($keyGroup, $subGroup, $key, $element, $forceInit = false) {
1332                 // Debug message
1333                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',forceInit=' . intval($forceInit));
1334
1335                 // Is it already set?
1336                 if (($forceInit === false) && ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element))) {
1337                         // Already initialized
1338                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' already initialized.');
1339                         exit;
1340                 }
1341
1342                 // Initialize it
1343                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = [];
1344         }
1345
1346         /**
1347          * Pushes an element to a generic key
1348          *
1349          * @param       $keyGroup       Main group for the key
1350          * @param       $subGroup       Sub group for the key
1351          * @param       $key            Key to use
1352          * @param       $value          Value to add/append
1353          * @return      $count          Number of array elements
1354          */
1355         protected final function pushValueToGenericArrayKey ($keyGroup, $subGroup, $key, $value) {
1356                 // Debug message
1357                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1358
1359                 // Is it set?
1360                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1361                         // Initialize array
1362                         $this->initGenericArrayKey($keyGroup, $subGroup, $key);
1363                 }
1364
1365                 // Then push it
1366                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key], $value);
1367
1368                 // Return count
1369                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1370                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1371                 return $count;
1372         }
1373
1374         /**
1375          * Pushes an element to a generic array element
1376          *
1377          * @param       $keyGroup       Main group for the key
1378          * @param       $subGroup       Sub group for the key
1379          * @param       $key            Key to use
1380          * @param       $element        Element to check
1381          * @param       $value          Value to add/append
1382          * @return      $count          Number of array elements
1383          */
1384         protected final function pushValueToGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value) {
1385                 // Debug message
1386                 //* 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));
1387
1388                 // Is it set?
1389                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1390                         // Initialize array
1391                         $this->initGenericArrayElement($keyGroup, $subGroup, $key, $element);
1392                 }
1393
1394                 // Then push it
1395                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key][$element], $value);
1396
1397                 // Return count
1398                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1399                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1400                 return $count;
1401         }
1402
1403         /**
1404          * Pops an element from  a generic group
1405          *
1406          * @param       $keyGroup       Main group for the key
1407          * @param       $subGroup       Sub group for the key
1408          * @param       $key            Key to unset
1409          * @return      $value          Last "popped" value
1410          */
1411         protected final function popGenericArrayElement ($keyGroup, $subGroup, $key) {
1412                 // Debug message
1413                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1414
1415                 // Is it set?
1416                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1417                         // Not found
1418                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1419                         exit;
1420                 }
1421
1422                 // Then "pop" it
1423                 $value = array_pop($this->genericArray[$keyGroup][$subGroup][$key]);
1424
1425                 // Return value
1426                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1427                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1428                 return $value;
1429         }
1430
1431         /**
1432          * Shifts an element from  a generic group
1433          *
1434          * @param       $keyGroup       Main group for the key
1435          * @param       $subGroup       Sub group for the key
1436          * @param       $key            Key to unset
1437          * @return      $value          Last "popped" value
1438          */
1439         protected final function shiftGenericArrayElement ($keyGroup, $subGroup, $key) {
1440                 // Debug message
1441                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1442
1443                 // Is it set?
1444                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1445                         // Not found
1446                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1447                         exit;
1448                 }
1449
1450                 // Then "shift" it
1451                 $value = array_shift($this->genericArray[$keyGroup][$subGroup][$key]);
1452
1453                 // Return value
1454                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1455                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1456                 return $value;
1457         }
1458
1459         /**
1460          * Count generic array group
1461          *
1462          * @param       $keyGroup       Main group for the key
1463          * @return      $count          Count of given group
1464          */
1465         protected final function countGenericArray ($keyGroup) {
1466                 // Debug message
1467                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1468
1469                 // Is it there?
1470                 if (!isset($this->genericArray[$keyGroup])) {
1471                         // Abort here
1472                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' not found.');
1473                         exit;
1474                 }
1475
1476                 // Then count it
1477                 $count = count($this->genericArray[$keyGroup]);
1478
1479                 // Debug message
1480                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',count=' . $count);
1481
1482                 // Return it
1483                 return $count;
1484         }
1485
1486         /**
1487          * Count generic array sub group
1488          *
1489          * @param       $keyGroup       Main group for the key
1490          * @param       $subGroup       Sub group for the key
1491          * @return      $count          Count of given group
1492          */
1493         protected final function countGenericArrayGroup ($keyGroup, $subGroup) {
1494                 // Debug message
1495                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1496
1497                 // Is it there?
1498                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1499                         // Abort here
1500                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1501                         exit;
1502                 }
1503
1504                 // Then count it
1505                 $count = count($this->genericArray[$keyGroup][$subGroup]);
1506
1507                 // Debug message
1508                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',count=' . $count);
1509
1510                 // Return it
1511                 return $count;
1512         }
1513
1514         /**
1515          * Count generic array elements
1516          *
1517          * @param       $keyGroup       Main group for the key
1518          * @param       $subGroup       Sub group for the key
1519          * @para        $key            Key to count
1520          * @return      $count          Count of given key
1521          */
1522         protected final function countGenericArrayElements ($keyGroup, $subGroup, $key) {
1523                 // Debug message
1524                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1525
1526                 // Is it there?
1527                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1528                         // Abort here
1529                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1530                         exit;
1531                 } elseif (!$this->isValidGenericArrayGroup($keyGroup, $subGroup)) {
1532                         // Not valid
1533                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' is not an array.');
1534                         exit;
1535                 }
1536
1537                 // Then count it
1538                 $count = count($this->genericArray[$keyGroup][$subGroup][$key]);
1539
1540                 // Debug message
1541                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',count=' . $count);
1542
1543                 // Return it
1544                 return $count;
1545         }
1546
1547         /**
1548          * Getter for whole generic group array
1549          *
1550          * @param       $keyGroup       Key group to get
1551          * @return      $array          Whole generic array group
1552          */
1553         protected final function getGenericArray ($keyGroup) {
1554                 // Debug message
1555                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1556
1557                 // Is it there?
1558                 if (!isset($this->genericArray[$keyGroup])) {
1559                         // Then abort here
1560                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' does not exist.');
1561                         exit;
1562                 }
1563
1564                 // Return it
1565                 return $this->genericArray[$keyGroup];
1566         }
1567
1568         /**
1569          * Setter for generic array key
1570          *
1571          * @param       $keyGroup       Key group to get
1572          * @param       $subGroup       Sub group for the key
1573          * @param       $key            Key to unset
1574          * @param       $value          Mixed value from generic array element
1575          * @return      void
1576          */
1577         protected final function setGenericArrayKey ($keyGroup, $subGroup, $key, $value) {
1578                 // Debug message
1579                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1580
1581                 // Set value here
1582                 $this->genericArray[$keyGroup][$subGroup][$key] = $value;
1583         }
1584
1585         /**
1586          * Getter for generic array key
1587          *
1588          * @param       $keyGroup       Key group to get
1589          * @param       $subGroup       Sub group for the key
1590          * @param       $key            Key to unset
1591          * @return      $value          Mixed value from generic array element
1592          */
1593         protected final function getGenericArrayKey ($keyGroup, $subGroup, $key) {
1594                 // Debug message
1595                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1596
1597                 // Is it there?
1598                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1599                         // Then abort here
1600                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' does not exist.');
1601                         exit;
1602                 }
1603
1604                 // Return it
1605                 return $this->genericArray[$keyGroup][$subGroup][$key];
1606         }
1607
1608         /**
1609          * Sets a value in given generic array key/element
1610          *
1611          * @param       $keyGroup       Main group for the key
1612          * @param       $subGroup       Sub group for the key
1613          * @param       $key            Key to set
1614          * @param       $element        Element to set
1615          * @param       $value          Value to set
1616          * @return      void
1617          */
1618         protected final function setGenericArrayElement ($keyGroup, $subGroup, $key, $element, $value) {
1619                 // Debug message
1620                 //* 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));
1621
1622                 // Then set it
1623                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = $value;
1624         }
1625
1626         /**
1627          * Getter for generic array element
1628          *
1629          * @param       $keyGroup       Key group to get
1630          * @param       $subGroup       Sub group for the key
1631          * @param       $key            Key to look for
1632          * @param       $element        Element to look for
1633          * @return      $value          Mixed value from generic array element
1634          */
1635         protected final function getGenericArrayElement ($keyGroup, $subGroup, $key, $element) {
1636                 // Debug message
1637                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1638
1639                 // Is it there?
1640                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1641                         // Then abort here
1642                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' does not exist.');
1643                         exit;
1644                 }
1645
1646                 // Return it
1647                 return $this->genericArray[$keyGroup][$subGroup][$key][$element];
1648         }
1649
1650         /**
1651          * Checks if a given sub group is valid (array)
1652          *
1653          * @param       $keyGroup       Key group to get
1654          * @param       $subGroup       Sub group for the key
1655          * @return      $isValid        Whether given sub group is valid
1656          */
1657         protected final function isValidGenericArrayGroup ($keyGroup, $subGroup) {
1658                 // Debug message
1659                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1660
1661                 // Determine it
1662                 $isValid = (($this->isGenericArrayGroupSet($keyGroup, $subGroup)) && (is_array($this->getGenericSubArray($keyGroup, $subGroup))));
1663
1664                 // Return it
1665                 return $isValid;
1666         }
1667
1668         /**
1669          * Checks if a given key is valid (array)
1670          *
1671          * @param       $keyGroup       Key group to get
1672          * @param       $subGroup       Sub group for the key
1673          * @param       $key            Key to check
1674          * @return      $isValid        Whether given sub group is valid
1675          */
1676         protected final function isValidGenericArrayKey ($keyGroup, $subGroup, $key) {
1677                 // Debug message
1678                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1679
1680                 // Determine it
1681                 $isValid = (($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) && (is_array($this->getGenericArrayKey($keyGroup, $subGroup, $key))));
1682
1683                 // Return it
1684                 return $isValid;
1685         }
1686
1687         /**
1688          * Initializes the web output instance
1689          *
1690          * @return      void
1691          */
1692         protected function initWebOutputInstance () {
1693                 // Get application instance
1694                 $applicationInstance = GenericRegistry::getRegistry()->getInstance('application');
1695
1696                 // Init web output instance
1697                 $outputInstance = ObjectFactory::createObjectByConfiguredName('output_class', array($applicationInstance));
1698
1699                 // Set it locally
1700                 $this->setWebOutputInstance($outputInstance);
1701         }
1702
1703         /**
1704          * Translates boolean true to 'Y' and false to 'N'
1705          *
1706          * @param       $boolean                Boolean value
1707          * @return      $translated             Translated boolean value
1708          */
1709         public static final function translateBooleanToYesNo (bool $boolean) {
1710                 // Make sure it is really boolean
1711                 assert(is_bool($boolean));
1712
1713                 // "Translate" it
1714                 $translated = ($boolean === true) ? 'Y' : 'N';
1715
1716                 // ... and return it
1717                 return $translated;
1718         }
1719
1720         /**
1721          * Creates a full-qualified file name (FQFN) for given file name by adding
1722          * a configured temporary file path to it.
1723          *
1724          * @param       $infoInstance   An instance of a SplFileInfo class
1725          * @return      $tempInstance   An instance of a SplFileInfo class (temporary file)
1726          * @throw       PathWriteProtectedException If the path in 'temp_file_path' is write-protected
1727          * @throws      FileIoException If the file cannot be written
1728          */
1729          protected static function createTempPathForFile (SplFileInfo $infoInstance) {
1730                 // Get config entry
1731                 $basePath = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('temp_file_path');
1732
1733                 // Is the path writeable?
1734                 if (!is_writable($basePath)) {
1735                         // Path is write-protected
1736                         throw new PathWriteProtectedException($infoInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
1737                 }
1738
1739                 // Add it
1740                 $tempInstance = new SplFileInfo($basePath . DIRECTORY_SEPARATOR . $infoInstance->getBasename());
1741
1742                 // Is it reachable?
1743                 if (!FrameworkBootstrap::isReachableFilePath($tempInstance)) {
1744                         // Not reachable
1745                         throw new FileIoException($tempInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
1746                 }
1747
1748                 // Return it
1749                 return $tempInstance;
1750          }
1751
1752         /**
1753          * "Getter" for a printable state name
1754          *
1755          * @return      $stateName      Name of the node's state in a printable format
1756          * @todo        Move this class away from this monolithic place (not whole class is monolithic)
1757          */
1758         public final function getPrintableState () {
1759                 // Default is 'null'
1760                 $stateName = 'null';
1761
1762                 // Get the state instance
1763                 $stateInstance = $this->getStateInstance();
1764
1765                 // Is it an instance of Stateable?
1766                 if ($stateInstance instanceof Stateable) {
1767                         // Then use that state name
1768                         $stateName = $stateInstance->getStateName();
1769                 }
1770
1771                 // Return result
1772                 return $stateName;
1773         }
1774
1775 }