]> git.mxchange.org Git - core.git/blob - framework/main/classes/class_BaseFrameworkSystem.php
77cc954dad062cf75bcb8acd057d45ac988aa0ba
[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                 // Is it there?
1022                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1023                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1024
1025                 // Return it
1026                 return $isset;
1027         }
1028         /**
1029          * Determines if a key is set in the generic array
1030          *
1031          * @param       $keyGroup       Main group for the key
1032          * @param       $subGroup       Sub group for the key
1033          * @param       $key            Key to check
1034          * @return      $isset          Whether the given key is set
1035          */
1036         protected final function isGenericArrayKeySet (string $keyGroup, string $subGroup, $key) {
1037                 // Is it there?
1038                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1039                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key]);
1040
1041                 // Return it
1042                 return $isset;
1043         }
1044
1045
1046         /**
1047          * Determines if a group is set in the generic array
1048          *
1049          * @param       $keyGroup       Main group
1050          * @param       $subGroup       Sub group
1051          * @return      $isset          Whether the given group is set
1052          */
1053         protected final function isGenericArrayGroupSet (string $keyGroup, string $subGroup) {
1054                 // Is it there?
1055                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1056                 $isset = isset($this->genericArray[$keyGroup][$subGroup]);
1057
1058                 // Return it
1059                 return $isset;
1060         }
1061
1062         /**
1063          * Getter for sub key group
1064          *
1065          * @param       $keyGroup       Main key group
1066          * @param       $subGroup       Sub key group
1067          * @return      $array          An array with all array elements
1068          */
1069         protected final function getGenericSubArray (string $keyGroup, string $subGroup) {
1070                 // Is it there?
1071                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1072                         // No, then abort here
1073                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1074                         exit;
1075                 }
1076
1077                 // Return it
1078                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',value=' . print_r($this->genericArray[$keyGroup][$subGroup], true));
1079                 return $this->genericArray[$keyGroup][$subGroup];
1080         }
1081
1082         /**
1083          * Unsets a given key in generic array
1084          *
1085          * @param       $keyGroup       Main group for the key
1086          * @param       $subGroup       Sub group for the key
1087          * @param       $key            Key to unset
1088          * @return      void
1089          */
1090         protected final function unsetGenericArrayKey (string $keyGroup, string $subGroup, $key) {
1091                 // Remove it
1092                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1093                 unset($this->genericArray[$keyGroup][$subGroup][$key]);
1094         }
1095
1096         /**
1097          * Unsets a given element in generic array
1098          *
1099          * @param       $keyGroup       Main group for the key
1100          * @param       $subGroup       Sub group for the key
1101          * @param       $key            Key to unset
1102          * @param       $element        Element to unset
1103          * @return      void
1104          */
1105         protected final function unsetGenericArrayElement (string $keyGroup, string $subGroup, $key, $element) {
1106                 // Debug message
1107                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1108
1109                 // Remove it
1110                 unset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1111         }
1112
1113         /**
1114          * Append a string to a given generic array key
1115          *
1116          * @param       $keyGroup       Main group for the key
1117          * @param       $subGroup       Sub group for the key
1118          * @param       $key            Key to unset
1119          * @param       $value          Value to add/append
1120          * @return      void
1121          */
1122         protected final function appendStringToGenericArrayKey (string $keyGroup, string $subGroup, $key, string $value, $appendGlue = '') {
1123                 // Debug message
1124                 //* 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);
1125
1126                 // Is it already there?
1127                 if ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1128                         // Append it
1129                         $this->genericArray[$keyGroup][$subGroup][$key] .= $appendGlue . (string) $value;
1130                 } else {
1131                         // Add it
1132                         $this->genericArray[$keyGroup][$subGroup][$key] = (string) $value;
1133                 }
1134         }
1135
1136         /**
1137          * Append a string to a given generic array element
1138          *
1139          * @param       $keyGroup       Main group for the key
1140          * @param       $subGroup       Sub group for the key
1141          * @param       $key            Key to unset
1142          * @param       $element        Element to check
1143          * @param       $value          Value to add/append
1144          * @return      void
1145          */
1146         protected final function appendStringToGenericArrayElement (string $keyGroup, string $subGroup, $key, $element, $value, $appendGlue = '') {
1147                 // Debug message
1148                 //* 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);
1149
1150                 // Is it already there?
1151                 if ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1152                         // Append it
1153                         $this->genericArray[$keyGroup][$subGroup][$key][$element] .= $appendGlue . (string) $value;
1154                 } else {
1155                         // Add it
1156                         $this->setStringGenericArrayElement($keyGroup, $subGroup, $key, $element, $value);
1157                 }
1158         }
1159
1160         /**
1161          * Sets a string in a given generic array element
1162          *
1163          * @param       $keyGroup       Main group for the key
1164          * @param       $subGroup       Sub group for the key
1165          * @param       $key            Key to unset
1166          * @param       $element        Element to check
1167          * @param       $value          Value to add/append
1168          * @return      void
1169          */
1170         protected final function setStringGenericArrayElement (string $keyGroup, string $subGroup, $key, $element, $value, $appendGlue = '') {
1171                 // Debug message
1172                 //* 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);
1173
1174                 // Set it
1175                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = (string) $value;
1176         }
1177
1178         /**
1179          * Initializes given generic array group
1180          *
1181          * @param       $keyGroup       Main group for the key
1182          * @param       $subGroup       Sub group for the key
1183          * @param       $key            Key to use
1184          * @param       $forceInit      Optionally force initialization
1185          * @return      void
1186          */
1187         protected final function initGenericArrayGroup (string $keyGroup, string $subGroup, bool $forceInit = false) {
1188                 // Debug message
1189                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',forceInit=' . intval($forceInit));
1190
1191                 // Is it already set?
1192                 if (($forceInit === false) && ($this->isGenericArrayGroupSet($keyGroup, $subGroup))) {
1193                         // Already initialized
1194                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' already initialized.');
1195                         exit;
1196                 }
1197
1198                 // Initialize it
1199                 $this->genericArray[$keyGroup][$subGroup] = [];
1200         }
1201
1202         /**
1203          * Initializes given generic array key
1204          *
1205          * @param       $keyGroup       Main group for the key
1206          * @param       $subGroup       Sub group for the key
1207          * @param       $key            Key to use
1208          * @param       $forceInit      Optionally force initialization
1209          * @return      void
1210          */
1211         protected final function initGenericArrayKey (string $keyGroup, string $subGroup, $key, bool $forceInit = false) {
1212                 // Debug message
1213                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',forceInit=' . intval($forceInit));
1214
1215                 // Is it already set?
1216                 if (($forceInit === false) && ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key))) {
1217                         // Already initialized
1218                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' already initialized.');
1219                         exit;
1220                 }
1221
1222                 // Initialize it
1223                 $this->genericArray[$keyGroup][$subGroup][$key] = [];
1224         }
1225
1226         /**
1227          * Initializes given generic array element
1228          *
1229          * @param       $keyGroup       Main group for the key
1230          * @param       $subGroup       Sub group for the key
1231          * @param       $key            Key to use
1232          * @param       $element        Element to use
1233          * @param       $forceInit      Optionally force initialization
1234          * @return      void
1235          */
1236         protected final function initGenericArrayElement (string $keyGroup, string $subGroup, $key, $element, bool $forceInit = false) {
1237                 // Debug message
1238                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',forceInit=' . intval($forceInit));
1239
1240                 // Is it already set?
1241                 if (($forceInit === false) && ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element))) {
1242                         // Already initialized
1243                         trigger_error(__METHOD__ . ':keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' already initialized.');
1244                         exit;
1245                 }
1246
1247                 // Initialize it
1248                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = [];
1249         }
1250
1251         /**
1252          * Pushes an element to a generic key
1253          *
1254          * @param       $keyGroup       Main group for the key
1255          * @param       $subGroup       Sub group for the key
1256          * @param       $key            Key to use
1257          * @param       $value          Value to add/append
1258          * @return      $count          Number of array elements
1259          */
1260         protected final function pushValueToGenericArrayKey (string $keyGroup, string $subGroup, $key, $value) {
1261                 // Debug message
1262                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1263
1264                 // Is it set?
1265                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1266                         // Initialize array
1267                         $this->initGenericArrayKey($keyGroup, $subGroup, $key);
1268                 }
1269
1270                 // Then push it
1271                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key], $value);
1272
1273                 // Return count
1274                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1275                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1276                 return $count;
1277         }
1278
1279         /**
1280          * Pushes an element to a generic array element
1281          *
1282          * @param       $keyGroup       Main group for the key
1283          * @param       $subGroup       Sub group for the key
1284          * @param       $key            Key to use
1285          * @param       $element        Element to check
1286          * @param       $value          Value to add/append
1287          * @return      $count          Number of array elements
1288          */
1289         protected final function pushValueToGenericArrayElement (string $keyGroup, string $subGroup, $key, $element, $value) {
1290                 // Debug message
1291                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true));
1292
1293                 // Is it set?
1294                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1295                         // Initialize array
1296                         $this->initGenericArrayElement($keyGroup, $subGroup, $key, $element);
1297                 }
1298
1299                 // Then push it
1300                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key][$element], $value);
1301
1302                 // Return count
1303                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1304                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1305                 return $count;
1306         }
1307
1308         /**
1309          * Pops an element from  a generic group
1310          *
1311          * @param       $keyGroup       Main group for the key
1312          * @param       $subGroup       Sub group for the key
1313          * @param       $key            Key to unset
1314          * @return      $value          Last "popped" value
1315          */
1316         protected final function popGenericArrayElement (string $keyGroup, string $subGroup, $key) {
1317                 // Debug message
1318                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1319
1320                 // Is it set?
1321                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1322                         // Not found
1323                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1324                         exit;
1325                 }
1326
1327                 // Then "pop" it
1328                 $value = array_pop($this->genericArray[$keyGroup][$subGroup][$key]);
1329
1330                 // Return value
1331                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1332                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1333                 return $value;
1334         }
1335
1336         /**
1337          * Shifts an element from  a generic group
1338          *
1339          * @param       $keyGroup       Main group for the key
1340          * @param       $subGroup       Sub group for the key
1341          * @param       $key            Key to unset
1342          * @return      $value          Last "popped" value
1343          */
1344         protected final function shiftGenericArrayElement (string $keyGroup, string $subGroup, $key) {
1345                 // Debug message
1346                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1347
1348                 // Is it set?
1349                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1350                         // Not found
1351                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' not found.');
1352                         exit;
1353                 }
1354
1355                 // Then "shift" it
1356                 $value = array_shift($this->genericArray[$keyGroup][$subGroup][$key]);
1357
1358                 // Return value
1359                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1360                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1361                 return $value;
1362         }
1363
1364         /**
1365          * Count generic array group
1366          *
1367          * @param       $keyGroup       Main group for the key
1368          * @return      $count          Count of given group
1369          */
1370         protected final function countGenericArray ($keyGroup) {
1371                 // Debug message
1372                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1373
1374                 // Is it there?
1375                 if (!isset($this->genericArray[$keyGroup])) {
1376                         // Abort here
1377                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' not found.');
1378                         exit;
1379                 }
1380
1381                 // Then count it
1382                 $count = count($this->genericArray[$keyGroup]);
1383
1384                 // Debug message
1385                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',count=' . $count);
1386
1387                 // Return it
1388                 return $count;
1389         }
1390
1391         /**
1392          * Count generic array sub group
1393          *
1394          * @param       $keyGroup       Main group for the key
1395          * @param       $subGroup       Sub group for the key
1396          * @return      $count          Count of given group
1397          */
1398         protected final function countGenericArrayGroup (string $keyGroup, string $subGroup) {
1399                 // Debug message
1400                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1401
1402                 // Is it there?
1403                 if (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1404                         // Abort here
1405                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1406                         exit;
1407                 }
1408
1409                 // Then count it
1410                 $count = count($this->genericArray[$keyGroup][$subGroup]);
1411
1412                 // Debug message
1413                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',count=' . $count);
1414
1415                 // Return it
1416                 return $count;
1417         }
1418
1419         /**
1420          * Count generic array elements
1421          *
1422          * @param       $keyGroup       Main group for the key
1423          * @param       $subGroup       Sub group for the key
1424          * @para        $key            Key to count
1425          * @return      $count          Count of given key
1426          */
1427         protected final function countGenericArrayElements (string $keyGroup, string $subGroup, $key) {
1428                 // Debug message
1429                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1430
1431                 // Is it there?
1432                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1433                         // Abort here
1434                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' not found.');
1435                         exit;
1436                 } elseif (!$this->isValidGenericArrayGroup($keyGroup, $subGroup)) {
1437                         // Not valid
1438                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ' is not an array.');
1439                         exit;
1440                 }
1441
1442                 // Then count it
1443                 $count = count($this->genericArray[$keyGroup][$subGroup][$key]);
1444
1445                 // Debug message
1446                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',count=' . $count);
1447
1448                 // Return it
1449                 return $count;
1450         }
1451
1452         /**
1453          * Getter for whole generic group array
1454          *
1455          * @param       $keyGroup       Key group to get
1456          * @return      $array          Whole generic array group
1457          */
1458         protected final function getGenericArray ($keyGroup) {
1459                 // Debug message
1460                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1461
1462                 // Is it there?
1463                 if (!isset($this->genericArray[$keyGroup])) {
1464                         // Then abort here
1465                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ' does not exist.');
1466                         exit;
1467                 }
1468
1469                 // Return it
1470                 return $this->genericArray[$keyGroup];
1471         }
1472
1473         /**
1474          * Setter for generic array key
1475          *
1476          * @param       $keyGroup       Key group to get
1477          * @param       $subGroup       Sub group for the key
1478          * @param       $key            Key to unset
1479          * @param       $value          Mixed value from generic array element
1480          * @return      void
1481          */
1482         protected final function setGenericArrayKey (string $keyGroup, string $subGroup, $key, $value) {
1483                 // Debug message
1484                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1485
1486                 // Set value here
1487                 $this->genericArray[$keyGroup][$subGroup][$key] = $value;
1488         }
1489
1490         /**
1491          * Getter for generic array key
1492          *
1493          * @param       $keyGroup       Key group to get
1494          * @param       $subGroup       Sub group for the key
1495          * @param       $key            Key to unset
1496          * @return      $value          Mixed value from generic array element
1497          */
1498         protected final function getGenericArrayKey (string $keyGroup, string $subGroup, $key) {
1499                 // Debug message
1500                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1501
1502                 // Is it there?
1503                 if (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1504                         // Then abort here
1505                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ' does not exist.');
1506                         exit;
1507                 }
1508
1509                 // Return it
1510                 return $this->genericArray[$keyGroup][$subGroup][$key];
1511         }
1512
1513         /**
1514          * Sets a value in given generic array key/element
1515          *
1516          * @param       $keyGroup       Main group for the key
1517          * @param       $subGroup       Sub group for the key
1518          * @param       $key            Key to set
1519          * @param       $element        Element to set
1520          * @param       $value          Value to set
1521          * @return      void
1522          */
1523         protected final function setGenericArrayElement (string $keyGroup, string $subGroup, $key, $element, $value) {
1524                 // Debug message
1525                 //* 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));
1526
1527                 // Then set it
1528                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = $value;
1529         }
1530
1531         /**
1532          * Getter for generic array element
1533          *
1534          * @param       $keyGroup       Key group to get
1535          * @param       $subGroup       Sub group for the key
1536          * @param       $key            Key to look for
1537          * @param       $element        Element to look for
1538          * @return      $value          Mixed value from generic array element
1539          */
1540         protected final function getGenericArrayElement (string $keyGroup, string $subGroup, $key, $element) {
1541                 // Debug message
1542                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1543
1544                 // Is it there?
1545                 if (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1546                         // Then abort here
1547                         trigger_error(__METHOD__ . ': keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ' does not exist.');
1548                         exit;
1549                 }
1550
1551                 // Return it
1552                 return $this->genericArray[$keyGroup][$subGroup][$key][$element];
1553         }
1554
1555         /**
1556          * Checks if a given sub group is valid (array)
1557          *
1558          * @param       $keyGroup       Key group to get
1559          * @param       $subGroup       Sub group for the key
1560          * @return      $isValid        Whether given sub group is valid
1561          */
1562         protected final function isValidGenericArrayGroup (string $keyGroup, string $subGroup) {
1563                 // Debug message
1564                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1565
1566                 // Determine it
1567                 $isValid = (($this->isGenericArrayGroupSet($keyGroup, $subGroup)) && (is_array($this->getGenericSubArray($keyGroup, $subGroup))));
1568
1569                 // Return it
1570                 return $isValid;
1571         }
1572
1573         /**
1574          * Checks if a given key is valid (array)
1575          *
1576          * @param       $keyGroup       Key group to get
1577          * @param       $subGroup       Sub group for the key
1578          * @param       $key            Key to check
1579          * @return      $isValid        Whether given sub group is valid
1580          */
1581         protected final function isValidGenericArrayKey (string $keyGroup, string $subGroup, $key) {
1582                 // Debug message
1583                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1584
1585                 // Determine it
1586                 $isValid = (($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) && (is_array($this->getGenericArrayKey($keyGroup, $subGroup, $key))));
1587
1588                 // Return it
1589                 return $isValid;
1590         }
1591
1592         /**
1593          * Initializes the web output instance
1594          *
1595          * @return      void
1596          */
1597         protected function initWebOutputInstance () {
1598                 // Init web output instance
1599                 $outputInstance = ObjectFactory::createObjectByConfiguredName('output_class');
1600
1601                 // Set it locally
1602                 $this->setWebOutputInstance($outputInstance);
1603         }
1604
1605         /**
1606          * Translates boolean true to 'Y' and false to 'N'
1607          *
1608          * @param       $boolean                Boolean value
1609          * @return      $translated             Translated boolean value
1610          */
1611         public static final function translateBooleanToYesNo (bool $boolean) {
1612                 // Make sure it is really boolean
1613                 assert(is_bool($boolean));
1614
1615                 // "Translate" it
1616                 $translated = ($boolean === true) ? 'Y' : 'N';
1617
1618                 // ... and return it
1619                 return $translated;
1620         }
1621
1622         /**
1623          * Creates a full-qualified file name (FQFN) for given file name by adding
1624          * a configured temporary file path to it.
1625          *
1626          * @param       $infoInstance   An instance of a SplFileInfo class
1627          * @return      $tempInstance   An instance of a SplFileInfo class (temporary file)
1628          * @throw       PathWriteProtectedException If the path in 'temp_file_path' is write-protected
1629          * @throws      FileIoException If the file cannot be written
1630          */
1631          protected static function createTempPathForFile (SplFileInfo $infoInstance) {
1632                 // Get config entry
1633                 $basePath = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('temp_file_path');
1634
1635                 // Is the path writeable?
1636                 if (!is_writable($basePath)) {
1637                         // Path is write-protected
1638                         throw new PathWriteProtectedException($infoInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
1639                 }
1640
1641                 // Add it
1642                 $tempInstance = new SplFileInfo($basePath . DIRECTORY_SEPARATOR . $infoInstance->getBasename());
1643
1644                 // Is it reachable?
1645                 if (!FrameworkBootstrap::isReachableFilePath($tempInstance)) {
1646                         // Not reachable
1647                         throw new FileIoException($tempInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
1648                 }
1649
1650                 // Return it
1651                 return $tempInstance;
1652          }
1653
1654         /**
1655          * "Getter" for a printable state name
1656          *
1657          * @return      $stateName      Name of the node's state in a printable format
1658          * @todo        Move this class away from this monolithic place (not whole class is monolithic)
1659          */
1660         public final function getPrintableState () {
1661                 // Default is 'null'
1662                 $stateName = 'null';
1663
1664                 // Get the state instance
1665                 $stateInstance = $this->getStateInstance();
1666
1667                 // Is it an instance of Stateable?
1668                 if ($stateInstance instanceof Stateable) {
1669                         // Then use that state name
1670                         $stateName = $stateInstance->getStateName();
1671                 }
1672
1673                 // Return result
1674                 return $stateName;
1675         }
1676
1677 }