pb_message.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. /**
  3. * Including of all files needed to parse messages
  4. * @author Nikolai Kordulla
  5. */
  6. require_once(dirname(__FILE__). '/' . 'encoding/pb_base128.php');
  7. require_once(dirname(__FILE__). '/' . 'type/pb_scalar.php');
  8. require_once(dirname(__FILE__). '/' . 'type/pb_enum.php');
  9. require_once(dirname(__FILE__). '/' . 'type/pb_bytes.php');
  10. require_once(dirname(__FILE__). '/' . 'type/pb_string.php');
  11. require_once(dirname(__FILE__). '/' . 'type/pb_int.php');
  12. require_once(dirname(__FILE__). '/' . 'type/pb_bool.php');
  13. require_once(dirname(__FILE__). '/' . 'type/pb_signed_int.php');
  14. require_once(dirname(__FILE__). '/' . 'reader/pb_input_reader.php');
  15. require_once(dirname(__FILE__). '/' . 'reader/pb_input_string_reader.php');
  16. /**
  17. * Abstract Message class
  18. * @author Nikolai Kordulla
  19. */
  20. abstract class PBMessage
  21. {
  22. const WIRED_VARINT = 0;
  23. const WIRED_64BIT = 1;
  24. const WIRED_LENGTH_DELIMITED = 2;
  25. const WIRED_START_GROUP = 3;
  26. const WIRED_END_GROUP = 4;
  27. const WIRED_32BIT = 5;
  28. var $base128;
  29. // here are the field types
  30. var $fields = array();
  31. // the values for the fields
  32. var $values = array();
  33. // type of the class
  34. var $wired_type = 2;
  35. // the value of a class
  36. var $value = null;
  37. // modus byte or string parse (byte for productive string for better reading and debuging)
  38. // 1 = byte, 2 = String
  39. const MODUS = 1;
  40. // now use pointer for speed improvement
  41. // pointer to begin
  42. protected $reader;
  43. // chunk which the class not understands
  44. var $chunk = '';
  45. // variable for Send method
  46. var $_d_string = '';
  47. /**
  48. * Constructor - initialize base128 class
  49. */
  50. public function __construct($reader=null)
  51. {
  52. $this->reader = $reader;
  53. $this->value = $this;
  54. $this->base128 = new base128varint(PBMessage::MODUS);
  55. }
  56. /**
  57. * Get the wired_type and field_type
  58. * @param $number as decimal
  59. * @return array wired_type, field_type
  60. */
  61. public function get_types($number)
  62. {
  63. $binstring = decbin($number);
  64. $types = array();
  65. $low = substr($binstring, strlen($binstring) - 3, strlen($binstring));
  66. $high = substr($binstring,0, strlen($binstring) - 3) . '0000';
  67. $types['wired'] = bindec($low);
  68. $types['field'] = bindec($binstring) >> 3;
  69. return $types;
  70. }
  71. /**
  72. * Encodes a Message
  73. * @return string the encoded message
  74. */
  75. public function SerializeToString($rec=-1)
  76. {
  77. $string = '';
  78. // wired and type
  79. if ($rec > -1)
  80. {
  81. $string .= $this->base128->set_value($rec << 3 | $this->wired_type);
  82. }
  83. $stringinner = '';
  84. foreach ($this->fields as $index => $field)
  85. {
  86. if (is_array($this->values[$index]) && count($this->values[$index]) > 0)
  87. {
  88. // make serialization for every array
  89. foreach ($this->values[$index] as $array)
  90. {
  91. $newstring = '';
  92. $newstring .= $array->SerializeToString($index);
  93. $stringinner .= $newstring;
  94. }
  95. }
  96. else if ($this->values[$index] != null)
  97. {
  98. // wired and type
  99. $newstring = '';
  100. $newstring .= $this->values[$index]->SerializeToString($index);
  101. $stringinner .= $newstring;
  102. }
  103. }
  104. $this->_serialize_chunk($stringinner);
  105. if ($this->wired_type == PBMessage::WIRED_LENGTH_DELIMITED && $rec > -1)
  106. {
  107. $stringinner = $this->base128->set_value(strlen($stringinner) / PBMessage::MODUS) . $stringinner;
  108. }
  109. return $string . $stringinner;
  110. }
  111. /**
  112. * Serializes the chunk
  113. * @param String $stringinner - String where to append the chunk
  114. */
  115. public function _serialize_chunk(&$stringinner)
  116. {
  117. $stringinner .= $this->chunk;
  118. }
  119. /**
  120. * Decodes a Message and Built its things
  121. *
  122. * @param message as stream of hex example '1a 03 08 96 01'
  123. */
  124. public function ParseFromString($message)
  125. {
  126. $this->reader = new PBInputStringReader($message);
  127. $this->_ParseFromArray();
  128. }
  129. /**
  130. * Internal function
  131. */
  132. public function ParseFromArray()
  133. {
  134. $this->chunk = '';
  135. // read the length byte
  136. $length = $this->reader->next();
  137. // just take the splice from this array
  138. $this->_ParseFromArray($length);
  139. }
  140. /**
  141. * Internal function
  142. */
  143. private function _ParseFromArray($length=99999999)
  144. {
  145. $_begin = $this->reader->get_pointer();
  146. while ($this->reader->get_pointer() - $_begin < $length)
  147. {
  148. $next = $this->reader->next();
  149. if ($next === false)
  150. break;
  151. // now get the message type
  152. $messtypes = $this->get_types($next);
  153. // now make method test
  154. if (!isset($this->fields[$messtypes['field']]))
  155. {
  156. // field is unknown so just ignore it
  157. // throw new Exception('Field ' . $messtypes['field'] . ' not present ');
  158. if ($messtypes['wired'] == PBMessage::WIRED_LENGTH_DELIMITED)
  159. {
  160. $consume = new PBString($this->reader);
  161. }
  162. else if ($messtypes['wired'] == PBMessage::WIRED_VARINT)
  163. {
  164. $consume = new PBInt($this->reader);
  165. }
  166. else
  167. {
  168. throw new Exception('I dont understand this wired code:' . $messtypes['wired']);
  169. }
  170. // perhaps send a warning out
  171. // @TODO SEND CHUNK WARNING
  172. $_oldpointer = $this->reader->get_pointer();
  173. $consume->ParseFromArray();
  174. // now add array from _oldpointer to pointer to the chunk array
  175. $this->chunk .= $this->reader->get_message_from($_oldpointer);
  176. continue;
  177. }
  178. // now array or not
  179. if (is_array($this->values[$messtypes['field']]))
  180. {
  181. $this->values[$messtypes['field']][] = new $this->fields[$messtypes['field']]($this->reader);
  182. $index = count($this->values[$messtypes['field']]) - 1;
  183. if ($messtypes['wired'] != $this->values[$messtypes['field']][$index]->wired_type)
  184. {
  185. throw new Exception('Expected type:' . $messtypes['wired'] . ' but had ' . $this->fields[$messtypes['field']]->wired_type);
  186. }
  187. $this->values[$messtypes['field']][$index]->ParseFromArray();
  188. }
  189. else
  190. {
  191. $this->values[$messtypes['field']] = new $this->fields[$messtypes['field']]($this->reader);
  192. if ($messtypes['wired'] != $this->values[$messtypes['field']]->wired_type)
  193. {
  194. throw new Exception('Expected type:' . $messtypes['wired'] . ' but had ' . $this->fields[$messtypes['field']]->wired_type);
  195. }
  196. $this->values[$messtypes['field']]->ParseFromArray();
  197. }
  198. }
  199. }
  200. /**
  201. * Add an array value
  202. * @param int - index of the field
  203. */
  204. protected function _add_arr_value($index)
  205. {
  206. return $this->values[$index][] = new $this->fields[$index]();
  207. }
  208. /**
  209. * Set an array value - @TODO failure check
  210. * @param int - index of the field
  211. * @param int - index of the array
  212. * @param object - the value
  213. */
  214. protected function _set_arr_value($index, $index_arr, $value)
  215. {
  216. $this->values[$index][$index_arr] = $value;
  217. }
  218. /**
  219. * Remove the last array value
  220. * @param int - index of the field
  221. */
  222. protected function _remove_last_arr_value($index)
  223. {
  224. array_pop($this->values[$index]);
  225. }
  226. /**
  227. * Set an value
  228. * @param int - index of the field
  229. * @param Mixed value
  230. */
  231. protected function _set_value($index, $value)
  232. {
  233. if (gettype($value) == 'object')
  234. {
  235. $this->values[$index] = $value;
  236. }
  237. else
  238. {
  239. $this->values[$index] = new $this->fields[$index]();
  240. $this->values[$index]->value = $value;
  241. }
  242. }
  243. /**
  244. * Get a value
  245. * @param id of the field
  246. */
  247. protected function _get_value($index)
  248. {
  249. if ($this->values[$index] == null)
  250. return null;
  251. return $this->values[$index]->value;
  252. }
  253. /**
  254. * Get array value
  255. * @param id of the field
  256. * @param value
  257. */
  258. protected function _get_arr_value($index, $value)
  259. {
  260. return $this->values[$index][$value];
  261. }
  262. /**
  263. * Get array size
  264. * @param id of the field
  265. */
  266. protected function _get_arr_size($index)
  267. {
  268. return count($this->values[$index]);
  269. }
  270. /**
  271. * Helper method for send string
  272. */
  273. protected function _save_string($ch, $string)
  274. {
  275. $this->_d_string .= $string;
  276. $content_length = strlen($this->_d_string);
  277. return strlen($string);
  278. }
  279. /**
  280. * Sends the message via post request ['message'] to the url
  281. * @param the url
  282. * @param the PBMessage class where the request should be encoded
  283. *
  284. * @return String - the return string from the request to the url
  285. */
  286. public function Send($url, &$class = null)
  287. {
  288. $ch = curl_init();
  289. $this->_d_string = '';
  290. curl_setopt($ch, CURLOPT_URL, $url);
  291. curl_setopt($ch, CURLOPT_POST, true);
  292. curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, '_save_string'));
  293. curl_setopt($ch, CURLOPT_POSTFIELDS, 'message=' . urlencode($this->SerializeToString()));
  294. $result = curl_exec($ch);
  295. if ($class != null)
  296. $class->parseFromString($this->_d_string);
  297. return $this->_d_string;
  298. }
  299. /**
  300. * Fix Memory Leaks with Objects in PHP 5
  301. * http://paul-m-jones.com/?p=262
  302. *
  303. * thanks to cheton
  304. * http://code.google.com/p/pb4php/issues/detail?id=3&can=1
  305. */
  306. public function _destruct()
  307. {
  308. if (isset($this->reader))
  309. {
  310. unset($this->reader);
  311. }
  312. if (isset($this->value))
  313. {
  314. unset($this->value);
  315. }
  316. // base128
  317. if (isset($this->base128))
  318. {
  319. unset($this->base128);
  320. }
  321. // fields
  322. if (isset($this->fields))
  323. {
  324. foreach ($this->fields as $name => $value)
  325. {
  326. unset($this->$name);
  327. }
  328. unset($this->fields);
  329. }
  330. // values
  331. if (isset($this->values))
  332. {
  333. foreach ($this->values as $name => $value)
  334. {
  335. if (is_array($value))
  336. {
  337. foreach ($value as $name2 => $value2)
  338. {
  339. if (is_object($value2) AND method_exists($value2, '__destruct'))
  340. {
  341. $value2->__destruct();
  342. }
  343. unset($value2);
  344. }
  345. if (isset($name2))
  346. unset($value->$name2);
  347. }
  348. else
  349. {
  350. if (is_object($value) AND method_exists($value, '__destruct'))
  351. {
  352. $value->__destruct();
  353. }
  354. unset($value);
  355. }
  356. unset($this->values->$name);
  357. }
  358. unset($this->values);
  359. }
  360. }
  361. }
  362. ?>