From d734efaffe406a766669c7f1443fef2f77f6495a Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 17 Dec 2025 18:11:01 -0500 Subject: [PATCH] alt_val_test --- src/bootstrap/errors.php | 6 +- src/bootstrap/main.php | 6 +- src/bootstrap/safer_io.php | 122 ++++++------- src/classes/assets.php | 4 +- src/classes/base_controller.php | 1 + src/classes/common.php | 12 ++ src/classes/enums/form_input_types.php | 35 ++++ src/classes/form_builder/html_form.php | 210 +++++++++++++++++++++++ src/classes/form_builder/html_helper.php | 160 +++++++++++++++++ 9 files changed, 481 insertions(+), 75 deletions(-) create mode 100644 src/classes/enums/form_input_types.php create mode 100644 src/classes/form_builder/html_form.php create mode 100644 src/classes/form_builder/html_helper.php diff --git a/src/bootstrap/errors.php b/src/bootstrap/errors.php index c81a7ba..f5f653d 100644 --- a/src/bootstrap/errors.php +++ b/src/bootstrap/errors.php @@ -150,7 +150,7 @@ 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()) { + if (method_exists(errors::class, "get_handle_shutdown_errors") && ! errors::get_handle_shutdown_errors()) { return false; } @@ -193,7 +193,7 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) { // Handle exceptions set_exception_handler(function($e) { - if (method_exists("errors", "get_handle_exceptions") && ! errors::get_handle_exceptions()) { + if (method_exists(errors::class, "get_handle_exceptions") && ! errors::get_handle_exceptions()) { return false; } $logMessage = " [EXCEPTION] " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine() . PHP_EOL; @@ -216,7 +216,7 @@ 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()) { + if (method_exists(errors::class, "get_handle_global_errors") && ! errors::get_handle_global_errors()) { return false; } $error = error_get_last(); diff --git a/src/bootstrap/main.php b/src/bootstrap/main.php index fde2511..dae1aae 100644 --- a/src/bootstrap/main.php +++ b/src/bootstrap/main.php @@ -12,8 +12,6 @@ namespace CodeHydrater\bootstrap; define("Memory_Baseline", memory_get_usage()); -site_helper::load_file(CodeHydrater_FRAMEWORK . 'bootstrap/errors.php'); - final class views { public static function ob_start(): void { if (extension_loaded('mbstring')) { @@ -60,6 +58,8 @@ final class errors { } } +site_helper::load_file(CodeHydrater_FRAMEWORK . 'bootstrap/errors.php'); + final class configure { private static $config = []; @@ -276,8 +276,10 @@ load_all::init(CodeHydrater_PROJECT); if (! defined('ENVIRONMENT')) { if (is_live() === false) { define('ENVIRONMENT', 'development'); + error_reporting(E_ALL); // Show Deprecated erorrs when DEVing... } else { define('ENVIRONMENT', 'production'); + error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); } } diff --git a/src/bootstrap/safer_io.php b/src/bootstrap/safer_io.php index 384d326..826763b 100644 --- a/src/bootstrap/safer_io.php +++ b/src/bootstrap/safer_io.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @author Robert Strutts * @copyright Copyright (c) 2022, Robert Strutts. - * @license https://mit-license.org/ + * @license MIT */ /* Sanitize Input, Validate data and Escape output. @@ -34,20 +34,23 @@ use \CodeHydrater\enums\FIELD_FILTER; // Defined in enum\safer_io_enums use \CodeHydrater\enums\DB_FILTER; use \CodeHydrater\enums\HTML_FLAG; use \CodeHydrater\enums\INPUTS; +use \CodeHydrater\enums\form_input_types as FORM_INPUT_TYPES; /** * use_io defines public members to be used on safer_io INPUTS */ final class use_io { public $input_var; - public $input_type; - public $field_filter; - public $escape_html; - public bool $error_on_null = false; - public $validation_rule; - public $validation_message; - public $skip_the_db; - public $use_db_filter; + public string $field_description; + public FORM_INPUT_TYPES $form_type; + public INPUTS $input_type; + public FIELD_FILTER $field_filter; + public HTML_FLAG $escape_html; + public bool $error_on_null = false; // Should be false, as Required Validation Rule will force errors any ways... + public string $validation_rule; + public array $validation_message; + public bool $skip_the_db; + public DB_FILTER $use_db_filter; } /** @@ -95,8 +98,13 @@ final class safer_io { } - // Get raw data for inputs, to be used by filters + /** + * Get raw data for inputs, to be used by filters + * @todo remove this + */ + #[Deprecated("use useio input_var instead")] public static function set_data_from(array|object $ao): void { + \CodeHydrater\common::deprecated_error("Please use useio input_var instead of set_data_from"); if (is_object($ao)) { $a = get_object_vars($ao); if ($a === false) { @@ -110,8 +118,14 @@ final class safer_io { } } - // Allow anything to set_data_inputs is desired here + /** + * Get raw data for inputs, to be used by filters + * @todo remove this + */ + #[Deprecated("use useio input_var instead")] public static function set_data_input(string $var_name, mixed $data_in): void { + \CodeHydrater\common::deprecated_error("Please use useio input_var instead of: set_data_input"); + if (! isset(self::$DATA_INPUTS[$var_name])) { self::$DATA_INPUTS[$var_name] = $data_in; } @@ -453,7 +467,7 @@ final class safer_io { } private static function sanitize_helper( - string $from, + string $for_what, string $input_field_name, use_io $a, FIELD_FILTER $default_filter = FIELD_FILTER::raw_string, @@ -466,6 +480,20 @@ final class safer_io { $rules = []; $messages = []; + if (isset($a->field_description)) { + $meta['field_desc'][$input_field_name] = $a->field_description; + } + + if (isset($a->validation_rule)) { + if (str_contains($a->validation_rule, "required")) { + $meta['required'][$input_field_name] = true; + } + } + + if (isset($a->form_type)) { + $meta['form_type'][$input_field_name] = $a->form_type ?? FORM_INPUT_TYPES::text->value; + } + if (isset($a->field_filter) && $a->field_filter instanceof \UnitEnum) { $field_type = $a->field_filter; } else { @@ -498,7 +526,6 @@ final class safer_io { } $meta[$input_field_name]['validation_rules_set'] = (count($rules)) ? true : false; - $db = (isset($a->skip_the_db)) ? $a->skip_the_db : false; $meta[$input_field_name]['type'] = $field_type->name; $meta[$input_field_name]['skip_db'] = $db; @@ -539,7 +566,7 @@ final class safer_io { } } - if ($from === "html") { + if ($for_what === "html") { $safer_html = self::get_safer_html($safer_data, $a); if ($safer_html !== false) { $safer_html_data = $safer_html; @@ -558,7 +585,7 @@ final class safer_io { if ($field_type == FIELD_FILTER::floating_point) { $safer_data = floatval($safer_data); } - if ($from === "db") { + if ($for_what === "db") { if ($field_type == FIELD_FILTER::integer_number || $field_type == FIELD_FILTER::floating_point) { $safer_db_data = $safer_data; } else { @@ -575,13 +602,13 @@ final class safer_io { } $ret['name'] = $input_field_name; $ret['meta'] = $meta; - if ($from === "db") { + if ($for_what === "db") { $ret['db'] = $safer_db_data; $data[$input_field_name] = $safer_db_data; - } elseif ($from === "logic") { + } elseif ($for_what === "logic") { $ret['logic'] = $safer_data; $data[$input_field_name] = $safer_data; - } elseif ($from === "html") { + } elseif ($for_what === "html") { $ret['html'] = $safer_html_data; $data[$input_field_name] = $safer_html_data; } @@ -601,69 +628,28 @@ final class safer_io { return str_replace(chr(0), '', $input); } - public static function db_sanitize( - array $inputs, - FIELD_FILTER $default_filter = FIELD_FILTER::raw_string, - bool $trim = true, - ) : \Generator { - foreach ($inputs as $input_field_name => $a) { - if (! $a instanceof use_io) { - continue; - } - $yield = static::sanitize_helper( - "db", - $input_field_name, - $a, - $default_filter, - $trim - ); - yield $yield; - } - } - - public static function logic_sanitize( - array $inputs, - FIELD_FILTER $default_filter = FIELD_FILTER::raw_string, - bool $trim = true, - ) : \Generator { - foreach ($inputs as $input_field_name => $a) { - if (! $a instanceof use_io) { - continue; - } - $yield = static::sanitize_helper( - "logic", - $input_field_name, - $a, - $default_filter, - $trim - ); - yield $yield; - } - } - - /** - * Sanitize the inputs based on the rules an optionally trim the string - * @param FIELD_FILTER $default_filter FILTER_SANITIZE_STRING - * @param bool $trim - * @return Generator - */ - public static function html_escape_and_sanitize( + public static function esv( array $inputs, + string $for_what = "html", FIELD_FILTER $default_filter = FIELD_FILTER::raw_string, bool $trim = true, ) : \Generator { + $what = match($for_what) { + "db" => "db", + "logic" => "logic", + default => "html", + }; foreach ($inputs as $input_field_name => $a) { if (! $a instanceof use_io) { continue; } - $yield = static::sanitize_helper( - "html", + yield static::sanitize_helper( + $what, $input_field_name, $a, $default_filter, $trim ); - yield $yield; } } diff --git a/src/classes/assets.php b/src/classes/assets.php index 6723cc1..35b4274 100644 --- a/src/classes/assets.php +++ b/src/classes/assets.php @@ -172,7 +172,7 @@ final class assets { * @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 { + public static function wrap_js(string $file, string $scope = 'project', array $a = array()): string { $more = ''; if (count($a)) { foreach ($a as $k => $v) { @@ -185,7 +185,7 @@ final class assets { if ($ver === false || $wrap_file === false) { return ""; } - return "\r\n"; + return "\r\n"; //return ""; } diff --git a/src/classes/base_controller.php b/src/classes/base_controller.php index 2ca5a07..4c903e6 100644 --- a/src/classes/base_controller.php +++ b/src/classes/base_controller.php @@ -20,6 +20,7 @@ use \CodeHydrater\http\response as Response; * @author Robert Strutts */ class base_controller { + public string $page_output = ''; // To keep views working...without Dynamic Variable Error! public static $params; // To keep Routes working... public $view; public $html; diff --git a/src/classes/common.php b/src/classes/common.php index bba42f2..1545fea 100644 --- a/src/classes/common.php +++ b/src/classes/common.php @@ -89,6 +89,18 @@ final class common { } return $subject; } + + public static function deprecated_error(string $msg): void { + if (\CodeHydrater\bootstrap\configure::get('security', 'show_dumps') !== true) { + return; // Avoid Live Deprecated Errors + } + if (\PHP_VERSION_ID < 80400) { + trigger_error($msg); + echo "
";
+            print_r(debug_backtrace()[1]);
+            echo "
"; + } + } // Begin Strings Functions here:: public static function string_position(string $string, string $needle, int $offset = 0, $encoding = null) { diff --git a/src/classes/enums/form_input_types.php b/src/classes/enums/form_input_types.php new file mode 100644 index 0000000..0a90400 --- /dev/null +++ b/src/classes/enums/form_input_types.php @@ -0,0 +1,35 @@ + + * @copyright (c) 2025, Robert Strutts + * @license MIT + */ +namespace CodeHydrater\enums; + +enum form_input_types: string { + case none = "none"; // Don't Display control + case checkbox ="checkbox"; + case color = "color"; + case date = "date"; + case datetime = "datetime-local"; + case email = "email"; + case file = "file"; + case hidden = "hidden"; + case image = "image"; + case month = "month"; + case number = "number"; + case password = "password"; + case radio = "radio"; + case range = "range"; + case reset = "rest"; + case search = "search"; + case submit = "submit"; + case tel = "tel"; + case text = "text"; + case time = "time"; + case url = "url"; + case week = "week"; +} \ No newline at end of file diff --git a/src/classes/form_builder/html_form.php b/src/classes/form_builder/html_form.php new file mode 100644 index 0000000..41cfcd6 --- /dev/null +++ b/src/classes/form_builder/html_form.php @@ -0,0 +1,210 @@ + + * @copyright (c) 2020 Elusive, Modifications by (c) 2025 Robert Strutts + * @link https://github.com/elusivecodes/FyreFormBuilder/blob/main/LICENSE + * @license MIT + */ +namespace CodeHydrater\form_builder; + +use \CodeHydrater\form_builder\html_helper as Helper; +use CodeHydrater\enums\form_input_types as FORM_TYPE; + +class html_form { + private $output = ""; + private bool $escape = true; + + public function __construct(protected Helper $html = new Helper()) {} + + public function get_output(): ?string { + return $this->output; + } + + public function set_escape(bool $escape = true) { + $this->escape = $escape; + } + + private function append_out(?string $out, int $tabs = 2): ?string { + $this->output .= str_repeat("\t", $tabs) . $out . PHP_EOL; + return $out; + } + + public function __call(string $type, array $arguments): string { + // Get all valid values + $validTypes = array_column(FORM_TYPE::cases(), 'value'); + if (!in_array($type, $validTypes, true)) { + throw new \InvalidArgumentException( + "Invalid input type: '$type'. Must be one of: " . + implode(', ', $validTypes) + ); + } + + if ($type === "none") { + return ""; + } + + $name = array_shift($arguments); + $options = array_shift($arguments) ?? []; + + $options['type'] = $type; + + return $this->input($name, $options); + } + + private function do_escape(string $content, array & $options): ?string { + $escape = $options['escape'] ?? $this->escape; + if ($escape) { + return $this->html->escape($content); + } + return $content; + } + + public function button(string $content = '', array $options = []): string + { + $options['type'] ??= 'button'; + return $this->append_out('html->attributes($options).'>'.$this->do_escape($content, $options).''); + } + + public function close(): string + { + return $this->append_out('', 0); + } + + public function fieldset_close(): string + { + return $this->append_out(''); + } + + public function fieldset_open(array $options = []): string + { + return $this->append_out('html->attributes($options).'>'); + } + + public function input(string|null $name = null, array $options = []): string + { + if ($name !== null) { + $options['name'] ??= $name; + } + + $options['type'] ??= 'text'; + + return $this->append_out('html->attributes($options).' />'); + } + + public function open_div(array $options = []): string + { + return $this->append_out('html->attributes($options).'>'); + } + + public function close_div(): string + { + return $this->append_out(''); + } + + public function label(string $content = '', array $options = []): string + { + return $this->append_out('html->attributes($options).'>'.$this->do_escape($content, $options).''); + } + + public function legend(string $content = '', array $options = []): string + { + return $this->append_out('html->attributes($options).'>'.$this->do_escape($content, $options).''); + } + + public function open(string|null $action = null, array $options = []): string + { + if ($action !== null) { + $options['action'] ??= $action; + } + + $options['method'] ??= 'post'; + $options['accept-charset'] ??= $this->html->get_charset(); + + return $this->append_out('html->attributes($options).'>', 0); + } + + public function open_multipart(string|null $action = null, array $options = []): string + { + $options['enctype'] = 'multipart/form-data'; + return $this->append_out($this->open($action, $options)); + } + + public function select(string|null $name = null, array $options = []): string + { + if ($name !== null) { + $options['name'] ??= $name; + } + + $select_options = $options['options'] ?? []; + unset($options['options']); + + $selected = $options['value'] ?? []; + unset($options['value']); + + if (!is_array($selected)) { + $selected = [$selected]; + } + + $html = 'html->attributes($options).'>'; + $html .= $this->build_options($select_options, $selected); + $html .= ''; + + return $this->append_out($html); + } + + public function select_multi(string|null $name = null, array $options = []): string + { + $options[] = 'multiple'; + return $this->append_out($this->select($name, $options)); + } + + public function textarea(string|null $name = null, array $options = []): string + { + if ($name !== null) { + $options['name'] ??= $name; + } + + $value = $options['value'] ?? ''; + unset($options['value']); + + return $this->append_out('html->attributes($options).'>'.$this->html->escape($value).''); + } + + protected function build_options(array $options, array $selected): string + { + $html = ''; + + foreach ($options as $value => $option) { + if (!is_array($option)) { + $option = [ + 'label' => $option, + ]; + } + + if (array_key_exists('children', $option)) { + $children = $option['children']; + unset($option['children']); + + $html .= 'html->attributes($option).'>'; + $html .= $this->build_options($children, $selected); + $html .= ''; + } else { + $option['value'] ??= $value; + $label = $option['label'] ?? $option['value']; + unset($option['label']); + + if (in_array($option['value'], $selected)) { + $option[] = 'selected'; + } + + $html .= 'html->attributes($option).'>'.$this->html->escape($label).''; + } + } + + return $html; + } + +} diff --git a/src/classes/form_builder/html_helper.php b/src/classes/form_builder/html_helper.php new file mode 100644 index 0000000..d97f0bd --- /dev/null +++ b/src/classes/form_builder/html_helper.php @@ -0,0 +1,160 @@ + + * @copyright (c) 2020 Elusive, Modifications by (c) 2025 Robert Strutts + * @link https://github.com/elusivecodes/FyreFormBuilder/blob/main/LICENSE + * @license MIT + */ +namespace CodeHydrater\form_builder; + +class html_helper { + + protected const ATTRIBUTES_ORDER = [ + 'action', + 'class', + 'id', + 'placeholder', + 'required', + 'disabled', + 'readonly', + 'maxlength', + 'minlength', + 'min', + 'max', + 'step', + 'pattern', + 'autocomplete', + 'size', + 'name', + 'data-', + 'src', + 'for', + 'type', + 'href', + 'action', + 'method', + 'value', + 'title', + 'alt', + 'target', + 'role', + 'aria-', + 'accept-charset', + 'charset', + 'style', + ]; + + protected string $charset = 'UTF-8'; + + /** + * Generate an attribute string. + * + * @param array $options The attributes. + * @return string The attribute string. + */ + public function attributes(array $options = []): string { + unset($options['escape']); + $attributes = []; + + foreach ($options as $key => $value) { + if ($value === null) { + continue; + } + + if (is_numeric($key)) { + $key = $value; + $value = null; + } + + $key = preg_replace('/[^\w\-:.@]/', '', $key); + $key = strtolower($key); + + if (!$key) { + continue; + } + + if (is_bool($value)) { + $value = $value ? null : 'false'; + } else if (is_array($value) || is_object($value)) { + $value = json_encode($value); + $value = $this->escape($value); + } else if ($value !== null) { + $value = $this->escape((string) $value); + } + + $attributes[$key] = $value; + } + + if ($attributes === []) { + return ''; + } + + uksort( + $attributes, + static fn(string $a, string $b): int => static::attribute_index($a) <=> static::attribute_index($b) + ); + + $html = ''; + + foreach ($attributes as $key => $value) { + if (str_contains($key, "data-") || str_contains($key, "aria-")) { + // Okay, it's valid... + } else { + if (!in_array($key, static::ATTRIBUTES_ORDER)) { + continue; // Not in the list, skip it + } + } + + if ($value !== null) { + $html .= ' '.$key.'="'.$value.'"'; + } else { + $html .= ' '.$key; + } + } + + return $html; + } + + /** + * Escape characters in a string for use in HTML. + * + * @param string $string The input string. + * @return string The escaped string. + */ + public function escape(string $string): string { + return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE | \ENT_HTML5, $this->charset); + } + + public function get_charset(): string { + return $this->charset; + } + + public function set_charset(string $charset): static { + $this->charset = $charset; + + return $this; + } + + /** + * Get the index for an attribute. + * + * @param string $attribute The attribute name. + * @return int The attribute index. + */ + protected static function attribute_index(string $attribute): int { + if (preg_match('/^(data|aria)-/', $attribute)) { + $attribute = substr($attribute, 0, 5); + } + + $index = array_search($attribute, static::ATTRIBUTES_ORDER); + + if ($index === false) { + return count(static::ATTRIBUTES_ORDER); + } + + return $index; + } +} \ No newline at end of file