Skip to content

feat: Filter Arguments with $filters in Config\Filters #7159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/Config/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class Filters extends BaseConfig
/**
* Configures aliases for Filter classes to
* make reading things nicer and simpler.
*
* @var array<string, string>
* @phpstan-var array<string, class-string>
*/
public array $aliases = [
'csrf' => CSRF::class,
Expand All @@ -26,6 +29,9 @@ class Filters extends BaseConfig
/**
* List of filter aliases that are always
* applied before and after every request.
*
* @var array<string, array<string, array<string, string>>>|array<string, array<string>>
* @phpstan-var array<string, list<string>>|array<string, array<string, array<string, string>>>
*/
public array $globals = [
'before' => [
Expand Down
113 changes: 91 additions & 22 deletions system/Filters/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace CodeIgniter\Filters;

use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\Filters\Exceptions\FilterException;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
Expand Down Expand Up @@ -63,7 +64,7 @@ class Filters
* The processed filters that will
* be used to check against.
*
* @var array
* @var array<string, array>
*/
protected $filters = [
'before' => [],
Expand All @@ -74,7 +75,7 @@ class Filters
* The collection of filters' class names that will
* be used to execute in each position.
*
* @var array
* @var array<string, array>
*/
protected $filtersClass = [
'before' => [],
Expand All @@ -84,14 +85,16 @@ class Filters
/**
* Any arguments to be passed to filters.
*
* @var array
* @var array<string, array<int, string>|null> [name => params]
* @phpstan-var array<string, list<string>|null>
*/
protected $arguments = [];

/**
* Any arguments to be passed to filtersClass.
*
* @var array
* @var array<string, array|null> [classname => arguments]
* @phpstan-var array<class-string, array<string, list<string>>|null>
*/
protected $argumentsClass = [];

Expand Down Expand Up @@ -170,7 +173,10 @@ public function run(string $uri, string $position = 'before')
}

if ($position === 'before') {
$result = $class->before($this->request, $this->argumentsClass[$className] ?? null);
$result = $class->before(
$this->request,
$this->argumentsClass[$className] ?? null
);

if ($result instanceof RequestInterface) {
$this->request = $result;
Expand All @@ -193,7 +199,11 @@ public function run(string $uri, string $position = 'before')
}

if ($position === 'after') {
$result = $class->after($this->request, $this->response, $this->argumentsClass[$className] ?? null);
$result = $class->after(
$this->request,
$this->response,
$this->argumentsClass[$className] ?? null
);

if ($result instanceof ResponseInterface) {
$this->response = $result;
Expand Down Expand Up @@ -316,23 +326,17 @@ public function addFilter(string $class, ?string $alias = null, string $when = '
* after the filter name, followed by a comma-separated list of arguments that
* are passed to the filter when executed.
*
* @return Filters
* @param string $name filter_name or filter_name:arguments like 'role:admin,manager'
*
* @return $this
*
* @deprecated Use enableFilters(). This method will be private.
*/
public function enableFilter(string $name, string $when = 'before')
{
// Get parameters and clean name
if (strpos($name, ':') !== false) {
[$name, $params] = explode(':', $name);

$params = explode(',', $params);
array_walk($params, static function (&$item) {
$item = trim($item);
});

$this->arguments[$name] = $params;
}
// Get arguments and clean name
[$name, $arguments] = $this->getCleanName($name);
$this->arguments[$name] = ($arguments !== []) ? $arguments : null;

if (class_exists($name)) {
$this->config->aliases[$name] = $name;
Expand All @@ -354,13 +358,39 @@ public function enableFilter(string $name, string $when = 'before')
return $this;
}

/**
* Get clean name and arguments
*
* @param string $name filter_name or filter_name:arguments like 'role:admin,manager'
*
* @return array [name, arguments]
* @phpstan-return array{0: string, 1: list<string>}
*/
private function getCleanName(string $name): array
{
$arguments = [];

if (strpos($name, ':') !== false) {
[$name, $arguments] = explode(':', $name);

$arguments = explode(',', $arguments);
array_walk($arguments, static function (&$item) {
$item = trim($item);
});
}

return [$name, $arguments];
}

/**
* Ensures that specific filters are on and enabled for the current request.
*
* Filters can have "arguments". This is done by placing a colon immediately
* after the filter name, followed by a comma-separated list of arguments that
* are passed to the filter when executed.
*
* @params array<string> $names filter_name or filter_name:arguments like 'role:admin,manager'
*
* @return Filters
*/
public function enableFilters(array $names, string $when = 'before')
Expand Down Expand Up @@ -463,20 +493,59 @@ protected function processFilters(?string $uri = null)
// Look for inclusion rules
if (isset($settings['before'])) {
$path = $settings['before'];

if ($this->pathApplies($uri, $path)) {
$this->filters['before'][] = $alias;
// Get arguments and clean name
[$name, $arguments] = $this->getCleanName($alias);

$this->filters['before'][] = $name;

$this->registerArguments($name, $arguments);
}
}

if (isset($settings['after'])) {
$path = $settings['after'];

if ($this->pathApplies($uri, $path)) {
$this->filters['after'][] = $alias;
// Get arguments and clean name
[$name, $arguments] = $this->getCleanName($alias);

$this->filters['after'][] = $name;

// The arguments may have already been registered in the before filter.
// So disable check.
$this->registerArguments($name, $arguments, false);
}
}
}
}

/**
* @param string $name filter alias
* @param array $arguments filter arguments
* @param bool $check if true, check if already defined
*/
private function registerArguments(string $name, array $arguments, bool $check = true): void
{
if ($arguments !== []) {
if ($check && array_key_exists($name, $this->arguments)) {
throw new ConfigException(
'"' . $name . '" already has arguments: '
. (($this->arguments[$name] === null) ? 'null' : implode(',', $this->arguments[$name]))
);
}

$this->arguments[$name] = $arguments;
}

$classNames = (array) $this->config->aliases[$name];

foreach ($classNames as $className) {
$this->argumentsClass[$className] = $this->arguments[$name] ?? null;
}
}

/**
* Maps filter aliases to the equivalent filter classes
*
Expand All @@ -500,8 +569,8 @@ protected function processAliasesToClass(string $position)
}
}

// when using enableFilter() we already write the class name in ->filtersClass as well as the
// alias in ->filters. This leads to duplicates when using route filters.
// when using enableFilter() we already write the class name in $filtersClass as well as the
// alias in $filters. This leads to duplicates when using route filters.
// Since some filters like rate limiters rely on being executed once a request we filter em here.
$this->filtersClass[$position] = array_values(array_unique($this->filtersClass[$position]));
}
Expand Down
Loading