XMLStream.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. <?php
  2. /**
  3. * XMPPHP: The PHP XMPP Library
  4. * Copyright (C) 2008 Nathanael C. Fritz
  5. * This file is part of SleekXMPP.
  6. *
  7. * XMPPHP is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * XMPPHP is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with XMPPHP; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category xmpphp
  22. * @package XMPPHP
  23. * @author Nathanael C. Fritz <JID: fritzy@netflint.net>
  24. * @author Stephan Wentz <JID: stephan@jabber.wentz.it>
  25. * @author Michael Garvin <JID: gar@netflint.net>
  26. * @author Alexander Birkner (https://github.com/BirknerAlex)
  27. * @author zorn-v (https://github.com/zorn-v/xmpphp/)
  28. * @author GNU social
  29. * @copyright 2008 Nathanael C. Fritz
  30. */
  31. namespace XMPPHP;
  32. /** Exception */
  33. require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exception.php';
  34. /** XMLObj */
  35. require_once __DIR__ . DIRECTORY_SEPARATOR . 'XMLObj.php';
  36. /** Log */
  37. require_once __DIR__ . DIRECTORY_SEPARATOR . 'Log.php';
  38. /**
  39. * XMPPHP XMLStream
  40. *
  41. * @package XMPPHP
  42. * @author Nathanael C. Fritz <JID: fritzy@netflint.net>
  43. * @author Stephan Wentz <JID: stephan@jabber.wentz.it>
  44. * @author Michael Garvin <JID: gar@netflint.net>
  45. * @copyright 2008 Nathanael C. Fritz
  46. * @version $Id$
  47. */
  48. class XMLStream
  49. {
  50. /**
  51. * @var resource
  52. */
  53. protected $socket;
  54. /**
  55. * @var resource
  56. */
  57. protected $parser;
  58. /**
  59. * @var string
  60. */
  61. protected $buffer;
  62. /**
  63. * @var integer
  64. */
  65. protected $xml_depth = 0;
  66. /**
  67. * @var string
  68. */
  69. protected $host;
  70. /**
  71. * @var integer
  72. */
  73. protected $port;
  74. /**
  75. * @var string
  76. */
  77. protected $stream_start = '<stream>';
  78. /**
  79. * @var string
  80. */
  81. protected $stream_end = '</stream>';
  82. /**
  83. * @var boolean
  84. */
  85. protected $disconnected = false;
  86. /**
  87. * @var boolean
  88. */
  89. protected $sent_disconnect = false;
  90. /**
  91. * @var array
  92. */
  93. protected $ns_map = array();
  94. /**
  95. * @var array
  96. */
  97. protected $current_ns = array();
  98. /**
  99. * @var array
  100. */
  101. protected $xmlobj = null;
  102. /**
  103. * @var array
  104. */
  105. protected $nshandlers = array();
  106. /**
  107. * @var array
  108. */
  109. protected $xpathhandlers = array();
  110. /**
  111. * @var array
  112. */
  113. protected $idhandlers = array();
  114. /**
  115. * @var array
  116. */
  117. protected $eventhandlers = array();
  118. /**
  119. * @var integer
  120. */
  121. protected $lastid = 0;
  122. /**
  123. * @var string
  124. */
  125. protected $default_ns;
  126. /**
  127. * @var string[]
  128. */
  129. protected $until = [];
  130. /**
  131. * @var int[]
  132. */
  133. protected $until_count = [];
  134. /**
  135. * @var array
  136. */
  137. protected $until_happened = false;
  138. /**
  139. * @var array
  140. */
  141. protected $until_payload = array();
  142. /**
  143. * @var Log
  144. */
  145. protected $log;
  146. /**
  147. * @var boolean
  148. */
  149. protected $reconnect = true;
  150. /**
  151. * @var boolean
  152. */
  153. protected $been_reset = false;
  154. /**
  155. * @var boolean
  156. */
  157. protected $is_server;
  158. /**
  159. * @var float
  160. */
  161. protected $last_send = 0;
  162. /**
  163. * @var boolean
  164. */
  165. protected $use_ssl = false;
  166. /**
  167. * @var integer
  168. */
  169. protected $reconnectTimeout = 30;
  170. /**
  171. * Constructor
  172. *
  173. * @param string $host
  174. * @param string $port
  175. * @param boolean $printlog
  176. * @param string $loglevel
  177. * @param boolean $is_server
  178. */
  179. public function __construct($host = null, $port = null, $printlog = false, $loglevel = null, $is_server = false)
  180. {
  181. $this->reconnect = !$is_server;
  182. $this->is_server = $is_server;
  183. $this->host = $host;
  184. $this->port = $port;
  185. $this->setupParser();
  186. $this->log = new Log($printlog, $loglevel);
  187. }
  188. /**
  189. * Setup the XML parser
  190. */
  191. public function setupParser()
  192. {
  193. $this->parser = xml_parser_create('UTF-8');
  194. xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
  195. xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
  196. xml_set_object($this->parser, $this);
  197. xml_set_element_handler($this->parser, 'startXML', 'endXML');
  198. xml_set_character_data_handler($this->parser, 'charXML');
  199. }
  200. /**
  201. * Destructor
  202. * Cleanup connection
  203. */
  204. public function __destruct()
  205. {
  206. if (!$this->disconnected && $this->socket) {
  207. $this->disconnect();
  208. }
  209. }
  210. /**
  211. * Disconnect from XMPP Host
  212. */
  213. public function disconnect()
  214. {
  215. $this->log->log("Disconnecting...", Log::LEVEL_VERBOSE);
  216. if (false == (bool)$this->socket) {
  217. return;
  218. }
  219. $this->reconnect = false;
  220. $this->send($this->stream_end);
  221. $this->sent_disconnect = true;
  222. $this->processUntil('end_stream', 5);
  223. $this->disconnected = true;
  224. }
  225. /**
  226. * Send to socket
  227. *
  228. * @param string $msg
  229. * @param null $timeout
  230. * @return bool|int
  231. * @throws Exception
  232. */
  233. public function send($msg, $timeout = NULL)
  234. {
  235. if (is_null($timeout)) {
  236. $secs = NULL;
  237. $usecs = NULL;
  238. } else if ($timeout == 0) {
  239. $secs = 0;
  240. $usecs = 0;
  241. } else {
  242. $maximum = $timeout * 1000000;
  243. $usecs = $maximum % 1000000;
  244. $secs = floor(($maximum - $usecs) / 1000000);
  245. }
  246. $read = array();
  247. $write = array($this->socket);
  248. $except = array();
  249. $select = @stream_select($read, $write, $except, $secs, $usecs);
  250. if ($select === False) {
  251. $this->log->log("ERROR sending message; reconnecting.");
  252. $this->doReconnect();
  253. # TODO: retry send here
  254. return false;
  255. } elseif ($select > 0) {
  256. $this->log->log("Socket is ready; send it.", Log::LEVEL_VERBOSE);
  257. } else {
  258. $this->log->log("Socket is not ready; break.", Log::LEVEL_ERROR);
  259. return false;
  260. }
  261. $sentbytes = @fwrite($this->socket, $msg);
  262. $this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), Log::LEVEL_VERBOSE);
  263. if ($sentbytes === FALSE) {
  264. $this->log->log("ERROR sending message; reconnecting.", Log::LEVEL_ERROR);
  265. $this->doReconnect();
  266. return false;
  267. }
  268. $this->log->log("Successfully sent $sentbytes bytes.", Log::LEVEL_VERBOSE);
  269. return $sentbytes;
  270. }
  271. /**
  272. * Reconnect XMPP Host
  273. * @throws Exception
  274. */
  275. public function doReconnect()
  276. {
  277. if (!$this->is_server) {
  278. $this->log->log("Reconnecting ($this->reconnectTimeout)...", Log::LEVEL_WARNING);
  279. $this->connect($this->reconnectTimeout, false, false);
  280. $this->reset();
  281. $this->event('reconnect');
  282. }
  283. }
  284. /**
  285. * Connect to XMPP Host
  286. *
  287. * @param integer $timeout
  288. * @param boolean $persistent
  289. * @param boolean $sendinit
  290. * @throws Exception
  291. * @throws Exception
  292. */
  293. public function connect($timeout = 30, $persistent = false, $sendinit = true)
  294. {
  295. $this->sent_disconnect = false;
  296. $starttime = time();
  297. do {
  298. $this->disconnected = false;
  299. $this->sent_disconnect = false;
  300. if ($persistent) {
  301. $conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
  302. } else {
  303. $conflag = STREAM_CLIENT_CONNECT;
  304. }
  305. $conntype = 'tcp';
  306. if ($this->use_ssl) $conntype = 'ssl';
  307. $this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
  308. $this->socket = @stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
  309. if (!$this->socket) {
  310. $this->log->log("Could not connect.", Log::LEVEL_ERROR);
  311. $this->disconnected = true;
  312. # Take it easy for a few seconds
  313. sleep(min($timeout, 5));
  314. }
  315. } while (!$this->socket && (time() - $starttime) < $timeout);
  316. if ($this->socket) {
  317. stream_set_blocking($this->socket, 1);
  318. if ($sendinit) $this->send($this->stream_start);
  319. } else {
  320. throw new Exception("Could not connect before timeout.");
  321. }
  322. }
  323. /**
  324. * Reset connection
  325. */
  326. public function reset()
  327. {
  328. $this->xml_depth = 0;
  329. unset($this->xmlobj);
  330. $this->xmlobj = array();
  331. $this->setupParser();
  332. if (!$this->is_server) {
  333. $this->send($this->stream_start);
  334. }
  335. $this->been_reset = true;
  336. }
  337. /**
  338. * Event?
  339. *
  340. * @param string $name
  341. * @param string $payload
  342. */
  343. public function event($name, $payload = null)
  344. {
  345. $this->log->log("EVENT: $name", Log::LEVEL_DEBUG);
  346. foreach ($this->eventhandlers as $handler) {
  347. if ($name == $handler[0]) {
  348. if ($handler[2] === null) {
  349. $handler[2] = $this;
  350. }
  351. $handler[2]->{$handler[1]}($payload);
  352. }
  353. }
  354. foreach ($this->until as $key => $until) {
  355. if (is_array($until)) {
  356. if (in_array($name, $until)) {
  357. $this->until_payload[$key][] = array($name, $payload);
  358. if (!isset($this->until_count[$key])) {
  359. $this->until_count[$key] = 0;
  360. }
  361. $this->until_count[$key] += 1;
  362. #$this->until[$key] = false;
  363. }
  364. }
  365. }
  366. }
  367. /**
  368. * Process until a specified event or a timeout occurs
  369. *
  370. * @param string|array $event
  371. * @param integer $timeout
  372. * @return string
  373. * @throws Exception
  374. */
  375. public function processUntil($event, $timeout = -1)
  376. {
  377. $start = time();
  378. if (!is_array($event)) $event = array($event);
  379. $this->until[] = $event;
  380. end($this->until);
  381. $event_key = key($this->until);
  382. reset($this->until);
  383. $this->until_count[$event_key] = 0;
  384. while (!$this->disconnected and $this->until_count[$event_key] < 1 and (time() - $start < $timeout or $timeout == -1)) {
  385. $this->__process();
  386. }
  387. if (array_key_exists($event_key, $this->until_payload)) {
  388. $payload = $this->until_payload[$event_key];
  389. unset($this->until_payload[$event_key]);
  390. unset($this->until_count[$event_key]);
  391. unset($this->until[$event_key]);
  392. } else {
  393. $payload = array();
  394. }
  395. return $payload;
  396. }
  397. /**
  398. * Core reading tool
  399. * 0 -> only read if data is immediately ready
  400. * NULL -> wait forever and ever
  401. * integer -> process for this amount of time
  402. * @param int $maximum
  403. * @return bool
  404. * @throws Exception
  405. */
  406. private function __process($maximum = 5)
  407. {
  408. $remaining = $maximum;
  409. do {
  410. $starttime = (microtime(true) * 1000000);
  411. $read = array($this->socket);
  412. $write = array();
  413. $except = array();
  414. if (is_null($maximum)) {
  415. $secs = NULL;
  416. $usecs = NULL;
  417. } else if ($maximum == 0) {
  418. $secs = 0;
  419. $usecs = 0;
  420. } else {
  421. $usecs = $remaining % 1000000;
  422. $secs = floor(($remaining - $usecs) / 1000000);
  423. }
  424. $updated = @stream_select($read, $write, $except, $secs, $usecs);
  425. if ($updated === false) {
  426. $this->log->log("Error on stream_select()", Log::LEVEL_VERBOSE);
  427. if ($this->reconnect) {
  428. $this->doReconnect();
  429. } else {
  430. fclose($this->socket);
  431. $this->socket = NULL;
  432. return false;
  433. }
  434. } else if ($updated > 0) {
  435. # XXX: Is this big enough?
  436. $buff = @fread($this->socket, 4096);
  437. if (!$buff) {
  438. if ($this->reconnect) {
  439. $this->doReconnect();
  440. } else {
  441. fclose($this->socket);
  442. $this->socket = NULL;
  443. return false;
  444. }
  445. }
  446. $this->log->log("RECV: $buff", Log::LEVEL_VERBOSE);
  447. xml_parse($this->parser, $buff, false);
  448. } // Otherwise,
  449. // $updated == 0 means no changes during timeout.
  450. $endtime = (microtime(true) * 1000000);
  451. $time_past = $endtime - $starttime;
  452. $remaining = $remaining - $time_past;
  453. } while (is_null($maximum) || $remaining > 0);
  454. return true;
  455. }
  456. /**
  457. * Return the log instance
  458. *
  459. * @return Log
  460. */
  461. public function getLog()
  462. {
  463. return $this->log;
  464. }
  465. /**
  466. * Get next ID
  467. *
  468. * @return integer
  469. */
  470. public function getId()
  471. {
  472. $this->lastid++;
  473. return $this->lastid;
  474. }
  475. /**
  476. * Set SSL
  477. * @param bool $use
  478. */
  479. public function useSSL($use = true)
  480. {
  481. $this->use_ssl = $use;
  482. }
  483. /**
  484. * Add ID Handler
  485. *
  486. * @param integer $id
  487. * @param string $pointer
  488. * @param string $obj
  489. */
  490. public function addIdHandler($id, $pointer, $obj = null)
  491. {
  492. $this->idhandlers[$id] = array($pointer, $obj);
  493. }
  494. /**
  495. * Add Handler
  496. *
  497. * @param string $name
  498. * @param string $ns
  499. * @param string $pointer
  500. * @param string $obj
  501. * @param integer $depth
  502. */
  503. public function addHandler($name, $ns, $pointer, $obj = null, $depth = 1)
  504. {
  505. #TODO deprication warning
  506. $this->nshandlers[] = array($name, $ns, $pointer, $obj, $depth);
  507. }
  508. /**
  509. * Add XPath Handler
  510. *
  511. * @param string $xpath
  512. * @param string $pointer
  513. * @param
  514. */
  515. public function addXPathHandler($xpath, $pointer, $obj = null)
  516. {
  517. if (preg_match_all("/\(?{[^\}]+}\)?(\/?)[^\/]+/", $xpath, $regs)) {
  518. $ns_tags = $regs[0];
  519. } else {
  520. $ns_tags = array($xpath);
  521. }
  522. foreach ($ns_tags as $ns_tag) {
  523. list($l, $r) = explode("}", $ns_tag);
  524. if ($r != null) {
  525. $xpart = array(substr($l, 1), $r);
  526. } else {
  527. $xpart = array(null, $l);
  528. }
  529. $xpath_array[] = $xpart;
  530. }
  531. $this->xpathhandlers[] = array($xpath_array, $pointer, $obj);
  532. }
  533. /**
  534. * Add Event Handler
  535. *
  536. * @param $name
  537. * @param string $pointer
  538. * @param string $obj
  539. */
  540. public function addEventHandler($name, $pointer, $obj)
  541. {
  542. $this->eventhandlers[] = array($name, $pointer, $obj);
  543. }
  544. public function setReconnectTimeout($timeout)
  545. {
  546. $this->reconnectTimeout = $timeout;
  547. }
  548. /**
  549. * Are we are disconnected?
  550. *
  551. * @return boolean
  552. */
  553. public function isDisconnected()
  554. {
  555. return $this->disconnected;
  556. }
  557. /**
  558. * Process
  559. *
  560. */
  561. public function process()
  562. {
  563. $this->__process(NULL);
  564. }
  565. /**
  566. * Process until a timeout occurs
  567. *
  568. * @param integer $timeout
  569. * @return string
  570. * @throws Exception
  571. */
  572. public function processTime($timeout = NULL)
  573. {
  574. if (is_null($timeout)) {
  575. return $this->__process(NULL);
  576. } else {
  577. return $this->__process($timeout * 1000000);
  578. }
  579. }
  580. /**
  581. * Obsolete?
  582. * @param $socket
  583. */
  584. public function Xapply_socket($socket)
  585. {
  586. $this->socket = $socket;
  587. }
  588. /**
  589. * XML start callback
  590. *
  591. * @param resource $parser
  592. * @param string $name
  593. * @param $attr
  594. * @see xml_set_element_handler
  595. */
  596. public function startXML($parser, $name, $attr)
  597. {
  598. if ($this->been_reset) {
  599. $this->been_reset = false;
  600. $this->xml_depth = 0;
  601. }
  602. $this->xml_depth++;
  603. if (array_key_exists('XMLNS', $attr)) {
  604. $this->current_ns[$this->xml_depth] = $attr['XMLNS'];
  605. } else {
  606. $this->current_ns[$this->xml_depth] = $this->current_ns[$this->xml_depth - 1];
  607. if (!$this->current_ns[$this->xml_depth]) $this->current_ns[$this->xml_depth] = $this->default_ns;
  608. }
  609. $ns = $this->current_ns[$this->xml_depth];
  610. foreach ($attr as $key => $value) {
  611. if (strstr($key, ":")) {
  612. $key = explode(':', $key);
  613. $key = $key[1];
  614. $this->ns_map[$key] = $value;
  615. }
  616. }
  617. if (!strstr($name, ":") === false) {
  618. $name = explode(':', $name);
  619. $ns = $this->ns_map[$name[0]];
  620. $name = $name[1];
  621. }
  622. $obj = new XMLObj($name, $ns, $attr);
  623. if ($this->xml_depth > 1) {
  624. $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj;
  625. }
  626. $this->xmlobj[$this->xml_depth] = $obj;
  627. }
  628. /**
  629. * XML end callback
  630. *
  631. * @param resource $parser
  632. * @param string $name
  633. * @throws Exception
  634. * @see xml_set_element_handler
  635. *
  636. */
  637. public function endXML($parser, $name)
  638. {
  639. #$this->log->log("Ending $name", Log::LEVEL_DEBUG);
  640. #print "$name\n";
  641. if ($this->been_reset) {
  642. $this->been_reset = false;
  643. $this->xml_depth = 0;
  644. }
  645. $this->xml_depth--;
  646. if ($this->xml_depth == 1) {
  647. #clean-up old objects
  648. #$found = false; #FIXME This didn't appear to be in use --Gar
  649. foreach ($this->xpathhandlers as $handler) {
  650. if (is_array($this->xmlobj) && array_key_exists(2, $this->xmlobj)) {
  651. $searchxml = $this->xmlobj[2];
  652. $nstag = array_shift($handler[0]);
  653. if (($nstag[0] == null or $searchxml->ns == $nstag[0]) and ($nstag[1] == "*" or $nstag[1] == $searchxml->name)) {
  654. foreach ($handler[0] as $nstag) {
  655. if ($searchxml !== null and $searchxml->hasSub($nstag[1], $ns = $nstag[0])) {
  656. $searchxml = $searchxml->sub($nstag[1], $ns = $nstag[0]);
  657. } else {
  658. $searchxml = null;
  659. break;
  660. }
  661. }
  662. if ($searchxml !== null) {
  663. if ($handler[2] === null) $handler[2] = $this;
  664. $this->log->log("Calling {$handler[1]}", Log::LEVEL_DEBUG);
  665. $handler[2]->{$handler[1]}($this->xmlobj[2]);
  666. }
  667. }
  668. }
  669. }
  670. foreach ($this->nshandlers as $handler) {
  671. if ($handler[4] != 1 and array_key_exists(2, $this->xmlobj) and $this->xmlobj[2]->hasSub($handler[0])) {
  672. $searchxml = $this->xmlobj[2]->sub($handler[0]);
  673. } elseif (is_array($this->xmlobj) and array_key_exists(2, $this->xmlobj)) {
  674. $searchxml = $this->xmlobj[2];
  675. }
  676. if ($searchxml !== null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) {
  677. if ($handler[3] === null) $handler[3] = $this;
  678. $this->log->log("Calling {$handler[2]}", Log::LEVEL_DEBUG);
  679. $handler[3]->{$handler[2]}($this->xmlobj[2]);
  680. }
  681. }
  682. foreach ($this->idhandlers as $id => $handler) {
  683. if (array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) {
  684. if ($handler[1] === null) $handler[1] = $this;
  685. $handler[1]->{$handler[0]}($this->xmlobj[2]);
  686. #id handlers are only used once
  687. unset($this->idhandlers[$id]);
  688. break;
  689. }
  690. }
  691. if (is_array($this->xmlobj)) {
  692. $this->xmlobj = array_slice($this->xmlobj, 0, 1);
  693. if (isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMLObj) {
  694. $this->xmlobj[0]->subs = null;
  695. }
  696. }
  697. unset($this->xmlobj[2]);
  698. }
  699. if ($this->xml_depth == 0 and !$this->been_reset) {
  700. if (!$this->disconnected) {
  701. if (!$this->sent_disconnect) {
  702. $this->send($this->stream_end);
  703. }
  704. $this->disconnected = true;
  705. $this->sent_disconnect = true;
  706. fclose($this->socket);
  707. if ($this->reconnect) {
  708. $this->doReconnect();
  709. }
  710. }
  711. $this->event('end_stream');
  712. }
  713. }
  714. /**
  715. * XML character callback
  716. * @param resource $parser
  717. * @param string $data
  718. * @see xml_set_character_data_handler
  719. *
  720. */
  721. public function charXML($parser, $data)
  722. {
  723. if (array_key_exists($this->xml_depth, $this->xmlobj)) {
  724. $this->xmlobj[$this->xml_depth]->data .= $data;
  725. }
  726. }
  727. /**
  728. * Read from socket
  729. */
  730. public function read()
  731. {
  732. $buff = @fread($this->socket, 1024);
  733. if (!$buff) {
  734. if ($this->reconnect) {
  735. $this->doReconnect();
  736. } else {
  737. fclose($this->socket);
  738. return false;
  739. }
  740. }
  741. $this->log->log("RECV: $buff", Log::LEVEL_VERBOSE);
  742. xml_parse($this->parser, $buff, false);
  743. }
  744. public function time()
  745. {
  746. list($usec, $sec) = explode(" ", microtime());
  747. return (float)$sec + (float)$usec;
  748. }
  749. public function readyToProcess()
  750. {
  751. $read = array($this->socket);
  752. $write = array();
  753. $except = array();
  754. $updated = @stream_select($read, $write, $except, 0);
  755. return (($updated !== false) && ($updated > 0));
  756. }
  757. }