diff options
| author | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-03-26 04:50:46 +0300 |
|---|---|---|
| committer | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-03-26 04:50:46 +0300 |
| commit | 21fedb3af692b12c1f0aa266bbf788b01f2cbe4c (patch) | |
| tree | e600e16a2894407eba840e7694a4f290669e31d8 /core | |
| parent | d90b51ce89bcc693e3014b313fe8e3b975031adf (diff) | |
| download | classroom-21fedb3af692b12c1f0aa266bbf788b01f2cbe4c.tar.gz classroom-21fedb3af692b12c1f0aa266bbf788b01f2cbe4c.tar.bz2 classroom-21fedb3af692b12c1f0aa266bbf788b01f2cbe4c.zip | |
auth tests
Diffstat (limited to 'core')
| -rw-r--r-- | core/classes/Render.php | 343 | ||||
| -rw-r--r-- | core/classes/session/DefaultSession.php | 9 | ||||
| -rw-r--r-- | core/classes/session/FilesSession.php | 20 | ||||
| -rw-r--r-- | core/config/anom_settings.php | 312 | ||||
| -rw-r--r-- | core/config/credentials.php | 126 | ||||
| -rw-r--r-- | core/config/init.php | 17 | ||||
| -rw-r--r-- | core/helpers/error_handling.php | 8 | ||||
| -rw-r--r-- | core/helpers/render.php | 338 |
8 files changed, 602 insertions, 571 deletions
diff --git a/core/classes/Render.php b/core/classes/Render.php new file mode 100644 index 0000000..401178d --- /dev/null +++ b/core/classes/Render.php @@ -0,0 +1,343 @@ +<?php +/** Rendering System + * + * this is the View part of the anom MVC framework + ***/ + + Class Render { + + /** load template + * --- + * TODO: + * explain/document the difference between a template and a view + * + * TODO: + * Implementing the Render/View operation as a class, using the same- + * name methods with Laraver or CodeIgniter could be good idea; + * and get rid of if(!defined('OUTPUT_STARTED')) define('OUTPUT_STARTED',1); + */ + public static function template($template, $data) { + + $file = VIEWS_DIRECTORY . $template . '.php'; + + if (file_exists($file)) { + if (!defined('OUTPUT_STARTED')) define('OUTPUT_STARTED', 1); + + ob_start(); + + extract( self::sanitize_output($data) ); + require( $file ); + + ob_flush(); + + } else if (!PRODUCTION) { + + echo "<!-- view ". $file ." is missing -->"; + } + + } + + /** parse_sections + * --- + * parse a group of views (sections) + * @param $sections: an array of sections + * + * each section group is an array with view, key and data properties + * + view: defines the view template/file + * + key: the variable name that view uses to parse data OR empty-string* + * * if key is an empty then $data should be an array (which + * includes all [variable-name:data] pairs utilized by the view) + * + data: holds the actual data + */ + + public static function sections($sections) { + + foreach($sections as $sect) { + + if ($sect['key'] == '') { + self::view( $sect['view'], $sect['data'] ); + + } else { + self::view( $sect['view'], [ $sect['key'] => $sect['data']] ); + } + } + } + + + /** render function + * --- + * uses php's short-tag syntax for templating system + * extract data into template + * @param $view: view-template filename + * @param $data: data to embed into view-template + * @param $sanitize: of true then sanitize data. + * important NOTE: data is an array [key => value] + */ + public static function view($view, $data=[], $sanitize = false) { + + $file = VIEWS_DIRECTORY . $view . '.php'; + + if (file_exists($file)) { + if (!defined('OUTPUT_STARTED')) define('OUTPUT_STARTED', 1); + + extract( $sanitize ? self::sanitize_output($data) : $data ); + require( $file ); + + } else if (!PRODUCTION) { + + echo "<!-- view ". $file ." is missing -->"; + } + + } + + + /** render asap + * --- + * render_view then output code + * so that client will get html to render + * while server calculates next html + */ + public static function asap($view, $data, $sanitize = false) { + self::view($view, $data, $sanitize); + ob_flush(); + } + + + /** render text + * --- + * This function simply echoes text + * with Content-Type and Cache Headers + * @param $data : the text to echo + * @param $cType : Content-Type header + * @param $ttl : cache-contol headers; if false response is not-cached; else (int) chache for $ttl seconds + */ + public static function text( $data, $contentType = 'text/html; charset=UTF-8', $ttl = false ) { + header('Content-Type: '. $contentType); + if ($ttl) { + $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; + header("Expires: {$ts}"); + header("Pragma: cache"); + header("Cache-Control: max-age={$ttl}"); + + } else { + $ts = gmdate("D, d M Y H:i:s") . " GMT"; + header("Expires: {$ts}"); + header("Last-Modified: {$ts}"); + header("Pragma: no-cache"); + header("Cache-Control: no-cache, must-revalidate"); + } + echo htmlspecialchars($data); + } + + + /** reply_json + * --- + * transform a php-array to json and echo to client + * Can be used for API calls + * + * @param $data : the php array) + * @param $ttl : cache-contol headers; if false response is not-cached; else chache for $ttl seconds + */ + public static function json( $data, $ttl = false ) { + header('Content-Type: application/json'); + if ($ttl) { + $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; + header("Expires: {$ts}"); + header("Pragma: cache"); + header("Cache-Control: max-age={$ttl}"); + + } else { + $ts = gmdate("D, d M Y H:i:s") . " GMT"; + header("Expires: {$ts}"); + header("Last-Modified: {$ts}"); + header("Pragma: no-cache"); + header("Cache-Control: no-cache, must-revalidate"); + } + echo json_encode($data, JSON_UNESCAPED_UNICODE); + } + + + + + // HELPER FUNCTIONS + // ----------------------------------------------------------------------------- + + + /** link asset + * --- + * the function defines the assets (css or js) + * to be loaded on the client (creates the HTML) + * @param $type : type of asset [css|js] + * @param $assetIDs : an array of ('assetID' => 'paramaters') + * + * example calls: + * load_asset('css', ['main' => 'media="all"', 'filters' => 'media="all"']); + * load_asset( 'js', ['jquery' => '', 'lazyloader' => 'async']); + */ + public static function asset( $type, $assetIDs, $paramatres = '' ) { + + $code = ""; // code to return + + // make sure $assetIDs is array (for coding simplicity) + if (!is_array($assetIDs)) { + $assetIDs = [ $assetIDs ]; + } + + // check asset type and construct all asset inserts + switch ($type) { + + case 'font': + foreach($assetIDs as $asset) { + $code .= "\n\t<link href='". FONTS_DIR . FONT_FILES[$asset] ."' as='font' type='font/woff2' {$paramatres}/>"; + } + break; + + case 'js': + foreach($assetIDs as $asset) { + $code .= "\n\t<script src='". JS_DIR . JS_LIBRARIES[$asset] ."' id='{$asset}' {$paramatres}></script>"; + } + break; + + case 'css': + default: + foreach($assetIDs as $asset) { + $code .= "\n\t<link rel='stylesheet' href='". CSS_DIR . CSS_FILES[$asset] ."' id='{$asset}' {$paramatres} />"; + } + } + + echo $code; + } + + + /** sanitize_output (recursive) + * --- + * Sanitizes data that are about to rendered + * (usually when render_view() is called). + * + * NOTE: + * mitigates XSS attachs + * + * @param $data: (array) + */ + public static function sanitize_output($data) { + //// check https://stackoverflow.com/questions/2002710/php-how-to-perform-htmlspecialchar-on-an-array-of-arrays + + //// $output = array_map("myFunc", $data); + + global $secure; + + $output = array(); + foreach($data as $key => $val) { + + if (is_string($val)) { + $output[$key] = htmlspecialchars(self::remove_invisible_characters($val)); + + } else if (is_array($val)) { + $output[$key] = self::sanitize_output($val); + + } else { + $output[$key] = $val; + } + } + return $output; + } + + + /** remove_invisible_characters() + * --- + * @used by sanitize_output() + */ + public static function remove_invisible_characters($str, $url_encoded = TRUE) + { + $non_displayables = array(); + + // every control character except newline (dec 10), + // carriage return (dec 13) and horizontal tab (dec 09) + if ($url_encoded) { + $non_displayables[] = '/%0[0-8bcef]/i'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-f]/i'; // url encoded 16-31 + $non_displayables[] = '/%7f/i'; // url encoded 127 + } + + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 + + do { + $str = preg_replace($non_displayables, '', $str, -1, $count); + } while ($count); + + return $str; + } + + + /** html + * --- + * outputs code as html + * + * @param $str (string) + * @return html5 (string) + */ + public static function html($str) { + if (!isset($str) || $str== null) return; + if (($str == '') || is_numeric($str)) return $str; + return htmlspecialchars_decode($str, ENT_QUOTES|ENT_HTML5); + } + + + /** set_headers + * --- + * set custom response-Headers + * + * @param $contentType + * @param $ttl: int or false) + * @param $more: array of (content-type => content-value) pairs + */ + public static function set_headers($contentType = 'text/html; charset=UTF-8', $ttl = false, $more = [] ) { + + // handle common content-type shorcuts + switch ($contentType) { + case 'text': + case 'html': + $contentType = 'text/html; charset=UTF-8'; + break; + case 'json': + $contentType = 'application/json; charset=utf-8'; + break; + case 'js': + $contentType = 'application/javascript; charset=utf-8'; + break; + case 'css': + $contentType = 'text/css'; + break; + default: + // $contentType stays as-is + break; + } + + // send headers + header('Content-Type: '. $contentType); + if ($ttl) { + $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; + header("Expires: {$ts}"); + header("Pragma: cache"); + header("Cache-Control: max-age={$ttl}"); + + } else { + $ts = gmdate("D, d M Y H:i:s") . " GMT"; + header("Expires: {$ts}"); + header("Last-Modified: {$ts}"); + header("Pragma: no-cache"); + header("Cache-Control: no-cache, must-revalidate"); + } + + // send more headers + if ($more != []) { + foreach($more as $header => $value) { + header($header .': '. $value); + } + } + + } + + + +}
\ No newline at end of file diff --git a/core/classes/session/DefaultSession.php b/core/classes/session/DefaultSession.php index d3099a5..079ab9d 100644 --- a/core/classes/session/DefaultSession.php +++ b/core/classes/session/DefaultSession.php @@ -2,14 +2,19 @@ /** DefauleSession * -- + * * is a dummy session hanlder that wraps the * php's default session engine implementation; + * */ class DefaultSession implements SessionHandlerInterface { public function __construct() { // Start the session + // (the default way) + // and let the session* functions + // do what they know session_name(SESSION_NAME); session_start(); } @@ -21,7 +26,9 @@ class DefaultSession implements SessionHandlerInterface { public function close() {} #[ReturnTypeWillChange] - public function read(string $id) {} + public function read(string $id) { + + } #[ReturnTypeWillChange] public function write(string $id, string $data) {} diff --git a/core/classes/session/FilesSession.php b/core/classes/session/FilesSession.php index 8762776..a06f29c 100644 --- a/core/classes/session/FilesSession.php +++ b/core/classes/session/FilesSession.php @@ -35,6 +35,7 @@ class FileSessionHandler implements SessionHandlerInterface public function __construct() { // values comming from configuration + $this->sessionName = SESSION_NAME; $this->sess_filename = $path; $this->minutes = $minutes; @@ -49,7 +50,7 @@ class FileSessionHandler implements SessionHandlerInterface ); // Start the session - session_name(SESSION_NAME); + session_name($this->sessionName); session_start(); } @@ -80,14 +81,9 @@ class FileSessionHandler implements SessionHandlerInterface public function read($sessionId): string|false { if (!file_exists($filename) || !is_readable($filename)) return false; - return file_get_contents($filename); + $data = file_get_contents($filename); - # if ($this->files->isFile($path = $this->path.'/'.$sessionId) && - # $this->files->lastModified($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) { - # return $this->files->sharedGet($path); - # } - - # depricated? return ''; + return @unserialize($data); } /** write @@ -96,11 +92,11 @@ class FileSessionHandler implements SessionHandlerInterface public function write($sessionId, $data): bool { $h = fopen($filename, 'w'); - if (fwrite($h,$data) === false) { + if (fwrite($h, serialize($data)) === false) { throw new Exception('Could not write session data'); return false; - } - fclose($h); + } + fclose($h); return true; } @@ -128,7 +124,7 @@ class FileSessionHandler implements SessionHandlerInterface ->in($this->path) ->files() ->ignoreDotFiles(true) - ->date('<= now - '.$lifetime.' seconds'); + ->date('<= now - '. $lifetime .' seconds'); $deletedSessions = 0; diff --git a/core/config/anom_settings.php b/core/config/anom_settings.php index 406c6aa..76c7c62 100644 --- a/core/config/anom_settings.php +++ b/core/config/anom_settings.php @@ -1,35 +1,37 @@ <?php + /** CONFIGURATION PATAMETRES - * --- + * ----------------------------------------------------------------------------- * This is the first file you need to setup * in order to start a new application + * + * NOTE: + * You may rewrite the defines manualy setting any values manualy + * Here we propose that you use an .env file (with is a very safe option) + * the env file is saved into `/core/auth` folder */ +if (file_exists("../core/auth/.env")) { + $ini_array = parse_ini_file("../core/auth/.env"); +} + // DEFINE WHETTHER THE APP RUNS ON PRODUCTION // Don't forget to change this setting // when deploying to a different stage // ----------------------------------------------------------------------------- -// define by OS parameter -//// if (getenv('ENVIRONMENT')) { -//// if (getenv('ENVIRONMENT') == 'PRODUCTION') { -//// -//// define('PRODUCTION', true); -//// -//// } else { -//// define('PRODUCTION', false); -//// } -//// } -//// // define by .env file -//// if (!defined('PRODUCTION')) { -//// if (file_exists('../core/auth/.env')) { -//// $ini_array = parse_ini_file("../core/auth/.env"); -//// -//// define('PRODUCTION', $ini_array['ENVIRONMENT']); -//// } -//// } else { -//// define('PRODUCTION', false); // or define manualy -//// } -define('PRODUCTION', false); + +# // define by OS parameter +# if (getenv('ENVIRONMENT')) { +# if (getenv('ENVIRONMENT') == 'PRODUCTION') { +# define('PRODUCTION', true); +# } else { +# define('PRODUCTION', false); +# } +# } + +# define by .env file +define('PRODUCTION', ($ini_array['ENVIRONMENT'] == 'production') ); + // Custom Names //////////////////////////////////////////////////////////////// @@ -43,15 +45,17 @@ define('SESSION_NAME', 'clroom'); // Session Name -// APPLICATION PATHS /////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- - -// Obligarory -// These are needed in order to run the bare minimum MVC system -// They also make code easier to read -// -// NOTE: probably you don't need to chage these defines -// ----------------------------------------------------------------------------- +/** APPLICATION PATHS /////////////////////////////////////////////////////////// + * ----------------------------------------------------------------------------- + * + * Obligarory + * These are needed in order to run the bare minimum MVC system + * They also make code easier to read + * + * NOTE: + * probably you don't need to chage these defines + * ----------------------------------------------------------------------------- + */ define('APP_ROOT', dirname(get_included_files()[0]) ); @@ -68,22 +72,28 @@ define('VIEWS_DIRECTORY', APP_ROOT.'/app/views/'); define('TESTS_DIRECTORY', APP_ROOT."/../tests/"); -// AUTOLOADER ////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- - -// An autoloader for classes is required; -// OPTION 1: COMPOSER -// Composer is recommended and makes much more than simple autoloding +/** AUTOLOADER ///////////////////////////////////////////////////////////////// + * ----------------------------------------------------------------------------- + * An autoloader for classes is required; + * + * + * OPTION 1: COMPOSER + * Composer is recommended and makes much more than simple autoloding + */ define('AUTOLOADER' , '../vendor/autoload.php'); -// OPTION 2: Custom Autoloader -// If Composer is not supported, another autoloading proccess required -// Comment/Disable the 1st option and uncomment/Enable the next line +/** ... + * + * OPTION 2: Custom Autoloader + * If Composer is not supported, another autoloading proccess required + * Comment/Disable the 1st option and uncomment/Enable the next line + */ # define('AUTOLOADER' , '../core/helpers/autoload.php'); -// CLASSPATHS : array of paths where classes are saved -// Needed only when the custom autoloader is used -// (like autoload.classmap section of Composer) +/** CLASSPATHS : array of paths where classes are saved + * Needed only when the custom autoloader is used + * (like autoload.classmap section of Composer) + */ # define('CLASSPATHS', array( # '/../core/classes/', # '/app/controllers/', @@ -94,27 +104,45 @@ define('AUTOLOADER' , '../vendor/autoload.php'); -// ADVANCED OPTIMIZATION /////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- - -// APP_CACHE defines the cache-driver; -// accepted values are the name of the Cache-interface impementations (the exact -// names of the classes) -// -// 'FileCache' : caching in filesystem; fair if caching on HDD; great on SSD -// 'RedisCache' : caching in Redis server; generally recommended if available -// 'MemCachedCache' : caching in Memcached server; recommended if available -// -// NOTE: FileCache on a NVMe SDD is the fastest option; -// Redis and Memcached are really-fast caching options and are available for -// scaling horizontaly your application (eache one has it's own strngths; -// study, then choose the one that fulfills your needs; -// ----------------------------------------------------------------------------- + /** SESSION HANDLING ////////////////////////////////////////////////////////// + * ---------------------------------------------------------------------------- + * + * there are several session drivers for session handling: + * + * 'DefaultSession' : uses the default (file-based) php session management + * 'FileSession' : defines a custom file-based session mechanism + * 'DatabaseSession' : mechanism that stores Session data into a DataBase + * ---------------------------------------------------------------------------- + */ + +define('SESSION_DRIVER', 'DefaultSession'); + + + +/* ADVANCED OPTIMIZATION /////////////////////////////////////////////////////// + * ----------------------------------------------------------------------------- + * + * APP_CACHE defines the cache-driver; + * accepted values are the name of the Cache-interface impementations (the exact + * names of the classes) + * + * 'FileCache' : caching in filesystem; fair if caching on HDD; great on SSD + * 'RedisCache' : caching in Redis server; generally recommended if available + * 'MemCachedCache' : caching in Memcached server; recommended if available + * + * NOTE: FileCache on a NVMe SDD is the fastest option; + * Redis and Memcached are really-fast caching options and are available for + * scaling horizontaly your application (eache one has it's own strngths; + * study, then choose the one that fulfills your needs; + * ----------------------------------------------------------------------------- + */ define('CACHE_DRIVER', 'FileCache'); -// Caching expiration times fpr various expensive objects -// ----------------------------------------------------------------------------- + +/** Caching expiration times fpr various expensive objects + * ----------------------------------------------------------------------------- + */ // Root objects like product-categories tree define('CACHE_ROOT_TTL', 28800); // 8 hours @@ -126,17 +154,11 @@ define('CACHE_CATEGORY_TTL', 18000); // 5 hours define('CACHE_PRODUCT_TTL', 3600); // 1 hour -// SESSION_DRIVER ; accepted values... -// 'files' : (defult) php uses filesystem for saving session; fair if caching in SSD drive -// 'redis' : keep sessions in a Redis server; rapid-fast but wastes large amount of RAM -// 'database' : keep session data in Database; ideal for session across multiple servers -// ----------------------------------------------------------------------------- -define('SESSION_DRIVER', 'files'); // CONNECTIONS ... -define('FILECACHE_PATH', APP_ROOT.'/cache/'); // if CACHE_DRIVER is set to 'FileCache' +define('FILECACHE_PATH', APP_ROOT.'/cache/'); // if CACHE_DRIVER is set to 'FileCache' # define('REDIS_HOST', '127.0.0.1'); // if CACHE_DRIVER is set to 'RedisCache' @@ -144,16 +166,16 @@ define('FILECACHE_PATH', APP_ROOT.'/cache/'); // if CACHE_DRIVER is set to 'Fi - -// SECURITY SETTINGS /////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- - -// Cross Site Request Forgery -// --- -// Enables a CSRF cookie token to be set. When set to TRUE, token will be -// checked on a submitted form. If you are accepting user data, it is strongly -// recommended CSRF protection be enabled. -// ----------------------------------------------------------------------------- +/** SECURITY SETTINGS /////////////////////////////////////////////////////////// + * ----------------------------------------------------------------------------- + * + * Cross Site Request Forgery + * --- + * Enables a CSRF cookie token to be set. When set to TRUE, token will be + * checked on a submitted form. If you are accepting user data, it is strongly + * recommended CSRF protection be enabled. + * ----------------------------------------------------------------------------- + */ define('CSRF_PROTCTION', false); @@ -166,3 +188,137 @@ define('CSRF_EXPIRE', 7200); // The number in seconds the token shoul define('CSRF_REGENERATE', TRUE); // Regenerate token on every submission define('CSRF_EXCLUDE_URIS', array()); // Array of URIs which ignore CSRF checks + + + + +/** CREDENTIALS + * ----------------------------------------------------------------------------- + */ + +/** SERVICE PARAMETRES + * ----------------------------------------------------------------------------- + * + * Connection parametres and credentials for accssing services + * needed by the application; Such services may be.. + * + * - RDBMS + * -- MySQL + * -- PotgresSQL + * + * - Caching services + * -- Redis + * -- MemCached + * + * Edit only the constants needed by the application; + * Comment those you do not need; + * + * ///////////////////////////////////////////////////////////////////////////// + */ + +/** DATABASE CONNECTION PARAMETRES + * ----------------------------------------------------------------------------- + * Production and staging environments may use different databases. + * + * A safe practice is to keep credentials outside plain files like this one + * in environmental variable or other secret file etc. + * + * Uncomment/enable each group of definitions suits your case to define + * the credentials needed for accessing the database + */ + +/** This is a SAFE method (define credentials as OS environmental variables) + * ----------------------------------------------------------------------------- + */ +# define('DB_NAME', getenv('DB_NAME')); // database name +# +# define('DB_USER', getenv('DB_USER')); // database user-name +# +# define('DB_PASS', getenv('DB_PASS')); // user's pass +# +# define('PDO_HOST', getenv('PDO_HOST')); // database host (connection string) + + +/** This is another SAFE method (keep credentials in an .env file) + * ----------------------------------------------------------------------------- + */ + + $ini_array = parse_ini_file("../core/auth/.env"); + + define('DB_NAME', $ini_array['DB_NAME']); + + define('DB_USER', $ini_array['DB_USER']); + + define('DB_PASS', $ini_array['DB_PASS']); + + define('PDO_HOST', $ini_array['PDO_HOST']); + + + /** This is just good enough + * ----------------------------------------------------------------------------- + */ + + # if (!PRODUCTION) { // == Deployed on staging + # + # // STAGING SETTINGS: + # + # define('DB_NAME', 'dev_db_name'); + # + # define('DB_USER', 'devUserName'); + # + # define('DB_PASS', getenv('DBPASSWORD')); + # + # // PDO_HOST can be a hostname/port combination -or- a unix socket + # // ...examples: + # // define('PDO_HOST', 'host=/localhost') ## hostname case + # // define('PDO_HOST', 'host=/localhost;port=3456') ## hostname/port case + # // define('PDO_HOST', 'unix_socket=/sql/ex123:europe:some-db'); ## unix socket + # define('PDO_HOST', 'unix_socket=/cloudsql/name-123456:europe-west4:name-db-eu'); + # + # } else { // == Deployed on production + # + # // PRODUCTION SETTINGS: + # + # define('DB_NAME', 'your_db_name'); + # define('DB_USER', 'dbusername'); + # define('DB_PASS', getenv('DBPASSWORD')); + # define('PDO_HOST', 'unix_socket=/cloudsql/name-123456:europe-west4:name-db-eu'); + # + # } + + define('DB_TIMEZONE', "SET time_zone = 'Europe/Athens'"); + + /** NOTE: + * Both Redis and Memcached configurations are given as a template to work on; + * In most cases the default values should do the job -- of course you need to + * read the README file (check the project's root); + * As these caching services are not fully tested you may need to ochestrate + * the services in detail or edit the connection strings into the core classes + * (hosted under the '/core/classes/cacher' folder) + */ + + + /** REDIS SERVICE + * ----------------------------------------------------------------------------- + * Most of the times Redis is running on 'localhost' (host = '127.0.0.1') + * When implemented via anom's docker-composer (redis via bridge) then you need + * do declare the hostname as 'redis' + */ + # + # if (!defined('REDIS_HOST')) define('REDIS_HOST', 'redis'); + # + # if (!defined('REDIS_PORT')) define('REDIS_PORT', 6379); + # + # if (!defined('REDIS_PASS')) define('REDIS_PASS', null); + + + /** MEMCACHED + * ----------------------------------------------------------------------------- + * Memcached looks very much like Redis (plus, both are serving from RAM) + * Usualy Memcashed is running localy so host is 'localhost' ('127.0.0.1') + * If you use the framework's docker-composer implementation then + * the hostname is 'anomemcached' + */ + # + #if (!defined('MEMCACHE_HOST')) define('MEMCACHE_HOST', 'anomemcached'); +
\ No newline at end of file diff --git a/core/config/credentials.php b/core/config/credentials.php deleted file mode 100644 index 43fa0a4..0000000 --- a/core/config/credentials.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php -/** SERVICE PARAMETRES - * ----------------------------------------------------------------------------- - * - * Connection parametres and credentials for accssing services - * needed by the application; Such services may be.. - * - * - RDBMS - * -- MySQL - * -- PotgresSQL - * - * - Caching services - * -- Redis - * -- MemCached - * - * Edit only the constants needed by the application; - * Comment those you do not need; - * - * ///////////////////////////////////////////////////////////////////////////// - */ - -/** DATABASE CONNECTION PARAMETRES - * ----------------------------------------------------------------------------- - * Production and staging environments may use different databases. - * - * A safe practice is to keep credentials outside plain files like this one - * in environmental variable or other secret file etc. - * - * Uncomment/enable each group of definitions suits your case to define - * the credentials needed for accessing the database - */ - -/** This is a SAFE method (define credentials as OS environmental variables) - * ----------------------------------------------------------------------------- - */ -# define('DB_NAME', getenv('DB_NAME')); // database name -# -# define('DB_USER', getenv('DB_USER')); // database user-name -# -# define('DB_PASS', getenv('DB_PASS')); // user's pass -# -# define('PDO_HOST', getenv('PDO_HOST')); // database host (connection string) - - -/** This is another SAFE method (keep credentials in an .env file) - * ----------------------------------------------------------------------------- - */ - -$ini_array = parse_ini_file("../core/auth/.env"); - -define('DB_NAME', $ini_array['DB_NAME']); - -define('DB_USER', $ini_array['DB_USER']); - -define('DB_PASS', $ini_array['DB_PASS']); - -define('PDO_HOST', $ini_array['PDO_HOST']); - - -/** This is just good enough - * ----------------------------------------------------------------------------- - */ -# -# if (!PRODUCTION) { // == Deployed on staging -# -# // STAGING SETTINGS: -# -# define('DB_NAME', 'dev_db_name'); -# -# define('DB_USER', 'devUserName'); -# -# define('DB_PASS', getenv('DBPASSWORD')); -# -# // PDO_HOST can be a hostname/port combination -or- a unix socket -# // ...examples: -# // define('PDO_HOST', 'host=/localhost') ## hostname case -# // define('PDO_HOST', 'host=/localhost;port=3456') ## hostname/port case -# // define('PDO_HOST', 'unix_socket=/sql/ex123:europe:some-db'); ## unix socket -# define('PDO_HOST', 'unix_socket=/cloudsql/name-123456:europe-west4:name-db-eu'); -# -# } else { // == Deployed on production -# -# // PRODUCTION SETTINGS: -# -# define('DB_NAME', 'your_db_name'); -# define('DB_USER', 'dbusername'); -# define('DB_PASS', getenv('DBPASSWORD')); -# define('PDO_HOST', 'unix_socket=/cloudsql/name-123456:europe-west4:name-db-eu'); -# -# } - -define('DB_TIMEZONE', "SET time_zone = 'Europe/Athens'"); - -/** NOTE: - * Both Redis and Memcached configurations are given as a template to work on; - * In most cases the default values should do the job -- of course you need to - * read the README file (check the project's root); - * As these caching services are not fully tested you may need to ochestrate - * the services in detail or edit the connection strings into the core classes - * (hosted under the '/core/classes/cacher' folder) - */ - - -/** REDIS SERVICE - * ----------------------------------------------------------------------------- - * Most of the times Redis is running on 'localhost' (host = '127.0.0.1') - * When implemented via anom's docker-composer (redis via bridge) then you need - * do declare the hostname as 'redis' - */ -# -# if (!defined('REDIS_HOST')) define('REDIS_HOST', 'redis'); -# -# if (!defined('REDIS_PORT')) define('REDIS_PORT', 6379); -# -# if (!defined('REDIS_PASS')) define('REDIS_PASS', null); - - -/** MEMCACHED - * ----------------------------------------------------------------------------- - * Memcached looks very much like Redis (plus, both are serving from RAM) - * Usualy Memcashed is running localy so host is 'localhost' ('127.0.0.1') - * If you use the framework's docker-composer implementation then - * the hostname is 'anomemcached' - */ -# -#if (!defined('MEMCACHE_HOST')) define('MEMCACHE_HOST', 'anomemcached'); diff --git a/core/config/init.php b/core/config/init.php index b035faf..2db2503 100644 --- a/core/config/init.php +++ b/core/config/init.php @@ -16,19 +16,16 @@ * anything else can be injected on-demand */ -// Load authorization parametres -// ----------------------------------------------------------------------------- -require_once CREDENTIALS; - // setup error-handliing // ----------------------------------------------------------------------------- require_once '../core/helpers/error_handling.php'; -// Load rendering sub-system -// ----------------------------------------------------------------------------- -require_once RENDERING_SYSTEM; +// depricated: +/// // Load rendering sub-system +/// // ----------------------------------------------------------------------------- +/// require_once RENDERING_SYSTEM; // Setup Application Engiine @@ -43,16 +40,12 @@ Registry::vow('database', function() { return new Database(); }); // Attach Cache to the Resitry as a vow // (CACHE_DRIVER) acts as driver-wrapper +// --- -- -- - - - Registry::vow('cache', function() { return new (CACHE_DRIVER)(); }); // CHECK: if strict mode has any benefits: // Registry::vow('cache', function():Cache_interface { return new (CACHE_DRIVER)(); }); -// Start session -Registry::set('session', new DefaultSession()); - - - // Initialize THE REQUEST // ----------------------------------------------------------------------------- Registry::set('REQUEST', new Request()); diff --git a/core/helpers/error_handling.php b/core/helpers/error_handling.php index bc60968..95db98c 100644 --- a/core/helpers/error_handling.php +++ b/core/helpers/error_handling.php @@ -82,16 +82,16 @@ function on_shutdown_check_for_fatal() // 2nd: Show Errors if (PRODUCTION) { - if (!defined('VIEW_LOADED')) { + if (!defined('OUTPUT_STARTED')) { // throw a prety-error message - render_view('error/general', ['message' => 'Please forgive me, I know not what I do']); + Render::view('error/general', ['message' => 'Please forgive me, I know not what I do']); } } else { - if (!defined('VIEW_LOADED')) { + if (!defined('OUTPUT_STARTED')) { // no template loaded; render erros in general-error template - render_view('error/general', ['message' => implode("<br/>And:<br/>", $anom_ERRORS)]); + Render::view('error/general', ['message' => implode("<br/>And:<br/>", $anom_ERRORS)]); } else { echo '<div style="padding:1em;background:#933;width:100%;color:#fff"> diff --git a/core/helpers/render.php b/core/helpers/render.php deleted file mode 100644 index 9dcd34a..0000000 --- a/core/helpers/render.php +++ /dev/null @@ -1,338 +0,0 @@ -<?php -/** Rendering System - * --- - * this is the View part of the MVC framework. - * Decided not to make it a Class - ***/ - - -/** load template - * --- - * TODO: - * explain/document the difference between a template and a view - * - * TODO: - * Implementing the Render/View operation as a class, using the same- - * name methods with Laraver or CodeIgniter could be good idea; - * and get rid of if(!defined('VIEW_LOADED')) define('VIEW_LOADED',1); - */ -function load_template($template, $data) { - - $file = VIEWS_DIRECTORY . $template . '.php'; - - if (file_exists($file)) { - if (!defined('VIEW_LOADED')) define('VIEW_LOADED', 1); - - ob_start(); - - extract( sanitize_output($data) ); - require( $file ); - - ob_flush(); - - } else if (!PRODUCTION) { - - echo "<!-- view ". $file ." is missing -->"; - } - -} - -/** parse_sections - * --- - * parse a group of views (sections) - * @param $sections: an array of sections - * - * each section group is an array with view, key and data properties - * + view: defines the view template/file - * + key: the variable name that view uses to parse data OR empty-string* - * * if key is an empty then $data should be an array (which - * includes all [variable-name:data] pairs utilized by the view) - * + data: holds the actual data - */ - -function parse_sections($sections) { - - foreach($sections as $sect) { - - if ($sect['key'] == '') { - render_view( $sect['view'], $sect['data'] ); - - } else { - render_view( $sect['view'], [ $sect['key'] => $sect['data']] ); - } - } -} - - -/** render function - * --- - * uses php's short-tag syntax for templating system - * extract data into template - * @param $view: view-template filename - * @param $data: data to embed into view-template - * @param $sanitize: of true then sanitize data. - * important NOTE: data is an array [key => value] - */ -function render_view($view, $data, $sanitize = false) { - - $file = VIEWS_DIRECTORY . $view . '.php'; - - if (file_exists($file)) { - if (!defined('VIEW_LOADED')) define('VIEW_LOADED', 1); - - extract( $sanitize ? sanitize_output($data) : $data ); - require( $file ); - - } else if (!PRODUCTION) { - - echo "<!-- view ". $file ." is missing -->"; - } - -} - - -/** render asap - * --- - * render_view then output code - * so that client will get html to render - * while server calculates next html - */ -function render_asap($view, $data, $sanitize = false) { - render_view($view, $data, $sanitize); - ob_flush(); -} - - -/** render text - * --- - * This function simply echoes text - * with Content-Type and Cache Headers - * @param $data : the text to echo - * @param $cType : Content-Type header - * @param $ttl : cache-contol headers; if false response is not-cached; else (int) chache for $ttl seconds - */ -function render_text( $data, $contentType = 'text/html; charset=UTF-8', $ttl = false ) { - header('Content-Type: '. $contentType); - if ($ttl) { - $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; - header("Expires: {$ts}"); - header("Pragma: cache"); - header("Cache-Control: max-age={$ttl}"); - - } else { - $ts = gmdate("D, d M Y H:i:s") . " GMT"; - header("Expires: {$ts}"); - header("Last-Modified: {$ts}"); - header("Pragma: no-cache"); - header("Cache-Control: no-cache, must-revalidate"); - } - echo htmlspecialchars($data); -} - - -/** reply_json - * --- - * transform a php-array to json and echo to client - * Can be used for API calls - * - * @param $data : the php array) - * @param $ttl : cache-contol headers; if false response is not-cached; else chache for $ttl seconds - */ -function reply_json( $data, $ttl = false ) { - header('Content-Type: application/json'); - if ($ttl) { - $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; - header("Expires: {$ts}"); - header("Pragma: cache"); - header("Cache-Control: max-age={$ttl}"); - - } else { - $ts = gmdate("D, d M Y H:i:s") . " GMT"; - header("Expires: {$ts}"); - header("Last-Modified: {$ts}"); - header("Pragma: no-cache"); - header("Cache-Control: no-cache, must-revalidate"); - } - echo json_encode($data, JSON_UNESCAPED_UNICODE); -} - - - - -// HELPER FUNCTIONS -// ----------------------------------------------------------------------------- - - -/** link asset - * --- - * the function defines the assets (css or js) - * to be loaded on the client (creates the HTML) - * @param $type : type of asset [css|js] - * @param $assetIDs : an array of ('assetID' => 'paramaters') - * - * example calls: - * load_asset('css', ['main' => 'media="all"', 'filters' => 'media="all"']); - * load_asset( 'js', ['jquery' => '', 'lazyloader' => 'async']); - */ -function link_asset( $type, $assetIDs, $paramatres = '' ) { - - $code = ""; // code to return - - // make sure $assetIDs is array (for coding simplicity) - if (!is_array($assetIDs)) { - $assetIDs = [ $assetIDs ]; - } - - // check asset type and construct all asset inserts - switch ($type) { - - case 'font': - foreach($assetIDs as $asset) { - $code .= "\n\t<link href='". FONTS_DIR . FONT_FILES[$asset] ."' as='font' type='font/woff2' {$paramatres}/>"; - } - break; - - case 'js': - foreach($assetIDs as $asset) { - $code .= "\n\t<script src='". JS_DIR . JS_LIBRARIES[$asset] ."' id='{$asset}' {$paramatres}></script>"; - } - break; - - case 'css': - default: - foreach($assetIDs as $asset) { - $code .= "\n\t<link rel='stylesheet' href='". CSS_DIR . CSS_FILES[$asset] ."' id='{$asset}' {$paramatres} />"; - } - } - - echo $code; -} - - -/** sanitize_output (recursive) - * --- - * Sanitizes data that are about to rendered - * (usually when render_view() is called). - * NOTE: mitigates XSS attachs - * @param $data: (array) - */ -function sanitize_output($data) { - //// // check https://stackoverflow.com/questions/2002710/php-how-to-perform-htmlspecialchar-on-an-array-of-arrays - - //// $output = array_map("myFunc", $data); - global $secure; - - $output = array(); - foreach($data as $key => $val) { - - if (is_string($val)) { - $output[$key] = htmlspecialchars(remove_invisible_characters($val)); - - } else if (is_array($val)) { - $output[$key] = sanitize_output($val); - - } else { - $output[$key] = $val; - } - } - return $output; -} - - -/** remove_invisible_characters() - * --- - * @used by sanitize_output() - */ -function remove_invisible_characters($str, $url_encoded = TRUE) -{ - $non_displayables = array(); - - // every control character except newline (dec 10), - // carriage return (dec 13) and horizontal tab (dec 09) - if ($url_encoded) { - $non_displayables[] = '/%0[0-8bcef]/i'; // url encoded 00-08, 11, 12, 14, 15 - $non_displayables[] = '/%1[0-9a-f]/i'; // url encoded 16-31 - $non_displayables[] = '/%7f/i'; // url encoded 127 - } - - $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 - - do { - $str = preg_replace($non_displayables, '', $str, -1, $count); - } while ($count); - - return $str; -} - - -/** html - * --- - * outputs code as html - * - * @param $str (string) - * @return html5 (string) - */ -function html($str) { - if (!isset($str) || $str== null) return; - if (($str == '') || is_numeric($str)) return $str; - return htmlspecialchars_decode($str, ENT_QUOTES|ENT_HTML5); -} - - -/** set_headers - * --- - * set custom response-Headers - * - * @param $contentType - * @param $ttl: int or false) - * @param $more: array of (content-type => content-value) pairs - */ -function set_headers($contentType = 'text/html; charset=UTF-8', $ttl = false, $more = [] ) { - - // handle common content-type shorcuts - switch ($contentType) { - case 'text': - case 'html': - $contentType = 'text/html; charset=UTF-8'; - break; - case 'json': - $contentType = 'application/json; charset=utf-8'; - break; - case 'js': - $contentType = 'application/javascript; charset=utf-8'; - break; - case 'css': - $contentType = 'text/css'; - break; - default: - // $contentType stays as-is - break; - } - - // send headers - header('Content-Type: '. $contentType); - if ($ttl) { - $ts = gmdate("D, d M Y H:i:s", time() + $ttl) . " GMT"; - header("Expires: {$ts}"); - header("Pragma: cache"); - header("Cache-Control: max-age={$ttl}"); - - } else { - $ts = gmdate("D, d M Y H:i:s") . " GMT"; - header("Expires: {$ts}"); - header("Last-Modified: {$ts}"); - header("Pragma: no-cache"); - header("Cache-Control: no-cache, must-revalidate"); - } - - // send more headers - if ($more != []) { - foreach($more as $header => $value) { - header($header .': '. $value); - } - } - -} - - - |
