Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
tools: psalm, phpunit:${{ matrix.phpunit-versions }}

- name: Install dependencies
run: composer self-update; composer remove --dev vimeo/psalm; composer install
run: composer self-update; composer install

- name: PHPUnit tests
run: vendor/bin/phpunit
Expand Down
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@
"ParagonIE\\Ionizer\\": "./src"
}
},
"autoload-dev": {
"psr-4": {
"ParagonIE\\Ionizer\\Test\\": "./tests"
}
},
"license": "ISC",
"require": {
"ext-json": "*",
"php": "^7|^8",
"paragonie/constant_time_encoding": "^2.1|^3"
},
"require-dev": {
"phpunit/phpunit": "^6.5|^7|^8|^9",
"vimeo/psalm": "^1|^2|^3|^4"
"phpunit/phpunit": "^6.5|^7|^8|^9|^10|^11"
},
"scripts": {
"test": ["phpunit", "psalm"]
Expand Down
78 changes: 77 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ This document provides detailed examples for the different input filters availab

Below is the documentation for scalar type filters available in Ionizer.


### `BoolFilter`

The `BoolFilter` validates a single boolean value.
Expand Down Expand Up @@ -125,6 +124,9 @@ try {
Sometimes you need to accept a list of values, rather than a single value. These input filters allow you to limit the
inputs to a flat, one-dimensional array consisting of specific values.

By default, the array filters only examine the **values, not the keys** of an input array. You can specify
[Index Policies](#index-policies) if you interpret the array indices as data too.

### `BoolArrayFilter`

The `BoolArrayFilter` is used to ensure that the input is a one-dimensional array of booleans. It will cast any
Expand Down Expand Up @@ -246,6 +248,80 @@ try {
}
```

### Index Policies

Ionizer allows for policies to be defined on the indices (a.k.a. keys) of an array.

#### `AnyIndex`

Allows any string or integer index to be used on an array. This is congruent to not specifying an Index Policy at all.

```php
<?php
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\IndexPolicy\AnyIndex;

$gfc = new GeneralFilterContainer();
$gfc->setIndexPolicy(new AnyIndex());
```

#### `IntegersOnly`

Allows any integer key.

```php
<?php
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\IndexPolicy\IntegersOnly;

$gfc = new GeneralFilterContainer();
$gfc->setIndexPolicy(new IntegersOnly());
```

#### `StringsOnly`

Allows any string key.

```php
<?php
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\IndexPolicy\StringsOnly;

$gfc = new GeneralFilterContainer();
$gfc->setIndexPolicy(new StringsOnly());
```

#### `KeyAllowList`

Allows only a specific set of keys.

```php
<?php
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\IndexPolicy\IndexAllowList;

$gfc = new GeneralFilterContainer();
$gfc->setIndexPolicy(new IndexAllowList('foo', 'bar', 'baz'));
```

#### Custom Index Policies

To create your own index policy, create a class that implements
[`IndexPolicyInterface`](../src/Contract/IndexPolicyInterface.php).

```php
<?php
use ParagonIE\Ionizer\Contract\IndexPolicyInterface;

class FooPolicy implements IndexPolicyInterface
{
public function indexIsValid($index): bool
{
return $index === 'foo';
}
}
```

## Other Filters

### `AllowList`
Expand Down
36 changes: 21 additions & 15 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="true"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
displayDetailsOnPhpunitDeprecations="true"
failOnPhpunitDeprecation="true"
failOnRisky="true"
failOnWarning="true">
<testsuites>
<testsuite name="Unit Tests">
<directory suffix="Test.php">./tests</directory>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

<source restrictNotices="true" restrictWarnings="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
12 changes: 12 additions & 0 deletions src/Contract/IndexPolicyInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\Contract;

interface IndexPolicyInterface
{
/**
* @param array-key $index
* @return bool
*/
public function indexIsValid($index): bool;
}
28 changes: 28 additions & 0 deletions src/Filter/ArrayFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types=1);
namespace ParagonIE\Ionizer\Filter;

use ParagonIE\Ionizer\Contract\IndexPolicyInterface;
use ParagonIE\Ionizer\InputFilter;
use ParagonIE\Ionizer\InvalidDataException;

Expand All @@ -21,6 +22,23 @@ class ArrayFilter extends InputFilter
*/
protected $type = 'array';

/**
* @var ?IndexPolicyInterface $indexPolicy
*/
protected $indexPolicy = null;

/**
* Add restrictions to the keys allowed in this array
*
* @param IndexPolicyInterface $indexPolicy
* @return $this
*/
public function setIndexPolicy(IndexPolicyInterface $indexPolicy): self
{
$this->indexPolicy = $indexPolicy;
return $this;
}

/**
* Process data using the filter rules.
*
Expand All @@ -40,6 +58,16 @@ public function process($data = null)
\sprintf('Expected an array (%s).', $this->index)
);
}
if (!is_null($this->indexPolicy)) {
$keys = array_keys($data);
foreach ($keys as $arrayKey) {
if (!$this->indexPolicy->indexIsValid($arrayKey)) {
throw new \TypeError(
\sprintf("Invalid key (%s) in violation of key policy", $arrayKey)
);
}
}
}
return (array) parent::process($data);
}
}
19 changes: 19 additions & 0 deletions src/IndexPolicy/AnyIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\IndexPolicy;

use ParagonIE\Ionizer\Contract\IndexPolicyInterface;

class AnyIndex implements IndexPolicyInterface
{
/**
* Any integer or string index is valid.
*
* @param array-key $index
* @return bool
*/
public function indexIsValid($index): bool
{
return is_string($index) || is_int($index);
}
}
30 changes: 30 additions & 0 deletions src/IndexPolicy/IndexAllowList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\IndexPolicy;

use ParagonIE\Ionizer\Contract\IndexPolicyInterface;

class IndexAllowList implements IndexPolicyInterface
{
/** @var array-key[] $allowed */
private $allowed = [];

/**
* @param array-key ...$allowed
*/
public function __construct(...$allowed)
{
$this->allowed = $allowed;
}

/**
* Any integer or string key is valid.
*
* @param array-key $index
* @return bool
*/
public function indexIsValid($index): bool
{
return in_array($index, $this->allowed, true);
}
}
19 changes: 19 additions & 0 deletions src/IndexPolicy/IntegersOnly.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\IndexPolicy;

use ParagonIE\Ionizer\Contract\IndexPolicyInterface;

class IntegersOnly implements IndexPolicyInterface
{
/**
* Any integer key is valid.
*
* @param array-key $index
* @return bool
*/
public function indexIsValid($index): bool
{
return is_int($index);
}
}
19 changes: 19 additions & 0 deletions src/IndexPolicy/StringsOnly.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\IndexPolicy;

use ParagonIE\Ionizer\Contract\IndexPolicyInterface;

class StringsOnly implements IndexPolicyInterface
{
/**
* Any integer or string key is valid.
*
* @param array-key $index
* @return bool
*/
public function indexIsValid($index): bool
{
return is_string($index);
}
}
8 changes: 6 additions & 2 deletions tests/AllowListTest.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
<?php
declare(strict_types=1);
namespace ParagonIE\Ionizer\Test;

use Error;
use ParagonIE\Ionizer\Filter\AllowList;
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\InvalidDataException;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;


/**
* Class AllowListTest
*/
#[CoversClass(AllowList::class)]
class AllowListTest extends TestCase
{
/**
* @throws Error
* @throws InvalidDataException
*/
public function testWhiteList()
public function testAllowList()
{
$filter = (new GeneralFilterContainer())
->addFilter(
Expand Down
Loading