2021-04-02 17:20:02 +00:00
|
|
|
<?php
|
|
|
|
|
2021-06-08 09:52:11 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2021-04-02 17:20:02 +00:00
|
|
|
/**
|
|
|
|
* This file extends the Router class from the CodeIgniter 4 framework.
|
|
|
|
*
|
|
|
|
* It introduces the alternate-content option for a route.
|
|
|
|
*
|
2022-02-19 16:06:11 +00:00
|
|
|
* @copyright 2021 Ad Aures
|
2021-04-02 17:20:02 +00:00
|
|
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
|
|
* @link https://castopod.org/
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace App\Libraries;
|
|
|
|
|
|
|
|
use CodeIgniter\Router\Exceptions\RedirectException;
|
2021-05-12 14:00:25 +00:00
|
|
|
use CodeIgniter\Router\Router as CodeIgniterRouter;
|
2021-05-19 16:35:13 +00:00
|
|
|
use Config\Services;
|
2021-04-02 17:20:02 +00:00
|
|
|
|
2021-05-12 14:00:25 +00:00
|
|
|
class Router extends CodeIgniterRouter
|
2021-04-02 17:20:02 +00:00
|
|
|
{
|
|
|
|
/**
|
2021-05-19 16:35:13 +00:00
|
|
|
* Compares the uri string against the routes that the RouteCollection class defined for us, attempting to find a
|
|
|
|
* match. This method will modify $this->controller, etal as needed.
|
2021-04-02 17:20:02 +00:00
|
|
|
*
|
|
|
|
* @param string $uri The URI path to compare against the routes
|
|
|
|
*
|
|
|
|
* @return boolean Whether the route was matched or not.
|
|
|
|
*/
|
|
|
|
protected function checkRoutes(string $uri): bool
|
|
|
|
{
|
2021-05-06 14:00:48 +00:00
|
|
|
/** @noRector RemoveExtraParametersRector */
|
2021-06-08 09:52:11 +00:00
|
|
|
$routes = $this->collection->getRoutes($this->collection->getHTTPVerb());
|
2021-04-02 17:20:02 +00:00
|
|
|
|
|
|
|
// Don't waste any time
|
2021-05-18 17:16:36 +00:00
|
|
|
if ($routes === []) {
|
2021-04-02 17:20:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$uri = $uri === '/' ? $uri : ltrim($uri, '/ ');
|
|
|
|
|
|
|
|
// Loop through the route array looking for wildcards
|
|
|
|
foreach ($routes as $key => $val) {
|
|
|
|
// Reset localeSegment
|
|
|
|
$localeSegment = null;
|
|
|
|
|
|
|
|
$key = $key === '/' ? $key : ltrim($key, '/ ');
|
|
|
|
|
|
|
|
$matchedKey = $key;
|
|
|
|
|
|
|
|
// Are we dealing with a locale?
|
2021-05-14 17:59:35 +00:00
|
|
|
if (str_contains($key, '{locale}')) {
|
2021-04-02 17:20:02 +00:00
|
|
|
$localeSegment = array_search(
|
|
|
|
'{locale}',
|
2021-06-09 12:40:22 +00:00
|
|
|
preg_split('~[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+~m', $key),
|
2021-04-02 17:20:02 +00:00
|
|
|
true,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Replace it with a regex so it
|
|
|
|
// will actually match.
|
|
|
|
$key = str_replace('/', '\/', $key);
|
|
|
|
$key = str_replace('{locale}', '[^\/]+', $key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does the RegEx match?
|
|
|
|
if (preg_match('#^' . $key . '$#u', $uri, $matches)) {
|
2021-06-08 09:52:11 +00:00
|
|
|
$this->matchedRouteOptions = $this->collection->getRoutesOptions($matchedKey);
|
2021-04-02 17:20:02 +00:00
|
|
|
|
|
|
|
// Is this route supposed to redirect to another?
|
|
|
|
if ($this->collection->isRedirect($key)) {
|
|
|
|
throw new RedirectException(
|
|
|
|
is_array($val) ? key($val) : $val,
|
|
|
|
$this->collection->getRedirectCode($key),
|
|
|
|
);
|
|
|
|
}
|
2022-03-04 14:33:48 +00:00
|
|
|
|
2021-04-02 17:20:02 +00:00
|
|
|
// Store our locale so CodeIgniter object can
|
|
|
|
// assign it to the Request.
|
|
|
|
if (isset($localeSegment)) {
|
|
|
|
// The following may be inefficient, but doesn't upset NetBeans :-/
|
|
|
|
$temp = explode('/', $uri);
|
|
|
|
$this->detectedLocale = $temp[$localeSegment];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are we using Closures? If so, then we need
|
|
|
|
// to collect the params into an array
|
|
|
|
// so it can be passed to the controller method later.
|
2021-05-19 16:35:13 +00:00
|
|
|
if (! is_string($val) && is_callable($val)) {
|
2021-04-02 17:20:02 +00:00
|
|
|
$this->controller = $val;
|
|
|
|
|
|
|
|
// Remove the original string from the matches array
|
|
|
|
array_shift($matches);
|
|
|
|
|
|
|
|
$this->params = $matches;
|
|
|
|
|
|
|
|
$this->matchedRoute = [$matchedKey, $val];
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is there an alternate content for the matchedRoute?
|
|
|
|
|
|
|
|
// check if the alternate-content has been requested in the accept
|
|
|
|
// header and overwrite the $val with the matching controller method
|
|
|
|
if (
|
2021-06-09 12:40:22 +00:00
|
|
|
array_key_exists('alternate-content', $this->matchedRouteOptions) &&
|
2021-04-02 17:20:02 +00:00
|
|
|
is_array($this->matchedRouteOptions['alternate-content'])
|
|
|
|
) {
|
|
|
|
$request = Services::request();
|
|
|
|
$negotiate = Services::negotiator();
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
$acceptHeader = $request->getHeader('Accept')
|
|
|
|
->getValue();
|
2021-04-02 17:20:02 +00:00
|
|
|
$parsedHeader = $negotiate->parseHeader($acceptHeader);
|
|
|
|
|
2021-06-08 09:52:11 +00:00
|
|
|
$supported = array_keys($this->matchedRouteOptions['alternate-content']);
|
2021-04-02 17:20:02 +00:00
|
|
|
|
|
|
|
$expectedContentType = $parsedHeader[0];
|
|
|
|
foreach ($supported as $available) {
|
|
|
|
if (
|
2021-06-09 12:40:22 +00:00
|
|
|
$negotiate->callMatch($expectedContentType, $available, true)
|
2021-04-02 17:20:02 +00:00
|
|
|
) {
|
|
|
|
if (
|
|
|
|
array_key_exists(
|
|
|
|
'namespace',
|
|
|
|
$this->matchedRouteOptions[
|
|
|
|
'alternate-content'
|
|
|
|
][$available],
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
$this->collection->setDefaultNamespace(
|
|
|
|
$this->matchedRouteOptions[
|
|
|
|
'alternate-content'
|
|
|
|
][$available]['namespace'],
|
|
|
|
);
|
|
|
|
}
|
2022-03-04 14:33:48 +00:00
|
|
|
|
2021-04-02 17:20:02 +00:00
|
|
|
$val =
|
|
|
|
$this->collection->getDefaultNamespace() .
|
|
|
|
$this->directory .
|
|
|
|
$this->matchedRouteOptions['alternate-content'][
|
|
|
|
$available
|
|
|
|
]['controller-method'];
|
|
|
|
|
|
|
|
// no need to continue loop as $val has been overwritten
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are we using the default method for back-references?
|
|
|
|
|
|
|
|
// Support resource route when function with subdirectory
|
|
|
|
// ex: $routes->resource('Admin/Admins');
|
|
|
|
if (
|
2021-05-14 17:59:35 +00:00
|
|
|
str_contains($val, '$') &&
|
|
|
|
str_contains($key, '(') &&
|
|
|
|
str_contains($key, '/')
|
2021-04-02 17:20:02 +00:00
|
|
|
) {
|
|
|
|
$replacekey = str_replace('/(.*)', '', $key);
|
|
|
|
$val = preg_replace('#^' . $key . '$#u', $val, $uri);
|
2021-06-08 09:52:11 +00:00
|
|
|
$val = str_replace($replacekey, str_replace('/', '\\', $replacekey), $val);
|
2021-05-18 17:16:36 +00:00
|
|
|
} elseif (str_contains($val, '$') && str_contains($key, '(')) {
|
2021-04-02 17:20:02 +00:00
|
|
|
$val = preg_replace('#^' . $key . '$#u', $val, $uri);
|
2021-05-14 17:59:35 +00:00
|
|
|
} elseif (str_contains($val, '/')) {
|
2021-04-02 17:20:02 +00:00
|
|
|
[$controller, $method] = explode('::', $val);
|
|
|
|
|
|
|
|
// Only replace slashes in the controller, not in the method.
|
|
|
|
$controller = str_replace('/', '\\', $controller);
|
|
|
|
|
|
|
|
$val = $controller . '::' . $method;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->setRequest(explode('/', $val));
|
|
|
|
|
|
|
|
$this->matchedRoute = [$matchedKey, $val];
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
}
|