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