validate-json 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/usr/bin/env php
  2. <?php
  3. /**
  4. * JSON schema validator
  5. *
  6. * @author Christian Weiske <christian.weiske@netresearch.de>
  7. */
  8. /**
  9. * Dead simple autoloader
  10. *
  11. * @param string $className Name of class to load
  12. *
  13. * @return void
  14. */
  15. function __autoload($className)
  16. {
  17. $className = ltrim($className, '\\');
  18. $fileName = '';
  19. $namespace = '';
  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;
  24. }
  25. $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
  26. if (stream_resolve_include_path($fileName)) {
  27. require_once $fileName;
  28. }
  29. }
  30. /**
  31. * Show the json parse error that happened last
  32. *
  33. * @return void
  34. */
  35. function showJsonError()
  36. {
  37. $constants = get_defined_constants(true);
  38. $json_errors = array();
  39. foreach ($constants['json'] as $name => $value) {
  40. if (!strncmp($name, 'JSON_ERROR_', 11)) {
  41. $json_errors[$value] = $name;
  42. }
  43. }
  44. echo 'JSON parse error: ' . $json_errors[json_last_error()] . "\n";
  45. }
  46. function getUrlFromPath($path)
  47. {
  48. if (parse_url($path, PHP_URL_SCHEME) !== null) {
  49. //already an URL
  50. return $path;
  51. }
  52. if ($path{0} == '/') {
  53. //absolute path
  54. return 'file://' . $path;
  55. }
  56. //relative path: make absolute
  57. return 'file://' . getcwd() . '/' . $path;
  58. }
  59. /**
  60. * Take a HTTP header value and split it up into parts.
  61. *
  62. * @return array Key "_value" contains the main value, all others
  63. * as given in the header value
  64. */
  65. function parseHeaderValue($headerValue)
  66. {
  67. if (strpos($headerValue, ';') === false) {
  68. return array('_value' => $headerValue);
  69. }
  70. $parts = explode(';', $headerValue);
  71. $arData = array('_value' => array_shift($parts));
  72. foreach ($parts as $part) {
  73. list($name, $value) = explode('=', $part);
  74. $arData[$name] = trim($value, ' "\'');
  75. }
  76. return $arData;
  77. }
  78. // support running this tool from git checkout
  79. if (is_dir(__DIR__ . '/../src/JsonSchema')) {
  80. set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path());
  81. }
  82. $arOptions = array();
  83. $arArgs = array();
  84. array_shift($argv);//script itself
  85. foreach ($argv as $arg) {
  86. if ($arg{0} == '-') {
  87. $arOptions[$arg] = true;
  88. } else {
  89. $arArgs[] = $arg;
  90. }
  91. }
  92. if (count($arArgs) == 0
  93. || isset($arOptions['--help']) || isset($arOptions['-h'])
  94. ) {
  95. echo <<<HLP
  96. Validate schema
  97. Usage: validate-json data.json
  98. or: validate-json data.json schema.json
  99. Options:
  100. --dump-schema Output full schema and exit
  101. --dump-schema-url Output URL of schema
  102. -h --help Show this help
  103. HLP;
  104. exit(1);
  105. }
  106. if (count($arArgs) == 1) {
  107. $pathData = $arArgs[0];
  108. $pathSchema = null;
  109. } else {
  110. $pathData = $arArgs[0];
  111. $pathSchema = getUrlFromPath($arArgs[1]);
  112. }
  113. $urlData = getUrlFromPath($pathData);
  114. $context = stream_context_create(
  115. array(
  116. 'http' => array(
  117. 'header' => array(
  118. 'Accept: */*',
  119. 'Connection: Close'
  120. ),
  121. 'max_redirects' => 5
  122. )
  123. )
  124. );
  125. $dataString = file_get_contents($pathData, false, $context);
  126. if ($dataString == '') {
  127. echo "Data file is not readable or empty.\n";
  128. exit(3);
  129. }
  130. $data = json_decode($dataString);
  131. unset($dataString);
  132. if ($data === null) {
  133. echo "Error loading JSON data file\n";
  134. showJsonError();
  135. exit(5);
  136. }
  137. if ($pathSchema === null) {
  138. if (isset($http_response_header)) {
  139. array_shift($http_response_header);//HTTP/1.0 line
  140. foreach ($http_response_header as $headerLine) {
  141. list($hName, $hValue) = explode(':', $headerLine, 2);
  142. $hName = strtolower($hName);
  143. if ($hName == 'link') {
  144. //Link: <http://example.org/schema#>; rel="describedBy"
  145. $hParts = parseHeaderValue($hValue);
  146. if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') {
  147. $pathSchema = trim($hParts['_value'], ' <>');
  148. }
  149. } else if ($hName == 'content-type') {
  150. //Content-Type: application/my-media-type+json;
  151. // profile=http://example.org/schema#
  152. $hParts = parseHeaderValue($hValue);
  153. if (isset($hParts['profile'])) {
  154. $pathSchema = $hParts['profile'];
  155. }
  156. }
  157. }
  158. }
  159. if (is_object($data) && property_exists($data, '$schema')) {
  160. $pathSchema = $data->{'$schema'};
  161. }
  162. //autodetect schema
  163. if ($pathSchema === null) {
  164. echo "JSON data must be an object and have a \$schema property.\n";
  165. echo "You can pass the schema file on the command line as well.\n";
  166. echo "Schema autodetection failed.\n";
  167. exit(6);
  168. }
  169. }
  170. if ($pathSchema{0} == '/') {
  171. $pathSchema = 'file://' . $pathSchema;
  172. }
  173. $resolver = new JsonSchema\Uri\UriResolver();
  174. $retriever = new JsonSchema\Uri\UriRetriever();
  175. try {
  176. $urlSchema = $resolver->resolve($pathSchema, $urlData);
  177. if (isset($arOptions['--dump-schema-url'])) {
  178. echo $urlSchema . "\n";
  179. exit();
  180. }
  181. $schema = $retriever->retrieve($urlSchema);
  182. if ($schema === null) {
  183. echo "Error loading JSON schema file\n";
  184. echo $urlSchema . "\n";
  185. showJsonError();
  186. exit(2);
  187. }
  188. } catch (Exception $e) {
  189. echo "Error loading JSON schema file\n";
  190. echo $urlSchema . "\n";
  191. echo $e->getMessage() . "\n";
  192. exit(2);
  193. }
  194. $refResolver = new JsonSchema\RefResolver($retriever);
  195. $refResolver->resolve($schema, $urlSchema);
  196. if (isset($arOptions['--dump-schema'])) {
  197. $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
  198. echo json_encode($schema, $options) . "\n";
  199. exit();
  200. }
  201. try {
  202. $validator = new JsonSchema\Validator();
  203. $validator->check($data, $schema);
  204. if ($validator->isValid()) {
  205. echo "OK. The supplied JSON validates against the schema.\n";
  206. } else {
  207. echo "JSON does not validate. Violations:\n";
  208. foreach ($validator->getErrors() as $error) {
  209. echo sprintf("[%s] %s\n", $error['property'], $error['message']);
  210. }
  211. exit(23);
  212. }
  213. } catch (Exception $e) {
  214. echo "JSON does not validate. Error:\n";
  215. echo $e->getMessage() . "\n";
  216. echo "Error code: " . $e->getCode() . "\n";
  217. exit(24);
  218. }
  219. ?>