/** Datei: library/ARUNOX/Core/Cache.php **/ isFile() && filemtime( $f->getPathname() ) > $cacheTime ) { return FALSE; } } } elseif( is_file( $item ) && filemtime( $item ) > $cacheTime ) { return FALSE; } } return TRUE; } /* ### PUBLIC STATIC CACHE FUNCTIONS ############################ # */ public static function setData( string $key, mixed $data, string|array $name = "unknown" ) : bool { return static::saveFile( $key, serialize( $data ), $name ); } public static function getData( string $key, string|array $watchFilesOrDirs = "unknown" ) : mixed { if( $content = static::getFile( $key, $watchFilesOrDirs ) ) { return unserialize( $content ); } return NULL; } public static function clear( string $key, string|array $watchFilesOrDirs = "unknown" ) : bool { if( $filename = self::getFilename( $key, $watchFilesOrDirs ) ) { if( file_exists( $filename ) ) { return unlink( $filename ); } } return TRUE; } } /** Datei: library/ARUNOX/Core/Hooks.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Core; use ARUNOX\API\PHP; use ARUNOX\Tools\Debug; class Hooks { /* ### VARIABLES ################################################ # */ private static array $HOOKIDS = []; private static array $HOOKS = []; private static array $REGEX = []; public static function registerNEW( $pluginName ) : bool { $root = PLUGINS_DIR . "/{$pluginName}/hooks"; $hooks = Cache::fetch( "hooks", function() use( $root ) { $hooks = []; if( !$root = realpath( $root ) ) return $hooks; $files = glob_recursive( "{$root}/*.php" ); foreach( $files as $filename ) { $files = Cache::fetch( "hooks", function() use( $filename ) { $phpfile = new PHP( $filename ); $comment = $phpfile->get( T_DOC_COMMENT ); $header = (array)$comment->data; if( key_exists( "hooks", $header ) ) { $files = []; $uses = $phpfile->getAll( T_USE ); $last = end( $uses ); $uses = implode( "", $uses ); $count = explode( "\n", $comment->text ); $count = count( $count ) + $comment->line; $lines = explode( "\n", $phpfile->content ); $lines = array_slice( $lines, $last->nextLine ?? ++$count ); $lines = implode( "\n", $lines ); $prio = $header["priority"] ?? 10; $params = $header["params"] ?? []; $params = array_keys( $params ); $params = implode( ", ", $params ); foreach( $header["hooks"] as $hook ) { $name = "{$filename}_{$hook}"; $file = Cache::getFilename( "hooks/files", $name ); if( $file == " /home2/www/b90/arunox/dev.arunox/tools/framework/storage/cache/hooks/files/3c93c461128506fac2a18d7009240cf8.cache.php" ) { show( $uses, $lines, "STOP" ); } $php = implode( "\n", [ 'get( T_DOC_COMMENT ); $header = (array)$comment->data; return $header; }, $filename ); $hooks[] = [ "filename" => $filename, "hooks" => $header["hooks"] ?? [], "params" => array_keys( $header["params"] ?? [] ), "priority" => $header["priority"] ?? 10, ]; } return $hooks; }, $root ); foreach( $hooks as $file ) { if( $file["hooks"] ) { foreach( $file["hooks"] as $hook ) { static::addFile( $hook, $file["filename"], $file["params"], $file["priority"] ); } continue; } @include_once $file["filename"]; } // show( $path, $hooks, false ); return !!$hooks; } public static function addFile( string $hook, string $file, array $params, int $priority = 10 ) : void { $params = array_filter( $params ); $params = array_values( $params ); $params = array_map( "trim", $params ); static::add( $hook, function( ...$ARGS ) use( $file, $params, $hook ) { foreach( $params as $index => &$name ) { $name = str_replace( "$", "", $name ); ${$name} = $ARGS[ $index ] ?? NULL; } // if( $hook != "database.attribute.options::get" ) echo "{$hook}
"; unset( $name ); return include $file; }, $priority, hashid( "HOOK-", $file ) ); } public static function add( string $name, callable|string $callback, int $priority = 10, string|bool|null $uuid = NULL ) : void { $name = strtolower( $name ); $uuid = $uuid === TRUE ? $name : $uuid; $hook = $name; if( is_string( $callback ) ) { if( preg_match( "/__file_([^|]*)(\|(.*))?/", $callback, $matches ) ) { $matches[3] = explode( ",", $matches[3] ); $matches[3] = array_filter( $matches[3] ); $matches[3] = array_values( $matches[3] ); $matches[3] = array_map( "trim", $matches[3] ); static::addFile( $name, $matches[1], $matches[3], $priority ); return; } else if( preg_match( "/__return_(.*)/", $callback, $matches ) ) { $callback = function() use( $matches ){ return match ( $matches[1] ) { "false" => FALSE, "true" => TRUE, "null" => NULL, default => $matches[1], }; }; } else if( preg_match( "/__push_(.*)/", $callback, $matches ) ) { $callback = function( $values ) use( $matches ){ $values[] = $matches[1]; return $values; }; } } $uuid = $uuid ?? uniqid_index( static::$HOOKIDS, "HOOK-" ); if( preg_match( '/\*/', $name ) ) { $name = preg_quote( $name ); $name = preg_replace( '/(\\\\\*)/', "([a-z0-9._-]+)", $name ); static::$REGEX[ $name ][ $priority ][ $uuid ] = $callback; static::$HOOKIDS[ $uuid ] = &static::$REGEX[ $name ][ $priority ][ $uuid ]; ksort(static::$REGEX[ $name ] ); return; } static::$HOOKS[ $name ][ $priority ][ $uuid ] = $callback; static::$HOOKIDS[ $uuid ] = &static::$HOOKS[ $name ][ $priority ][ $uuid ]; ksort(static::$HOOKS[ $name ] ); } public static function list( $name = NULL ) { $name = strtolower( $name ); if( $name ) return static::$HOOKS[ $name ]; else return array_keys( static::$HOOKS ); } public static function remove( $uuid ) { static::$HOOKIDS[ $uuid ] = NULL; } public static function exists( $uuid ) { return !!static::$HOOKIDS[ $uuid ]; } public static function apply(string $name, ...$args): void { $name = strtolower( $name ); Debug::log( $name, "hooks" ); if (!isset(self::$HOOKS[$name])) { return; } foreach( self::$HOOKS[$name] as $priority => $callbacks ) { foreach( $callbacks as $callback ) { if( is_callable( $callback ) ) { call_user_func_array($callback, $args); } } } } public static function applyWithNames( string $hook, array|string $names, ...$args) : void { static::apply( "{$hook}", ...$args ); // show("{$hook}", FALSE ); $name = []; foreach( (array)$names as $item ) { $name[] = $item; static::apply("{$hook}->" . implode( ".", $name ), ...$args ); // show("{$hook}->" . implode( ".", $name ), FALSE ); } } public static function filter( string $name, mixed $value = NULL, ...$args ) : mixed { $name = strtolower( $name ); Debug::log( $name, "hooks" ); if( static::$REGEX ) { foreach( static::$REGEX as $regex => $hooks ) { if( preg_match( "/{$regex}/", $name, $matches ) ) { array_shift( $matches ); foreach( $hooks as $priority => $callbacks ) { foreach( $callbacks as $callback ) { $value = call_user_func_array( $callback, array_merge( [ $value ], $args, [ $matches ] ) ); } } } } } if( !isset( self::$HOOKS[ $name ] ) ) { return $value; } // if( $name == "router::resolve->before" ) show( $name, self::$HOOKS[ $name ] ); foreach( self::$HOOKS[ $name ] as $priority => $callbacks ) { foreach( $callbacks as $callback ) { if( is_callable( $callback ) ) { $value = call_user_func_array( $callback, [ $value, ...$args ] ); } } } return $value; } public static function filterWithNames( string $hook, array|string $names, mixed $value, ...$args) : mixed { // if( $hook == "model::updated" ) show("{$hook}", $names, FALSE ); $value = static::filter("{$hook}", $value, ...$args ); $name = []; foreach( (array)$names as $item ) { $name[] = $item; // if( $hook == "model::updated" ) show("{$hook}->" . implode( ".", $name ), FALSE ); // show("{$hook}->" . implode( ".", $name ), FALSE ); $value = static::filter("{$hook}->" . implode( ".", $name ), $value, ...$args ); } return $value; } public static function parseName( string $name ) { if( class_exists( $name ) ) { $name = preg_replace( '/ARUNOX\\\[A-Z][a-z]+\\\/', "", $name ); } $name = strtolower( $name ); $name = preg_replace('/(\/\{[a-z_]+\})/', '', $name );; $name = preg_replace( '/(\/|\\\)/', ".", $name ); return $name; } } /** Datei: library/ARUNOX/Core/Errors/Errors.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Core; class Errors { /* ### CONSTANTS ################################################ # */ /* ### VARIABLES ################################################ # */ protected static array $ERRORS = []; protected static array $CODES = [ "100" => "Continue", "200" => "OK", "208" => "Already Reported", "304" => "Not Modified", "400" => "Bad Request", "401" => "Unauthorized", "402" => "Payment Required", "403" => "Forbidden", "404" => "Not Found", "405" => "Method Not Allowed", "409" => "Conflict", "500" => "Internal Server Error", ]; /* ################################################################ # */ /* ### PUBLIC FUNCTIONS ######################################### # */ /* ################################################################ # */ public static function init() : void { if( !key_exists( "errors", $_SESSION ) ) { $_SESSION["errors"] = []; } static::$ERRORS = &$_SESSION["errors"]; } public static function reset( int $index = -1 ) : void { $index = static::getIndex( $index ); unset( static::$ERRORS[ $index ] ); } public static function resetAll() : void { static::$ERRORS = []; } public static function get( bool $reset = TRUE, int $index = -1 ) : array { $index = static::getIndex( $index ); $error = static::$ERRORS[ $index ]; if( $reset ) static::reset( $index ); return $error; } public static function getAll( bool $reset = TRUE ) : array { $errors = static::$ERRORS; if( $reset ) static::resetAll(); return $errors; } public static function getIndex( int $index = -1 ) : int { $errors = array_keys( static::$ERRORS ); $errors = array_slice( $errors, $index, 1 ); return current( $errors ); } public static function has( bool|string $includes = "all" ) : bool { if( $includes === "all" ) return !!static::$ERRORS; if( $includes === TRUE ) return !!array_filter( static::$ERRORS, fn( $item ) => $item["code"] < 300 ); if( $includes === FALSE ) return !!array_filter( static::$ERRORS, fn( $item ) => $item["code"] >= 300 ); if( is_numeric( $includes ) ) return !!array_filter( static::$ERRORS, fn( $item ) => $item["code"] == $includes ); if( preg_match( '/([0-9])([0-9]{2})(s|\+|-)/', $includes, $matches ) ) { if( $matches[3] == "s" ) $range = [ "start" => "{$matches[1]}00", "end" => "{$matches[1]}99" ]; if( $matches[3] == "+" ) $range = [ "start" => "{$matches[1]}00", "end" => "999" ]; if( isset( $range ) ) return !!array_filter( static::$ERRORS, fn( $item ) => $item["code"] >= $range["start"] && $item["code"] <= $range["end"] ); } return FALSE; } public static function add( int $code, null|string $message = NULL, null|string $title = NULL, null|string $icon = NULL, null|string $color = NULL ) { static::$ERRORS[] = static::parse(...[ "code" => $code, "message" => $message, "title" => $title, "icon" => $icon, "color" => $color, ]); } /* ### PARSING FUNCTIONS ####################################### # */ public static function parseCode( int $code, null|string $value = NULL ) { if( $code >= 100 ) $info = [ "color" => "primary", "beep" => "short", "title" => __( "Information" ), "icon" => "bi bi-info-lg" ]; if( $code >= 200 ) $info = [ "color" => "success", "beep" => "short", "title" => __( "Erfolg" ), "icon" => "bi bi-check-lg" ]; if( $code >= 300 ) $info = [ "color" => "warning", "beep" => "alert", "title" => __( "Warnung" ), "icon" => "bi bi-exclamation-lg" ]; if( $code >= 400 ) $info = [ "color" => "danger", "beep" => "alert", "title" => __( "Fehler" ), "icon" => "bi bi-shield-exclamation" ]; if( $code >= 500 ) $info = [ "color" => "danger", "beep" => "long", "title" => __( "Unbekannter Fehler" ), "icon" => "bi bi-exclamation-diamond" ]; $info["code"] = $code; $info["origin"] = static::$CODES[ $code ] ?? NULL; if( $value ) return $info[ $value ] ?? NULL; return $info; } /* ################################################################ # */ /* ### PROTECTED FUNCTIONS ###################################### # */ /* ################################################################ # */ protected static function parse( int $code, null|string $message = NULL, null|string $title = NULL, null|string $icon = NULL, null|string $color = NULL ) { return [ "code" => $code, "message" => __( $message ), "title" => __( $title ?? static::parseCode( $code, "title" )), "icon" => $icon, "color" => $color ?? static::parseCode( $code, "color" ), ]; } } /** Datei: library/ARUNOX/Core/Plugins/Plugins.php **/ $config ) { // static::checkDependencies( $plugin, $config->dependencies ); } } public static function checkDependencies( string $plugin, array $dependencies ) : bool { foreach( $dependencies as $dependency ) { if( !( static::$allPlugins[ $dependency ]->active ?? FALSE ) ) { throw new \Exception("Missing dependency: {$dependency} for plugin {$plugin}"); } } return TRUE; } public static function get( string $plugin, bool $force = FALSE ) : ?Entity { if( empty( static::$allPlugins ) || $force ) self::loadAll( $force ); return static::$allPlugins[ $plugin ] ?? NULL; } public static function getAll( bool $force = FALSE ) : array { if( empty( static::$allPlugins ) || $force ) self::loadAll( $force ); return static::$allPlugins; } public static function getActive( bool $force = FALSE ) : array { static::getAll( $force ); return array_filter( static::$allPlugins, function( $plugin ) { return !$plugin->ignore && $plugin->active; } ); } public static function getPreload( bool $force = FALSE ) : array { static::getAll( $force ); return array_filter( static::$allPlugins, function( $plugin ) { return !$plugin->ignore && $plugin->preload; } ); } } /** Datei: library/ARUNOX/Core/Plugins/Entity.php **/ active = TRUE; } public function deactivate() { $this->active = FALSE; } public function setStatus( int|bool $status ) { if( $status ) $this->activate(); else $this->deactivate(); } public function checkDependencies() : bool { return Plugins::checkDependencies( $this->name, $this->dependencies ); } /* ### PROTECTED FUNCTIONS ###################################### # */ protected function load( $filename ) { $phpfile = new PHP( $filename ); $comment = $phpfile->get( T_DOC_COMMENT ); $data = (array)$comment->data; return (object)$data; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( $filename ) { $header = $this->load( $filename ); foreach( $header as $name => $value ) { $this->{$name} = $value; } $this->path = dirname( $filename ); $this->id = basename( $this->path ); $this->label = $this->name; $this->name = strtolower( $this->name ?? "" ); $this->plugin = $this->name; $this->ignore = isset( $this->ignore ); $this->active = filter_var( $this->active, FILTER_VALIDATE_BOOLEAN ); $this->preload = filter_var( $this->preload, FILTER_VALIDATE_BOOLEAN ); $this->dependencies = array_filter( preg_split( "/,\s/", $this->dependencies ?? "" ) ); } } /** Datei: library/ARUNOX/Core/Auth.php **/ time(); } public static function createHash( string $value, string $salt = AUTH_HASH_SALT ) { return hash_hmac("sha256", strtolower( trim( $value ) ), $salt ); } } /** Datei: library/ARUNOX/Core/Launcher.php **/ path; $file = $ref->getFileName(); $name = str_replace( $root, "", $file ); $name = preg_replace( '/library\/.*/', "", $name ); $name = trim( $name, '/' ); return Directory::get( "{$root}/{$name}" ); } public static function getUrl() : null|string { $root = static::getRootUrl(); $name = static::getName(); return "{$root}/{$name}"; } public static function getName() : string { $dir = static::getDir(); return $dir->name; } /* ### PRIVATE FUNCTIONS ######################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __get( string $name ) { if( method_exists( $this, "get{$name}" ) ) { return $this->{"get{$name}"}(); } } public function __toString() : string { return static::getDir(); } } /** Datei: library/ARUNOX/Core/Router.php **/ {$subClassName}", "registerAPIs" ); } static::$REGISTERED[] = $className; break 2; } } } } public static function handleRequest(Request $request) : void { $uri = $request->getUri(); $method = $request->getMethod(); foreach (self::$ROUTES as $route) { if ($method === $route['method'] && $uri === $route['path']) { call_user_func($route['action'], $request); return; } } http_response_code(404); echo json_encode(['error' => 'Route not found']); } public static function add( callable | array $action ) : array { if( !is_callable( $action ) ) return []; if( is_array( $action ) ) $ref = new \ReflectionMethod( ...$action ); else $ref = new \ReflectionFunction( $action ); if( $docComment = $ref->getDocComment() ) { $docComment = static::parseDocTags( $docComment ); $routePath = trim( $docComment["route"]["path"] ?? "", "/" ); $routePath = array_filter([ $docComment["internal"] ?? NULL, $routePath ]); $routePath = implode( "/", $routePath ); $routeData = [ "method" => $docComment["method"] ?? "GET", "path" => "/{$routePath}", "action" => $action, "config" => $docComment, ]; // if( preg_match( '/.*\/logs$/', $routeData["path"] ) ) show( $routeData, FALSE ); return static::addRoute( ...$routeData ); } } public static function addAll( string $class ) : array { $refClass = new \ReflectionClass( $class ); $routes = Cache::fetch( "routes", function() use( $refClass, $class ) { $methods = $refClass->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC ); $routes = []; foreach( $methods as $method ) { $doc = $method->getDocComment() ?: ''; if( !preg_match('/@route\.path\s+([^\s]+)/', $doc, $pathMatch ) ) continue; if( !preg_match('/@method\s+([A-Z]+)/', $doc, $httpMatch ) ) continue; $routes[] = static::add([ $class, $method->getName() ]); } return array_filter( $routes ); }, $refClass->getFileName() ); self::$ROUTES = array_merge( self::$ROUTES, $routes ); static::sortRoutes(); return $routes; } public static function any( string $path, callable | array $action ) : void { self::addRoute("*", $path, $action ); } public static function get( string $path, callable | array $action ) : void { self::addRoute("GET", $path, $action ); } public static function put( string $path, callable | array $action ) : void { self::addRoute("PUT", $path, $action ); } public static function post( string $path, callable | array $action ) : void { self::addRoute("POST", $path, $action ); } public static function patch( string $path, callable | array $action ) : void { self::addRoute("PATCH", $path, $action ); } public static function delete( string $path, callable | array $action ) : void { self::addRoute("DELETE", $path, $action ); } public static function getRoutes() { return self::$ROUTES; } public static function resolve( string $method, string $uri ) : array|bool{ $result = FALSE; $method = strtoupper( $method ); $uri = preg_replace( "/(\/api\/v[0-9]+)\/?/", "/", $uri ); $hook = trim( $uri, "/" ); $hook = preg_replace( "/\//", ".", $hook ); $hook = strtolower( "{$method}.{$hook}"); [ $uri, $method ] = Hooks::filterWithNames("router::resolve->before", $hook, [ $uri, $method ] ); // show( $uri, $method, self::$ROUTES ); foreach( self::$ROUTES as $route ) { if( !in_array( $route["method"], [ $method, "*" ] ) ) continue; if( !preg_match( "/^{$route["pattern"]}$/", $uri, $matches ) ) continue; if( $matches ) array_shift( $matches ); $route["matches"] = $matches; $result = $route; break; } return Hooks::filterWithNames("router::resolve->after", $hook, $result ); } public static function dispatch( string $method, string $uri, ?array $route = NULL ) : mixed { if( $route = $route ?? static::resolve( $method, $uri ) ) { if( !$route['isDynamic'] ) { $action = $route['action']; $request = new Request(); $response = new Response(); $data = $request->getBodyParams(); $arguments = []; Hooks::filterWithNames("router::dispatch->before", $route["hook"], $route, $request ); if( is_callable( $action ) ) { $arguments[] = $data; $arguments[] = $request; $arguments[] = $response; $result = call_user_func( $action, ...$arguments ); } else { $result = call_user_func( [ new $action[0]( $request, $response ), $action[1] ], $data ); } $route["request"] = $request; $route["response"] = $response; return Hooks::filterWithNames("router::dispatch->after", $route["hook"], $result ?? NULL, $route, $data ); } if( $route['isDynamic'] ) { if( $route["matches"] ) { $action = $route["action"]; $request = new Request(); $response = new Response(); $matches = $route["matches"]; if( is_array( $action ) && method_exists( $action[0], $action[1] ) ) { $refMethod = new \ReflectionMethod( $action[0], $action[1] ); $params = $refMethod->getParameters(); foreach( $params as $i => $param ) { if( !isset( $matches[ $i ] ) ) continue; $value = $matches[ $i ]; $type = $param->getType(); if( $type instanceof \ReflectionUnionType ) { $allowedTypes = array_map(fn($t) => $t->getName(), $type->getTypes()); } elseif( $type instanceof \ReflectionNamedType ) { $allowedTypes = [ $type->getName() ]; } else { $allowedTypes = []; } // Verhindere numerische Werte für string-typisierte IDs if( in_array("string", $allowedTypes ) && !in_array("int", $allowedTypes ) && is_numeric( $value ) ) { http_response_code(400); echo json_encode([ "error" => "Numeric ID not allowed here. Use hashid." ]); return null; } // Wenn int erwartet, aber String übergeben wurde if ( in_array("int", $allowedTypes ) && !in_array("string", $allowedTypes ) && is_string( $value ) && is_numeric( $value ) === false ) { http_response_code(400); echo json_encode([ "error" => "Invalid ID format. Expected numeric ID." ]); return null; } } } $matches[] = $data = $request->getBodyParams(); $matches[] = &$request; Hooks::applyWithNames("router::dispatch->before", $route["hook"], $route, $request ); $result = is_callable( $action ) ? call_user_func_array( $action, $matches ) : call_user_func_array([ new $action[0]( $request, $response ), $action[1] ], $matches ); $route["request"] = &$request; $route["response"] = &$response; return Hooks::filterWithNames("router::dispatch->after", $route["hook"], $result, $route, $data, ...$matches ); } } } // show( $method, $uri, $route, static::resolve( $method, $uri ), self::$ROUTES, "STOP" ); Hooks::apply('router::dispatch->error', $uri, $method ); http_response_code(404); return ['error' => 'Route not found']; } public static function sortRoutes( null|array $routes = NULL ) : array { $routes = $routes ?? self::$ROUTES; usort($routes, function( $a, $b ) { if( $a["isDynamic"] !== $b["isDynamic"] ) { return $a["isDynamic"] <=> $b["isDynamic"]; } $aPos = array_search( "{*}", explode( "/", $a["path"] ) ); $bPos = array_search( "{*}", explode( "/", $b["path"] ) ); $aPos = $aPos === FALSE ? PHP_INT_MAX : $aPos; $bPos = $bPos === FALSE ? PHP_INT_MAX : $bPos; if( $aPos !== $bPos ) { return $bPos <=> $aPos; } $aCount = substr_count( $a["path"], "{*}" ); $bCount = substr_count( $b["path"], "{*}" ); return $aCount <=> $bCount; }); return self::$ROUTES = $routes; } /* ### PRIVATE FUNCTIONS ######################################## # */ private static function addRoute( string $method, string $path, callable | array $action, array $config = [] ) : array { $route = static::parseRoute([ "method" => strtoupper($method), "path" => $path, "action" => Hooks::filter('router::addRoute->action', $action, $method, $path ), "config" => $config, ]); self::$ROUTES[] = $route; static::sortRoutes(); return $route; } private static function parseRoute( array $route ) : array { $route["method"] = strtoupper( $route["method"] ); if( preg_match( "/(\/api\/v[0-9]+)\/?(.*)/", $route["path"], $matches ) ) { $route["path"] = $matches[2]; } $route["path"] = trim( $route["path"], "/" ); $route["path"] = "/{$route["path"]}"; $route["isDynamic"] = preg_match_all('/(\{[^\}]+\}|\*)/', $route["path"] , $matches ); // $route["matches"] = $matches[0]; $route["pattern"] = $route["path"]; $route["pattern"] = preg_replace( '/\//', '\/', $route["pattern"] ); $route["pattern"] = preg_replace( '/\{\*\}|\*/', '(.*)', $route["pattern"] ); // $route["pattern"] = preg_replace( '/\{\*\}/', '([a-zA-Z0-9_-\]+)', $route["pattern"] ); $route["pattern"] = preg_replace( '/\{[^\}]+\}/', '([a-zA-Z0-9_-]+)', $route["pattern"] ); $route["hook"] = trim( $route["path"], "/" ); $route["hook"] = preg_replace( "/\//", ".", $route["hook"] ); $route["hook"] = strtolower( "{$route["method"]}.{$route["hook"]}"); // if( preg_match( '/.*\/logs$/', $route["path"] ) ) show( $route, FALSE ); return $route; } private static function parseDocTags( string $docComment ) : array { $tags = []; $lines = explode("\n", $docComment ); foreach( $lines as $line ) { $line = trim( $line, " \t\n\r\0\x0B*/" ); if( $line === "" ) continue; if( preg_match('/^@([a-zA-Z0-9_.-]+)(?:\s+(.*))?$/', $line, $match ) ) { $value = isset( $match[2] ) ? trim( $match[2] ) : true; $names = explode( ".", $match[1] ); $current = &$tags; foreach( $names as $name ) { if( !key_exists( $name, $current ) ) { $current[ $name ] = []; } $current = &$current[ $name ]; } $current = $value; } } return $tags; } }