PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/addendum/annotations.php

http://php-xframe.googlecode.com/
PHP | 410 lines | 323 code | 66 blank | 21 comment | 48 complexity | 63a57f7b6fed848bc15f698ca4a4aa4b MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * Addendum PHP Reflection Annotations
  4. * http://code.google.com/p/addendum/
  5. *
  6. * Copyright (C) 2006-2009 Jan "johno Suchal <johno@jsmf.net>
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. **/
  19. require_once(dirname(__FILE__).'/annotations/annotation_parser.php');
  20. class Annotation {
  21. public $value;
  22. private static $creationStack = array();
  23. public final function __construct($data = array(), $target = false) {
  24. $reflection = new ReflectionClass($this);
  25. $class = $reflection->getName();
  26. if(isset(self::$creationStack[$class])) {
  27. trigger_error("Circular annotation reference on '$class'", E_USER_ERROR);
  28. return;
  29. }
  30. self::$creationStack[$class] = true;
  31. foreach($data as $key => $value) {
  32. if($reflection->hasProperty($key)) {
  33. $this->$key = $value;
  34. } else {
  35. trigger_error("Property '$key' not defined for annotation '$class'");
  36. }
  37. }
  38. $this->checkTargetConstraints($target);
  39. $this->checkConstraints($target);
  40. unset(self::$creationStack[$class]);
  41. }
  42. private function checkTargetConstraints($target) {
  43. $reflection = new ReflectionAnnotatedClass($this);
  44. if($reflection->hasAnnotation('Target')) {
  45. $value = $reflection->getAnnotation('Target')->value;
  46. $values = is_array($value) ? $value : array($value);
  47. foreach($values as $value) {
  48. if($value == 'class' && $target instanceof ReflectionClass) return;
  49. if($value == 'method' && $target instanceof ReflectionMethod) return;
  50. if($value == 'property' && $target instanceof ReflectionProperty) return;
  51. if($value == 'nested' && $target === false) return;
  52. }
  53. if($target === false) {
  54. trigger_error("Annotation '".get_class($this)."' nesting not allowed", E_USER_ERROR);
  55. } else {
  56. trigger_error("Annotation '".get_class($this)."' not allowed on ".$this->createName($target), E_USER_ERROR);
  57. }
  58. }
  59. }
  60. private function createName($target) {
  61. if($target instanceof ReflectionMethod) {
  62. return $target->getDeclaringClass()->getName().'::'.$target->getName();
  63. } elseif($target instanceof ReflectionProperty) {
  64. return $target->getDeclaringClass()->getName().'::$'.$target->getName();
  65. } else {
  66. return $target->getName();
  67. }
  68. }
  69. protected function checkConstraints($target) {}
  70. }
  71. class AnnotationsCollection {
  72. private $annotations;
  73. public function __construct($annotations) {
  74. $this->annotations = $annotations;
  75. }
  76. public function hasAnnotation($class) {
  77. $class = Addendum::resolveClassName($class);
  78. return isset($this->annotations[$class]);
  79. }
  80. public function getAnnotation($class) {
  81. $class = Addendum::resolveClassName($class);
  82. return isset($this->annotations[$class]) ? end($this->annotations[$class]) : false;
  83. }
  84. public function getAnnotations() {
  85. $result = array();
  86. foreach($this->annotations as $instances) {
  87. $result[] = end($instances);
  88. }
  89. return $result;
  90. }
  91. public function getAllAnnotations($restriction = false) {
  92. $restriction = Addendum::resolveClassName($restriction);
  93. $result = array();
  94. foreach($this->annotations as $class => $instances) {
  95. if(!$restriction || $restriction == $class) {
  96. $result = array_merge($result, $instances);
  97. }
  98. }
  99. return $result;
  100. }
  101. }
  102. class Annotation_Target extends Annotation {}
  103. class AnnotationsBuilder {
  104. private static $cache = array();
  105. public function build($targetReflection) {
  106. $data = $this->parse($targetReflection);
  107. $annotations = array();
  108. foreach($data as $class => $parameters) {
  109. foreach($parameters as $params) {
  110. $annotation = $this->instantiateAnnotation($class, $params, $targetReflection);
  111. if($annotation !== false) {
  112. $annotations[get_class($annotation)][] = $annotation;
  113. }
  114. }
  115. }
  116. return new AnnotationsCollection($annotations);
  117. }
  118. public function instantiateAnnotation($class, $parameters, $targetReflection = false) {
  119. $class = Addendum::resolveClassName($class);
  120. if(!Addendum::ignores($class) && is_subclass_of($class, 'Annotation') || $class == 'Annotation') {
  121. $annotationReflection = new ReflectionClass($class);
  122. return $annotationReflection->newInstance($parameters, $targetReflection);
  123. }
  124. return false;
  125. }
  126. private function parse($reflection) {
  127. $key = $this->createName($reflection);
  128. if(!isset(self::$cache[$key])) {
  129. $parser = new AnnotationsMatcher;
  130. $parser->matches($this->getDocComment($reflection), $data);
  131. self::$cache[$key] = $data;
  132. }
  133. return self::$cache[$key];
  134. }
  135. private function createName($target) {
  136. if($target instanceof ReflectionMethod) {
  137. return $target->getDeclaringClass()->getName().'::'.$target->getName();
  138. } elseif($target instanceof ReflectionProperty) {
  139. return $target->getDeclaringClass()->getName().'::$'.$target->getName();
  140. } else {
  141. return $target->getName();
  142. }
  143. }
  144. protected function getDocComment($reflection) {
  145. return Addendum::getDocComment($reflection);
  146. }
  147. public static function clearCache() {
  148. self::$cache = array();
  149. }
  150. }
  151. class ReflectionAnnotatedClass extends ReflectionClass {
  152. private $annotations;
  153. public function __construct($class) {
  154. parent::__construct($class);
  155. $this->annotations = $this->createAnnotationBuilder()->build($this);
  156. }
  157. public function hasAnnotation($class) {
  158. return $this->annotations->hasAnnotation($class);
  159. }
  160. public function getAnnotation($annotation) {
  161. return $this->annotations->getAnnotation($annotation);
  162. }
  163. public function getAnnotations() {
  164. return $this->annotations->getAnnotations();
  165. }
  166. public function getAllAnnotations($restriction = false) {
  167. return $this->annotations->getAllAnnotations($restriction);
  168. }
  169. public function getConstructor() {
  170. return $this->createReflectionAnnotatedMethod(parent::getConstructor());
  171. }
  172. public function getMethod($name) {
  173. return $this->createReflectionAnnotatedMethod(parent::getMethod($name));
  174. }
  175. public function getMethods($filter = -1) {
  176. $result = array();
  177. foreach(parent::getMethods($filter) as $method) {
  178. $result[] = $this->createReflectionAnnotatedMethod($method);
  179. }
  180. return $result;
  181. }
  182. public function getProperty($name) {
  183. return $this->createReflectionAnnotatedProperty(parent::getProperty($name));
  184. }
  185. public function getProperties($filter = -1) {
  186. $result = array();
  187. foreach(parent::getProperties($filter) as $property) {
  188. $result[] = $this->createReflectionAnnotatedProperty($property);
  189. }
  190. return $result;
  191. }
  192. public function getInterfaces() {
  193. $result = array();
  194. foreach(parent::getInterfaces() as $interface) {
  195. $result[] = $this->createReflectionAnnotatedClass($interface);
  196. }
  197. return $result;
  198. }
  199. public function getParentClass() {
  200. $class = parent::getParentClass();
  201. return $this->createReflectionAnnotatedClass($class);
  202. }
  203. protected function createAnnotationBuilder() {
  204. return new AnnotationsBuilder();
  205. }
  206. private function createReflectionAnnotatedClass($class) {
  207. return ($class !== false) ? new ReflectionAnnotatedClass($class->getName()) : false;
  208. }
  209. private function createReflectionAnnotatedMethod($method) {
  210. return ($method !== null) ? new ReflectionAnnotatedMethod($this->getName(), $method->getName()) : null;
  211. }
  212. private function createReflectionAnnotatedProperty($property) {
  213. return ($property !== null) ? new ReflectionAnnotatedProperty($this->getName(), $property->getName()) : null;
  214. }
  215. }
  216. class ReflectionAnnotatedMethod extends ReflectionMethod {
  217. private $annotations;
  218. public function __construct($class, $name) {
  219. parent::__construct($class, $name);
  220. $this->annotations = $this->createAnnotationBuilder()->build($this);
  221. }
  222. public function hasAnnotation($class) {
  223. return $this->annotations->hasAnnotation($class);
  224. }
  225. public function getAnnotation($annotation) {
  226. return $this->annotations->getAnnotation($annotation);
  227. }
  228. public function getAnnotations() {
  229. return $this->annotations->getAnnotations();
  230. }
  231. public function getAllAnnotations($restriction = false) {
  232. return $this->annotations->getAllAnnotations($restriction);
  233. }
  234. public function getDeclaringClass() {
  235. $class = parent::getDeclaringClass();
  236. return new ReflectionAnnotatedClass($class->getName());
  237. }
  238. protected function createAnnotationBuilder() {
  239. return new AnnotationsBuilder();
  240. }
  241. }
  242. class ReflectionAnnotatedProperty extends ReflectionProperty {
  243. private $annotations;
  244. public function __construct($class, $name) {
  245. parent::__construct($class, $name);
  246. $this->annotations = $this->createAnnotationBuilder()->build($this);
  247. }
  248. public function hasAnnotation($class) {
  249. return $this->annotations->hasAnnotation($class);
  250. }
  251. public function getAnnotation($annotation) {
  252. return $this->annotations->getAnnotation($annotation);
  253. }
  254. public function getAnnotations() {
  255. return $this->annotations->getAnnotations();
  256. }
  257. public function getAllAnnotations($restriction = false) {
  258. return $this->annotations->getAllAnnotations($restriction);
  259. }
  260. public function getDeclaringClass() {
  261. $class = parent::getDeclaringClass();
  262. return new ReflectionAnnotatedClass($class->getName());
  263. }
  264. protected function createAnnotationBuilder() {
  265. return new AnnotationsBuilder();
  266. }
  267. }
  268. class Addendum {
  269. private static $rawMode;
  270. private static $ignore;
  271. private static $classnames = array();
  272. private static $annotations = false;
  273. public static function getDocComment($reflection) {
  274. if(self::checkRawDocCommentParsingNeeded()) {
  275. $docComment = new DocComment();
  276. return $docComment->get($reflection);
  277. } else {
  278. return $reflection->getDocComment();
  279. }
  280. }
  281. /** Raw mode test */
  282. private static function checkRawDocCommentParsingNeeded() {
  283. if(self::$rawMode === null) {
  284. $reflection = new ReflectionClass('Addendum');
  285. $method = $reflection->getMethod('checkRawDocCommentParsingNeeded');
  286. self::setRawMode($method->getDocComment() === false);
  287. }
  288. return self::$rawMode;
  289. }
  290. public static function setRawMode($enabled = true) {
  291. if($enabled) {
  292. require_once(dirname(__FILE__).'/annotations/doc_comment.php');
  293. }
  294. self::$rawMode = $enabled;
  295. }
  296. public static function resetIgnoredAnnotations() {
  297. self::$ignore = array();
  298. }
  299. public static function ignores($class) {
  300. return isset(self::$ignore[$class]);
  301. }
  302. public static function ignore() {
  303. foreach(func_get_args() as $class) {
  304. self::$ignore[$class] = true;
  305. }
  306. }
  307. public static function resolveClassName($class) {
  308. if(isset(self::$classnames[$class])) return self::$classnames[$class];
  309. $matching = array();
  310. foreach(self::getDeclaredAnnotations() as $declared) {
  311. if($declared == $class) {
  312. $matching[] = $declared;
  313. } else {
  314. $pos = strrpos($declared, "_$class");
  315. if($pos !== false && ($pos + strlen($class) == strlen($declared) - 1)) {
  316. $matching[] = $declared;
  317. }
  318. }
  319. }
  320. $result = null;
  321. switch(count($matching)) {
  322. case 0: $result = $class; break;
  323. case 1: $result = $matching[0]; break;
  324. default: trigger_error("Cannot resolve class name for '$class'. Possible matches: " . join(', ', $matching), E_USER_ERROR);
  325. }
  326. self::$classnames[$class] = $result;
  327. return $result;
  328. }
  329. public static function setClassnames($classnames) {
  330. self::$classnames = $classnames;
  331. }
  332. private static function getDeclaredAnnotations() {
  333. if(!self::$annotations) {
  334. self::$annotations = array();
  335. foreach(get_declared_classes() as $class) {
  336. if(is_subclass_of($class, 'Annotation') || $class == 'Annotation') self::$annotations[] = $class;
  337. }
  338. }
  339. return self::$annotations;
  340. }
  341. }