%PDF- %PDF-
| Direktori : /home/vacivi36/vacivitta_bakcup/system/Router/ |
| Current File : /home/vacivi36/vacivitta_bakcup/system/Router/AutoRouterImproved.php |
<?php
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace CodeIgniter\Router;
use CodeIgniter\Exceptions\PageNotFoundException;
use ReflectionClass;
use ReflectionException;
/**
* New Secure Router for Auto-Routing
*/
final class AutoRouterImproved implements AutoRouterInterface
{
/**
* List of controllers in Defined Routes that should not be accessed via this Auto-Routing.
*
* @var class-string[]
*/
private array $protectedControllers;
/**
* Sub-directory that contains the requested controller class.
*/
private ?string $directory = null;
/**
* Sub-namespace that contains the requested controller class.
*/
private ?string $subNamespace = null;
/**
* The name of the controller class.
*/
private string $controller;
/**
* The name of the method to use.
*/
private string $method;
/**
* An array of params to the controller method.
*/
private array $params = [];
/**
* Whether dashes in URI's should be converted
* to underscores when determining method names.
*/
private bool $translateURIDashes;
/**
* HTTP verb for the request.
*/
private string $httpVerb;
/**
* The namespace for controllers.
*/
private string $namespace;
/**
* The name of the default controller class.
*/
private string $defaultController;
/**
* The name of the default method
*/
private string $defaultMethod;
/**
* @param class-string[] $protectedControllers
* @param string $defaultController Short classname
*/
public function __construct(
array $protectedControllers,
string $namespace,
string $defaultController,
string $defaultMethod,
bool $translateURIDashes,
string $httpVerb
) {
$this->protectedControllers = $protectedControllers;
$this->namespace = rtrim($namespace, '\\') . '\\';
$this->translateURIDashes = $translateURIDashes;
$this->httpVerb = $httpVerb;
$this->defaultController = $defaultController;
$this->defaultMethod = $httpVerb . ucfirst($defaultMethod);
// Set the default values
$this->controller = $this->defaultController;
$this->method = $this->defaultMethod;
}
/**
* Finds controller, method and params from the URI.
*
* @return array [directory_name, controller_name, controller_method, params]
*/
public function getRoute(string $uri): array
{
$segments = explode('/', $uri);
// WARNING: Directories get shifted out of the segments array.
$nonDirSegments = $this->scanControllers($segments);
$controllerSegment = '';
$baseControllerName = $this->defaultController;
// If we don't have any segments left - use the default controller;
// If not empty, then the first segment should be the controller
if (! empty($nonDirSegments)) {
$controllerSegment = array_shift($nonDirSegments);
$baseControllerName = $this->translateURIDashes(ucfirst($controllerSegment));
}
if (! $this->isValidSegment($baseControllerName)) {
throw new PageNotFoundException($baseControllerName . ' is not a valid controller name');
}
// Prevent access to default controller path
if (
strtolower($baseControllerName) === strtolower($this->defaultController)
&& strtolower($controllerSegment) === strtolower($this->defaultController)
) {
throw new PageNotFoundException(
'Cannot access the default controller "' . $baseControllerName . '" with the controller name URI path.'
);
}
// Use the method name if it exists.
if (! empty($nonDirSegments)) {
$methodSegment = $this->translateURIDashes(array_shift($nonDirSegments));
// Prefix HTTP verb
$this->method = $this->httpVerb . ucfirst($methodSegment);
// Prevent access to default method path
if (strtolower($this->method) === strtolower($this->defaultMethod)) {
throw new PageNotFoundException(
'Cannot access the default method "' . $this->method . '" with the method name URI path.'
);
}
}
if (! empty($nonDirSegments)) {
$this->params = $nonDirSegments;
}
// Ensure the controller stores the fully-qualified class name
$this->controller = '\\' . ltrim(
str_replace(
'/',
'\\',
$this->namespace . $this->subNamespace . $baseControllerName
),
'\\'
);
// Ensure routes registered via $routes->cli() are not accessible via web.
$this->protectDefinedRoutes();
// Check _remap()
$this->checkRemap();
// Check parameters
try {
$this->checkParameters($uri);
} catch (ReflectionException $e) {
throw PageNotFoundException::forControllerNotFound($this->controller, $this->method);
}
return [$this->directory, $this->controller, $this->method, $this->params];
}
private function protectDefinedRoutes(): void
{
$controller = strtolower($this->controller);
foreach ($this->protectedControllers as $controllerInRoutes) {
$routeLowerCase = strtolower($controllerInRoutes);
if ($routeLowerCase === $controller) {
throw new PageNotFoundException(
'Cannot access the controller in Defined Routes. Controller: ' . $controllerInRoutes
);
}
}
}
private function checkParameters(string $uri): void
{
$refClass = new ReflectionClass($this->controller);
$refMethod = $refClass->getMethod($this->method);
$refParams = $refMethod->getParameters();
if (! $refMethod->isPublic()) {
throw PageNotFoundException::forMethodNotFound($this->method);
}
if (count($refParams) < count($this->params)) {
throw new PageNotFoundException(
'The param count in the URI are greater than the controller method params.'
. ' Handler:' . $this->controller . '::' . $this->method
. ', URI:' . $uri
);
}
}
private function checkRemap(): void
{
try {
$refClass = new ReflectionClass($this->controller);
$refClass->getMethod('_remap');
throw new PageNotFoundException(
'AutoRouterImproved does not support `_remap()` method.'
. ' Controller:' . $this->controller
);
} catch (ReflectionException $e) {
// Do nothing.
}
}
/**
* Scans the controller directory, attempting to locate a controller matching the supplied uri $segments
*
* @param array $segments URI segments
*
* @return array returns an array of remaining uri segments that don't map onto a directory
*/
private function scanControllers(array $segments): array
{
$segments = array_filter($segments, static fn ($segment) => $segment !== '');
// numerically reindex the array, removing gaps
$segments = array_values($segments);
// Loop through our segments and return as soon as a controller
// is found or when such a directory doesn't exist
$c = count($segments);
while ($c-- > 0) {
$segmentConvert = ucfirst(
$this->translateURIDashes === true
? str_replace('-', '_', $segments[0])
: $segments[0]
);
// as soon as we encounter any segment that is not PSR-4 compliant, stop searching
if (! $this->isValidSegment($segmentConvert)) {
return $segments;
}
$test = $this->namespace . $this->subNamespace . $segmentConvert;
// as long as each segment is *not* a controller file, add it to $this->subNamespace
if (! class_exists($test)) {
$this->setSubNamespace($segmentConvert, true, false);
array_shift($segments);
$this->directory .= $this->directory . $segmentConvert . '/';
continue;
}
return $segments;
}
// This means that all segments were actually directories
return $segments;
}
/**
* Returns true if the supplied $segment string represents a valid PSR-4 compliant namespace/directory segment
*
* regex comes from https://www.php.net/manual/en/language.variables.basics.php
*/
private function isValidSegment(string $segment): bool
{
return (bool) preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $segment);
}
/**
* Sets the sub-namespace that the controller is in.
*
* @param bool $validate if true, checks to make sure $dir consists of only PSR4 compliant segments
*/
private function setSubNamespace(?string $namespace = null, bool $append = false, bool $validate = true): void
{
if ($validate) {
$segments = explode('/', trim($namespace, '/'));
foreach ($segments as $segment) {
if (! $this->isValidSegment($segment)) {
return;
}
}
}
if ($append !== true || empty($this->subNamespace)) {
$this->subNamespace = trim($namespace, '/') . '\\';
} else {
$this->subNamespace .= trim($namespace, '/') . '\\';
}
}
private function translateURIDashes(string $classname): string
{
return $this->translateURIDashes
? str_replace('-', '_', $classname)
: $classname;
}
}