Skip to content
Open
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
38 changes: 36 additions & 2 deletions src/RomaricDrigon/MetaYaml/Exception/NodeValidatorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,50 @@
class NodeValidatorException extends \Exception
{
private $node_path;
private $paths = [];
private $messages = [];

public function __construct($node_path, $message)
public function __construct($node_path, $messages, $paths)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of why we need to pass an array of messages? The code is much less visible and I'm not sure current($messages) will always return what we expect. What about having $message (string) and an extra parameter $previousMessages(array) ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it looks like there's a small indentation glitch just in front of the constructor.

{
$this->node_path = $node_path;
if (!is_array($messages)) {
$messages = [$messages];
}
if (!is_array($paths)) {
$paths = [$paths];
}
$this->paths = $paths;
$this->messages = $messages;

parent::__construct($message);
parent::__construct(current($messages));
}

public function getNodePath()
{
return $this->node_path;
}

public function getPaths()
{
return $this->paths;
}

public function getMessages()
{
return $this->messages;
}

public function getReport()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure to understand what getReport()is giving? As it will become part of MetaYaml API, there should be a test over it.

{
$candidates = [];
$max_length = 0;
foreach($this->paths as $index => $path) {
$length = count(explode('/', $path));
$candidates[$length][] = $result[] = $path . ': ' . $this->messages[$index];
if ($length > $max_length) {
$max_length = $length;
}
}
return implode("\n", $candidates[$max_length]);
}
}
5 changes: 3 additions & 2 deletions src/RomaricDrigon/MetaYaml/MetaYaml.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace RomaricDrigon\MetaYaml;

use RomaricDrigon\MetaYaml\Exception\NodeValidatorException;
use RomaricDrigon\MetaYaml\SchemaValidator;
use RomaricDrigon\MetaYaml\Loader\JsonLoader;

Expand All @@ -23,8 +24,8 @@ public function __construct(array $schema, $validate = false)
// we validate the schema using the meta schema, defining the structure of our schema
try {
$this->validateSchema();
} catch (\Exception $e) {
throw new \Exception("Unable to validate schema with error: {$e->getMessage()}");
} catch (NodeValidatorException $e) {
throw new \Exception("Unable to validate schema with error: {$e->getMessage()}\n{$e->getReport()}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public function validate($name, $node, $data)
if ($this->checkRequired($name, $node, $data)) return true;

if (! is_array($data)) {
throw new NodeValidatorException($name, "The node '$name' is not an array");
throw new NodeValidatorException($name, "The node '$name' is not an array", $this->path);
}

$this->checkEmpty($name, $node, $data);
Expand All @@ -20,7 +20,8 @@ public function validate($name, $node, $data)
$this->schema_validator->validateNode($name.'.'.$key,
$value[$this->schema_validator->getFullName('type')],
$value,
isset($data[$key]) ? $data[$key] : null // isset(null) is false, but no big deal
isset($data[$key]) ? $data[$key] : null, // isset(null) is false, but no big deal
$this->path . '/' .$key
);

if (array_key_exists($key, $data)) {
Expand All @@ -35,14 +36,14 @@ public function validate($name, $node, $data)
}

throw new NodeValidatorException($name,
"The node '$name' has not allowed extra key(s): ".implode(', ', array_keys($data)));
"The node '$name' has not allowed extra key(s): ".implode(', ', array_keys($data)), $this->path);
}

protected function checkEmpty($name, array $node, $data) {
if ($data === array()
&& isset($node[$this->schema_validator->getFullName('not_empty')])
&& $node[$this->schema_validator->getFullName('not_empty')]) {
throw new NodeValidatorException($name, "The node '$name' can not be empty");
throw new NodeValidatorException($name, "The node '$name' can not be empty", $this->path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function validate($name, $node, $data)
if (is_bool($data) || (! $strict && ($data == 'true' || $data == 'false'))) {
return true;
} else {
throw new NodeValidatorException($name, "The node '$name' is not a boolean");
throw new NodeValidatorException($name, "The node '$name' is not a boolean", $this->path);
}
}
}
12 changes: 10 additions & 2 deletions src/RomaricDrigon/MetaYaml/NodeValidator/ChoiceNodeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ public function validate($name, $node, $data)
$valid = false;
$message = '';
$count_levels = -1;
$sub_messages = [];
$sub_paths = [];

foreach ($node[$this->schema_validator->getFullName('choices')] as $choice_config) {
try {
$this->schema_validator->validateNode($name, $choice_config[$this->schema_validator->getFullName('type')],
$choice_config, $data);
$choice_config, $data, $this->path);
$valid = true;
break;
} catch (NodeValidatorException $e) {
Expand All @@ -27,11 +29,17 @@ public function validate($name, $node, $data)
$message = $e->getMessage();
$count_levels = $current_count_levels;
}

$sub_messages = array_merge($sub_messages, $e->getMessages());
$sub_paths = array_merge($sub_paths, $e->getPaths());

}
}

if (! $valid) {
throw new NodeValidatorException($name, "The choice node '$name' is invalid with error: $message");
$messages = array_merge($sub_messages, ["The choice node '$name' is invalid with error: $message"]);
$paths = array_merge($sub_paths, [$this->path]);
throw new NodeValidatorException($name, $messages, $paths);
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function validate($name, $node, $data)
}

if (! in_array($data, $haystack, $strict)) {
throw new NodeValidatorException($name, "The value '$data' is not allowed for node '$name'");
throw new NodeValidatorException($name, "The value '$data' is not allowed for node '$name'", $this->path);
}

return true;
Expand Down
10 changes: 8 additions & 2 deletions src/RomaricDrigon/MetaYaml/NodeValidator/NodeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,25 @@
abstract class NodeValidator implements NodeValidatorInterface
{
protected $schema_validator;
protected $path;

public function __construct(SchemaValidator $schema_validator)
public function __construct(SchemaValidator $schema_validator)
{
$this->schema_validator = $schema_validator;
}

public function setPath($path)
{
$this->path = $path;
}

protected function checkRequired($name, array $node, $data)
{
if (! is_null($data)) return false; // ok anyway

if (isset($node[$this->schema_validator->getFullName('required')])
&& $node[$this->schema_validator->getFullName('required')]) {
throw new NodeValidatorException($name, sprintf("The node '$name' is required"));
throw new NodeValidatorException($name, sprintf("The node '$name' is required"), $this->path);
} else {
return true; // data null & not required, stop further validations
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function getValidator($name, $type, SchemaValidator $validator)
case 'partial':
return new PartialNodeValidator($validator);
default:
throw new NodeValidatorException($name, 'Unknown validator type : '.$type);
throw new NodeValidatorException($name, 'Unknown validator type : '.$type, $this->path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function validate($name, $node, $data)
$strict = isset($node[$this->schema_validator->getFullName('strict')]) && isset($node[$this->schema_validator->getFullName('strict')]);

if (! is_numeric($data) || ($strict && ! (is_integer($data) || is_float($data)))) {
throw new NodeValidatorException($name, "The node '$name' is not a number");
throw new NodeValidatorException($name, "The node '$name' is not a number", $this->path);
}

return true;
Expand All @@ -24,7 +24,7 @@ protected function checkEmpty($name, array $node, $data) {
if (($data === '0' || $data === 0 || $data === 0.0)
&& isset($node[$this->schema_validator->getFullName('not_empty')])
&& $node[$this->schema_validator->getFullName('not_empty')]) {
throw new NodeValidatorException($name, "The node '$name' can not be empty");
throw new NodeValidatorException($name, "The node '$name' can not be empty", $this->path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ public function validate($name, $node, $data)
if ($this->checkRequired($name, $node, $data)) return true;

// we will validate using the partial defined in schema -> _partials -> name
return $this->schema_validator->validatePartial($node[$this->schema_validator->getFullName('partial')], $data);
return $this->schema_validator->validatePartial($node[$this->schema_validator->getFullName('partial')], $data, $this->path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public function validate($name, $node, $data)
// preg_match return false if there's an error, 0 if not found, 1 if found
if (1 !== preg_match($node[$this->schema_validator->getFullName('pattern')], $data)) {
throw new NodeValidatorException($name,
"Node '$name' does not match pattern '{$node[$this->schema_validator->getFullName('pattern')]}'"
"Node '$name' does not match pattern '{$node[$this->schema_validator->getFullName('pattern')]}'",
$this->path
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public function validate($name, $node, $data)
if ($this->checkRequired($name, $node, $data)) return true;

if (! is_array($data)) {
throw new NodeValidatorException($name, "The node '$name' is not an array");
throw new NodeValidatorException($name, "The node '$name' is not an array", $this->path);
}

// get min and max number of prototype repetition
Expand All @@ -21,19 +21,20 @@ public function validate($name, $node, $data)

foreach ($data as $key => $subdata) {
if ($n >= $max) { // because we count from 0
throw new NodeValidatorException($name, "Prototype node '$name' has too much children");
throw new NodeValidatorException($name, "Prototype node '$name' has too much children", $this->path);
}

$this->schema_validator->validateNode($name.'.'.$key,
$node[$this->schema_validator->getFullName('prototype')][$this->schema_validator->getFullName('type')],
$node[$this->schema_validator->getFullName('prototype')],
$subdata);
$subdata,
$this->path . '/' . $key);

$n++;
}

if ($n < $min) {
throw new NodeValidatorException($name, "Prototype node '$name' has not enough children");
throw new NodeValidatorException($name, "Prototype node '$name' has not enough children", $this->path);
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function validate($name, $node, $data)
(isset($node[$this->schema_validator->getFullName('strict')])
&& $node[$this->schema_validator->getFullName('strict')]
&& ! is_string($data))) {
throw new NodeValidatorException($name, "The node '$name' is not a text value");
throw new NodeValidatorException($name, "The node '$name' is not a text value", $this->path);
}

return true;
Expand All @@ -25,7 +25,7 @@ protected function checkEmpty($name, array $node, $data) {
if ($data === ''
&& isset($node[$this->schema_validator->getFullName('not_empty')])
&& $node[$this->schema_validator->getFullName('not_empty')]) {
throw new NodeValidatorException($name, "The node '$name' can not be empty");
throw new NodeValidatorException($name, "The node '$name' can not be empty", $this->path);
}
}
}
11 changes: 8 additions & 3 deletions src/RomaricDrigon/MetaYaml/SchemaValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,18 @@ public function getFullName($name)

// validate nodes

public function validateNode($name, $type, $node, $data)
/**
* @throws Exception\NodeValidatorException
*/
public function validateNode($name, $type, $node, $data, $path = '')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation glitch again.

{
$validator = $this->factory->getValidator($name, $type, $this);
$validator->setPath($path);

return $validator->validate($name, $node, $data);
}

public function validatePartial($name, $data)
public function validatePartial($name, $data, $path = '')
{
if (! isset($this->schema_config['partials']) || ! isset($this->schema_config['partials'][$name])) {
throw new \Exception("You're using a partial but partial '$name' is not defined in your schema");
Expand All @@ -57,7 +61,8 @@ public function validatePartial($name, $data)
return $this->validateNode($name,
$this->schema_config['partials'][$name][$this->getFullName('type')],
$this->schema_config['partials'][$name],
$data
$data,
$path
);
}
}