diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/autoload.php | 43 | ||||
| -rw-r--r-- | core/design_patterns.php | 104 | ||||
| -rw-r--r-- | core/error_handling.php | 119 |
3 files changed, 266 insertions, 0 deletions
diff --git a/core/autoload.php b/core/autoload.php new file mode 100644 index 0000000..c72c335 --- /dev/null +++ b/core/autoload.php @@ -0,0 +1,43 @@ +<?php +/** register class-autoloader + * + * NOTE: + * Composer is strongly recommended; it is a much better option + * and takes care for many more than just autoloading classes; + * + * This's a fair autoloader in case composer is not available; + * supports single and namespaced classes. + */ +spl_autoload_register( + function($className) { + + // in order to support common namespaced classes + // replace '\' with '/' to get the path + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, $className); + + // check all possible classPaths for class + foreach(CLASSPATHS as $key => $basePath) { + + $file = WEB_ROOT . $basePath . $classPath .'.php'; + + // if class-file exist, include it + if (file_exists($file)) { + require_once $file; + break; + } + + } + + // // Feel free to extend the fanctionality + // // example: + // // custom (exception) namespaced classes loader + // $customNSclasses = array( + // '\\George\\Class' => 'path/to/George/Class', + // '\\Mary\\Class' => 'path/to/Marias/Class', + // ... + // ); + // if array_key_exists($className) { + // require_once $customNSclasses[$className]; + // } + } +); diff --git a/core/design_patterns.php b/core/design_patterns.php new file mode 100644 index 0000000..d66ffa9 --- /dev/null +++ b/core/design_patterns.php @@ -0,0 +1,104 @@ +<?php +/** proxy + * implements the proxy design pattern + * + * uses: Registry::cache + * + * algorithm: + * - 1: proxy gets (Class::method, Arguments) and calculates a unique key + * - 2: if a valid Cache for the triplete's key exists, it is served immediately + * - 3: otherwise calls Class::Method(Arguments), caches then serves response + * + * @param $class_method (array): a 2 (string) items array in the form + * : [ 'fully\qualifies\ClassName' , 'methodName' ] + * : Using the ClassName::class notation is strongly recommended. + * @param $args: array of arguments (passed to the Class::Method()) + * @param $ttl: time (in sec) where the data will remain valid (not expired) + * @param $options: bitwise flags* + * --- + * @return $data (mixed) + * + * (*) option flags + * all option flags represend non default behaviour of proxy + * --- + * PROXY_CACHE_ERRORS : cache value even if callback returns error + * PROXY_IGNORE_CACHE : ignore if cache exist; get result from callback + * PROXY_DO_NOT_CACHE : do not cache the result (even if from callback) + */ +function proxy(array $callable, array $args, int $ttl, int $options=0) +{ + // resolve option flags + $cache_errors = ($options&PROXY_CACHE_ERRORS) ? true : false; + $ignore_cache = ($options&PROXY_IGNORE_CACHE) ? true : false; + $do_not_cache = ($options&PROXY_DO_NOT_CACHE) ? true : false; + + // we're gonna use cache; + $cache = Registry::use('cache'); + + + // check validity of callable argument + // ------------------------------------------------------------------------- + if ((!is_callable($callable)) || (!is_array($callable))) { + + throw new Exception( + 'Proxy\'s first parameter has to be a callable array [fully\\qualified\\ClassName, method], passed:'.print_r($callable, true) + ); + + } + + // create a unique key + // ------------------------------------------------------------------------- + list($class, $method) = $callable; + $key = sha1( + $class .':'. $method .':'. json_encode( + $args, JSON_UNESCAPED_LINE_TERMINATORS|JSON_UNESCAPED_UNICODE + ) + ); + + // search cache if data for this key exist + // ------------------------------------------------------------------------- + if (!$ignore_cache) { + $data = $cache->get($key); + + } else { $data = false; } // ignore cache? pass false + + + if ($data !== false) { // if exists, serve cached data + + // if cashed data exist + // --------------------------------------------------------------------- + # if (!PRODUCTION) Benchmark::add_spot('proxy-cached'); + if ($data == '_ERROR_') return false; // previous cached error + else return $data; // previous cache () + + + } else { // else (not exist)... + + // no data on cache -> get data from Class::method( arguments ) + // --------------------------------------------------------------------- + $data = call_user_func_array($callable, $args); + + // handle newly created data + // --------------------------------------------------------------------- + + // if faulty data, return false + if ($data == false) { + + if ($cache_errors && !$do_not_cache) { // cache the error + $cache->set($key, '_ERROR_', $ttl); + } + + # if (!PRODUCTION) Benchmark::add_spot('proxy-new-error'); + return false; + } + + // store to cache + if (!$do_not_cache) { + $cache->set($key, $data, $ttl); + } + + # if (!PRODUCTION) Benchmark::add_spot('proxy-new'); + return $data; // serve data + } + +} diff --git a/core/error_handling.php b/core/error_handling.php new file mode 100644 index 0000000..95db98c --- /dev/null +++ b/core/error_handling.php @@ -0,0 +1,119 @@ +<?php +/** ERROR HANDLING SECTION + * handles exceptions and errors (even fatal) + * ----------------------------------------------------------------------------- + * The code... + * + Catches and handles all Errors and Throwable exceptions; + * - creates the error messages (for the requester and developer) + * ? TODO: routes the error messages to the logging channels + */ + + +// Global array to save errors +$anom_ERRORS = []; // ok, Globals are not recommended, but... + // this one collects only errors (about the application); + // it is oriented for the developer; not for the request. + + +/** Error handler, + * passes flow over the exception logger with new ErrorException. + */ +function handle_error(int $type, string $message, string $file, int $line) +{ + handle_exception( new ErrorException($message, 0, $type, $file, $line) ); +} + + +/** Uncaught exception handler. +*/ +function handle_exception(Throwable $e) +{ + global $anom_ERRORS; + + // full message description to be logged + $message = get_class($e) .": ". $e->getMessage() + . " on file '". $e->getFile() . "' at line {$e->getLine()}"; + + $anom_ERRORS[] = $message; // store the error +} + + +/** + * Catch fatal error work-around + * (set_error_handler not working on fatal errors). + */ +function on_shutdown_check_for_fatal() +{ + global $anom_ERRORS; + + $error = error_get_last(); + + if ($error != null) { // ignore 'normal' system shutdowns [exit|die]() + + if ( $error["type"] == E_ERROR ) { // get as much error-info + handle_error( + $error["type"] ?? -1, + $error["message"] ?? 'unknown error', + $error["file"] ?? 'unknown file', + $error["line"] ?? -1 + ); + } + } + + if (($anom_ERRORS != [])) { + + // TODO: + // Implemetnig the design/layout of the error-messages + // output should not be an error's-handling task, thus + // shall be assigned to the Rendes/View level; + // Same about the log service channels + + // TODO: + // FORWARD ERRORS into LOG-SYSTEM + // ex. into some file... + // file_put_contents("logs/thowable.log", $message_template .PHP_EOL, FILE_APPEND); + // or ...some other channer (Email, Slack, Database etc). + // Channes can be decided according to error severity; + // a file-system error loggin for backup is recommended + // partitioniong of errors onto file-system can be useful + + // TODO: + // 1st: Log Errors + // 2nd: Show Errors + + if (PRODUCTION) { + if (!defined('OUTPUT_STARTED')) { + // throw a prety-error message + Render::view('error/general', ['message' => 'Please forgive me, I know not what I do']); + } + + } else { + + if (!defined('OUTPUT_STARTED')) { + // no template loaded; render erros in general-error template + Render::view('error/general', ['message' => implode("<br/>And:<br/>", $anom_ERRORS)]); + + } else { + echo '<div style="padding:1em;background:#933;width:100%;color:#fff"> + Errors:<br />'. implode("<br/>And:<br/>", $anom_ERRORS) .'</div>'; + } + } + } +} + + +// register callback on shutdown +register_shutdown_function( "on_shutdown_check_for_fatal" ); + +// register custom error handler callback +set_error_handler( "handle_error" ); + +// set custom exception handler function +set_exception_handler( "handle_exception" ); + +// send errors to stderr (instead of stdout) +ini_set( "display_errors", "off" ); + +// thow out all errors +error_reporting( E_ALL ); + |
