PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/didactilab/gwt/phprpc/phpderpc/phprpc/derpc/SimplePayloadDecoder.php

http://gwtphp-derpc.googlecode.com/
PHP | 388 lines | 328 code | 30 blank | 30 comment | 34 complexity | 0a92a997e3a1205426d4147949bf44f3 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright 2011 DidactiLab SAS
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  6. * use this file except in compliance with the License. You may obtain a copy of
  7. * the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. *
  17. * Date: 30 avr. 2011
  18. * Author: Mathieu LIGOCKI
  19. */
  20. require_once PHPRPC_ROOT . 'derpc/ast.php';
  21. require_once PHPRPC_ROOT . 'classes.php';
  22. require_once PHPRPC_ROOT . 'derpc/SimplePayloadSink.php';
  23. require_once PHPRPC_ROOT . 'derpc/ClientOracle.php';
  24. class SimplePayloadDecoder {
  25. const OBFUSCATED_CLASS_REFIX = 'Class$ ';
  26. private static $PRIMITIVE_TYPES = array();
  27. public static function init() {
  28. // Obfuscated
  29. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . BOOLEAN_TYPE] = Boolean::typeClass();
  30. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . BYTE_TYPE] = Byte::typeClass();
  31. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . CHAR_TYPE] = Character::typeClass();
  32. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . DOUBLE_TYPE] = Double::typeClass();
  33. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . FLOAT_TYPE] = Float::typeClass();
  34. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . INT_TYPE] = Integer::typeClass();
  35. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . LONG_TYPE] = Long::typeClass();
  36. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . SHORT_TYPE] = Short::typeClass();
  37. self::$PRIMITIVE_TYPES[self::OBFUSCATED_CLASS_REFIX . VOID_TYPE] = Void::typeClass();
  38. // Regular
  39. self::$PRIMITIVE_TYPES[Boolean::typeClass()->getName()] = Boolean::typeClass();
  40. self::$PRIMITIVE_TYPES[Byte::typeClass()->getName()] = Byte::typeClass();
  41. self::$PRIMITIVE_TYPES[Character::typeClass()->getName()] = Character::typeClass();
  42. self::$PRIMITIVE_TYPES[Double::typeClass()->getName()] = Double::typeClass();
  43. self::$PRIMITIVE_TYPES[Float::typeClass()->getName()] = Float::typeClass();
  44. self::$PRIMITIVE_TYPES[Integer::typeClass()->getName()] = Integer::typeClass();
  45. self::$PRIMITIVE_TYPES[Long::typeClass()->getName()] = Long::typeClass();
  46. self::$PRIMITIVE_TYPES[Short::typeClass()->getName()] = Short::typeClass();
  47. self::$PRIMITIVE_TYPES[Void::typeClass()->getName()] = Void::typeClass();
  48. }
  49. private $backRefs = array();
  50. private $classCache = array();
  51. private $clientOracle;
  52. private $commands = array();
  53. private $idx;
  54. private $payload;
  55. private $toReturn;
  56. private $toThrow;
  57. public function __construct(ClientOracle $clientOracle, $payload) {
  58. $this->clientOracle = $clientOracle;
  59. $this->payload = $payload;
  60. while ($this->toReturn == null && $this->idx < mb_strlen($payload)) {
  61. $this->decodeCommand();
  62. if ($this->toThrow != null) {
  63. return;
  64. }
  65. }
  66. }
  67. public function getThrownValue() {
  68. return $this->toThrow;
  69. }
  70. public function getValues() {
  71. return $this->toReturn == null ? array() : $this->toReturn->getValues();
  72. }
  73. private function decodeCommand() {
  74. $command = $this->next();
  75. if ($command == NL_CHAR) {
  76. $command = $this->next();
  77. }
  78. $token = $this->token();
  79. switch ($command) {
  80. case BOOLEAN_TYPE: {
  81. $this->pushScalar(new BooleanValueCommand($token == '1'));
  82. break;
  83. }
  84. case BYTE_TYPE: {
  85. $this->pushScalar(new ByteValueCommand(Byte::valueOf($token)));
  86. break;
  87. }
  88. case CHAR_TYPE: {
  89. $this->pushScalar(new CharValueCommand(Character::chr(intval($token))));
  90. break;
  91. }
  92. case DOUBLE_TYPE: {
  93. $this->pushScalar(new DoubleValueCommand(Double::valueOf($token)));
  94. break;
  95. }
  96. case FLOAT_TYPE: {
  97. $this->pushScalar(new FloatValueCommand(Float::valueOf($token)));
  98. break;
  99. }
  100. case INT_TYPE: {
  101. $this->pushScalar(new IntValueCommand(Integer::valueOf($token)));
  102. break;
  103. }
  104. case LONG_TYPE: {
  105. $this->pushScalar(new LongValueCommand(Long::valueOf($token)));
  106. break;
  107. }
  108. case VOID_TYPE: {
  109. $this->pushScalar(NullValueCommand::INSTANCE());
  110. break;
  111. }
  112. case SHORT_TYPE: {
  113. $this->pushScalar(new ShortValueCommand(Short::valueOf($token)));
  114. break;
  115. }
  116. case STRING_TYPE: {
  117. // "4~abcd
  118. $length = Integer::valueOf($token);
  119. $value = $this->nextCount($length);
  120. if ($this->next() != RPC_SEPARATOR_CHAR) {
  121. throw new RuntimeException('Overran string');
  122. }
  123. $this->pushString(new StringValueCommand($value));
  124. break;
  125. }
  126. case ENUM_TYPE: {
  127. // ETypeSeedName~IOrdinal~
  128. $ordinal = $this->readCommand('IntValueCommand')->getValue();
  129. $clazz = $this->findClass($token);
  130. $x = new EnumValueCommand($clazz);
  131. $this->pushIdentity($x);
  132. $x->setValue($ordinal);
  133. break;
  134. }
  135. case ARRAY_TYPE: {
  136. // Encoded as (leafType, dimensions, length, ...)
  137. $leaf = $this->findClass($token);
  138. $numDims = $this->readCommand('IntValueCommand')->getValue();
  139. $clazz = null;
  140. if ($numDims > 1) {
  141. $clazz = ArrayType::clazz($leaf, $numDims);
  142. }
  143. else {
  144. $clazz = $leaf;
  145. }
  146. $x = new ArrayValueCommand($clazz);
  147. $this->pushIdentity($x);
  148. $length = $this->readCommand('IntValueCommand')->getValue();
  149. for ($i = 0; $i < $length; $i++) {
  150. $x->add($this->readCommand('ValueCommand'));
  151. }
  152. break;
  153. }
  154. case OBJECT_TYPE: {
  155. // LTypeSeedName~3... N-many setters ...
  156. $clazz = $this->findClass($token);
  157. $x = new InstantiateCommand($clazz);
  158. $this->pushIdentity($x);
  159. $this->readSetters($clazz, $x);
  160. break;
  161. }
  162. case INVOKE_TYPE: {
  163. // !TypeSeedName~Number of objects written by CFS~...CFS objects...~
  164. // Number of extra fields~...N-many setters...
  165. $clazz = $this->findClass($token);
  166. $serializerClass = null;
  167. $manualType = $clazz;
  168. while ($manualType != null) {
  169. $serializerClass = SerializabilityUtil::hasCustomFieldSerializer($manualType);
  170. if ($serializerClass != null) {
  171. break;
  172. }
  173. $manualType = $manualType->getSuperClass();
  174. }
  175. if ($serializerClass == null) {
  176. throw new IncompatibleRemoteServiceException('Class [' . $clazz->getName() . '] has no custom serializer on server');
  177. }
  178. $x = new InvokeCustomFieldSerializerCommand($clazz, $serializerClass, $manualType);
  179. $this->pushIdentity($x);
  180. $this->readFields($x);
  181. $this->readSetters($clazz, $x);
  182. break;
  183. }
  184. case RETURN_TYPE: {
  185. // R4~...values...
  186. $this->toReturn = new ReturnCommand();
  187. $toRead = Integer::valueOf($token);
  188. for ($i=0; $i<$toRead; $i++) {
  189. $this->toReturn->addValue($this->readCommand('ValueCommand'));
  190. }
  191. break;
  192. }
  193. case THROW_TYPE: {
  194. // T...value...
  195. $this->toThrow = $this->readCommand('ValueCommand');
  196. break;
  197. }
  198. case BACKREF_TYPE: {
  199. // @backrefNumber~
  200. $x = $this->backRefs[Integer::valueOf($token)];
  201. assert($x != null);
  202. array_push($this->commands, $x);
  203. break;
  204. }
  205. case RPC_SEPARATOR_CHAR: {
  206. throw new RuntimeException('Segmentation overrun at ' + $this->idx);
  207. }
  208. default: {
  209. throw new RuntimeException('Unknown Command ' + $command);
  210. }
  211. }
  212. }
  213. private function findClass($token) {
  214. if (isset($this->classCache[$token])) {
  215. return $this->classCache[$token];
  216. }
  217. $className = $this->clientOracle->getTypeName($token);
  218. if ($className == null) {
  219. $className = $token;
  220. }
  221. if (strstr($className, '[]') !== false) {
  222. $firstIndex = -1;
  223. $j = -1;
  224. $dims = 0;
  225. while (($j = strpos($className, '[')) !== false) {
  226. if ($dims++ == 0) {
  227. $firstIndex = $j;
  228. }
  229. }
  230. $componentType = $this->findClass(substr($className, 0, $firstIndex));
  231. assert($componentType != null);
  232. $clazz = ArrayType::clazz($componentType, $dims);
  233. }
  234. else {
  235. $clazz = Classes::classOf($className);
  236. }
  237. $this->classCache[$token] = $clazz;
  238. return $clazz;
  239. }
  240. private function next() {
  241. $c = mb_substr($this->payload, $this->idx++, 1);
  242. //$c = $this->payload[$this->idx++];
  243. if ($c == '\\') {
  244. switch (mb_substr($this->payload, $this->idx++, 1)) {
  245. //switch ($this->payload[$this->idx++]) {
  246. case '0':
  247. $c = '\0';
  248. break;
  249. case '!':
  250. $c = '|';
  251. break;
  252. case 'b':
  253. $c = '\b';
  254. break;
  255. case 't':
  256. $c = '\t';
  257. break;
  258. case 'n':
  259. $c = '\n';
  260. break;
  261. case 'f':
  262. $c = '\f';
  263. break;
  264. case 'r':
  265. $c = '\r';
  266. break;
  267. case '\\':
  268. $c = '\\';
  269. break;
  270. case '"':
  271. $c = '"';
  272. break;
  273. case 'u':
  274. $c = chr(hexdec(mb_substr($this->payload, $this->idx, 4)));
  275. $this->idx += 4;
  276. break;
  277. case 'x':
  278. $c = chr(hexdec(mb_substr($this->payload, $this->idx, 2)));
  279. $this->idx += 2;
  280. break;
  281. default:
  282. throw new RuntimeException('Unhandled escape ' . $this->payload[$this->idx]);
  283. }
  284. }
  285. return $c;
  286. }
  287. private function nextCount($count) {
  288. $buffer = '';
  289. while ($count-- > 0) {
  290. $buffer .= $this->next();
  291. }
  292. return $buffer;
  293. }
  294. private function pushIdentity(IdentityValueCommand $x) {
  295. array_push($this->commands, $x);
  296. $this->backRefs[count($this->backRefs)] = $x;
  297. }
  298. private function pushScalar(ScalarValueCommand $x) {
  299. array_push($this->commands, $x);
  300. }
  301. private function pushString(StringValueCommand $x) {
  302. array_push($this->commands, $x);
  303. $this->backRefs[count($this->backRefs)] = $x;
  304. }
  305. private function readCommand($className) {
  306. $this->decodeCommand();
  307. $value = array_pop($this->commands);
  308. assert($value instanceof $className);
  309. return $value;
  310. }
  311. private function readFields(InvokeCustomFieldSerializerCommand $x) {
  312. $length = $this->readCommand('IntValueCommand')->getValue();
  313. for ($i=0; $i<$length; $i++) {
  314. $x->addValue($this->readCommand('ValueCommand'));
  315. }
  316. }
  317. private function readSetter(Clazz $clazz, HasSetters $x) {
  318. if (!$this->clientOracle->isScript()) {
  319. $fieldDeclClassName = $this->readCommand('StringValueCommand')->getValue();
  320. if ($fieldDeclClassName != null) {
  321. $clazz = $this->findClass($fieldDeclClassName);
  322. }
  323. }
  324. $fieldId = $this->readCommand('StringValueCommand')->getValue();
  325. $data = $this->clientOracle->getFieldName($clazz, $fieldId);
  326. $value = $this->readCommand('ValueCommand');
  327. $x->set($data->class, $data->fieldName, $value);
  328. }
  329. private function readSetters(Clazz $clazz, HasSetters $x) {
  330. $length = $this->readCommand('IntValueCommand')->getValue();
  331. for ($i=0; $i<$length; $i++) {
  332. $this->readSetter($clazz, $x);
  333. }
  334. }
  335. private function token() {
  336. $buffer = '';
  337. $n = $this->next();
  338. while ($n != RPC_SEPARATOR_CHAR) {
  339. $buffer .= $n;
  340. $n = $this->next();
  341. }
  342. return $buffer;
  343. }
  344. }
  345. SimplePayloadDecoder::init();