Introspector.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /** @see Zend_Amf_Parse_TypeLoader */
  22. require_once 'Zend/Amf/Parse/TypeLoader.php';
  23. /** @see Zend_Reflection_Class */
  24. require_once 'Zend/Reflection/Class.php';
  25. /** @see 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-2012 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. if($param->isDefaultValueAvailable()) {
  164. $arg->setAttribute('defaultvalue', $param->getDefaultValue());
  165. }
  166. $op->appendChild($arg);
  167. }
  168. $target->appendChild($op);
  169. }
  170. }
  171. }
  172. /**
  173. * Extract type of the property from DocBlock
  174. *
  175. * @param Zend_Reflection_Property $prop reflection property object
  176. * @return string Property type
  177. */
  178. protected function _getPropertyType(Zend_Reflection_Property $prop)
  179. {
  180. $docBlock = $prop->getDocComment();
  181. if (!$docBlock) {
  182. return 'Unknown';
  183. }
  184. if (!$docBlock->hasTag('var')) {
  185. return 'Unknown';
  186. }
  187. $tag = $docBlock->getTag('var');
  188. return trim($tag->getDescription());
  189. }
  190. /**
  191. * Get the array of service directories
  192. *
  193. * @return array Service class directories
  194. */
  195. protected function _getServicePath()
  196. {
  197. if (isset($this->_options['server'])) {
  198. return $this->_options['server']->getDirectory();
  199. }
  200. if (isset($this->_options['directories'])) {
  201. return $this->_options['directories'];
  202. }
  203. return array();
  204. }
  205. /**
  206. * Map from PHP type name to AS type name
  207. *
  208. * @param string $typename PHP type name
  209. * @return string AS type name
  210. */
  211. protected function _phpTypeToAS($typename)
  212. {
  213. if (class_exists($typename)) {
  214. $vars = get_class_vars($typename);
  215. if (isset($vars['_explicitType'])) {
  216. return $vars['_explicitType'];
  217. }
  218. }
  219. if (false !== ($asname = Zend_Amf_Parse_TypeLoader::getMappedClassName($typename))) {
  220. return $asname;
  221. }
  222. return $typename;
  223. }
  224. /**
  225. * Register new type on the system
  226. *
  227. * @param string $typename type name
  228. * @return string New type name
  229. */
  230. protected function _registerType($typename)
  231. {
  232. // Known type - return its AS name
  233. if (isset($this->_typesMap[$typename])) {
  234. return $this->_typesMap[$typename];
  235. }
  236. // Standard types
  237. if (in_array($typename, array('void', 'null', 'mixed', 'unknown_type'))) {
  238. return 'Unknown';
  239. }
  240. // Arrays
  241. if ('array' == $typename) {
  242. return 'Unknown[]';
  243. }
  244. if (in_array($typename, array('int', 'integer', 'bool', 'boolean', 'float', 'string', 'object', 'Unknown', 'stdClass'))) {
  245. return $typename;
  246. }
  247. // Resolve and store AS name
  248. $asTypeName = $this->_phpTypeToAS($typename);
  249. $this->_typesMap[$typename] = $asTypeName;
  250. // Create element for the name
  251. $typeEl = $this->_xml->createElement('type');
  252. $typeEl->setAttribute('name', $asTypeName);
  253. $this->_addClassAttributes($typename, $typeEl);
  254. $this->_types->appendChild($typeEl);
  255. return $asTypeName;
  256. }
  257. /**
  258. * Return error with error message
  259. *
  260. * @param string $msg Error message
  261. * @return string
  262. */
  263. protected function _returnError($msg)
  264. {
  265. return 'ERROR: $msg';
  266. }
  267. }