PHP Classes

File: src/InputFilterContainer.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   Ionizer PHP Filter Input   src/InputFilterContainer.php   Download  
File: src/InputFilterContainer.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Ionizer PHP Filter Input
Filter input values by chaining filter objects
Author: By
Last change:
Date: 2 years ago
Size: 5,338 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace
ParagonIE\Ionizer;

use
ParagonIE\Ionizer\Contract\{
   
FilterInterface,
   
FilterContainerInterface
};

/**
 * Class InputFilterContainer
 *
 * Contains a set of filter rules, useful for enforcing a strict type on
 * unstructured data (e.g. HTTP POST parameters).
 *
 * @package ParagonIE\Ionizer
 */
abstract class InputFilterContainer implements FilterContainerInterface
{
   
/** @const string SEPARATOR */
   
const SEPARATOR = '.';

   
/**
     * @var array<string, array<mixed, FilterInterface>>
     */
   
protected $filterMap = [];

   
/**
     * InputFilterContainer constructor.
     */
   
abstract public function __construct();

   
/**
     * Add a new filter to this input value
     *
     * @param string $path
     * @param FilterInterface $filter
     * @return FilterContainerInterface
     */
   
public function addFilter(
       
string $path,
       
FilterInterface $filter
   
): FilterContainerInterface {
        if (!isset(
$this->filterMap[$path])) {
           
$this->filterMap[$path] = [];
        }
       
/** @psalm-suppress MixedArrayAssignment */
       
$this->filterMap[$path][] = $filter->setIndex($path);
        return
$this;
    }

   
/**
     * @param string $path
     * @return array<array-key, FilterInterface>
     */
   
public function getFiltersForPath(string $path)
    {
        if (!isset(
$this->filterMap[$path])) {
           
$this->filterMap[$path] = [];
        }
        return
$this->filterMap[$path];
    }

   
/**
     * Use firstlevel.second_level.thirdLevel to find indices in an array
     *
     * @param string $key
     * @param mixed $multiDimensional
     * @return mixed
     * @throws \Error
     * @throws InvalidDataException
     *
     * @psalm-suppress UnusedVariable
     */
   
public function filterValue(string $key, $multiDimensional)
    {
       
/** @var array<int, string> $pieces */
       
$pieces = Util::chunk($key, (string) static::SEPARATOR);
       
/** @var array|string $filtered */
       
$filtered =& $multiDimensional;

       
/**
         * @security This shouldn't be escapable. We know eval is evil, but
         * there's not a more elegant way to process this in PHP.
         */
       
if (\is_array($multiDimensional)) {
           
$var = '$multiDimensional';
            foreach (
$pieces as $piece) {
               
$append = '[' . self::sanitize($piece) . ']';

               
// Alphabetize the parent array
               
eval(
                   
'if (!isset(' . $var . $append . ')) {' . "\n" .
                   
' ' . $var . $append . ' = null;' . "\n" .
                   
'}' . "\n" .
                   
'\ksort(' . $var . ');' . "\n"
               
);
               
$var .= $append;
            }
            eval(
'$filtered =& ' . $var. ';');
        }

       
// If we have filters, let's apply them:
       
if (isset($this->filterMap[$key])) {
           
/** @var object|null $filter */
           
foreach ($this->filterMap[$key] as $filter) {
                if (
$filter instanceof FilterInterface) {
                   
/** @var string|int|bool|float|array $filtered */
                   
$filtered = $filter->process($filtered);
                }
            }
        }
        return
$multiDimensional;
    }

   
/**
     * Use firstlevel.second_level.thirdLevel to find indices in an array
     *
     * Doesn't apply filters
     *
     * @param string $key
     * @param array<string, string|array> $multiDimensional
     * @return mixed
     * @psalm-suppress PossiblyInvalidArrayOffset
     */
   
public function getUnfilteredValue(string $key, array $multiDimensional = [])
    {
       
/** @var array<string, string> $pieces */
       
$pieces = Util::chunk($key, (string) static::SEPARATOR);

       
/** @var string|array<string, string|array> $value */
       
$value = $multiDimensional;

       
/**
         * @var array<string, string> $pieces
         * @var string $piece
         */
       
foreach ($pieces as $piece) {
            if (!isset(
$value[$piece])) {
                return
null;
            }
           
/** @var string|array<string, string|array> $next */
           
$next = $value[$piece];

           
/** @var string|array<string, string|array> $value */
           
$value = $next;
        }
        return
$value;
    }

   
/**
     * Only allow allow printable ASCII characters:
     *
     * @param string $input
     * @return string
     * @throws \Error
     */
   
protected static function sanitize(string $input): string
   
{
       
/** @var string|bool $sanitized */
       
$sanitized = \json_encode(
            \
preg_replace('#[^\x20-\x7e]#', '', $input)
        );
        if (!\
is_string($sanitized)) {
            throw new \
Error('Could not sanitize string');
        }
        return
$sanitized;
    }

   
/**
     * Process the input array.
     *
     * @param array $dataInput
     * @return array
     * @throws \Error
     * @throws InvalidDataException
     */
   
public function __invoke(array $dataInput = []): array
    {
       
/** @var string $key */
       
foreach (\array_keys($this->filterMap) as $key) {
           
/** @var array $dataInput */
           
$dataInput = $this->filterValue($key, $dataInput);
        }
        return
$dataInput;
    }
}