/vendor/justinrainbow/json-schema/src/JsonSchema/RefResolver.php

https://gitlab.com/yousafsyed/easternglamor · PHP · 277 lines · 130 code · 41 blank · 106 comment · 20 complexity · a28cced699d60a97258994f66e4cad48 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the JsonSchema package.
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. namespace JsonSchema;
  9. use JsonSchema\Exception\JsonDecodingException;
  10. use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
  11. use JsonSchema\Uri\UriRetriever;
  12. /**
  13. * Take in an object that's a JSON schema and take care of all $ref references
  14. *
  15. * @author Tyler Akins <fidian@rumkin.com>
  16. * @see README.md
  17. */
  18. class RefResolver
  19. {
  20. /**
  21. * HACK to prevent too many recursive expansions.
  22. * Happens e.g. when you want to validate a schema against the schema
  23. * definition.
  24. *
  25. * @var integer
  26. */
  27. protected static $depth = 0;
  28. /**
  29. * maximum references depth
  30. * @var integer
  31. */
  32. public static $maxDepth = 7;
  33. /**
  34. * @var UriRetrieverInterface
  35. */
  36. protected $uriRetriever = null;
  37. /**
  38. * @var object
  39. */
  40. protected $rootSchema = null;
  41. /**
  42. * @param UriRetriever $retriever
  43. */
  44. public function __construct($retriever = null)
  45. {
  46. $this->uriRetriever = $retriever;
  47. }
  48. /**
  49. * Retrieves a given schema given a ref and a source URI
  50. *
  51. * @param string $ref Reference from schema
  52. * @param string $sourceUri URI where original schema was located
  53. * @return object Schema
  54. */
  55. public function fetchRef($ref, $sourceUri)
  56. {
  57. $retriever = $this->getUriRetriever();
  58. $jsonSchema = $retriever->retrieve($ref, $sourceUri);
  59. $this->resolve($jsonSchema);
  60. return $jsonSchema;
  61. }
  62. /**
  63. * Return the URI Retriever, defaulting to making a new one if one
  64. * was not yet set.
  65. *
  66. * @return UriRetriever
  67. */
  68. public function getUriRetriever()
  69. {
  70. if (is_null($this->uriRetriever)) {
  71. $this->setUriRetriever(new UriRetriever);
  72. }
  73. return $this->uriRetriever;
  74. }
  75. /**
  76. * Resolves all $ref references for a given schema. Recurses through
  77. * the object to resolve references of any child schemas.
  78. *
  79. * The 'format' property is omitted because it isn't required for
  80. * validation. Theoretically, this class could be extended to look
  81. * for URIs in formats: "These custom formats MAY be expressed as
  82. * an URI, and this URI MAY reference a schema of that format."
  83. *
  84. * The 'id' property is not filled in, but that could be made to happen.
  85. *
  86. * @param object $schema JSON Schema to flesh out
  87. * @param string $sourceUri URI where this schema was located
  88. */
  89. public function resolve($schema, $sourceUri = null)
  90. {
  91. if (self::$depth > self::$maxDepth) {
  92. self::$depth = 0;
  93. throw new JsonDecodingException(JSON_ERROR_DEPTH);
  94. }
  95. ++self::$depth;
  96. if (! is_object($schema)) {
  97. --self::$depth;
  98. return;
  99. }
  100. if (null === $sourceUri && ! empty($schema->id)) {
  101. $sourceUri = $schema->id;
  102. }
  103. if (null === $this->rootSchema) {
  104. $this->rootSchema = $schema;
  105. }
  106. // Resolve $ref first
  107. $this->resolveRef($schema, $sourceUri);
  108. // These properties are just schemas
  109. // eg. items can be a schema or an array of schemas
  110. foreach (array('additionalItems', 'additionalProperties', 'extends', 'items') as $propertyName) {
  111. $this->resolveProperty($schema, $propertyName, $sourceUri);
  112. }
  113. // These are all potentially arrays that contain schema objects
  114. // eg. type can be a value or an array of values/schemas
  115. // eg. items can be a schema or an array of schemas
  116. foreach (array('disallow', 'extends', 'items', 'type', 'allOf', 'anyOf', 'oneOf') as $propertyName) {
  117. $this->resolveArrayOfSchemas($schema, $propertyName, $sourceUri);
  118. }
  119. // These are all objects containing properties whose values are schemas
  120. foreach (array('dependencies', 'patternProperties', 'properties') as $propertyName) {
  121. $this->resolveObjectOfSchemas($schema, $propertyName, $sourceUri);
  122. }
  123. --self::$depth;
  124. }
  125. /**
  126. * Given an object and a property name, that property should be an
  127. * array whose values can be schemas.
  128. *
  129. * @param object $schema JSON Schema to flesh out
  130. * @param string $propertyName Property to work on
  131. * @param string $sourceUri URI where this schema was located
  132. */
  133. public function resolveArrayOfSchemas($schema, $propertyName, $sourceUri)
  134. {
  135. if (! isset($schema->$propertyName) || ! is_array($schema->$propertyName)) {
  136. return;
  137. }
  138. foreach ($schema->$propertyName as $possiblySchema) {
  139. $this->resolve($possiblySchema, $sourceUri);
  140. }
  141. }
  142. /**
  143. * Given an object and a property name, that property should be an
  144. * object whose properties are schema objects.
  145. *
  146. * @param object $schema JSON Schema to flesh out
  147. * @param string $propertyName Property to work on
  148. * @param string $sourceUri URI where this schema was located
  149. */
  150. public function resolveObjectOfSchemas($schema, $propertyName, $sourceUri)
  151. {
  152. if (! isset($schema->$propertyName) || ! is_object($schema->$propertyName)) {
  153. return;
  154. }
  155. foreach (get_object_vars($schema->$propertyName) as $possiblySchema) {
  156. $this->resolve($possiblySchema, $sourceUri);
  157. }
  158. }
  159. /**
  160. * Given an object and a property name, that property should be a
  161. * schema object.
  162. *
  163. * @param object $schema JSON Schema to flesh out
  164. * @param string $propertyName Property to work on
  165. * @param string $sourceUri URI where this schema was located
  166. */
  167. public function resolveProperty($schema, $propertyName, $sourceUri)
  168. {
  169. if (! isset($schema->$propertyName)) {
  170. return;
  171. }
  172. $this->resolve($schema->$propertyName, $sourceUri);
  173. }
  174. /**
  175. * Look for the $ref property in the object. If found, remove the
  176. * reference and augment this object with the contents of another
  177. * schema.
  178. *
  179. * @param object $schema JSON Schema to flesh out
  180. * @param string $sourceUri URI where this schema was located
  181. */
  182. public function resolveRef($schema, $sourceUri)
  183. {
  184. $ref = '$ref';
  185. if (empty($schema->$ref)) {
  186. return;
  187. }
  188. $splitRef = explode('#', $schema->$ref, 2);
  189. $refDoc = $splitRef[0];
  190. $refPath = null;
  191. if (count($splitRef) === 2) {
  192. $refPath = explode('/', $splitRef[1]);
  193. array_shift($refPath);
  194. }
  195. if (empty($refDoc) && empty($refPath)) {
  196. // TODO: Not yet implemented - root pointer ref, causes recursion issues
  197. return;
  198. }
  199. if (!empty($refDoc)) {
  200. $refSchema = $this->fetchRef($refDoc, $sourceUri);
  201. } else {
  202. $refSchema = $this->rootSchema;
  203. }
  204. if (null !== $refPath) {
  205. $refSchema = $this->resolveRefSegment($refSchema, $refPath);
  206. }
  207. unset($schema->$ref);
  208. // Augment the current $schema object with properties fetched
  209. foreach (get_object_vars($refSchema) as $prop => $value) {
  210. $schema->$prop = $value;
  211. }
  212. }
  213. /**
  214. * Set URI Retriever for use with the Ref Resolver
  215. *
  216. * @param UriRetriever $retriever
  217. * @return $this for chaining
  218. */
  219. public function setUriRetriever(UriRetriever $retriever)
  220. {
  221. $this->uriRetriever = $retriever;
  222. return $this;
  223. }
  224. protected function resolveRefSegment($data, $pathParts)
  225. {
  226. foreach ($pathParts as $path) {
  227. $path = strtr($path, array('~1' => '/', '~0' => '~', '%25' => '%'));
  228. if (is_array($data)) {
  229. $data = $data[$path];
  230. } else {
  231. $data = $data->{$path};
  232. }
  233. }
  234. return $data;
  235. }
  236. }