little refactoring to work easier on other servers

git-svn-id: https://jsonschemaphpv.svn.sourceforge.net/svnroot/jsonschemaphpv/trunk@2 14558f9d-7ea9-46ec-92da-52a2cad6a683
This commit is contained in:
bruno_p_reis 2008-12-11 12:56:29 +00:00
parent fdf1e754f7
commit 4b9018fa97
23 changed files with 482 additions and 22 deletions

455
JsonSchema.php Normal file
View File

@ -0,0 +1,455 @@
<?php
/*
Copyright (c) 2008 Gradua Networks (www.gradua.net)
Author: Bruno Prieto Reis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
class JsonSchema {
static $errors = array();
static $formatValidator;
/**
* Validates a php object against a schema. Both the php object and the schema
* are supposed to be a result of a json_decode call.
* The validation works as defined by the schema proposal in
* http://www.json.com/json-schema-proposal/
*
* @param StdClass $instance
* @param StdClass $schema
* @param JsonFormatValidator $formatValidator an optional class that have methods to validate the format definitions.
* If this is null, so format validation will not be applied, but if its true, then the validation will throw
* an error if any format defined on the schema is not supported by the validator.
* @return unknown
*/
static public function validate($instance, $schema = null, $formatValidator = null) {
if($formatValidator) self::$formatValidator = $formatValidator;
$res = self::_validate($instance,$schema,false);
self::$formatValidator = null;
return $res;
}
static function _validate($instance,$schema = null,$_changing) {
// verify passed schema
if ($schema) {
self::checkProp($instance,$schema,'','',$_changing);
}
// verify "inline" schema
$propName = '$schema';
if (!$_changing && isset($instance->$propName)) {
self::checkProp($instance,$instance->$propName,'','',$_changing);
}
// show results
$obj = new stdClass();
$obj->valid = ! ((boolean)count(self::$errors));
$obj->errors = self::$errors;
return $obj;
}
static function incrementPath($path,$i) {
if($path) {
if(is_int($i)) {
$path .= '['.$i.']';
}
elseif($i = '') {
$path .= '';
}
else {
$path .= '.'.$i;
}
}
else {
$path = $i;
}
return $path;
}
static function checkArray($value,$schema,$path,$i,$_changing) {
//verify items
if(isset($schema->items)) {
//tuple typing
if(is_array($schema->items)) {
foreach($value as $k=>$v) {
if(array_key_exists($k,$schema->items)) {
self::checkProp($v,$schema->items[$k],$path,$k,$_changing);
}
else {
// aditional array properties
if(array_key_exists('additionalProperties',$schema)) {
if($schema->additionalProperties === false) {
self::adderror(
$path,
'The item '.$i.'['.$k.'] is not defined in the objTypeDef and the objTypeDef does not allow additional properties'
);
}
else {
self::checkProp($v,$schema->additionalProperties,$path,$k,$_changing);
}
}
}
}//foreach($value as $k=>$v) {
// treat when we have more schema definitions than values
for($k = count($value); $k < count($schema->items); $k++) {
self::checkProp(
new JsonSchemaUndefined(),
$schema->items[$k], $path, $k, $_changing
);
}
}
// just one type definition for the whole array
else {
foreach($value as $k=>$v) {
self::checkProp($v,$schema->items,$path,$k,$_changing);
}
}
}
// verify number of array items
if(isset($schema->minItems) && count($value) < $schema->minItems) {
self::adderror($path,"There must be a minimum of " . $schema->minItems . " in the array");
}
if(isset($schema->maxItems) && count($value) > $schema->maxItems) {
self::adderror($path,"There must be a maximum of " . $schema->maxItems . " in the array");
}
}
static function checkProp($value, $schema, $path, $i = '', $_changing = false) {
if (!is_object($schema)) {
return;
}
$path = self::incrementPath($path,$i);
// verify readonly
if($_changing && $schema.readonly) {
self::adderror($path,'is a readonly field, it can not be changed');
}
// I think a schema cant be an array, only the items property
/*if(is_array($schema)) {
if(!is_array($value)) {
return array(array('property'=>$path,'message'=>'An array tuple is required'));
}
for($a = 0; $a < count($schema); $a++) {
self::$errors = array_merge(
self::$errors,
self::checkProp($value->$a,$schema->$a,$path,$i,$_changing)
);
return self::$errors;
}
}*/
// if it extends another schema, it must pass that schema as well
if(isset($schema->extends)) {
self::checkProp($value,$schema->extends,$path,$i,$_changing);
}
// verify optional values
if (is_object($value) && $value instanceOf JsonSchemaUndefined) {
if ( isset($schema->optional) ? !$schema->optional : true) {
self::adderror($path,"is missing and it is not optional");
}
}
// normal verifications
else {
self::$errors = array_merge(
self::$errors,
self::checkType( isset($schema->type) ? $schema->type : null , $value, $path)
);
}
if(array_key_exists('disallow',$schema)) {
$errorsBeforeDisallowCheck = self::$errors;
Dbg::object($errorsBeforeDisallowCheck,'$errorsBeforeDisallowCheck');
$response = self::checkType($schema->disallow, $value, $path);
Dbg::object(count(self::$errors),'after errors');
if(
( count($errorsBeforeDisallowCheck) == count(self::$errors) ) &&
!count($response)
) {
self::adderror($path," disallowed value was matched");
}
else {
self::$errors = $errorsBeforeDisallowCheck;
}
}
//verify the itens on an array and min and max number of items.
if(is_array($value)) {
self::checkArray($value,$schema,$path,$i,$_changing);
}
############ verificar!
elseif(isset($schema->properties) && is_object($value)) {
self::checkObj(
$value,
$schema->properties,
$path,
isset($schema->additionalProperties) ? $schema->additionalProperties : null,
$_changing
);
}
// verify a regex pattern
if( isset($schema->pattern) && is_string($value) && !preg_match('/'.$schema->pattern.'/',$value)) {
self::adderror($path,"does not match the regex pattern " . $schema->pattern);
}
// verify maxLength, minLength, maximum and minimum values
if( isset($schema->maxLength) && is_string($value) && (strlen($value) > $schema->maxLength)) {
self::adderror($path,"must be at most " . $schema->maxLength . " characters long");
}
if( isset($schema->minLength) && is_string($value) && strlen($value) < $schema->minLength) {
self::adderror($path,"must be at least " . $schema->minLength . " characters long");
}
if( isset($schema->minimum) && gettype($value) == gettype($schema->minimum) && $value < $schema->minimum) {
self::adderror($path,"must have a minimum value of " . $schema->minimum);
}
if( isset($schema->maximum) && gettype($value) == gettype($schema->maximum) && $value > $schema->maximum) {
self::adderror($path,"must have a maximum value of " . $schema->maximum);
}
// verify enum values
if(isset($schema->enum)) {
$found = false;
foreach($schema->enum as $possibleValue) {
if($possibleValue == $value) {
$found = true;
break;
}
}
if(!$found) {
self::adderror($path,"does not have a value in the enumeration " . implode(', ',$schema->enum));
}
}
if(
isset($schema->maxDecimal) &&
( ($value * pow(10,$schema->maxDecimal)) != (int)($value * pow(10,$schema->maxDecimal)) )
) {
self::adderror($path,"may only have " . $schema->maxDecimal . " digits of decimal places");
}
if( isset($schema->format) && isset(self::$formatValidator) ) {
$error = self::$formatValidator->validate($value,$schema->format);
if($error) {
self::adderror($path,$error);
}
}
}
static function adderror($path,$message) {
self::$errors[] = array(
'property'=>$path,
'message'=>$message
);
}
/**
* @return array
*/
static function checkType($type, $value, $path) {
if($type) {
$wrongType = false;
if(is_string($type) && $type !== 'any') {
if($type == 'null') {
if (!is_null($value)) {
$wrongType = true;
}
}
else {
if($type == 'number') {
if(!in_array(gettype($value),array('integer','double'))) {
$wrongType = true;
}
} elseif( $type !== gettype($value) ) {
$wrongType = true;
}
}
}
if($wrongType) {
return array(
array(
'property'=>$path,
'message'=>gettype($value)." value found, but a ".$type." is required "
)
);
}
// Union Types :: for now, just return the message for the last expected type!!
if(is_array($type)) {
$validatedOneType = false;
$errors = array();
foreach($type as $tp) {
$error = self::checkType($tp,$value,$path);
if(!count($error)) {
$validatedOneType = true;
break;
}
else {
$errors[] = $error;
$errors = $error;
}
}
if(!$validatedOneType) {
return $errors;
}
}
elseif(is_object($type)) {
self::checkProp($value,$type,$path);
}
}
return array();
}
static function checkObj($instance, $objTypeDef, $path, $additionalProp,$_changing) {
if($objTypeDef instanceOf StdClass) {
if( ! ($instance instanceOf StdClass) || is_array($instance) ) {
self::$errors[] = array(
'property'=>$path,
'message'=>"an object is required"
);
}
foreach($objTypeDef as $i=>$value) {
$value =
array_key_exists($i,$instance) ?
$instance->$i :
new JsonSchemaUndefined();
$propDef = $objTypeDef->$i;
self::checkProp($value,$propDef,$path,$i,$_changing);
}
}
// additional properties and requires
foreach($instance as $i=>$value) {
// verify additional properties, when its not allowed
if( !isset($objTypeDef->$i) && ($additionalProp === false) && $i !== '$schema' ) {
self::$errors[] = array(
'property'=>$path,
'message'=>"The property " . $i . " is not defined in the objTypeDef and the objTypeDef does not allow additional properties"
);
}
// verify requires
if($objTypeDef && isset($objTypeDef->$i) && isset($objTypeDef->$i->requires)) {
$requires = $objTypeDef->$i->requires;
if(!array_key_exists($requires,$instance)) {
self::$errors[] = array(
'property'=>$path,
'message'=>"the presence of the property " . $i . " requires that " . $requires . " also be present"
);
}
}
$value = $instance->$i;
// To verify additional properties types.
if ($objTypeDef && is_object($objTypeDef) && !isset($objTypeDef->$i)) {
self::checkProp($value,$additionalProp,$path,$i);
}
// Verify inner schema definitions
$schemaPropName = '$schema';
if (!$_changing && $value && isset($value->$schemaPropName)) {
self::$errors = array_merge(
self::$errors,
checkProp($value,$value->$schemaPropname,$path,$i)
);
}
}
return self::$errors;
}
}
class Dbg {
static public $quietMode = false;
static function func($print = true, $numStackSteps = 1) {
$ar = debug_backtrace();
$ret = '';
for($a = $numStackSteps; $a >= 1; $a--) {
$line = $ar[$a-1]['line'];
$step = $ar[$a];
$ret .= str_repeat('&nbsp;&nbsp;&nbsp;',$a).self::showStep($step,$print,$line);
}
if($print && !self::$quietMode) echo $ret;
return $ret;
}
static function mark($title,$print = true) {
$ar = debug_backtrace();
$ret = '';
$line = $ar[0]['line'];
$ret = "[MARK]".$title.'(line '.$line.')<br/>';
if($print && !self::$quietMode) echo $ret;
return $ret;
}
static function object($object,$linkTitle,$varDump = false,$print = true) {
static $divCount = 0;
$divCount++;
$ar = debug_backtrace();
$ret = '';
$line = $ar[0]['line'];
$ret = '[OBJECT]<a href="javascript:void(0);" onclick="$(\'#div-obj-'.$divCount.'\').toggle()">'.$linkTitle.'</a>';
$ret .= '(line '.$line.')<br/>';
$ret .= '<div id=\'div-obj-'.$divCount.'\' style="display:none;">';
if($varDump) {
ob_start();
var_dump($object);
$ret .= ob_get_clean();
}
else {
$ret .= print_r($object,true);
}
$ret .= '</div>';
if($print && !self::$quietMode) echo $ret;
return $ret;
}
static function showStep($step,$print,$line) {
static $divCount = 0;
$ret = '[STEP]'.$step['class'] . $step['type'] . $step['function'];
if(count($step['args'])) {
$ret .= '(';
$comma = '';
$exp = array();
foreach($step['args'] as $num=>$arg) {
$divCount++;
if(in_array(gettype($arg),array('object','array'))) {
if(is_object($arg)) {
$type = get_class($arg);
}
else {
$type = gettype($arg);
}
$argVal = '<a href="javascript:void(0);" onclick="$(\'#div-step-'.$divCount.'\').toggle()">click to see</a>';
$exp[] =
'<div id=\'div-step-'.$divCount.'\' style="display:none;">'
.print_r($arg,true)
.'</div>';
}
else {
$type = gettype($arg);
if($type == 'string') {
$argVal = "'".$arg."'";
}
else {
$argVal = $arg;
}
$argVal = '<font style="color:#060">'.$argVal.'</font>';
}
$ret .= $comma.' <i>' . $type . "</i> " . $argVal;
$comma = ',';
}
$ret .= ') (line '.$line.')<br/>';
foreach($exp as $text) {
$ret .= '<pre>' . $text . '</pre>';
}
}
return $ret;
}
}
?>

3
JsonSchemaUndefined.php Normal file
View File

@ -0,0 +1,3 @@
<?php
class JsonSchemaUndefined {}
?>

View File

@ -3,6 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
<link rel="selenium.base" href="http://jsonschemaphpvalidator" />
<title>Test Suite</title>
</head>
<body>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="selenium.base" href="http://essaylibrary/" />
<link rel="selenium.base" href="http://jsonschemaphpv" />
<title>basicTypes</title>
</head>
<body>
@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
<td>/jsonValidator/validator.html</td>
<td>/validator.html</td>
<td></td>
</tr>
<tr>

View File

@ -1,5 +1,6 @@
<?php
require_once('../../plugins/JsSchema/lib/JsSchema.php');
require_once('JsonSchema.php');
require_once('JsonSchemaUndefined.php');
Dbg::$quietMode = true;
if($_REQUEST['schema']) {
@ -17,7 +18,7 @@ if(!$json) {
trigger_error('Could not parse the JSON object.',E_USER_ERROR);
}
$result = JsSchema::validate(
$result = JsonSchema::validate(
$json,
$schema
);