main
Robert 5 months ago
parent 2fae0452e9
commit dde2638f10
  1. 22
      docs/TODO.md
  2. 0
      src/bootstrap/auto_loader.php
  3. 33
      src/bootstrap/errors.php
  4. 2
      src/bootstrap/load_all.php
  5. 6
      src/bootstrap/main.php
  6. 2
      src/bootstrap/safer_io.php
  7. 45
      src/bootstrap/site_helper.php
  8. 23
      src/classes/Collection.php
  9. 29
      src/classes/Dollars.php
  10. 95
      src/classes/LazyCollection.php
  11. 24
      src/classes/LazyObject.php
  12. 66
      src/classes/Middleware.php
  13. 13
      src/classes/Scalar.php
  14. 12
      src/classes/app.php
  15. 102
      src/classes/arrays/common_stuff.php
  16. 262
      src/classes/arrays/countries.php
  17. 166
      src/classes/arrays/mimes.php
  18. 45
      src/classes/arrays/mocking/address.php
  19. 43
      src/classes/arrays/mocking/phone.php
  20. 1179
      src/classes/arrays/mocking/rnd_names.php
  21. 29
      src/classes/arrays/shortn.php
  22. 185
      src/classes/arrays/zipcodes.php
  23. 262
      src/classes/assets.php
  24. 176
      src/classes/bb_code_parser.php
  25. 2
      src/classes/console_app.php
  26. 100
      src/classes/database/dummy_data.php
  27. 357
      src/classes/database/model.php
  28. 255
      src/classes/database/paginate.php
  29. 30
      src/classes/extras_booter.php
  30. 6
      src/classes/html.php
  31. 427
      src/classes/html_document.php
  32. 174
      src/classes/html_parser.php
  33. 0
      src/classes/make_license_files.php
  34. 44
      src/classes/mb_strings_fns.php
  35. 6
      src/classes/misc.php
  36. 2
      src/classes/page_not_found.php
  37. 106
      src/classes/random_engine.php
  38. 16
      src/classes/router.php
  39. 14
      src/classes/security.php
  40. 115
      src/classes/services/log.php
  41. 238
      src/classes/services/paragon_crypto/crypto.php
  42. 127
      src/classes/services/paragon_crypto/password_storage.php
  43. 133
      src/classes/services/paragon_crypto/sodium_storage.php
  44. 98
      src/classes/string_fns.php
  45. 63
      src/classes/tag_matches.php
  46. 49
      src/classes/time_zone_selection.php
  47. 274
      src/classes/time_zones.php
  48. 60
      src/classes/traits/Macroable.php
  49. 6
      src/classes/traits/security/csrf_token_functions.php
  50. 68
      src/classes/uuidv7.php
  51. 243
      src/classes/view.php

@ -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

@ -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, "<br>\n");
return "<div style='{$style}padding:10px;border:1px solid #f99;margin:10px;'>$message</div>";
}
}
@ -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');
}

@ -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";

@ -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) {

@ -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 = [];

@ -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/");
}

@ -0,0 +1,23 @@
<?php
namespace CodeHydrater;
/**
* @template TItem
*/
final readonly class Collection {
/**
* @param array<int, TItem> $items
*/
public function __construct(
private array $items
) {
}
/**
* @return array<int, TItem>
*/
public function all(): array {
return $this->items;
}
}

@ -0,0 +1,29 @@
<?php
namespace CodeHydrater;
/**
* @todo sudo apt install php8.4-bcmath
* @todo sudo apt install php8.4-intl
*/
use BcMath\Number;
class Dollars {
private function moneyAsFloat(Number $money): float {
// Remove any existing currency symbols and thousands separators
$t = preg_replace('/[^\d.-]/', '', $money);
return (float)$t;
}
public function formatAsUSD(Number $money): string {
return '$' . number_format($this->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;
}
}

@ -0,0 +1,95 @@
<?php
namespace CodeHydrater;
/**
* @template TKey
* @template TValue
* @implements \IteratorAggregate<TKey, TValue>
*/
class LazyCollection implements \IteratorAggregate
{
/** @var \Closure|self|array<TKey, TValue>|null */
protected $source;
/**
* @param \Closure(): iterable<TKey, TValue>|self<TKey, TValue>|iterable<TKey, TValue>|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<TKey, TValue>
*/
public function getIterator(): \Traversable
{
return $this->makeIterator($this->source);
}
/**
* @param \Closure(): iterable<TKey, TValue>|self<TKey, TValue>|array<TKey, TValue> $source
* @return \Traversable<TKey, TValue>
*/
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<TNewKey, TNewValue>|iterable<TNewKey, TNewValue>|null $source
* @return self<TNewKey, TNewValue>
*/
public static function make($source = null)
{
return new static($source instanceof \Closure ? $source() : $source);
}
/**
* @param callable(TValue, TKey): bool $callback
* @return self<TKey, TValue>
*/
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<TKey, TValue>
*/
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;
}
}

@ -0,0 +1,24 @@
<?php
namespace CodeHydrater;
class LazyObject {
public function __construct(public string $className) {}
public function proxy(mixed ...$args): Object {
$className = $this->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);
}
}

@ -0,0 +1,66 @@
<?php
namespace CodeHydrater;
interface Middleware
{
public function handle(array $request, \Closure $next, mixed ...$parameters): mixed;
}
class Pipeline
{
protected array $middleware = [];
protected $request;
public function send(array $request): Object
{
$this->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 ?? []);
};
};
}
}

@ -0,0 +1,13 @@
<?php
namespace CodeHydrater;
class Scalar {
public function intoString(mixed $name): string {
if (is_scalar($name) || (is_object($name) && method_exists($name, '__toString'))) {
return (string)$name;
} else {
throw new \Exception("Cannot convert to string!");
}
}
}

@ -14,7 +14,7 @@ use Exception;
/**
*
* @todo Ensure tts JS error reporting works
* @todo Ensure JS error reporting works
* @todo Finish Session MGT, Encrypted Sessions
* @todo Make Cached Sessions
* @todo Force Database methods to try Cache First and Save Cache Data
@ -42,7 +42,7 @@ class app {
* Do not declare a return type here, as it will Error out!!
*/
public function __construct() {
$full_route = bootstrap\siteHelper::get_route();
$full_route = bootstrap\site_helper::get_route();
$no_hmtl = str_replace(".html", "", $full_route);
// Find the Route
@ -51,7 +51,7 @@ class app {
// Find the Method
$the_method = $this->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 = "";

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays;
class common_stuff {
public static function get_ordinal_number_suffix(int $number): string {
$number = abs($number);
if (in_array(($number % 100),array(11,12,13))){
return 'th';
}
switch ($number % 10) {
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
default: return 'th';
}
}
/**
* Fetch Months
* @return array of months
*/
public static function months(): array {
return array(
'1' => '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',
);
}
}

@ -0,0 +1,262 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays;
class countries {
/**
* Fetch countries
* @return array of countries
*/
public static function countries_array(): array {
return array(
'AX' => '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'
);
}
}

@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays;
class mimes {
/**
* Fetch Mime Types
* @return array of mime types
*/
public static function mimes_array(): array {
return [
'text' => [
'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;
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays\mocking;
class phone {
const TOLLFREE = [800, 844, 855, 866, 877, 888];
const AREACODES = [201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 212, 213, 214, 215, 216, 217, 218, 219, 220, 224, 225, 226, 228, 229, 231, 234, 236, 239, 240, 242, 246, 248, 249, 250, 251, 252, 253, 254, 256, 260, 262, 264, 267, 268, 269, 270, 272, 276, 281, 284, 289, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 323, 325, 330, 331, 334, 336, 337, 339, 340, 343, 345, 346, 347, 351, 352, 360, 361, 364, 365, 380, 385, 386, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 412, 413, 414, 415, 416, 417, 418, 419, 423, 424, 425, 430, 431, 432, 434, 435, 437, 438, 440, 441, 442, 443, 450, 458, 469, 470, 473, 475, 478, 479, 480, 484, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 512, 513, 514, 515, 516, 517, 518, 519, 520, 530, 531, 534, 539, 540, 541, 548, 551, 559, 561, 562, 563, 567, 570, 571, 573, 574, 575, 579, 580, 581, 585, 586, 587, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 612, 613, 614, 615, 616, 617, 618, 619, 620, 623, 626, 628, 629, 630, 631, 636, 639, 641, 646, 647, 649, 650, 651, 657, 660, 661, 662, 664, 667, 669, 670, 671, 678, 681, 682, 684, 701, 702, 703, 704, 705, 706, 707, 708, 709, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 724, 725, 727, 731, 732, 734, 737, 740, 743, 747, 754, 757, 758, 760, 762, 763, 765, 767, 769, 770, 772, 773, 774, 775, 778, 779, 780, 781, 782, 784, 785, 786, 787, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 812, 813, 814, 815, 816, 817, 818, 819, 825, 828, 829, 830, 831, 832, 843, 845, 847, 848, 849, 850, 854, 856, 857, 858, 859, 860, 862, 863, 864, 865, 867, 868, 869, 870, 872, 873, 876, 878, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 912, 913, 914, 915, 916, 917, 918, 919, 920, 925, 928, 929, 930, 931, 934, 936, 937, 938, 939, 940, 941, 947, 949, 951, 952, 954, 956, 959, 970, 971, 972, 973, 978, 979, 980, 984, 985, 989];
private static function rnd_a(array $a) {
$array_count = \CodeHydrater\bootstrap\common::get_count($a);
return $a[rand(0, $array_count - 1)];
}
public static function get_phone(int $rows = 100, bool $formatted = true, bool $tollfree = false): array {
$phone_numbers = [];
for($i =0; $i < $rows; $i++) {
$use_tollfree = ($tollfree && rand(0,9) === 4) ? true : false;
$random_number = (string) rand(1000, 9999);
$prefix = (string) rand(111,899);
$toll = "";
if ($use_tollfree) {
$toll = (string) self::rnd_a(self::TOLLFREE);
if ($formatted) {
$phone_numbers[] = "1-(" . $toll . ")" . $prefix . "-" . $random_number;
} else {
$phone_numbers[] = "1" . $toll . $prefix . $random_number;
}
} else {
$areacode = (string) self::rnd_a(self::AREACODES);
if ($formatted) {
$phone_numbers[] = "(" . $areacode . ")" . $prefix . "-". $random_number;
} else {
$phone_numbers[] = $areacode . $prefix . $random_number;
}
}
}
return $phone_numbers;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays;
final class shortn {
public static function short_street_suffix($text) {
$usps_abbreviations = [
'AVENUE' => '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;
}
}

@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\arrays;
class zipcodes {
public static function get_state(string $zipcode): array {
/* Ensure we have exactly 5 characters to parse */
if (strlen($zipcode) !== 5) {
return 'Must pass a 5-digit $zipcode.';
}
$st = '';
$state = '';
/* Code cases alphabetized by state */
if ($zipcode >= 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];
}
}

@ -0,0 +1,262 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater;
final class assets {
public static function get_ver(string $filename): string {
if (self::attempts_root_dir($filename)) {
return "";
}
$safe_file = security::filter_uri($filename);
if (! file_exists($safe_file)) {
return "?ver=false";
}
return "?ver=" . date('Y.m.d_H.i.s', filemtime($safe_file));
}
/**
* Check for / or absolute path not belonging to /var/www or PRJ:ROOT
* @param string|null $path
* @return bool
*/
private static function attempts_root_dir(?string $path): bool {
// up from src and back up to public
$open_base_dir_path = dirname(bootstrap\site_helper::get_root(), 1) . '/public/';
if ($path === null || $path === '') {
return false;
}
if (bootstrap\common::get_string_left($path, strlen($open_base_dir_path)) == $open_base_dir_path) {
return false;
}
if (bootstrap\common::get_string_left($path, 1) == '/') {
return true;
}
return false;
}
public static function get_ajax_files(string $ajax_folder): string {
if (self::attempts_root_dir($ajax_folder)) {
return "";
}
$safe_folder = security::filter_uri($ajax_folder);
$ret = "var assets_files = [];" . PHP_EOL;
$js = glob($safe_folder . "*.{js,css}", GLOB_BRACE);
foreach($js as $file) {
$ret .= 'assets_files.push({ filename: "' . basename($file) . '", ts: "' . self::get_ver($file) . '" });' . PHP_EOL;
}
return $ret;
}
public static function loadall_css(array $files, string $scope='project'): string {
$ret = '';
foreach($files as $file=>$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 "<img src=\"{$file}\" {$more}/>\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 "<!-- CSS: {$file} not Found -->";
}
return "<link rel=\"stylesheet\" href=\"{$wrap_file}{$ver}\" type=\"text/css\"{$more}/>\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 "<!-- Script: {$file} not Found -->";
}
return "<script src=\"{$wrap_file}{$ver}\" type=\"{$type}\"{$more}></script>\r\n";
//return "<script type=\"text/javascript\">js_loader(\"{$file}\");</script>";
}
/**
* 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 "<script type=\"text/javascript\">\r\n//<![CDATA[\r\n {$code}\r\n //]]> \r\n </script>\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 '<META http-equiv="refresh" content="0;URL=' . $url . '">';
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('&amp;', "\n", "\r"), array('&', '', ''), $url);
if (!headers_sent()) {
header('Location: ' . $url);
} else {
self::goto_url($url);
}
exit;
}
}

@ -0,0 +1,176 @@
<?php
declare(strict_types=1);
/**
* @link https://github.com/genert/bbcode/blob/master/src/Parser/BBCodeParser.php
* @license The MIT License (MIT)
* @copyright (c) Genert 2017 - present.
*
* Take BB code 2 HTML, to display output
*/
namespace CodeHydrater;
final class bb_code_parser {
protected $parsers = [
'h1' => [
'pattern' => '/\[h1\](.*?)\[\/h1\]/s',
'replace' => '<h1>$1</h1>',
'content' => '$1'
],
'h2' => [
'pattern' => '/\[h2\](.*?)\[\/h2\]/s',
'replace' => '<h2>$1</h2>',
'content' => '$1'
],
'h3' => [
'pattern' => '/\[h3\](.*?)\[\/h3\]/s',
'replace' => '<h3>$1</h3>',
'content' => '$1'
],
'h4' => [
'pattern' => '/\[h4\](.*?)\[\/h4\]/s',
'replace' => '<h4>$1</h4>',
'content' => '$1'
],
'h5' => [
'pattern' => '/\[h5\](.*?)\[\/h5\]/s',
'replace' => '<h5>$1</h5>',
'content' => '$1'
],
'h6' => [
'pattern' => '/\[h6\](.*?)\[\/h6\]/s',
'replace' => '<h6>$1</h6>',
'content' => '$1'
],
'bold' => [
'pattern' => '/\[b\](.*?)\[\/b\]/s',
'replace' => '<b>$1</b>',
'content' => '$1'
],
'italic' => [
'pattern' => '/\[i\](.*?)\[\/i\]/s',
'replace' => '<i>$1</i>',
'content' => '$1'
],
'underline' => [
'pattern' => '/\[u\](.*?)\[\/u\]/s',
'replace' => '<u>$1</u>',
'content' => '$1'
],
'strikethrough' => [
'pattern' => '/\[s\](.*?)\[\/s\]/s',
'replace' => '<s>$1</s>',
'content' => '$1'
],
'quote' => [
'pattern' => '/\[quote\](.*?)\[\/quote\]/s',
'replace' => '<blockquote>$1</blockquote>',
'content' => '$1'
],
'link' => [
'pattern' => '/\[url\](.*?)\[\/url\]/s',
'replace' => '<a href="$1">$1</a>',
'content' => '$1'
],
'namedlink' => [
'pattern' => '/\[url\=(.*?)\](.*?)\[\/url\]/s',
'replace' => '<a href="$1">$2</a>',
'content' => '$2'
],
'image' => [
'pattern' => '/\[img\](.*?)\[\/img\]/s',
'replace' => '<img src="$1">',
'content' => '$1'
],
'orderedlistnumerical' => [
'pattern' => '/\[list=1\](.*?)\[\/list\]/s',
'replace' => '<ol>$1</ol>',
'content' => '$1'
],
'orderedlistalpha' => [
'pattern' => '/\[list=a\](.*?)\[\/list\]/s',
'replace' => '<ol type="a">$1</ol>',
'content' => '$1'
],
'unorderedlist' => [
'pattern' => '/\[list\](.*?)\[\/list\]/s',
'replace' => '<ul>$1</ul>',
'content' => '$1'
],
'listitem' => [
'pattern' => '/\[\*\](.*)/',
'replace' => '<li>$1</li>',
'content' => '$1'
],
'code' => [
'pattern' => '/\[code\](.*?)\[\/code\]/s',
'replace' => '<code>$1</code>',
'content' => '$1'
],
'youtube' => [
'pattern' => '/\[youtube\](.*?)\[\/youtube\]/s',
'replace' => '<iframe width="560" height="315" src="//www.youtube-nocookie.com/embed/$1" frameborder="0" allowfullscreen></iframe>',
'content' => '$1'
],
'sub' => [
'pattern' => '/\[sub\](.*?)\[\/sub\]/s',
'replace' => '<sub>$1</sub>',
'content' => '$1'
],
'sup' => [
'pattern' => '/\[sup\](.*?)\[\/sup\]/s',
'replace' => '<sup>$1</sup>',
'content' => '$1'
],
'small' => [
'pattern' => '/\[small\](.*?)\[\/small\]/s',
'replace' => '<small>$1</small>',
'content' => '$1'
],
'table' => [
'pattern' => '/\[table\](.*?)\[\/table\]/s',
'replace' => '<table>$1</table>',
'content' => '$1',
],
'table-row' => [
'pattern' => '/\[tr\](.*?)\[\/tr\]/s',
'replace' => '<tr>$1</tr>',
'content' => '$1',
],
'table-data' => [
'pattern' => '/\[td\](.*?)\[\/td\]/s',
'replace' => '<td>$1</td>',
'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;
}
}

@ -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() {

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater\database;
final class dummy_data {
private $pdo;
private $random_engine;
public function __construct($pdo) {
$this->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);
}
}
}

@ -0,0 +1,357 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Robert@TryingToScale.com>
* @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:<br>" . PHP_EOL;
$this->do_dump($diff);
return true;
}
return false;
}
private function do_dump(array $data) {
echo "<pre style=\"border: 1px solid #000; overflow: auto; margin: 0.5em;\">";
print_r($data);
echo '</pre>';
}
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 */
}

@ -0,0 +1,255 @@
<?php
declare(strict_types=1);
/**
* Modified from:
* @link https://code.tutsplus.com/tutorials/how-to-paginate-data-with-php--net-2928
*/
namespace tts\database;
class paginate {
private $_conn;
private int $_limit;
private int $_page;
private $_query;
private $_total;
private $_url;
private $_url_limit;
private $_url_page;
public $max_limit = 500;
/*
* Query needs to be string for MySQL and array for MonogDB
*/
public function __construct($conn, string|array $query, string $db_type = "mysql") {
$this->_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 = '<div class="' . $list_class . '">';
$class = ( $this->_page == 1 ) ? "disabled " : "";
$item = " " . $item;
$html .= '<a class="' . $class . $item . '" ' . $this->do_href($this->_limit, $this->_page - 1) . '>&laquo;</a>';
if ($start > 1) {
$html .= '<a class="' . $item . '" ' . $this->do_href($this->_limit, 1) .'>1</a>';
$html .= '<div class="disabled' . $item . '"><span>...</span></div>';
}
for ($i = $start; $i <= $end; $i++) {
$class = ( $this->_page == $i ) ? "active" : "";
$html .= '<a class="' . $class . $item . '" ' . $this->do_href($this->_limit, $i) . '>' . $i . '</a>';
}
if ($end < $last) {
$html .= '<div class="disabled' . $item . '"><span>...</span></div>';
$html .= '<a class="' . $class . $item . '" ' . $this->do_href($this->_limit, $last) . '>' . $last . '</a>';
}
$class = ( $this->_page == $last ) ? "disabled" : "";
$html .= '<a class="' . $class . $item . '" ' . $this->do_href($this->_limit, $this->_page + 1) . '>&raquo;</a>';
$html .= '</div>';
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 .= '<a class="' . $class . ' item" ' . $href . '>&laquo;</a>';
$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 .= '<a class="' . $class . ' item" ' . $href . '>&raquo;</a>';
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) ? "<option value=\"{$i}\" selected>Page {$i}</option>\n":"<option value=\"{$i}\">Page {$i}</option>\n";
}
return "<label>{$label}<select class=\"{$item}\" onchange=\"window.location='". $this->_url . $this->_url_limit . $this->_limit . $this->_url_page . "'+this[this.selectedIndex].value;return false;\">"
. "{$option}</select>"
. "{$end_label}</label>\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) ? "<option selected value=\"{$ipp_opt}\">{$ipp_opt}</option>\n" : "<option value=\"{$ipp_opt}\">{$ipp_opt}</option>\n";
}
if ($found === false) {
$items = "<option selected value=\"{$this->_limit}\">{$this->_limit}</option>\n" . $items;
}
$my_page = str_replace("&", "?", $this->_url_page);
$my_limit = str_replace("?", "&", $this->_url_limit);
return "<label>{$label}<select class=\"{$item}\" onchange=\"window.location='". $my_page . "1" . $my_limit . "'+this[this.selectedIndex].value;return false;\">{$items}</select>"
. "{$end_label}</label>\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;}
*/

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Bob_586@Yahoo.com>
* @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");

@ -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 .= "<option value=\"{$value}\" " .
$selected . " " . $more . ">{$text}</option>";

@ -0,0 +1,427 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater;
/**
* HTML Document
*/
class html_document {
private $title = '';
private $author = '';
private $description = '';
private $keywords = '';
private $robots = '';
private $head = '';
private $header = '';
private $body = '';
private $footer = '';
private $js_onready = '';
private $styles = '';
private $scripts = '';
private $main_styles = '';
private $main_scripts = '';
private $active_crumb = '';
private $breadcrumb = array();
public function __construct() {
$this->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 = "<!-- {$failed} -->";
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;
}
}

@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
/**
* @link https://github.com/genert/bbcode/blob/master/src/Parser/HTMLParser.php
* @license The MIT License (MIT)
* @copyright (c) Genert 2017 - present.
*
* Take HTML 2 BB code .. to save into db
*/
namespace CodeHydrater;
final class html_parser {
protected $parsers = [
'h1' => [
'pattern' => '/<h1>(.*?)<\/h1>/s',
'replace' => '[h1]$1[/h1]',
'content' => '$1'
],
'h2' => [
'pattern' => '/<h2>(.*?)<\/h2>/s',
'replace' => '[h2]$1[/h2]',
'content' => '$1'
],
'h3' => [
'pattern' => '/<h3>(.*?)<\/h3>/s',
'replace' => '[h3]$1[/h3]',
'content' => '$1'
],
'h4' => [
'pattern' => '/<h4>(.*?)<\/h4>/s',
'replace' => '[h4]$1[/h4]',
'content' => '$1'
],
'h5' => [
'pattern' => '/<h5>(.*?)<\/h5>/s',
'replace' => '[h5]$1[/h5]',
'content' => '$1'
],
'h6' => [
'pattern' => '/<h6>(.*?)<\/h6>/s',
'replace' => '[h6]$1[/h6]',
'content' => '$1'
],
'bold' => [
'pattern' => '/<b>(.*?)<\/b>/s',
'replace' => '[b]$1[/b]',
'content' => '$1',
],
'strong' => [
'pattern' => '/<strong>(.*?)<\/strong>/s',
'replace' => '[b]$1[/b]',
'content' => '$1',
],
'italic' => [
'pattern' => '/<i>(.*?)<\/i>/s',
'replace' => '[i]$1[/i]',
'content' => '$1'
],
'em' => [
'pattern' => '/<em>(.*?)<\/em>/s',
'replace' => '[i]$1[/i]',
'content' => '$1'
],
'underline' => [
'pattern' => '/<u>(.*?)<\/u>/s',
'replace' => '[u]$1[/u]',
'content' => '$1',
],
'strikethrough' => [
'pattern' => '/<s>(.*?)<\/s>/s',
'replace' => '[s]$1[/s]',
'content' => '$1',
],
'del' => [
'pattern' => '/<del>(.*?)<\/del>/s',
'replace' => '[s]$1[/s]',
'content' => '$1',
],
'code' => [
'pattern' => '/<code>(.*?)<\/code>/s',
'replace' => '[code]$1[/code]',
'content' => '$1'
],
'orderedlistnumerical' => [
'pattern' => '/<ol>(.*?)<\/ol>/s',
'replace' => '[list=1]$1[/list]',
'content' => '$1'
],
'unorderedlist' => [
'pattern' => '/<ul>(.*?)<\/ul>/s',
'replace' => '[list]$1[/list]',
'content' => '$1'
],
'listitem' => [
'pattern' => '/<li>(.*?)<\/li>/s',
'replace' => '[*]$1',
'content' => '$1'
],
'link' => [
'pattern' => '/<a href="(.*?)">(.*?)<\/a>/s',
'replace' => '[url=$1]$2[/url]',
'content' => '$1'
],
'quote' => [
'pattern' => '/<blockquote>(.*?)<\/blockquote>/s',
'replace' => '[quote]$1[/quote]',
'content' => '$1'
],
'image' => [
'pattern' => '/<img src="(.*?)">/s',
'replace' => '[img]$1[/img]',
'content' => '$1'
],
'youtube' => [
'pattern' => '/<iframe width="560" height="315" src="\/\/www\.youtube\.com\/embed\/(.*?)" frameborder="0" allowfullscreen><\/iframe>/s',
'replace' => '[youtube]$1[/youtube]',
'content' => '$1'
],
'linebreak' => [
'pattern' => '/<br\s*\/?>/',
'replace' => '/\r\n/',
'content' => '',
],
'sub' => [
'pattern' => '/<sub>(.*?)<\/sub>/s',
'replace' => '[sub]$1[/sub]',
'content' => '$1'
],
'sup' => [
'pattern' => '/<sup>(.*?)<\/sup>/s',
'replace' => '[sup]$1[/sup]',
'content' => '$1'
],
'small' => [
'pattern' => '/<small>(.*?)<\/small>/s',
'replace' => '[small]$1[/small]',
'content' => '$1',
],
'table' => [
'pattern' => '/<table>(.*?)<\/table>/s',
'replace' => '[table]$1[/table]',
'content' => '$1',
],
'table-row' => [
'pattern' => '/<tr>(.*?)<\/tr>/s',
'replace' => '[tr]$1[/tr]',
'content' => '$1',
],
'table-data' => [
'pattern' => '/<td>(.*?)<\/td>/s',
'replace' => '[td]$1[/td]',
'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 parse(string $source): string {
foreach ($this->parsers as $name => $parser) {
$source = $this->searchAndReplace($parser['pattern'], $parser['replace'], $source);
}
return $source;
}
}

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Bob_586@Yahoo.com>
* @copyright (c) 2024, Robert Strutts
* @license MIT
*/
namespace CodeHydrater;
class mb_strings_fns {
public static function isUTF8(string $string) {
return mb_check_encoding($string, 'UTF-8');
}
// Check if string contains multibyte characters
public static function has_multibyte_chars(string $string) {
return strlen($string) !== mb_strlen($string, 'UTF-8');
}
// Override to get the length of a string with multibyte support
public function strlen($string) {
return mb_strlen($string);
}
// Override to convert a string to lowercase with multibyte support
public function strtolower($string) {
return mb_strtolower($string);
}
// Override to convert a string to uppercase with multibyte support
public function strtoupper($string) {
return mb_strtoupper($string);
}
// Override to get a substring from a string with multibyte support
public function substr($string, $start, $length = null) {
return ($length !== null) ? mb_substr($string, $start, $length) : mb_substr($string, $start);
}
}

@ -3,7 +3,7 @@
declare(strict_types=1);
/**
* @author Robert Strutts <Robert@TryingToScale.com>
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
@ -36,7 +36,7 @@ final class misc {
* @retval boolean true if AJAX request by JQuery, etc...
*/
public static function is_ajax(): bool {
$http_x_requested_with = bootstrap\saferIO::get_clean_server_var('HTTP_X_REQUESTED_WITH');
$http_x_requested_with = bootstrap\safer_io::get_clean_server_var('HTTP_X_REQUESTED_WITH');
return (self::compair_it($http_x_requested_with, 'xmlhttprequest'));
}
@ -320,7 +320,7 @@ final class misc {
* @retval bool
*/
public static function is_api(): bool {
$uri = bootstrap\siteHelper::request_uri();
$uri = bootstrap\site_helper::request_uri();
if (strlen($uri) > 2) {
return ((substr_count($uri, "/api/") == 1 && self::get_var('api') == 'true') || (substr_count($uri, "/api2/") == 1 && self::get_var('api2') == 'true')) ? true : false;
} else {

@ -38,7 +38,7 @@ class page_not_found {
* Displays 404 Page not Found
*/
public static function error404(): void {
if (consoleApp::is_cli()) {
if (console_app::is_cli()) {
self::error404_cli();
} else {
$use_api = misc::is_api();

@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater;
/**
* random_engine - Provides a high-level API to the randomness
* provided by an Random\Engine OR uses the next Fall Back FN.
*
* @author Robert Strutts
*/
class random_engine {
private $engine = false;
public function __construct() {
$version = (float) phpversion();
if ($version > 8.1) {
$this->engine = new \Random\Randomizer();
}
}
public function get_bytes(int $bytes_length = 16): string {
return ($this->engine) ? $this->engine->getBytes($bytes_length) :
random_bytes($bytes_length);
}
public function get_int(int $min, int $max): int {
if ($this->engine) {
return $this->engine->getInt($min, $max);
}
if (function_exists('random_int')) {
return random_int($min, $max); // secure fallback
} elseif (function_exists('mt_rand')) {
return mt_rand($min, $max); // fast
}
return rand($min, $max); // old
}
// Took from source https://pageconfig.com/post/fixed-length-large-random-numbers-with-php
private function big_rand(int $len = 18 ): int {
$rand = '';
while( !( isset( $rand[$len-1] ) ) ) {
$rand .= mt_rand( );
}
return (int) substr( $rand , 0 , $len );
}
public function get_next_big_postive_int(): int {
if ($this->engine) {
return $this->engine->nextInt();
}
return $this->big_rand();
}
private function select_from_array(array $a, int $num ): array {
$array_count = \bs_tts\common::get_count($a) - 1;
if ($array_count < 1) {
return [];
}
$ret = [];
for($i=0; $i<$num; $i++) {
$ret[] = $a[$this->get_int(0, $array_count)];
}
return $ret;
}
/**
* Pick random keys from an Array
*/
public function get_array_keys(array $a, int $num): array {
if ($this->engine) {
return $this->engine->pickArrayKeys($a, $num);
}
return $this->select_from_array($a, $num);
}
public function get_shuffled_array(array $a): array {
if ($this->engine) {
return $this->engine->shuffleArray($a);
}
shuffle($a);
return $a;
}
public function get_shuffled_bytes(string $bytes): string {
if ($this->engine) {
return $this->engine->shuffleBytes($bytes);
}
$len = mb_strlen($bytes);
$a = [];
while($len-- > 0) {
$a[] = mb_substr($bytes, $len, 1);
}
shuffle($a);
return join('', $a);
}
}

@ -321,7 +321,7 @@ class router
* with a method called get!
*/
private static function get_all_routes(bool $testing = false) {
$route_name = (consoleApp::is_cli()) ? "cli_routes" : "routes";
$route_name = (console_app::is_cli()) ? "cli_routes" : "routes";
$routes = ($testing) ? "test_routes" : $route_name;
$routes_class = "\\Project\\routes\\{$routes}";
@ -338,10 +338,10 @@ class router
* @todo add Kernel/Requests...
*/
public static function execute() {
$ROOT = bootstrap\siteHelper::get_root();
$request_uri = bootstrap\siteHelper::get_uri();
$request_method = bootstrap\siteHelper::get_method();
$testing = bootstrap\siteHelper::get_testing();
$ROOT = bootstrap\site_helper::get_root();
$request_uri = bootstrap\site_helper::get_uri();
$request_method = bootstrap\site_helper::get_method();
$testing = bootstrap\site_helper::get_testing();
// Generate request and absolute path
self::generateURL($ROOT, $request_uri);
@ -460,13 +460,13 @@ class router
*/
private static function generateURL(string $ROOT, string $request_uri)
{
$https = bootstrap\saferIO::get_clean_server_var('HTTPS');
$https = bootstrap\safer_io::get_clean_server_var('HTTPS');
$baseLink = ($https === 'on') ? "https" : "http";
$server_name = bootstrap\saferIO::get_clean_server_var('SERVER_NAME');
$server_name = bootstrap\safer_io::get_clean_server_var('SERVER_NAME');
$baseLink .= "://" . $server_name;
$port = bootstrap\saferIO::get_clean_server_var('SERVER_PORT');
$port = bootstrap\safer_io::get_clean_server_var('SERVER_PORT');
$baseLink .= ($port !== '80') ? ':' . $port : ':';
$baseRequest = '';

@ -220,6 +220,20 @@ final class security {
}
}
public static function safe_for_eval(string $s): string {
//new line check
$nl = chr(10);
if (strpos($s, $nl)) {
throw new \Exception("String CR/LF not permitted");
}
$meta = ['$','{','}','[',']','`',';'];
$escaped = ['&#36','&#123','&#125','&#91','&#96','&#59'];
// add slashed for quotes and blackslashes
$out = addslashes($s);
// replace php meta chrs
$out = str_repeat($meta, $escaped, $out);
return $out;
}
public static function get_client_ip_address() {
$ipaddress = '';

@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater\services;
final class log {
private $handle;
/**
* Get Filename for writing to Log file
* @param string $filename for log
* @param int $max_count of lines before file wipe, to keep small logs.
*/
public function __construct(string $filename = 'system', int $max_count = 1000) {
if (strpos($filename, "..") !== false) {
$this->handle = false; // Too dangerious, so return false
} else {
$log_dir = BaseDir . '/protected/logs';
if (! is_dir($log_dir)){
//Directory does not exist, so lets create it.
mkdir($log_dir, 0775);
}
$filename = preg_replace("|[^A-Za-z0-9_]|", "", $filename);
$filename = escapeshellcmd($filename);
$file = $log_dir . '/' . $filename . ".log.txt";
if ($max_count > 1) {
if ($this->get_lines($file) > $max_count) {
unlink($file);
}
}
$success = file_put_contents($file, "\n", FILE_APPEND);
// $success = @touch($file);
if ($success === false) {
$this->handle = false;
\CodeHydrater\bootstrap\common::dump($file);
return false;
}
@chmod($file, 0660);
@chgrp($file, "www-data");
if (! is_writable($file)) {
$this->handle = false;
return false;
}
$this->handle = fopen($file, 'a');
}
}
/**
* Count number of lines in Log File
* @param string $file
* @return int line count
*/
public function get_lines(string $file): int {
// No such file, so return zero for length.
if (! file_exists($file)) {
return 0;
}
$f = fopen($file, 'rb');
$lines = 0;
if ($f === false || !is_resource($f)) {
return 0;
}
while (!feof($f)) {
$line = fread($f, 8192);
if ($line === false) {
return 0;
}
$lines += substr_count($line, "\n");
}
fclose($f);
return $lines;
}
/**
* Write to Log File
* @param string $message to save
* @return bool able to write to log file
*/
public function write(string $message): bool {
if ( $this->handle === false || ! is_resource($this->handle) ) {
return false;
}
$dt = new \DateTime();
$now = $dt->format('g:i A \o\n l jS F Y');
fwrite($this->handle, $now . ' - ' . print_r($message, true) . "\n");
return true;
}
/**
* Close Log File Handle
*/
public function __destruct() {
if ($this->handle !== false && is_resource($this->handle)) {
fclose($this->handle);
}
}
}

@ -0,0 +1,238 @@
<?php
declare(strict_types=1);
/**
* @license ISC
* @copyright Copyright (c) 2016-2023, Paragon Initiative Enterprises
* @link https://paragonie.com/book/pecl-libsodium/read/05-publickey-crypto.md
*/
namespace CodeHydrater\services\paragon_crypto;
class crypto {
const single_key = true;
const multiple_keys = false;
private $rnd;
public function __construct(#[\SensitiveParameter] private string $key) {
$this->rnd = new \tts\random_engine();
}
/*
* Secret key encryption (or symmetric encryption as it’s also
* known) uses a single key to both encrypt and decrypt data.
* In the past PHP relied on mcrypt and openssl for secret key
* encryption. PHP 7.2 introduced Sodium, which is more modern
* and widely considered more secure.
*
* In order to encrypt a value, first you’ll need an encryption
* key, which can be generated using the
* sodium_crypto_secretbox_keygen() function.
*
* $key = sodium_crypto_secretbox_keygen();
* You can also use the random_bytes() function with the
* SODIUM_CRYPTO_SECRETBOX_KEYBYTES integer constant for the
* key length, but using sodium_crypto_secretbox_keygen()
* ensures that the key length is always correct (i.e. not too
* short), and it’s easier.
*
* $key = random_bytes( SODIUM_CRYPTO_SECRETBOX_KEYBYTES );
* Either way, you’ll usually only do this once and store the
* result as an environment variable. Remember that this key
* must be kept secret at all costs. If the key is ever
* compromised, so is any data encrypted by using it.
*/
public function encrypt(
#[\SensitiveParameter] string $message,
bool $key_usage = self::single_key
): string {
$key = $this->key;
$nonce = $this->rnd->get_bytes(
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
);
$fn = ($key_usage == self::single_key) ? "sodium_crypto_secretbox" : "sodium_crypto_box";
$cipher = base64_encode(
$nonce .
$fn(
$message,
$nonce,
base64_decode($key)
)
);
sodium_memzero($message);
sodium_memzero($key);
return $cipher;
}
public function decrypt(
string $encrypted,
bool $key_usage = self::single_key
): string | false {
$key = $this->key;
$decoded = base64_decode($encrypted);
if ($decoded === false) {
throw new Exception('Unable to decode');
}
if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
throw new Exception('Message was truncated');
}
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$fn = ($key_usage == self::single_key) ? "sodium_crypto_secretbox_open" : "sodium_crypto_box_open";
$plain = $fn(
$ciphertext,
$nonce,
base64_decode($key)
);
sodium_memzero($ciphertext);
sodium_memzero($key);
return $plain;
}
public static function make_user_box_kp() {
return sodium_crypto_box_keypair();
}
public static function make_user_sign_kp() {
return sodium_crypto_sign_keypair();
}
public static function get_user_box_secret_key(string $from_make_user_box_kp): string {
return base64_encode(sodium_crypto_box_secretkey($from_make_user_box_kp));
}
public static function get_user_box_public_key(string $from_make_user_box_kp): string {
return base64_encode(sodium_crypto_box_publickey($from_make_user_box_kp));
}
public static function get_user_sign_secret_key(string $from_make_user_sign_secret_kp): string {
return sodium_crypto_sign_secretkey($from_make_user_sign_secret_kp);
}
public static function get_user_sign_public_key(string $from_make_user_sign_public_kp): string {
return sodium_crypto_sign_secretkey($from_make_user_sign_public_kp);
}
public static function box_reassemble_keypair(
string $from_get_userA_box_secret_key,
string $from_get_userB_box_public_key
): string {
return base64_encode(sodium_crypto_box_keypair_from_secretkey_and_publickey(
base64_decode($from_get_userA_box_secret_key),
base64_decode($from_get_userB_box_public_key)
));
}
public static function sign_message(#[\SensitiveParameter] string $message, #[\SensitiveParameter] string $user_sign_secretkey): string {
return sodium_crypto_sign(
$message,
$user_sign_secretkey
);
}
public static function get_signed_message(#[\SensitiveParameter] string $signed_message, #[\SensitiveParameter] string $user_sign_publickey): string | false {
return sodium_crypto_sign_open(
$signed_message,
$user_sign_publickey
);
}
public static function make_just_signature(#[\SensitiveParameter] string $message, #[\SensitiveParameter] string $user_sign_secretkey): string {
return sodium_crypto_sign_detached(
$message,
$user_sign_secretkey
);
}
public static function is_a_valid_signature(
#[\SensitiveParameter] string $signature,
#[\SensitiveParameter] string $message,
#[\SensitiveParameter] string $user_sign_public_key
): bool {
if ( sodium_crypto_sign_verify_detached(
$signature,
$message,
$user_sign_publickey
) ) {
return true;
} else {
return false;
}
}
public static function a_single_key_maker(): string {
$rnd = new \tts\random_engine();
return base64_encode($rnd->get_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES));
}
/*
* Extract the public key from the secret key
*/
public static function sign_publickey_from_secretkey(#[\SensitiveParameter] string $key) {
return sodium_crypto_sign_publickey_from_secretkey($key);
}
public static function increment_sequential_nonce($x) {
return sodium_increment($x); // The obtain the next nonce
}
// Should you Proceed with crypto_box decryption, if true go ahead do it.
public static function is_safe_to_decode($message_nonce, $expected_nonce): bool {
return (sodium_compare($message_nonce, $expected_nonce) === 0);
}
/*
* Sometimes you don't need to hide the contents of a message with encryption,
* but you still want to ensure that nobody on the network can tamper with
* it. For example, if you want to eschew server-side session storage and
* instead use HTTP cookies as your storage mechanism.
*/
public static function sign_outbound_message_only(#[\SensitiveParameter] string $message, #[\SensitiveParameter] string $key): string {
$mac = sodium_crypto_auth($message, $key);
return $mac . "@@" . $message;
}
public static function is_valid_inbound_message(#[\SensitiveParameter] string $message, #[\SensitiveParameter] string $key): bool {
$a = explode("@@", $message, 2);
$mac = $a[0];
$old_message = $a[1];
$ret_bool = (sodium_crypto_auth_verify($mac, $old_message, $key));
if ($ret_bool === false) {
sodium_memzero($key);
throw new \Exception("Malformed message or invalid MAC");
}
return $ret_bool;
}
}
/*
* Usage:
*
$key = tts\services\paragon_crypto\crypto::a_single_key_maker();
$enc = tts\services\paragon_crypto\crypto::safe_encrypt('Encrypt This String...', $key, \tts\services\paragon_crypto\crypto::single_key);
echo $enc . "<br/>";
$dec = \tts\services\paragon_crypto\crypto::safe_decrypt($enc, $key, \tts\services\paragon_crypto\crypto::single_key);
echo $dec . "<br/>";;
// --------------------
$key_pair_Alice = \tts\services\paragon_crypto\crypto::make_user_box_kp();
$secret_Alice = \tts\services\paragon_crypto\crypto::get_user_box_secret_key($key_pair_Alice);
$public_Alice = \tts\services\paragon_crypto\crypto::get_user_box_public_key($key_pair_Alice);
$key_pair_Bob = \tts\services\paragon_crypto\crypto::make_user_box_kp();
$secret_Bob = \tts\services\paragon_crypto\crypto::get_user_box_secret_key($key_pair_Bob);
$public_Bob = \tts\services\paragon_crypto\crypto::get_user_box_public_key($key_pair_Bob);
$kA = \tts\services\paragon_crypto\crypto::box_reassemble_keypair($secret_Alice, $public_Bob);
$enc = \tts\services\paragon_crypto\crypto::safe_encrypt('Encrypt This String2...', $kA, \tts\services\paragon_crypto\crypto::multiple_keys);
echo $enc . "<br/>";
$kB = \tts\services\paragon_crypto\crypto::box_reassemble_keypair($secret_Bob, $public_Alice);
$dec = \tts\services\paragon_crypto\crypto::safe_decrypt($enc, $kB, \tts\services\paragon_crypto\crypto::multiple_keys);
echo $dec;
*/

@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace CodeHydrater\services\paragon_crypto;
/**
* @copyright Paragon Initiative Enterprises
* @license Creative Commons Attribution 4.0 International / ISC
* @link https://github.com/paragonie/pecl-libsodium-doc/blob/master/chapters/09-recipes.md
*/
class password_storage {
const SALT_SIZE_IN_BYTES = 16;
private $random_engine;
public function __construct() {
$this->random_engine = new \tts\random_engine();
}
public function generate_a_key(): string {
return sodium_bin2hex($this->random_engine->get_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES));
}
/**
* Hash then encrypt a password
*
* @param string $password - The user's password
* @param string $secret_key - The main_magic key for all passwords
* @return string
*/
public function hash(#[\SensitiveParameter] string $password, #[\SensitiveParameter] string $secret_key): string {
// First, let's calculate the hash
$hashed = sodium_crypto_pwhash_scryptsalsa208sha256_str(
$password,
SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE
);
$salt = $this->random_engine->get_bytes(self::SALT_SIZE_IN_BYTES);
list ($enc_key, $auth_key) = $this->split_keys($secret_key, sodium_bin2hex($salt));
sodium_memzero($secret_key);
sodium_memzero($password);
$nonce = $this->random_engine->get_bytes(
SODIUM_CRYPTO_STREAM_NONCEBYTES
);
$cipher_text = sodium_crypto_stream_xor(
$hashed,
$nonce,
$enc_key
);
$mac = sodium_crypto_auth($nonce . $cipher_text, $auth_key);
sodium_memzero($enc_key);
sodium_memzero($auth_key);
return sodium_bin2hex($mac . $nonce . $salt . $cipher_text);
}
/**
* Decrypt then verify a password
*
* @param string $password - The user-provided password
* @param string $stored - The encrypted password hash
* @param string $secret_key - The main_magic key for all passwords
*/
public function verify(#[\SensitiveParameter] string $password, string $stored, #[\SensitiveParameter] string $secret_key): bool {
$data = sodium_hex2bin($stored);
$mac = mb_substr(
$data,
0,
SODIUM_CRYPTO_AUTH_BYTES,
'8bit'
);
$nonce = mb_substr(
$data,
SODIUM_CRYPTO_AUTH_BYTES,
SODIUM_CRYPTO_STREAM_NONCEBYTES,
'8bit'
);
$salt = mb_substr(
$data,
SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES,
self::SALT_SIZE_IN_BYTES,
'8bit'
);
$cipher_text = mb_substr(
$data,
SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES + self::SALT_SIZE_IN_BYTES,
null,
'8bit'
);
list ($enc_key, $auth_key) = $this->split_keys($secret_key, sodium_bin2hex($salt));
if (sodium_crypto_auth_verify($mac, $nonce . $cipher_text, $auth_key)) {
sodium_memzero($auth_key);
$hash_str = sodium_crypto_stream_xor($cipher_text, $nonce, $enc_key);
sodium_memzero($enc_key);
if ($hash_str !== false) {
$ret = sodium_crypto_pwhash_scryptsalsa208sha256_str_verify($hash_str, $password);
sodium_memzero($password);
return $ret;
}
} else {
sodium_memzero($auth_key);
sodium_memzero($enc_key);
}
throw new \Exception('Decryption failed.');
}
/**
* @return array(2) [encryption key, authentication key]
*/
private function split_keys(#[\SensitiveParameter] string $secret_key, #[\SensitiveParameter] string $salt): array {
$enc_key = hash_hkdf('sha256', $secret_key, SODIUM_CRYPTO_STREAM_KEYBYTES, 'encryption', $salt);
$auth_key = hash_hkdf('sha256', $secret_key, SODIUM_CRYPTO_AUTH_KEYBYTES, 'authentication', $salt);
return [$enc_key, $auth_key];
}
}
/*
$c = new \tts\services\paragon_crypto\password_storage();
$k = $c->generate_a_key();
$h = $c->hash("HelpMe", $k);
var_dump( $c->verify("HelpMe", $h, $k) );
*/

@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
/**
* @license MIT/ISC
* @author by Paragon Initiative Enterprises
* @link https://github.com/paragonie/pecl-libsodium-doc/blob/master/chapters/09-recipes.md
*
* I've added HKDF extracts a pseudorandom key (PRK) using an HMAC hash function
* (e.g. HMAC-SHA256) on salt (acting as a key) to secure the input key
*
*
* Encrypted Storage
* Problem: We want to store data in a cookie such that user cannot read nor alter its contents.
* Desired Solution: Authenticated secret-key encryption, wherein the nonce is stored with the ciphertext.
* Each encryption and authentication key should be attached to the cookie name.
* This strategy combines both sodium_crypto_stream_xor() with sodium_crypto_auth().
*/
namespace CodeHydrater\services\paragon_crypto;
class sodium_storage {
private $random_engine;
const SALT_SIZE_IN_BYTES = 16;
/**
* Sets the encryption key
*/
public function __construct(#[\SensitiveParameter] private string $key) {
$this->random_engine = new \tts\random_engine();
}
public function encrypt(#[\SensitiveParameter] string $plain_text, string $item_name = ""): string {
$nonce = $this->random_engine->get_bytes(
SODIUM_CRYPTO_STREAM_NONCEBYTES
);
$salt = $this->random_engine->get_bytes(self::SALT_SIZE_IN_BYTES);
list ($enc_key, $auth_key) = $this->split_keys($item_name, sodium_bin2hex($salt));
$cipher_text = sodium_crypto_stream_xor(
$plain_text,
$nonce,
$enc_key
);
sodium_memzero($plain_text);
$mac = sodium_crypto_auth($nonce . $cipher_text, $auth_key);
sodium_memzero($enc_key);
sodium_memzero($auth_key);
return sodium_bin2hex($mac . $nonce . $salt . $cipher_text);
}
public function decrypt(string $cypher_data, string $item_name = ""): string {
$bin_data = sodium_hex2bin($cypher_data);
$mac = mb_substr(
$bin_data,
0,
SODIUM_CRYPTO_AUTH_BYTES,
'8bit'
);
$nonce = mb_substr(
$bin_data,
SODIUM_CRYPTO_AUTH_BYTES,
SODIUM_CRYPTO_STREAM_NONCEBYTES,
'8bit'
);
$salt = mb_substr(
$bin_data,
SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES,
self::SALT_SIZE_IN_BYTES,
'8bit'
);
$cipher_text = mb_substr(
$bin_data,
SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES + self::SALT_SIZE_IN_BYTES,
null,
'8bit'
);
list ($enc_key, $auth_key) = $this->split_keys($item_name, sodium_bin2hex($salt));
if (sodium_crypto_auth_verify($mac, $nonce . $cipher_text, $auth_key)) {
sodium_memzero($auth_key);
$plaintext = sodium_crypto_stream_xor($cipher_text, $nonce, $enc_key);
sodium_memzero($enc_key);
if ($plaintext !== false) {
return $plaintext;
}
} else {
sodium_memzero($auth_key);
sodium_memzero($enc_key);
}
throw new \Exception('Decryption failed.');
}
/**
* @return array(2) [encryption key, authentication key]
*/
private function split_keys(string $item_name, #[\SensitiveParameter] string $salt): array {
$enc_key = hash_hkdf('sha256', $this->key, SODIUM_CRYPTO_STREAM_KEYBYTES, md5('encryption' . $item_name), $salt);
$auth_key = hash_hkdf('sha256', $this->key, SODIUM_CRYPTO_AUTH_KEYBYTES, md5('authentication' . $item_name), $salt);
return [$enc_key, $auth_key];
}
}
/*
* Example:
$secretkey = "78a5011b9997cd03a28a3412c66565b7c32715b35e055d7abfc228236308d3b2";
$plain_text = "Hello World!";
$sc = new \tts\services\paragon_crypto\sodium_storage($secretkey);
$index = "sensitive";
$encoded = $sc->encode($index, $plain_text);
setcookie($index, $encoded);
// On the next page load:
try {
if (!array_key_exists($index, $_COOKIE)) {
throw new \Exception('No Cookie!');
}
$data = $_COOKIE[$index];
$plain_text = $sc->decode($index, $data);
echo $plain_text;
} catch (Exception $ex) {
// Handle the exception here
echo $ex->getMessage();
}
*/

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Bob_586@Yahoo.com>
* @copyright (c) 2024, Robert Strutts
* @license MIT
*/
namespace CodeHydrater;
class String_fns {
public static function isUTF8(string $string) {
// Empty string is valid UTF-8
if ($string === '') {
return true;
}
// Convert string to array of bytes
$bytes = unpack('C*', $string);
// Pattern matching state
$state = 0;
$expectedBytes = 0;
foreach ($bytes as $byte) {
// Single byte character (0xxxxxxx)
if ($byte <= 0x7F) {
$state = 0;
continue;
}
// Start of multibyte sequence
if ($state === 0) {
// 2 bytes (110xxxxx)
if (($byte & 0xE0) === 0xC0) {
$expectedBytes = 1;
}
// 3 bytes (1110xxxx)
elseif (($byte & 0xF0) === 0xE0) {
$expectedBytes = 2;
}
// 4 bytes (11110xxx)
elseif (($byte & 0xF8) === 0xF0) {
$expectedBytes = 3;
}
// Invalid UTF-8 start byte
else {
return false;
}
$state = $expectedBytes;
continue;
}
// Continuation byte (10xxxxxx)
if (($byte & 0xC0) !== 0x80) {
return false;
}
$state--;
}
// Check if we finished the last multibyte sequence
return $state === 0;
}
// Check if string contains multibyte characters
public static function has_multibyte_chars(string $string) {
return (bool) preg_match('/[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2}/', $string);
}
// Get the length of a string
public function strlen($string) {
return strlen($string);
}
// Convert a string to lowercase
public function strtolower($string) {
return strtolower($string);
}
// Convert a string to uppercase
public function strtoupper($string) {
return strtoupper($string);
}
// Get a substring from a string
public function substr($string, $start, $length = null) {
return ($length !== null) ? substr($string, $start, $length) : substr($string, $start);
}
// Reverse a string
public function strrev($string) {
return strrev($string);
}
}

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Robert@TryingToScale.com>
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace tts;
final class tag_matches {
const tags_to_check = array('div', 'span', 'form', 'i*', 'a*', 'h1', 'p*');
/**
* Function checks tags to make sure they match.
* Used by view.php
* @param string $page
* @return array [output, alert]
*/
public static function check_tags(string $page): array {
$alert = '';
$output = '';
$l_page = \bs_tts\common::string_to_lowercase($page);
unset($page);
$assets = \bs_tts\site_helper::get_asset("uikit/css/uikit.gradient.min.css");
$ui = '<link rel="stylesheet" href="' . $assets . '/uikit/css/uikit.gradient.min.css" type="text/css" media="all" />';
$ui .= '<div class="uk-alert uk-alert-danger"><b>';
$ui_end = '</b></div>';
foreach (self::tags_to_check as $tag_name) {
if (\bs_tts\common::is_string_found($tag_name, '*')) {
$tag_name = str_replace('*', '', $tag_name);
$otag = "<{$tag_name}>"; // Open Tag
$open = substr_count($l_page, $otag); // Count open tags in page
$otag = "<{$tag_name} "; /* Open Tag with space */
$open += substr_count($l_page, $otag); // Count open tags in page
} else {
$otag = "<{$tag_name}"; // Open Tag
$open = substr_count($l_page, $otag); // Count open tags in page
}
$ctag = "</{$tag_name}>"; // Close Tag
$closed = substr_count($l_page, $ctag); // Count Close tags in page
$total_still_open = $open - $closed; // Difference of open vs. closed....
if ($total_still_open > 0) {
$msg = "{$total_still_open} possibly MISSING closing {$tag_name} !!!";
$alert .= "console.log('{$msg}');\r\n";
$output .= (\main_tts\is_live()) ? "<!-- {$msg} -->\r\n" : "{$ui}{$msg}{$ui_end}\r\n";
} elseif ($total_still_open < 0) {
$msg = abs($total_still_open) . " possibly MISSING opening {$tag_name} !!!";
$alert .= "console.log('{$msg}');\r\n";
$output .= (\main_tts\is_live()) ? "<!-- {$msg} -->\r\n" : "{$ui}{$msg}{$ui_end}\r\n";
}
}
return array('output' => $output, 'alert' => $alert);
}
}

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* @folked from https://gist.github.com/Xeoncross/1204255
* @site https://gist.github.com/vrdriver/659562a150ba955063ad849e44dac2cc
*/
namespace CodeHydrater;
final class time_zone_selection {
protected $timezones = array();
public function __construct() {
$regions = array(
'America' => \DateTimeZone::AMERICA,
'Europe' => \DateTimeZone::EUROPE,
'Africa' => \DateTimeZone::AFRICA,
'Antarctica' => \DateTimeZone::ANTARCTICA,
'Aisa' => \DateTimeZone::ASIA,
'Atlantic' => \DateTimeZone::ATLANTIC,
'Indian' => \DateTimeZone::INDIAN,
'Pacific' => \DateTimeZone::PACIFIC
);
foreach ($regions as $name => $mask) {
$zones = \DateTimeZone::listIdentifiers($mask);
foreach ($zones as $timezone) {
$this->timezones[$name][$timezone] = substr($timezone, strlen($name) + 1);
}
}
}
public function view() {
echo '<label>Select Your Timezone</label><br/><select id="timezone">';
foreach ($this->timezones as $region => $list) {
echo '<optgroup label="' . $region . '">' . "\n";
foreach ($list as $timezone => $name) {
echo '<option value="' . $timezone . '">' . $name . '</option>' . "\n";
}
echo '<optgroup>' . "\n";
}
echo '</select>';
}
}

@ -0,0 +1,274 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater;
final class time_zones {
/**
* MySQL time conversion!
* @param type $field time
* @param type $time_zone_name, leave blank for users time zone!
* @return type
*/
public static function db_convert_tz($field, $time_zone_name = '') {
$session_time_zone = (isset($_SESSION['time_zone'])) ? $_SESSION['time_zone'] : 'America/Detroit';
$tz = (!empty($time_zone_name) ) ? $time_zone_name : $session_time_zone;
$to_tz = self::current_time_for_time_zone($tz);
return "CONVERT_TZ({$field}, '+00:00', '{$to_tz}')";
}
/**
* Purpose: To convert time zone to offset time
* @param type $time_zone_name
* @return offset
*/
public static function current_time_for_time_zone($time_zone_name) {
$time = new \DateTime('now', new \DateTimeZone($time_zone_name));
return $time->format('P');
}
/**
* Purpose: To make a temporary valid timestamp for a database
*/
public static function expires_in_hours(int $hours = 1) {
$hours = ($hours > 0 && $hours < 10) ? $hours : 1;
$expires = new DateTime('NOW', new \DateTimeZone('UTC'));
$expires->add(new DateInterval("PT0{$hours}H"));
return $expires->format('Y-m-d H:i:s');
}
private static function mdy($userTime, $format) {
switch (bootstrap\common::string_to_lowercase($format)) {
case 'actions':
return $userTime->format('m-d-Y / h:iA');
case 'report':
return $userTime->format('l jS \of F\, Y\, h:i a');
case 'fancy':
return $userTime->format('l jS \of F Y h:i:s A');
case 'logging':
case 'log':
return $userTime->format('g:i A \o\n l jS F Y');
case 'legal_date':
return $userTime->format('n/j/Y');
case 'date':
return $userTime->format('m/d/Y');
case 'date-time':
return $userTime->format('m/d/Y h:i:s A');
case 'full':
return $userTime->format('m-d-Y h:i:s A');
case 'normal':
return $userTime->format('m/d/Y h:i A');
case 'default':
return $userTime->format('m-d-Y h:i A');
default:
return $userTime->format($format);
}
}
private static function dmy($userTime, $format) {
switch (bootstrap\common::string_to_lowercase($format)) {
case 'actions':
return $userTime->format('d-m-Y / h:iA');
case 'report':
return $userTime->format('l jS \of F\, Y\, h:i a');
case 'fancy':
return $userTime->format('l jS \of F Y h:i:s A');
case 'logging':
case 'log':
return $userTime->format('g:i A \o\n l jS F Y');
case 'legal_date':
return $userTime->format('j/n/Y');
case 'date':
return $userTime->format('d/m/Y');
case 'date-time':
return $userTime->format('d/m/Y h:i:s A');
case 'full':
return $userTime->format('d-m-Y h:i:s A');
case 'normal':
return $userTime->format('d/m/Y h:i A');
case 'default':
return $userTime->format('d-m-Y h:i A');
default:
return $userTime->format($format);
}
}
private static function ymd($userTime, $format) {
switch (bootstrap\common::string_to_lowercase($format)) {
case 'actions':
return $userTime->format('Y-m-d / h:iA');
case 'report':
return $userTime->format('l jS \of F\, Y\, h:i a');
case 'fancy':
return $userTime->format('l jS \of F Y h:i:s A');
case 'logging':
case 'log':
return $userTime->format('g:i A \o\n l jS F Y');
case 'legal_date':
return $userTime->format('Y/n/j');
case 'date':
return $userTime->format('Y/m/d');
case 'date-time':
return $userTime->format('Y/m/d h:i:s A');
case 'full':
return $userTime->format('Y-m-d h:i:s A');
case 'normal':
return $userTime->format('Y/m/d h:i A');
case 'default':
return $userTime->format('Y-m-d h:i A');
default:
return $userTime->format($format);
}
}
public static function dt_format($userTime, $format, $country) {
switch (bootstrap\common::string_to_lowercase($format)) {
case 'object':
return $userTime;
case 'unix':
return $userTime->format('U');
case 'day':
return $userTime->format('l');
case 'time':
return $userTime->format('h:i A');
case 'military':
return $userTime->format('H:i:s');
case 'atom':
return $userTime->format(DateTime::ATOM);
case 'cookie':
return $userTime->format(DateTime::COOKIE);
case 'iso8601':
case 'iso':
case '8601':
return $userTime->format(DateTime::ISO8601);
case 'rfc822':
return $userTime->format(DateTime::RFC822);
case 'rfc850':
return $userTime->format(DateTime::RFC850);
case 'rfc1036':
return $userTime->format(DateTime::RFC1036);
case 'rfc1123':
return $userTime->format(DateTime::RFC1123);
case 'rfc2822':
return $userTime->format(DateTime::RFC2822);
case 'rfc3339':
return $userTime->format(DateTime::RFC3339);
case 'rss':
return $userTime->format(DateTime::RSS);
case 'w3c':
return $userTime->format(DateTime::W3C);
case 'standard':
case 'computer':
case 'database':
return $userTime->format('Y-m-d H:i:s');
}
switch (bootstrap\common::string_to_lowercase($country)) {
case 'china':
return self::ymd($userTime, $format);
case 'int':
case 'other':
return self::dmy($userTime, $format);
default:
return self::mdy($userTime, $format);
}
}
/**
* Purpose: To convert a database timestamp into the users own Time Zone.
*/
public static function convert_time_zone($options) {
$format = (isset($options['format'])) ? $options['format'] : 'normal';
$session_time_zone = (isset($_SESSION['time_zone'])) ? $_SESSION['time_zone'] : 'America/Detroit';
$tz = (isset($options['time_zone']) && !empty($options['time_zone'])) ? $options['time_zone'] : $session_time_zone;
$offset = (isset($options['offset'])) ? $options['offset'] : '';
$db_time = (isset($options['time'])) ? $options['time'] : '';
if ($db_time === '0000-00-00 00:00:00') {
return false;
}
$new_time = (empty($db_time)) ? self::get_offset($offset) : self::get_offset_by($offset, $db_time);
// Convert date("U"); unix timestamps to proper format for DateTime function...
if (substr_count($new_time, ':') == 0) {
$the_time = (empty($new_time) || $new_time == 'now' || $new_time == 'current') ? gmdate("Y-m-d H:i:s") : gmdate("Y-m-d H:i:s", $new_time);
}
$userTime = new \DateTime($the_time, new \DateTimeZone('UTC'));
// Set the users time_zone to their zone
if ($tz !== 'UTC') {
$userTime->setTimezone(new \DateTimeZone($tz));
}
$country = (isset($options['country'])) ? $options['country'] : 'usa';
return self::dt_format($userTime, $format, $country);
}
public static function human_date($input_date) {
if (empty($input_date)) {
return '';
}
$today = self::convert_time_zone(array('format' => 'm/d/Y'));
$date = strtotime($input_date);
if (date('m/d/Y', $date) == $today) {
return date('g:i', $date) . "<span style=\"font-size: 0.65em; vertical-align: text-top; margin-left: 1px; text-transform: lowercase;\">" . date('A', $date) . "</span>";
} elseif (date('Y', $date) == date('Y')) {
return "<span style=\"font-weight: 500;\">" . date('M', $date) . " " . date('j', $date) . "<span style=\"font-size: 0.65em; vertical-align: text-top; margin-left: 1px;\">" . date('S', $date) . "</span></span>";
} else {
return date('m/d/y', $date);
}
}
private static function is_valid_offset($offset) {
if (substr_count($offset, 'second') > 0) {
return true;
} elseif (substr_count($offset, 'minute') > 0) {
return true;
} elseif (substr_count($offset, 'hour') > 0) {
return true;
} elseif (substr_count($offset, 'day') > 0) {
return true;
} elseif (substr_count($offset, 'week') > 0) {
return true;
} elseif (substr_count($offset, 'month') > 0) {
return true;
} elseif (substr_count($offset, 'year') > 0) {
return true;
} elseif (substr_count($offset, 'next') > 0) {
return true;
} elseif (substr_count($offset, 'last') > 0) {
return true;
} else {
return false;
}
}
private static function get_offset($offset) {
return (self::is_valid_offset($offset)) ? strtotime($offset) : $offset;
}
private static function get_offset_by($offset, $db_time) {
// strtotime requires a int timestamp
if (substr_count($db_time, ':') > 0) {
$UTC = new \DateTime($db_time, new \DateTimeZone('UTC'));
$db_time = $UTC->format('U');
}
return (self::is_valid_offset($offset)) ? strtotime($offset, $db_time) : $db_time;
}
}

@ -0,0 +1,60 @@
<?php
namespace CodeHydrater\traits;
trait Macroable
{
protected static array $macros = [];
/**
* Register a custom macro.
*/
public static function macro(string $name, mixed $macro): void
{
static::$macros[$name] = $macro;
}
/**
* Checks if macro is registered.
*/
public static function hasMacro(string $name): bool
{
return isset(static::$macros[$name]);
}
/**
* Dynamically handle calls to the class.
*/
public function __call(string $method, mixed $parameters): mixed
{
if (!static::hasMacro($method)) {
throw new \BadMethodCallException("Method {$method} does not exist.");
}
$macro = static::$macros[$method];
if ($macro instanceof \Closure) {
return call_user_func_array($macro->bindTo($this, static::class), $parameters);
}
return call_user_func_array($macro, $parameters);
}
/**
* Dynamically handle static calls to the class.
*/
public static function __callStatic(string $method, mixed $parameters): mixed
{
if (!static::hasMacro($method)) {
throw new \BadMethodCallException("Method {$method} does not exist.");
}
$macro = static::$macros[$method];
if ($macro instanceof \Closure) {
return call_user_func_array(\Closure::bind($macro, null, static::class), $parameters);
}
return call_user_func_array($macro, $parameters);
}
}

@ -50,12 +50,12 @@ trait csrf_token_functions {
public static function csrf_token_is_valid(): bool {
$is_csrf = filter_has_var(INPUT_POST, 'csrf_token');
if ($is_csrf) {
$user_token = \tts\misc::post_var('csrf_token');
$user_token = \CodeHydrater\misc::post_var('csrf_token');
$stored_token = $_SESSION['csrf_token'] ?? '';
if (empty($stored_token)) {
return false;
}
return \tts\misc::compair_it($user_token, $stored_token);
return \CodeHydrater\misc::compair_it($user_token, $stored_token);
} else {
return false;
}
@ -66,7 +66,7 @@ trait csrf_token_functions {
* @return bool
*/
public static function csrf_token_is_recent(): bool {
$max_elapsed = intval(\main_tts\configure::get(
$max_elapsed = intval(\CodeHydrater\bootstrap\configure::get(
'security',
'max_token_age'
));

@ -0,0 +1,68 @@
<?php
declare(strict_types = 1);
/**
* @author Robert Strutts <Bob_586@Yahoo.com>
* @copyright (c) 2025, Robert Strutts
* @license MIT
*/
namespace CodeHydrater;
/**
* Description of uuidv7
*
* @author Robert Strutts <Bob_586@Yahoo.com>
*/
class uuidv7 {
public static function base32EncodedUUID(): string {
$uuid = self::generateUUIDv7();
$uuidHex = str_replace('-', '', $uuid);
return self::base32Encode($uuidHex);
}
public static function generateUUIDv7(): string {
$timestamp = microtime(true) * 10000; // current timestamp in milliseconds
$timeHex = str_pad(dechex((int)$timestamp), 12, '0', STR_PAD_LEFT);
$randomHex = bin2hex(random_bytes(10));
return sprintf(
'%s-%s-%s-%s-%s',
substr($timeHex, 0, 8),
substr($timeHex, 8, 4),
'7' . substr($timeHex, 12, 3) . substr($randomHex, 0, 1), // UUIDv7
substr($randomHex, 1, 4),
substr($randomHex, 5, 12)
);
}
public static function base32Encode(string $hex): string {
$base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
$binary = '';
// Convert hex to binary
foreach (str_split($hex) as $hexChar) {
$binary .= str_pad(base_convert($hexChar, 16, 2), 4, '0', STR_PAD_LEFT);
}
// Split binary string into 5-bit chunks and convert to base32
$base32 = '';
foreach (str_split($binary, 5) as $chunk) {
$base32 .= $base32Chars[bindec(str_pad($chunk, 5, '0', STR_PAD_RIGHT))];
}
return $base32;
}
/**
* @example Database SQL:
CREATE TABLE test_table (
id CHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
*/
}

@ -0,0 +1,243 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace CodeHydrater;
final class view {
public $white_space_control = false;
public $page_output;
private $vars = [];
private $project_dir = ""; // Not used anymore
private $files = [];
private $template = false;
private $use_template_engine = false;
private $template_type = 'tpl';
protected $tempalte_engine = null;
private function get_file(string $view_file, string $default): string {
$file_ext = bootstrap\common::get_string_right($view_file, 4);
if (! bootstrap\common::is_string_found($file_ext, '.')) {
$view_file .= '.php';
}
$file = (empty($default)) ? "{$this->project_dir}/views/{$view_file}" : "{$this->project_dir}/views/{$default}/{$view_file}";
$path = bootstrap\site_helper::get_root();
$vf = $path . $file;
if ( bootstrap\requires::safer_file_exists($vf) !== false) {
return $file;
}
return '';
}
/**
* Alias to set_view
*/
public function include(string $file): void {
$this->set_view($file);
}
/**
* Alias to set_view
*/
public function add_view(string $file): void {
$this->set_view($file);
}
private function find_view_path(string $view_file) {
$found = false;
$default_paths = bootstrap\configure::get('view_mode', 'default_paths');
$get = misc::request_var('render');
if (in_array($get, $default_paths)) {
if (($key = array_search($get, $default_paths)) !== false) {
unset($default_paths[$key]); // Remove as we'll make it first later...
}
array_unshift($default_paths, $get); // Make First in Array!
}
foreach ($default_paths as $default) {
$file = $this->get_file($view_file, $default);
if ( ! empty($file) ) {
$found = true;
break;
}
}
return ($found) ? $file : false;
}
private function find_template_path($tpl_file) {
$file = "{$this->project_dir}/views/includes/{$tpl_file}";
$path = bootstrap\site_helper::get_root();
$vf = $path . $file;
return bootstrap\requires::safer_file_exists($vf);
}
/**
* Use View File
* @param string $view_file
* @param string $render_path
* @throws Exception
*/
public function set_view(string $view_file = null): void {
if ($view_file === null) {
return;
}
$file_ext = bootstrap\common::get_string_right($view_file, 4);
if (! bootstrap\common::is_string_found($file_ext, '.')) {
$file_ext = '.php';
} else if ($file_ext !== '.php') {
$this->use_template_engine = true;
$this->template_type = str_replace('.', '', $file_ext);
}
if ($file_ext === '.php') {
$file = $this->find_view_path($view_file);
} else {
$file = $this->find_template_path($view_file);
}
if ($file === false) {
echo "No view file exists for: {$view_file}!";
throw new \Exception("View File does not exist: " . $view_file);
} else {
$this->files[] = array('file'=>$file, 'path'=>"project", 'file_type'=>$file_ext);
}
}
public function clear_template(): void {
$this->template = false;
}
/**
* Use Template with view
* @param string $render_page
* @return bool was found
*/
public function set_template(string $render_page): bool {
if (! empty($render_page)) {
$render_page = str_replace('.php', '', $render_page);
$templ = "{$this->project_dir}/templates/{$render_page}";
if (bootstrap\requires::safer_file_exists(bootstrap\site_helper::get_root() . $templ. '.php') !== false) {
$this->template = $templ;
// echo $this->template;
return true;
}
}
$this->template = false;
return false;
}
private function clear_ob(int $saved_ob_level): void {
while (ob_get_level() > $saved_ob_level) {
ob_end_flush();
}
}
/**
* Sets a variable in this view with the given name and value
*
* @param mixed $name Name of the variable to set in the view, or an array of key/value pairs where each key is the variable and each value is the value to set.
* @param mixed $value Value of the variable to set in the view.
*/
public function set($name, $value = null): void {
if (is_array($name)) {
foreach ($name as $var_name => $value) {
$this->vars[$var_name] = $value;
}
} else {
$this->vars[$name] = $value;
}
}
/**
* Alias to fetch with Built-in echo
* @param type $local
* @param string $file
*/
public function render($local, string $file = null) {
echo $this->fetch($local, $file);
}
/**
* Outputs view
* @param type $local = $this
* @param $file (optional view file)
*/
public function fetch($local, string $file = null): string {
$page_output = ob_get_clean(); // Get echos before View
bootstrap\views::ob_start();
$saved_ob_level = ob_get_level();
$this->set_view($file);
unset($file);
if (!is_object($local)) {
$local = $this; // FALL Back, please use fetch($this);
}
if ($this->use_template_engine) {
$this->tempalte_engine = bootstrap\registry::get('di')->get_service('templates', [$this->template_type]);
if ($this->white_space_control) {
$this->tempalte_engine->whitespace_control();
}
}
if (count($this->files) > 0) {
foreach ($this->files as $view_file) {
if ($view_file['file_type'] == '.php') {
bootstrap\requires::secure_include($view_file['file'], $view_file['path'], $local, $this->vars); // Include the file
} else {
$template_file = str_replace('.tpl', '', $view_file['file']);
$this->tempalte_engine->parse_file($template_file);
$assigns = $this->vars['template_assigns'] ?? [];
$filters = $this->vars['template_filters'] ?? null;
$registers = $this->vars['template_registers'] ?? [];
$assigns['production'] =(\main_tts\is_live());
echo $this->tempalte_engine->render($assigns, $filters, $registers);
}
}
}
$this->clear_ob($saved_ob_level);
$page_output .= ob_get_clean();
try {
if (bootstrap\common::get_bool(bootstrap\configure::get('tts', 'check_HTML_tags')) === true) {
$tags = \tts\tag_matches::check_tags($page_output);
if (! empty($tags['output'])) {
$page_output .= $tags['output'];
$page_output .= '<script type="text/javascript">'.$tags['alert'].'</script>';
foreach($this->files as $bad) {
$page_output .= "<script type=\"text/javascript\">console.log'In view file:{$bad['file']}');\r\n</script>";
}
}
}
} catch (exceptions\Bool_Exception $e) {
if (bootstrap\configure::get('tts', 'live') === false) {
$page_output .= assets::alert('SET Config for tts: check_HTML_tags');
}
}
if ($this->template !== false && bootstrap\requires::safer_file_exists(bootstrap\site_helper::get_root() . $this->template . ".php") !== false) {
bootstrap\views::ob_start();
$saved_ob_level = ob_get_level();
$local->page_output = $page_output;
bootstrap\requires::secure_include($this->template, 'project', $local, $this->vars);
$this->clear_ob($saved_ob_level);
$page_output = ob_get_clean();
}
// Reset Files to zero, as they were outputed....
unset($this->files);
$this->files = [];
return $page_output;
}
}
Loading…
Cancel
Save