Skip to content

Commit 5082715

Browse files
committed
Merge with upstream master branch.
2 parents 52fb7c1 + b2165e1 commit 5082715

File tree

11 files changed

+632
-60
lines changed

11 files changed

+632
-60
lines changed

bin/validate-json

Lines changed: 171 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ function __autoload($className)
2424
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
2525
}
2626
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
27-
require_once $fileName;
27+
if (stream_resolve_include_path($fileName)) {
28+
require_once $fileName;
29+
}
2830
}
2931

3032
/**
@@ -45,55 +47,198 @@ function showJsonError()
4547
echo 'JSON parse error: ' . $json_errors[json_last_error()] . "\n";
4648
}
4749

50+
function getUrlFromPath($path)
51+
{
52+
if (parse_url($path, PHP_URL_SCHEME) !== null) {
53+
//already an URL
54+
return $path;
55+
}
56+
if ($path{0} == '/') {
57+
//absolute path
58+
return 'file://' . $path;
59+
}
60+
61+
//relative path: make absolute
62+
return 'file://' . getcwd() . '/' . $path;
63+
}
64+
65+
/**
66+
* Take a HTTP header value and split it up into parts.
67+
*
68+
* @return array Key "_value" contains the main value, all others
69+
* as given in the header value
70+
*/
71+
function parseHeaderValue($headerValue)
72+
{
73+
if (strpos($headerValue, ';') === false) {
74+
return array('_value' => $headerValue);
75+
}
76+
77+
$parts = explode(';', $headerValue);
78+
$arData = array('_value' => array_shift($parts));
79+
foreach ($parts as $part) {
80+
list($name, $value) = explode('=', $part);
81+
$arData[$name] = trim($value, ' "\'');
82+
}
83+
return $arData;
84+
}
85+
4886

4987
// support running this tool from git checkout
5088
if (is_dir(__DIR__ . '/../src/JsonSchema')) {
5189
set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path());
5290
}
5391

54-
if ($argc < 3) {
55-
echo "Usage: validate-json schema.json data.json\n";
56-
exit(1);
92+
$arOptions = array();
93+
$arArgs = array();
94+
array_shift($argv);//script itself
95+
foreach ($argv as $arg) {
96+
if ($arg{0} == '-') {
97+
$arOptions[$arg] = true;
98+
} else {
99+
$arArgs[] = $arg;
100+
}
57101
}
58102

59-
$pathSchema = $argv[1];
60-
$pathData = $argv[2];
103+
if (count($arArgs) == 0
104+
|| isset($arOptions['--help']) || isset($arOptions['-h'])
105+
) {
106+
echo <<<HLP
107+
Validate schema
108+
Usage: validate-json data.json
109+
or: validate-json data.json schema.json
61110
62-
if (!is_readable($pathSchema)) {
63-
echo "Schema file is not readable.\n";
64-
exit(2);
111+
Options:
112+
--dump-schema Output full schema and exit
113+
--dump-schema-url Output URL of schema
114+
-h --help Show this help
115+
116+
HLP;
117+
exit(1);
65118
}
66119

67-
if (!is_readable($pathData)) {
68-
echo "Data file is not readable.\n";
69-
exit(3);
120+
if (count($arArgs) == 1) {
121+
$pathData = $arArgs[0];
122+
$pathSchema = null;
123+
} else {
124+
$pathData = $arArgs[0];
125+
$pathSchema = getUrlFromPath($arArgs[1]);
70126
}
71127

72-
$data = json_decode(file_get_contents($pathData));
128+
$urlData = getUrlFromPath($pathData);
129+
130+
$context = stream_context_create(
131+
array(
132+
'http' => array(
133+
'header' => array(
134+
'Accept: */*',
135+
'Connection: Close'
136+
),
137+
'max_redirects' => 5
138+
)
139+
)
140+
);
141+
$dataString = file_get_contents($pathData, false, $context);
142+
if ($dataString == '') {
143+
echo "Data file is not readable or empty.\n";
144+
exit(3);
145+
}
73146

147+
$data = json_decode($dataString);
148+
unset($dataString);
74149
if ($data === null) {
75150
echo "Error loading JSON data file\n";
76151
showJsonError();
77152
exit(5);
78153
}
79154

80-
$schema = json_decode(file_get_contents($pathSchema));
155+
if ($pathSchema === null) {
156+
if (isset($http_response_header)) {
157+
array_shift($http_response_header);//HTTP/1.0 line
158+
foreach ($http_response_header as $headerLine) {
159+
list($hName, $hValue) = explode(':', $headerLine, 2);
160+
$hName = strtolower($hName);
161+
if ($hName == 'link') {
162+
//Link: <http://example.org/schema#>; rel="describedBy"
163+
$hParts = parseHeaderValue($hValue);
164+
if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') {
165+
$pathSchema = trim($hParts['_value'], ' <>');
166+
}
167+
} else if ($hName == 'content-type') {
168+
//Content-Type: application/my-media-type+json;
169+
// profile=http://example.org/schema#
170+
$hParts = parseHeaderValue($hValue);
171+
if (isset($hParts['profile'])) {
172+
$pathSchema = $hParts['profile'];
173+
}
174+
175+
}
176+
}
177+
}
178+
if (is_object($data) && property_exists($data, '$schema')) {
179+
$pathSchema = $data->{'$schema'};
180+
}
181+
182+
//autodetect schema
183+
if ($pathSchema === null) {
184+
echo "JSON data must be an object and have a \$schema property.\n";
185+
echo "You can pass the schema file on the command line as well.\n";
186+
echo "Schema autodetection failed.\n";
187+
exit(6);
188+
}
189+
}
190+
if ($pathSchema{0} == '/') {
191+
$pathSchema = 'file://' . $pathSchema;
192+
}
193+
194+
$resolver = new JsonSchema\Uri\UriResolver();
195+
$retriever = new JsonSchema\Uri\UriRetriever();
196+
try {
197+
$urlSchema = $resolver->resolve($pathSchema, $urlData);
198+
199+
if (isset($arOptions['--dump-schema-url'])) {
200+
echo $urlSchema . "\n";
201+
exit();
202+
}
81203

82-
if ($schema === null) {
204+
$schema = $retriever->retrieve($urlSchema);
205+
if ($schema === null) {
206+
echo "Error loading JSON schema file\n";
207+
echo $urlSchema . "\n";
208+
showJsonError();
209+
exit(2);
210+
}
211+
} catch (Exception $e) {
83212
echo "Error loading JSON schema file\n";
84-
showJsonError();
85-
exit(6);
213+
echo $urlSchema . "\n";
214+
echo $e->getMessage() . "\n";
215+
exit(2);
86216
}
217+
$refResolver = new JsonSchema\RefResolver($retriever);
218+
$refResolver->resolve($schema, $urlSchema);
87219

88-
$validator = new JsonSchema\Validator();
89-
$validator->check($data, $schema);
220+
if (isset($arOptions['--dump-schema'])) {
221+
echo json_encode($schema, JSON_PRETTY_PRINT) . "\n";
222+
exit();
223+
}
90224

91-
if ($validator->isValid()) {
92-
echo "OK. The supplied JSON validates against the schema.\n";
93-
} else {
94-
echo "JSON does not validate. Violations:\n";
95-
foreach ($validator->getErrors() as $error) {
96-
echo sprintf("[%s] %s\n", $error['property'], $error['message']);
225+
try {
226+
$validator = new JsonSchema\Validator();
227+
$validator->check($data, $schema);
228+
229+
if ($validator->isValid()) {
230+
echo "OK. The supplied JSON validates against the schema.\n";
231+
} else {
232+
echo "JSON does not validate. Violations:\n";
233+
foreach ($validator->getErrors() as $error) {
234+
echo sprintf("[%s] %s\n", $error['property'], $error['message']);
235+
}
236+
exit(23);
97237
}
98-
exit(23);
238+
} catch (Exception $e) {
239+
echo "JSON does not validate. Error:\n";
240+
echo $e->getMessage() . "\n";
241+
echo "Error code: " . $e->getCode() . "\n";
242+
exit(24);
99243
}
244+
?>

src/JsonSchema/Constraints/Undefined.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ public function check($value, $schema = null, $path = null, $i = null)
3030
}
3131

3232
if (!is_object($schema)) {
33-
throw new InvalidArgumentException('Given schema must be an object.');
33+
throw new InvalidArgumentException(
34+
'Given schema must be an object in ' . $path
35+
. ' but is a ' . gettype($schema)
36+
);
3437
}
3538

3639
$i = is_null($i) ? "" : $i;

src/JsonSchema/RefResolver.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
*/
2020
class RefResolver
2121
{
22+
/**
23+
* HACK to prevent too many recursive expansions.
24+
* Happens e.g. when you want to validate a schema against the schema
25+
* definition.
26+
*
27+
* @var integer
28+
*/
29+
protected static $depth = 0;
30+
2231
/**
2332
* @var UriRetrieverInterface
2433
*/
@@ -41,7 +50,7 @@ public function __construct($retriever = null)
4150
*/
4251
public function fetchRef($ref, $sourceUri)
4352
{
44-
$retriever = $this->getUriRetriever();
53+
$retriever = $this->getUriRetriever();
4554
$jsonSchema = $retriever->retrieve($ref, $sourceUri);
4655
$this->resolve($jsonSchema);
4756

@@ -79,7 +88,13 @@ public function getUriRetriever()
7988
*/
8089
public function resolve($schema, $sourceUri = null)
8190
{
91+
if (self::$depth > 7) {
92+
return;
93+
}
94+
++self::$depth;
95+
8296
if (! is_object($schema)) {
97+
--self::$depth;
8398
return;
8499
}
85100

@@ -107,6 +122,8 @@ public function resolve($schema, $sourceUri = null)
107122
foreach (array('dependencies', 'patternProperties', 'properties') as $propertyName) {
108123
$this->resolveObjectOfSchemas($schema, $propertyName, $sourceUri);
109124
}
125+
126+
--self::$depth;
110127
}
111128

112129
/**
@@ -201,4 +218,4 @@ public function setUriRetriever(UriRetriever $retriever)
201218

202219
return $this;
203220
}
204-
}
221+
}

src/JsonSchema/Uri/Retrievers/Curl.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
use JsonSchema\Validator;
1313

14-
use JsonSchema\Exception\ResourceNotFoundException;
15-
1614
/**
1715
* Tries to retrieve JSON schemas from a URI using cURL library
1816
*
@@ -44,7 +42,7 @@ public function retrieve($uri)
4442

4543
$response = curl_exec($ch);
4644
if (false === $response) {
47-
throw new ResourceNotFoundException('JSON schema not found');
45+
throw new \JsonSchema\Exception\ResourceNotFoundException('JSON schema not found');
4846
}
4947

5048
$this->fetchMessageBody($response);

src/JsonSchema/Uri/Retrievers/FileGetContents.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace JsonSchema\Uri\Retrievers;
1111

12+
use JsonSchema\Exception\ResourceNotFoundException;
1213
use JsonSchema\Validator;
1314

1415
/**
@@ -34,9 +35,14 @@ public function retrieve($uri)
3435

3536
$response = file_get_contents($uri);
3637
if (false === $response) {
37-
throw new ResourceNotFoundException('JSON schema not found');
38+
throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
3839
}
39-
40+
if ($response == ''
41+
&& substr($uri, 0, 7) == 'file://' && substr($uri, -1) == '/'
42+
) {
43+
throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
44+
}
45+
4046
$this->messageBody = $response;
4147
if (! empty($http_response_header)) {
4248
$this->fetchContentType($http_response_header);
@@ -73,4 +79,4 @@ protected static function getContentTypeMatchInHeader($header)
7379
return trim($match[1]);
7480
}
7581
}
76-
}
82+
}

src/JsonSchema/Uri/Retrievers/PredefinedArray.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use JsonSchema\Validator;
66
use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
7-
use JsonSchema\Exception\ResourceNotFoundException;
87

98
/**
109
* URI retrieved based on a predefined array of schemas
@@ -45,7 +44,7 @@ public function __construct(array $schemas, $contentType = Validator::SCHEMA_MED
4544
public function retrieve($uri)
4645
{
4746
if (!array_key_exists($uri, $this->schemas)) {
48-
throw new ResourceNotFoundException(sprintf(
47+
throw new \JsonSchema\Exception\ResourceNotFoundException(sprintf(
4948
'The JSON schema "%s" was not found.',
5049
$uri
5150
));

0 commit comments

Comments
 (0)