Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
9 / 9 |
CRAP | |
100.00% |
71 / 71 |
Gate | |
100.00% |
1 / 1 |
|
100.00% |
9 / 9 |
42 | |
100.00% |
71 / 71 |
__construct | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
accepts | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
addFields | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
accept | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
addField | |
100.00% |
1 / 1 |
6 | |
100.00% |
9 / 9 |
|||
assert | |
100.00% |
1 / 1 |
10 | |
100.00% |
20 / 20 |
|||
setData | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
getData | |
100.00% |
1 / 1 |
9 | |
100.00% |
19 / 19 |
|||
get | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
__get | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
validate | |
100.00% |
1 / 1 |
6 | |
100.00% |
11 / 11 |
|||
getErrors | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
<?php | |
namespace Verja; | |
use Verja\Exception\InvalidValue; | |
class Gate | |
{ | |
/** @var Field[] */ | |
protected $fields = []; | |
/** @var array */ | |
protected $rawData = []; | |
/** @var string */ | |
protected $filteredDataHash; | |
/** @var array */ | |
protected $filteredData = []; | |
/** @var array */ | |
protected $errors = []; | |
public function __construct(array $data = null) | |
{ | |
if ($data) { | |
$this->setData($data); | |
} | |
} | |
/** | |
* Alias for addFields | |
* | |
* @param array $fields | |
* @return $this | |
* @see Gate::addFields() | |
*/ | |
public function accepts(array $fields) | |
{ | |
return $this->addFields($fields); | |
} | |
/** | |
* Add an array of fields | |
* | |
* Definitions are defined as in addField. | |
* | |
* Accepts fields without filter and validator definitions as values. So the following examples are equal: | |
* | |
* `[ $key => [] ]` | |
* | |
* `[ $key => null ]` | |
* | |
* `[ $key ]` | |
* | |
* @param array $fields | |
* @return $this | |
* @see Gate::addField() how to pass definitions | |
*/ | |
public function addFields(array $fields) | |
{ | |
foreach ($fields as $key => $field) { | |
if (is_int($key)) { | |
$this->addField($field); // field is the key in numeric arrays | |
} else { | |
$this->addField($key, $field); | |
} | |
} | |
return $this; | |
} | |
/** | |
* Alias for addField | |
* | |
* @param string $key | |
* @param mixed $field | |
* @return $this | |
* @see Gate::addField() | |
*/ | |
public function accept($key, $field = null) | |
{ | |
return $this->addField($key, $field); | |
} | |
/** | |
* Add an accepted field to this gate | |
* | |
* The definition can be a single validator or filter as string or object, an array of validators and filters | |
* as string or object or an instance of Field. | |
* | |
* The following examples are equal: | |
* | |
* `(new Field)->addValidator('strLen:2:5')` | |
* | |
* `'strLen:2:5'` | |
* | |
* `['strLen:2:5']` | |
* | |
* `new Field(['strLen:2:5'])` | |
* | |
* @param string $key The key in the data array | |
* @param mixed $field Definition of the field | |
* @return $this | |
*/ | |
public function addField($key, $field = null) | |
{ | |
if ($field instanceof Field) { | |
$this->fields[$key] = $field; | |
} else { | |
$definitions = []; | |
if (is_array($field)) { | |
$definitions = $field; | |
} elseif (is_string($field) || $field instanceof ValidatorInterface || $field instanceof FilterInterface) { | |
$definitions = [$field]; | |
} | |
$this->fields[$key] = new Field($definitions); | |
} | |
return $this; | |
} | |
/** | |
* Quick assertion with filters and validators | |
* | |
* Asserts that $value in $context can be filtered and validated by $field. | |
* | |
* If not an InvalidValue exception is thrown. | |
* | |
* Field can be defined as for addField. | |
* | |
* @param mixed $field | |
* @param mixed $value | |
* @param array $context | |
* @return mixed | |
* @throws InvalidValue | |
* @see Gate::addField() | |
*/ | |
public static function assert($field, $value, array $context = []) | |
{ | |
if (!$field instanceof Field) { | |
$definitions = []; | |
if (is_array($field)) { | |
$definitions = $field; | |
} elseif (is_string($field) || | |
$field instanceof ValidatorInterface || | |
$field instanceof FilterInterface || | |
is_callable($field) | |
) { | |
$definitions = [$field]; | |
} | |
$field = new Field($definitions); | |
} | |
$filtered = $field->filter($value, $context); | |
if (!$field->validate($filtered, $context)) { | |
$errors = $field->getErrors(); | |
if (count($errors) === 1) { | |
throw new InvalidValue(sprintf('Assertion failed: %s', $errors[0]->message), ...$errors); | |
} elseif (count($errors) > 1) { | |
// Ignoring coverage because of error in coverage analysis | |
// @codeCoverageIgnoreStart | |
throw new InvalidValue(sprintf( | |
'Assertion failed: %s', | |
implode('; ', array_map(function (Error $error) { | |
return $error->message; | |
}, $errors)) | |
), ...$errors); | |
// @codeCoverageIgnoreEnd | |
} else { | |
throw new InvalidValue(sprintf( | |
'Failed asserting that %s is valid (unknown error)', | |
json_encode($value) | |
)); | |
} | |
} | |
return $filtered; | |
} | |
/** | |
* Set the data that should be covered (the context) | |
* | |
* @param array $data | |
* @return $this | |
*/ | |
public function setData(array $data) | |
{ | |
$this->rawData = $data; | |
return $this; | |
} | |
/** | |
* Get all data or the value for $key | |
* | |
* @param string $key | |
* @param bool $validate | |
* @return array|mixed | |
* @throws InvalidValue When value is invalid | |
*/ | |
public function getData(string $key = null, $validate = true) | |
{ | |
if ($key !== null) { | |
if (!isset($this->fields[$key])) { | |
return null; | |
} | |
$fields = [$key => $this->fields[$key]]; | |
} else { | |
$fields = $this->fields; | |
} | |
$result = []; | |
foreach ($fields as $k => $field) { | |
$filtered = $field->filter($this->rawData[$k] ?? null, $this->rawData); | |
if ($validate && !$field->validate($this->rawData[$k] ?? null, $this->rawData)) { | |
if ($field->isRequired()) { | |
$errors = $field->getErrors(); | |
if (count($errors) > 0) { | |
throw new InvalidValue(sprintf('Invalid %s: %s', $k, $errors[0]->message), ...$errors); | |
} | |
throw new InvalidValue(sprintf('The value %s is not valid for %s', json_encode($filtered), $k)); | |
} else { | |
$filtered = null; | |
} | |
} | |
if ($key !== null) { | |
return $filtered; | |
} | |
$result[$k] = $filtered; | |
} | |
return $result; | |
} | |
/** | |
* Alias for getData | |
* | |
* @param string $key | |
* @return mixed | |
* @see Gate::getData() | |
* @codeCoverageIgnore trivial | |
* @throws InvalidValue | |
*/ | |
public function get(string $key = null) | |
{ | |
return $this->getData($key); | |
} | |
/** | |
* Alias for getData | |
* | |
* @param string $key | |
* @return mixed | |
* @see Gate::getData() | |
* @codeCoverageIgnore trivial | |
* @throws InvalidValue | |
*/ | |
public function __get(string $key) | |
{ | |
return $this->getData($key); | |
} | |
/** | |
* Validate $data or previously stored data | |
* | |
* @param array $data | |
* @return bool | |
*/ | |
public function validate(array $data = null) | |
{ | |
if ($data) { | |
$this->setData($data); | |
} | |
$valid = true; | |
$this->errors = []; | |
foreach ($this->fields as $key => $field) { | |
if (empty($this->rawData[$key]) && !$field->isRequired()) { | |
continue; | |
} | |
if (!$field->validate($this->rawData[$key] ?? null, $this->rawData)) { | |
$valid = false; | |
$this->errors[$key] = $field->getErrors(); | |
} | |
} | |
return $valid; | |
} | |
/** | |
* Get all reported errors | |
* | |
* @return array | |
* @codeCoverageIgnore trivial | |
*/ | |
public function getErrors() | |
{ | |
return $this->errors; | |
} | |
} |