diff --git a/src/Bootstrap.php b/src/Bootstrap.php
index f68c9a6..53393bd 100644
--- a/src/Bootstrap.php
+++ b/src/Bootstrap.php
@@ -59,7 +59,7 @@ function dump($var = 'nothing', endDump $end = endDump::KEEP_WORKING)
Common::dump($var, $end);
}
-$debug = true; // <------------------- make false in production
+$debug = false; // <------------------- make false in production
$myErrorHandler = new ErrorHandler($debug);
$myErrorHandler->register();
diff --git a/src/Framework/Assets.php b/src/Framework/Assets.php
index f9aedef..ffa0ce4 100644
--- a/src/Framework/Assets.php
+++ b/src/Framework/Assets.php
@@ -14,8 +14,11 @@ use IOcornerstone\Framework\{
Security,
Common,
String\StringFacade,
+ Http\HttpFactory,
};
+use Psr\Http\Message\ResponseInterface;
+
class Assets
{
private static $files = [];
@@ -278,30 +281,29 @@ class Assets
return (file_exists($safe_path . $safe_file)) ? $safe_file : false;
}
- /**
- * meta redirect when headers are already sent...
- * @param string url - site to do redirect on
- * @reval none
- */
- public static function gotoUrl(string $url): void
- {
- echo '';
- exit;
- }
-
/**
* Rediect to url and attempt to send via header.
* @param string $url - site to do redirect for
- * @retval none - exits once done
+ * @param HttpFactory or $this->html...in controllers...
+ * @return ResponseInterface for Controllers to return...
*/
- public static function redirectUrl(string $url): void
+ public static function redirectUrl(string $url, HttpFactory $http): ResponseInterface
{
$url = str_replace(array('&', "\n", "\r"), array('&', '', ''), $url);
if (!headers_sent()) {
- header('Location: ' . $url);
+ return $http->createResponse(403, ['Location' => $url], "");
} else {
- self::goto_url($url);
+ return $http->returnResponse(403, [], self::gotoUrl($url));
}
- exit;
}
+
+ /**
+ * meta redirect when headers are already sent...
+ * @param string url - site to do redirect on
+ */
+ private static function gotoUrl(string $url): string
+ {
+ return '';
+ }
+
}
diff --git a/src/Framework/Common.php b/src/Framework/Common.php
index 9f22faf..e33b6d3 100644
--- a/src/Framework/Common.php
+++ b/src/Framework/Common.php
@@ -26,6 +26,11 @@ final class Common
{
return (is_array($i) || is_object($i)) ? count($i) : 0;
}
+
+ public static function hasKeys(array $array): bool
+ {
+ return array_keys($array) !== range(0, count($array) - 1);
+ }
public static function stringSubPart(string $string, int $offset = 0, ?int $length = null, $encoding = null) {
if ($length === null) {
diff --git a/src/Framework/Enum/FieldFilter.php b/src/Framework/Enum/FieldFilter.php
new file mode 100644
index 0000000..75b1cf6
--- /dev/null
+++ b/src/Framework/Enum/FieldFilter.php
@@ -0,0 +1,56 @@
+ 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
+ ],
+ };
+ }
+}
diff --git a/src/Framework/Enum/Flags.php b/src/Framework/Enum/Flags.php
new file mode 100644
index 0000000..04a3cd4
--- /dev/null
+++ b/src/Framework/Enum/Flags.php
@@ -0,0 +1,25 @@
+debug) {
$this->renderConsole($e);
} else {
- $this->renderProductionConsole();
+ $this->renderProductionConsole($e);
$this->logException($e);
}
return true;
@@ -122,7 +122,7 @@ final class ErrorHandler
if ($this->debug) {
$this->renderDebug($e);
} else {
- $this->renderProduction();
+ $this->renderProduction($e);
$this->logException($e);
}
// Don't execute PHP's internal error handler
@@ -247,9 +247,17 @@ final class ErrorHandler
public function getJsonDebug(Throwable $e): string
{
$this->setJsonHeaders();
+
+ $dCode = $e->getCode() ?? 0;
+ if ($dCode > 0) {
+ $aCode = ['code' => $dCode];
+ } else {
+ $aCode = [];
+ }
return json_encode([
'error' => [
+ $aCode,
'type' => $this->getErrorType($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),
@@ -259,12 +267,13 @@ final class ErrorHandler
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
- private function renderJsonProduction(): void
+ private function renderJsonProduction(Throwable $e): void
{
$this->setJsonHeaders();
echo json_encode([
'error' => [
+ 'code' => $this->getCodeNumber($e),
'message' => 'Internal Server Error'
]
]);
@@ -304,6 +313,12 @@ final class ErrorHandler
echo $color . $out . PHP_EOL;
}
+ public function getCodeNumber(Throwable $e): string
+ {
+ $dCode = $e->getCode() ?? 0;
+ return "(Code #{$dCode}); ";
+ }
+
public function formatWebMessage(Throwable $e): string
{
$styles = [
@@ -315,8 +330,8 @@ final class ErrorHandler
$style = $styles[$type] ?? $styles['error'];
$content = htmlspecialchars((string) $e);
-
- $message = wordwrap($content, WORD_WRAP_CHRS, "
\n");
+ $message = $this->getCodeNumber($e);
+ $message .= wordwrap($content, WORD_WRAP_CHRS, "
\n");
$assets = "/assets/uikit/css/uikit.gradient.min.css";
if (defined("BaseDir") &&
@@ -332,21 +347,22 @@ final class ErrorHandler
return $msg;
}
- public function getProdMessage(): string
+ public function getProdMessage(Throwable $e): string
{
if (Console::isConsole()) {
- return $this->myErr;
+ return $this->getCodeNumber($e) . $this->myErr;
}
if ($this->isJsonRequest()) {
$this->setJsonHeaders();
return json_encode([
'error' => [
+ 'code' => $this->getCodeNumber($e),
'message' => 'Internal Server Error'
]
]);
}
- return "
" . $this->myErr . "
";
+ return "" . $this->getCodeNumber($e) . $this->myErr . "
";
}
private function renderDebug(Throwable $e): void
@@ -355,17 +371,17 @@ final class ErrorHandler
echo $this->formatWebMessage($e);
}
- private function renderProductionConsole(): void
+ private function renderProductionConsole(Throwable $e): void
{
- echo $this->myErr;
+ echo $this->getCodeNumber($e) . $this->myErr;
}
/**
* @todo Make error page red, etc...
*/
- private function renderProduction(): void
+ private function renderProduction(Throwable $e): void
{
- echo "" . $this->myErr . "
";
+ echo "" . $this->getCodeNumber($e) . $this->myErr . "
";
}
private function setLoggerByLevel(Throwable $e): void
diff --git a/src/Framework/HtmlDocument.php b/src/Framework/HtmlDocument.php
index e5bf40f..c6028a6 100644
--- a/src/Framework/HtmlDocument.php
+++ b/src/Framework/HtmlDocument.php
@@ -116,6 +116,16 @@ class HtmlDocument
$this->author = $author;
}
+ public function addToTitle(string $title): void
+ {
+ $this->title .= $title;
+ }
+
+ public function beforeTitle(string $title): void
+ {
+ $this->title = $title . $this->title;
+ }
+
/**
* Set Title for HTML
* @param string $title
@@ -199,22 +209,33 @@ class HtmlDocument
$this->breadcrumb = $crumbs;
}
- public function setAssetsFromArray(array $files, string $which, string $scope = 'project'): void
- {
- foreach ($files as $file => $a) {
- switch ($which) {
- case 'main_css':
- $this->addMainCss($file, $scope, $a);
- break;
- case 'css':
- $this->addCss($file, $scope, $a);
- break;
- case 'main_js':
- $this->addMainJs($file, $scope, $a);
- break;
- case 'js':
- $this->addJs($file, $scope, $a);
- break;
+ private function doAction(string $file, string $which, string $scope, array $a): void
+ {
+ switch ($which) {
+ case 'main_css':
+ $this->addMainCss($file, $scope, $a);
+ break;
+ case 'css':
+ $this->addCss($file, $scope, $a);
+ break;
+ case 'main_js':
+ $this->addMainJs($file, $scope, $a);
+ break;
+ case 'js':
+ $this->addJs($file, $scope, $a);
+ break;
+ }
+ }
+
+ public function setAssetsFromArray(array $files, string $which, string $scope = 'project', array $options = []): void
+ {
+ if (Common::hasKeys($files)) {
+ foreach ($files as $file => $a) {
+ $this->doAction($file, $which, $scope, $a);
+ }
+ } else {
+ foreach ($files as $file) {
+ $this->doAction($file, $which, $scope, $options);
}
}
}
@@ -476,18 +497,22 @@ class HtmlDocument
return $this->head;
}
- public function getBreadcrumbsAuto(): string
+ public function getBreadcrumbsAuto(bool $showHomeSVG = false): string
{
- if (! count($this->breadcrumb) && empty($this->activeCrumb)) {
+ if (!count($this->breadcrumb) && empty($this->activeCrumb)) {
return "";
}
-
+
$out = "" . PHP_EOL;
diff --git a/src/Framework/Http/App/App.php b/src/Framework/Http/App/App.php
index 17e5463..375da10 100644
--- a/src/Framework/Http/App/App.php
+++ b/src/Framework/Http/App/App.php
@@ -22,7 +22,8 @@ use IOcornerstone\Framework\{
Configure,
Common,
Console,
- Security
+ Security,
+ View,
};
use Exception;
@@ -111,7 +112,7 @@ class App implements MiddlewareAwareInterface
private function getCtrlDir(): string
{
- $ctrl = (Console::isConsole()) ? "cli_" : "";
+ $ctrl = (Console::isConsole()) ? "CLI_" : "";
return ($this->testing) ? "test_" : $ctrl;
}
@@ -193,7 +194,10 @@ class App implements MiddlewareAwareInterface
private function local404(): ResponseInterface
{
- return (new HttpFactory())->createResponse(404, [], '404 Page - Not Found');
+ $view = new View();
+ $view->addView("OnError/404Page");
+ $myView = $view->fetch($this);
+ return (new HttpFactory())->createResponse(404, [], $myView);
}
/**
diff --git a/src/Framework/Http/Kernel.php b/src/Framework/Http/Kernel.php
index fe8b03a..d1c05ba 100644
--- a/src/Framework/Http/Kernel.php
+++ b/src/Framework/Http/Kernel.php
@@ -124,6 +124,7 @@ class Kernel {
http_response_code($response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
if (! is_array($values) && ! is_object($values)) {
+ header("$name: $values", false);
continue;
}
foreach ($values as $value) {
diff --git a/src/Framework/Http/Request.php b/src/Framework/Http/Request.php
index 208fe6b..b6eaddf 100644
--- a/src/Framework/Http/Request.php
+++ b/src/Framework/Http/Request.php
@@ -29,6 +29,11 @@ final class Request implements ServerRequestInterface
private string $protocol = '1.1'
) {}
+ public function JSON_PostVar(): ParameterBag
+ {
+ return new ParameterBag((array) json_decode($this->getBody(), true));
+ }
+
/**
* Parameter Bags [has and get - methods]
*/
diff --git a/src/Framework/LoadAll.php b/src/Framework/LoadAll.php
index 4c11f4b..200a3f6 100644
--- a/src/Framework/LoadAll.php
+++ b/src/Framework/LoadAll.php
@@ -24,7 +24,7 @@ final class LoadAll
self::doLoop($cdir, $config_path);
}
}
- $service_path = $path . "Services";
+ $service_path = $path . "LoadServices";
if (is_dir($service_path)) {
$sdir = scandir($service_path);
if ($sdir !== false) {
diff --git a/src/Framework/Middleware/ErrorMiddleware.php b/src/Framework/Middleware/ErrorMiddleware.php
index 1eb267b..82217f5 100644
--- a/src/Framework/Middleware/ErrorMiddleware.php
+++ b/src/Framework/Middleware/ErrorMiddleware.php
@@ -21,7 +21,7 @@ final class ErrorMiddleware implements MiddlewareInterface
{
public function __construct(
private LoggerInterface $logger,
- private bool $displayErrors = false
+ private bool $hideErrors = false
) {}
public function process(
@@ -31,12 +31,14 @@ final class ErrorMiddleware implements MiddlewareInterface
try {
return $handler->handle($request);
} catch (\Throwable $e) {
+ $codeNumber = Reg::get('error_handler')->getCodeNumber($e);
+ $message = $codeNumber . $e->getMessage();
$this->logger->error(
- $e->getMessage(),
+ $message,
['exception' => $e]
);
- $bodyString = $this->displayErrors
+ $bodyString = $this->hideErrors
? $this->formatException($e)
: 'Internal Server Error';
@@ -55,12 +57,14 @@ final class ErrorMiddleware implements MiddlewareInterface
}
if ($live) {
- return Reg::get('error_handler')->getProdMessage();
+ return Reg::get('error_handler')->getProdMessage($e);
}
if (Console::isConsole()) {
+ $codeNumber = $e->getCode() ?? 0;
return sprintf(
- "%s\n\n%s",
+ "Code# %d; %s\n\n%s",
+ $codeNumber,
$e->getMessage(),
$e->getTraceAsString()
);
diff --git a/src/Framework/ParagonCrypto/Crypto.php b/src/Framework/ParagonCrypto/Crypto.php
index df8a651..811752e 100644
--- a/src/Framework/ParagonCrypto/Crypto.php
+++ b/src/Framework/ParagonCrypto/Crypto.php
@@ -53,7 +53,7 @@ class Crypto {
): string {
$key = $this->key;
- $nonce = $this->rnd->get_bytes(
+ $nonce = $this->rnd->getBytes(
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
);
$fn = ($key_usage == self::singleKey) ? "sodium_crypto_secretbox" : "sodium_crypto_box";
diff --git a/src/Framework/ParagonCrypto/PasswordStorage.php b/src/Framework/ParagonCrypto/PasswordStorage.php
index 7fff020..5466c78 100644
--- a/src/Framework/ParagonCrypto/PasswordStorage.php
+++ b/src/Framework/ParagonCrypto/PasswordStorage.php
@@ -21,7 +21,7 @@ class PasswordStorage {
}
public function generateKey(): string {
- return sodium_bin2hex($this->random_engine->get_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES));
+ return sodium_bin2hex($this->random_engine->getBytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES));
}
/**
@@ -38,11 +38,11 @@ class PasswordStorage {
SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE
);
- $salt = $this->random_engine->get_bytes(self::SALT_SIZE_IN_BYTES);
+ $salt = $this->random_engine->getBytes(self::SALT_SIZE_IN_BYTES);
list ($enc_key, $auth_key) = $this->splitKeys($secret_key, sodium_bin2hex($salt));
sodium_memzero($secret_key);
sodium_memzero($password);
- $nonce = $this->random_engine->get_bytes(
+ $nonce = $this->random_engine->getBytes(
SODIUM_CRYPTO_STREAM_NONCEBYTES
);
$cipher_text = sodium_crypto_stream_xor(
diff --git a/src/Framework/ParagonCrypto/SodiumStorage.php b/src/Framework/ParagonCrypto/SodiumStorage.php
index 0d70fd1..f285068 100644
--- a/src/Framework/ParagonCrypto/SodiumStorage.php
+++ b/src/Framework/ParagonCrypto/SodiumStorage.php
@@ -35,10 +35,10 @@ class SodiumStorage {
}
public function encrypt(#[\SensitiveParameter] string $plain_text, string $item_name = ""): string {
- $nonce = $this->randomEngine->get_bytes(
+ $nonce = $this->randomEngine->getBytes(
SODIUM_CRYPTO_STREAM_NONCEBYTES
);
- $salt = $this->randomEngine->get_bytes(self::SALT_SIZE_IN_BYTES);
+ $salt = $this->randomEngine->getBytes(self::SALT_SIZE_IN_BYTES);
list ($enc_key, $auth_key) = $this->splitKeys($item_name, sodium_bin2hex($salt));
$cipher_text = sodium_crypto_stream_xor(
$plain_text,
diff --git a/src/Framework/Playground/HttpContainer.php b/src/Framework/Playground/HttpContainer.php
deleted file mode 100644
index 4ccd560..0000000
--- a/src/Framework/Playground/HttpContainer.php
+++ /dev/null
@@ -1,109 +0,0 @@
-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;
- }
-}
\ No newline at end of file
diff --git a/src/Framework/Playground/JunkForNow.php b/src/Framework/Playground/JunkForNow.php
deleted file mode 100644
index 184f037..0000000
--- a/src/Framework/Playground/JunkForNow.php
+++ /dev/null
@@ -1,79 +0,0 @@
- '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;
- }
-}
diff --git a/src/Framework/Playground/RouteServiceProvider.php b/src/Framework/Playground/RouteServiceProvider.php
deleted file mode 100644
index 1ffa46b..0000000
--- a/src/Framework/Playground/RouteServiceProvider.php
+++ /dev/null
@@ -1,77 +0,0 @@
-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']);
- }
- });
- }
-}
diff --git a/src/Framework/Playground/Router.php b/src/Framework/Playground/Router.php
deleted file mode 100644
index c56faa5..0000000
--- a/src/Framework/Playground/Router.php
+++ /dev/null
@@ -1,492 +0,0 @@
- '(\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];
- }
-
-}
\ No newline at end of file
diff --git a/src/Framework/Playground/ServiceProvider.php b/src/Framework/Playground/ServiceProvider.php
deleted file mode 100644
index 2bc0de5..0000000
--- a/src/Framework/Playground/ServiceProvider.php
+++ /dev/null
@@ -1,46 +0,0 @@
-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')
- );
- });
- }
-}
- */
\ No newline at end of file
diff --git a/src/Framework/Playground/pipeOp.php b/src/Framework/Playground/pipeOp.php
deleted file mode 100644
index 485d6aa..0000000
--- a/src/Framework/Playground/pipeOp.php
+++ /dev/null
@@ -1,32 +0,0 @@
-)
- * 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(...);
- *
- */
\ No newline at end of file
diff --git a/src/Framework/SaferOutput.php b/src/Framework/SaferOutput.php
new file mode 100644
index 0000000..ad039b4
--- /dev/null
+++ b/src/Framework/SaferOutput.php
@@ -0,0 +1,156 @@
+
+ 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);
+ }
+}
diff --git a/src/Framework/Security.php b/src/Framework/Security.php
index 1b434b4..3f283a9 100644
--- a/src/Framework/Security.php
+++ b/src/Framework/Security.php
@@ -1,17 +1,19 @@
getBytes($bytes));
+ }
+
+ public static function base64PWD(int $bytes = 16): string
+ {
+ $r = new RandomEngine();
+ return rtrim(strtr(
+ base64_encode($r->getBytes($bytes)),
+ '+/',
+ '-_'
+ ), '=');
+ }
+
/**
* Get unique IDs for database
* @return int
*/
- public static function getUniqueNumber(): int {
+ public static function getUniqueNumber(): int
+ {
return abs(crc32(microtime()));
}
@@ -36,13 +56,15 @@ class Security
* Get token
* @return string
*/
- public static function getUniqueId(): string {
+ public static function getUniqueId(): string
+ {
$moreEntropy = true;
$prefix = ""; // Blank is a rand string
return md5(uniqid($prefix, $moreEntropy));
}
- public static function useHmac(string $algo, string $pepper) {
+ public static function useHmac(string $algo, string $pepper)
+ {
if (!function_exists("hash_hmac_algos")) {
throw new \Exception("hash_hmac not installed!");
}
@@ -72,8 +94,9 @@ class Security
* intentionally slower hashing algorithms such as bcrypt
* or Argon2 should be used.
*/
-
- public static function findDefaultHashAlgo() {
+
+ public static function findDefaultHashAlgo()
+ {
if (defined("PASSWORD_ARGON2ID"))
return PASSWORD_ARGON2ID;
if (defined("PASSWORD_ARGON2"))
@@ -84,35 +107,38 @@ class Security
return PASSWORD_BCRYPT;
return false;
}
-
- private static function isValidHashAlgo($algo): bool {
+
+ private static function isValidHashAlgo($algo): bool
+ {
return (in_array($algo, password_algos()));
- }
-
+ }
+
/*
* The password_hash() function not only uses a secure
* one-way hashing algorithm, but it automatically handles
* salt and prevents time based side-channel attacks.
*/
-
- public static function do_password_hash(#[\SensitiveParameter] string $password): bool | string {
+
+ public static function do_password_hash(#[\SensitiveParameter] string $password): bool|string
+ {
$pwdPeppered = self::makeHash($password);
$hashAlgo = Configure::get(
- "security",
- "hash_algo"
- ) ?? false;
+ "security",
+ "hash_algo"
+ ) ?? false;
if ($hashAlgo === false) {
throw new \Exception("Security Hash Algo not set!");
}
- if (! self::isValidHashAlgo($hashAlgo)) {
+ if (!self::isValidHashAlgo($hashAlgo)) {
throw new \Exception("Invalid Security Hash Alogo set");
}
return password_hash($pwdPeppered, $hashAlgo);
}
-
+
public static function doPasswordVerify(
- #[\SensitiveParameter] string $inputPwd, #[\SensitiveParameter] $dbPassword
- ): bool {
+ #[\SensitiveParameter] string $inputPwd, #[\SensitiveParameter] $dbPassword
+ ): bool
+ {
$pwdPeppered = self::makeHash($inputPwd);
return password_verify($pwdPeppered, $dbPassword);
}
@@ -123,7 +149,8 @@ class Security
* @param string $level (weak, low, high, max)
* @return string new Hashed
*/
- public static function makeHash(#[\SensitiveParameter] string $text): string {
+ public static function makeHash(#[\SensitiveParameter] string $text): string
+ {
$level = Configure::get('security', 'hash_level');
if (empty($level)) {
$level = "normal";
@@ -167,19 +194,20 @@ class Security
}
return self::useHmac($level, $pepper);
}
-
- /**
- * @method filter_class
- * @param type $class
- * Please NEVER add a period or SLASH as it will allow BAD things!
- * IT should be a-zA-Z0-9_ and that's it.
- * @retval string of safe class name
- */
- public static function filterClass(string $class): string {
- if (Requires::isDangerous($class)) {
- throw new \Exception("Dangerious URI!");
- }
- return preg_replace('/[^a-zA-Z0-9_]/', '', $class);
+
+ /**
+ * @method filter_class
+ * @param type $class
+ * Please NEVER add a period or SLASH as it will allow BAD things!
+ * IT should be a-zA-Z0-9_ and that's it.
+ * @retval string of safe class name
+ */
+ public static function filterClass(string $class): string
+ {
+ if (Requires::isDangerous($class)) {
+ throw new \Exception("Dangerious URI!");
+ }
+ return preg_replace('/[^a-zA-Z0-9_]/', '', $class);
}
/**
@@ -187,48 +215,55 @@ class Security
* @param string $uri
* @return string Safe URI
*/
- public static function filterUri(string $uri): string {
+ public static function filterUri(string $uri): string
+ {
if (Requires::isDangerous($uri) === true) {
throw new \Exception("Dangerious URI!");
}
return Requires::filterFileName($uri);
}
- public static function idHash(): string {
+ public static function idHash(): string
+ {
return crc32($_SESSION['user_id']);
}
-
- public static function isPrivateOrLocalIPSimple(string $ip): bool {
- if (! self::getValidIp($ip)) {
+
+ public static function isPrivateOrLocalIPSimple(string $ip): bool
+ {
+ if (!self::getValidIp($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)
+ $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 getValidIp(string $ip) {
- return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6));
+ public static function getValidIp(string $ip)
+ {
+ return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6));
}
- public static function getValidPublicIp(string $ip) {
- return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|FILTER_FLAG_NO_PRIV_RANGE));
+
+ public static function getValidPublicIp(string $ip)
+ {
+ return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE));
}
/**
* Is the server on the local test domain name
* @return bool SERVER Domain name is on whitelist
*/
- public static function isServerNameOnDomainList(array $whitelist): bool {
+ public static function isServerNameOnDomainList(array $whitelist): bool
+ {
if (!isset($_SERVER['SERVER_NAME'])) {
return false;
}
@@ -239,7 +274,8 @@ class Security
* Check if same Domain as Server
* @return bool
*/
- public static function requestIsSameDomain(): bool {
+ public static function requestIsSameDomain(): bool
+ {
if (!isset($_SERVER['HTTP_REFERER'])) {
// No referer send, so can't be same domain!
return false;
@@ -255,14 +291,15 @@ class Security
}
}
- public static function safeForEval(string $s): string {
+ public static function safeForEval(string $s): string
+ {
//new line check
$nl = chr(10);
if (strpos($s, $nl)) {
throw new \Exception("String CR/LF not permitted");
}
- $meta = ['$','{','}','[',']','`',';'];
- $escaped = ['$','{','}','[','`',';'];
+ $meta = ['$', '{', '}', '[', ']', '`', ';'];
+ $escaped = ['$', '{', '}', '[', '`', ';'];
// add slashed for quotes and blackslashes
$out = addslashes($s);
// replace php meta chrs
@@ -270,7 +307,8 @@ class Security
return $out;
}
- public static function getClientIpAddress() {
+ public static function getClientIpAddress(): string
+ {
$ipaddress = '';
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
@@ -296,7 +334,8 @@ class Security
* @param string $file
* @return bool true if PHP was found
*/
- public static function fileContainsPhp(string $file): bool {
+ public static function fileContainsPhp(string $file): bool
+ {
$file_handle = fopen($file, "r");
while (!feof($file_handle)) {
$line = fgets($file_handle);
@@ -308,5 +347,5 @@ class Security
}
fclose($file_handle);
return false;
- }
+ }
}
diff --git a/src/Framework/Services/Sessions/CookieSessionHandler.php b/src/Framework/Services/Sessions/CookieSessionHandler.php
index 109e720..d67490d 100644
--- a/src/Framework/Services/Sessions/CookieSessionHandler.php
+++ b/src/Framework/Services/Sessions/CookieSessionHandler.php
@@ -6,6 +6,7 @@ namespace IOcornerstone\Framework\Services\Sessions;
use IOcornerstone\Framework\GzCompression;
use IOcornerstone\Framework\Enum\CompressionMethod as Method;
+use IOcornerstone\ErrorCodes;
/**
* @author Robert Strutts
@@ -93,7 +94,12 @@ class CookieSessionHandler implements \SessionHandlerInterface {
$data = $de; // data is now decrypted
}
}
- $gd = $this->compression->decompress($data);
+ try {
+ $gd = $this->compression->decompress($data);
+ } catch (\Exception $ex) {
+ echo ErrorCodes::getErrorCodeFor(ErrorCodes::GZ_Inflate_Cookie_Data_Tampered_With, $ex, throwMe: true);
+ return "";
+ }
return ($gd !== false) ? $gd : $data;
}
diff --git a/src/Framework/Trait/Security/CsrfTokenFunctions.php b/src/Framework/Trait/Security/CsrfTokenFunctions.php
index ec2faa0..1e141df 100644
--- a/src/Framework/Trait/Security/CsrfTokenFunctions.php
+++ b/src/Framework/Trait/Security/CsrfTokenFunctions.php
@@ -14,82 +14,133 @@ use IOcornerstone\Framework\Configure;
trait CsrfTokenFunctions
{
- /**
- * Get an Cross-Site Request Forge - Prevention Token
- * @return string
- */
- public static function csrfToken(): string {
- return self::get_unique_id();
- }
/**
* Set Session to use CSRF Token
+ * Useful for JSON data...
* @return string CSRF Token
*/
- public static function createCsrfToken(): string {
- $token = self::csrfToken();
- $_SESSION['csrf_token'] = $token;
- $_SESSION['csrf_token_time'] = time();
- return $token;
+ public static function createCsrfToken(): string
+ {
+ self::setup();
+
+ $newToken = self::csrfToken();
+ $_SESSION['csrf_pool'][$newToken] = time();
+
+ return $newToken;
}
/**
- * Destroy CSRF Token from Session
- * @return bool success
+ * Keep only last 15 tokens to prevent memory issues.
*/
- public static function destroyCsrfToken(): bool {
- $_SESSION['csrf_token'] = null;
- $_SESSION['csrf_token_time'] = null;
- return true;
+ public static function cleanUp(){
+ $keepOnly = 15;
+ if (count($_SESSION['csrf_pool']) > $keepOnly) {
+ $_SESSION['csrf_pool'] = array_slice($_SESSION['csrf_pool'], -$keepOnly, null, true);
+ }
}
-
+
/**
* Get CSRF Token for use with HTML Form
* @return string Hidden Form with token set
*/
- public static function csrfTokenTag(): string {
+ public static function csrfTokenTag(): string
+ {
$token = self::createCsrfToken();
- return "";
+ return "";
}
- /**
- * Check if POST data CSRF Token is Valid
- * @return bool is valid
- */
- public static function csrfTokenIsValid(int $filter = FILTER_UNSAFE_RAW): bool {
- $isCsrf = filter_has_var(INPUT_POST, 'csrf_token');
- if ($isCsrf) {
- $userToken = filter_input(INPUT_POST, 'csrf_token', $filter);
- $storedToken = $_SESSION['csrf_token'] ?? '';
- if (empty($storedToken)) {
- return false;
- }
- return ($user_token === $stored_token);
- } else {
+ public static function csrfTokenStillValid(string $csrfTokenKeyName = ""): bool
+ {
+ if (empty($csrfTokenKeyName)) {
+ $csrfTokenKeyName = $_POST['csrf_token'] ?? "";
+ }
+
+ $validTimeStamp = self::csrfTokenIsValid($csrfTokenKeyName);
+ if ($validTimeStamp === false) {
return false;
}
+ $recent = self::csrfTokenIsRecent($validTimeStamp);
+
+ self::destroyCsrfToken($csrfTokenKeyName); // Done, so clean up Consume token
+
+ return $recent;
}
/**
- * Optional check to see if token is also recent
- * @return bool
+ * Get an Cross-Site Request Forge - Prevention Token
+ * @return string
*/
- public static function csrfTokenIsRecent(): bool {
+ private static function csrfToken(): string
+ {
+ return self::getUniqueId();
+ }
+
+ private static function setup(): void
+ {
+ $clean_ts = intval(Configure::get(
+ 'security',
+ 'token_life'
+ ));
+
+ if ($clean_ts < 60) {
+ $clean_ts = 3600;
+ }
+
+ if (!isset($_SESSION['csrf_pool'])) {
+ $_SESSION['csrf_pool'] = [];
+ }
+
+ // Clean old tokens by useage time token_life
+ foreach ($_SESSION['csrf_pool'] as $key => $timestamp) {
+ if ($timestamp < (time() - $clean_ts)) {
+ unset($_SESSION['csrf_pool'][$key]);
+ }
+ }
+ }
+
+ private static function csrfTokenIsValid(string $csrfTokenKeyName = ""): false|int
+ {
+ if (empty($csrfTokenKeyName)) {
+ $csrfTokenKeyName = $_POST['csrf_token'] ?? "";
+ }
+
+ if (!isset($_SESSION['csrf_pool'][$csrfTokenKeyName])) {
+ return false;
+ }
+ $tsToken = $_SESSION['csrf_pool'][$csrfTokenKeyName];
+ if (is_int($tsToken)){
+ return $tsToken;
+ }
+ return false;
+ }
+
+ private static function csrfTokenIsRecent($tokenTimeStored): bool
+ {
$max_elapsed = intval(Configure::get(
- 'security',
+ 'security',
'max_token_age'
));
if ($max_elapsed < 30) {
$max_elapsed = 60 * 60 * 24; // 1 day
}
- if (isset($_SESSION['csrf_token_time'])) {
- $stored_time = $_SESSION['csrf_token_time'];
- return ($stored_time + $max_elapsed) >= time();
- } else {
- // Remove expired token
- self::destroyCsrfToken();
+ return ($tokenTimeStored + $max_elapsed) >= time();
+ }
+
+ /**
+ * Destroy CSRF Token from Session
+ * @return bool success
+ */
+ private static function destroyCsrfToken(string $csrfTokenKeyName = ""): bool
+ {
+ if (empty($csrfTokenKeyName)) {
+ $csrfTokenKeyName = $_POST['csrf_token'] ?? "";
+ }
+ if (!isset($_SESSION['csrf_pool'][$csrfTokenKeyName])) {
return false;
}
+ unset($_SESSION['csrf_pool'][$csrfTokenKeyName]); // Consume token
+ return true;
}
}
diff --git a/src/Framework/Trait/Security/SessionHijackingFunctions.php b/src/Framework/Trait/Security/SessionHijackingFunctions.php
index 8e482c1..35f1625 100644
--- a/src/Framework/Trait/Security/SessionHijackingFunctions.php
+++ b/src/Framework/Trait/Security/SessionHijackingFunctions.php
@@ -18,13 +18,18 @@ use IOcornerstone\Framework\Registry;
*/
trait SessionHijackingFunctions
{
+ /**
+ * Begin Sessions
+ */
public static function initSessions() {
if (Registry::get('di')->has('sessions')) {
Registry::get('di')->get_service('sessions');
}
}
-// Function to forcibly end the session
+/**
+ * Function to forcibly end the session
+ */
public static function endSession() {
// Use both for compatibility with all browsers
// and all versions of PHP.
@@ -32,59 +37,18 @@ trait SessionHijackingFunctions
session_destroy();
}
-// Does the request IP match the stored value?
- public static function requestIpMatchesSession() {
- // return false if either value is not set
- if (!isset($_SESSION['ip']) || !isset($_SERVER['REMOTE_ADDR'])) {
- return false;
- }
- if ($_SESSION['ip'] === $_SERVER['REMOTE_ADDR']) {
- return true;
- } else {
- return false;
- }
- }
-
-// Does the request user agent match the stored value?
- public static function requestUserAgentMatchesSession() {
- // return false if either value is not set
- if (!isset($_SESSION['user_agent']) || !isset($_SERVER['HTTP_USER_AGENT'])) {
- return false;
- }
- if ($_SESSION['user_agent'] === $_SERVER['HTTP_USER_AGENT']) {
- return true;
- } else {
- return false;
- }
- }
-
-// Has too much time passed since the last login?
- public static function lastLoginIsRecent() {
- $max_elapsed = intval(Configure::get(
- 'security',
- 'max_last_login_age'
- ));
- if ($max_elapsed < 30) {
- $max_elapsed = 60 * 60 * 24; // 1 day
- }
-
- // return false if value is not set
- if (!isset($_SESSION['last_login'])) {
- return false;
- }
- if (($_SESSION['last_login'] + $max_elapsed) >= time()) {
- return true;
- } else {
- return false;
- }
- }
-
-// Should the session be considered valid?
- public static function isSessionValid() {
- $check_ip = true;
- $check_user_agent = true;
- $check_last_login = true;
-
+/**
+ *
+ * @param bool $check_ip
+ * @param bool $check_user_agent
+ * @param bool $check_last_login
+ * @return bool Should the session be considered valid?
+ */
+ public static function isSessionValid(
+ bool $check_ip = true,
+ bool $check_user_agent = true,
+ bool $check_last_login = true,
+ ) {
if ($check_ip && !self::requestIpMatchesSession()) {
return false;
}
@@ -97,28 +61,20 @@ trait SessionHijackingFunctions
return true;
}
-// If session is not valid, end and redirect to login page.
- public static function confirmSessionIsValid(
- string $login = "login.php"
- ) {
- if (!self::isSessionValid()) {
- self::endSession();
- // Note that header redirection requires output buffering
- // to be turned on or requires nothing has been output
- // (not even whitespace).
- header("Location: " . $login );
- exit;
- }
- }
-
-// Is user logged in already?
- public static function isLoggedIn() {
+/**
+ *
+ * @return bool Is user logged in already?
+ */
+ public static function isLoggedIn(): bool {
return (isset($_SESSION['logged_in']) && $_SESSION['logged_in']);
}
-// If user is not logged in, end and redirect to login page.
+/**
+ * If user is not logged in, end and redirect to login page.
+ * @param string $login
+ */
public static function confirmUserLoggedIn(
- string $login = "login.php"
+ string $login = "/App/Home/Login.html"
) {
if (!self::isLoggedIn()) {
self::endSession();
@@ -130,7 +86,9 @@ trait SessionHijackingFunctions
}
}
-// Actions to preform after every successful login
+/**
+ * Actions to preform after every successful login
+ */
public static function afterSuccessfulLogin() {
// Regenerate session ID to invalidate the old one.
// Super important to prevent session hijacking/fixation.
@@ -144,18 +102,103 @@ trait SessionHijackingFunctions
$_SESSION['last_login'] = time();
}
-// Actions to preform after every successful logout
+/**
+ * Actions to preform after every successful logout
+ */
public static function afterSuccessfulLogout() {
$_SESSION['logged_in'] = false;
self::endSession();
}
-// Actions to preform before giving access to any
-// access-restricted page.
+/**
+ * Actions to preform before giving access to any
+ * access-restricted page.
+ */
public static function beforeEveryProtectedPage(
- string $login = "login.php"
+ string $login = "/App/Home/Login.html",
+ array $options = []
) {
self::confirmUserLoggedIn($login);
- self::confirmSessionIsValid();
+ self::confirmSessionIsValid($login, $options);
}
+
+/**
+ * If session is not valid, end and redirect to login page.
+ * @param string $login
+ * @param array $options
+ */
+ private static function confirmSessionIsValid(
+ string $login = "/App/Home/Login.html",
+ array $options = []
+ ) {
+ if (!self::isSessionValid(
+ $options['check_ip'] ?? true,
+ $options['check_agent'] ?? true,
+ $options['check_is_recent_login'] ?? true
+ )) {
+ self::endSession();
+ // Note that header redirection requires output buffering
+ // to be turned on or requires nothing has been output
+ // (not even whitespace).
+ header("Location: " . $login );
+ exit;
+ }
+ }
+
+/**
+ *
+ * @return bool Does the request IP match the stored value?
+ */
+ private static function requestIpMatchesSession() {
+ // return false if either value is not set
+ if (!isset($_SESSION['ip']) || !isset($_SERVER['REMOTE_ADDR'])) {
+ return false;
+ }
+ if ($_SESSION['ip'] === $_SERVER['REMOTE_ADDR']) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+/**
+ *
+ * @return bool Does the request user agent match the stored value?
+ */
+ private static function requestUserAgentMatchesSession() {
+ // return false if either value is not set
+ if (!isset($_SESSION['user_agent']) || !isset($_SERVER['HTTP_USER_AGENT'])) {
+ return false;
+ }
+ if ($_SESSION['user_agent'] === $_SERVER['HTTP_USER_AGENT']) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+/**
+ *
+ * @return bool Has too much time passed since the last login?
+ */
+ private static function lastLoginIsRecent() {
+ $max_elapsed = intval(Configure::get(
+ 'security',
+ 'max_last_login_age'
+ ));
+ if ($max_elapsed < 30) {
+ $max_elapsed = 60 * 60 * 24; // 1 day
+ }
+
+ // return false if value is not set
+ if (!isset($_SESSION['last_login'])) {
+ return false;
+ }
+ if (($_SESSION['last_login'] + $max_elapsed) >= time()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
}
diff --git a/src/Framework/View.php b/src/Framework/View.php
index 9bf5831..85143fc 100644
--- a/src/Framework/View.php
+++ b/src/Framework/View.php
@@ -173,7 +173,7 @@ final class View
* @param mixed $name Name of the variable to set in the view, or an array of key/value pairs where each key is the variable and each value is the value to set.
* @param mixed $value Value of the variable to set in the view.
*/
- public function set($name, mixed $value = null): void
+ public function set(array|string $name, mixed $value = null): void
{
if (is_array($name)) {
foreach ($name as $var_name => $value) {
@@ -212,6 +212,10 @@ final class View
$local = $this; // FALL Back, please use fetch($this);
}
+ if (isset($local->html)) {
+ $this->vars['html'] = $local->html;
+ }
+
if ($this->useTemplateEngineLiquid) {
$this->tempalteEngineLiquid = Registry::get('di')->get_service('liquid', [$this->templateType]);
if ($this->whiteSpaceControl) {
diff --git a/src/Psr4AutoloaderClass.php b/src/Psr4AutoloaderClass.php
index fbc2a30..b8a03a2 100644
--- a/src/Psr4AutoloaderClass.php
+++ b/src/Psr4AutoloaderClass.php
@@ -10,171 +10,212 @@ namespace IOcornerstone;
* @license MIT
* @site https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md
*/
-final class Psr4AutoloaderClass {
-
- /**
- * An associative array where the key is a namespace prefix and the value
- * is an array of base directories for classes in that namespace.
- *
- * @var array
- */
- protected $prefixes = [];
- protected $loadedFiles = [];
-
- /**
- * Register loader with SPL autoloader stack.
- *
- * @return void
- */
- public function register() {
- spl_autoload_register(array($this, 'loadClass'));
- }
-
- public function isLoaded(string $prefix): bool {
- $prefix = trim($prefix, '\\') . '\\';
- return (isset($this->prefixes[$prefix])) ? true : false;
- }
-
- public function getList(): array {
- return $this->prefixes;
- }
-
- public function getFilesList(): array {
- return $this->loadedFiles;
- }
-
- /**
- * Adds a base directory for a namespace prefix.
- *
- * @param string $prefix The namespace prefix.
- * @param string|array $base_dir A base directory for class files in the
- * namespace.
- * @param bool $prepend If true, prepend the base directory to the stack
- * instead of appending it; this causes it to be searched first rather
- * than last.
- * @return void
- */
- public function addNamespace(string $prefix, string|array $baseDir, bool $prepend = false): void {
- $prefix = trim($prefix, '\\') . '\\';
-
- // Normalize baseDir to always be an array
- if (!is_array($baseDir)) {
- $baseDir = [$baseDir];
+final class Psr4AutoloaderClass
+{
+ /*
+ * log, and sql do not contian PHP files, so they are fine...
+ * a correct NameSpace must be set in the files, so this is not a bug issue...
+ */
+
+ private $blockListOfDirs = [
+ "vendor", // Stop Entry into Vendor folder
+ ];
+
+ /**
+ * An associative array where the key is a namespace prefix and the value
+ * is an array of base directories for classes in that namespace.
+ *
+ * @var array
+ */
+ protected $prefixes = [];
+ protected $loadedFiles = [];
+
+ /**
+ * Register loader with SPL autoloader stack.
+ */
+ public function register(): void
+ {
+ /**
+ * use [[[ var_dump ]]] in this file or enable dd & Dumps here...
+ */
+// $this->enableDumps();
+ spl_autoload_register(array($this, 'loadClass'));
}
-
- // Normalize each directory path
- $baseDir = array_map(function($dir) {
- return rtrim($dir, DIRECTORY_SEPARATOR) . '/';
- }, $baseDir);
-
- // Initialize array if prefix doesn't exist
- if (!isset($this->prefixes[$prefix])) {
- $this->prefixes[$prefix] = [];
+
+ public function enableDumps(): void
+ {
+ include IO_CORNERSTONE_FRAMEWORK . "Framework" . DIRECTORY_SEPARATOR . "Enum" . DIRECTORY_SEPARATOR . "ExitOnDump.php";
+ include IO_CORNERSTONE_FRAMEWORK . "Framework" . DIRECTORY_SEPARATOR . "Common.php";
+ include IO_CORNERSTONE_FRAMEWORK . "Framework" . DIRECTORY_SEPARATOR . "Configure.php";
}
-
- // Add directories
- if ($prepend) {
- // Merge and prepend new directories
- $this->prefixes[$prefix] = array_merge($baseDir, $this->prefixes[$prefix]);
- } else {
- // Merge and append new directories
- $this->prefixes[$prefix] = array_merge($this->prefixes[$prefix], $baseDir);
+
+ public function isLoaded(string $prefix): bool
+ {
+ $prefix = trim($prefix, '\\') . '\\';
+ return (isset($this->prefixes[$prefix])) ? true : false;
}
-
- // Optional: Remove duplicates while preserving order
- $this->prefixes[$prefix] = array_values(array_unique($this->prefixes[$prefix]));
-}
-/**
- * Loads the class file for a given class name.
- *
- * @param string $class The fully-qualified class name.
- * @return mixed The mapped file name on success, or boolean false on
- * failure.
- */
-protected function loadClass(string $class): false|string {
- if (!strrpos($class, '\\')) {
- $ret = $this->loadMappedFile($class . '\\', $class);
- if ($ret !== false) {
- return $ret;
- }
- }
-
- $prefix = $class;
- while (false !== $pos = strrpos($prefix, '\\')) {
- // retain the trailing namespace separator in the prefix
- $prefix = substr($class, 0, $pos + 1);
- $relativeClass = substr($class, $pos + 1);
-
- $mappedFile = $this->loadMappedFile($prefix, $relativeClass);
- if ($mappedFile) {
- return $mappedFile;
- }
-
- // remove the trailing namespace separator for the next iteration
- $prefix = rtrim($prefix, '\\');
- }
-
- // never found a mapped file
- return false;
-}
+ public function getList(): array
+ {
+ return $this->prefixes;
+ }
-/**
- * Load the mapped file for a namespace prefix and relative class.
- *
- * @param string $prefix The namespace prefix
- * @param string $relativeClass The relative class name
- * @return false|string Boolean false if no mapped file can be loaded,
- * or the name of the mapped file that was loaded
- */
-protected function loadMappedFile(string $prefix, string $relativeClass): false|string {
- // Check if there are any base directories for this namespace prefix
- if (!isset($this->prefixes[$prefix])) {
- return false;
- }
-
- // Iterate through all base directories for this prefix
- foreach ($this->prefixes[$prefix] as $baseDir) {
- // Replace namespace separators with directory separators
- $file = str_replace('\\', '/', $relativeClass) . '.php';
-
- // If the mapped file exists, require it
- if ($this->requireFile($baseDir, $file)) {
- return $file;
- }
- }
-
- // None of the directories contained the file
- return false;
-}
+ public function getFilesList(): array
+ {
+ return $this->loadedFiles;
+ }
- /**
- * If a file exists, require it from the file system.
- *
- * @param string $file The file to require.
- * @return bool True if the file exists, false if not.
- */
- private function requireFile(string $path, string $file): bool {
- if ($file === "Framework/Requires.php") {
- return true; // Already used...
+ /**
+ * Adds a base directory for a namespace prefix.
+ *
+ * @param string $prefix The namespace prefix.
+ * @param string|array $base_dir A base directory for class files in the
+ * namespace.
+ * @param bool $prepend If true, prepend the base directory to the stack
+ * instead of appending it; this causes it to be searched first rather
+ * than last.
+ * @return void
+ */
+ public function addNamespace(
+ string $prefix, string|array $baseDir,
+ bool $prepend = false
+ ): void
+ {
+ $prefix = trim($prefix, '\\') . '\\';
+
+ // Normalize baseDir to always be an array
+ if (!is_array($baseDir)) {
+ $baseDir = [$baseDir];
+ }
+
+ // Normalize each directory path
+ $baseDir = array_map(function ($dir) {
+ return rtrim($dir, DIRECTORY_SEPARATOR) . '/';
+ }, $baseDir);
+
+ // Initialize array if prefix doesn't exist
+ if (!isset($this->prefixes[$prefix])) {
+ $this->prefixes[$prefix] = [];
+ }
+
+ // Add directories
+ if ($prepend) {
+ // Merge and prepend new directories
+ $this->prefixes[$prefix] = array_merge($baseDir, $this->prefixes[$prefix]);
+ } else {
+ // Merge and append new directories
+ $this->prefixes[$prefix] = array_merge($this->prefixes[$prefix], $baseDir);
+ }
+
+ // Optional: Remove duplicates while preserving order
+ $this->prefixes[$prefix] = array_values(array_unique($this->prefixes[$prefix]));
}
- $req_class = \IOcornerstone\Framework\Requires::class;
- if (! method_exists($req_class, "saferFileExists")) {
- require_once IO_CORNERSTONE_FRAMEWORK . DIRECTORY_SEPARATOR . "Framework" . DIRECTORY_SEPARATOR . "Requires.php";
+
+ /**
+ * Loads the class file for a given class name.
+ *
+ * @param string $class The fully-qualified class name.
+ * @return mixed The mapped file name on success, or boolean false on
+ * failure.
+ */
+ protected function loadClass(string $class): false|string
+ {
+ if (!strrpos($class, '\\')) {
+ $ret = $this->loadMappedFile($class . '\\', $class);
+ if ($ret !== false) {
+ return $ret;
+ }
+ }
+
+ $prefix = $class;
+ while (false !== $pos = strrpos($prefix, '\\')) {
+ // retain the trailing namespace separator in the prefix
+ $prefix = substr($class, 0, $pos + 1);
+ $relativeClass = substr($class, $pos + 1);
+
+ $mappedFile = $this->loadMappedFile($prefix, $relativeClass);
+ if ($mappedFile) {
+ return $mappedFile;
+ }
+
+ // remove the trailing namespace separator for the next iteration
+ $prefix = rtrim($prefix, '\\');
+ }
+
+ // never found a mapped file
+ return false;
}
- $saferFile = $req_class::saferFileExists($file, $path);
- if ($saferFile !== false) {
- if (defined('CountFiles') && CountFiles) {
- if (! isset($this->loadedFiles[$saferFile])) {
- require $saferFile;
- $this->loadedFiles[$saferFile] = true;
- }
- } else {
- require_once $saferFile;
- }
- return true;
+
+ /**
+ * Load the mapped file for a namespace prefix and relative class.
+ *
+ * @param string $prefix The namespace prefix
+ * @param string $relativeClass The relative class name
+ * @return false|string Boolean false if no mapped file can be loaded,
+ * or the name of the mapped file that was loaded
+ */
+ protected function loadMappedFile(
+ string $prefix,
+ string $relativeClass
+ ): false|string
+ {
+ // Check if there are any base directories for this namespace prefix
+ if (!isset($this->prefixes[$prefix])) {
+ return false;
+ }
+ // Iterate through all base directories for this prefix
+ foreach ($this->prefixes[$prefix] as $baseDir) {
+ // Replace namespace separators with directory separators
+ $file = str_replace('\\', '/', $relativeClass);
+ /**
+ * Lock Down our NameSpaces More...
+ */
+ if ($prefix === "IOcornerstone\\" || $prefix === "Project\\") {
+ foreach ($this->blockListOfDirs as $blockedDir) {
+ $sDir = "/" . $blockedDir . "/";
+ if (str_contains($file, $sDir)) {
+ return false; // Return false = Deny
+ }
+ }
+ }
+ $file .= ".php"; // Make sure we ONLY work with PHP!!!!
+ // If the mapped file exists, require it
+ if ($this->requireFile($baseDir, $file)) {
+ return $file;
+ }
+ }
+
+ // None of the directories contained the file
+ return false;
}
- return false;
- }
+ /**
+ * If a file exists, require it from the file system.
+ *
+ * @param string $file The file to require.
+ * @return bool True if the file exists, false if not.
+ */
+ private function requireFile(string $path, string $file): bool
+ {
+ if ($file === "Framework/Requires.php") {
+ return true; // Already used...
+ }
+ $req_class = \IOcornerstone\Framework\Requires::class;
+ if (!method_exists($req_class, "saferFileExists")) {
+ require_once IO_CORNERSTONE_FRAMEWORK . DIRECTORY_SEPARATOR . "Framework" . DIRECTORY_SEPARATOR . "Requires.php";
+ }
+ $saferFile = $req_class::saferFileExists($file, $path);
+ if ($saferFile !== false) {
+ if (defined('CountFiles') && CountFiles) {
+ if (!isset($this->loadedFiles[$saferFile])) {
+ require $saferFile;
+ $this->loadedFiles[$saferFile] = true;
+ }
+ } else {
+ require_once $saferFile;
+ }
+ return true;
+ }
+ return false;
+ }
}