4 * JSON schema validator
6 * @author Christian Weiske <christian.weiske@netresearch.de>
10 * Dead simple autoloader
12 * @param string $className Name of class to load
16 function __autoload($className)
18 $className = ltrim($className, '\\');
20 if ($lastNsPos = strrpos($className, '\\')) {
21 $namespace = substr($className, 0, $lastNsPos);
22 $className = substr($className, $lastNsPos + 1);
23 $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
25 $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
26 if (stream_resolve_include_path($fileName)) {
27 require_once $fileName;
31 // support running this tool from git checkout
32 if (is_dir(__DIR__ . '/../src/JsonSchema')) {
33 set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path());
38 array_shift($argv);//script itself
39 foreach ($argv as $arg) {
41 $arOptions[$arg] = true;
47 if (count($arArgs) == 0
48 || isset($arOptions['--help']) || isset($arOptions['-h'])
52 Usage: validate-json data.json
53 or: validate-json data.json schema.json
56 --dump-schema Output full schema and exit
57 --dump-schema-url Output URL of schema
58 --verbose Show additional output
59 --quiet Suppress all output
60 -h --help Show this help
66 if (count($arArgs) == 1) {
67 $pathData = $arArgs[0];
70 $pathData = $arArgs[0];
71 $pathSchema = getUrlFromPath($arArgs[1]);
75 * Show the json parse error that happened last
79 function showJsonError()
81 $constants = get_defined_constants(true);
82 $json_errors = array();
83 foreach ($constants['json'] as $name => $value) {
84 if (!strncmp($name, 'JSON_ERROR_', 11)) {
85 $json_errors[$value] = $name;
89 output('JSON parse error: ' . $json_errors[json_last_error()] . "\n");
92 function getUrlFromPath($path)
94 if (parse_url($path, PHP_URL_SCHEME) !== null) {
98 if ($path{0} == '/') {
100 return 'file://' . $path;
103 //relative path: make absolute
104 return 'file://' . getcwd() . '/' . $path;
108 * Take a HTTP header value and split it up into parts.
110 * @param $headerValue
111 * @return array Key "_value" contains the main value, all others
112 * as given in the header value
114 function parseHeaderValue($headerValue)
116 if (strpos($headerValue, ';') === false) {
117 return array('_value' => $headerValue);
120 $parts = explode(';', $headerValue);
121 $arData = array('_value' => array_shift($parts));
122 foreach ($parts as $part) {
123 list($name, $value) = explode('=', $part);
124 $arData[$name] = trim($value, ' "\'');
130 * Send a string to the output stream, but only if --quiet is not enabled
132 * @param $str A string output
134 function output($str) {
136 if (!isset($arOptions['--quiet'])) {
141 $urlData = getUrlFromPath($pathData);
143 $context = stream_context_create(
154 $dataString = file_get_contents($pathData, false, $context);
155 if ($dataString == '') {
156 output("Data file is not readable or empty.\n");
160 $data = json_decode($dataString);
162 if ($data === null) {
163 output("Error loading JSON data file\n");
168 if ($pathSchema === null) {
169 if (isset($http_response_header)) {
170 array_shift($http_response_header);//HTTP/1.0 line
171 foreach ($http_response_header as $headerLine) {
172 list($hName, $hValue) = explode(':', $headerLine, 2);
173 $hName = strtolower($hName);
174 if ($hName == 'link') {
175 //Link: <http://example.org/schema#>; rel="describedBy"
176 $hParts = parseHeaderValue($hValue);
177 if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') {
178 $pathSchema = trim($hParts['_value'], ' <>');
180 } else if ($hName == 'content-type') {
181 //Content-Type: application/my-media-type+json;
182 // profile=http://example.org/schema#
183 $hParts = parseHeaderValue($hValue);
184 if (isset($hParts['profile'])) {
185 $pathSchema = $hParts['profile'];
191 if (is_object($data) && property_exists($data, '$schema')) {
192 $pathSchema = $data->{'$schema'};
196 if ($pathSchema === null) {
197 output("JSON data must be an object and have a \$schema property.\n");
198 output("You can pass the schema file on the command line as well.\n");
199 output("Schema autodetection failed.\n");
203 if ($pathSchema{0} == '/') {
204 $pathSchema = 'file://' . $pathSchema;
207 $resolver = new JsonSchema\Uri\UriResolver();
208 $retriever = new JsonSchema\Uri\UriRetriever();
210 $urlSchema = $resolver->resolve($pathSchema, $urlData);
212 if (isset($arOptions['--dump-schema-url'])) {
213 echo $urlSchema . "\n";
216 } catch (Exception $e) {
217 output("Error loading JSON schema file\n");
218 output($urlSchema . "\n");
219 output($e->getMessage() . "\n");
222 $refResolver = new JsonSchema\SchemaStorage($retriever, $resolver);
223 $schema = $refResolver->resolveRef($urlSchema);
225 if (isset($arOptions['--dump-schema'])) {
226 $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
227 echo json_encode($schema, $options) . "\n";
232 $validator = new JsonSchema\Validator();
233 $validator->check($data, $schema);
235 if ($validator->isValid()) {
236 if(isset($arOptions['--verbose'])) {
237 output("OK. The supplied JSON validates against the schema.\n");
240 output("JSON does not validate. Violations:\n");
241 foreach ($validator->getErrors() as $error) {
242 output(sprintf("[%s] %s\n", $error['property'], $error['message']));
246 } catch (Exception $e) {
247 output("JSON does not validate. Error:\n");
248 output($e->getMessage() . "\n");
249 output("Error code: " . $e->getCode() . "\n");