Analysis.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /**
  3. * @license Apache 2.0
  4. */
  5. namespace Swagger;
  6. use Closure;
  7. use Exception;
  8. use SplObjectStorage;
  9. use stdClass;
  10. use Swagger\Annotations\AbstractAnnotation;
  11. use Swagger\Annotations\Swagger;
  12. use Swagger\Processors\AugmentDefinitions;
  13. use Swagger\Processors\AugmentOperations;
  14. use Swagger\Processors\AugmentParameters;
  15. use Swagger\Processors\AugmentProperties;
  16. use Swagger\Processors\BuildPaths;
  17. use Swagger\Processors\CleanUnmerged;
  18. use Swagger\Processors\HandleReferences;
  19. use Swagger\Processors\InheritProperties;
  20. use Swagger\Processors\MergeIntoSwagger;
  21. /**
  22. * Result of the analyser which pretends to be an array of annotations, but also contains detected classes and helper functions for the processors.
  23. */
  24. class Analysis
  25. {
  26. /**
  27. * @var SplObjectStorage
  28. */
  29. public $annotations;
  30. /**
  31. * Class definitions
  32. * @var array
  33. */
  34. public $classes = [];
  35. /**
  36. * The target Swagger annotation.
  37. * @var Swagger
  38. */
  39. public $swagger;
  40. /**
  41. * Registry for the post-processing operations.
  42. * @var Closure[]
  43. */
  44. private static $processors;
  45. /**
  46. * @param array $annotations
  47. * @param null $context
  48. */
  49. public function __construct($annotations = [], $context = null)
  50. {
  51. $this->annotations = new SplObjectStorage();
  52. if (count($annotations) !== 0) {
  53. if ($context === null) {
  54. $context = Context::detect(1);
  55. }
  56. $this->addAnnotations($annotations, $context);
  57. }
  58. }
  59. /**
  60. * @param AbstractAnnotation $annotation
  61. * @param Context $context
  62. */
  63. public function addAnnotation($annotation, $context)
  64. {
  65. if ($this->annotations->contains($annotation)) {
  66. return;
  67. }
  68. if ($annotation instanceof AbstractAnnotation) {
  69. $context = $annotation->_context;
  70. } else {
  71. if ($context->is('annotations') === false) {
  72. $context->annotations = [];
  73. }
  74. if (in_array($annotation, $context->annotations, true) === false) {
  75. $context->annotations[] = $annotation;
  76. }
  77. }
  78. $this->annotations->attach($annotation, $context);
  79. $blacklist = property_exists($annotation, '_blacklist') ? $annotation::$_blacklist : [];
  80. foreach ($annotation as $property => $value) {
  81. if (in_array($property, $blacklist)) {
  82. if ($property === '_unmerged') {
  83. foreach ($value as $item) {
  84. $this->addAnnotation($item, $context);
  85. }
  86. }
  87. continue;
  88. } elseif (is_array($value)) {
  89. foreach ($value as $item) {
  90. if ($item instanceof AbstractAnnotation) {
  91. $this->addAnnotation($item, $context);
  92. }
  93. }
  94. } elseif ($value instanceof AbstractAnnotation) {
  95. $this->addAnnotation($value, $context);
  96. }
  97. }
  98. }
  99. /**
  100. * @param array $annotations
  101. * @param Context $context
  102. */
  103. public function addAnnotations($annotations, $context)
  104. {
  105. foreach ($annotations as $annotation) {
  106. $this->addAnnotation($annotation, $context);
  107. }
  108. }
  109. /**
  110. * @param array $definition
  111. */
  112. public function addClassDefinition($definition)
  113. {
  114. $class = $definition['context']->fullyQualifiedName($definition['class']);
  115. $this->classes[$class] = $definition;
  116. }
  117. /**
  118. * @param Analysis $analysis
  119. */
  120. public function addAnalysis($analysis)
  121. {
  122. foreach ($analysis->annotations as $annotation) {
  123. $this->addAnnotation($annotation, $analysis->annotations[$annotation]);
  124. }
  125. $this->classes = array_merge($this->classes, $analysis->classes);
  126. if ($this->swagger === null && $analysis->swagger) {
  127. $this->swagger = $analysis->swagger;
  128. $analysis->target->_context->analysis = $this;
  129. }
  130. }
  131. public function getSubClasses($class)
  132. {
  133. $definitions = [];
  134. foreach ($this->classes as $subclass => $definition) {
  135. if ($definition['extends'] === $class) {
  136. $definitions[$subclass] = $definition;
  137. $definitions = array_merge($definitions, $this->getSubClasses($subclass));
  138. }
  139. }
  140. return $definitions;
  141. }
  142. public function getSuperClasses($class)
  143. {
  144. $classDefinition = isset($this->classes[$class]) ? $this->classes[$class] : null;
  145. if (!$classDefinition || empty($classDefinition['extends'])) { // unknown class, or no inheritance?
  146. return [];
  147. }
  148. $extends = $classDefinition['extends'];
  149. $extendsDefinition = isset($this->classes[$extends]) ? $this->classes[$extends] : null;
  150. if (!$extendsDefinition) {
  151. return [];
  152. }
  153. $definitions = array_merge([$extends => $extendsDefinition], $this->getSuperClasses($extends));
  154. return $definitions;
  155. }
  156. /**
  157. *
  158. * @param string $class
  159. * @param boolean $strict Innon-strict mode childclasses are also detected.
  160. * @return array
  161. */
  162. public function getAnnotationsOfType($class, $strict = false)
  163. {
  164. $annotations = [];
  165. if ($strict) {
  166. foreach ($this->annotations as $annotation) {
  167. if (get_class($annotation) === $class) {
  168. $annotations[] = $annotation;
  169. }
  170. }
  171. } else {
  172. foreach ($this->annotations as $annotation) {
  173. if ($annotation instanceof $class) {
  174. $annotations[] = $annotation;
  175. }
  176. }
  177. }
  178. return $annotations;
  179. }
  180. /**
  181. *
  182. * @param object $annotation
  183. * @return \Swagger\Context
  184. */
  185. public function getContext($annotation)
  186. {
  187. if ($annotation instanceof AbstractAnnotation) {
  188. return $annotation->_context;
  189. }
  190. if ($this->annotations->contains($annotation) === false) {
  191. throw new Exception('Annotation not found');
  192. }
  193. $context = $this->annotations[$annotation];
  194. if ($context instanceof Context) {
  195. return $context;
  196. }
  197. var_dump($context);
  198. ob_flush();
  199. die;
  200. throw new Exception('Annotation has no context'); // Weird, did you use the addAnnotation/addAnnotations methods?
  201. }
  202. /**
  203. * Build an analysis with only the annotations that are merged into the swagger annotation.
  204. *
  205. * @return Analysis
  206. */
  207. public function merged()
  208. {
  209. if (!$this->swagger) {
  210. throw new Exception('No swagger target set. Run the MergeIntoSwagger processor');
  211. }
  212. $unmerged = $this->swagger->_unmerged;
  213. $this->swagger->_unmerged = [];
  214. $analysis = new Analysis([$this->swagger]);
  215. $this->swagger->_unmerged = $unmerged;
  216. return $analysis;
  217. }
  218. /**
  219. * Analysis with only the annotations that not merged.
  220. *
  221. * @return Analysis
  222. */
  223. public function unmerged()
  224. {
  225. return $this->split()->unmerged;
  226. }
  227. /**
  228. * Split the annotation into two analysis.
  229. * One with annotations that are merged and one with annotations that are not merged.
  230. *
  231. * @return object {merged: Analysis, unmerged: Analysis}
  232. */
  233. public function split()
  234. {
  235. $result = new stdClass();
  236. $result->merged = $this->merged();
  237. $result->unmerged = new Analysis();
  238. foreach ($this->annotations as $annotation) {
  239. if ($result->merged->annotations->contains($annotation) === false) {
  240. $result->unmerged->annotations->attach($annotation, $this->annotations[$annotation]);
  241. }
  242. }
  243. return $result;
  244. }
  245. /**
  246. * Apply the processor(s)
  247. * @param Closure|Closure[] $processors One or more processors
  248. */
  249. public function process($processors = null)
  250. {
  251. if ($processors === null) { // Use the default and registered processors.
  252. $processors = self::processors();
  253. }
  254. if (is_array($processors) === false && is_callable($processors)) {
  255. $processors = [$processors];
  256. }
  257. foreach ($processors as $processor) {
  258. $processor($this);
  259. }
  260. }
  261. /**
  262. * Get direct access to the processors array.
  263. * @return array reference
  264. */
  265. public static function &processors()
  266. {
  267. if (!self::$processors) {
  268. // Add default processors.
  269. self::$processors = [
  270. new MergeIntoSwagger(),
  271. new BuildPaths(),
  272. new HandleReferences(),
  273. new AugmentDefinitions(),
  274. new AugmentProperties(),
  275. new InheritProperties(),
  276. new AugmentOperations(),
  277. new AugmentParameters(),
  278. new CleanUnmerged(),
  279. ];
  280. }
  281. return self::$processors;
  282. }
  283. /**
  284. * Register a processor
  285. * @param Closure $processor
  286. */
  287. public static function registerProcessor($processor)
  288. {
  289. array_push(self::processors(), $processor);
  290. }
  291. /**
  292. * Unregister a processor
  293. * @param Closure $processor
  294. */
  295. public static function unregisterProcessor($processor)
  296. {
  297. $processors = &self::processors();
  298. $key = array_search($processor, $processors, true);
  299. if ($key === false) {
  300. throw new Exception('Given processor was not registered');
  301. }
  302. unset($processors[$key]);
  303. }
  304. public function validate()
  305. {
  306. if ($this->swagger) {
  307. return $this->swagger->validate();
  308. }
  309. Logger::notice('No swagger target set. Run the MergeIntoSwagger processor before validate()');
  310. return false;
  311. }
  312. }