From dde2638f10f13cc1ebc2f53a912a667fcbd7667b Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 24 Jul 2025 14:53:46 -0400 Subject: [PATCH] init 3 --- docs/TODO.md | 22 +- .../{autoLoader.php => auto_loader.php} | 0 src/bootstrap/errors.php | 33 +- src/bootstrap/{loadAll.php => load_all.php} | 2 +- src/bootstrap/main.php | 6 +- src/bootstrap/{saferIO.php => safer_io.php} | 2 +- .../{siteHelper.php => site_helper.php} | 45 +- src/classes/Collection.php | 23 + src/classes/Dollars.php | 29 + src/classes/LazyCollection.php | 95 ++ src/classes/LazyObject.php | 24 + src/classes/Middleware.php | 66 + src/classes/Scalar.php | 13 + src/classes/app.php | 12 +- src/classes/arrays/common_stuff.php | 102 ++ src/classes/arrays/countries.php | 262 ++++ src/classes/arrays/mimes.php | 166 +++ src/classes/arrays/mocking/address.php | 45 + src/classes/arrays/mocking/phone.php | 43 + src/classes/arrays/mocking/rnd_names.php | 1179 +++++++++++++++++ src/classes/arrays/shortn.php | 29 + src/classes/arrays/zipcodes.php | 185 +++ src/classes/assets.php | 262 ++++ src/classes/bb_code_parser.php | 176 +++ .../{consoleApp.php => console_app.php} | 2 +- src/classes/database/dummy_data.php | 100 ++ src/classes/database/model.php | 357 +++++ src/classes/database/paginate.php | 255 ++++ src/classes/extras_booter.php | 30 + src/classes/html.php | 6 +- src/classes/html_document.php | 427 ++++++ src/classes/html_parser.php | 174 +++ ...icenseFiles.php => make_license_files.php} | 0 src/classes/mb_strings_fns.php | 44 + src/classes/misc.php | 6 +- src/classes/page_not_found.php | 2 +- src/classes/random_engine.php | 106 ++ src/classes/router.php | 16 +- src/classes/security.php | 14 + src/classes/services/log.php | 115 ++ .../services/paragon_crypto/crypto.php | 238 ++++ .../paragon_crypto/password_storage.php | 127 ++ .../paragon_crypto/sodium_storage.php | 133 ++ src/classes/string_fns.php | 98 ++ src/classes/tag_matches.php | 63 + src/classes/time_zone_selection.php | 49 + src/classes/time_zones.php | 274 ++++ src/classes/traits/Macroable.php | 60 + .../traits/security/csrf_token_functions.php | 6 +- src/classes/uuidv7.php | 68 + src/classes/view.php | 243 ++++ 51 files changed, 5774 insertions(+), 60 deletions(-) rename src/bootstrap/{autoLoader.php => auto_loader.php} (100%) rename src/bootstrap/{loadAll.php => load_all.php} (98%) rename src/bootstrap/{saferIO.php => safer_io.php} (99%) rename src/bootstrap/{siteHelper.php => site_helper.php} (80%) create mode 100644 src/classes/Collection.php create mode 100644 src/classes/Dollars.php create mode 100644 src/classes/LazyCollection.php create mode 100644 src/classes/LazyObject.php create mode 100644 src/classes/Middleware.php create mode 100644 src/classes/Scalar.php create mode 100644 src/classes/arrays/common_stuff.php create mode 100644 src/classes/arrays/countries.php create mode 100644 src/classes/arrays/mimes.php create mode 100644 src/classes/arrays/mocking/address.php create mode 100644 src/classes/arrays/mocking/phone.php create mode 100644 src/classes/arrays/mocking/rnd_names.php create mode 100644 src/classes/arrays/shortn.php create mode 100644 src/classes/arrays/zipcodes.php create mode 100644 src/classes/assets.php create mode 100644 src/classes/bb_code_parser.php rename src/classes/{consoleApp.php => console_app.php} (96%) create mode 100644 src/classes/database/dummy_data.php create mode 100644 src/classes/database/model.php create mode 100644 src/classes/database/paginate.php create mode 100644 src/classes/extras_booter.php create mode 100644 src/classes/html_document.php create mode 100644 src/classes/html_parser.php rename src/classes/{makeLicenseFiles.php => make_license_files.php} (100%) create mode 100644 src/classes/mb_strings_fns.php create mode 100644 src/classes/random_engine.php create mode 100644 src/classes/services/log.php create mode 100644 src/classes/services/paragon_crypto/crypto.php create mode 100644 src/classes/services/paragon_crypto/password_storage.php create mode 100644 src/classes/services/paragon_crypto/sodium_storage.php create mode 100644 src/classes/string_fns.php create mode 100644 src/classes/tag_matches.php create mode 100644 src/classes/time_zone_selection.php create mode 100644 src/classes/time_zones.php create mode 100644 src/classes/traits/Macroable.php create mode 100644 src/classes/uuidv7.php create mode 100644 src/classes/view.php diff --git a/docs/TODO.md b/docs/TODO.md index f73eca0..9ffdd58 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -11,9 +11,9 @@ [ ] → HTTP → Requests, Responce - [ ] → Middleware + [x] → Middleware - [ ] → Macros + [x] → Macros [ ] → Routes @@ -23,25 +23,25 @@ [ ] → Views (Twig/Liquid/PHP) - [ ] → JavaScript/CSS Asset loading + [x] → JavaScript/CSS Asset loading - [ ] → Strings & Random Engine + [x] → Strings & Random Engine - [ ] → HTML Document + [x] → HTML Document [ ] → Main Project Tempates [o] → 404 Pages/Dev/Prod Errors - [ ] → CLI Detect + [x] → CLI Detect [ ] → Paginator [x] → Safer I/O - [ ] → End/Open Tag Matching + [x] → End/Open Tag Matching - [ ] → UUIDv7 for APIs? + [x] → UUIDv7 for APIs? [ ] → Rate Limiting (Cached in JSON) @@ -49,13 +49,13 @@ [ ] → Twilio Support - [ ] → Logger + [x] → Logger [x] → Error Handler [ ] → CSRF Tokens - [ ] → Password Hashing/Verify + [x] → Password Hashing/Verify [ ] → Sane Config/Service file defaults @@ -64,7 +64,7 @@ [x] → Default Routes, then load Controllers ## Extras: - [ ] → LazyCollections, LazyObjects, Money Class + [x] → LazyCollections, LazyObjects, Money Class [ ] → Tests diff --git a/src/bootstrap/autoLoader.php b/src/bootstrap/auto_loader.php similarity index 100% rename from src/bootstrap/autoLoader.php rename to src/bootstrap/auto_loader.php diff --git a/src/bootstrap/errors.php b/src/bootstrap/errors.php index 382e738..df6f8f0 100644 --- a/src/bootstrap/errors.php +++ b/src/bootstrap/errors.php @@ -2,9 +2,11 @@ namespace CodeHydrater\bootstrap; +define('WORD_WRAP_CHRS', 80); // Letters before Line Wrap on Errors + // Configuration if (! defined('BaseDir')) { - define('LOG_FILE', __DIR__ . '/protected/logs/error_log.txt'); + define('LOG_FILE', false); } else { define('LOG_FILE', BaseDir . '/protected/logs/error_log.txt'); } @@ -25,6 +27,7 @@ function formatMessage($message, $type = 'error') { 'reset' => "\033[0m" // reset ]; $color = $colors[$type] ?? $colors['error']; + $message = wordwrap($message, WORD_WRAP_CHRS, "\n"); return $color . $message . $colors['reset'] . PHP_EOL; } else { // Web HTML formatting @@ -34,6 +37,7 @@ function formatMessage($message, $type = 'error') { 'notice' => 'color:blue;' ]; $style = $styles[$type] ?? $styles['error']; + $message = wordwrap($message, WORD_WRAP_CHRS, "
\n"); return "
$message
"; } } @@ -44,6 +48,9 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) { if (!(error_reporting() & $errno)) { return false; } + if (method_exists("errors", "get_handle_shutdown_errors") && ! errors::get_handle_shutdown_errors()) { + return false; + } $errorTypes = [ E_ERROR => ['ERROR', 'error'], @@ -71,8 +78,9 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) { $displayMessage = "$errorType: $errstr in $errfile on line $errline"; // Log to file - file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); - + if (LOG_FILE !== false) { + file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); + } // Display in development environment if (ENVIRONMENT === 'development') { echo formatMessage($displayMessage, $errorCategory); @@ -84,12 +92,15 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) { // Handle exceptions set_exception_handler(function($e) { - $logMessage = date('[Y-m-d H:i:s]') . " [EXCEPTION] " . $e->getMessage() . - " in " . $e->getFile() . " on line " . $e->getLine() . PHP_EOL; + if (method_exists("errors", "get_handle_exceptions") && ! errors::get_handle_exceptions()) { + return false; + } + $logMessage = date('[Y-m-d H:i:s]') . " [EXCEPTION] " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine() . PHP_EOL; $displayMessage = "UNCAUGHT EXCEPTION: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine(); - file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); - + if (LOG_FILE !== false) { + file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); + } if (ENVIRONMENT === 'development') { echo formatMessage($displayMessage, 'error'); } else { @@ -102,13 +113,17 @@ set_exception_handler(function($e) { // Handle fatal errors register_shutdown_function(function() { + if (method_exists("errors", "get_handle_global_errors") && ! errors::get_handle_global_errors()) { + return false; + } $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { $logMessage = date('[Y-m-d H:i:s]') . " [FATAL] {$error['message']} in {$error['file']} on line {$error['line']}" . PHP_EOL; $displayMessage = "FATAL ERROR: {$error['message']} in {$error['file']} on line {$error['line']}"; - file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); - + if (LOG_FILE !== false) { + file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); + } if (ENVIRONMENT === 'development') { echo formatMessage($displayMessage, 'error'); } diff --git a/src/bootstrap/loadAll.php b/src/bootstrap/load_all.php similarity index 98% rename from src/bootstrap/loadAll.php rename to src/bootstrap/load_all.php index 28611d6..a1c154f 100644 --- a/src/bootstrap/loadAll.php +++ b/src/bootstrap/load_all.php @@ -9,7 +9,7 @@ declare(strict_types=1); */ namespace CodeHydrater\bootstrap; -final class loadAll { +final class load_all { public static function init(string $path): void { $config_path = $path . "configs"; diff --git a/src/bootstrap/main.php b/src/bootstrap/main.php index 2361f43..c9e46c4 100644 --- a/src/bootstrap/main.php +++ b/src/bootstrap/main.php @@ -35,7 +35,7 @@ final class views { } } -if (siteHelper::get_testing() === false) { +if (site_helper::get_testing() === false) { views::ob_start(); } @@ -280,7 +280,7 @@ function is_live() { require_once CodeHydrater_FRAMEWORK . 'bootstrap/common.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/requires.php'; -require_once CodeHydrater_FRAMEWORK . 'bootstrap/autoLoader.php'; +require_once CodeHydrater_FRAMEWORK . 'bootstrap/auto_loader.php'; registry::set('loader', new Psr4AutoloaderClass); registry::get('loader')->register(); @@ -288,7 +288,7 @@ registry::get('loader')->add_namespace("CodeHydrater\bootstrap", CodeHydrater_FR registry::get('loader')->add_namespace("CodeHydrater", CodeHydrater_FRAMEWORK . "classes"); registry::get('loader')->add_namespace("Project", CodeHydrater_PROJECT); -loadAll::init(CodeHydrater_PROJECT); +load_all::init(CodeHydrater_PROJECT); $returned_route = \CodeHydrater\router::execute(); if ($returned_route["found"] === false) { diff --git a/src/bootstrap/saferIO.php b/src/bootstrap/safer_io.php similarity index 99% rename from src/bootstrap/saferIO.php rename to src/bootstrap/safer_io.php index 96ec61e..2097b4e 100644 --- a/src/bootstrap/saferIO.php +++ b/src/bootstrap/safer_io.php @@ -75,7 +75,7 @@ final class use_iol { } } -final class saferIO { +final class safer_io { private static string $string_of_POST_data = ""; private static array $DATA_INPUTS = []; diff --git a/src/bootstrap/siteHelper.php b/src/bootstrap/site_helper.php similarity index 80% rename from src/bootstrap/siteHelper.php rename to src/bootstrap/site_helper.php index fc00b2b..4eb574b 100644 --- a/src/bootstrap/siteHelper.php +++ b/src/bootstrap/site_helper.php @@ -10,7 +10,7 @@ declare(strict_types=1); namespace CodeHydrater\bootstrap; -final class siteHelper { +final class site_helper { private static $ROOT; private static $ROUTE; @@ -40,14 +40,14 @@ final class siteHelper { public static function set_allowed_Private_IPs(string|array $IP_addresses): void { if (is_array($IP_addresses)) { foreach($IP_addresses as $IP) { - $s_ip = \tts\security::get_valid_ip($IP); + $s_ip = \CodeHydrater\security::get_valid_ip($IP); if ($s_ip === false) { continue; } self::$Private_IPs_allowed[] = $IP; } } elseif (is_string($IP_addresses)) { - $s_ip = \tts\security::get_valid_ip($IP); + $s_ip = \CodeHydrater\security::get_valid_ip($IP); if ($s_ip === false) { return; } @@ -58,14 +58,14 @@ final class siteHelper { public static function set_allowed_Public_IPs(string|array $IP_addresses): void { if (is_array($IP_addresses)) { foreach($IP_addresses as $IP) { - $s_ip = \tts\security::get_valid_public_ip($IP); + $s_ip = \CodeHydrater\security::get_valid_public_ip($IP); if ($s_ip === false) { continue; } self::$Public_IPs_allowed[] = $s_ip; } } elseif (is_string($IP_addresses)) { - $s_ip = \tts\security::get_valid_public_ip($IP); + $s_ip = \CodeHydrater\ecurity::get_valid_public_ip($IP); if ($s_ip === false) { return; } @@ -73,6 +73,26 @@ final class siteHelper { } } + public static function is_server_name_a_private_domain(): bool { + $white_list = array_merge(self::$local_site_domains, self::$Private_IPs_allowed); + return (\CodeHydrater\security::is_server_name_on_domain_list($white_list)); + } + + public static function remote_not_allowed_force_live(): bool { + return (! self::is_allowed()); + } + + public static function is_allowed(): bool { + $remote_ip = \CodeHydrater\security::get_client_ip_address(); + if (in_array($remote_ip, self::$Public_IPs_allowed)) { + return true; + } + if (self::is_server_name_a_private_domain() && in_array($remote_ip, self::$Private_IPs_allowed)) { + return true; + } + return false; + } + public static function get_route(): string { return self::$ROUTE; } @@ -85,6 +105,10 @@ final class siteHelper { return self::$TESTING; } + public static function get_path_info(): string { + return strtok(self::$REQUEST_URI, '?'); + } + public static function get_uri(): string { return self::$REQUEST_URI; } @@ -110,14 +134,14 @@ final class siteHelper { if (self::$REQUEST_URI !== null && !empty(self::$REQUEST_URI)) { $uri = self::$REQUEST_URI; } else if (isset($_SERVER['REQUEST_URI'])) { - $uri = saferIO::get_clean_server_var('REQUEST_URI'); + $uri = safer_io::get_clean_server_var('REQUEST_URI'); } else { if (isset($_SERVER['argv'])) { - $uri = saferIO::get_clean_server_var('SCRIPT_NAME') . '?' . $_SERVER['argv'][0]; + $uri = safer_io::get_clean_server_var('SCRIPT_NAME') . '?' . $_SERVER['argv'][0]; } elseif (isset($_SERVER['QUERY_STRING'])) { - $uri = saferIO::get_clean_server_var('SCRIPT_NAME') . '?' . \bs_tts\safer_io::get_clean_server_var('QUERY_STRING'); + $uri = safer_io::get_clean_server_var('SCRIPT_NAME') . '?' . \bs_tts\safer_io::get_clean_server_var('QUERY_STRING'); } else { - $uri = saferIO::get_clean_server_var('SCRIPT_NAME'); + $uri = safer_io::get_clean_server_var('SCRIPT_NAME'); } } // Prevent multiple slashes to avoid cross site requests via the Form API. @@ -184,7 +208,7 @@ final class siteHelper { self::$queryParams = $queryParams; } - public static function restrictSite(): void { + public static function restrict_site(): void { if ($_SERVER['HTTP_REFERER'] != $_SERVER['HTTP_HOST']) { die("Form may not be used outside of parent site!"); } @@ -216,6 +240,7 @@ final class siteHelper { define('ASSETS_BASE_REF', "/assets/"); } define('SITE_URL', self::site_url()); + define('PROJECT_BASE_REF', SITE_URL); define("BROWSER", self::get_clean_server_var('HTTP_USER_AGENT')); define("ASSETS_DIR", "/public/assets/"); } diff --git a/src/classes/Collection.php b/src/classes/Collection.php new file mode 100644 index 0000000..11c3848 --- /dev/null +++ b/src/classes/Collection.php @@ -0,0 +1,23 @@ + $items + */ + public function __construct( + private array $items + ) { + } + + /** + * @return array + */ + public function all(): array { + return $this->items; + } +} diff --git a/src/classes/Dollars.php b/src/classes/Dollars.php new file mode 100644 index 0000000..ea50cf3 --- /dev/null +++ b/src/classes/Dollars.php @@ -0,0 +1,29 @@ +moneyAsFloat($money), 2, '.', ','); + } + + public function intlUSD(Number $money): string|false { + if (class_exists('NumberFormatter')) { + $formatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); + return $formatter->formatCurrency($this->moneyAsFloat($money), 'USD'); + } + return false; + } +} diff --git a/src/classes/LazyCollection.php b/src/classes/LazyCollection.php new file mode 100644 index 0000000..21c71cb --- /dev/null +++ b/src/classes/LazyCollection.php @@ -0,0 +1,95 @@ + + */ +class LazyCollection implements \IteratorAggregate +{ + /** @var \Closure|self|array|null */ + protected $source; + + /** + * @param \Closure(): iterable|self|iterable|null $source + */ + public function __construct($source = null) + { + if ($source instanceof \Closure || $source instanceof self) { + $this->source = $source; + } elseif (is_null($source)) { + $this->source = static::empty(); + } else { + $this->source = $this->getArrayableItems($source); + } + } + + /** + * @return \Traversable + */ + public function getIterator(): \Traversable + { + return $this->makeIterator($this->source); + } + + /** + * @param \Closure(): iterable|self|array $source + * @return \Traversable + */ + protected function makeIterator($source) + { + if ($source instanceof \Closure) { + $source = $source(); + } + + if (is_array($source)) { + return new \ArrayIterator($source); + } + + return $source; + } + + /** + * @template TNewKey + * @template TNewValue + * @param \Closure(): iterable|iterable|null $source + * @return self + */ + public static function make($source = null) + { + return new static($source instanceof \Closure ? $source() : $source); + } + + /** + * @param callable(TValue, TKey): bool $callback + * @return self + */ + public function each(callable $callback): LazyCollection + { + foreach ($this->getIterator() as $key => $value) { + if ($callback($value, $key) === false) { + break; + } + } + + return $this; + } + + /** + * @param mixed $items + * @return array + */ + protected function getArrayableItems($items): array + { + if (is_array($items)) { + return $items; + } elseif ($items instanceof \Traversable) { + return iterator_to_array($items); + } elseif (is_object($items) && method_exists($items, 'toArray')) { + return $items->toArray(); + } + + return (array) $items; + } +} diff --git a/src/classes/LazyObject.php b/src/classes/LazyObject.php new file mode 100644 index 0000000..ae1615e --- /dev/null +++ b/src/classes/LazyObject.php @@ -0,0 +1,24 @@ +className; + if (!class_exists($className, true)) { + throw new \InvalidArgumentException("Class {$className} does not exist"); + } + $reflector = new \ReflectionClass($className); + if (!$reflector->isInstantiable()) { + throw new \InvalidArgumentException("Class {$className} cannot be instantiated"); + } + + $initializer = static function () use ($className, $args) { + return new $className(...$args); + }; + + return $reflector->newLazyProxy($initializer); + } +} diff --git a/src/classes/Middleware.php b/src/classes/Middleware.php new file mode 100644 index 0000000..a168c82 --- /dev/null +++ b/src/classes/Middleware.php @@ -0,0 +1,66 @@ +request = $request; + return $this; + } + + public function through(array $middleware): Object + { + $this->middleware = $middleware; + return $this; + } + + public function then(\Closure $destination): array + { + $pipeline = array_reduce( + array_reverse($this->parseMiddleware($this->middleware)), + $this->carry(), + function ($request) use ($destination) { + return $destination($request); + } + ); + + return $pipeline($this->request); + } + + protected function parseMiddleware(array $middleware): array + { + return array_map(function ($m) { + return is_string($m) ? explode(':', $m, 2) : $m; + }, $middleware); + } + + protected function carry(): mixed + { + return function ($stack, $middleware) { + return function ($request) use ($stack, $middleware) { + if (is_array($middleware)) { + [$middleware, $parameters] = [ + $middleware[0], + isset($middleware[1]) ? explode(',', $middleware[1]) : [] + ]; + } + + if (is_string($middleware)) { + $middleware = new $middleware; + } + + return $middleware->handle($request, $stack, ...$parameters ?? []); + }; + }; + } +} diff --git a/src/classes/Scalar.php b/src/classes/Scalar.php new file mode 100644 index 0000000..fc34288 --- /dev/null +++ b/src/classes/Scalar.php @@ -0,0 +1,13 @@ +get_last_part($no_hmtl); - $params = bootstrap\siteHelper::get_params(); + $params = bootstrap\site_helper::get_params(); // Now load Route $is_from_the_controller = true; // TRUE for from Constructor... @@ -59,8 +59,8 @@ class app { } private function get_ctrl_dir(): string { - $ctrl = (consoleApp::is_cli()) ? "cli_" : ""; - return (bootstrap\siteHelper::get_testing()) ? "test_" : $ctrl; + $ctrl = (console_app::is_cli()) ? "cli_" : ""; + return (bootstrap\site_helper::get_testing()) ? "test_" : $ctrl; } /** @@ -71,7 +71,7 @@ class app { * @retval action */ public function router(?string $route, ?string $method, $params, bool $is_controller = false) { - $ROOT = bootstrap\siteHelper::get_root(); + $ROOT = bootstrap\site_helper::get_root(); $file = ""; $class = ""; diff --git a/src/classes/arrays/common_stuff.php b/src/classes/arrays/common_stuff.php new file mode 100644 index 0000000..21d149e --- /dev/null +++ b/src/classes/arrays/common_stuff.php @@ -0,0 +1,102 @@ + 'January', + '2' => 'February', + '3' => 'March', + '4' => 'April', + '5' => 'May', + '6' => 'June', + '7' => 'July', + '8' => 'August', + '9' => 'September', + '10' => 'October', + '11' => 'November', + '12' => 'December' + ); + } + + /** + * Fetch States in USA + * @return array of states + */ + public static function states_array(): array { + return array( + 'AL' => 'Alabama', + 'AK' => 'Alaska', + 'AZ' => 'Arizona', + 'AR' => 'Arkansas', + 'CA' => 'California', + 'CO' => 'Colorado', + 'CT' => 'Connecticut', + 'DE' => 'Delaware', + 'DC' => 'District of Columbia', + 'FL' => 'Florida', + 'GA' => 'Georgia', + 'HI' => 'Hawaii', + 'ID' => 'Idaho', + 'IL' => 'Illinois', + 'IN' => 'Indiana', + 'IA' => 'Iowa', + 'KS' => 'Kansas', + 'KY' => 'Kentucky', + 'LA' => 'Louisiana', + 'ME' => 'Maine', + 'MD' => 'Maryland', + 'MA' => 'Massachusetts', + 'MI' => 'Michigan', + 'MN' => 'Minnesota', + 'MS' => 'Mississippi', + 'MO' => 'Missouri', + 'MT' => 'Montana', + 'NE' => 'Nebraska', + 'NV' => 'Nevada', + 'NH' => 'New Hampshire', + 'NJ' => 'New Jersey', + 'NM' => 'New Mexico', + 'NY' => 'New York', + 'NC' => 'North Carolina', + 'ND' => 'North Dakota', + 'OH' => 'Ohio', + 'OK' => 'Oklahoma', + 'OR' => 'Oregon', + 'PA' => 'Pennsylvania', + 'RI' => 'Rhode Island', + 'SC' => 'South Carolina', + 'SD' => 'South Dakota', + 'TN' => 'Tennessee', + 'TX' => 'Texas', + 'UT' => 'Utah', + 'VT' => 'Vermont', + 'VA' => 'Virginia', + 'WA' => 'Washington', + 'WV' => 'West Virginia', + 'WI' => 'Wisconsin', + 'WY' => 'Wyoming', + ); + } + +} \ No newline at end of file diff --git a/src/classes/arrays/countries.php b/src/classes/arrays/countries.php new file mode 100644 index 0000000..ac7ad19 --- /dev/null +++ b/src/classes/arrays/countries.php @@ -0,0 +1,262 @@ + 'Aland Islands', + 'AF' => 'Afghanistan', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AQ' => 'Antarctica', + 'AG' => 'Antigua And Barbuda', + 'AR' => 'Argentina', + 'AM' => 'Armenia', + 'AW' => 'Aruba', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BA' => 'Bosnia And Herzegovina', + 'BW' => 'Botswana', + 'BV' => 'Bouvet Island', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory', + 'BN' => 'Brunei', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'AR' => 'Burma', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'CV' => 'Cape Verde', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CC' => 'Cocos (Keeling) Islands', + 'CO' => 'Columbia', + 'KM' => 'Comoros', + 'CG' => 'Congo', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'CI' => 'Cote D\'Ivorie (Ivory Coast)', + 'HR' => 'Croatia (Hrvatska)', + 'CU' => 'Cuba', + 'CY' => 'Cyprus', + 'CZ' => 'Czech Republic static', + 'CD' => 'Democratic Republic Of Congo (Zaire)', + 'DK' => 'Denmark', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'TP' => 'East Timor', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GB' => 'England', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'EU' => 'European Union', + 'FK' => 'Falkland Islands (Malvinas)', + 'FO' => 'Faroe Islands', + 'FJ' => 'Fiji', + 'FI' => 'Finland', + 'FR' => 'France', + 'FX' => 'France, Metropolitan', + 'GF' => 'French Guinea', + 'PF' => 'French Polynesia', + 'TF' => 'French Southern Territories', + 'GA' => 'Gabon', + 'GM' => 'Gambia', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HM' => 'Heard And McDonald Islands', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyzstan', + 'LA' => 'Laos', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macau', + 'MK' => 'Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia', + 'MD' => 'Moldova', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar (Burma)', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'NL' => 'Netherlands', + 'AN' => 'Netherlands Antilles', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'KP' => 'North Korea', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestine', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PN' => 'Pitcairn', + 'PL' => 'Poland', + 'PT' => 'Portugal', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'RE' => 'Reunion', + 'RO' => 'Romania', + 'RU' => 'Russia', + 'RW' => 'Rwanda', + 'SH' => 'Saint Helena', + 'KN' => 'Saint Kitts And Nevis', + 'LC' => 'Saint Lucia', + 'PM' => 'Saint Pierre And Miquelon', + 'VC' => 'Saint Vincent And The Grenadines', + 'SM' => 'San Marino', + 'ST' => 'Sao Tome And Principe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SK' => 'Slovakia', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia', + 'ZA' => 'South Africa', + 'GS' => 'South Georgia And South Sandwich Islands', + 'KR' => 'South Korea', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard And Jan Mayen', + 'SZ' => 'Swaziland', + 'SE' => 'Sweden', + 'CH' => 'Switzerland', + 'SY' => 'Syria', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania', + 'TH' => 'Thailand', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad And Tobago', + 'TN' => 'Tunisia', + 'TR' => 'Turkey', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks And Caicos Islands', + 'TV' => 'Tuvalu', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'UK' => 'United Kingdom', + 'US' => 'United States', + 'UM' => 'United States Minor Outlying Islands', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekistan', + 'VU' => 'Vanuatu', + 'VA' => 'Vatican City', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'VG' => 'Virgin Islands (British)', + 'VI' => 'Virgin Islands (US)', + 'WF' => 'Wallis And Futuna Islands', + 'EH' => 'Western Sahara', + 'WS' => 'Western Samoa', + 'YE' => 'Yemen', + 'YU' => 'Yugoslavia', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe' + ); + } +} \ No newline at end of file diff --git a/src/classes/arrays/mimes.php b/src/classes/arrays/mimes.php new file mode 100644 index 0000000..d7a7fa4 --- /dev/null +++ b/src/classes/arrays/mimes.php @@ -0,0 +1,166 @@ + [ + 'txt' => 'text/plain', + ], + 'web' => [ + 'htm' => 'text/html', + 'html' => 'text/html', + 'css' => 'text/css', + 'json' => 'application/json', + 'xml' => 'application/xml' + ], + 'images' => [ + 'png' => 'image/png', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'ico' => 'image/vnd.microsoft.icon', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'webp' => 'image/webp' + ], + 'archives' => [ + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'cab' => 'application/vnd.ms-cab-compressed', + 'gz' => 'application/gzip', + 'tgz' => 'application/x-compressed', + 'tar' => 'application/x-tar', + 'iso' => 'application/x-iso9660-image', + 'bz2' => 'application/x-bzip2', + 'lz' => 'application/x-lzip', + 'xz' => 'application/x-xz', + '7z' => 'application/x-7z-compressed', + ], + 'audio' => [ + 'oga' => 'audio/ogg', 'ogg' => 'audio/ogg', 'opus' => 'audio/ogg', 'spx' => 'audio/ogg', + 'mp3' => 'audio/mpeg', + 'wav' => 'audio/x-wav' + ], + 'video' => [ + 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mpe' => 'video/mpeg', + 'mp4' => 'video/mp4', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'ogv' => 'video/ogg' + ], + 'adobe' => [ + 'pdf' => 'application/pdf', + 'psd' => 'image/vnd.adobe.photoshop', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript' + ], + 'office' => [ + 'doc' => 'application/msword', + 'rtf' => 'application/rtf', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet' + ] + ]; + } + + public static function mimes_dangerious(): array { + return [ + // Programming + 'js' => 'application/javascript', + 'php' => 'application/x-php-code', + 'py' => 'application/x-python-code', + 'pyc' => 'application/x-python-code', + 'pyo' => 'application/x-python-code', + 'torrent' => 'application/x-bittorrent', + // Linux + 'deb' => 'application/vnd.debian.binary-package', + 'rpm' => 'application/x-redhat-package-manager', + 'bash' => 'application/x-shellscript', + 'sh' => 'application/x-sh', + 'jar' => 'application/java', + 'java' => 'application/java', + // Windows + 'swf' => 'application/x-shockwave-flash', 'swfl' => 'application/x-shockwave-flash', + 'reg' => 'application/registry', + 'lnk' => 'application/shortcut', + 'inf' => 'application/config', + 'scf' => 'application/explorer', + // A Monad script file. Monad was later renamed PowerShell. + 'msh' => 'shell', 'msh1' => 'shell', 'msh2' => 'shell', 'mshxml' => 'shell', 'msh1xml' => 'shell', 'msh2xml' => 'shell', + // Power Shell + 'ps1' => 'shell', 'ps1xml' => 'shell', 'ps2' => 'shell', 'ps2xml' => 'shell', 'psc1' => 'shell', 'psc2' => 'shell', + 'wsc' => 'application/scripting-component', 'wsh' => 'application/scripting-host', 'ws' => 'application/scripting', 'wsf' => 'application/scripting', + 'jse' => 'application/javascript-encrypted', + 'vbe' => 'application/vbscript-encrypted', + 'vb' => 'application/visualbasic', + 'vbs' => 'application/visualbasic', + 'bat' => 'application/batch', + 'cmd' => 'application/batch', + 'exe' => 'application/x-msdownload', + 'pif' => 'application/executiable', + 'application' => 'application/oneclick', + 'gadget' => 'vista/desktop', + 'msi' => 'application/x-msdownload', + 'msp' => 'application/patch', + 'com' => 'application/dos', + 'scr' => 'application/screen-saver', + 'hta' => 'html/application', + 'cpl' => 'application/control-panel', + 'msc' => 'application/m-console', + // Office 2007 Macros + 'docm' => 'macro/msword', + 'dotm' => 'macro/template', + 'xlsm' => 'macro/excel', + 'xltm' => 'macro', 'xlam' => 'macro', 'pptm' => 'macro', 'potm' => 'macro', 'ppam' => 'macro', 'ppsm' => 'macro', 'sldm' => 'marco' + ]; + } + + public static function get_mime_type(string $filename, array $allowed = ['all']) { + $ext = strtolower(array_pop(explode('.', $filename))); + $bad = self::mimes_dangerious(); + + if (array_key_exists($ext, $bad)) { + return false; + } + + $mime_types = self::mimes_array(); + + $everyone = in_array('all', $allowed); + + foreach ($mime_types as $key => $mime) { + if (($everyone || in_array($key, $allowed) ) && array_key_exists($ext, $mime)) { + return $mime[$ext]; + } + } + + if ($everyone) { + if (function_exists('finfo_open')) { + $finfo = finfo_open(FILEINFO_MIME); + $mimetype = finfo_file($finfo, $filename); + finfo_close($finfo); + return $mimetype; + } else { + return 'application/octet-stream'; + } + } + + return false; + } + +} diff --git a/src/classes/arrays/mocking/address.php b/src/classes/arrays/mocking/address.php new file mode 100644 index 0000000..ae15e8a --- /dev/null +++ b/src/classes/arrays/mocking/address.php @@ -0,0 +1,45 @@ + 'Ave', + 'BOULEVARD' => 'Blvd', + 'DRIVE' => 'Dr', + 'LANE' => 'Ln', + 'MOUNT' => 'Mt', + 'ROAD' => 'Rd', + 'STREET' => 'St', + ' NORTH ' => ' N. ', + ' SOUTH ' => ' S. ', + ' EAST ' => ' E. ', + ' WEST ' => ' W. ' + ]; + foreach ($usps_abbreviations as $key => $value) { + $text = str_ireplace($key, $value, $text); + } + return $text; + } + +} \ No newline at end of file diff --git a/src/classes/arrays/zipcodes.php b/src/classes/arrays/zipcodes.php new file mode 100644 index 0000000..8d702f9 --- /dev/null +++ b/src/classes/arrays/zipcodes.php @@ -0,0 +1,185 @@ += 35000 && $zipcode <= 36999) { + $st = 'AL'; + $state = 'Alabama'; + } else if ($zipcode >= 99500 && $zipcode <= 99999) { + $st = 'AK'; + $state = 'Alaska'; + } else if ($zipcode >= 85000 && $zipcode <= 86999) { + $st = 'AZ'; + $state = 'Arizona'; + } else if ($zipcode >= 71600 && $zipcode <= 72999) { + $st = 'AR'; + $state = 'Arkansas'; + } else if ($zipcode >= 90000 && $zipcode <= 96699) { + $st = 'CA'; + $state = 'California'; + } else if ($zipcode >= 80000 && $zipcode <= 81999) { + $st = 'CO'; + $state = 'Colorado'; + } else if (($zipcode >= 6000 && $zipcode <= 6389) || ($zipcode >= 6391 && $zipcode <= 6999)) { + $st = 'CT'; + $state = 'Connecticut'; + } else if ($zipcode >= 19700 && $zipcode <= 19999) { + $st = 'DE'; + $state = 'Delaware'; + } else if ($zipcode >= 32000 && $zipcode <= 34999) { + $st = 'FL'; + $state = 'Florida'; + } else if (($zipcode >= 30000 && $zipcode <= 31999) || ($zipcode >= 39800 && $zipcode <= 39999)) { + $st = 'GA'; + $state = 'Georgia'; + } else if ($zipcode >= 96700 && $zipcode <= 96999) { + $st = 'HI'; + $state = 'Hawaii'; + } else if ($zipcode >= 83200 && $zipcode <= 83999) { + $st = 'ID'; + $state = 'Idaho'; + } else if ($zipcode >= 60000 && $zipcode <= 62999) { + $st = 'IL'; + $state = 'Illinois'; + } else if ($zipcode >= 46000 && $zipcode <= 47999) { + $st = 'IN'; + $state = 'Indiana'; + } else if ($zipcode >= 50000 && $zipcode <= 52999) { + $st = 'IA'; + $state = 'Iowa'; + } else if ($zipcode >= 66000 && $zipcode <= 67999) { + $st = 'KS'; + $state = 'Kansas'; + } else if ($zipcode >= 40000 && $zipcode <= 42999) { + $st = 'KY'; + $state = 'Kentucky'; + } else if ($zipcode >= 70000 && $zipcode <= 71599) { + $st = 'LA'; + $state = 'Louisiana'; + } else if ($zipcode >= 3900 && $zipcode <= 4999) { + $st = 'ME'; + $state = 'Maine'; + } else if ($zipcode >= 20600 && $zipcode <= 21999) { + $st = 'MD'; + $state = 'Maryland'; + } else if (($zipcode >= 1000 && $zipcode <= 2799) || ($zipcode == 5501) || ($zipcode == 5544 )) { + $st = 'MA'; + $state = 'Massachusetts'; + } else if ($zipcode >= 48000 && $zipcode <= 49999) { + $st = 'MI'; + $state = 'Michigan'; + } else if ($zipcode >= 55000 && $zipcode <= 56899) { + $st = 'MN'; + $state = 'Minnesota'; + } else if ($zipcode >= 38600 && $zipcode <= 39999) { + $st = 'MS'; + $state = 'Mississippi'; + } else if ($zipcode >= 63000 && $zipcode <= 65999) { + $st = 'MO'; + $state = 'Missouri'; + } else if ($zipcode >= 59000 && $zipcode <= 59999) { + $st = 'MT'; + $state = 'Montana'; + } else if ($zipcode >= 27000 && $zipcode <= 28999) { + $st = 'NC'; + $state = 'North Carolina'; + } else if ($zipcode >= 58000 && $zipcode <= 58999) { + $st = 'ND'; + $state = 'North Dakota'; + } else if ($zipcode >= 68000 && $zipcode <= 69999) { + $st = 'NE'; + $state = 'Nebraska'; + } else if ($zipcode >= 88900 && $zipcode <= 89999) { + $st = 'NV'; + $state = 'Nevada'; + } else if ($zipcode >= 3000 && $zipcode <= 3899) { + $st = 'NH'; + $state = 'New Hampshire'; + } else if ($zipcode >= 7000 && $zipcode <= 8999) { + $st = 'NJ'; + $state = 'New Jersey'; + } else if ($zipcode >= 87000 && $zipcode <= 88499) { + $st = 'NM'; + $state = 'New Mexico'; + } else if (($zipcode >= 10000 && $zipcode <= 14999) || ($zipcode == 6390) || ($zipcode == 501) || ($zipcode == 544)) { + $st = 'NY'; + $state = 'New York'; + } else if ($zipcode >= 43000 && $zipcode <= 45999) { + $st = 'OH'; + $state = 'Ohio'; + } else if (($zipcode >= 73000 && $zipcode <= 73199) || ($zipcode >= 73400 && $zipcode <= 74999)) { + $st = 'OK'; + $state = 'Oklahoma'; + } else if ($zipcode >= 97000 && $zipcode <= 97999) { + $st = 'OR'; + $state = 'Oregon'; + } else if ($zipcode >= 15000 && $zipcode <= 19699) { + $st = 'PA'; + $state = 'Pennsylvania'; + } else if ($zipcode >= 300 && $zipcode <= 999) { + $st = 'PR'; + $state = 'Puerto Rico'; + } else if ($zipcode >= 2800 && $zipcode <= 2999) { + $st = 'RI'; + $state = 'Rhode Island'; + } else if ($zipcode >= 29000 && $zipcode <= 29999) { + $st = 'SC'; + $state = 'South Carolina'; + } else if ($zipcode >= 57000 && $zipcode <= 57999) { + $st = 'SD'; + $state = 'South Dakota'; + } else if ($zipcode >= 37000 && $zipcode <= 38599) { + $st = 'TN'; + $state = 'Tennessee'; + } else if (($zipcode >= 75000 && $zipcode <= 79999) || ($zipcode >= 73301 && $zipcode <= 73399) || ($zipcode >= 88500 && $zipcode <= 88599)) { + $st = 'TX'; + $state = 'Texas'; + } else if ($zipcode >= 84000 && $zipcode <= 84999) { + $st = 'UT'; + $state = 'Utah'; + } else if ($zipcode >= 5000 && $zipcode <= 5999) { + $st = 'VT'; + $state = 'Vermont'; + } else if (($zipcode >= 20100 && $zipcode <= 20199) || ($zipcode >= 22000 && $zipcode <= 24699) || ($zipcode == 20598)) { + $st = 'VA'; + $state = 'Virgina'; + } else if (($zipcode >= 20000 && $zipcode <= 20099) || ($zipcode >= 20200 && $zipcode <= 20599) || ($zipcode >= 56900 && $zipcode <= 56999)) { + $st = 'DC'; + $state = 'Washington DC'; + } else if ($zipcode >= 98000 && $zipcode <= 99499) { + $st = 'WA'; + $state = 'Washington'; + } else if ($zipcode >= 24700 && $zipcode <= 26999) { + $st = 'WV'; + $state = 'West Virginia'; + } else if ($zipcode >= 53000 && $zipcode <= 54999) { + $st = 'WI'; + $state = 'Wisconsin'; + } else if ($zipcode >= 82000 && $zipcode <= 83199) { + $st = 'WY'; + $state = 'Wyoming'; + } else { + $st = 'none'; + $state = 'none'; + return 'No state found matching' . $zipcode; + } + + return ['abbr' => $st, 'state' => $state]; + } + +} \ No newline at end of file diff --git a/src/classes/assets.php b/src/classes/assets.php new file mode 100644 index 0000000..5573370 --- /dev/null +++ b/src/classes/assets.php @@ -0,0 +1,262 @@ +$a_media) { + $ret .= self::wrap_css($file, $scope, $a_media); + } + return $ret; + } + + public static function loadall_js(array $files, string $scope='project'): string { + $ret = ''; + foreach($files as $file=>$a) { + $ret .= self::wrap_js($file, $scope, $a); + } + return $ret; + } + + public static function image(string $file, string $scope = '', array $a = array()): string { + $more = ''; + if (count($a)) { + foreach ($a as $k => $v) { + $more .= " {$k}=\"{$v}\""; + } + } + $file = self::wrap_asset($file, $scope); + if ($file === false) { + return ''; + } + return "\r\n"; + } + + public static function alert($msg) { + return self::inline_js('alert("'.$msg.'");'); + } + + /** + * Wrapper for JS/CSS assets for the page. + * @param string $file or CDN + * @param string $scope (project/framework/cdn) + * @retval boolean|string false is not found, else Asset REF + */ + public static function wrap_asset(string $file, string $scope = 'project') { + $scope = bootstrap\common::string_to_lowercase($scope); + if ($scope === 'cdn') { + $proto = bootstrap\safer_io::get_clean_server_var('SERVER_PROTOCOL'); + if ($proto === null) { + $protocol = '://'; + } else { + $protocol = strtolower(substr($proto, 0, strpos($proto, '/'))) . '://'; + } + return (bootstrap\common::is_string_found($file, '://') === true) ? $file : $protocol . $file; + } + if ($scope === 'project' || $scope === 'app') { + $path = PROJECT_ASSETS_DIR . "/"; + $tts = self::is_minified($path, $file); + return ($tts !== false) ? PROJECT_ASSETS_BASE_REF . "/" . $tts : false; + } + if ($scope === 'assets') { + $path = ASSETS_DIR . "/"; + $tts = self::is_minified($path, $file); + return ($tts !== false) ? ASSETS_BASE_REF . "/" . $tts : false; + } + return $file; + } + /** + * Fetch Version of file if exists + * @param string $file + * @param string $scope + * @return string|bool + */ + private static function project_paths(string $file, string $scope = 'project'): string | bool { + $scope = bootstrap\common::string_to_lowercase($scope); + if ($scope === 'cdn') { + return ""; + } else if ($scope === 'project' || $scope === 'app') { + $path = PROJECT_ASSETS_DIR . "/"; + } else if ($scope === 'assets') { + $path = ASSETS_DIR . "/"; + } else { + return ""; + } + $check = self::is_minified($path, $file); + return ($check !==false) ? self::get_ver($path . $check) : false; + } + + /** + * Wrapper to return the CSS href=file.. stylesheet code for use in page. + * @param string $file - CSS file + * @param string $media - default of all media + * @retval string + */ + public static function wrap_css(string $file, string $scope = 'project', array $a_media = array()): string { + $more = ''; + if (count($a_media)) { + foreach ($a_media as $k => $v) { + $more .= " {$k}=\"{$v}\""; + } + } else { + $more .= " media=\"all\""; + } + + $ver = self::project_paths($file, $scope); + $wrap_file = self::wrap_asset($file, $scope); + if ($ver === false || $wrap_file === false) { + return ""; + } + return "\r\n"; + } + + /** + * Wrapper to return the JavaScript src=file... code for use with page. + * @param type $file - external JS file. + * @retval string of script src=file + */ + public static function wrap_js(string $file, string $scope = 'project', array $a = array(), $type = "module"): string { + $more = ''; + if (count($a)) { + foreach ($a as $k => $v) { + $more .= (isset($k) && $k !=0 ) ? " {$k}=\"{$v}\"" : " {$v}"; + } + } + + $ver = self::project_paths($file, $scope); + $wrap_file = self::wrap_asset($file, $scope); + if ($ver === false || $wrap_file === false) { + return ""; + } + return "\r\n"; + //return ""; + } + + /** + * Purpose: To do inline JavaScript. + * @param type $code string of code to inline into page. + * @retval type + */ + public static function inline_js(string $code): string { + return "\r\n"; + } + + /** + * Purpose: To execute this JavaScript code once JQuery is Ready(). + * @param string $code to do once JQuery is Ready + * @retval string wrapped in ready code... + */ + public static function jquery_load(string $code): string { + return "\r\n$(function() { \r\n \t {$code} \r\n }); \r\n"; + } + + public static function is_minified(string $path, string $file) { + if (self::attempts_root_dir($path)) { + return false; + } + $safe_path = security::filter_uri($path); + $safe_file = security::filter_uri($file); + + if (bootstrap\common::is_string_found($safe_file, '.auto.js')) { + $production = (bootstrap\is_live()); + $safe_file = str_replace('.auto.js', '', $safe_file); + if ( $production && file_exists($safe_path . $safe_file . '.min.js') ) { + return $safe_file . '.min.js'; + } + return (file_exists($safe_path . $safe_file . '.js')) ? $safe_file . '.js' : false; + } + + if (bootstrap\common::is_string_found($safe_file, '.auto.css')) { + $production = (bootstrap\is_live()); + $safe_file = str_replace('.auto.css', '', $safe_file); + if ( $production && file_exists($safe_path . $safe_file . '.min.css') ) { + return $safe_file . '.min.css'; + } + return (file_exists($safe_path . $safe_file . '.css')) ? $safe_file . '.css' : false; + } + return ( file_exists($safe_path . $safe_file) ) ? $safe_file : false; + } + + /** + * meta redirect when headers are already sent... + * @param string url - site to do redirect on + * @reval none + */ + public static function goto_url(string $url): void { + echo ''; + exit; + } + + /** + * Rediect to url and attempt to send via header. + * @param string $url - site to do redirect for + * @retval none - exits once done + */ + public static function redirect_url(string $url): void { + $url = str_replace(array('&', "\n", "\r"), array('&', '', ''), $url); + if (!headers_sent()) { + header('Location: ' . $url); + } else { + self::goto_url($url); + } + exit; + } + +} diff --git a/src/classes/bb_code_parser.php b/src/classes/bb_code_parser.php new file mode 100644 index 0000000..198ce15 --- /dev/null +++ b/src/classes/bb_code_parser.php @@ -0,0 +1,176 @@ + [ + 'pattern' => '/\[h1\](.*?)\[\/h1\]/s', + 'replace' => '

$1

', + 'content' => '$1' + ], + 'h2' => [ + 'pattern' => '/\[h2\](.*?)\[\/h2\]/s', + 'replace' => '

$1

', + 'content' => '$1' + ], + 'h3' => [ + 'pattern' => '/\[h3\](.*?)\[\/h3\]/s', + 'replace' => '

$1

', + 'content' => '$1' + ], + 'h4' => [ + 'pattern' => '/\[h4\](.*?)\[\/h4\]/s', + 'replace' => '

$1

', + 'content' => '$1' + ], + 'h5' => [ + 'pattern' => '/\[h5\](.*?)\[\/h5\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'h6' => [ + 'pattern' => '/\[h6\](.*?)\[\/h6\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'bold' => [ + 'pattern' => '/\[b\](.*?)\[\/b\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'italic' => [ + 'pattern' => '/\[i\](.*?)\[\/i\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'underline' => [ + 'pattern' => '/\[u\](.*?)\[\/u\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'strikethrough' => [ + 'pattern' => '/\[s\](.*?)\[\/s\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'quote' => [ + 'pattern' => '/\[quote\](.*?)\[\/quote\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'link' => [ + 'pattern' => '/\[url\](.*?)\[\/url\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'namedlink' => [ + 'pattern' => '/\[url\=(.*?)\](.*?)\[\/url\]/s', + 'replace' => '$2', + 'content' => '$2' + ], + 'image' => [ + 'pattern' => '/\[img\](.*?)\[\/img\]/s', + 'replace' => '', + 'content' => '$1' + ], + 'orderedlistnumerical' => [ + 'pattern' => '/\[list=1\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'orderedlistalpha' => [ + 'pattern' => '/\[list=a\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'unorderedlist' => [ + 'pattern' => '/\[list\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'listitem' => [ + 'pattern' => '/\[\*\](.*)/', + 'replace' => '
  • $1
  • ', + 'content' => '$1' + ], + 'code' => [ + 'pattern' => '/\[code\](.*?)\[\/code\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'youtube' => [ + 'pattern' => '/\[youtube\](.*?)\[\/youtube\]/s', + 'replace' => '', + 'content' => '$1' + ], + 'sub' => [ + 'pattern' => '/\[sub\](.*?)\[\/sub\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'sup' => [ + 'pattern' => '/\[sup\](.*?)\[\/sup\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'small' => [ + 'pattern' => '/\[small\](.*?)\[\/small\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'table' => [ + 'pattern' => '/\[table\](.*?)\[\/table\]/s', + 'replace' => '$1
    ', + 'content' => '$1', + ], + 'table-row' => [ + 'pattern' => '/\[tr\](.*?)\[\/tr\]/s', + 'replace' => '$1', + 'content' => '$1', + ], + 'table-data' => [ + 'pattern' => '/\[td\](.*?)\[\/td\]/s', + 'replace' => '$1', + 'content' => '$1', + ], + ]; + + private function searchAndReplace(string $pattern, string $replace, string $source): string { + while (preg_match($pattern, $source)) { + $source = preg_replace($pattern, $replace, $source); + } + + return $source; + } + + public function stripTags(string $source): string { + foreach ($this->parsers as $name => $parser) { + $source = $this->searchAndReplace($parser['pattern'] . 'i', $parser['content'], $source); + } + + return $source; + } + + public function parse(string $source, $caseInsensitive = null): string { + $caseInsensitive = $caseInsensitive === self::CASE_INSENSITIVE ? true : false; + + foreach ($this->parsers as $name => $parser) { + $pattern = ($caseInsensitive) ? $parser['pattern'] . 'i' : $parser['pattern']; + + $source = $this->searchAndReplace($pattern, $parser['replace'], $source); + } + + return $source; + } +} diff --git a/src/classes/consoleApp.php b/src/classes/console_app.php similarity index 96% rename from src/classes/consoleApp.php rename to src/classes/console_app.php index 523a0ec..4145e1b 100644 --- a/src/classes/consoleApp.php +++ b/src/classes/console_app.php @@ -10,7 +10,7 @@ declare(strict_types=1); namespace CodeHydrater; -final class consoleApp { +final class console_app { public static $is_cli = false; public static function is_cli() { diff --git a/src/classes/database/dummy_data.php b/src/classes/database/dummy_data.php new file mode 100644 index 0000000..e275bdb --- /dev/null +++ b/src/classes/database/dummy_data.php @@ -0,0 +1,100 @@ +pdo = $pdo; + $this->random_engine = new \CodeHydrater\random_engine(); + } + + /* + * Helper for add_dummy_data and get_dummy_data. + * Purpose: To return ONE ROW of junk/dummy data from input data. + * @param array $data + * @retval array of random dummy data AKA one row worth of it. + */ + + private function use_dummy_data(array $data): array { + $ret = []; + foreach ($data as $field => $array_values) { + $array_count = \CodeHydrater\common::get_count($array_values); + if ($array_count) { + $ret[$field] = $array_values[$this->random_engine->get_int(0, $array_count - 1)]; + } + } + return $ret; + } + + /** + * Inserts Dummy Data to DB + * @param int $num_rows to add/make + * @param array $data sample data EX: array('fname'=>array('bob','kim','lisa', ect...), ...) + * @retval bool true + */ + public function add_dummy_data(string $table, int $num_rows, array $data): bool { + for ($i = 0; $i < $num_rows; $i++) { + $bind_data = $this->use_dummy_data($data); + $fields = []; + foreach ($data as $field => $array_values) { + $fields[] = $field; + } + + $sql = "INSERT INTO `{$table}` " + . "(" . implode(", ", $fields) . ") " + . "VALUES (:" . implode(", :", $fields) . ");"; + + $bind = []; + foreach ($fields as $field) { + $bind[":$field"] = $bind_data[$field]; + } + unset($bind_data); + unset($fields); + + $pdo_stmt = $this->pdo->prepare($sql); + $exec = $pdo_stmt->execute($bind); + unset($bind); + } + return true; + } + + /** + * Make an array of fields for a fake row of dummy data out of input data. + * @param int $num_rows to make + * @param array $data sample data + * @retval array of rows with dummy data in it. + */ + public function get_dummy_data(int $num_rows, array $data): array { + if ($num_rows > 100) { + throw new \LengthException("Generating too much dummy data via \$num_rows!"); + } + $ret = []; + for ($i = 0; $i < $num_rows; $i++) { + $ret[] = $this->use_dummy_data($data); + } + return $ret; + } + + public function get_dummy_data_generator( + int $num_rows, + array $data + ): \Generator { + for ($i = 0; $i < $num_rows; $i++) { + yield $this->use_dummy_data($data); + } + } + + +} diff --git a/src/classes/database/model.php b/src/classes/database/model.php new file mode 100644 index 0000000..5b1d27e --- /dev/null +++ b/src/classes/database/model.php @@ -0,0 +1,357 @@ + + * @copyright Copyright (c) 2022, Robert Strutts. + * @license MIT + */ + +namespace CodeHydrater\database; + +use CodeHydrater\bootstrap\safer_io as SafeIO; + +class model { + + use \CodeHydrater\traits\database\run_sql; + use \CodeHydrater\traits\database\validation; + + const successful_save = 1; + const duplicate_found = 2; + const validation_error = 3; + const pre_save_failed = 4; + const post_save_failed = 5; + + private $members = []; + private $validation_members = []; + private $missing = []; + private $error_message; + private $primary_key = 'id'; + private $db_skiped = []; + + public function saved(bool $worked = true): bool { + if (headers_sent()) { + return false; + } + header("Content-Type: application/json; charset=UTF-8"); + $time = \CodeHydrater\time_zones::convert_time_zone(array('format' => 'g:i a')); + echo json_encode(['time' => $time, 'saved' => $worked]); + return true; + } + + /** + * JSON API inform AJAX that save failed + */ + public function save_failed($msg = 'Save Failed!'): void { + \CodeHydrater\api::error(array('code' => 500, 'success' => false, 'error' => $msg, 'responce' => \tts\api::INTERNAL_ERROR)); + } + + public function dump_table_fields(string $table) { + $fields = $this->get_fields($table); + $this->do_dump($fields); + } + + public function dump_diff(): bool { + $fields = $this->get_fields($this->table); + + $diff = array_diff($fields, array_keys($this->members)); + + if (($key = array_search('id', $diff)) !== false) { + unset($diff[$key]); // Who cares about IDs + } + + foreach($this->db_skiped as $skip) { + unset($diff[$skip]); + } + + if (count($diff)) { + echo "Diff on fields:
    " . PHP_EOL; + $this->do_dump($diff); + return true; + } + return false; + } + + private function do_dump(array $data) { + echo "
    ";
    +        print_r($data);
    +        echo '
    '; + } + + public function set_primary_key_name(string $key): void { + $this->primary_key = $key; + } + + public function set_members_by_generator(array $input): bool { + $good = true; + foreach(SafeIO::db_sanitize($input) as $data) { + $key = $data['name'] ?? false; + $value = $data['db'] ?? null; + $error_count = (isset($data['errors'])) ? \CodeHydrater\bootstrap\common::get_count($data['errors']) : 0; + if ($error_count) { + return false; + } + + if (isset($data['meta']['missing']) && \CodeHydrater\bootstrap\common::get_count($data['meta']['missing'])) { + $this->missing = $data['meta']['missing']; + } + + $meta = $data['meta'][$key] ?? false; + if ($meta !== false) { + $skip_db = $meta['skip_db'] ?? false; + if ($skip_db === true) { + $this->db_skiped[$key] = true; + continue; // Skip Field + } + } + if ($key !== false) { + $this->members[$key] = $value; + } else { + $good = false; + } + } + return $good; + } + + /** + * Do not use this one in production! AS anyone can override + * the DB security and Inject stuff into the DB via POST, etc... + */ + public function auto_set_members(array $post = [], array $skip = ['route', 'm'], array $only_these = []): void { + if (\CodeHydrater\bootstrap\is_live()) { + return; // Bail if LIVE + } + if (isset($post['json'])) { + $globals = \CodeHydrater\bootstrap\safer_io::get_json_post_data(); + } else { + $json_post = count($post); + $globals = ($json_post) ? $post : filter_input_array(INPUT_POST) ?? []; + } + foreach ($globals as $key => $data) { + if (count($skip) && in_array($key, $skip)) { + continue; + } + if (in_array($key, $only_these) || !count($only_these)) { + $this->members[$key] = (\CodeHydrater\misc::is_not_empty($data)) ? $data : ""; + } + } + } + + public function get_missing() { + return $this->missing; + } + + public function get_member(string $member) { + return $this->members[$member] ?? null; + } + + public function get_members() { + return $this->members; + } + + public function set_member(string $member, string $data): void { + $this->members[$member] = $data; + } + + public function has_member($member): bool { + return isset($this->members[$member]); + } + + public function get_last_error(): string { + return $this->error_message; + } + + /** + * Set Member for Validation + * @param string $key + * @param array $a + */ + public function set_member_for_validation(string $key, array $a): void { + $this->validation_members[$key] = $a; + } + + /** + * Get Validation Member + * @param string $key + * @return array + */ + public function get_vaildation_member(string $key): array { + return $this->validation_members[$key] ?? array(); + } + + /** + * Unset Validation Member + * @param string $key + */ + public function clear_validation_member(string $key): void { + unset($this->validation_members[$key]); + } + + private function cleanup($bind) { + if (!is_array($bind)) { + if (!empty($bind)) { + $bind = array($bind); + } else { + $bind = array(); + } + } + return $bind; + } + + public static function empty_generator(): \Generator { + yield from []; + } + + /** + * Please use this fetch_lazy instead of fetch_all!!! + * To AVOID running out of Memory!!!!!!!!!!!!!!!!!!!! + * @param \PDOStatement $stmt + * @return \Generator + */ + public static function pdo_fetch_lazy(\PDOStatement $stmt): \Generator { + foreach($stmt as $record) { + yield $record; + } + } + + public static function is_valid(array $data): bool { + $error_count = (isset($data['errors'])) ? count($data['errors']) : 0; + return ($error_count === 0); + } + + + public function load(int $id): bool{ + if (method_exists($this, 'pre_load')) { + $this->pre_load(); + } + + $sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?"; + $query = $this->pdo->prepare($sql); + $query->execute(array($id)); + if ($query === false) { + return false; + } + $data = $query->fetch(\PDO::FETCH_ASSOC); + if ($data === false) { + return false; + } + $this->members = array_merge($this->members, $data); + + if (method_exists($this, 'post_load')) { + $this->post_load(); + } + return true; + } + + public function insert($table, $info) { + $fields = $this->filter($table, $info); + + if (strrpos($table, "`") === false) { + $table = "`{$table}`"; + } + + $sql = "INSERT INTO {$table} (" . implode(", ", $fields) . ") VALUES (:" . implode(", :", $fields) . ");"; + $bind = array(); + foreach ($fields as $field) { + $bind[":$field"] = $info[$field]; + } + return $this->run($sql, $bind); + } + + public function update($table, $info, $where, $bind = "") { + $bind = $this->cleanup($bind); + $fields = $this->filter($table, $info); + + if (strrpos($table, "`") === false) { + $table = "`{$table}`"; + } + + $sql = "UPDATE {$table} SET "; + $f = 0; + + foreach ($fields as $key => $value) { + if ($f > 0) { + $sql .= ", "; + } + $f++; + + $value = trim($value); + + if (strrpos($value, "`") === false) { + $cf = '`' . $value . '`'; + } else { + $cf = $value; + } + + $sql .= $cf . " = :update_" . $value; + $bind[":update_$value"] = $info[$value]; + } + + $sql .= " WHERE " . $where . ";"; + + return $this->run($sql, $bind); + } + + public static function make_db_time_stamp(): string { + return \CodeHydrater\time_zones::convert_time_zone(['format' => 'database', 'timezone' => 'UTC']); + } + + /** + * Save + * + * @param object $lcoal_model Should be $this from the Model + * + * Insert if primary key not set + * Update if primary key set + */ + public function save(bool $validate = true): int { + if (method_exists($this, 'pre_save')) { + $pre_was_successfull = $this->pre_save(); + if (! $pre_was_successfull) { + return self::pre_save_failed; + } + } + $name = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); // Get DB Driver + if ($name === 'mysql' && $validate) { + if (!$this->validate_mysql()) { + return self::validation_error; + } + } + + if ($this->has_member('modified')) { + $this->set_member('modified', self::make_db_time_stamp()); + } + + if (empty($this->members[$this->primary_key])) { + if (method_exists($this, 'duplicate_check')) { + $dup = $this->duplicate_check(); + if ($dup) { + return self::duplicate_found; + } + } + + if ($this->has_member('created')) { + $this->set_member('created', self::make_db_time_stamp()); + } + + $this->members[$this->primary_key] = $this->insert($this->table, $this->members); + } else { + $this->set_member("row_count", $this->update($this->table, $this->members, + "`{$this->primary_key}` = :search_key", + array(':search_key' => $this->members[$this->primary_key]) + )); + } + + if (method_exists($this, 'post_save')) { + $post_save_successfull = $this->post_save(); + if (! $post_save_successfull) { + return self::post_save_failed; + } + } + + return self::successful_save; + } + + /* End Model */ +} diff --git a/src/classes/database/paginate.php b/src/classes/database/paginate.php new file mode 100644 index 0000000..f1cd04a --- /dev/null +++ b/src/classes/database/paginate.php @@ -0,0 +1,255 @@ +_conn = $conn; + $this->_query = $query; + + if ($db_type === "mysql") { + $rs = $this->_conn->query($this->_query); + $this->_total = $rs->rowCount(); + } + } + + private function set_limit(int $limit = 10): int { + if ($limit < 1) { + return 10; + } + return ($limit > $this->max_limit) ? $this->max_limit : $limit; + } + + public function mongo_get_data(int $limit = 10, int $page = 1, array $options = []) { + $this->_limit = $this->set_limit($limit); // Number of items per page + $this->_page = $page; // The current page number + + $skip = (( $this->_page - 1 ) * $this->_limit ); + + if ($this->_limit === 0) { + $db_options = $options; + } else { + $limits = ['limit' => $this->_limit, 'skip' => $skip]; + $db_options = array_merge($limits, $options); + } + + // NOTE _query is the WHERE condition as an array + $collection = $this->_conn; + $documents = $collection->find($this->_query, $db_options); + + // Calculate the total number of documents in the collection + $this->_total = $collection->countDocuments($this->_query); + + $result = new \stdClass(); + $result->page = $this->_page; + $result->limit = $this->_limit; + $result->total = $this->_total; + $result->data = $documents; + + return $result; + } + + public function get_data(int $limit = 10, int $page = 1) { + $this->_limit = $this->set_limit($limit); + $this->_page = $page; + + if ($this->_limit === 0) { + $query = $this->_query; + } else { + $query = $this->_query . " LIMIT " . ( ( $this->_page - 1 ) * $this->_limit ) . ", $this->_limit"; + } + $rs = $this->_conn->query($query); + + $results = []; + while ($row = $rs->fetch(\PDO::FETCH_ASSOC)) { + $results[] = $row; + } + + $result = new \stdClass(); + $result->page = $this->_page; + $result->limit = $this->_limit; + $result->total = $this->_total; + $result->data = $results; + + return $result; + } + + public function use_hash_routes(string $url): void { + $this->_url = $url; + $this->_url_limit = "/"; + $this->_url_page = "/"; + } + + public function use_get_routes(string $url = ""): void { + $this->_url = $url; + $this->_url_limit = "?limit="; + $this->_url_page = "&page="; + } + + private function do_href(int $limit, int $page): string { + return 'href="' . $this->_url . $this->_url_limit . $limit . $this->_url_page . $page . '"'; + } + + public function create_links(int $links = 7, string $list_class = "ui pagination menu", string $item = "item") { + $last = ceil($this->_total / $this->_limit); + + if ($this->_limit === 0 || $last < 2) { + return ''; + } + + $start = ( ( $this->_page - $links ) > 0 ) ? $this->_page - $links : 1; + $end = ( ( $this->_page + $links ) < $last ) ? $this->_page + $links : $last; + + $html = '
    '; + + $class = ( $this->_page == 1 ) ? "disabled " : ""; + $item = " " . $item; + + $html .= 'do_href($this->_limit, $this->_page - 1) . '>«'; + + if ($start > 1) { + $html .= 'do_href($this->_limit, 1) .'>1'; + $html .= '
    ...
    '; + } + + for ($i = $start; $i <= $end; $i++) { + $class = ( $this->_page == $i ) ? "active" : ""; + $html .= 'do_href($this->_limit, $i) . '>' . $i . ''; + } + + if ($end < $last) { + $html .= '
    ...
    '; + $html .= 'do_href($this->_limit, $last) . '>' . $last . ''; + } + + $class = ( $this->_page == $last ) ? "disabled" : ""; + $html .= 'do_href($this->_limit, $this->_page + 1) . '>»'; + + $html .= '
    '; + + return $html; + } + + public function create_jump_menu_with_links(int $links = 7, string $label = "Jump to ", string $end_label = " page.", string $item = "item"): string { + if ($this->_limit === 0) { + return ''; + } + + $last = ceil($this->_total / $this->_limit); + + $start = ( ( $this->_page - $links ) > 0 ) ? $this->_page - $links : 1; + $end = ( ( $this->_page + $links ) < $last ) ? $this->_page + $links : $last; + + // Prev. Page + $class = ( $this->_page == 1 ) ? "disabled" : ""; + $href = ( $this->_page == 1 ) ? "" : $this->do_href($this->_limit, $this->_page - 1 ); + $html .= '«'; + + $html .= $this->create_jump_menu(); + + // Next Page + $class = ( $this->_page == $last ) ? "disabled" : ""; + $href = ( $this->_page == $last ) ? "" : $this->do_href($this->_limit, $this->_page + 1 ); + $html .= '»'; + + return $html; + + } + + public function create_jump_menu(string $label = "Jump to ", string $end_label = " page.", string $item = "item"): string { + $last = ceil($this->_total / $this->_limit); + $option = ''; + for($i=1; $i <= $last; $i++) { + $option .= ($i == $this->_page) ? "\n":"\n"; + } + return "\n"; + } + + public function create_items_per_page(string $label = "Items ", string $end_label = " per page.", string $item = "item"): string { + $items = ''; + $ipp_array = array(3,6,12,24,50,100); + $found = false; + foreach($ipp_array as $ipp_opt) { + if ($ipp_opt == $this->_limit) { + $found = true; + } + $items .= ($ipp_opt == $this->_limit) ? "\n" : "\n"; + } + + if ($found === false) { + $items = "\n" . $items; + } + + $my_page = str_replace("&", "?", $this->_url_page); + $my_limit = str_replace("?", "&", $this->_url_limit); + + return "\n"; + } + +} + +/* + * mongo db + $limit = $_GET['limit'] ?? 18; + $page = $_GET['page'] ?? 1; + $url = "#pages/bill"; + $query = [ 'user_id' => (string) $user_id ]; + $mongo_options = [ 'projection' => [ 'billing' => 1 ]]; + + $pag = new paginate($collection, $query, "mongodb"); + $r = $pag->mongo_get_data($limit, $page, $mongo_options); + $pag->use_hash_routes($url); + + foreach ($r->data as $bill) { + echo $bill; + } + + echo $pag->create_links(); + */ + + +/* + * css + * +.pagination { + display: inline-block; +} + +.pagination a { + color: black; + float: left; + padding: 8px 16px; + text-decoration: none; +} + +.pagination a.active { + background-color: #4CAF50; + color: white; +} + +.pagination a:hover:not(.active) {background-color: #ddd;} + */ \ No newline at end of file diff --git a/src/classes/extras_booter.php b/src/classes/extras_booter.php new file mode 100644 index 0000000..14bbe81 --- /dev/null +++ b/src/classes/extras_booter.php @@ -0,0 +1,30 @@ + + * @copyright (c) 2025, Robert Strutts + * @license MIT + */ + +namespace CodeHydrater; + +use HydraterBootloader\LicenseVerifier; + +const PublicPEM = BaseDir."/keydata/public.pem"; +const AESKeysFile = BaseDir."/src/aeskeys.php"; +const LicenseFile = BaseDir."/keydata/license.json"; + +class extras_booter { + public static function tryToEnableFeature(string $featureName) { + + if (! class_exists("HydraterBootloader\LicenseVerifier")) { + return false; + } + + $verifier = new LicenseVerifier(); + return $verifier->tryEnabledItem(LicenseFile, AESKeysFile, $featureName, PublicPEM); + } +} +// tryToEnableFeature("testing"); \ No newline at end of file diff --git a/src/classes/html.php b/src/classes/html.php index d4e3244..f2a03b1 100644 --- a/src/classes/html.php +++ b/src/classes/html.php @@ -32,7 +32,7 @@ final class html { if (count($a)) { foreach ($a as $k => $v) { if ($escape) { - $more .= " ". bootstrap\saferIO::h($k) . "=\"" . bootstrap\saferIO::h($v) . "\""; + $more .= " ". bootstrap\safer_io::h($k) . "=\"" . bootstrap\safer_io::h($v) . "\""; } else { $more .= " {$k}=\"{$v}\""; } @@ -44,8 +44,8 @@ final class html { $compair_to = ($select_by == 'text') ? $text : $value; $selected = (!empty($default) && $default == $compair_to) ? 'selected' : ''; if ($escape) { - $value = bootstrap\saferIO::h($value); - $text = bootstrap\saferIO::h($text); + $value = bootstrap\safer_io::h($value); + $text = bootstrap\safer_io::h($text); } $values .= ""; diff --git a/src/classes/html_document.php b/src/classes/html_document.php new file mode 100644 index 0000000..3c0b2e4 --- /dev/null +++ b/src/classes/html_document.php @@ -0,0 +1,427 @@ +title = bootstrap\configure::get('html', 'title') ?? ''; + $this->author = bootstrap\configure::get('html', 'author') ?? ''; + $this->keywords = bootstrap\configure::get('html', 'keywords') ?? ''; + $this->description = bootstrap\configure::get('html', 'description') ?? ''; + $this->robots = bootstrap\configure::get('html', 'robots'); + $css = bootstrap\configure::get('html', 'css'); + if (bootstrap\common::get_count($css) > 0) { + foreach($css as $file=>$path) { + if (is_array($file)) continue; + if (is_array($path)) { + if (isset($path['path'])) { + $path_type = $path['path']; + unset($path['path']); + } else { + $path_type = "project"; + } + $this->add_css($file, $path_type, $path); + } else { + $this->add_css($file, $path); + } + } + } + $js = bootstrap\configure::get('html', 'javascript'); + if (bootstrap\common::get_count($js) >0) { + foreach($js as $file=>$path) { + if (is_array($file)) continue; + if (is_array($path)) { + if (isset($path['path'])) { + $path_type = $path['path']; + unset($path['path']); + } else { + $path_type = "project"; + } + $this->add_js($file, $path_type, $path); + } else { + $this->add_js($file, $path); + } + } + } + } + + public function clear_css(): void { + $this->styles = ''; + } + + public function clear_js(): void { + $this->scripts = ''; + } + + /** + * Set both Title and Header for HTML + * @param string $title + */ + public function set_title_and_header(string $title): void { + $this->title = $title; + $this->header = $title; + } + + /** + * Set Author for HTML + * @param string $title + */ + public function set_author(string $author): void { + $this->author = $author; + } + + /** + * Set Title for HTML + * @param string $title + */ + public function set_title(string $title): void { + $this->title = $title; + } + + /** + * Set Header for HTML + * @param string $header + */ + public function set_header(string $header): void { + $this->header = $header; + } + + public function set_head(string $head): void { + $this->head = $head; + } + + /** + * Set Footer for HTML + * @param string $footer + */ + public function set_footer(string $footer): void { +// $this->add_css('footer.css', 'project'); + $this->footer = $footer; + } + + /** + * Set Description for HTML + * @param string $description + */ + public function set_description(string $description): void { + $this->description = $description; + } + + /** + * Set Keywords for HTML + * @param string $keywords + */ + public function set_keywords(string $keywords): void { + $this->keywords = $keywords; + } + + /** + * Set Robots for HTML + * @param string $robot + */ + public function set_robots(string $robot): void { + $this->robots = $robot; + } + + public function set_body(string $body): void { + $this->body = $body; + } + + /** + * Set Active BreadCrumb in HTML + * @param string $active + */ + public function set_active_crumb(string $active): void { + $this->active_crumb = $active; + } + + /** + * Set BreadCrumbs using array (HyperLink => Name of Crumb) + * @param array $crumbs Array(href => name) + */ + public function set_breadcrumbs(array $crumbs): void { + $this->breadcrumb = $crumbs; + } + + public function set_assets_from_array(array $files, string $which, string $scope = 'project'): void { + foreach($files as $file => $a) { + switch($which) { + case 'main_css': + $this->add_main_css($file, $scope, $a); + break; + case 'css': + $this->add_css($file, $scope, $a); + break; + case 'main_js': + $this->add_main_js($file, $scope, $a); + break; + case 'js': + $this->add_js($file, $scope, $a); + break; + } + } + } + + private function missing_file(string $file, string $scope, string $kind) { + $failed = strtoupper($kind) . " filename of {$file} - {$scope} Asset Failed to Load!"; + $this->add_to_javascript("console.log(\"%c {$failed}\", \"color: red\")"); + $comment = ""; + if ($kind === "css") { + $this->styles .= $comment; + } else if ($kind === "main_css") { + $this->main_styles .= $comment; + } else if ($kind === "js") { + $this->scripts .= $comment; + } else if ($kind === "main_js") { + $this->main_scripts .= $comment; + } + } + + /** + * Add CSS stylesheet to HTML under main CSS + * @param string $file + * @param string $scope (project, framework, cdn) + * @return bool was successful + */ + public function add_css(string $file, string $scope = 'project', array $a = array()): bool { + $css = assets::wrap_asset($file, $scope); + if ($css === false) { + $this->missing_file($file, $scope, "css"); + return false; + } + $this->styles .= assets::wrap_css($file, $scope, $a); + return true; + } + + /** + * Add JS JavaScript to HTML under main JS + * @param string $file + * @param string $scope (project, framework, cdn) + * @return bool was successful + */ + public function add_js(string $file, string $scope = 'project', array $a = array()): bool { + $js = assets::wrap_asset($file, $scope); + if ($js === false) { + $this->js_log($file . " - {$scope} Asset Failed to Load!"); + return false; + } + $this->scripts .= assets::wrap_js($file, $scope, $a); + return true; + } + + /** + * Add CSS stylesheet to HTML towards top of HTML for CSS + * @param string $file + * @param string $scope (project, framework, cdn) + * @return bool was successful + */ + public function add_main_css(string $file, string $scope = 'project', array $a = array()): bool { + $css = assets::wrap_asset($file, $scope); + if ($css === false) { + $this->js_log($file . " - {$scope} Asset Failed to Load!"); + return false; + } + $this->main_styles .= assets::wrap_css($file, $scope, $a); + return true; + } + + /** + * Add JavaScript to HTML towards top of HTML for JS + * @param string $file + * @param string $scope (project, framework, cdn) + * @return bool was successful + */ + public function add_main_js(string $file, string $scope = 'project', array $a = array()): bool { + $js = assets::wrap_asset($file, $scope); + if ($js === false) { + $this->js_log($file . " - {$scope} Asset Failed to Load!"); + return false; + } + $this->main_scripts .= assets::wrap_js($file, $scope, $a); + return true; + } + + /** + * Adds JavaScript code to called after JQuery is ready. + * @param string $code + */ + //public function add_js_onready(string $code): void { + // $this->js_onready .= \px_inline_js(\px_jquery_load($code)); + //} + + /** + * Log to JavaScript Console under Chrome Browser + * @param string $log + */ + public function js_log(string $log): void { + $this->add_to_javascript("console.log('{$log}');"); + } + + /** + * Place JavaScript in HTML + * @param string $js + */ + public function add_to_javascript(string $js): void { + if (! empty($js)) { + $this->scripts .= assets::inline_js($js); + } + } + + /** + * Use CSS/JS for Database SSP + public function datatables_code(): void { + $this->add_css('datatables/datatables.min.css', 'cl'); + $this->add_js('datatables/datatables_no_jquery.min.js', 'cl'); + } + * + */ + + /** + * Used by Template file to render HTML Author + * @return string HTML Author + */ + public function get_author(): string { + return $this->author; + } + + /** + * Used by Template file to render HTML TITLE + * @return string HTML TITLE + */ + public function get_title(): string { + return $this->title; + } + + /** + * Used by Template file to render HTML Header + * @return string HTML Header + */ + public function get_header(): string { + return $this->header; + } + + public function get_body(): string { + return $this->body; + } + + /** + * Used by Template file to render HTML Footer + * @return string HTML Footer + */ + public function get_footer(): string { + return $this->footer; + } + + /** + * Used by Template file to render HTML Meta data for Description + * @return string HTML Meta Description + */ + public function get_description(): string { + return $this->description; + } + + /** + * Used by Template file to render HTML Meta data for Keywords + * @return string HTML Meta Keywords + */ + public function get_keywords(): string { + return $this->keywords; + } + + /** + * Used by Template file to render HTML Meta data for Robots + * @return string HTML Meta Robots + */ + public function get_robots(): string { + return $this->robots; + } + + /** + * Used by Template file to render HTML CSS + * @return string HTML CSS + */ + public function get_styles(): string { + return $this->styles; + } + + /** + * Used by Template file to render HTML JavaScripts + * @return string HTML JS + */ + public function get_scripts(): string { + return $this->scripts; + } + + /** + * Used by Template file to render HTML main CSS @Top + * @return string HTML CSS + */ + public function get_main_styles(): string { + return $this->main_styles; + } + + /** + * Used by Template file to render HTML main JS @Top + * @return string HTML JavaScript + */ + public function get_main_scripts(): string { + return $this->main_scripts; + } + + /** + * Used by Template file to render HTML JS after main JS + * @return string HTML JS + */ + public function get_js_onready(): string { + return $this->js_onready; + } + +/** + * Used by Template file to render HTML Active BreadCrumb + * @return string HTML Active BreadCrumb + */ + public function get_active_crumb(): string { + return $this->active_crumb; + } + + /** + * Used by Template file to render HTML BreadCrumbs + * @return string HTML BreadCrumbs + */ + public function get_breadcrumbs(): array { + return $this->breadcrumb; + } + + public function get_head(): string { + return $this->head; + } + +} \ No newline at end of file diff --git a/src/classes/html_parser.php b/src/classes/html_parser.php new file mode 100644 index 0000000..0ab5159 --- /dev/null +++ b/src/classes/html_parser.php @@ -0,0 +1,174 @@ + [ + 'pattern' => '/

    (.*?)<\/h1>/s', + 'replace' => '[h1]$1[/h1]', + 'content' => '$1' + ], + 'h2' => [ + 'pattern' => '/

    (.*?)<\/h2>/s', + 'replace' => '[h2]$1[/h2]', + 'content' => '$1' + ], + 'h3' => [ + 'pattern' => '/

    (.*?)<\/h3>/s', + 'replace' => '[h3]$1[/h3]', + 'content' => '$1' + ], + 'h4' => [ + 'pattern' => '/

    (.*?)<\/h4>/s', + 'replace' => '[h4]$1[/h4]', + 'content' => '$1' + ], + 'h5' => [ + 'pattern' => '/

    (.*?)<\/h5>/s', + 'replace' => '[h5]$1[/h5]', + 'content' => '$1' + ], + 'h6' => [ + 'pattern' => '/
    (.*?)<\/h6>/s', + 'replace' => '[h6]$1[/h6]', + 'content' => '$1' + ], + 'bold' => [ + 'pattern' => '/(.*?)<\/b>/s', + 'replace' => '[b]$1[/b]', + 'content' => '$1', + ], + 'strong' => [ + 'pattern' => '/(.*?)<\/strong>/s', + 'replace' => '[b]$1[/b]', + 'content' => '$1', + ], + 'italic' => [ + 'pattern' => '/(.*?)<\/i>/s', + 'replace' => '[i]$1[/i]', + 'content' => '$1' + ], + 'em' => [ + 'pattern' => '/(.*?)<\/em>/s', + 'replace' => '[i]$1[/i]', + 'content' => '$1' + ], + 'underline' => [ + 'pattern' => '/(.*?)<\/u>/s', + 'replace' => '[u]$1[/u]', + 'content' => '$1', + ], + 'strikethrough' => [ + 'pattern' => '/(.*?)<\/s>/s', + 'replace' => '[s]$1[/s]', + 'content' => '$1', + ], + 'del' => [ + 'pattern' => '/(.*?)<\/del>/s', + 'replace' => '[s]$1[/s]', + 'content' => '$1', + ], + 'code' => [ + 'pattern' => '/(.*?)<\/code>/s', + 'replace' => '[code]$1[/code]', + 'content' => '$1' + ], + 'orderedlistnumerical' => [ + 'pattern' => '/
      (.*?)<\/ol>/s', + 'replace' => '[list=1]$1[/list]', + 'content' => '$1' + ], + 'unorderedlist' => [ + 'pattern' => '/