PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/db/library/xmlstream.php

https://github.com/tjgillies/dbscript
PHP | 347 lines | 301 code | 22 blank | 24 comment | 58 complexity | 7eaee7fa96e2cebd398b4054da427772 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /*
  3. XMPPHP: The PHP XMPP Library
  4. Copyright (C) 2008 Nathanael C. Fritz
  5. This file is part of SleekXMPP.
  6. XMPPHP is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. XMPPHP is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with XMPPHP; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. require_once("xmlobj.php");
  19. require_once("logging.php");
  20. class XMLStream {
  21. var $socket;
  22. var $parser;
  23. var $buffer;
  24. var $xml_depth = 0;
  25. var $host;
  26. var $port;
  27. var $stream_start = '<stream>';
  28. var $stream_end = '</stream';
  29. var $disconnected = false;
  30. var $sent_disconnect = False;
  31. var $ns_map = array();
  32. var $current_ns = array();
  33. var $xmlobj = Null;
  34. var $nshandlers = array();
  35. var $idhandlers = array();
  36. var $eventhandlers = array();
  37. var $lastid = 0;
  38. var $default_ns;
  39. var $until = '';
  40. var $until_happened = False;
  41. var $until_payload = array();
  42. var $log;
  43. var $reconnect = True;
  44. var $been_reset = False;
  45. var $is_server;
  46. function XMLStream($host=Null, $port=Null, $log=False, $loglevel=Null, $is_server=False) {
  47. $this->reconnect = !$is_server;
  48. $this->is_server = $is_server;
  49. $this->host = $host;
  50. $this->port = $port;
  51. $this->setupParser();
  52. $this->log = new Logging($log, $loglevel);
  53. }
  54. function getId() {
  55. $this->lastid++;
  56. return $this->lastid;
  57. }
  58. function addIdHandler($id, $pointer, $obj=Null) {
  59. $this->idhandlers[$id] = array($pointer, $obj);
  60. }
  61. function addHandler($name, $ns, $pointer, $obj=Null, $depth=1) {
  62. $this->nshandlers[] = array($name,$ns,$pointer,$obj, $depth);
  63. }
  64. function addEventHandler($name, $pointer, $obj) {
  65. $this->eventhanders[] = array($name, $pointer, $obj);
  66. }
  67. function connect($persistent=False, $sendinit=True) {
  68. $this->disconnected = False;
  69. $this->sent_disconnect = False;
  70. if($persistent) {
  71. $conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
  72. } else {
  73. $conflag = STREAM_CLIENT_CONNECT;
  74. }
  75. $this->log->log("Connecting to tcp://{$this->host}:{$this->port}");
  76. $this->socket = stream_socket_client("tcp://{$this->host}:{$this->port}", $flags=$conflag);
  77. if(!$this->socket) {
  78. $this->log->log("Could not connect.", LOGGING_ERROR);
  79. $this->disconnected = True;
  80. }
  81. stream_set_blocking($this->socket, 1);
  82. if($sendinit) $this->send($this->stream_start);
  83. }
  84. function apply_socket($socket) {
  85. $this->socket = $socket;
  86. }
  87. function process() {
  88. $updated = '';
  89. while(!$this->disconnect) {
  90. $read = array($this->socket);
  91. $write = NULL;
  92. $except = NULL;
  93. $updated = stream_select($read, $write, $except, 1);
  94. if ($updated > 0) {
  95. $buff = @fread($this->socket, 1024);
  96. if(!$buff) {
  97. if($this->reconnect) {
  98. $this->doReconnect();
  99. } else {
  100. fclose($this->socket);
  101. return False;
  102. }
  103. }
  104. $this->log->log("RECV: $buff", LOGGING_VERBOSE);
  105. xml_parse($this->parser, $buff, False);
  106. }
  107. }
  108. }
  109. function read() {
  110. $buff = @fread($this->socket, 1024);
  111. if(!$buff) {
  112. if($this->reconnect) {
  113. $this->doReconnect();
  114. } else {
  115. fclose($this->socket);
  116. return False;
  117. }
  118. }
  119. $this->log->log("RECV: $buff", LOGGING_VERBOSE);
  120. xml_parse($this->parser, $buff, False);
  121. }
  122. function processTime($timeout=-1) {
  123. $start = time();
  124. $updated = '';
  125. while(!$this->disconnected and ($timeout == -1 or time() - $start < $timeout)) {
  126. $read = array($this->socket);
  127. $write = NULL;
  128. $except = NULL;
  129. $updated = stream_select($read, $write, $except, 1);
  130. if ($updated > 0) {
  131. $buff = @fread($this->socket, 1024);
  132. if(!$buff) {
  133. if($this->reconnect) {
  134. $this->doReconnect();
  135. } else {
  136. fclose($this->socket);
  137. return False;
  138. }
  139. }
  140. $this->log->log("RECV: $buff", LOGGING_VERBOSE);
  141. xml_parse($this->parser, $buff, False);
  142. }
  143. }
  144. }
  145. function processUntil($event, $timeout=-1) {
  146. $start = time();
  147. if(!is_array($event)) $event = array($event);
  148. $this->until[] = $event;
  149. end($this->until);
  150. $event_key = key($this->until);
  151. reset($this->until);
  152. $updated = '';
  153. while(!$this->disconnected and $this->until[$event_key] and (time() - $start < $timeout or $timeout == -1)) {
  154. $read = array($this->socket);
  155. $write = NULL;
  156. $except = NULL;
  157. $updated = stream_select($read, $write, $except, 1);
  158. if ($updated > 0) {
  159. $buff = @fread($this->socket, 1024);
  160. if(!$buff) {
  161. if($this->reconnect) {
  162. $this->doReconnect();
  163. } else {
  164. fclose($this->socket);
  165. return False;
  166. }
  167. }
  168. $this->log->log("RECV: $buff", LOGGING_VERBOSE);
  169. xml_parse($this->parser, $buff, False);
  170. }
  171. }
  172. if(array_key_exists($event_key, $this->until_payload)) {
  173. $payload = $this->until_payload[$event_key];
  174. } else {
  175. $payload = array();
  176. }
  177. unset($this->until_payload[$event_key]);
  178. return $payload;
  179. }
  180. function startXML($parser, $name, $attr) {
  181. if($this->been_reset) {
  182. $this->been_reset = False;
  183. $this->xml_depth = 0;
  184. }
  185. $this->xml_depth++;
  186. if(array_key_exists('XMLNS', $attr)) {
  187. $this->current_ns[$this->xml_depth] = $attr['XMLNS'];
  188. } else {
  189. $this->current_ns[$this->xml_depth] = $this->current_ns[$this->xml_depth - 1];
  190. if(!$this->current_ns[$this->xml_depth]) $this->current_ns[$this->xml_depth] = $this->default_ns;
  191. }
  192. $ns = $this->current_ns[$this->xml_depth];
  193. foreach($attr as $key => $value) {
  194. if(strstr($key, ":")) {
  195. $key = explode(':', $key);
  196. $key = $key[1];
  197. $this->ns_map[$key] = $value;
  198. }
  199. }
  200. if(!strstr($name, ":") === False)
  201. {
  202. $name = explode(':', $name);
  203. $ns = $this->ns_map[$name[0]];
  204. $name = $name[1];
  205. }
  206. $obj = new XMLObj($name, $ns, $attr);
  207. if($this->xml_depth > 1)
  208. $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj;
  209. $this->xmlobj[$this->xml_depth] = $obj;
  210. }
  211. function endXML($parser, $name) {
  212. #$this->log->log("Ending $name", LOGGING_DEBUG);
  213. #print "$name\n";
  214. if($this->been_reset) {
  215. $this->been_reset = False;
  216. $this->xml_depth = 0;
  217. }
  218. $this->xml_depth--;
  219. if($this->xml_depth == 1) {
  220. #clean-up old objects
  221. $found = False;
  222. foreach($this->nshandlers as $handler) {
  223. if($handler[4] != 1 and $this->xmlobj[2]->hassub($handler[0])) {
  224. $searchxml = $this->xmlobj[2]->sub($handler[0]);
  225. } elseif(is_array($this->xmlobj) and array_key_exists(2, $this->xmlobj)) {
  226. $searchxml = $this->xmlobj[2];
  227. }
  228. if($searchxml !== Null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) {
  229. if($handler[3] === Null) $handler[3] = $this;
  230. $this->log->log("Calling {$handler[2]}", LOGGING_DEBUG);
  231. call_user_method($handler[2], $handler[3], $this->xmlobj[2]);
  232. }
  233. }
  234. foreach($this->idhandlers as $id => $handler) {
  235. if(array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) {
  236. if($handler[1] === Null) $handler[1] = $this;
  237. call_user_method($handler[0], $handler[1], $this->xmlobj[2]);
  238. #id handlers are only used once
  239. unset($this->idhandlers[$id]);
  240. break;
  241. }
  242. }
  243. if(is_array($this->xmlobj)) {
  244. $this->xmlobj = array_slice($this->xmlobj, 0, 1);
  245. $this->xmlobj[0]->subs = Null;
  246. }
  247. unset($this->xmlobj[2]);
  248. }
  249. if($this->xml_depth == 0 and !$this->been_reset) {
  250. if(!$this->disconnected) {
  251. if(!$this->sent_disconnect) {
  252. $this->send($this->stream_end);
  253. }
  254. $this->disconnected = True;
  255. $this->sent_disconnect = True;
  256. fclose($this->socket);
  257. if($this->reconnect) {
  258. $this->doReconnect();
  259. }
  260. }
  261. $this->event('end_stream');
  262. }
  263. }
  264. function doReconnect() {
  265. if(!$this->is_server) {
  266. $this->log->log("Reconnecting...", LOGGING_WARNING);
  267. $this->connect(False, False);
  268. $this->reset();
  269. }
  270. }
  271. function disconnect() {
  272. $this->reconnect = False;
  273. $this->send($this->stream_end);
  274. $this->sent_disconnect = True;
  275. $this->processUntil('end_stream', 5);
  276. $this->disconnected = True;
  277. }
  278. function event($name, $payload=Null) {
  279. $this->log->log("EVENT: $name", LOGGING_DEBUG);
  280. foreach($this->eventhandlers as $handler) {
  281. if($name == $handler[0]) {
  282. if($handler[2] === Null) $handler[2] = $this;
  283. call_user_method($handler[1], $handler[2], $payload);
  284. }
  285. }
  286. foreach($this->until as $key => $until) {
  287. if(is_array($until)) {
  288. if(in_array($name, $until)) {
  289. $this->until_payload[$key][] = array($name, $payload);
  290. $this->until[$key] = False;
  291. }
  292. }
  293. }
  294. }
  295. function charXML($parser, $data) {
  296. if(array_key_exists($this->xml_depth, $this->xmlobj))
  297. $this->xmlobj[$this->xml_depth]->data .= $data;
  298. }
  299. function send($msg) {
  300. #socket_write($this->socket, $msg);
  301. $this->log->log("SENT: $msg", LOGGING_VERBOSE);
  302. @fwrite($this->socket, $msg);
  303. }
  304. function reset() {
  305. $this->xml_depth = 0;
  306. unset($this->xmlobj);
  307. $this->xmlobj = array();
  308. $this->setupParser();
  309. if(!$this->is_server) {
  310. $this->send($this->stream_start);
  311. }
  312. $this->been_reset = True;
  313. }
  314. function setupParser() {
  315. $this->parser = xml_parser_create('UTF-8');
  316. xml_parser_set_option($this->parser,XML_OPTION_SKIP_WHITE,1);
  317. xml_parser_set_option($this->parser,XML_OPTION_TARGET_ENCODING, "UTF-8");
  318. xml_set_object($this->parser, $this);
  319. xml_set_element_handler($this->parser, 'startXML', 'endXML');
  320. xml_set_character_data_handler($this->parser, 'charXML');
  321. }
  322. }