diff --git a/src/bootstrap/main.php b/src/bootstrap/main.php index 531459c..1a96c66 100644 --- a/src/bootstrap/main.php +++ b/src/bootstrap/main.php @@ -268,15 +268,6 @@ final class di { // Initialize our Dependency Injector registry::set('di', new di()); -// Setup php for working with Unicode data, if possible -if (extension_loaded('mbstring')) { - mb_internal_encoding('UTF-8'); - mb_http_output('UTF-8'); - mb_language('uni'); - mb_regex_encoding('UTF-8'); - setlocale(LC_ALL, "en_US.UTF-8"); -} - require_once CodeHydrater_FRAMEWORK . 'bootstrap/common.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/requires.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/auto_loader.php'; diff --git a/src/classes/database/model.php b/src/classes/database/model.php index 265e4ae..5de15dd 100644 --- a/src/classes/database/model.php +++ b/src/classes/database/model.php @@ -357,7 +357,7 @@ class model { * This FN requires SSL connection to the database. * So, the KEY does not get exposed!!!! */ - public function mysql_aes_encode(string $data, string $key, bool $bind = true) { + public function mysql_aes_encode(#[\SensitiveParameter] string $data, #[\SensitiveParameter] string $key, bool $bind = true) { $safe_text = addslashes($data); $safe_key = addslashes($key); $parm = ($bind) ? ":{$safe_text}" : "'{$safe_text}'"; @@ -374,7 +374,7 @@ class model { * This FN requires SSL connection to the database. * So, the KEY does not get exposed!!!! */ - public function mysql_aes_decode(string $field_name, string $key, bool $add_as = false) { + public function mysql_aes_decode(string $field_name, #[\SensitiveParameter] string $key, bool $add_as = false) { $safe_field = addslashes($field_name); $safe_key = addslashes($key); $as = ($add_as === true) ? " AS `{$safe_field}`" : ""; diff --git a/src/classes/mb_strings_fns.php b/src/classes/mb_strings_fns.php deleted file mode 100644 index 5c8dc2c..0000000 --- a/src/classes/mb_strings_fns.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @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); - } -} - diff --git a/src/classes/string_fns.php b/src/classes/string_fns.php deleted file mode 100644 index c0d3b84..0000000 --- a/src/classes/string_fns.php +++ /dev/null @@ -1,98 +0,0 @@ - - * @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); - } -} - diff --git a/src/classes/strings/mb_string_fns.php b/src/classes/strings/mb_string_fns.php new file mode 100644 index 0000000..f303fff --- /dev/null +++ b/src/classes/strings/mb_string_fns.php @@ -0,0 +1,159 @@ + + * @copyright (c) 2024, Robert Strutts + * @license MIT + */ + +namespace CodeHydrater\strings; + +class mb_string_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'); + } + + // Return character by Unicode code point value + public static function chr(int $codepoint, ?string $encoding = null): string|false { + return mb_chr($codepoint, $encoding); + } + + // Parse GET/POST/COOKIE data and set global variable + public static function parse_str(string $string, array &$result): bool { + return mb_parse_str($string, $result); + } + + // Strip whitespace (or other characters) from the end of a string + public static function rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { + return mb_rtrim($string, $characters, $encoding); + } + + // Strip whitespace (or other characters) from the beginning of a string + public static function ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { + return mb_ltrim($string, $characters, $encoding); + } + + // Finds position of first occurrence of a string within another, case insensitive + public static function stripos( + string $haystack, + string $needle, + int $offset = 0, + ?string $encoding = null +): int|false { + return mb_stripos($haystack, $needle, $offset, $encoding); + } + + // Finds first occurrence of a string within another, case insensitive + public static function stristr( + string $haystack, + string $needle, + bool $before_needle = false, + ?string $encoding = null +): string|false { + return mb_stristr($haystack, $needle, $before_needle, $encoding); + } + + // Find position of first occurrence of string in a string + public static function strpos( + string $haystack, + string $needle, + int $offset = 0, + ?string $encoding = null +): int|false { + return mb_strpos($haystack, $needle, $offset, $encoding); + } + + // Finds the last occurrence of a character in a string within another + public static function strrchr( + string $haystack, + string $needle, + bool $before_needle = false, + ?string $encoding = null +): string|false { + return mb_strrchr($haystack, $needle, $before_needle, $encoding); + } + + // Finds the last occurrence of a character in a string within another, case insensitive + public static function strrichr( + string $haystack, + string $needle, + bool $before_needle = false, + ?string $encoding = null +): string|false { + return mb_strrichr($haystack, $needle, $before_needle, $encoding); + } + + // Finds position of last occurrence of a string within another, case insensitive + public static function strripos( + string $haystack, + string $needle, + int $offset = 0, + ?string $encoding = null +): int|false { + return mb_strripos($haystack, $needle, $offset, $encoding); + } + + // Find position of last occurrence of a string in a string + public static function strrpos( + string $haystack, + string $needle, + int $offset = 0, + ?string $encoding = null +): int|false { + return mb_strrpos($haystack, $needle, $offset, $encoding); + } + + // Finds first occurrence of a string within another + public static function strstr( + string $haystack, + string $needle, + bool $before_needle = false, + ?string $encoding = null +): string|false { + return mb_strstr($haystack, $needle, $before_needle, $encoding); + } + + // Strip whitespace (or other characters) from the beginning and end of a string + public static function trim(string $string, ?string $characters = null, ?string $encoding = null): string { + return mb_trim($string, $characters, $encoding); + } + + // Make a string's first character uppercase + public static function ucfirst(string $string, ?string $encoding = null): string { + return mb_ucfirst($string, $encoding); + } + + // Override to get the length of a string with multibyte support + public static function strlen($string) { + return mb_strlen($string); + } + + // Override to convert a string to lowercase with multibyte support + public static function strtolower($string) { + return mb_strtolower($string); + } + + // Override to convert a string to uppercase with multibyte support + public static function strtoupper($string) { + return mb_strtoupper($string); + } + + // Override to get part/substring from a string with multibyte support + public static function substr($string, $start, $length = null) { + return ($length !== null) ? mb_substr($string, $start, $length) : mb_substr($string, $start); + } + + // Count the number of substring occurrences + public static function substr_count(string $haystack, string $needle, ?string $encoding = null): int { + return mb_substr_count($haystack, $needle, $encoding); + } +} + diff --git a/src/classes/strings/string_facade.php b/src/classes/strings/string_facade.php new file mode 100644 index 0000000..18704df --- /dev/null +++ b/src/classes/strings/string_facade.php @@ -0,0 +1,60 @@ + + * @copyright (c) 2025, Robert Strutts + * @license MIT + */ +namespace CodeHydrater\strings; + +class string_facade { + + private static ?bool $mb_default = null; + + /** + * Set the default multibyte behavior. + * Pass `true` to force multibyte, `false` to force single-byte, or `null` to auto-detect. + */ + public static function set_multibyte_default(?bool $flag): void { + self::$mb_default = $flag; + } + + protected static function is_multibyte($str) { + // Use override if set + if (self::$mb_default !== null) { + return self::$mb_default; + } + + $enabled = extension_loaded('mbstring'); + if ($enabled === false) { + return false; + } + return mb_detect_encoding($str, mb_detect_order(), true) !== false && + preg_match('/[^\x00-\x7F]/', $str); + } + + protected static function get_class($str) { + return self::is_multibyte($str) ? 'mb_string_fns' : 'string_fns'; + } + + public static function get_fn($method, ...$args) { + if (empty($args)) { + throw new InvalidArgumentException("At least one argument is required for multibyte check."); + } + + $class = "\\CodeHydrater\\strings\\" . self::get_class($args[0]); + + if (!method_exists($class, $method)) { + throw new BadMethodCallException("Method $method does not exist in class $class."); + } + + return call_user_func_array([$class, $method], $args); + } + + // Optional: Static passthrough + public static function __callStatic($method, $args) { + return self::get_fn($method, ...$args); + } +} diff --git a/src/classes/strings/string_fns.php b/src/classes/strings/string_fns.php new file mode 100644 index 0000000..fb78f8a --- /dev/null +++ b/src/classes/strings/string_fns.php @@ -0,0 +1,200 @@ + + * @copyright (c) 2024, Robert Strutts + * @license MIT + */ + +namespace CodeHydrater\strings; + +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); + } +// Return character by Unicode code point value + public static function chr(int $codepoint): string|false { + return chr($codepoint); + } + + // Parse GET/POST/COOKIE data and set global variable + public static function parse_str(string $string, array &$result): bool { + return parse_str($string, $result); + } + + // Strip whitespace (or other characters) from the end of a string + public static function rtrim(string $string, ?string $characters = " \n\r\t\v\x00"): string { + return rtrim($string, $characters); + } + + // Strip whitespace (or other characters) from the beginning of a string + public static function ltrim(string $string, ?string $characters = " \n\r\t\v\x00"): string { + return ltrim($string, $characters); + } + + // Finds position of first occurrence of a string within another, case insensitive + public static function stripos( + string $haystack, + string $needle, + int $offset = 0 +): int|false { + return stripos($haystack, $needle, $offset); + } + + // Finds first occurrence of a string within another, case insensitive + public static function stristr( + string $haystack, + string $needle, + bool $before_needle = false +): string|false { + return stristr($haystack, $needle, $before_needle); + } + + // Find position of first occurrence of string in a string + public static function strpos( + string $haystack, + string $needle, + int $offset = 0 +): int|false { + return strpos($haystack, $needle, $offset); + } + + // Finds the last occurrence of a character in a string within another + public static function strrchr( + string $haystack, + string $needle, + bool $before_needle = false, +): string|false { + return strrchr($haystack, $needle, $before_needle); + } + + // Finds the last occurrence of a character in a string within another, case insensitive + public static function sstrrichr( + string $haystack, + string $needle, + bool $before_needle = false, +): string|false { + return strrichr($haystack, $needle, $before_needle); + } + + // Finds position of last occurrence of a string within another, case insensitive + public static function strripos( + string $haystack, + string $needle, + int $offset = 0, +): int|false { + return strripos($haystack, $needle, $offset); + } + + // Find position of last occurrence of a string in a string + public static function strrpos( + string $haystack, + string $needle, + int $offset = 0, +): int|false { + return strrpos($haystack, $needle, $offset); + } + + // Finds first occurrence of a string within another + public static function strstr( + string $haystack, + string $needle, + bool $before_needle = false, +): string|false { + return strstr($haystack, $needle, $before_needle); + } + + // Strip whitespace (or other characters) from the beginning and end of a string + public static function trim(string $string, ?string $characters = " \n\r\t\v\x00"): string { + return trim($string, $characters); + } + + // Make a string's first character uppercase + public static function ucfirst(string $string): string { + return ucfirst($string); + } + + // Override to get the length of a string with multibyte support + public static function strlen($string) { + return strlen($string); + } + + // Override to convert a string to lowercase with multibyte support + public static function strtolower($string) { + return strtolower($string); + } + + // Override to convert a string to uppercase with multibyte support + public static function strtoupper($string) { + return strtoupper($string); + } + + // Override to get part/substring from a string with multibyte support + public static function substr($string, $start, $length = null) { + return ($length !== null) ? substr($string, $start, $length) : substr($string, $start); + } + + // Count the number of substring occurrences + public static function substr_count(string $haystack, string $needle): int { + return substr_count($haystack, $needle); + } +} +