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