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