|
<?php |
|
|
|
/** |
|
* Class Http_Router |
|
* |
|
* @author rumur |
|
*/ |
|
class Http_Router |
|
{ |
|
/** @var string */ |
|
protected $namespace; |
|
|
|
/** @var array */ |
|
protected $routes = []; |
|
|
|
/** |
|
* Http_Router constructor. |
|
* |
|
* @param $namespace |
|
*/ |
|
public function __construct($namespace) |
|
{ |
|
$this->namespace = sanitize_title($namespace); |
|
} |
|
|
|
/** |
|
* Factory |
|
* |
|
* @param $namespace |
|
* |
|
* @return Http_Router |
|
* |
|
* @author rumur |
|
*/ |
|
static public function make($namespace) |
|
{ |
|
$self = new static($namespace); |
|
|
|
$self->registerNamespace(); |
|
$self->registerResolver(); |
|
|
|
return $self; |
|
} |
|
|
|
/** |
|
* @param string $route |
|
* @param array $args |
|
* |
|
* @return $this |
|
* |
|
* @author rumur |
|
*/ |
|
public function addRoute($route, array $args) |
|
{ |
|
$this->routes[$this->makeRouteId($route)] = $this->parseArgs($route, $args); |
|
|
|
return $this; |
|
} |
|
|
|
public function getNamespace() |
|
{ |
|
return $this->namespace; |
|
} |
|
|
|
/** |
|
* Prefix for variables that will occur within the $wp_query. |
|
* |
|
* @return string |
|
*/ |
|
public function paramPrefix() |
|
{ |
|
return trim($this->getNamespace(), '_') . '_'; |
|
} |
|
|
|
/** |
|
* Register all routes. |
|
* |
|
* @author rumur |
|
*/ |
|
public function registerRoutes() |
|
{ |
|
foreach ($this->routes as $route => $args) { |
|
$this->registerRoute($this->getNamespace(), $route, $args); |
|
} |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* @param string $route |
|
* @param array $args |
|
* @return array |
|
*/ |
|
protected function parseArgs($route, array $args = []) |
|
{ |
|
$args = wp_parse_args($args, [ |
|
'rewrite_rule' => [], |
|
'name' => false, |
|
'regexp' => $default_regexp = '(.+?)', |
|
'params' => [], |
|
'callback' => function () { |
|
return __('Default view'); |
|
}, |
|
]); |
|
|
|
$args = array_merge($args, [ |
|
'params_raw' => [], |
|
'route_raw' => $route, |
|
'route_url' => site_url($route), |
|
'route_id' => $this->makeRouteId($route) |
|
]); |
|
|
|
$rewrite_rule_query = []; |
|
$rewrite_rule_regexp = ''; |
|
|
|
preg_match_all('/{(\w*)}/', $route, $regexp); |
|
|
|
if (!empty($regexp)) { |
|
|
|
$params = array_combine($regexp[1], $regexp[0]); |
|
|
|
$params_with_prefix = []; |
|
|
|
foreach ($params as $param => $raw) { |
|
$params_with_prefix["{$this->paramPrefix()}{$param}"] = $raw; |
|
} |
|
|
|
$args['params_raw'] = $params; |
|
$args['params'] = array_keys($params_with_prefix); |
|
|
|
if (is_array($args['regexp'])) { |
|
// Could be if the regexp not filled for all params |
|
if ($missed_params_in_regexp = array_diff_key($params, $args['regexp'])) { |
|
// Fill missed regexp with default one. |
|
$missed_params_in_regexp = array_fill_keys(array_keys($missed_params_in_regexp), $default_regexp); |
|
// Merge it back for further use. |
|
$args['regexp'] = array_merge($args['regexp'], $missed_params_in_regexp); |
|
} |
|
|
|
// Populate params with prefix; |
|
$args_regexp = $args['regexp']; |
|
|
|
$args['regexp'] = []; |
|
|
|
foreach ($args_regexp as $param => $regexp) { |
|
$args['regexp']["{$this->paramPrefix()}{$param}"] = $regexp; |
|
} |
|
} |
|
|
|
$rewrite_rule_regexp = str_replace($params, $args['regexp'], $route); |
|
|
|
$matches_counter = 1; |
|
|
|
foreach ($params_with_prefix as $param => $raw_param) { |
|
array_push($rewrite_rule_query, sprintf('%s=$matches[%d]', $param, $matches_counter++)); |
|
} |
|
} |
|
|
|
$args['rewrite_rule'] = [ |
|
'regexp' => sprintf('^%s/?$', $rewrite_rule_regexp), |
|
'query' => sprintf('index.php?is_%s_route=%s&%s', |
|
$this->getNamespace(), $args['route_id'], join('&', $rewrite_rule_query) |
|
), |
|
]; |
|
|
|
if ($args['callback'] instanceof Closure) { |
|
$handler = $args['callback']; |
|
} else { |
|
$handler = explode('@', $args['callback']); |
|
} |
|
|
|
$args['callback'] = function () use ($handler) { |
|
|
|
try { |
|
echo call_user_func_array($handler, func_get_args()); |
|
} // Catch Exceptions |
|
catch (Throwable $e) { |
|
echo $e->getMessage(); |
|
error_log($e->getMessage()); |
|
} |
|
}; |
|
|
|
$this->saveRouteByName($args); |
|
|
|
return $args; |
|
} |
|
|
|
/** |
|
* @param $route |
|
* @return string |
|
*/ |
|
protected function makeRouteId($route) |
|
{ |
|
return md5($route); |
|
} |
|
|
|
/** |
|
* @param $route_id |
|
* @return bool |
|
*/ |
|
protected function has($route_id) |
|
{ |
|
return isset($this->routes[$route_id]); |
|
} |
|
|
|
/** |
|
* Gets routes args |
|
* |
|
* @param string $route_id. |
|
* |
|
* @return array routes args |
|
*/ |
|
protected function get($route_id) |
|
{ |
|
return $this->has($route_id) |
|
? $this->routes[$route_id] |
|
: []; |
|
} |
|
|
|
/** |
|
* Registers Namespace |
|
*/ |
|
protected function registerNamespace() |
|
{ |
|
\add_rewrite_tag('%is_' . $this->getNamespace() . '_route%', '(\w+)'); |
|
} |
|
|
|
/** |
|
* Resolver of the route content. |
|
*/ |
|
protected function registerResolver() |
|
{ |
|
\add_filter('template_include', function ($template) { |
|
|
|
if ($route_id = get_query_var("is_{$this->getNamespace()}_route", false)) { |
|
|
|
if ($this->has($route_id)) { |
|
|
|
$routes_args = $this->get($route_id); |
|
|
|
$params = array_map(function ($param) { |
|
return get_query_var($param); |
|
}, $routes_args['params']); |
|
|
|
$template = $routes_args['callback'](...$params); |
|
} |
|
} |
|
|
|
return $template; |
|
}); |
|
} |
|
|
|
/** |
|
* @param string $namespace |
|
* @param string $route |
|
* @param array $args |
|
*/ |
|
protected function registerRoute($namespace, $route, array $args) |
|
{ |
|
add_rewrite_rule($args['rewrite_rule']['regexp'], $args['rewrite_rule']['query'], 'top'); |
|
//add_rewrite_rule('^'.$namespace.'/(.*)?', $query, 'top'); |
|
// (?:\/(?:.*)?|\/?$) // The end of regexp for the whole route |
|
|
|
foreach ($args['params'] as $param) { |
|
add_rewrite_tag("%{$param}%", $args['regexp'][$param] ?? $args['regexp']); |
|
} |
|
|
|
add_rewrite_tag("%route_id%", '(\w+)'); |
|
} |
|
|
|
/** |
|
* @param array $args |
|
*/ |
|
protected function saveRouteByName(array $args) |
|
{ |
|
if ($args['name']) { |
|
|
|
$current_route_data = [ $args['name'] => $args ]; |
|
|
|
if ($cached = wp_cache_get('http_router', 'ep')) { |
|
$data = array_merge($cached, $current_route_data); |
|
|
|
wp_cache_replace('http_router', $data, 'ep'); |
|
} else { |
|
wp_cache_set('http_router', $current_route_data, 'ep'); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* @param $name |
|
* @return array |
|
*/ |
|
public static function getRouteByName($name) |
|
{ |
|
if ($cached = wp_cache_get('http_router', 'ep')) { |
|
|
|
if (isset($cached[$name])) { |
|
return $cached[$name]; |
|
} |
|
} |
|
|
|
return []; |
|
} |
|
} |