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