AmfphpVoConverter.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. /**
  3. * This file is part of amfPHP
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the license that is bundled
  8. * with this package in the file license.txt.
  9. */
  10. /**
  11. * Converts data from incoming packets with explicit types to Value Objects(Vos), and vice versa for the outgoing packets.
  12. *
  13. * This plugin can be deactivated if the project doesn't use Value Objects.
  14. *
  15. * The AMF deserializer reads a typed AMF object as a stdObj class, and sets the AMF type to a reserved "explicit type" field.
  16. * This plugin will look at deserialized data and try to convert any such objects to a real Value Object.
  17. *
  18. * It works in the opposite way on the way out: The AMF serializer needs a stdObj class with the explicit type marker set
  19. * to write a typed AMF object. This plugin will convert any typed PHP objects to a stdObj with the explicit type marker set.
  20. *
  21. * The explicit type marker is defined in Amfphp_Core_Amf_Constants
  22. *
  23. * If after deserialization the Value Object is not found, the object is unmodified and the explicit type marker is left set.
  24. * If the explicit type marker is already set in an outgoing object, the value is left as is.
  25. *
  26. *
  27. * This works for nested objects.
  28. *
  29. *
  30. * If you don't need strong typing in PHP but would like the objects in your client to be strongly typed, you can:
  31. * For example a stdObj like this will be returned in AMF as MyVO
  32. * <code>
  33. * $returnObj = new stdObj();
  34. * $explicitTypeField = Amfphp_Core_Amf_Constants::FIELD_EXPLICIT_TYPE;
  35. * $returnObj->$explicitTypeField = "MyVO";
  36. * </code>
  37. *
  38. * If you are using Flash, remember that you need to register the class alias so that Flash converts the MyVO AMF object to a Flash MyVO object.
  39. * If you are using Flex you can do this with the RemoteClass metadata tag.
  40. *
  41. * @see Amfphp_Core_Amf_Constants::FIELD_EXPLICIT_TYPE
  42. * @link http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/package.html#registerClassAlias%28%29
  43. * @link http://livedocs.adobe.com/flex/3/html/metadata_3.html#198729
  44. * @package Amfphp_Plugins_VoConverter
  45. * @author Ariel Sommeria-Klein
  46. */
  47. class AmfphpVoConverter implements Amfphp_Core_Common_IVoConverter {
  48. /**
  49. * paths to folders containing Value Objects(relative or absolute)
  50. * default is /Services/Vo/
  51. * if you need a namespace, use an array instead. First value is the path, second is the root namespace.
  52. * for example: array(AMFPHP_ROOTPATH . '/Services/Vo/', 'namespace');
  53. * @var array of folders
  54. */
  55. public $voFolders;
  56. /**
  57. * Set this to true if you want an exception to be thrown when a Value Object is not found.
  58. * Avoid setting this to true on a public server as the exception contains details about your server configuration.
  59. *
  60. * @var boolean
  61. */
  62. public $enforceConversion;
  63. /**
  64. * should objects be scanned or converted.
  65. * @see setScanEnabled
  66. * default true
  67. * @var boolean
  68. */
  69. protected $scanEnabled = true;
  70. /**
  71. * constructor.
  72. * @param array $config optional key/value pairs in an associative array. Used to override default configuration values.
  73. */
  74. public function __construct(array $config = null) {
  75. //default
  76. $this->voFolders = array(AMFPHP_ROOTPATH . '/Services/Vo/');
  77. if ($config) {
  78. if (isset($config['voFolders'])) {
  79. $this->voFolders = $config['voFolders'];
  80. }
  81. if (isset($config['enforceConversion'])) {
  82. $this->enforceConversion = $config['enforceConversion'];
  83. }
  84. }
  85. $filterManager = Amfphp_Core_FilterManager::getInstance();
  86. $filterManager->addFilter(Amfphp_Core_Gateway::FILTER_VO_CONVERTER, $this, 'filterVoConverter');
  87. $filterManager->addFilter(Amfphp_Core_Gateway::FILTER_DESERIALIZED_REQUEST, $this, 'filterDeserializedRequest');
  88. $filterManager->addFilter(Amfphp_Core_Gateway::FILTER_DESERIALIZED_RESPONSE, $this, 'filterDeserializedResponse');
  89. }
  90. /**
  91. * provides this as a default vo converter
  92. * @return \AmfphpVoConverter
  93. */
  94. public function filterVoConverter(){
  95. return $this;
  96. }
  97. /**
  98. * converts untyped objects to their typed counterparts. Loads the class if necessary
  99. * @param mixed $deserializedRequest
  100. * @return mixed
  101. */
  102. public function filterDeserializedRequest($deserializedRequest) {
  103. $ret = null;
  104. if($this->scanEnabled){
  105. $ret = Amfphp_Core_Amf_Util::applyFunctionToContainedObjects($deserializedRequest, array($this, 'convertToTyped'));
  106. }
  107. if(class_exists('AmfphpMonitor', false)){
  108. AmfphpMonitor::addTime('Request VO Conversion');
  109. }
  110. return $ret;
  111. }
  112. /**
  113. * looks at the outgoing packet and sets the explicit type field so that the serializer sends it properly
  114. * @param mixed $deserializedResponse
  115. * @return mixed
  116. */
  117. public function filterDeserializedResponse($deserializedResponse) {
  118. $ret = null;
  119. if($this->scanEnabled){
  120. $ret = Amfphp_Core_Amf_Util::applyFunctionToContainedObjects($deserializedResponse, array($this, 'markExplicitType'));
  121. }
  122. if(class_exists('AmfphpMonitor', false)){
  123. AmfphpMonitor::addTime('Response VO Conversion');
  124. }
  125. return $ret;
  126. }
  127. /**
  128. * for some protocols it is possible to call convertToType and markExplicitObject directly during deserialization and serialization.
  129. * This is typically the case of AMF, but not JSON.
  130. * In that case this function must be called with enabled set to false, so the plugin does not scan the objects to do it itself.
  131. * By default scanning is enabled
  132. * @param boolean $enabled
  133. */
  134. public function setScanEnabled($enabled){
  135. $this->scanEnabled = $enabled;
  136. }
  137. /**
  138. * get scan enabled.
  139. * @return boolean
  140. */
  141. public function getScanEnabled(){
  142. return $this->scanEnabled;
  143. }
  144. /**
  145. * if the object contains an explicit type marker, this method attempts to convert it to its typed counterpart
  146. * If then the class is still not available, the object is not converted
  147. * note: This is not a recursive function. Rather the recusrion is handled by Amfphp_Core_Amf_Util::applyFunctionToContainedObjects.
  148. * must be public so that Amfphp_Core_Amf_Util::applyFunctionToContainedObjects can call it
  149. * @param mixed $obj
  150. * @return mixed
  151. */
  152. public function convertToTyped($obj) {
  153. if (!is_object($obj)) {
  154. return $obj;
  155. }
  156. $explicitTypeField = Amfphp_Core_Amf_Constants::FIELD_EXPLICIT_TYPE;
  157. if (isset($obj->$explicitTypeField)) {
  158. $voName = $obj->$explicitTypeField;
  159. $typedObj = $this->getNewVoInstance($voName);
  160. if($typedObj){
  161. foreach ($obj as $key => $data) { // loop over each element to copy it into typed object
  162. if ($key != $explicitTypeField) {
  163. $typedObj->$key = $data;
  164. }
  165. }
  166. return $typedObj;
  167. }
  168. }
  169. return $obj;
  170. }
  171. /**
  172. * creates and returns an instance of of $voName.
  173. * if the Vo class is already available, then simply creates a new instance of it. If not,
  174. * attempts to load the file from the available service folders.
  175. * If all fails, there is the option to throw an error.
  176. *
  177. * @param type $voName
  178. * @return typed object or null
  179. */
  180. public function getNewVoInstance($voName){
  181. $fullyQualifiedClassName = $voName;
  182. if (!class_exists($voName, false)) {
  183. foreach ($this->voFolders as $folder) {
  184. $folderPath = NULL;
  185. $rootNamespace = NULL;
  186. if(is_array($folder)){
  187. $rootNamespace = $folder[1];
  188. $folderPath = $folder[0];
  189. }else{
  190. $folderPath = $folder;
  191. }
  192. $voNameWithSlashes = str_replace('.', '/', $voName);
  193. $voPath = $folderPath . $voNameWithSlashes . '.php';
  194. if (file_exists($voPath)) {
  195. require_once $voPath;
  196. if($rootNamespace != NULL){
  197. $fullyQualifiedClassName = $rootNamespace . '\\' . str_replace('/', '\\', $voNameWithSlashes);
  198. }
  199. break;
  200. }
  201. }
  202. }
  203. if (class_exists($fullyQualifiedClassName, false)) {
  204. //class is available. Use it!
  205. $vo = new $fullyQualifiedClassName();
  206. return $vo;
  207. }else{
  208. if($this->enforceConversion){
  209. throw new Amfphp_Core_Exception("\"$voName\" Vo not found. \nCustom Class folder paths : " . print_r($this->voFolders, true));
  210. }else{
  211. $ret = new stdClass();
  212. $explicitTypeField = Amfphp_Core_Amf_Constants::FIELD_EXPLICIT_TYPE;
  213. $ret->$explicitTypeField = $voName;
  214. return $ret;
  215. }
  216. }
  217. }
  218. /**
  219. * sets the the explicit type marker on the object. This is only done if it not already set, as in some cases
  220. * the service class might want to do this manually.
  221. * note: This is not a recursive function. Rather the recusrion is handled by Amfphp_Core_Amf_Util::applyFunctionToContainedObjects.
  222. * must be public so that Amfphp_Core_Amf_Util::applyFunctionToContainedObjects can call it
  223. *
  224. * @param mixed $obj
  225. * @return mixed
  226. */
  227. public function markExplicitType($obj) {
  228. if (!is_object($obj)) {
  229. return $obj;
  230. }
  231. $explicitTypeField = Amfphp_Core_Amf_Constants::FIELD_EXPLICIT_TYPE;
  232. $className = get_class($obj);
  233. if ($className != 'stdClass' && !isset($obj->$explicitTypeField)) {
  234. $obj->$explicitTypeField = $className;
  235. }
  236. return $obj;
  237. }
  238. }
  239. ?>