Skip to content

cleanup, refactoring, splitted validators, tweaks #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 3, 2012
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
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# JSON Schema for PHP [![Build status...](https://secure.travis-ci.org/justinrainbow/json-schema.png)](http://travis-ci.org/justinrainbow/json-schema)
# JSON Schema for PHP [![Build Status](https://secure.travis-ci.org/justinrainbow/json-schema.png)](http://travis-ci.org/justinrainbow/json-schema)

Documentation can be found at http://jsonschema.readthedocs.org/
A PHP Implementation for validating `JSON` Structures against a given `Schema`.

See [json-schema](http://json-schema.org/) for more details.

## Installation

### Library

$ git clone https://github.com/justinrainbow/json-schema.git

### Dependencies

#### via `submodules` (*will use the Symfony ClassLoader Component*)

$ git submodule update --init

#### via [`composer`](https://github.com/composer/composer) (*will use the Composer ClassLoader*)

$ wget http://getcomposer.org/composer.phar
$ php composer.phar install

## Usage

```php
<?php

$validator = new JsonSchema\Validator();
$result = $validator->validate(json_decode($json), json_decode($schema));
$validator->check(json_decode($json), json_decode($schema));

if ($result->valid) {
if ($validator->isValid()) {
echo "The supplied JSON validates against the schema.\n";
} else {
echo "JSON does not validate. Violations:\n";
foreach ($result->errors as $error) {
echo "[{$error['property']}] {$error['message']}\n";
foreach ($validator->getErrors() as $error) {
echo sprintf("[%s] %s\n",$error['property'], $error['message']);
}
}
```

## Running the tests

$ git submodule update --init
$ phpunit
10 changes: 9 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"homepage": "https://github.com/justinrainbow/json-schema",
"type": "library",
"license": "NewBSD",
"version": "1.0.0",
"version": "1.1.0",
"authors": [
{
"name": "Bruno Prieto Reis",
Expand All @@ -14,6 +14,14 @@
{
"name": "Justin Rainbow",
"email": "[email protected]"
},
{
"name": "Igor Wiedler",
"email": "[email protected]"
},
{
"name": "Robert Schönthal",
"email": "[email protected]"
}
],
"require": {
Expand Down
16 changes: 11 additions & 5 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
bootstrap="tests/bootstrap.php"
verbose="true"
>
<testsuites>
<testsuite name="JSON Schema Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="JSON Schema Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>

<filter>
<whitelist>
<directory>./src/JsonSchema/</directory>
</whitelist>
</filter>
</phpunit>
95 changes: 95 additions & 0 deletions src/JsonSchema/Constraints/Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace JsonSchema\Constraints;

/**
* The Collection Constraints, validates an array against a given schema
*
* @author Robert Schönthal <[email protected]>
* @author Bruno Prieto Reis <[email protected]>
*/
class Collection extends Constraint
{
/**
* {inheritDoc}
*/
public function check($value, $schema = null, $path = null, $i = null)
{
// verify minItems
if (isset($schema->minItems) && count($value) < $schema->minItems) {
$this->addError($path, "There must be a minimum of " . $schema->minItems . " in the array");
}
// verify maxItems
if (isset($schema->maxItems) && count($value) > $schema->maxItems) {
$this->addError($path, "There must be a maximum of " . $schema->maxItems . " in the array");
}
// verify uniqueItems
//TODO array_unique doesnt work with objects
if (isset($schema->uniqueItems) && array_unique($value) != $value) {
$this->addError($path, "There are no duplicates allowed in the array");
}

//verify items
if (isset($schema->items)) {
$this->validateItems($value, $schema, $path, $i);
}
}

/**
* validates the items
*
* @param array $value
* @param \stdClass $schema
* @param string $path
* @param string $i
*/
protected function validateItems($value, $schema = null, $path = null, $i = null)
{
if (!is_array($schema->items)) {
// just one type definition for the whole array
foreach ($value as $k => $v) {
$initErrors = $this->getErrors();

//first check if its defined in "items"
if (!isset($schema->additionalItems) || $schema->additionalItems === false) {
$this->checkUndefined($v, $schema->items, $path, $k);
}

//recheck with "additionalItems" if the first test fails
if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) {
$secondErrors = $this->getErrors();
$this->checkUndefined($v, $schema->additionalItems, $path, $k);
}

//reset errors if needed
if (isset($secondErrors) && count($secondErrors) < $this->getErrors()) {
$this->errors = $secondErrors;
} elseif (isset($secondErrors) && count($secondErrors) == count($this->getErrors())) {
$this->errors = $initErrors;
}
}
} else {
//defined item type definitions
foreach ($value as $k => $v) {
if (array_key_exists($k, $schema->items)) {
$this->checkUndefined($v, $schema->items[$k], $path, $k);
} else {
// additional items
if (array_key_exists('additionalItems', $schema) && $schema->additionalItems !== false) {
$this->checkUndefined($v, $schema->additionalItems, $path, $k);
} else {
$this->addError(
$path,
'The item ' . $i . '[' . $k . '] is not defined in the objTypeDef and the objTypeDef does not allow additional properties'
);
}
}
}

// treat when we have more schema definitions than values
for ($k = count($value); $k < count($schema->items); $k++) {
$this->checkUndefined(new Undefined(), $schema->items[$k], $path, $k);
}
}
}
}
198 changes: 198 additions & 0 deletions src/JsonSchema/Constraints/Constraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php

namespace JsonSchema\Constraints;

/**
* The Base Constraints, all Validators should extend this class
*
* @author Robert Schönthal <[email protected]>
* @author Bruno Prieto Reis <[email protected]>
*/
abstract class Constraint implements ConstraintInterface
{
protected $checkMode = self::CHECK_MODE_NORMAL;
protected $errors = array();
protected $inlineSchemaProperty = '$schema';

const CHECK_MODE_NORMAL = 1;
const CHECK_MODE_TYPE_CAST = 2;

/**
* @param int $checkMode
*/
public function __construct($checkMode = self::CHECK_MODE_NORMAL)
{
$this->checkMode = $checkMode;
}

/**
* {inheritDoc}
*/
public function addError($path, $message)
{
$this->errors[] = array(
'property' => $path,
'message' => $message
);
}

/**
* {inheritDoc}
*/
public function addErrors(array $errors)
{
$this->errors = array_merge($this->errors, $errors);
}

/**
* {inheritDoc}
*/
public function getErrors()
{
return array_unique($this->errors, SORT_REGULAR);
}

/**
* bubble down the path
*
* @param string $path
* @param mixed $i
* @return string
*/
protected function incrementPath($path, $i)
{
if ($path !== '') {
if (is_int($i)) {
$path .= '[' . $i . ']';
} else if ($i == '') {
$path .= '';
} else {
$path .= '.' . $i;
}
} else {
$path = $i;
}

return $path;
}

/**
* validates an array
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkArray($value, $schema = null, $path = null, $i = null)
{
$validator = new Collection($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* validates an object
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkObject($value, $schema = null, $path = null, $i = null)
{
$validator = new Object($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* validates the type of a property
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkType($value, $schema = null, $path = null, $i = null)
{
$validator = new Type($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* checks a undefined element
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkUndefined($value, $schema = null, $path = null, $i = null)
{
$validator = new Undefined($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* checks a string element
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkString($value, $schema = null, $path = null, $i = null)
{
$validator = new String($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* checks a number element
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkNumber($value, $schema = null, $path = null, $i = null)
{
$validator = new Number($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* checks a enum element
*
* @param mixed $value
* @param mixed $schema
* @param mixed $path
* @param mixed $i
*/
protected function checkEnum($value, $schema = null, $path = null, $i = null)
{
$validator = new Enum($this->checkMode);
$validator->check($value, $schema, $path, $i);

$this->addErrors($validator->getErrors());
}

/**
* {inheritDoc}
*/
public function isValid()
{
return !$this->getErrors();
}
}
Loading