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