main
Robert 5 months ago
parent dde2638f10
commit 8b02110167
  1. 8
      docs/TODO.md
  2. 158
      src/bootstrap/errors.php
  3. 29
      src/bootstrap/main.php
  4. 12
      src/bootstrap/site_helper.php
  5. 4
      src/classes/database/model.php
  6. 31
      src/classes/database/paginate.php
  7. 2
      src/classes/html_document.php
  8. 7
      src/classes/misc.php
  9. 2
      src/classes/router.php
  10. 19
      src/classes/security.php
  11. 4
      src/classes/view.php

@ -15,7 +15,7 @@
[x] → Macros [x] → Macros
[ ] → Routes [x] → Routes
[x] → Controllers [x] → Controllers
@ -31,11 +31,11 @@
[ ] → Main Project Tempates [ ] → Main Project Tempates
[o] → 404 Pages/Dev/Prod Errors [x] → 404 Pages/Dev/Prod Errors
[x] → CLI Detect [x] → CLI Detect
[ ] → Paginator [x] → Paginator
[x] → Safer I/O [x] → Safer I/O
@ -53,7 +53,7 @@
[x] → Error Handler [x] → Error Handler
[ ] → CSRF Tokens [x] → CSRF Tokens
[x] → Password Hashing/Verify [x] → Password Hashing/Verify

@ -11,9 +11,6 @@ if (! defined('BaseDir')) {
define('LOG_FILE', BaseDir . '/protected/logs/error_log.txt'); define('LOG_FILE', BaseDir . '/protected/logs/error_log.txt');
} }
if (! defined('ENVIRONMENT')) {
define('ENVIRONMENT', 'production'); // 'production' or 'development'
}
/** /**
* Format message with appropriate colors based on environment * Format message with appropriate colors based on environment
*/ */
@ -82,11 +79,18 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) {
file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); file_put_contents(LOG_FILE, $logMessage, FILE_APPEND);
} }
// Display in development environment // Display in development environment
if (ENVIRONMENT === 'development') { if (fallback_dev()) {
echo formatMessage($displayMessage, $errorCategory); echo formatMessage($displayMessage, $errorCategory);
// Prevent PHP's default error handler
return true;
} }
// Prevent PHP's default error handler if (is_on_error_page() === true) {
if (fallback_requires('prod_error.php') === false) {
fallback_requires('views/on_error/prod_error.php', true);
}
exit(1); // Prevent HTML Looping!!!
}
return true; return true;
}); });
@ -101,7 +105,8 @@ set_exception_handler(function($e) {
if (LOG_FILE !== false) { if (LOG_FILE !== false) {
file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); file_put_contents(LOG_FILE, $logMessage, FILE_APPEND);
} }
if (ENVIRONMENT === 'development') { // Are we developing...? if so, show details on error
if (fallback_dev()) {
echo formatMessage($displayMessage, 'error'); echo formatMessage($displayMessage, 'error');
} else { } else {
// In production, show user-friendly message // In production, show user-friendly message
@ -124,12 +129,151 @@ register_shutdown_function(function() {
if (LOG_FILE !== false) { if (LOG_FILE !== false) {
file_put_contents(LOG_FILE, $logMessage, FILE_APPEND); file_put_contents(LOG_FILE, $logMessage, FILE_APPEND);
} }
if (ENVIRONMENT === 'development') { // If developing, show error
if (fallback_dev()) {
echo formatMessage($displayMessage, 'error'); echo formatMessage($displayMessage, 'error');
} }
} }
}); });
function fallback_is_live(): bool {
return is_live(); // from main.php
}
/**
* Displays Error page
* @param type $ex error message
*/
function broken_error($ex = ''): void {
if (fallback_is_cli()) {
echo $ex;
exit(1);
}
$use_api = fallback_is_api();
if ($use_api === true) {
$internal_error = "An unexpected error occured.";
$status_code = 500;
fallback_json_error($internal_error, $status_code);
}
if (fallback_is_live()) {
// Try really Hard to display Prod Error Page
$exists = fallback_requires('prod_error.php');
if (! $exists) {
$exists = fallback_requires('views/on_error/prod_error.php', true);
}
if ($exists === false) {
echo "<h1>Sorry, we had an error...</h1><p>We apologize for any inconvenience this may cause.</p>";
}
} else {
if ($ex === "") {
echo "<h2>Unknown Issue...</h2>";
} else {
echo $ex;
}
echo "<h1>Error Page</h1><p>Please go back to another page...</p>";
}
}
function fallback_json_error(string $data, int $status_code): void {
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . " " . $status_code);
/*
* Allow JavaScript from anywhere. CORS - Cross Origin Resource Sharing
* @link https://manning-content.s3.amazonaws.com/download/f/54fa960-332e-4a8c-8e7f-1eb213831e5a/CORS_ch01.pdf
*/
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: *");
header('Content-Type: application/json; charset=utf-8', true, intval($status_code));
}
echo json_encode($data);
exit;
}
function fallback_is_api(): bool {
$uri = fallback_get_uri();
if (preg_match('~/api(\d*)/~', $uri)) {
return true;
}
return false;
}
function fallback_is_cli() {
if (defined('STDIN')) {
return true;
}
if (php_sapi_name() === 'cli') {
return true;
}
if (array_key_exists('SHELL', $_ENV)) {
return true;
}
if (!isset($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['HTTP_USER_AGENT'])) {
return true;
}
if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
return true;
}
return false;
}
function fallback_get_uri(): ?string {
return strtok($_SERVER['REQUEST_URI']);
}
function fallback_dev(): bool {
if (! defined("ENVIRONMENT")) {
return false; // Force Live on unknown state
}
if (ENVIRONMENT === 'development') {
return true;
}
return false;
}
function fallback_requires(string $file, bool $fw = false): bool {
$exists = method_exists("\CodeHydrater\bootstrap\requires", "secure_include");
if ($exists) {
if ($fw === false) {
$path = UseDir::ONERROR;
} else {
$path = UseDir::FRAMEWORK;
}
$good = \CodeHydrater\bootstrap\requires::secure_include($file, $path);
return ($good === false) ? false : true; // handle contents vs. failure = false
} else {
if ($fw === false) {
$view = CodeHydrater_PROJECT . "views/on_error/" . $file;
} else {
$view = CodeHydrater_FRAMEWORK . $file;
}
if (file_exists($view)) {
include $view;
return true;
}
}
return false; // You have failed me for the last time! LOL
}
/**
* Check if already on Error Page
* @return bool
*/
function is_on_error_page(): bool {
if (fallback_is_cli()) {
exit(1);
} else {
return (stripos(fallback_get_uri(), "error.html") !== false);
}
}
// Test the error handler (uncomment to test) // Test the error handler (uncomment to test)
// trigger_error("This is a test warning", E_USER_WARNING); // trigger_error("This is a test warning", E_USER_WARNING);
// throw new Exception("This is a test exception"); // throw new Exception("This is a test exception");

@ -40,10 +40,17 @@ if (site_helper::get_testing() === false) {
} }
final class errors { final class errors {
private static $is_live = true; // Fall Back Value if Config Not Found
private static $handle_global_errors = true; private static $handle_global_errors = true;
private static $handle_shutdown_errors = true; private static $handle_shutdown_errors = true;
private static $handle_exceptions = true; private static $handle_exceptions = true;
public static function set_live_mode(bool $is_live): void {
self::$is_live = $is_live;
}
public static function get_live_mode(): bool {
return self::$is_live;
}
public static function set_handle_shutdown_errors(bool $do_handle_errors): void { public static function set_handle_shutdown_errors(bool $do_handle_errors): void {
self::$handle_shutdown_errors = $do_handle_errors; self::$handle_shutdown_errors = $do_handle_errors;
} }
@ -274,10 +281,6 @@ if (extension_loaded('mbstring')) {
setlocale(LC_ALL, "en_US.UTF-8"); setlocale(LC_ALL, "en_US.UTF-8");
} }
function is_live() {
return (common::get_bool(configure::get('CodeHydrater', 'live')));
}
require_once CodeHydrater_FRAMEWORK . 'bootstrap/common.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/common.php';
require_once CodeHydrater_FRAMEWORK . 'bootstrap/requires.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/requires.php';
require_once CodeHydrater_FRAMEWORK . 'bootstrap/auto_loader.php'; require_once CodeHydrater_FRAMEWORK . 'bootstrap/auto_loader.php';
@ -290,6 +293,24 @@ registry::get('loader')->add_namespace("Project", CodeHydrater_PROJECT);
load_all::init(CodeHydrater_PROJECT); load_all::init(CodeHydrater_PROJECT);
if (! defined('ENVIRONMENT')) {
if (is_live() === false) {
define('ENVIRONMENT', 'development');
} else {
define('ENVIRONMENT', 'production');
}
}
function is_live(): bool {
if (configure::has('CodeHydrater', 'live')) {
$live = configure::get('CodeHydrater', 'live');
}
if ($live === null) {
$live = errors::get_live_mode();
}
return (bool) $live;
}
$returned_route = \CodeHydrater\router::execute(); $returned_route = \CodeHydrater\router::execute();
if ($returned_route["found"] === false) { if ($returned_route["found"] === false) {
$app = new \CodeHydrater\app(); $app = new \CodeHydrater\app();

@ -40,14 +40,14 @@ final class site_helper {
public static function set_allowed_Private_IPs(string|array $IP_addresses): void { public static function set_allowed_Private_IPs(string|array $IP_addresses): void {
if (is_array($IP_addresses)) { if (is_array($IP_addresses)) {
foreach($IP_addresses as $IP) { foreach($IP_addresses as $IP) {
$s_ip = \CodeHydrater\security::get_valid_ip($IP); $s_ip = \CodeHydrater\security::is_private_or_local_IP_simple($IP);
if ($s_ip === false) { if ($s_ip === false) {
continue; continue;
} }
self::$Private_IPs_allowed[] = $IP; self::$Private_IPs_allowed[] = $IP;
} }
} elseif (is_string($IP_addresses)) { } elseif (is_string($IP_addresses)) {
$s_ip = \CodeHydrater\security::get_valid_ip($IP); $s_ip = \CodeHydrater\security::is_private_or_local_IP_simple($IP);
if ($s_ip === false) { if ($s_ip === false) {
return; return;
} }
@ -79,7 +79,7 @@ final class site_helper {
} }
public static function remote_not_allowed_force_live(): bool { public static function remote_not_allowed_force_live(): bool {
return (! self::is_allowed()); return (self::is_allowed() === false) ? true : false;
} }
public static function is_allowed(): bool { public static function is_allowed(): bool {
@ -87,7 +87,10 @@ final class site_helper {
if (in_array($remote_ip, self::$Public_IPs_allowed)) { if (in_array($remote_ip, self::$Public_IPs_allowed)) {
return true; return true;
} }
if (self::is_server_name_a_private_domain() && in_array($remote_ip, self::$Private_IPs_allowed)) { if (in_array($remote_ip, self::$Private_IPs_allowed)) {
return true;
}
if (self::is_server_name_a_private_domain()) {
return true; return true;
} }
return false; return false;
@ -243,5 +246,6 @@ final class site_helper {
define('PROJECT_BASE_REF', SITE_URL); define('PROJECT_BASE_REF', SITE_URL);
define("BROWSER", self::get_clean_server_var('HTTP_USER_AGENT')); define("BROWSER", self::get_clean_server_var('HTTP_USER_AGENT'));
define("ASSETS_DIR", "/public/assets/"); define("ASSETS_DIR", "/public/assets/");
define("PROJECT_ASSETS_BASE_REF", ASSETS_BASE_REF);
} }
} }

@ -44,7 +44,7 @@ class model {
* JSON API inform AJAX that save failed * JSON API inform AJAX that save failed
*/ */
public function save_failed($msg = 'Save Failed!'): void { public function save_failed($msg = 'Save Failed!'): void {
\CodeHydrater\api::error(array('code' => 500, 'success' => false, 'error' => $msg, 'responce' => \tts\api::INTERNAL_ERROR)); \CodeHydrater\api::error(array('code' => 500, 'success' => false, 'error' => $msg, 'responce' => \CodeHydrater\api::INTERNAL_ERROR));
} }
public function dump_table_fields(string $table) { public function dump_table_fields(string $table) {
@ -123,7 +123,7 @@ class model {
return; // Bail if LIVE return; // Bail if LIVE
} }
if (isset($post['json'])) { if (isset($post['json'])) {
$globals = \CodeHydrater\bootstrap\safer_io::get_json_post_data(); $globals = SafeIO::get_json_post_data();
} else { } else {
$json_post = count($post); $json_post = count($post);
$globals = ($json_post) ? $post : filter_input_array(INPUT_POST) ?? []; $globals = ($json_post) ? $post : filter_input_array(INPUT_POST) ?? [];

@ -7,7 +7,7 @@ declare(strict_types=1);
* @link https://code.tutsplus.com/tutorials/how-to-paginate-data-with-php--net-2928 * @link https://code.tutsplus.com/tutorials/how-to-paginate-data-with-php--net-2928
*/ */
namespace tts\database; namespace CodeHydrater\database;
class paginate { class paginate {
@ -210,6 +210,12 @@ class paginate {
. "{$end_label}</label>\n"; . "{$end_label}</label>\n";
} }
public function css_style(): string {
return ".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; }";
}
} }
/* /*
@ -230,26 +236,3 @@ class paginate {
echo $pag->create_links(); 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;}
*/

@ -360,7 +360,7 @@ class html_document {
* Used by Template file to render HTML Meta data for Robots * Used by Template file to render HTML Meta data for Robots
* @return string HTML Meta Robots * @return string HTML Meta Robots
*/ */
public function get_robots(): string { public function get_robots(): ?string {
return $this->robots; return $this->robots;
} }

@ -321,11 +321,10 @@ final class misc {
*/ */
public static function is_api(): bool { public static function is_api(): bool {
$uri = bootstrap\site_helper::request_uri(); $uri = bootstrap\site_helper::request_uri();
if (strlen($uri) > 2) { if (preg_match('~/api(\d*)/~', $uri)) {
return ((substr_count($uri, "/api/") == 1 && self::get_var('api') == 'true') || (substr_count($uri, "/api2/") == 1 && self::get_var('api2') == 'true')) ? true : false; return true;
} else {
return false;
} }
return false;
} }
/** /**

@ -362,7 +362,7 @@ class router
// Find route // Find route
foreach (self::$routes as $routeKey => $route) { foreach (self::$routes as $routeKey => $route) {
$post_method = \tts\misc::post_var("_method"); $post_method = misc::post_var("_method");
$matchMethod = in_array($request_method, $route['method']) || ($post_method !== null $matchMethod = in_array($request_method, $route['method']) || ($post_method !== null
&& in_array($post_method, $route['method'])); && in_array($post_method, $route['method']));
if (preg_match($route['pattern'], $request, $match) && $matchMethod) { if (preg_match($route['pattern'], $request, $match) && $matchMethod) {

@ -182,6 +182,25 @@ final class security {
return crc32($_SESSION['user_id']); return crc32($_SESSION['user_id']);
} }
public static function is_private_or_local_IP_simple(string $ip): bool {
if (! self::get_valid_ip($ip)) {
return false; // Invalid
}
return (
$ip === '::1' || // IPv6 localhost
preg_match('/^127\./', $ip) || // IPv4 localhost
preg_match('/^10\./', $ip) || // 10.0.0.0/8
preg_match('/^172\.(1[6-9]|2[0-9]|3[0-1])\./', $ip) || // 172.16.0.0/12
preg_match('/^192\.168\./', $ip) || // 192.168.0.0/16
preg_match('/^fd[0-9a-f]{2}:/i', $ip) // IPv6 ULA (fc00::/7)
);
}
/**
* Filter IP return good IP or False!
* @param string $ip
* @return string | false
*/
public static function get_valid_ip(string $ip) { public static function get_valid_ip(string $ip) {
return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6)); return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6));
} }

@ -192,7 +192,7 @@ final class view {
if (count($this->files) > 0) { if (count($this->files) > 0) {
foreach ($this->files as $view_file) { foreach ($this->files as $view_file) {
if ($view_file['file_type'] == '.php') { if ($view_file['file_type'] == '.php') {
bootstrap\requires::secure_include($view_file['file'], $view_file['path'], $local, $this->vars); // Include the file bootstrap\requires::secure_include($view_file['file'], bootstrap\UseDir::PROJECT, $local, $this->vars); // Include the file
} else { } else {
$template_file = str_replace('.tpl', '', $view_file['file']); $template_file = str_replace('.tpl', '', $view_file['file']);
$this->tempalte_engine->parse_file($template_file); $this->tempalte_engine->parse_file($template_file);
@ -228,7 +228,7 @@ final class view {
bootstrap\views::ob_start(); bootstrap\views::ob_start();
$saved_ob_level = ob_get_level(); $saved_ob_level = ob_get_level();
$local->page_output = $page_output; $local->page_output = $page_output;
bootstrap\requires::secure_include($this->template, 'project', $local, $this->vars); bootstrap\requires::secure_include($this->template, bootstrap\UseDir::PROJECT, $local, $this->vars);
$this->clear_ob($saved_ob_level); $this->clear_ob($saved_ob_level);
$page_output = ob_get_clean(); $page_output = ob_get_clean();
} }

Loading…
Cancel
Save