parent
9b2d9f9390
commit
14b5a48bed
@ -0,0 +1,56 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2026, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework\Enum; |
||||
|
||||
/** |
||||
* |
||||
* @author Robert Strutts |
||||
*/ |
||||
enum FieldFilter: string { |
||||
case raw_string = "string"; |
||||
case array_of_strings = "strings"; |
||||
case email = "email-address"; |
||||
case url = "site-url"; |
||||
case raw = "unfiltered-non-sanitized"; |
||||
case integer_number = "integer"; |
||||
case array_of_ints = "integers"; |
||||
case floating_point = "float"; |
||||
case array_of_floats = "floats"; |
||||
|
||||
public function resolve() { |
||||
return match($this) { |
||||
self::raw_string => FILTER_UNSAFE_RAW, |
||||
self::array_of_strings => [ |
||||
'filter' => FILTER_UNSAFE_RAW, |
||||
'flags' => FILTER_REQUIRE_ARRAY |
||||
], |
||||
self::email => FILTER_SANITIZE_EMAIL, |
||||
self::url => FILTER_SANITIZE_URL, |
||||
self::raw => FILTER_DEFAULT, // Unfiltered, non-sanitized!!! |
||||
self::integer_number => [ |
||||
'filter' => FILTER_SANITIZE_NUMBER_INT, |
||||
'flags' => FILTER_REQUIRE_SCALAR |
||||
], |
||||
self::array_of_ints => [ |
||||
'filter' => FILTER_SANITIZE_NUMBER_INT, |
||||
'flags' => FILTER_REQUIRE_ARRAY |
||||
], |
||||
self::floating_point => [ |
||||
'filter' => FILTER_SANITIZE_NUMBER_FLOAT, |
||||
'flags' => FILTER_FLAG_ALLOW_FRACTION |
||||
], |
||||
self::array_of_floats => [ |
||||
'filter' => FILTER_SANITIZE_NUMBER_FLOAT, |
||||
'flags' => FILTER_REQUIRE_ARRAY |
||||
], |
||||
}; |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2026, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework\Enum; |
||||
|
||||
/** |
||||
* |
||||
* @author Robert Strutts |
||||
*/ |
||||
enum Flags |
||||
{ |
||||
case RAW; |
||||
case TRIM; |
||||
case HTML_STIP_TAGS; |
||||
case HTML_ESCAPE; |
||||
case HTML_PURIFY; |
||||
CASE JSON_ENCODE; |
||||
} |
||||
@ -1,109 +0,0 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2025, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
namespace IOcornerstone\Framework\Http; |
||||
|
||||
class HttpContainer { |
||||
protected array $bindings = []; |
||||
protected array $instances = []; |
||||
|
||||
public function bind(string $abstract, $concrete = null, bool $shared = false): void |
||||
{ |
||||
if ($concrete === null) { |
||||
$concrete = $abstract; |
||||
} |
||||
$this->bindings[$abstract] = [ |
||||
'concrete' => $concrete, |
||||
'shared' => $shared |
||||
]; |
||||
} |
||||
|
||||
public function singleton(string $abstract, $concrete = null): void |
||||
{ |
||||
$this->bind($abstract, $concrete, true); |
||||
} |
||||
|
||||
public function make(string $abstract, array $parameters = []) |
||||
{ |
||||
// Return if already resolved as singleton |
||||
if (isset($this->instances[$abstract])) { |
||||
return $this->instances[$abstract]; |
||||
} |
||||
|
||||
// Get the concrete implementation |
||||
$concrete = $this->bindings[$abstract]['concrete'] ?? $abstract; |
||||
|
||||
// If it's a closure, resolve it |
||||
if ($concrete instanceof \Closure) { |
||||
$object = $concrete($this, $parameters); |
||||
} elseif (is_string($concrete)) { |
||||
// If it's a class name, instantiate it |
||||
$object = $this->build($concrete, $parameters); |
||||
} else { // Otherwise use as-is |
||||
$object = $concrete; |
||||
} |
||||
|
||||
// Store if singleton |
||||
if (($this->bindings[$abstract]['shared'] ?? false) === true) { |
||||
$this->instances[$abstract] = $object; |
||||
} |
||||
|
||||
return $object; |
||||
} |
||||
|
||||
protected function build(string $class, array $parameters = []) |
||||
{ |
||||
$reflector = new \ReflectionClass($class); |
||||
|
||||
// Check if class is instantiable |
||||
if (!$reflector->isInstantiable()) { |
||||
throw new \Exception("Class {$class} is not instantiable"); |
||||
} |
||||
|
||||
// Get the constructor |
||||
$constructor = $reflector->getConstructor(); |
||||
|
||||
// If no constructor, instantiate without arguments |
||||
if ($constructor === null) { |
||||
return new $class(); |
||||
} |
||||
|
||||
// Get constructor parameters |
||||
$dependencies = $constructor->getParameters(); |
||||
$instances = $this->resolveDependencies($dependencies, $parameters); |
||||
|
||||
return $reflector->newInstanceArgs($instances); |
||||
} |
||||
|
||||
protected function resolveDependencies(array $dependencies, array $parameters = []) |
||||
{ |
||||
$results = []; |
||||
|
||||
foreach ($dependencies as $dependency) { |
||||
// Check if parameter was provided |
||||
if (array_key_exists($dependency->name, $parameters)) { |
||||
$results[] = $parameters[$dependency->name]; |
||||
continue; |
||||
} |
||||
|
||||
// Get the type hinted class |
||||
$type = $dependency->getType(); |
||||
|
||||
if ($type && !$type->isBuiltin()) { |
||||
$results[] = $this->make($type->getName()); |
||||
} elseif ($dependency->isDefaultValueAvailable()) { |
||||
$results[] = $dependency->getDefaultValue(); |
||||
} else { |
||||
throw new \Exception("Cannot resolve dependency {$dependency->name}"); |
||||
} |
||||
} |
||||
|
||||
return $results; |
||||
} |
||||
} |
||||
@ -1,79 +0,0 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace IOcornerstone\Framework; |
||||
|
||||
// https://www.php-fig.org/psr/psr-12/ |
||||
|
||||
final class JunkForNow { |
||||
|
||||
/** |
||||
* @url https://php.watch/versions/8.5/filter-validation-throw-exception |
||||
*/ |
||||
public static function main(): void { |
||||
// filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE); |
||||
// throws a Filter\FilterFailedException |
||||
|
||||
$data = [ |
||||
'myNumber' => '15', |
||||
]; |
||||
|
||||
$filters = [ |
||||
'component' => [ |
||||
'filter' => FILTER_VALIDATE_INT, |
||||
'flags' => FILTER_THROW_ON_FAILURE, |
||||
'options' => [ |
||||
'min_range' => 1, |
||||
'max_range' => 10, |
||||
], |
||||
], |
||||
]; |
||||
|
||||
filter_var_array($data, $filters); |
||||
|
||||
$login = "val1dL0gin"; |
||||
$filtered_login = filter_var($login, FILTER_CALLBACK, ['options' => 'validate_login']); |
||||
var_dump($filtered_login); |
||||
} |
||||
|
||||
/** |
||||
* @todo Remove from MISC.php |
||||
*/ |
||||
public static function post_var( |
||||
string $var, |
||||
int $filter = FILTER_UNSAFE_RAW, |
||||
array|int $options = FILTER_NULL_ON_FAILURE |
||||
): mixed { |
||||
return filter_input(INPUT_POST, $var, $filter, $options); |
||||
} |
||||
|
||||
public static function get_var( |
||||
string $var, |
||||
int $filter = FILTER_UNSAFE_RAW, |
||||
array|int $options = FILTER_NULL_ON_FAILURE |
||||
): mixed { |
||||
return filter_input(INPUT_GET, $var, $filter, $options); |
||||
} |
||||
|
||||
public static function request_var( |
||||
string $var, |
||||
int $filter = FILTER_UNSAFE_RAW, |
||||
array|int $options = FILTER_NULL_ON_FAILURE |
||||
): mixed { |
||||
if (filter_has_var(INPUT_POST, $var)) { |
||||
return self::post_var($var, $filter, $options); |
||||
} |
||||
if (filter_has_var(INPUT_GET, $var)) { |
||||
return self::get_var($var, $filter, $options); |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
function validate_login(string $value): ?string { |
||||
if (strlen($value) >= 5 && ctype_alnum($value)) { |
||||
return $value; |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
@ -1,77 +0,0 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2025, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework\Http; |
||||
|
||||
use \IOcornerstone\Framework\{ |
||||
Http\ServiceProvider, |
||||
Http\Kernel, |
||||
Http\Request, |
||||
Http\Response, |
||||
Router, |
||||
App, |
||||
}; |
||||
|
||||
/** |
||||
* Description of RouteServiceProvider |
||||
* Setup Router and Controllers |
||||
* |
||||
*/ |
||||
class RouteServiceProvider extends ServiceProvider |
||||
{ |
||||
public function __construct(protected Kernel $kernel) |
||||
{} |
||||
|
||||
private function build($handler, $response, $request, $next, $controllerMiddleware) |
||||
{ |
||||
// Build middleware stack |
||||
$middleware_stack = array_reduce( |
||||
array_reverse($controllerMiddleware), |
||||
function ($next, $middleware) { |
||||
return function ($request, $response) use ($next, $middleware) { |
||||
if ($middleware !== null ) { |
||||
$instance = new $middleware(); |
||||
return $instance($request, $response, $next); |
||||
} |
||||
}; |
||||
}, |
||||
function ($request, $response) use ($handler) { |
||||
try { |
||||
$data = $handler->getContent(); |
||||
} catch (\Throwable $e) { |
||||
throw new \Exception("No Response from [Controller]?"); |
||||
} |
||||
if (! empty($data)) { |
||||
$response->setContent($data); |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
); |
||||
return $middleware_stack($request, $response); |
||||
} |
||||
|
||||
public function register(): void |
||||
{ |
||||
// Add router middleware |
||||
$this->kernel->addMiddleware(function (Request $request, Response $response, $next) { |
||||
$returned_route = Router::execute($request, $response); |
||||
if ($returned_route["found"] === false) { |
||||
$app = new App($request, $response); |
||||
$returned = $app->loadController(); |
||||
$a_middleware = $returned['middleware'] ?? []; |
||||
$data = $returned['data'] ?? ""; |
||||
return $this->build($data, $response, $request, $next, $a_middleware); |
||||
} else { |
||||
return $this->build($returned_route['returned'], $response, $request, $next, $returned_route['middleware']); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
@ -1,492 +0,0 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @license MIT |
||||
* @copyright (c) 2018, Patrik Mokrý |
||||
* @author Patrik Mokrý |
||||
* @link https://github.com/MokryPatrik/PHP-Router |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework; |
||||
|
||||
use IOcornerstone\Framework\Http\Request; |
||||
use IOcornerstone\Framework\Http\Response; |
||||
|
||||
class router |
||||
{ |
||||
private static $instance = null; |
||||
public static $URL = null; |
||||
public static $REQUEST = null; |
||||
public static $params = []; |
||||
private static $queryParams = []; |
||||
private static $routes = []; // All routes |
||||
public static $route = ""; // Return current route |
||||
private static $last = null; // Last route added |
||||
|
||||
private static $prefix = null; |
||||
private static $name = null; |
||||
private static $doNotIncludeInParams = []; |
||||
private static $shortcuts = [ |
||||
'i' => '(\d+)', // Any Number |
||||
's' => '(\w+)', // Any Word |
||||
'locale' => '(sk|en)->en' |
||||
]; |
||||
|
||||
/** |
||||
* Init new self instance |
||||
*/ |
||||
public static function init() { |
||||
self::$instance = new self(); |
||||
} |
||||
|
||||
/** |
||||
* Assign name to route |
||||
* |
||||
* @param $name |
||||
*/ |
||||
public static function name($name) |
||||
{ |
||||
$route = self::$routes[self::$last]; |
||||
unset(self::$routes[self::$last]); |
||||
self::$routes[self::$name . $name] = $route; |
||||
} |
||||
|
||||
/** |
||||
* Add custom shortcut |
||||
* shortcut with default value -> $regex = (val1|val2)->val1; |
||||
* |
||||
* @param $shortcut |
||||
* @param $regex |
||||
*/ |
||||
public static function addShortcut($shortcut, $regex) |
||||
{ |
||||
self::$shortcuts[$shortcut] = $regex; |
||||
} |
||||
|
||||
/** |
||||
* Get link from route name and params |
||||
* |
||||
* @param $route |
||||
* @param array $params |
||||
* @param $absolute |
||||
* @return null |
||||
*/ |
||||
public static function link($route, $params = [], $absolute = true) |
||||
{ |
||||
if (!isset(self::$routes[$route])) return null; |
||||
$route = self::$routes[$route]; |
||||
|
||||
$link = ""; |
||||
foreach ($route['params'] as $key => $param) { |
||||
if (isset($param['real'])) { |
||||
$link .= $param['pattern'] . '/'; |
||||
} else if (isset($params[$param['name']])) { |
||||
// Chcek if param has default |
||||
if (isset($param['default']) && $param['default'] !== $params[$param['name']]) { |
||||
$link .= $params[$param['name']] . '/'; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Add absolute path |
||||
if ($absolute) { |
||||
$link = self::$URL . $link; |
||||
} |
||||
|
||||
// Cut slash at the end |
||||
return rtrim($link, '/'); |
||||
} |
||||
|
||||
/** |
||||
* Redirect to specific route or defined location |
||||
* |
||||
* @param $route |
||||
* @param array $params |
||||
*/ |
||||
public static function redirect($route, $params = []) |
||||
{ |
||||
if (isset(self::$routes[$route])) { |
||||
$route = self::link($route, $params); |
||||
} |
||||
header('Location: ' . $route, true); |
||||
die(); |
||||
} |
||||
|
||||
/** |
||||
* Create prefixed routes |
||||
* |
||||
* @param $prefix |
||||
* @param $callback |
||||
* @param string $name |
||||
* @param array $doNotIncludeInParams |
||||
*/ |
||||
public static function prefix($prefix, $callBack, $name = '', $doNotIncludeInParams = []) |
||||
{ |
||||
self::$prefix = $prefix; |
||||
self::$name = $name; |
||||
self::$doNotIncludeInParams = $doNotIncludeInParams; |
||||
call_user_func($callBack); |
||||
self::$prefix = null; |
||||
self::$name = null; |
||||
self::$doNotIncludeInParams = []; |
||||
} |
||||
|
||||
/** |
||||
* Create route |
||||
* |
||||
* @param $route |
||||
* @param $action |
||||
* @param array $method |
||||
* @return Router |
||||
*/ |
||||
public static function route($route, $action, $method = ["POST", "GET"]) |
||||
{ |
||||
$prefix = self::$prefix; |
||||
if (empty($route) && !empty(self::$prefix)) { |
||||
$prefix = rtrim(self::$prefix, '/'); |
||||
} |
||||
|
||||
|
||||
$explodedRoute = explode("/", $prefix . $route); |
||||
$params = []; |
||||
$pattern = ""; |
||||
|
||||
// create route |
||||
foreach ($explodedRoute as $key => $r) { |
||||
if (strpos($r, '}?') !== false) { |
||||
$r = self::dynamic($r, $params, true); |
||||
$pattern = substr($pattern, 0, -1); |
||||
$dyn = true; |
||||
} else if (strpos($r, '}') !== false) { |
||||
$r = self::dynamic($r, $params, false); |
||||
$pattern = substr($pattern, 0, -1); |
||||
$dyn = true; |
||||
} else { |
||||
$params[] = [ |
||||
'pattern' => $r, |
||||
'real' => false |
||||
]; |
||||
} |
||||
$pattern .= ($key == 0 && !isset($dyn) ? '/' : '') . $r . '/'; |
||||
} |
||||
|
||||
// Create pattern |
||||
$pattern = substr($pattern, 0, -1) . '/?'; |
||||
$pattern = '~^' . str_replace('/', '\/', $pattern) . '$~'; |
||||
|
||||
// Save data to static property |
||||
$name = uniqid(); |
||||
self::$routes[$name] = [ |
||||
'route' => $route, |
||||
'pattern' => $pattern, |
||||
'params' => $params, |
||||
'action' => $action, |
||||
'method' => $method, |
||||
'doNotIncludeInParams' => self::$doNotIncludeInParams |
||||
]; |
||||
// Set last added |
||||
self::$last = $name; |
||||
|
||||
// return self instance |
||||
if (self::$instance == null) { |
||||
self::init(); |
||||
} |
||||
return self::$instance; |
||||
} |
||||
|
||||
public static function get($route, $action) { |
||||
return self::route($route, $action, ['GET']); |
||||
} |
||||
|
||||
public static function post($route, $action) { |
||||
return self::route($route, $action, ['POST']); |
||||
} |
||||
|
||||
public static function put($route, $action) { |
||||
return self::route($route, $action, ['PUT']); |
||||
} |
||||
|
||||
public static function delete($route, $action) { |
||||
return self::route($route, $action, ['DELETE']); |
||||
} |
||||
|
||||
public static function any($route, $action) { |
||||
return self::route($route, $action, ['GET', 'POST', 'PUT', 'DELETE']); |
||||
} |
||||
|
||||
/** |
||||
* Create all routes for resource (CRUD) |
||||
* |
||||
* @param $route |
||||
* @param $controller |
||||
* @param string $name |
||||
* @param string $shortcut |
||||
*/ |
||||
public static function resource($route, $controller, $name = null, $shortcut = 's') |
||||
{ |
||||
self::$name = self::$name . $name; |
||||
|
||||
self::get($route . '/{page::paginator}?', $controller . "@index")->name('index'); |
||||
self::get($route . "/create", $controller . "@create")->name('create'); |
||||
self::post($route, $controller . "@store")->name('store'); |
||||
self::get($route . "/{id::" . $shortcut . "}", $controller . "@show")->name('show'); |
||||
self::get($route . "/{id::" . $shortcut . "}/edit", $controller . "@edit")->name('edit'); |
||||
self::put($route . "/{id::" . $shortcut . "}", $controller . "@update")->name('update'); |
||||
self::delete($route . "/{id::" . $shortcut . "}", $controller . "@destroy")->name('destroy'); |
||||
self::get($route . "/{id::" . $shortcut . "}/delete", $controller . "@destroy")->name('destroy_'); |
||||
|
||||
self::$name = str_replace($name, '', self::$name); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Handle dynamic parameter in route |
||||
* |
||||
* @param string $route |
||||
* @param array $params |
||||
* @param boolean $optional |
||||
* @return mixed |
||||
*/ |
||||
private static function dynamic($route, &$params, $optional = false) |
||||
{ |
||||
$shortcut = self::rules($route); |
||||
|
||||
$name = str_replace('{', '', $route); |
||||
if (!$optional) { |
||||
$name = str_replace('}', '', $name); |
||||
} else { |
||||
$name = str_replace('}?', '', $name); |
||||
} |
||||
|
||||
if (strpos($name, '::')) { |
||||
$name = substr($name, 0, strpos($name, "::")); |
||||
} |
||||
|
||||
if (array_search($name, array_column($params, 'name')) === false) { |
||||
|
||||
$pattern = str_replace('(', '(/', $shortcut['shortcut']); |
||||
$pattern = str_replace('|', '|/', $pattern); |
||||
|
||||
// If is optional add ? at the end of pattern |
||||
if ($optional) { |
||||
$pattern .= '?'; |
||||
} |
||||
|
||||
$params[] = [ |
||||
'name' => $name, |
||||
'pattern' => $pattern, |
||||
'default' => $shortcut['default'] |
||||
]; |
||||
} else { |
||||
throw new \Exception('Parameter with name ' . $name . ' has been already defined'); |
||||
} |
||||
return $pattern; |
||||
} |
||||
|
||||
/** |
||||
* Dynamic route rules |
||||
* |
||||
* @param $route |
||||
* @return mixed |
||||
*/ |
||||
private static function rules($route) |
||||
{ |
||||
if (preg_match('~::(.*?)}~', $route, $match)) { |
||||
list(, $shortcut) = $match; |
||||
if (isset(self::$shortcuts[$shortcut])) { |
||||
// Try to get default value from shortcut |
||||
$default = false; |
||||
$shortcut = self::$shortcuts[$shortcut]; |
||||
if (preg_match('~->(.*?)$~', $shortcut, $match)) { |
||||
$default = $match[1]; |
||||
$shortcut = str_replace($match[0], '', $shortcut); |
||||
} |
||||
// Return shortcut and default value |
||||
return [ |
||||
'shortcut' => $shortcut, |
||||
'default' => $default |
||||
]; |
||||
} |
||||
} |
||||
return [ |
||||
'shortcut' => self::$shortcuts['s'], |
||||
'default' => false |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* get_all_routes -> Added by Robert Strutts to auto load routes |
||||
* Namespace must start with Project |
||||
* Needs a routes folder, then |
||||
* either an routes.php or test_routes.php files must exists |
||||
* with a method called get! |
||||
*/ |
||||
private static function get_all_routes(bool $testing = false) { |
||||
$route_name = (console_app::is_cli()) ? "cli_routes" : "routes"; |
||||
$routes = ($testing) ? "test_routes" : $route_name; |
||||
$routes_class = "\\Project\\routes\\{$routes}"; |
||||
|
||||
if (class_exists($routes_class)) { |
||||
if (method_exists($routes_class, "get")) { |
||||
$callback = "{$routes_class}::get"; |
||||
call_user_func($callback); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Execute router |
||||
*/ |
||||
public static function execute(Request $my_request, Response $my_response) { |
||||
$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); |
||||
// Fecth all Routes from the Project |
||||
self::get_all_routes($testing); |
||||
|
||||
// Get query string |
||||
$request = explode('?', $request_uri); |
||||
$queryParams = []; |
||||
if (isset($request[1])) { |
||||
$queryParams = $request[1]; |
||||
parse_str($queryParams, $queryParams); |
||||
self::$queryParams = $queryParams; |
||||
} |
||||
|
||||
// Modify request |
||||
$request = '/' . trim(self::$REQUEST, '/'); |
||||
|
||||
// Find route |
||||
foreach (self::$routes as $routeKey => $route) { |
||||
$post_method = misc::post_var("_method"); |
||||
$matchMethod = in_array($request_method, $route['method']) || ($post_method !== null |
||||
&& in_array($post_method, $route['method'])); |
||||
if (preg_match($route['pattern'], $request, $match) && $matchMethod) { |
||||
|
||||
// Default variables |
||||
$explodedRequest = explode('/', ltrim($request, '/')); |
||||
$routeParams = $route['params']; |
||||
|
||||
// Match request params with params in array - static params |
||||
foreach ($explodedRequest as $key => $value) { |
||||
foreach ($routeParams as $k => $routeParam) { |
||||
// Go to the next request part |
||||
if (isset($routeParam['real']) && $routeParam['pattern'] == $value) { |
||||
unset($routeParams[$k]); |
||||
unset($explodedRequest[$key]); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
$number = "(/\d+)"; |
||||
$params = []; |
||||
// Match request params with params in array - dynamic params |
||||
foreach ($routeParams as $k => $routeParam) { |
||||
$value = $explodedRequest[$k] ?? false; |
||||
$default = $routeParam['default'] ?? true; |
||||
$pattern = $routeParam['pattern'] ?? ""; |
||||
$wild = str_contains($pattern, "?"); |
||||
if (! $wild && ($value === false && $default)) { |
||||
$params[$routeParam['name']] = null; |
||||
} else if ($wild && ($value === false && ! $default)) { |
||||
// Let the default function params be used by not setting anything here! |
||||
} else if (preg_match('~' . $pattern . '~', '/' . $value, $match)) { |
||||
if (str_contains($pattern, $number)) { |
||||
$params[$routeParam['name']] = (int) $value; |
||||
} else { |
||||
$params[$routeParam['name']] = $value; |
||||
} |
||||
unset($routeParams[$k]); |
||||
} |
||||
} |
||||
|
||||
// Merge with query params |
||||
self::$params = array_merge($params, $queryParams); |
||||
|
||||
// Setup default route and url |
||||
self::$route = $route; |
||||
|
||||
foreach ($params as $key => $value) { |
||||
if (in_array($key, $route['doNotIncludeInParams']) && !is_numeric($key)) { |
||||
unset($params[$key]); |
||||
} |
||||
} |
||||
|
||||
// Call action |
||||
if (is_callable($route['action'])) { |
||||
$returned = call_user_func_array($route['action'], $params); |
||||
return ["found"=> true, "returned"=> $returned]; |
||||
} else if (strpos($route['action'], '@') !== false) { |
||||
|
||||
// call controller |
||||
list($controller, $method) = explode('@', $route['action']); |
||||
|
||||
// init new controller |
||||
$controller = new $controller($my_request, $my_response); |
||||
|
||||
// Collect controller-level middleware |
||||
$controller_middleware = $controller::$middleware ?? []; |
||||
|
||||
// Check if class has parent |
||||
$parentControllers = class_parents($controller); |
||||
if (!empty($parentControllers)) { |
||||
end($parentControllers); |
||||
$parentController = $parentControllers[key($parentControllers)]; |
||||
$parentController = new $parentController($my_request, $my_response); |
||||
|
||||
// Add properties to parent class |
||||
foreach ($params as $key => $value) { |
||||
$parentController::$params[$key] = $value; |
||||
} |
||||
} |
||||
|
||||
//Call method |
||||
if (method_exists($controller, $method)) { |
||||
$returned = call_user_func_array([$controller, $method], $params); |
||||
return ["found"=> true, "returned"=> $returned, "middleware"=>$controller_middleware]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return ["found"=>false]; |
||||
} |
||||
|
||||
/** |
||||
* Generate URL |
||||
* |
||||
* @param $ROOT |
||||
*/ |
||||
private static function generateURL(string $ROOT, string $request_uri) |
||||
{ |
||||
$https = bootstrap\safer_io::get_clean_server_var('HTTPS'); |
||||
$baseLink = ($https === 'on') ? "https" : "http"; |
||||
|
||||
$server_name = bootstrap\safer_io::get_clean_server_var('SERVER_NAME'); |
||||
$baseLink .= "://" . $server_name; |
||||
|
||||
$port = bootstrap\safer_io::get_clean_server_var('SERVER_PORT'); |
||||
$baseLink .= ($port !== '80') ? ':' . $port : ':'; |
||||
|
||||
$baseRequest = ''; |
||||
|
||||
$request = $request_uri; |
||||
foreach (explode('/', $ROOT) as $key => $value) { |
||||
if ($value == '') continue; |
||||
if (preg_match('~/' . $value . '~', $request)) { |
||||
$baseRequest .= $value . '/'; |
||||
} |
||||
$request = preg_replace('~/' . $value . '~', '', $request, 1); |
||||
} |
||||
|
||||
self::$URL = $baseLink . '/' . $baseRequest; |
||||
self::$REQUEST = explode('?', $request)[0]; |
||||
} |
||||
|
||||
} |
||||
@ -1,46 +0,0 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2025, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework\Http; |
||||
|
||||
abstract class ServiceProvider { |
||||
protected kernel $kernel; |
||||
|
||||
public function __construct(kernel $kernel) |
||||
{ |
||||
$this->kernel = $kernel; |
||||
} |
||||
|
||||
abstract public function register(): void; |
||||
} |
||||
|
||||
/** |
||||
* Example Useage: |
||||
namespace App\Providers; |
||||
|
||||
use IOcornerstone\Framework\Http\kernel; |
||||
use IOcornerstone\Framework\Http\service_provider; |
||||
use App\Services\Database; |
||||
|
||||
class app_service_provider extends service_provider |
||||
{ |
||||
public function register(): void |
||||
{ |
||||
$this->kernel->getContainer()->singleton(Database::class, function() { |
||||
return new Database( |
||||
getenv('DB_HOST'), |
||||
getenv('DB_USER'), |
||||
getenv('DB_PASS'), |
||||
getenv('DB_NAME') |
||||
); |
||||
}); |
||||
} |
||||
} |
||||
*/ |
||||
@ -1,32 +0,0 @@ |
||||
<?php |
||||
|
||||
//echo trim(strtoupper(str_rot13(str_shuffle("Hello, World!")))); |
||||
|
||||
/** |
||||
* yonks: noun, A long time (especially a longer time than expected); ages. |
||||
* Here its a lot of useless data passed |
||||
*/ |
||||
function yonks(string $x): string |
||||
{ |
||||
return hash('sha256', $x); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* PHP 8.5 adds a new operator, the pipe operator (|>) |
||||
* to chain multiple callables from left to right, |
||||
* taking the return value of the left callable and |
||||
* passing it to the right. |
||||
* |
||||
* They are suitable when all of the functions in the |
||||
* chain require only one parameter, have return values, |
||||
* and do not accept by-reference parameters. |
||||
|
||||
|
||||
echo trim("Hello, World!") |
||||
|> strtoupper(...) |
||||
|> str_shuffle(...) |
||||
|> str_rot13(...) |
||||
|> yonks(...); |
||||
* |
||||
*/ |
||||
@ -0,0 +1,156 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @author Robert Strutts |
||||
* @copyright (c) 2026, Robert Strutts |
||||
* @license MIT |
||||
*/ |
||||
|
||||
namespace IOcornerstone\Framework; |
||||
|
||||
use IOcornerstone\Framework\Enum\Flags; |
||||
|
||||
/** |
||||
* Description of SaferOutput |
||||
* |
||||
* @author Robert Strutts |
||||
*/ |
||||
class SaferOutput |
||||
{ |
||||
|
||||
public static function get( |
||||
string $input, |
||||
string $default = '', |
||||
array $flags = [Flags::TRIM, Flags::HTML_ESCAPE] |
||||
): string |
||||
{ |
||||
$value = $input ?? $default; |
||||
|
||||
if (!is_string($value)) { |
||||
return $default; |
||||
} |
||||
|
||||
foreach ($flags as $flag) { |
||||
$value = match ($flag) { |
||||
Flags::TRIM => |
||||
trim($value), |
||||
|
||||
Flags::HTML_STIP_TAGS => |
||||
strip_tags($value), |
||||
|
||||
Flags::HTML_ESCAPE => |
||||
self::h($value), |
||||
|
||||
Flags::HTML_PURIFY => |
||||
self::p($value), |
||||
|
||||
Flags::JSON_ENCODE => |
||||
self::j($value), |
||||
}; |
||||
} |
||||
|
||||
return $value; |
||||
} |
||||
|
||||
public static function convertToUTF8(string $in_str): string |
||||
{ |
||||
if (!extension_loaded('mbstring')) { |
||||
return $in_str; |
||||
} |
||||
$cur_encoding = mb_detect_encoding($in_str); |
||||
if ($cur_encoding == "UTF-8" && mb_check_encoding($in_str, "UTF-8")) { |
||||
return $in_str; |
||||
} else { |
||||
return mb_convert_encoding($in_str, 'UTF-8', $cur_encoding); |
||||
} |
||||
} |
||||
|
||||
// Escape HTML output |
||||
public static function h(string $string): string |
||||
{ |
||||
$utf8 = self::convertToUTF8($string); |
||||
return htmlspecialchars($utf8, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8'); |
||||
} |
||||
|
||||
// Reverse encode of HTML |
||||
public static function htmlDecode(string $string): string |
||||
{ |
||||
return htmlspecialchars_decode($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5); |
||||
} |
||||
|
||||
/** |
||||
* @todo FIX ME to use IOConerstone....!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
||||
*/ |
||||
|
||||
// HTML Purify library |
||||
public static function p(string $string): string |
||||
{ |
||||
$purifer = \main_tts\registry::get('di')->get_service('html_filter'); |
||||
if (!$purifer->has_loaded()) { |
||||
$purifer->set_defaults(); |
||||
} |
||||
return $purifer->purify($string); |
||||
} |
||||
|
||||
// Escape JavaScript output |
||||
public static function j($input, int $levels_deep = 512): mixed |
||||
{ |
||||
try { |
||||
return json_encode($input, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR, $levels_deep); |
||||
} catch (\JsonException $ex) { |
||||
return $ex; |
||||
} |
||||
} |
||||
|
||||
public static function jsonDecode(string $string, bool $return_as_an_array = true, int $levels_deep = 512): mixed |
||||
{ |
||||
try { |
||||
return json_decode($string, $return_as_an_array, $levels_deep, JSON_THROW_ON_ERROR); |
||||
} catch (\JsonException $ex) { |
||||
return $ex; |
||||
} |
||||
} |
||||
|
||||
public static function hasJsonError($object): bool |
||||
{ |
||||
return ($object instanceof \JsonException); |
||||
} |
||||
|
||||
// Escape URL output |
||||
public static function u(string $string): string |
||||
{ |
||||
return urlencode($string); |
||||
} |
||||
|
||||
/* |
||||
* Encode HTML kindof... The problem with htmlentities() is that it is not |
||||
* very powerful, in fact, it does not escape single quotes, cannot detect |
||||
* the character set and does not validate HTML as well. |
||||
*/ |
||||
|
||||
public static function e(string $string): string |
||||
{ |
||||
$utf8 = self::convertToUTF8($string); |
||||
return htmlentities($utf8, ENT_QUOTES, 'UTF-8'); |
||||
} |
||||
|
||||
public static function de(string $data): string |
||||
{ |
||||
return html_entity_decode($data); |
||||
} |
||||
|
||||
/** |
||||
* As PHP uses the underlying C functions for file system related operations, |
||||
* it may handle null bytes in a quite unexpected way. |
||||
* As null bytes denote the end of a string in C, strings |
||||
* containing them won't be considered entirely but rather |
||||
* only until a null byte occurs. So, clean it out to |
||||
* avoid vulnerable code. |
||||
*/ |
||||
public static function removeNullByte(string $input): string |
||||
{ |
||||
return str_replace(chr(0), '', $input); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue