[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 /** 23 * 24 * Jabber class from Flyspray project 25 * 26 * @version class.jabber2.php 1595 2008-09-19 (0.9.9) 27 * @copyright 2006 Flyspray.org 28 * @author Florian Schmitz (floele) 29 * 30 * Only slightly modified by Acyd Burn 31 */ 32 class jabber 33 { 34 var $connection = null; 35 var $session = array(); 36 var $timeout = 10; 37 38 var $server; 39 var $connect_server; 40 var $port; 41 var $username; 42 var $password; 43 var $use_ssl; 44 var $verify_peer; 45 var $verify_peer_name; 46 var $allow_self_signed; 47 var $resource = 'functions_jabber.phpbb.php'; 48 49 var $enable_logging; 50 var $log_array; 51 52 var $features = array(); 53 54 /** 55 * Constructor 56 * 57 * @param string $server Jabber server 58 * @param int $port Jabber server port 59 * @param string $username Jabber username or JID 60 * @param string $password Jabber password 61 * @param boold $use_ssl Use ssl 62 * @param bool $verify_peer Verify SSL certificate 63 * @param bool $verify_peer_name Verify Jabber peer name 64 * @param bool $allow_self_signed Allow self signed certificates 65 */ 66 function __construct($server, $port, $username, $password, $use_ssl = false, $verify_peer = true, $verify_peer_name = true, $allow_self_signed = false) 67 { 68 $this->connect_server = ($server) ? $server : 'localhost'; 69 $this->port = ($port) ? $port : 5222; 70 71 // Get the server and the username 72 if (strpos($username, '@') === false) 73 { 74 $this->server = $this->connect_server; 75 $this->username = $username; 76 } 77 else 78 { 79 $jid = explode('@', $username, 2); 80 81 $this->username = $jid[0]; 82 $this->server = $jid[1]; 83 } 84 85 $this->password = $password; 86 $this->use_ssl = ($use_ssl && self::can_use_ssl()) ? true : false; 87 $this->verify_peer = $verify_peer; 88 $this->verify_peer_name = $verify_peer_name; 89 $this->allow_self_signed = $allow_self_signed; 90 91 // Change port if we use SSL 92 if ($this->port == 5222 && $this->use_ssl) 93 { 94 $this->port = 5223; 95 } 96 97 $this->enable_logging = true; 98 $this->log_array = array(); 99 } 100 101 /** 102 * Able to use the SSL functionality? 103 */ 104 static public function can_use_ssl() 105 { 106 return @extension_loaded('openssl'); 107 } 108 109 /** 110 * Able to use TLS? 111 */ 112 static public function can_use_tls() 113 { 114 if (!@extension_loaded('openssl') || !function_exists('stream_socket_enable_crypto') || !function_exists('stream_get_meta_data') || !function_exists('stream_set_blocking') || !function_exists('stream_get_wrappers')) 115 { 116 return false; 117 } 118 119 /** 120 * Make sure the encryption stream is supported 121 * Also seem to work without the crypto stream if correctly compiled 122 123 $streams = stream_get_wrappers(); 124 125 if (!in_array('streams.crypto', $streams)) 126 { 127 return false; 128 } 129 */ 130 131 return true; 132 } 133 134 /** 135 * Sets the resource which is used. No validation is done here, only escaping. 136 * @param string $name 137 * @access public 138 */ 139 function set_resource($name) 140 { 141 $this->resource = $name; 142 } 143 144 /** 145 * Connect 146 */ 147 function connect() 148 { 149 /* if (!$this->check_jid($this->username . '@' . $this->server)) 150 { 151 $this->add_to_log('Error: Jabber ID is not valid: ' . $this->username . '@' . $this->server); 152 return false; 153 }*/ 154 155 $this->session['ssl'] = $this->use_ssl; 156 157 if ($this->open_socket($this->connect_server, $this->port, $this->use_ssl, $this->verify_peer, $this->verify_peer_name, $this->allow_self_signed)) 158 { 159 $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n"); 160 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 161 } 162 else 163 { 164 $this->add_to_log('Error: connect() #2'); 165 return false; 166 } 167 168 // Now we listen what the server has to say...and give appropriate responses 169 $this->response($this->listen()); 170 return true; 171 } 172 173 /** 174 * Disconnect 175 */ 176 function disconnect() 177 { 178 if ($this->connected()) 179 { 180 // disconnect gracefully 181 if (isset($this->session['sent_presence'])) 182 { 183 $this->send_presence('offline', '', true); 184 } 185 186 $this->send('</stream:stream>'); 187 $this->session = array(); 188 return fclose($this->connection); 189 } 190 191 return false; 192 } 193 194 /** 195 * Connected? 196 */ 197 function connected() 198 { 199 return (is_resource($this->connection) && !feof($this->connection)) ? true : false; 200 } 201 202 203 /** 204 * Initiates login (using data from contructor, after calling connect()) 205 * @access public 206 * @return bool 207 */ 208 function login() 209 { 210 if (empty($this->features)) 211 { 212 $this->add_to_log('Error: No feature information from server available.'); 213 return false; 214 } 215 216 return $this->response($this->features); 217 } 218 219 /** 220 * Send data to the Jabber server 221 * @param string $xml 222 * @access public 223 * @return bool 224 */ 225 function send($xml) 226 { 227 if ($this->connected()) 228 { 229 $xml = trim($xml); 230 return fwrite($this->connection, $xml); 231 } 232 else 233 { 234 $this->add_to_log('Error: Could not send, connection lost (flood?).'); 235 return false; 236 } 237 } 238 239 /** 240 * OpenSocket 241 * @param string $server host to connect to 242 * @param int $port port number 243 * @param bool $use_ssl use ssl or not 244 * @param bool $verify_peer verify ssl certificate 245 * @param bool $verify_peer_name verify peer name 246 * @param bool $allow_self_signed allow self-signed ssl certificates 247 * @access public 248 * @return bool 249 */ 250 function open_socket($server, $port, $use_ssl, $verify_peer, $verify_peer_name, $allow_self_signed) 251 { 252 if (@function_exists('dns_get_record')) 253 { 254 $record = @dns_get_record("_xmpp-client._tcp.$server", DNS_SRV); 255 if (!empty($record) && !empty($record[0]['target'])) 256 { 257 $server = $record[0]['target']; 258 } 259 } 260 261 $options = array(); 262 263 if ($use_ssl) 264 { 265 $remote_socket = 'ssl://' . $server . ':' . $port; 266 267 // Set ssl context options, see http://php.net/manual/en/context.ssl.php 268 $options['ssl'] = array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed); 269 } 270 else 271 { 272 $remote_socket = $server . ':' . $port; 273 } 274 275 $socket_context = stream_context_create($options); 276 277 if ($this->connection = @stream_socket_client($remote_socket, $errorno, $errorstr, $this->timeout, STREAM_CLIENT_CONNECT, $socket_context)) 278 { 279 stream_set_blocking($this->connection, 0); 280 stream_set_timeout($this->connection, 60); 281 282 return true; 283 } 284 285 // Apparently an error occurred... 286 $this->add_to_log('Error: open_socket() - ' . $errorstr); 287 return false; 288 } 289 290 /** 291 * Return log 292 */ 293 function get_log() 294 { 295 if ($this->enable_logging && count($this->log_array)) 296 { 297 return implode("<br /><br />", $this->log_array); 298 } 299 300 return ''; 301 } 302 303 /** 304 * Add information to log 305 */ 306 function add_to_log($string) 307 { 308 if ($this->enable_logging) 309 { 310 $this->log_array[] = utf8_htmlspecialchars($string); 311 } 312 } 313 314 /** 315 * Listens to the connection until it gets data or the timeout is reached. 316 * Thus, it should only be called if data is expected to be received. 317 * @access public 318 * @return mixed either false for timeout or an array with the received data 319 */ 320 function listen($timeout = 10, $wait = false) 321 { 322 if (!$this->connected()) 323 { 324 return false; 325 } 326 327 // Wait for a response until timeout is reached 328 $start = time(); 329 $data = ''; 330 331 do 332 { 333 $read = trim(fread($this->connection, 4096)); 334 $data .= $read; 335 } 336 while (time() <= $start + $timeout && !feof($this->connection) && ($wait || $data == '' || $read != '' || (substr(rtrim($data), -1) != '>'))); 337 338 if ($data != '') 339 { 340 return $this->xmlize($data); 341 } 342 else 343 { 344 $this->add_to_log('Timeout, no response from server.'); 345 return false; 346 } 347 } 348 349 /** 350 * Initiates account registration (based on data used for contructor) 351 * @access public 352 * @return bool 353 */ 354 function register() 355 { 356 if (!isset($this->session['id']) || isset($this->session['jid'])) 357 { 358 $this->add_to_log('Error: Cannot initiate registration.'); 359 return false; 360 } 361 362 $this->send("<iq type='get' id='reg_1'><query xmlns='jabber:iq:register'/></iq>"); 363 return $this->response($this->listen()); 364 } 365 366 /** 367 * Sets account presence. No additional info required (default is "online" status) 368 * @param $message online, offline... 369 * @param $type dnd, away, chat, xa or nothing 370 * @param $unavailable set this to true if you want to become unavailable 371 * @access public 372 * @return bool 373 */ 374 function send_presence($message = '', $type = '', $unavailable = false) 375 { 376 if (!isset($this->session['jid'])) 377 { 378 $this->add_to_log('ERROR: send_presence() - Cannot set presence at this point, no jid given.'); 379 return false; 380 } 381 382 $type = strtolower($type); 383 $type = (in_array($type, array('dnd', 'away', 'chat', 'xa'))) ? '<show>'. $type .'</show>' : ''; 384 385 $unavailable = ($unavailable) ? " type='unavailable'" : ''; 386 $message = ($message) ? '<status>' . utf8_htmlspecialchars($message) .'</status>' : ''; 387 388 $this->session['sent_presence'] = !$unavailable; 389 390 return $this->send("<presence$unavailable>" . $type . $message . '</presence>'); 391 } 392 393 /** 394 * This handles all the different XML elements 395 * @param array $xml 396 * @access public 397 * @return bool 398 */ 399 function response($xml) 400 { 401 if (!is_array($xml) || !count($xml)) 402 { 403 return false; 404 } 405 406 // did we get multiple elements? do one after another 407 // array('message' => ..., 'presence' => ...) 408 if (count($xml) > 1) 409 { 410 foreach ($xml as $key => $value) 411 { 412 $this->response(array($key => $value)); 413 } 414 return; 415 } 416 else 417 { 418 // or even multiple elements of the same type? 419 // array('message' => array(0 => ..., 1 => ...)) 420 if (is_array(reset($xml)) && count(reset($xml)) > 1) 421 { 422 foreach (reset($xml) as $value) 423 { 424 $this->response(array(key($xml) => array(0 => $value))); 425 } 426 return; 427 } 428 } 429 430 switch (key($xml)) 431 { 432 case 'stream:stream': 433 // Connection initialised (or after authentication). Not much to do here... 434 435 if (isset($xml['stream:stream'][0]['#']['stream:features'])) 436 { 437 // we already got all info we need 438 $this->features = $xml['stream:stream'][0]['#']; 439 } 440 else 441 { 442 $this->features = $this->listen(); 443 } 444 445 $second_time = isset($this->session['id']); 446 $this->session['id'] = isset($xml['stream:stream'][0]['@']['id']) ? $xml['stream:stream'][0]['@']['id'] : ''; 447 448 if ($second_time) 449 { 450 // If we are here for the second time after TLS, we need to continue logging in 451 return $this->login(); 452 } 453 454 // go on with authentication? 455 if (isset($this->features['stream:features'][0]['#']['bind']) || !empty($this->session['tls'])) 456 { 457 return $this->response($this->features); 458 } 459 break; 460 461 case 'stream:features': 462 // Resource binding after successful authentication 463 if (isset($this->session['authenticated'])) 464 { 465 // session required? 466 $this->session['sess_required'] = isset($xml['stream:features'][0]['#']['session']); 467 468 $this->send("<iq type='set' id='bind_1'> 469 <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> 470 <resource>" . utf8_htmlspecialchars($this->resource) . '</resource> 471 </bind> 472 </iq>'); 473 return $this->response($this->listen()); 474 } 475 476 // Let's use TLS if SSL is not enabled and we can actually use it 477 if (!$this->session['ssl'] && self::can_use_tls() && self::can_use_ssl() && isset($xml['stream:features'][0]['#']['starttls'])) 478 { 479 $this->add_to_log('Switching to TLS.'); 480 $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n"); 481 return $this->response($this->listen()); 482 } 483 484 // Does the server support SASL authentication? 485 486 // I hope so, because we do (and no other method). 487 if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl') 488 { 489 // Now decide on method 490 $methods = array(); 491 492 foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value) 493 { 494 $methods[] = $value['#']; 495 } 496 497 // we prefer DIGEST-MD5 498 // we don't want to use plain authentication (neither does the server usually) if no encryption is in place 499 500 // http://www.xmpp.org/extensions/attic/jep-0078-1.7.html 501 // The plaintext mechanism SHOULD NOT be used unless the underlying stream is encrypted (using SSL or TLS) 502 // and the client has verified that the server certificate is signed by a trusted certificate authority. 503 504 if (in_array('DIGEST-MD5', $methods)) 505 { 506 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>"); 507 } 508 else if (in_array('PLAIN', $methods) && ($this->session['ssl'] || !empty($this->session['tls']))) 509 { 510 // http://www.ietf.org/rfc/rfc4616.txt (PLAIN SASL Mechanism) 511 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" 512 . base64_encode($this->username . '@' . $this->server . chr(0) . $this->username . chr(0) . $this->password) . 513 '</auth>'); 514 } 515 else if (in_array('ANONYMOUS', $methods)) 516 { 517 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>"); 518 } 519 else 520 { 521 // not good... 522 $this->add_to_log('Error: No authentication method supported.'); 523 $this->disconnect(); 524 return false; 525 } 526 527 return $this->response($this->listen()); 528 } 529 else 530 { 531 // ok, this is it. bye. 532 $this->add_to_log('Error: Server does not offer SASL authentication.'); 533 $this->disconnect(); 534 return false; 535 } 536 break; 537 538 case 'challenge': 539 // continue with authentication...a challenge literally -_- 540 $decoded = base64_decode($xml['challenge'][0]['#']); 541 $decoded = $this->parse_data($decoded); 542 543 if (!isset($decoded['digest-uri'])) 544 { 545 $decoded['digest-uri'] = 'xmpp/'. $this->server; 546 } 547 548 // better generate a cnonce, maybe it's needed 549 $decoded['cnonce'] = base64_encode(md5(uniqid(mt_rand(), true))); 550 551 // second challenge? 552 if (isset($decoded['rspauth'])) 553 { 554 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); 555 } 556 else 557 { 558 // Make sure we only use 'auth' for qop (relevant for $this->encrypt_password()) 559 // If the <response> is choking up on the changed parameter we may need to adjust encrypt_password() directly 560 if (isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) 561 { 562 $decoded['qop'] = 'auth'; 563 } 564 565 $response = array( 566 'username' => $this->username, 567 'response' => $this->encrypt_password(array_merge($decoded, array('nc' => '00000001'))), 568 'charset' => 'utf-8', 569 'nc' => '00000001', 570 'qop' => 'auth', // only auth being supported 571 ); 572 573 foreach (array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) 574 { 575 if (isset($decoded[$key])) 576 { 577 $response[$key] = $decoded[$key]; 578 } 579 } 580 581 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" . base64_encode($this->implode_data($response)) . '</response>'); 582 } 583 584 return $this->response($this->listen()); 585 break; 586 587 case 'failure': 588 $this->add_to_log('Error: Server sent "failure".'); 589 $this->disconnect(); 590 return false; 591 break; 592 593 case 'proceed': 594 // continue switching to TLS 595 $meta = stream_get_meta_data($this->connection); 596 stream_set_blocking($this->connection, 1); 597 598 if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) 599 { 600 $this->add_to_log('Error: TLS mode change failed.'); 601 return false; 602 } 603 604 stream_set_blocking($this->connection, $meta['blocked']); 605 $this->session['tls'] = true; 606 607 // new stream 608 $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n"); 609 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 610 611 return $this->response($this->listen()); 612 break; 613 614 case 'success': 615 // Yay, authentication successful. 616 $this->send("<stream:stream to='{$this->server}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n"); 617 $this->session['authenticated'] = true; 618 619 // we have to wait for another response 620 return $this->response($this->listen()); 621 break; 622 623 case 'iq': 624 // we are not interested in IQs we did not expect 625 if (!isset($xml['iq'][0]['@']['id'])) 626 { 627 return false; 628 } 629 630 // multiple possibilities here 631 switch ($xml['iq'][0]['@']['id']) 632 { 633 case 'bind_1': 634 $this->session['jid'] = $xml['iq'][0]['#']['bind'][0]['#']['jid'][0]['#']; 635 636 // and (maybe) yet another request to be able to send messages *finally* 637 if ($this->session['sess_required']) 638 { 639 $this->send("<iq to='{$this->server}' type='set' id='sess_1'> 640 <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> 641 </iq>"); 642 return $this->response($this->listen()); 643 } 644 645 return true; 646 break; 647 648 case 'sess_1': 649 return true; 650 break; 651 652 case 'reg_1': 653 $this->send("<iq type='set' id='reg_2'> 654 <query xmlns='jabber:iq:register'> 655 <username>" . utf8_htmlspecialchars($this->username) . "</username> 656 <password>" . utf8_htmlspecialchars($this->password) . "</password> 657 </query> 658 </iq>"); 659 return $this->response($this->listen()); 660 break; 661 662 case 'reg_2': 663 // registration end 664 if (isset($xml['iq'][0]['#']['error'])) 665 { 666 $this->add_to_log('Warning: Registration failed.'); 667 return false; 668 } 669 return true; 670 break; 671 672 case 'unreg_1': 673 return true; 674 break; 675 676 default: 677 $this->add_to_log('Notice: Received unexpected IQ.'); 678 return false; 679 break; 680 } 681 break; 682 683 case 'message': 684 // we are only interested in content... 685 if (!isset($xml['message'][0]['#']['body'])) 686 { 687 return false; 688 } 689 690 $message['body'] = $xml['message'][0]['#']['body'][0]['#']; 691 $message['from'] = $xml['message'][0]['@']['from']; 692 693 if (isset($xml['message'][0]['#']['subject'])) 694 { 695 $message['subject'] = $xml['message'][0]['#']['subject'][0]['#']; 696 } 697 $this->session['messages'][] = $message; 698 break; 699 700 default: 701 // hm...don't know this response 702 $this->add_to_log('Notice: Unknown server response'); 703 return false; 704 break; 705 } 706 } 707 708 function send_message($to, $text, $subject = '', $type = 'normal') 709 { 710 if (!isset($this->session['jid'])) 711 { 712 return false; 713 } 714 715 if (!in_array($type, array('chat', 'normal', 'error', 'groupchat', 'headline'))) 716 { 717 $type = 'normal'; 718 } 719 720 return $this->send("<message from='" . utf8_htmlspecialchars($this->session['jid']) . "' to='" . utf8_htmlspecialchars($to) . "' type='$type' id='" . uniqid('msg') . "'> 721 <subject>" . utf8_htmlspecialchars($subject) . "</subject> 722 <body>" . utf8_htmlspecialchars($text) . "</body> 723 </message>" 724 ); 725 } 726 727 /** 728 * Encrypts a password as in RFC 2831 729 * @param array $data Needs data from the client-server connection 730 * @access public 731 * @return string 732 */ 733 function encrypt_password($data) 734 { 735 // let's me think about <challenge> again... 736 foreach (array('realm', 'cnonce', 'digest-uri') as $key) 737 { 738 if (!isset($data[$key])) 739 { 740 $data[$key] = ''; 741 } 742 } 743 744 $pack = md5($this->username . ':' . $data['realm'] . ':' . $this->password); 745 746 if (isset($data['authzid'])) 747 { 748 $a1 = pack('H32', $pack) . sprintf(':%s:%s:%s', $data['nonce'], $data['cnonce'], $data['authzid']); 749 } 750 else 751 { 752 $a1 = pack('H32', $pack) . sprintf(':%s:%s', $data['nonce'], $data['cnonce']); 753 } 754 755 // should be: qop = auth 756 $a2 = 'AUTHENTICATE:'. $data['digest-uri']; 757 758 return md5(sprintf('%s:%s:%s:%s:%s:%s', md5($a1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($a2))); 759 } 760 761 /** 762 * parse_data like a="b",c="d",... or like a="a, b", c, d="e", f=g,... 763 * @param string $data 764 * @access public 765 * @return array a => b ... 766 */ 767 function parse_data($data) 768 { 769 $data = explode(',', $data); 770 $pairs = array(); 771 $key = false; 772 773 foreach ($data as $pair) 774 { 775 $dd = strpos($pair, '='); 776 777 if ($dd) 778 { 779 $key = trim(substr($pair, 0, $dd)); 780 $pairs[$key] = trim(trim(substr($pair, $dd + 1)), '"'); 781 } 782 else if (strpos(strrev(trim($pair)), '"') === 0 && $key) 783 { 784 // We are actually having something left from "a, b" values, add it to the last one we handled. 785 $pairs[$key] .= ',' . trim(trim($pair), '"'); 786 continue; 787 } 788 } 789 790 return $pairs; 791 } 792 793 /** 794 * opposite of jabber::parse_data() 795 * @param array $data 796 * @access public 797 * @return string 798 */ 799 function implode_data($data) 800 { 801 $return = array(); 802 foreach ($data as $key => $value) 803 { 804 $return[] = $key . '="' . $value . '"'; 805 } 806 return implode(',', $return); 807 } 808 809 /** 810 * xmlize() 811 * @author Hans Anderson 812 * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ 813 */ 814 function xmlize($data, $skip_white = 1, $encoding = 'UTF-8') 815 { 816 $data = trim($data); 817 818 if (substr($data, 0, 5) != '<?xml') 819 { 820 // mod 821 $data = '<root>'. $data . '</root>'; 822 } 823 824 $vals = $index = $array = array(); 825 $parser = xml_parser_create($encoding); 826 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 827 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, $skip_white); 828 xml_parse_into_struct($parser, $data, $vals, $index); 829 xml_parser_free($parser); 830 831 $i = 0; 832 $tagname = $vals[$i]['tag']; 833 834 $array[$tagname][0]['@'] = (isset($vals[$i]['attributes'])) ? $vals[$i]['attributes'] : array(); 835 $array[$tagname][0]['#'] = $this->_xml_depth($vals, $i); 836 837 if (substr($data, 0, 5) != '<?xml') 838 { 839 $array = $array['root'][0]['#']; 840 } 841 842 return $array; 843 } 844 845 /** 846 * _xml_depth() 847 * @author Hans Anderson 848 * @copyright Hans Anderson / http://www.hansanderson.com/php/xml/ 849 */ 850 function _xml_depth($vals, &$i) 851 { 852 $children = array(); 853 854 if (isset($vals[$i]['value'])) 855 { 856 array_push($children, $vals[$i]['value']); 857 } 858 859 while (++$i < count($vals)) 860 { 861 switch ($vals[$i]['type']) 862 { 863 case 'open': 864 865 $tagname = (isset($vals[$i]['tag'])) ? $vals[$i]['tag'] : ''; 866 $size = (isset($children[$tagname])) ? count($children[$tagname]) : 0; 867 868 if (isset($vals[$i]['attributes'])) 869 { 870 $children[$tagname][$size]['@'] = $vals[$i]['attributes']; 871 } 872 873 $children[$tagname][$size]['#'] = $this->_xml_depth($vals, $i); 874 875 break; 876 877 case 'cdata': 878 array_push($children, $vals[$i]['value']); 879 break; 880 881 case 'complete': 882 883 $tagname = $vals[$i]['tag']; 884 $size = (isset($children[$tagname])) ? count($children[$tagname]) : 0; 885 $children[$tagname][$size]['#'] = (isset($vals[$i]['value'])) ? $vals[$i]['value'] : array(); 886 887 if (isset($vals[$i]['attributes'])) 888 { 889 $children[$tagname][$size]['@'] = $vals[$i]['attributes']; 890 } 891 892 break; 893 894 case 'close': 895 return $children; 896 break; 897 } 898 } 899 900 return $children; 901 } 902 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |