53 if($this->_sharedCacheFile)
try{
55 $temp = $this->_sharedCacheFile . \
Rsi\Str::random() .
'.tmp',
56 '<?php return ' . var_export($this->_cache,
true) .
';' 58 if(!rename($temp,$this->_sharedCacheFile))
throw new \Exception(
"Could not rename('$temp','{$this->_sharedCacheFile}')");
59 chmod($this->_sharedCacheFile,0666);
63 \Rsi\File::unlink($temp);
75 protected function cache($route,$key){
76 if($this->_cache === null){
78 if($this->_sharedCacheFile)
try{
79 $this->_cache = include($this->_sharedCacheFile);
80 if(($this->_cache[self::CACHE_HASH] ?? null) != ($hash = crc32(
'v4' . serialize($this->routes)))){
81 $this->_cache = [self::CACHE_HASH => $hash,self::CACHE_ROUTES => [],self::CACHE_HASHED => []];
82 $this->session->lastPath = $this->session->lastRoute = null;
86 \Rsi\File::unlink($this->_sharedCacheFile);
89 else $this->_cache = $this->session->cache ?: [];
91 if(!array_key_exists($route,$this->_cache[self::CACHE_ROUTES])) $this->_cache[self::CACHE_ROUTES][$route] = [];
92 return $this->_cache[self::CACHE_ROUTES][$route][$key] ?? null;
100 protected function save($route,$key,$value){
101 $this->_cache[self::CACHE_ROUTES][$route][$key] = $value;
110 protected function mask($route,&$mask,&$slots){
111 if($cache = $this->
cache($route,
'mask')) extract($cache);
113 preg_match_all(
'/\\[(.+?)(|\\*.+?)(|:[^\\[\\]]*(\\[.+?\\])*[^\\[\\]]*)\\]/',$mask =
'/^' . str_replace(
'/',
'\\/',$route) .
'$/i',$matches,PREG_SET_ORDER);
115 foreach($matches as list($full,$key,$enum,$regex)){
116 $slot =
'slot' . count($slots);
118 if(substr($enum,0,2) ==
'*@'){
120 $str_id = substr($enum,2);
122 foreach($trans->langs() as $lang_id => $descr) $enum[$lang_id] = $trans->str(
"[@$str_id.$lang_id]");
124 else $enum = \Rsi\Record::explode(substr($enum,1),
';',
'=');
126 if($regex && array_key_exists($regex = substr($regex,1),$this->regexShortcuts)) $regex = $this->regexShortcuts[$regex];
127 if($component = preg_match(
'/^@(\\w+)\\.(\\w+)$/',$key,$match)) $key =
'component' . count($slots);
129 compact(
'full',
'regex',
'slot',
'enum') +
130 ($component ? [
'component' => $match[1],
'property' => $match[2]] : []);
131 $mask = str_replace($full,
"(?<$slot>.*?)",$mask);
133 $mask = strtr($mask,[
'{' =>
'(?:',
'}' =>
')?']);
134 foreach($slots as $slot)
if($slot[
'regex'] && extract($slot))
135 $mask = str_replace(
"<$slot>.*?",
"<$slot>$regex",$mask);
136 $this->
save($route,
'mask',compact(
'mask',
'slots'));
144 protected function data($route){
145 if($data = $this->
cache($route,
'data'))
return $data;
146 if(!is_array($name = $data = $this->routes[$route])){
147 parse_str(\
Rsi\Str::pop($name,
'?'),$extra);
148 $action = \Rsi\Str::pop($name,
'#');
149 $data = compact(
'name',
'action',
'extra');
151 $this->
save($route,
'data',$data);
160 protected function hash($hash,$params){
161 unset($params[$hash[
'key'] ?? $this->hashKey]);
164 $hash[
'algo'] ?? $this->hashAlgo,
165 ($hash[
'salt'] ?? $this->
component(
'encrypt')->key) .
166 print_r(array_key_exists(
'keys',$hash) ? \
Rsi\Record::select($params,$hash[
'keys']) : $params,
true)
175 return array_key_exists($key = strtolower($this->_controllerName .
'#' . ($_POST[
'action'] ?? null)),$this->_cache[self::CACHE_HASHED])
176 ? $this->_cache[self::CACHE_HASHED][$key]
185 protected function match($path,$route){
186 if(array_key_exists($route,$this->routes)){
187 $data = $this->
data($route);
188 $this->
mask($route,$mask,$slots);
189 if(preg_match($mask,$path,$values) && (!array_key_exists(
'match',$data) || call_user_func($data[
'match'],$this,$route,$path,$values))){
190 $name = $data[
'name'];
191 foreach($slots as $key => $slot)
if(array_key_exists($slot[
'slot'],$values)){
192 $component = $property = null;
193 if(extract($slot) && (($value = $values[$slot]) || !$regex || preg_match(
"/^$regex$/",$value))){
194 $value = urldecode($values[$slot]);
195 if($component) $component = $this->
component($component);
196 if($enum && (!$component || (($enum[$component->get($property)] ?? null) != $value))) $value = array_search($value,$enum);
197 if($component) $component->set($property,$value);
198 else \Rsi\Record::set($_GET,explode(
'.',$key),$value);
199 if(preg_match(
'/^\\w+$/',$value)) $name = str_replace(
"[$key]",$value,$name);
202 $this->_controllerName = $name;
203 if($action = $data[
'action'] ?? null) $_POST[
'action'] = $action;
204 if(array_key_exists(
'hash',$data)){
205 $hash = $data[
'hash'];
206 $keys = $hash[
'keys'] ?: null;
207 if($this->
hashed($cache) ===
false){
208 $this->_cache[self::CACHE_HASHED][$cache] = $keys;
211 if(hash_equals($this->
hash($hash,$_GET),$_GET[$hash[
'key'] ?? $this->hashKey] ?? null)) $this->_hashed =
true;
212 else foreach(($keys ?: array_keys($_GET)) as $key) $_GET[$key] = $_POST[$key] = null;
214 $_POST = array_merge($_POST,$data[
'extra'] ?? []);
227 $this->_viewType = $this->_controllerName = $this->_hashed = null;
228 if($path === null) $path = $this->pathInfo;
229 if($path = trim(preg_replace(
'/^' . preg_quote($this->prefix,
'/') .
'/i',
'',$path),
'/')){
230 if($this->_viewType = strtolower(pathinfo($path,PATHINFO_EXTENSION)))
231 $path = substr($path,0,-(1 + strlen($this->_viewType)));
232 if($path != $this->session->lastPath){
233 $this->session->lastPath = $path;
234 $this->session->lastRoute = null;
235 foreach($this->routes as $route => $data)
if($this->
match($path,$route)){
236 $this->session->lastRoute = $route;
240 elseif($route = $this->session->lastRoute) $this->
match($path,$route);
241 if(!$this->_controllerName) $this->_controllerName = ucwords(strtolower($path),
'/');
243 if(!$this->_hashed && (($keys = $this->
hashed()) !==
false)){
244 if($keys)
foreach($keys as $key) $_GET[$key] = $_POST[$key] = null;
245 else $_GET = $_POST = [];
255 return strtolower($route);
265 public function reverse($controller_name = null,$type = null,$params = null,$strict =
true){
266 if(!$controller_name) $controller_name = $this->controllerName;
267 $link = $this->
format($controller_name);
268 $query = $params = ($params ?: []) + $this->defaultParams;
269 foreach($this->routes as $route => $data){
270 $data = $this->
data($route);
271 if($data[
'name'] == $controller_name){
272 if(array_key_exists(
'extra',$data))
foreach($data[
'extra'] as $key => $value){
273 if(($query[$key] ?? null) != $value)
continue 2;
276 if(array_key_exists(
'hash',$data)) $query[$data[
'hash'][
'key'] ??
$this->hashKey] = $this->
hash($data[
'hash'],$params);
277 $this->
mask($route,$mask,$slots);
278 foreach($slots as $key => $slot)
if($slot[
'component'] ?? null)
279 $query[$key] = $this->
component($slot[
'component'])->get($slot[
'property']);
280 foreach($query as $key => $value)
if(array_key_exists($key,$slots) && is_scalar($value) && extract($slots[$key])){
281 if($enum) $value = $enum[$value] ?? $enum[null] ?? null;
282 if(!$strict || !$regex || preg_match(
"/^$regex$/",$value)){
283 $route = str_replace($full,urlencode($value),$route);
287 do($route = preg_replace(
'/{[^{}]*?\\[[^\\[]*?\\][^{}]*?}/',
'',$route,-1,$count));
289 if(strpos($route,
'[') ===
false){
290 $link = str_replace([
'{',
'}'],
'',$route);
293 else $query = $params;
296 if($type) $link .=
'.' . $type;
297 if($query = http_build_query($query)) $link .= (strpos($link,
'?') ===
false ?
'?' :
'&') . $query;
298 return $this->prefix . $link;
302 if($this->_controllerName === null) $this->
execute();
307 return parse_url($_SERVER[
'REQUEST_URI'] ?? null,PHP_URL_PATH);
311 if($this->_controllerName === null) $this->
execute();
data($route)
Parsed data for route.
hashed(&$key=null)
Check if the current controller (+ action) has a hashed version.
$hashKey
Default hash key.
$prefix
Prefix to add before each route.
save($route, $key, $value)
Save data to the route cache.
cache($route, $key)
Retrieve data from the route cache.
hash($hash, $params)
Calculate the hash for a set of parameters.
format($route)
Format a (default) route.
mask($route, &$mask, &$slots)
Parse a route and generate a mask (regex).
reverse($controller_name=null, $type=null, $params=null, $strict=true)
Determine a route from a controller name and type.
$routes
Available routes (key = route, value = controller data). Parameters can be present in a route (e...
match($path, $route)
Check if a path matches a route and extract the parameters.
$defaultParams
Default params for routes.
execute($path=null)
Determine controller name and type.
$_sharedCacheFile
Shared cache file for compiled routes.
$hashAlgo
Default hash algo.
component($name)
Get a component (local or default).