Introspector.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Amf
  17. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Introspector.php 2504 2011-12-28 07:35:29Z liu21st $
  20. */
  21. /** Zend_Amf_Parse_TypeLoader */
  22. require_once 'Zend/Amf/Parse/TypeLoader.php';
  23. /** Zend_Reflection_Class */
  24. require_once 'Zend/Reflection/Class.php';
  25. /** Zend_Server_Reflection */
  26. require_once 'Zend/Server/Reflection.php';
  27. /**
  28. * This class implements a service for generating AMF service descriptions as XML.
  29. *
  30. * @package Zend_Amf
  31. * @subpackage Adobe
  32. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Amf_Adobe_Introspector
  36. {
  37. /**
  38. * Options used:
  39. * - server: instance of Zend_Amf_Server to use
  40. * - directories: directories where class files may be looked up
  41. *
  42. * @var array Introspector options
  43. */
  44. protected $_options;
  45. /**
  46. * @var DOMElement DOM element to store types
  47. */
  48. protected $_types;
  49. /**
  50. * @var array Map of the known types
  51. */
  52. protected $_typesMap = array();
  53. /**
  54. * @var DOMDocument XML document to store data
  55. */
  56. protected $_xml;
  57. /**
  58. * Constructor
  59. *
  60. * @return void
  61. */
  62. public function __construct()
  63. {
  64. $this->_xml = new DOMDocument('1.0', 'utf-8');
  65. }
  66. /**
  67. * Create XML definition on an AMF service class
  68. *
  69. * @param string $serviceClass Service class name
  70. * @param array $options invocation options
  71. * @return string XML with service class introspection
  72. */
  73. public function introspect($serviceClass, $options = array())
  74. {
  75. $this->_options = $options;
  76. if (strpbrk($serviceClass, '\\/<>')) {
  77. return $this->_returnError('Invalid service name');
  78. }
  79. // Transform com.foo.Bar into com_foo_Bar
  80. $serviceClass = str_replace('.' , '_', $serviceClass);
  81. // Introspect!
  82. if (!class_exists($serviceClass)) {
  83. require_once 'Zend/Loader.php';
  84. Zend_Loader::loadClass($serviceClass, $this->_getServicePath());
  85. }
  86. $serv = $this->_xml->createElement('service-description');
  87. $serv->setAttribute('xmlns', 'http://ns.adobe.com/flex/service-description/2008');
  88. $this->_types = $this->_xml->createElement('types');
  89. $this->_ops = $this->_xml->createElement('operations');
  90. $r = Zend_Server_Reflection::reflectClass($serviceClass);
  91. $this->_addService($r, $this->_ops);
  92. $serv->appendChild($this->_types);
  93. $serv->appendChild($this->_ops);
  94. $this->_xml->appendChild($serv);
  95. return $this->_xml->saveXML();
  96. }
  97. /**
  98. * Authentication handler
  99. *
  100. * @param Zend_Acl $acl
  101. * @return unknown_type
  102. */
  103. public function initAcl(Zend_Acl $acl)
  104. {
  105. return false; // we do not need auth for this class
  106. }
  107. /**
  108. * Generate map of public class attributes
  109. *
  110. * @param string $typename type name
  111. * @param DOMElement $typexml target XML element
  112. * @return void
  113. */
  114. protected function _addClassAttributes($typename, DOMElement $typexml)
  115. {
  116. // Do not try to autoload here because _phpTypeToAS should
  117. // have already attempted to load this class
  118. if (!class_exists($typename, false)) {
  119. return;
  120. }
  121. $rc = new Zend_Reflection_Class($typename);
  122. foreach ($rc->getProperties() as $prop) {
  123. if (!$prop->isPublic()) {
  124. continue;
  125. }
  126. $propxml = $this->_xml->createElement('property');
  127. $propxml->setAttribute('name', $prop->getName());
  128. $type = $this->_registerType($this->_getPropertyType($prop));
  129. $propxml->setAttribute('type', $type);
  130. $typexml->appendChild($propxml);
  131. }
  132. }
  133. /**
  134. * Build XML service description from reflection class
  135. *
  136. * @param Zend_Server_Reflection_Class $refclass
  137. * @param DOMElement $target target XML element
  138. * @return void
  139. */
  140. protected function _addService(Zend_Server_Reflection_Class $refclass, DOMElement $target)
  141. {
  142. foreach ($refclass->getMethods() as $method) {
  143. if (!$method->isPublic()
  144. || $method->isConstructor()
  145. || ('__' == substr($method->name, 0, 2))
  146. ) {
  147. continue;
  148. }
  149. foreach ($method->getPrototypes() as $proto) {
  150. $op = $this->_xml->createElement('operation');
  151. $op->setAttribute('name', $method->getName());
  152. $rettype = $this->_registerType($proto->getReturnType());
  153. $op->setAttribute('returnType', $rettype);
  154. foreach ($proto->getParameters() as $param) {
  155. $arg = $this->_xml->createElement('argument');
  156. $arg->setAttribute('name', $param->getName());
  157. $type = $param->getType();
  158. if ($type == 'mixed' && ($pclass = $param->getClass())) {
  159. $type = $pclass->getName();
  160. }
  161. $ptype = $this->_registerType($type);
  162. $arg->setAttribute('type', $ptype);
  163. $op->appendChild($arg);
  164. }
  165. $target->appendChild($op);
  166. }
  167. }
  168. }
  169. /**
  170. * Extract type of the property from DocBlock
  171. *
  172. * @param Zend_Reflection_Property $prop reflection property object
  173. * @return string Property type
  174. */
  175. protected function _getPropertyType(Zend_Reflection_Property $prop)
  176. {
  177. $docBlock = $prop->getDocComment();
  178. if (!$docBlock) {
  179. return 'Unknown';
  180. }
  181. if (!$docBlock->hasTag('var')) {
  182. return 'Unknown';
  183. }
  184. $tag = $docBlock->getTag('var');
  185. return trim($tag->getDescription());
  186. }
  187. /**
  188. * Get the array of service directories
  189. *
  190. * @return array Service class directories
  191. */
  192. protected function _getServicePath()
  193. {
  194. if (isset($this->_options['server'])) {
  195. return $this->_options['server']->getDirectory();
  196. }
  197. if (isset($this->_options['directories'])) {
  198. return $this->_options['directories'];
  199. }
  200. return array();
  201. }
  202. /**
  203. * Map from PHP type name to AS type name
  204. *
  205. * @param string $typename PHP type name
  206. * @return string AS type name
  207. */
  208. protected function _phpTypeToAS($typename)
  209. {
  210. if (class_exists($typename)) {
  211. $vars = get_class_vars($typename);
  212. if (isset($vars['_explicitType'])) {
  213. return $vars['_explicitType'];
  214. }
  215. }
  216. if (false !== ($asname = Zend_Amf_Parse_TypeLoader::getMappedClassName($typename))) {
  217. return $asname;
  218. }
  219. return $typename;
  220. }
  221. /**
  222. * Register new type on the system
  223. *
  224. * @param string $typename type name
  225. * @return string New type name
  226. */
  227. protected function _registerType($typename)
  228. {
  229. // Known type - return its AS name
  230. if (isset($this->_typesMap[$typename])) {
  231. return $this->_typesMap[$typename];
  232. }
  233. // Standard types
  234. if (in_array($typename, array('void', 'null', 'mixed', 'unknown_type'))) {
  235. return 'Unknown';
  236. }
  237. if (in_array($typename, array('int', 'integer', 'bool', 'boolean', 'float', 'string', 'object', 'Unknown', 'stdClass', 'array'))) {
  238. return $typename;
  239. }
  240. // Resolve and store AS name
  241. $asTypeName = $this->_phpTypeToAS($typename);
  242. $this->_typesMap[$typename] = $asTypeName;
  243. // Create element for the name
  244. $typeEl = $this->_xml->createElement('type');
  245. $typeEl->setAttribute('name', $asTypeName);
  246. $this->_addClassAttributes($typename, $typeEl);
  247. $this->_types->appendChild($typeEl);
  248. return $asTypeName;
  249. }
  250. /**
  251. * Return error with error message
  252. *
  253. * @param string $msg Error message
  254. * @return string
  255. */
  256. protected function _returnError($msg)
  257. {
  258. return 'ERROR: $msg';
  259. }
  260. }