at the end of the day, it was inevitable
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher;
|
||||
|
||||
/**
|
||||
* Class AppMatcher
|
||||
* Facade for matching process.
|
||||
*
|
||||
* @package Common\Util\Matcher
|
||||
*/
|
||||
class AppMatcher
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of entities metadata.
|
||||
* Make static because for some reason ExpanderInitializer from
|
||||
* coduo/php-Matcher mark as final and haven't interface, so we can't override
|
||||
* or decorate it and pass this entities into it. Therefore we can't pass
|
||||
* this map into concrete expander ... :-(
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $entities;
|
||||
|
||||
/**
|
||||
* @param array $entities Array of entities pattern.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function registerEntities(array $entities = [])
|
||||
{
|
||||
self::$entities = $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName Entity name.
|
||||
*
|
||||
* @return \Common\Util\Metadata\EntityMetadata
|
||||
*/
|
||||
public static function getEntityMetadata($entityName)
|
||||
{
|
||||
return self::$entities[$entityName];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value Checked value.
|
||||
* @param string $pattern Pattern.
|
||||
* @param null|string $error Error.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function match($value, $pattern, &$error = null)
|
||||
{
|
||||
$factory = new MatcherFactory();
|
||||
$matcher = $factory->createMatcher();
|
||||
|
||||
if (! $matcher->match($value, $pattern)) {
|
||||
$error = $matcher->getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander;
|
||||
|
||||
/**
|
||||
* Class AbstractChainExpander
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
abstract class AbstractChainExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var PatternExpander[]
|
||||
*/
|
||||
protected $expanders = [];
|
||||
|
||||
/**
|
||||
* @param PatternExpander $expander A PatternExpander instance.
|
||||
* @param PatternExpander $expander,... A PatternExpander's instances.
|
||||
*/
|
||||
public function __construct(PatternExpander $expander)
|
||||
{
|
||||
$this->expanders[] = $expander;
|
||||
|
||||
if (func_num_args() > 1) {
|
||||
$arguments = func_get_args();
|
||||
$length = count($arguments);
|
||||
for ($i = 1; $i < $length; ++$i) {
|
||||
if (!$arguments[$i] instanceof PatternExpander) {
|
||||
throw new \InvalidArgumentException('Has invalid expander.');
|
||||
}
|
||||
|
||||
$this->expanders[] = $arguments[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
foreach ($this->expanders as $expander) {
|
||||
if (! $expander->match($value)) {
|
||||
$className = get_class($expander);
|
||||
$className = substr($className, strrpos($className, '\\'));
|
||||
|
||||
$this->error = "Expander {$className} don't matches value: ".
|
||||
$expander->getError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander;
|
||||
|
||||
/**
|
||||
* Class AbstractExpander
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
abstract class AbstractExpander implements PatternExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $error;
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Common\Util\Converter\DateConverter;
|
||||
|
||||
/**
|
||||
* Class BetweenExpander
|
||||
* Check that value is between specified bounds.
|
||||
* Except integers, float and datetime values.
|
||||
*
|
||||
* Example:
|
||||
* - integer.between(10, 20)
|
||||
* - date.between('2017-10-01', '2017-11-01')
|
||||
* - .field('date', between('2017-10-01', '2017-11-01'))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class BetweenExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var integer|float|string
|
||||
*/
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* @var integer|float|string
|
||||
*/
|
||||
private $end;
|
||||
|
||||
/**
|
||||
* @param integer|float|string $start Start bound.
|
||||
* @param integer|float|string $end End bound.
|
||||
*/
|
||||
public function __construct($start, $end)
|
||||
{
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_numeric($value) && ! is_int($value) && ! is_float($value)
|
||||
&& ! is_string($value)) {
|
||||
$this->error = 'Can match only integers, float and datetime values';
|
||||
return false;
|
||||
}
|
||||
|
||||
$start = $this->start;
|
||||
$end = $this->end;
|
||||
if (DateConverter::can($value)) {
|
||||
// For string which represent date try to convert it into \DateTime
|
||||
// instances.
|
||||
try {
|
||||
$value = DateConverter::convert($value);
|
||||
$start = DateConverter::convert($start);
|
||||
$end = DateConverter::convert($end);
|
||||
|
||||
$value->setTimezone($start->getTimezone());
|
||||
} catch (\Exception $e) {
|
||||
$this->error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// For scalar types convert all values to the same type.
|
||||
$type = gettype($start);
|
||||
settype($value, $type);
|
||||
}
|
||||
|
||||
return ($value >= $start) && ($value <= $end);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Common\Util\Matcher\AppMatcher;
|
||||
|
||||
/**
|
||||
* Class EntityExpander
|
||||
* Check that expanded object is serialized application entity.
|
||||
*
|
||||
* Example: object.entity('AppBundle:User', 'user, post')
|
||||
*
|
||||
* @package Util\Matcher\Expander
|
||||
*/
|
||||
class EntityExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $entityName;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $serializationGroups;
|
||||
|
||||
/**
|
||||
* @param string $entityName Entity name like
|
||||
* 'BundleName:EntityName'.
|
||||
* @param string|array $serializationGroups Serialization groups in string
|
||||
* format delimited by ','.
|
||||
*/
|
||||
public function __construct($entityName, $serializationGroups = [])
|
||||
{
|
||||
$this->entityName = $entityName;
|
||||
|
||||
// Split serialization groups string into array.
|
||||
$serializationGroups = explode(',', $serializationGroups);
|
||||
// Trim values and remove empty.
|
||||
$serializationGroups = array_filter(
|
||||
array_map('trim', $serializationGroups)
|
||||
);
|
||||
|
||||
$this->serializationGroups = $serializationGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
// Get entity metadata for specified entity.
|
||||
$metadata = AppMatcher::getEntityMetadata($this->entityName);
|
||||
// Get patter fot specified entity with given serialization group.
|
||||
$pattern = $metadata->getPattern($this->serializationGroups);
|
||||
|
||||
if ($pattern && ! AppMatcher::match($value, $pattern, $this->error)) {
|
||||
$this->error =
|
||||
"Invalid entity {$this->entityName}: {$this->error}";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\ToString\StringConverter;
|
||||
|
||||
/**
|
||||
* Class EveryExpander
|
||||
* Check that every array elements matches specified expander.
|
||||
*
|
||||
* Example:
|
||||
* - array.every(field('property', value))
|
||||
* - array.every(entity('AppBundle:User', 'user, post'))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class EveryExpander extends AbstractChainExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
$this->error = 'Every expander require "array", got '.
|
||||
new StringConverter($value) .'.';
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $row) {
|
||||
if (! parent::match($row)) {
|
||||
$this->error = 'Checked value '. new StringConverter($row)
|
||||
.' is invalid: '. $this->error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander;
|
||||
|
||||
/**
|
||||
* Class FieldExpander
|
||||
* Check that expanded object or array has specific field which matched
|
||||
* given matcher expander.
|
||||
*
|
||||
* Example:
|
||||
* - .field('username', contains('admin'))
|
||||
* - .field('user', entity('AppBundle:User', 'user'), field('id', 1))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class FieldExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $fieldName;
|
||||
|
||||
/**
|
||||
* @var mixed|PatternExpander[]
|
||||
*/
|
||||
private $expander;
|
||||
|
||||
/**
|
||||
* @param string $fieldName Field name.
|
||||
* @param mixed|PatternExpander $expander A PatternExpander instance.
|
||||
* @param PatternExpander $expander,... A PatternExpander's instances.
|
||||
*/
|
||||
public function __construct($fieldName, $expander)
|
||||
{
|
||||
$this->fieldName = $fieldName;
|
||||
$this->expander = $expander;
|
||||
|
||||
if ($expander instanceof PatternExpander) {
|
||||
$expander = func_get_args();
|
||||
$length = count($expander);
|
||||
// Process all except first argument which contains field name.
|
||||
$this->expander = [];
|
||||
for ($i = 1; $i < $length; ++$i) {
|
||||
if (!$expander[$i] instanceof PatternExpander) {
|
||||
throw new \InvalidArgumentException('Has invalid expander.');
|
||||
}
|
||||
|
||||
$this->expander[] = $expander[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_array($value) && !isset($value[$this->fieldName])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($this->expander)) {
|
||||
// Match all expanders.
|
||||
foreach ($this->expander as $expander) {
|
||||
if (! $expander->match($value[$this->fieldName])) {
|
||||
$this->error = "Field {$this->fieldName}: expander don't matches value. ".
|
||||
$expander->getError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All expanders successfully matches.
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($value[$this->fieldName] !== $this->expander) {
|
||||
$this->error = "Field {$this->fieldName}: don't equal to {$this->expander}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Common\Util\Converter\DateConverter;
|
||||
|
||||
/**
|
||||
* Class GteExpander
|
||||
* Check that value is greater or equal to another value.
|
||||
* Except integers, float and datetime values.
|
||||
*
|
||||
* Example:
|
||||
* - integer.gte(10)
|
||||
* - date.gte('2017-10-01')
|
||||
* - .field('date', gte('2017-10-01'))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class GteExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var integer|string|\DateTimeInterface|float
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param integer|string|\DateTimeInterface|float $value Expected value.
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_numeric($value) && ! is_int($value) && ! is_float($value)
|
||||
&& ! is_string($value)) {
|
||||
$this->error = 'Can match only integers, float and datetime values';
|
||||
return false;
|
||||
}
|
||||
|
||||
$bound = $this->value;
|
||||
if (DateConverter::can($value)) {
|
||||
// For string which represent date try to convert it into \DateTime
|
||||
// instances.
|
||||
try {
|
||||
$value = DateConverter::convert($value);
|
||||
$bound = DateConverter::convert($bound);
|
||||
|
||||
$bound->setTimezone($value->getTimezone());
|
||||
} catch (\Exception $e) {
|
||||
$this->error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// For scalar types convert all values to the same type.
|
||||
$type = gettype($bound);
|
||||
settype($value, $type);
|
||||
}
|
||||
|
||||
if (! ($matched = $value >= $bound)) {
|
||||
if ($value instanceof \DateTime) {
|
||||
$value = $value->format('c');
|
||||
$bound = $bound->format('c');
|
||||
}
|
||||
|
||||
$this->error = "Checked value {$value} less than {$bound}.";
|
||||
}
|
||||
|
||||
return $matched;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander;
|
||||
|
||||
/**
|
||||
* Class LengthExpander
|
||||
* Check that expanded array contains specified number of elements.
|
||||
*
|
||||
* Example: array.length(2)
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class LengthExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* @param PatternExpander|integer $count Expected number of elements or
|
||||
* appropriate pattern expander.
|
||||
*/
|
||||
public function __construct($count)
|
||||
{
|
||||
if (! $count instanceof PatternExpander) {
|
||||
$count = (int) $count;
|
||||
}
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->count instanceof PatternExpander) {
|
||||
return $this->count->match(count($value));
|
||||
}
|
||||
|
||||
return count($value) === $this->count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander;
|
||||
|
||||
/**
|
||||
* Class FieldExpander
|
||||
* Check that inner expander not match.
|
||||
*
|
||||
* Example: .not(contains('some'))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class NotExpander extends AbstractExpander
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $expander;
|
||||
|
||||
/**
|
||||
* @param PatternExpander $expander A PatternExpander instance.
|
||||
*/
|
||||
public function __construct(PatternExpander $expander)
|
||||
{
|
||||
$this->expander = $expander;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if ($this->expander->match($value)) {
|
||||
$this->error = 'Expander match this value.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\ToString\StringConverter;
|
||||
|
||||
/**
|
||||
* Class OneExpander
|
||||
* Check that only one array element matches specified expander.
|
||||
*
|
||||
* Example:
|
||||
* - array.one(field('property', value))
|
||||
* - array.one(entity('AppBundle:User'), field('id', 1))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class OneExpander extends AbstractChainExpander
|
||||
{
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
$this->error = 'One expander require "array", got '.
|
||||
new StringConverter($value) .'.';
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $row) {
|
||||
if (parent::match($row)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
use Coduo\ToString\StringConverter;
|
||||
|
||||
/**
|
||||
* Class SomeExpander
|
||||
* Check that some array elements matches specified expander.
|
||||
*
|
||||
* Example:
|
||||
* - array.some(field('property', value))
|
||||
* - array.some(entity('AppBundle:User'), field('id', 1))
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class SomeExpander extends AbstractChainExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
$this->error = 'Some expander require "array", got '.
|
||||
new StringConverter($value) .'.';
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $row) {
|
||||
if (parent::match($row)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->error = 'No element does not match specified expanders';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Expander;
|
||||
|
||||
/**
|
||||
* Class TypeExpander
|
||||
* Check that value has specified type.
|
||||
*
|
||||
* Example:
|
||||
* - wildcart.oneOf(isEmpty(), type('double'))
|
||||
* - wildcart.type('string')
|
||||
*
|
||||
* @package Common\Util\Matcher\Expander
|
||||
*/
|
||||
class TypeExpander extends AbstractExpander
|
||||
{
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @param string $type Expected value type.
|
||||
*/
|
||||
public function __construct($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Value to match.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value)
|
||||
{
|
||||
$valueType = gettype($value);
|
||||
|
||||
if ($valueType === 'float') {
|
||||
$valueType = 'double';
|
||||
}
|
||||
|
||||
return $valueType === $this->type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Matcher;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Matcher;
|
||||
use Coduo\PHPMatcher\Matcher\Pattern\Assert\Json;
|
||||
use Coduo\PHPMatcher\Matcher\ValueMatcher;
|
||||
|
||||
/**
|
||||
* Class JsonMatcher
|
||||
* @package Common\Util\Matcher\Matcher
|
||||
*/
|
||||
class JsonMatcher extends Matcher
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $matcher;
|
||||
|
||||
/**
|
||||
* @param ValueMatcher $matcher
|
||||
*/
|
||||
public function __construct(ValueMatcher $matcher)
|
||||
{
|
||||
$this->matcher = $matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if matcher can match the pattern
|
||||
*
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canMatch($pattern)
|
||||
{
|
||||
return Json::isValidPattern($pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches value against the pattern
|
||||
*
|
||||
* @param mixed $value Checked value.
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value, $pattern)
|
||||
{
|
||||
if (parent::match($value, $pattern)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Json::isValid($value)) {
|
||||
$this->error = sprintf("Invalid given JSON of value. %s", $this->getErrorMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Json::isValidPattern($pattern) ) {
|
||||
$this->error = sprintf("Invalid given JSON of pattern. %s", $this->getErrorMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$transformedPattern = Json::transformPattern($pattern);
|
||||
$match = $this->matcher->match(json_decode($value, true), json_decode($transformedPattern, true));
|
||||
if (!$match) {
|
||||
$this->error = $this->matcher->getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getErrorMessage()
|
||||
{
|
||||
switch(json_last_error()) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
return 'Maximum stack depth exceeded';
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
return 'Underflow or the modes mismatch';
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
return 'Unexpected control character found';
|
||||
case JSON_ERROR_SYNTAX:
|
||||
return 'Syntax error, malformed JSON';
|
||||
case JSON_ERROR_UTF8:
|
||||
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
default:
|
||||
return 'Unknown error';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Matcher;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Matcher;
|
||||
use Coduo\PHPMatcher\Parser;
|
||||
use Seld\JsonLint\JsonParser;
|
||||
|
||||
/**
|
||||
* Class ObjectMatcher
|
||||
* Add 'object' pattern.
|
||||
* Used by object expander's which makes all useful tests.
|
||||
*
|
||||
* @package Common\Util\Matcher\Matcher
|
||||
*/
|
||||
class ObjectMatcher extends Matcher
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @param Parser $parser A Parser instance.
|
||||
*/
|
||||
public function __construct(Parser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if matcher can match the pattern
|
||||
*
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canMatch($pattern)
|
||||
{
|
||||
if (! is_string($pattern)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->parser->hasValidSyntax($pattern)
|
||||
&& $this->parser->parse($pattern)->is('object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches value against the pattern
|
||||
*
|
||||
* @param mixed $value Checked value.
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value, $pattern)
|
||||
{
|
||||
if (parent::match($value, $pattern)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add ability to match serialized json.
|
||||
$lint = new JsonParser();
|
||||
if (is_string($value) && ($lint->lint($value) === null)) {
|
||||
$value = json_decode($value, true);
|
||||
}
|
||||
|
||||
if (! is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that given value is assoc array.
|
||||
if (array_keys($value) === range(0, count($value) - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$typePattern = $this->parser->parse($pattern);
|
||||
|
||||
// Match all expanders.
|
||||
if (!$typePattern->matchExpanders($value)) {
|
||||
$this->error = $typePattern->getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher\Matcher;
|
||||
|
||||
use Coduo\PHPMatcher\Matcher\Matcher;
|
||||
use Coduo\PHPMatcher\Parser;
|
||||
|
||||
/**
|
||||
* Class WildcardMatcher
|
||||
* Replace default coduo/php-matcher WildcardMatcher in order to process
|
||||
* expanders.
|
||||
*
|
||||
* @package Common\Util\Matcher\Matcher
|
||||
*/
|
||||
class WildcardMatcher extends Matcher
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @param Parser $parser A Parser instance.
|
||||
*/
|
||||
public function __construct(Parser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
const MATCH_PATTERN = "@wildcard@";
|
||||
|
||||
/**
|
||||
* Checks if matcher can match the pattern
|
||||
*
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canMatch($pattern)
|
||||
{
|
||||
return is_string($pattern)
|
||||
&& strpos($pattern, self::MATCH_PATTERN) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches value against the pattern
|
||||
*
|
||||
* @param mixed $value Checked value.
|
||||
* @param mixed $pattern Pattern.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value, $pattern)
|
||||
{
|
||||
$typePattern = $this->parser->parse($pattern);
|
||||
|
||||
// Match all expanders.
|
||||
if (!$typePattern->matchExpanders($value)) {
|
||||
$this->error = $typePattern->getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Util\Matcher;
|
||||
|
||||
use Coduo\PHPMatcher\Factory\SimpleFactory;
|
||||
use Coduo\PHPMatcher\Lexer;
|
||||
use Coduo\PHPMatcher\Matcher\ChainMatcher;
|
||||
use Coduo\PHPMatcher\Parser;
|
||||
use Coduo\PHPMatcher\Matcher;
|
||||
use Common\Util\Matcher\Expander as AppExpanders;
|
||||
use Common\Util\Matcher\Matcher as AppMatchers;
|
||||
|
||||
/**
|
||||
* Class MatcherFactory
|
||||
* @package Common\Util\Matcher
|
||||
*/
|
||||
class MatcherFactory extends SimpleFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private static $parser;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $additionalExpanders = [
|
||||
'field' => AppExpanders\FieldExpander::class,
|
||||
'some' => AppExpanders\SomeExpander::class,
|
||||
'every' => AppExpanders\EveryExpander::class,
|
||||
'length' => AppExpanders\LengthExpander::class,
|
||||
'one' => AppExpanders\OneExpander::class,
|
||||
'type' => AppExpanders\TypeExpander::class,
|
||||
'entity' => AppExpanders\EntityExpander::class,
|
||||
'not' => AppExpanders\NotExpander::class,
|
||||
'gte' => AppExpanders\GteExpander::class,
|
||||
'between' => AppExpanders\BetweenExpander::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return ChainMatcher
|
||||
*/
|
||||
protected function buildScalarMatchers()
|
||||
{
|
||||
$parser = $this->buildParser();
|
||||
|
||||
return new Matcher\ChainMatcher([
|
||||
// Default matchers.
|
||||
new Matcher\CallbackMatcher(),
|
||||
new Matcher\ExpressionMatcher(),
|
||||
new Matcher\NullMatcher(),
|
||||
new Matcher\StringMatcher($parser),
|
||||
new Matcher\IntegerMatcher($parser),
|
||||
new Matcher\BooleanMatcher(),
|
||||
new Matcher\DoubleMatcher($parser),
|
||||
new Matcher\NumberMatcher(),
|
||||
new Matcher\ScalarMatcher(),
|
||||
|
||||
// Custom matchers.
|
||||
new AppMatchers\ObjectMatcher($parser),
|
||||
new AppMatchers\WildcardMatcher($parser),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Parser
|
||||
*/
|
||||
protected function buildParser()
|
||||
{
|
||||
if (!self::$parser) {
|
||||
// Register all expanders.
|
||||
$expanderInitializer = new Parser\ExpanderInitializer();
|
||||
|
||||
foreach (self::$additionalExpanders as $name => $class) {
|
||||
$expanderInitializer->setExpanderDefinition($name, $class);
|
||||
}
|
||||
|
||||
self::$parser = new Parser(new Lexer(), $expanderInitializer);
|
||||
}
|
||||
|
||||
return self::$parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Coduo\PHPMatcher\Matcher\ChainMatcher
|
||||
*/
|
||||
protected function buildMatchers()
|
||||
{
|
||||
$scalarMatchers = $this->buildScalarMatchers();
|
||||
$orMatcher = $this->buildOrMatcher();
|
||||
|
||||
$chainMatcher = new Matcher\ChainMatcher([
|
||||
$scalarMatchers,
|
||||
$orMatcher,
|
||||
new AppMatchers\JsonMatcher($orMatcher),
|
||||
new Matcher\XmlMatcher($orMatcher),
|
||||
new Matcher\TextMatcher($scalarMatchers, $this->buildParser()),
|
||||
]);
|
||||
|
||||
return $chainMatcher;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user