[ Index ] |
PHP Cross Reference of phpBB-3.1.12-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 namespace phpbb\db\driver; 15 16 /** 17 * Oracle Database Abstraction Layer 18 */ 19 class oracle extends \phpbb\db\driver\driver 20 { 21 var $last_query_text = ''; 22 var $connect_error = ''; 23 24 /** 25 * {@inheritDoc} 26 */ 27 function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) 28 { 29 $this->persistency = $persistency; 30 $this->user = $sqluser; 31 $this->server = $sqlserver . (($port) ? ':' . $port : ''); 32 $this->dbname = $database; 33 34 $connect = $database; 35 36 // support for "easy connect naming" 37 if ($sqlserver !== '' && $sqlserver !== '/') 38 { 39 if (substr($sqlserver, -1, 1) == '/') 40 { 41 $sqlserver == substr($sqlserver, 0, -1); 42 } 43 $connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database; 44 } 45 46 if ($new_link) 47 { 48 if (!function_exists('ocinlogon')) 49 { 50 $this->connect_error = 'ocinlogon function does not exist, is oci extension installed?'; 51 return $this->sql_error(''); 52 } 53 $this->db_connect_id = @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8'); 54 } 55 else if ($this->persistency) 56 { 57 if (!function_exists('ociplogon')) 58 { 59 $this->connect_error = 'ociplogon function does not exist, is oci extension installed?'; 60 return $this->sql_error(''); 61 } 62 $this->db_connect_id = @ociplogon($this->user, $sqlpassword, $connect, 'UTF8'); 63 } 64 else 65 { 66 if (!function_exists('ocilogon')) 67 { 68 $this->connect_error = 'ocilogon function does not exist, is oci extension installed?'; 69 return $this->sql_error(''); 70 } 71 $this->db_connect_id = @ocilogon($this->user, $sqlpassword, $connect, 'UTF8'); 72 } 73 74 return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); 75 } 76 77 /** 78 * {@inheritDoc} 79 */ 80 function sql_server_info($raw = false, $use_cache = true) 81 { 82 /** 83 * force $use_cache false. I didn't research why the caching code below is commented out 84 * but I assume its because the Oracle extension provides a direct method to access it 85 * without a query. 86 */ 87 88 $use_cache = false; 89 /* 90 global $cache; 91 92 if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false) 93 { 94 $result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\''); 95 @ociexecute($result, OCI_DEFAULT); 96 @ocicommit($this->db_connect_id); 97 98 $row = array(); 99 @ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS); 100 @ocifreestatement($result); 101 $this->sql_server_version = trim($row['BANNER']); 102 103 $cache->put('oracle_version', $this->sql_server_version); 104 } 105 */ 106 $this->sql_server_version = @ociserverversion($this->db_connect_id); 107 108 return $this->sql_server_version; 109 } 110 111 /** 112 * SQL Transaction 113 * @access private 114 */ 115 function _sql_transaction($status = 'begin') 116 { 117 switch ($status) 118 { 119 case 'begin': 120 return true; 121 break; 122 123 case 'commit': 124 return @ocicommit($this->db_connect_id); 125 break; 126 127 case 'rollback': 128 return @ocirollback($this->db_connect_id); 129 break; 130 } 131 132 return true; 133 } 134 135 /** 136 * Oracle specific code to handle the fact that it does not compare columns properly 137 * @access private 138 */ 139 function _rewrite_col_compare($args) 140 { 141 if (sizeof($args) == 4) 142 { 143 if ($args[2] == '=') 144 { 145 return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))'; 146 } 147 else if ($args[2] == '<>') 148 { 149 // really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P 150 return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))'; 151 } 152 } 153 else 154 { 155 return $this->_rewrite_where($args[0]); 156 } 157 } 158 159 /** 160 * Oracle specific code to handle it's lack of sanity 161 * @access private 162 */ 163 function _rewrite_where($where_clause) 164 { 165 preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER); 166 $out = ''; 167 foreach ($result as $val) 168 { 169 if (!isset($val[5])) 170 { 171 if ($val[4] !== "''") 172 { 173 $out .= $val[0]; 174 } 175 else 176 { 177 $out .= ' ' . $val[1] . ' ' . $val[2]; 178 if ($val[3] == '=') 179 { 180 $out .= ' is NULL'; 181 } 182 else if ($val[3] == '<>') 183 { 184 $out .= ' is NOT NULL'; 185 } 186 } 187 } 188 else 189 { 190 $in_clause = array(); 191 $sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1); 192 $extra = false; 193 preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER); 194 $i = 0; 195 foreach ($sub_vals[0] as $sub_val) 196 { 197 // two things: 198 // 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison 199 // 2) This fixes the 1000 list limit that Oracle has (ORA-01795) 200 if ($sub_val !== "''") 201 { 202 $in_clause[(int) $i++/1000][] = $sub_val; 203 } 204 else 205 { 206 $extra = true; 207 } 208 } 209 if (!$extra && $i < 1000) 210 { 211 $out .= $val[0]; 212 } 213 else 214 { 215 $out .= ' ' . $val[1] . '('; 216 $in_array = array(); 217 218 // constuct each IN() clause 219 foreach ($in_clause as $in_values) 220 { 221 $in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')'; 222 } 223 224 // Join the IN() clauses against a few ORs (IN is just a nicer OR anyway) 225 $out .= implode(' OR ', $in_array); 226 227 // handle the empty string case 228 if ($extra) 229 { 230 $out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL'; 231 } 232 $out .= ')'; 233 234 unset($in_array, $in_clause); 235 } 236 } 237 } 238 239 return $out; 240 } 241 242 /** 243 * {@inheritDoc} 244 */ 245 function sql_query($query = '', $cache_ttl = 0) 246 { 247 if ($query != '') 248 { 249 global $cache; 250 251 // EXPLAIN only in extra debug mode 252 if (defined('DEBUG')) 253 { 254 $this->sql_report('start', $query); 255 } 256 else if (defined('PHPBB_DISPLAY_LOAD_TIME')) 257 { 258 $this->curtime = microtime(true); 259 } 260 261 $this->last_query_text = $query; 262 $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; 263 $this->sql_add_num_queries($this->query_result); 264 265 if ($this->query_result === false) 266 { 267 $in_transaction = false; 268 if (!$this->transaction) 269 { 270 $this->sql_transaction('begin'); 271 } 272 else 273 { 274 $in_transaction = true; 275 } 276 277 $array = array(); 278 279 // We overcome Oracle's 4000 char limit by binding vars 280 if (strlen($query) > 4000) 281 { 282 if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs)) 283 { 284 if (strlen($regs[3]) > 4000) 285 { 286 $cols = explode(', ', $regs[2]); 287 288 preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER); 289 290 /* The code inside this comment block breaks clob handling, but does allow the 291 database restore script to work. If you want to allow no posts longer than 4KB 292 and/or need the db restore script, uncomment this. 293 294 295 if (sizeof($cols) !== sizeof($vals)) 296 { 297 // Try to replace some common data we know is from our restore script or from other sources 298 $regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]); 299 $_vals = explode(', ', $regs[3]); 300 301 $vals = array(); 302 $is_in_val = false; 303 $i = 0; 304 $string = ''; 305 306 foreach ($_vals as $value) 307 { 308 if (strpos($value, "'") === false && !$is_in_val) 309 { 310 $vals[$i++] = $value; 311 continue; 312 } 313 314 if (substr($value, -1) === "'") 315 { 316 $vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value; 317 $string = ''; 318 $is_in_val = false; 319 320 if ($vals[$i][0] !== "'") 321 { 322 $vals[$i] = "''" . $vals[$i]; 323 } 324 $i++; 325 continue; 326 } 327 else 328 { 329 $string .= (($is_in_val) ? ', ' : '') . $value; 330 $is_in_val = true; 331 } 332 } 333 334 if ($string) 335 { 336 // New value if cols != value 337 $vals[(sizeof($cols) !== sizeof($vals)) ? $i : $i - 1] .= $string; 338 } 339 340 $vals = array(0 => $vals); 341 } 342 */ 343 344 $inserts = $vals[0]; 345 unset($vals); 346 347 foreach ($inserts as $key => $value) 348 { 349 if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2 350 { 351 $inserts[$key] = ':' . strtoupper($cols[$key]); 352 $array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1)); 353 } 354 } 355 356 $query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')'; 357 } 358 } 359 else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER)) 360 { 361 if (strlen($data[0][2]) > 4000) 362 { 363 $update = $data[0][1]; 364 $where = $data[0][3]; 365 preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER); 366 unset($data); 367 368 $cols = array(); 369 foreach ($temp as $value) 370 { 371 if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2 372 { 373 $cols[] = $value[1] . '=:' . strtoupper($value[1]); 374 $array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1)); 375 } 376 else 377 { 378 $cols[] = $value[1] . '=' . $value[2]; 379 } 380 } 381 382 $query = $update . implode(', ', $cols) . ' ' . $where; 383 unset($cols); 384 } 385 } 386 } 387 388 switch (substr($query, 0, 6)) 389 { 390 case 'DELETE': 391 if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs)) 392 { 393 $query = $regs[1] . $this->_rewrite_where($regs[2]); 394 unset($regs); 395 } 396 break; 397 398 case 'UPDATE': 399 if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s', $query, $regs)) 400 { 401 $query = $regs[1] . $this->_rewrite_where($regs[2]); 402 unset($regs); 403 } 404 break; 405 406 case 'SELECT': 407 $query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query); 408 break; 409 } 410 411 $this->query_result = @ociparse($this->db_connect_id, $query); 412 413 foreach ($array as $key => $value) 414 { 415 @ocibindbyname($this->query_result, $key, $array[$key], -1); 416 } 417 418 $success = @ociexecute($this->query_result, OCI_DEFAULT); 419 420 if (!$success) 421 { 422 $this->sql_error($query); 423 $this->query_result = false; 424 } 425 else 426 { 427 if (!$in_transaction) 428 { 429 $this->sql_transaction('commit'); 430 } 431 } 432 433 if (defined('DEBUG')) 434 { 435 $this->sql_report('stop', $query); 436 } 437 else if (defined('PHPBB_DISPLAY_LOAD_TIME')) 438 { 439 $this->sql_time += microtime(true) - $this->curtime; 440 } 441 442 if ($cache && $cache_ttl) 443 { 444 $this->open_queries[(int) $this->query_result] = $this->query_result; 445 $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); 446 } 447 else if (strpos($query, 'SELECT') === 0 && $this->query_result) 448 { 449 $this->open_queries[(int) $this->query_result] = $this->query_result; 450 } 451 } 452 else if (defined('DEBUG')) 453 { 454 $this->sql_report('fromcache', $query); 455 } 456 } 457 else 458 { 459 return false; 460 } 461 462 return $this->query_result; 463 } 464 465 /** 466 * Build LIMIT query 467 */ 468 function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) 469 { 470 $this->query_result = false; 471 472 $query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset; 473 474 return $this->sql_query($query, $cache_ttl); 475 } 476 477 /** 478 * {@inheritDoc} 479 */ 480 function sql_affectedrows() 481 { 482 return ($this->query_result) ? @ocirowcount($this->query_result) : false; 483 } 484 485 /** 486 * {@inheritDoc} 487 */ 488 function sql_fetchrow($query_id = false) 489 { 490 global $cache; 491 492 if ($query_id === false) 493 { 494 $query_id = $this->query_result; 495 } 496 497 if ($cache && $cache->sql_exists($query_id)) 498 { 499 return $cache->sql_fetchrow($query_id); 500 } 501 502 if ($query_id !== false) 503 { 504 $row = array(); 505 $result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS); 506 507 if (!$result || !$row) 508 { 509 return false; 510 } 511 512 $result_row = array(); 513 foreach ($row as $key => $value) 514 { 515 // Oracle treats empty strings as null 516 if (is_null($value)) 517 { 518 $value = ''; 519 } 520 521 // OCI->CLOB? 522 if (is_object($value)) 523 { 524 $value = $value->load(); 525 } 526 527 $result_row[strtolower($key)] = $value; 528 } 529 530 return $result_row; 531 } 532 533 return false; 534 } 535 536 /** 537 * {@inheritDoc} 538 */ 539 function sql_rowseek($rownum, &$query_id) 540 { 541 global $cache; 542 543 if ($query_id === false) 544 { 545 $query_id = $this->query_result; 546 } 547 548 if ($cache && $cache->sql_exists($query_id)) 549 { 550 return $cache->sql_rowseek($rownum, $query_id); 551 } 552 553 if ($query_id === false) 554 { 555 return false; 556 } 557 558 // Reset internal pointer 559 @ociexecute($query_id, OCI_DEFAULT); 560 561 // We do not fetch the row for rownum == 0 because then the next resultset would be the second row 562 for ($i = 0; $i < $rownum; $i++) 563 { 564 if (!$this->sql_fetchrow($query_id)) 565 { 566 return false; 567 } 568 } 569 570 return true; 571 } 572 573 /** 574 * {@inheritDoc} 575 */ 576 function sql_nextid() 577 { 578 $query_id = $this->query_result; 579 580 if ($query_id !== false && $this->last_query_text != '') 581 { 582 if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename)) 583 { 584 $query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL'; 585 $stmt = @ociparse($this->db_connect_id, $query); 586 @ociexecute($stmt, OCI_DEFAULT); 587 588 $temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS); 589 @ocifreestatement($stmt); 590 591 if ($temp_result) 592 { 593 return $temp_array['CURRVAL']; 594 } 595 else 596 { 597 return false; 598 } 599 } 600 } 601 602 return false; 603 } 604 605 /** 606 * {@inheritDoc} 607 */ 608 function sql_freeresult($query_id = false) 609 { 610 global $cache; 611 612 if ($query_id === false) 613 { 614 $query_id = $this->query_result; 615 } 616 617 if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) 618 { 619 return $cache->sql_freeresult($query_id); 620 } 621 622 if (isset($this->open_queries[(int) $query_id])) 623 { 624 unset($this->open_queries[(int) $query_id]); 625 return @ocifreestatement($query_id); 626 } 627 628 return false; 629 } 630 631 /** 632 * {@inheritDoc} 633 */ 634 function sql_escape($msg) 635 { 636 return str_replace(array("'", "\0"), array("''", ''), $msg); 637 } 638 639 /** 640 * Build LIKE expression 641 * @access private 642 */ 643 function _sql_like_expression($expression) 644 { 645 return $expression . " ESCAPE '\\'"; 646 } 647 648 /** 649 * Build NOT LIKE expression 650 * @access private 651 */ 652 function _sql_not_like_expression($expression) 653 { 654 return $expression . " ESCAPE '\\'"; 655 } 656 657 function _sql_custom_build($stage, $data) 658 { 659 return $data; 660 } 661 662 function _sql_bit_and($column_name, $bit, $compare = '') 663 { 664 return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); 665 } 666 667 function _sql_bit_or($column_name, $bit, $compare = '') 668 { 669 return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); 670 } 671 672 /** 673 * return sql error array 674 * @access private 675 */ 676 function _sql_error() 677 { 678 if (function_exists('ocierror')) 679 { 680 $error = @ocierror(); 681 $error = (!$error) ? @ocierror($this->query_result) : $error; 682 $error = (!$error) ? @ocierror($this->db_connect_id) : $error; 683 684 if ($error) 685 { 686 $this->last_error_result = $error; 687 } 688 else 689 { 690 $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array(); 691 } 692 } 693 else 694 { 695 $error = array( 696 'message' => $this->connect_error, 697 'code' => '', 698 ); 699 } 700 701 return $error; 702 } 703 704 /** 705 * Close sql connection 706 * @access private 707 */ 708 function _sql_close() 709 { 710 return @ocilogoff($this->db_connect_id); 711 } 712 713 /** 714 * Build db-specific report 715 * @access private 716 */ 717 function _sql_report($mode, $query = '') 718 { 719 switch ($mode) 720 { 721 case 'start': 722 723 $html_table = false; 724 725 // Grab a plan table, any will do 726 $sql = "SELECT table_name 727 FROM USER_TABLES 728 WHERE table_name LIKE '%PLAN_TABLE%'"; 729 $stmt = ociparse($this->db_connect_id, $sql); 730 ociexecute($stmt); 731 $result = array(); 732 733 if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS)) 734 { 735 $table = $result['TABLE_NAME']; 736 737 // This is the statement_id that will allow us to track the plan 738 $statement_id = substr(md5($query), 0, 30); 739 740 // Remove any stale plans 741 $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'"); 742 ociexecute($stmt2); 743 ocifreestatement($stmt2); 744 745 // Explain the plan 746 $sql = "EXPLAIN PLAN 747 SET STATEMENT_ID = '$statement_id' 748 FOR $query"; 749 $stmt2 = ociparse($this->db_connect_id, $sql); 750 ociexecute($stmt2); 751 ocifreestatement($stmt2); 752 753 // Get the data from the plan 754 $sql = "SELECT operation, options, object_name, object_type, cardinality, cost 755 FROM plan_table 756 START WITH id = 0 AND statement_id = '$statement_id' 757 CONNECT BY PRIOR id = parent_id 758 AND statement_id = '$statement_id'"; 759 $stmt2 = ociparse($this->db_connect_id, $sql); 760 ociexecute($stmt2); 761 762 $row = array(); 763 while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS)) 764 { 765 $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); 766 } 767 768 ocifreestatement($stmt2); 769 770 // Remove the plan we just made, we delete them on request anyway 771 $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'"); 772 ociexecute($stmt2); 773 ocifreestatement($stmt2); 774 } 775 776 ocifreestatement($stmt); 777 778 if ($html_table) 779 { 780 $this->html_hold .= '</table>'; 781 } 782 783 break; 784 785 case 'fromcache': 786 $endtime = explode(' ', microtime()); 787 $endtime = $endtime[0] + $endtime[1]; 788 789 $result = @ociparse($this->db_connect_id, $query); 790 $success = @ociexecute($result, OCI_DEFAULT); 791 $row = array(); 792 793 while (@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS)) 794 { 795 // Take the time spent on parsing rows into account 796 } 797 @ocifreestatement($result); 798 799 $splittime = explode(' ', microtime()); 800 $splittime = $splittime[0] + $splittime[1]; 801 802 $this->sql_report('record_fromcache', $query, $endtime, $splittime); 803 804 break; 805 } 806 } 807 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |