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