123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- #!/usr/bin/env php
- <?php
- /**
- * JSON schema validator
- *
- * @author Christian Weiske <christian.weiske@netresearch.de>
- */
- /**
- * Dead simple autoloader
- *
- * @param string $className Name of class to load
- *
- * @return void
- */
- spl_autoload_register(function ($className)
- {
- $className = ltrim($className, '\\');
- $fileName = '';
- if ($lastNsPos = strrpos($className, '\\')) {
- $namespace = substr($className, 0, $lastNsPos);
- $className = substr($className, $lastNsPos + 1);
- $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
- }
- $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
- if (stream_resolve_include_path($fileName)) {
- require_once $fileName;
- }
- });
- // support running this tool from git checkout
- if (is_dir(__DIR__ . '/../src/JsonSchema')) {
- set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path());
- }
- $arOptions = array();
- $arArgs = array();
- array_shift($argv);//script itself
- foreach ($argv as $arg) {
- if ($arg[0] == '-') {
- $arOptions[$arg] = true;
- } else {
- $arArgs[] = $arg;
- }
- }
- if (count($arArgs) == 0
- || isset($arOptions['--help']) || isset($arOptions['-h'])
- ) {
- echo <<<HLP
- Validate schema
- Usage: validate-json data.json
- or: validate-json data.json schema.json
- Options:
- --dump-schema Output full schema and exit
- --dump-schema-url Output URL of schema
- --verbose Show additional output
- --quiet Suppress all output
- -h --help Show this help
- HLP;
- exit(1);
- }
- if (count($arArgs) == 1) {
- $pathData = $arArgs[0];
- $pathSchema = null;
- } else {
- $pathData = $arArgs[0];
- $pathSchema = getUrlFromPath($arArgs[1]);
- }
- /**
- * Show the json parse error that happened last
- *
- * @return void
- */
- function showJsonError()
- {
- $constants = get_defined_constants(true);
- $json_errors = array();
- foreach ($constants['json'] as $name => $value) {
- if (!strncmp($name, 'JSON_ERROR_', 11)) {
- $json_errors[$value] = $name;
- }
- }
- output('JSON parse error: ' . $json_errors[json_last_error()] . "\n");
- }
- function getUrlFromPath($path)
- {
- if (parse_url($path, PHP_URL_SCHEME) !== null) {
- //already an URL
- return $path;
- }
- if ($path[0] == '/') {
- //absolute path
- return 'file://' . $path;
- }
- //relative path: make absolute
- return 'file://' . getcwd() . '/' . $path;
- }
- /**
- * Take a HTTP header value and split it up into parts.
- *
- * @param $headerValue
- * @return array Key "_value" contains the main value, all others
- * as given in the header value
- */
- function parseHeaderValue($headerValue)
- {
- if (strpos($headerValue, ';') === false) {
- return array('_value' => $headerValue);
- }
- $parts = explode(';', $headerValue);
- $arData = array('_value' => array_shift($parts));
- foreach ($parts as $part) {
- list($name, $value) = explode('=', $part);
- $arData[$name] = trim($value, ' "\'');
- }
- return $arData;
- }
- /**
- * Send a string to the output stream, but only if --quiet is not enabled
- *
- * @param $str string A string output
- */
- function output($str) {
- global $arOptions;
- if (!isset($arOptions['--quiet'])) {
- echo $str;
- }
- }
- $urlData = getUrlFromPath($pathData);
- $context = stream_context_create(
- array(
- 'http' => array(
- 'header' => array(
- 'Accept: */*',
- 'Connection: Close'
- ),
- 'max_redirects' => 5
- )
- )
- );
- $dataString = file_get_contents($pathData, false, $context);
- if ($dataString == '') {
- output("Data file is not readable or empty.\n");
- exit(3);
- }
- $data = json_decode($dataString);
- unset($dataString);
- if ($data === null) {
- output("Error loading JSON data file\n");
- showJsonError();
- exit(5);
- }
- if ($pathSchema === null) {
- if (isset($http_response_header)) {
- array_shift($http_response_header);//HTTP/1.0 line
- foreach ($http_response_header as $headerLine) {
- list($hName, $hValue) = explode(':', $headerLine, 2);
- $hName = strtolower($hName);
- if ($hName == 'link') {
- //Link: <http://example.org/schema#>; rel="describedBy"
- $hParts = parseHeaderValue($hValue);
- if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') {
- $pathSchema = trim($hParts['_value'], ' <>');
- }
- } else if ($hName == 'content-type') {
- //Content-Type: application/my-media-type+json;
- // profile=http://example.org/schema#
- $hParts = parseHeaderValue($hValue);
- if (isset($hParts['profile'])) {
- $pathSchema = $hParts['profile'];
- }
- }
- }
- }
- if (is_object($data) && property_exists($data, '$schema')) {
- $pathSchema = $data->{'$schema'};
- }
- //autodetect schema
- if ($pathSchema === null) {
- output("JSON data must be an object and have a \$schema property.\n");
- output("You can pass the schema file on the command line as well.\n");
- output("Schema autodetection failed.\n");
- exit(6);
- }
- }
- if ($pathSchema[0] == '/') {
- $pathSchema = 'file://' . $pathSchema;
- }
- $resolver = new JsonSchema\Uri\UriResolver();
- $retriever = new JsonSchema\Uri\UriRetriever();
- try {
- $urlSchema = $resolver->resolve($pathSchema, $urlData);
- if (isset($arOptions['--dump-schema-url'])) {
- echo $urlSchema . "\n";
- exit();
- }
- } catch (Exception $e) {
- output("Error loading JSON schema file\n");
- output($urlSchema . "\n");
- output($e->getMessage() . "\n");
- exit(2);
- }
- $refResolver = new JsonSchema\SchemaStorage($retriever, $resolver);
- $schema = $refResolver->resolveRef($urlSchema);
- if (isset($arOptions['--dump-schema'])) {
- $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
- echo json_encode($schema, $options) . "\n";
- exit();
- }
- try {
- $validator = new JsonSchema\Validator();
- $validator->check($data, $schema);
- if ($validator->isValid()) {
- if(isset($arOptions['--verbose'])) {
- output("OK. The supplied JSON validates against the schema.\n");
- }
- } else {
- output("JSON does not validate. Violations:\n");
- foreach ($validator->getErrors() as $error) {
- output(sprintf("[%s] %s\n", $error['property'], $error['message']));
- }
- exit(23);
- }
- } catch (Exception $e) {
- output("JSON does not validate. Error:\n");
- output($e->getMessage() . "\n");
- output("Error code: " . $e->getCode() . "\n");
- exit(24);
- }
|