[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/database/src/ -> DatabaseDriver.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Database Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Database;
  10  
  11  use Joomla\Database\Event\ConnectionEvent;
  12  use Joomla\Database\Exception\ConnectionFailureException;
  13  use Joomla\Database\Exception\ExecutionFailureException;
  14  use Joomla\Database\Exception\PrepareStatementFailureException;
  15  use Joomla\Event\DispatcherAwareInterface;
  16  use Joomla\Event\DispatcherAwareTrait;
  17  use Joomla\Event\EventInterface;
  18  
  19  /**
  20   * Joomla Framework Database Driver Class
  21   *
  22   * @since  1.0
  23   */
  24  abstract class DatabaseDriver implements DatabaseInterface, DispatcherAwareInterface
  25  {
  26      use DispatcherAwareTrait;
  27  
  28      /**
  29       * The name of the database.
  30       *
  31       * @var    string
  32       * @since  1.0
  33       */
  34      private $database;
  35  
  36      /**
  37       * The name of the database driver.
  38       *
  39       * @var    string
  40       * @since  1.0
  41       */
  42      protected $name;
  43  
  44      /**
  45       * The type of the database server family supported by this driver.
  46       *
  47       * @var    string
  48       * @since  1.4.0
  49       */
  50      public $serverType;
  51  
  52      /**
  53       * The database connection resource.
  54       *
  55       * @var    resource
  56       * @since  1.0
  57       */
  58      protected $connection;
  59  
  60      /**
  61       * Holds the list of available db connectors.
  62       *
  63       * @var    array
  64       * @since  1.0
  65       */
  66      protected static $connectors = [];
  67  
  68      /**
  69       * The number of SQL statements executed by the database driver.
  70       *
  71       * @var    integer
  72       * @since  1.0
  73       */
  74      protected $count = 0;
  75  
  76      /**
  77       * The database connection cursor from the last query.
  78       *
  79       * @var    resource
  80       * @since  1.0
  81       */
  82      protected $cursor;
  83  
  84      /**
  85       * Contains the current query execution status
  86       *
  87       * @var    boolean
  88       * @since  2.0.0
  89       */
  90      protected $executed = false;
  91  
  92      /**
  93       * The affected row limit for the current SQL statement.
  94       *
  95       * @var    integer
  96       * @since  1.0
  97       */
  98      protected $limit = 0;
  99  
 100      /**
 101       * The character(s) used to quote SQL statement names such as table names or field names, etc.
 102       *
 103       * If a single character string the same character is used for both sides of the quoted name, else the first character will be used for the
 104       * opening quote and the second for the closing quote.
 105       *
 106       * @var    string
 107       * @since  1.0
 108       */
 109      protected $nameQuote;
 110  
 111      /**
 112       * The null or zero representation of a timestamp for the database driver.
 113       *
 114       * @var    string
 115       * @since  1.0
 116       */
 117      protected $nullDate;
 118  
 119      /**
 120       * The affected row offset to apply for the current SQL statement.
 121       *
 122       * @var    integer
 123       * @since  1.0
 124       */
 125      protected $offset = 0;
 126  
 127      /**
 128       * Passed in upon instantiation and saved.
 129       *
 130       * @var    array
 131       * @since  1.0
 132       */
 133      protected $options;
 134  
 135      /**
 136       * The current SQL statement to execute.
 137       *
 138       * @var    mixed
 139       * @since  1.0
 140       */
 141      protected $sql;
 142  
 143      /**
 144       * The prepared statement.
 145       *
 146       * @var    StatementInterface
 147       * @since  2.0.0
 148       */
 149      protected $statement;
 150  
 151      /**
 152       * The common database table prefix.
 153       *
 154       * @var    string
 155       * @since  1.0
 156       */
 157      protected $tablePrefix;
 158  
 159      /**
 160       * True if the database engine supports UTF-8 character encoding.
 161       *
 162       * @var    boolean
 163       * @since  1.0
 164       */
 165      protected $utf = true;
 166  
 167      /**
 168       * The database error number.
 169       *
 170       * @var    integer
 171       * @since  1.0
 172       */
 173      protected $errorNum = 0;
 174  
 175      /**
 176       * The database error message.
 177       *
 178       * @var    string
 179       * @since  1.0
 180       */
 181      protected $errorMsg;
 182  
 183      /**
 184       * DatabaseDriver instances container.
 185       *
 186       * @var    DatabaseDriver[]
 187       * @since  1.0
 188       * @deprecated  3.0  Singleton storage will no longer be supported.
 189       */
 190      protected static $instances = [];
 191  
 192      /**
 193       * The minimum supported database version.
 194       *
 195       * @var    string
 196       * @since  1.0
 197       */
 198      protected static $dbMinimum;
 199  
 200      /**
 201       * The depth of the current transaction.
 202       *
 203       * @var    integer
 204       * @since  1.0
 205       */
 206      protected $transactionDepth = 0;
 207  
 208      /**
 209       * DatabaseFactory object
 210       *
 211       * @var    DatabaseFactory
 212       * @since  2.0.0
 213       */
 214      protected $factory;
 215  
 216      /**
 217       * Query monitor object
 218       *
 219       * @var    QueryMonitorInterface
 220       * @since  2.0.0
 221       */
 222      protected $monitor;
 223  
 224      /**
 225       * Get a list of available database connectors.
 226       *
 227       * The list will only be populated with connectors that the class exists for and the environment supports its use.
 228       * This gives us the ability to have a multitude of connector classes that are self-aware as to whether or not they
 229       * are able to be used on a given system.
 230       *
 231       * @return  array  An array of available database connectors.
 232       *
 233       * @since   1.0
 234       */
 235  	public static function getConnectors()
 236      {
 237          if (empty(self::$connectors))
 238          {
 239              // Get an iterator and loop trough the driver classes.
 240              $dir      = __DIR__;
 241              $iterator = new \DirectoryIterator($dir);
 242  
 243              /** @var $file \DirectoryIterator */
 244              foreach ($iterator as $file)
 245              {
 246                  // Only load for php files.
 247                  if (!$file->isDir())
 248                  {
 249                      continue;
 250                  }
 251  
 252                  $baseName = $file->getBasename();
 253  
 254                  // Derive the class name from the type.
 255                  /** @var $class DatabaseDriver */
 256                  $class = __NAMESPACE__ . '\\' . ucfirst(strtolower($baseName)) . '\\' . ucfirst(strtolower($baseName)) . 'Driver';
 257  
 258                  // If the class doesn't exist, or if it's not supported on this system, move on to the next type.
 259                  if (!class_exists($class) || !$class::isSupported())
 260                  {
 261                      continue;
 262                  }
 263  
 264                  // Everything looks good, add it to the list.
 265                  self::$connectors[] = $baseName;
 266              }
 267          }
 268  
 269          return self::$connectors;
 270      }
 271  
 272      /**
 273       * Method to return a DatabaseDriver instance based on the given options.
 274       *
 275       * There are three global options and then the rest are specific to the database driver.
 276       *
 277       * - The 'driver' option defines which DatabaseDriver class is used for the connection -- the default is 'mysqli'.
 278       * - The 'database' option determines which database is to be used for the connection.
 279       * - The 'select' option determines whether the connector should automatically select the chosen database.
 280       *
 281       * Instances are unique to the given options and new objects are only created when a unique options array is
 282       * passed into the method.  This ensures that we don't end up with unnecessary database connection resources.
 283       *
 284       * @param   array  $options  Parameters to be passed to the database driver.
 285       *
 286       * @return  DatabaseDriver
 287       *
 288       * @since   1.0
 289       * @throws  \RuntimeException
 290       * @deprecated  3.0  Use DatabaseFactory::getDriver() instead
 291       */
 292  	public static function getInstance(array $options = [])
 293      {
 294          trigger_deprecation(
 295              'joomla/database',
 296              '2.0.0',
 297              '%s() is deprecated and will be removed in 3.0, use %s::getDriver() instead.',
 298              __METHOD__,
 299              DatabaseFactory::class
 300          );
 301  
 302          // Sanitize the database connector options.
 303          $options['driver']   = isset($options['driver']) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli';
 304          $options['database'] = $options['database'] ?? null;
 305          $options['select']   = $options['select'] ?? true;
 306          $options['factory']  = $options['factory'] ?? new DatabaseFactory;
 307          $options['monitor']  = $options['monitor'] ?? null;
 308  
 309          // Get the options signature for the database connector.
 310          $signature = md5(serialize($options));
 311  
 312          // If we already have a database connector instance for these options then just use that.
 313          if (empty(self::$instances[$signature]))
 314          {
 315              // Set the new connector to the global instances based on signature.
 316              self::$instances[$signature] = $options['factory']->getDriver($options['driver'], $options);
 317          }
 318  
 319          return self::$instances[$signature];
 320      }
 321  
 322      /**
 323       * Splits a string of multiple queries into an array of individual queries.
 324       *
 325       * @param   string  $sql  Input SQL string with which to split into individual queries.
 326       *
 327       * @return  array
 328       *
 329       * @since   1.0
 330       */
 331  	public static function splitSql($sql)
 332      {
 333          $start     = 0;
 334          $open      = false;
 335          $comment   = false;
 336          $endString = '';
 337          $end       = \strlen($sql);
 338          $queries   = [];
 339          $query     = '';
 340  
 341          for ($i = 0; $i < $end; $i++)
 342          {
 343              $current      = substr($sql, $i, 1);
 344              $current2     = substr($sql, $i, 2);
 345              $current3     = substr($sql, $i, 3);
 346              $lenEndString = \strlen($endString);
 347              $testEnd      = substr($sql, $i, $lenEndString);
 348  
 349              if ($current === '"' || $current === "'" || $current2 === '--'
 350                  || ($current2 === '/*' && $current3 !== '/*!' && $current3 !== '/*+')
 351                  || ($current === '#' && $current3 !== '#__')
 352                  || ($comment && $testEnd === $endString))
 353              {
 354                  // Check if quoted with previous backslash
 355                  $n = 2;
 356  
 357                  while (substr($sql, $i - $n + 1, 1) === '\\' && $n < $i)
 358                  {
 359                      $n++;
 360                  }
 361  
 362                  // Not quoted
 363                  if ($n % 2 === 0)
 364                  {
 365                      if ($open)
 366                      {
 367                          if ($testEnd === $endString)
 368                          {
 369                              if ($comment)
 370                              {
 371                                  $comment = false;
 372  
 373                                  if ($lenEndString > 1)
 374                                  {
 375                                      $i += ($lenEndString - 1);
 376                                      $current = substr($sql, $i, 1);
 377                                  }
 378  
 379                                  $start = $i + 1;
 380                              }
 381  
 382                              $open      = false;
 383                              $endString = '';
 384                          }
 385                      }
 386                      else
 387                      {
 388                          $open = true;
 389  
 390                          if ($current2 === '--')
 391                          {
 392                              $endString = "\n";
 393                              $comment   = true;
 394                          }
 395                          elseif ($current2 === '/*')
 396                          {
 397                              $endString = '*/';
 398                              $comment   = true;
 399                          }
 400                          elseif ($current === '#')
 401                          {
 402                              $endString = "\n";
 403                              $comment   = true;
 404                          }
 405                          else
 406                          {
 407                              $endString = $current;
 408                          }
 409  
 410                          if ($comment && $start < $i)
 411                          {
 412                              $query .= substr($sql, $start, $i - $start);
 413                          }
 414                      }
 415                  }
 416              }
 417  
 418              if ($comment)
 419              {
 420                  $start = $i + 1;
 421              }
 422  
 423              if (($current === ';' && !$open) || $i === $end - 1)
 424              {
 425                  if ($start <= $i)
 426                  {
 427                      $query .= substr($sql, $start, $i - $start + 1);
 428                  }
 429  
 430                  $query = trim($query);
 431  
 432                  if ($query)
 433                  {
 434                      if (($i === $end - 1) && ($current !== ';'))
 435                      {
 436                          $query .= ';';
 437                      }
 438  
 439                      $queries[] = $query;
 440                  }
 441  
 442                  $query = '';
 443                  $start = $i + 1;
 444              }
 445  
 446              $endComment = false;
 447          }
 448  
 449          return $queries;
 450      }
 451  
 452      /**
 453       * Magic method to access properties of the database driver.
 454       *
 455       * @param   string  $name  The name of the property.
 456       *
 457       * @return  mixed   A value if the property name is valid, null otherwise.
 458       *
 459       * @since       1.4.0
 460       * @deprecated  3.0  This is a B/C proxy since $this->name was previously public
 461       */
 462  	public function __get($name)
 463      {
 464          switch ($name)
 465          {
 466              case 'name':
 467                  trigger_deprecation(
 468                      'joomla/database',
 469                      '1.4.0',
 470                      'Accessing the name property of %s is deprecated, use the getName() method instead.',
 471                      self::class
 472                  );
 473  
 474                  return $this->getName();
 475  
 476              default:
 477                  $trace = debug_backtrace();
 478                  trigger_error(
 479                      sprintf(
 480                          'Undefined property via __get(): %1$s in %2$s on line %3$s',
 481                          $name,
 482                          $trace[0]['file'],
 483                          $trace[0]['line']
 484                      ),
 485                      \E_USER_NOTICE
 486                  );
 487          }
 488      }
 489  
 490      /**
 491       * Constructor.
 492       *
 493       * @param   array  $options  List of options used to configure the connection
 494       *
 495       * @since   1.0
 496       */
 497  	public function __construct(array $options)
 498      {
 499          // Initialise object variables.
 500          $this->database    = $options['database'] ?? '';
 501          $this->tablePrefix = $options['prefix'] ?? '';
 502          $this->count       = 0;
 503          $this->errorNum    = 0;
 504  
 505          // Set class options.
 506          $this->options = $options;
 507  
 508          // Register the DatabaseFactory
 509          $this->factory = $options['factory'] ?? new DatabaseFactory;
 510  
 511          // Register the query monitor if available
 512          $this->monitor = $options['monitor'] ?? null;
 513      }
 514  
 515      /**
 516       * Destructor.
 517       *
 518       * @since   2.0.0
 519       */
 520  	public function __destruct()
 521      {
 522          $this->disconnect();
 523      }
 524  
 525      /**
 526       * Alter database's character set.
 527       *
 528       * @param   string  $dbName  The database name that will be altered
 529       *
 530       * @return  boolean|resource
 531       *
 532       * @since   2.0.0
 533       * @throws  \RuntimeException
 534       */
 535  	public function alterDbCharacterSet($dbName)
 536      {
 537          if ($dbName === null)
 538          {
 539              throw new \RuntimeException('Database name must not be null.');
 540          }
 541  
 542          $this->setQuery($this->getAlterDbCharacterSet($dbName));
 543  
 544          return $this->execute();
 545      }
 546  
 547      /**
 548       * Create a new database using information from $options object.
 549       *
 550       * @param   \stdClass  $options  Object used to pass user and database name to database driver. This object must have "db_name" and "db_user" set.
 551       * @param   boolean    $utf      True if the database supports the UTF-8 character set.
 552       *
 553       * @return  boolean|resource
 554       *
 555       * @since   2.0.0
 556       * @throws  \RuntimeException
 557       */
 558  	public function createDatabase($options, $utf = true)
 559      {
 560          if ($options === null)
 561          {
 562              throw new \RuntimeException('$options object must not be null.');
 563          }
 564  
 565          if (empty($options->db_name))
 566          {
 567              throw new \RuntimeException('$options object must have db_name set.');
 568          }
 569  
 570          if (empty($options->db_user))
 571          {
 572              throw new \RuntimeException('$options object must have db_user set.');
 573          }
 574  
 575          $this->setQuery($this->getCreateDatabaseQuery($options, $utf));
 576  
 577          return $this->execute();
 578      }
 579  
 580      /**
 581       * Disconnects the database.
 582       *
 583       * @return  void
 584       *
 585       * @since   2.0.0
 586       */
 587  	public function disconnect()
 588      {
 589          $this->freeResult();
 590          $this->connection = null;
 591  
 592          $this->dispatchEvent(new ConnectionEvent(DatabaseEvents::POST_DISCONNECT, $this));
 593      }
 594  
 595      /**
 596       * Dispatch an event.
 597       *
 598       * @param   EventInterface  $event  The event to dispatch
 599       *
 600       * @return  void
 601       *
 602       * @since   2.0.0
 603       */
 604  	protected function dispatchEvent(EventInterface $event)
 605      {
 606          try
 607          {
 608              $this->getDispatcher()->dispatch($event->getName(), $event);
 609          }
 610          catch (\UnexpectedValueException $exception)
 611          {
 612              // Don't error if a dispatcher hasn't been set
 613          }
 614      }
 615  
 616      /**
 617       * Drops a table from the database.
 618       *
 619       * @param   string   $table     The name of the database table to drop.
 620       * @param   boolean  $ifExists  Optionally specify that the table must exist before it is dropped.
 621       *
 622       * @return  $this
 623       *
 624       * @since   2.0.0
 625       * @throws  \RuntimeException
 626       */
 627  	public function dropTable($table, $ifExists = true)
 628      {
 629          $this->connect();
 630  
 631          $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($table))
 632              ->execute();
 633  
 634          return $this;
 635      }
 636  
 637      /**
 638       * Execute the SQL statement.
 639       *
 640       * @return  boolean
 641       *
 642       * @since   2.0.0
 643       * @throws  \RuntimeException
 644       */
 645  	public function execute()
 646      {
 647          $this->connect();
 648  
 649          // Increment the query counter.
 650          $this->count++;
 651  
 652          // Get list of bound parameters
 653          $bounded =& $this->sql->getBounded();
 654  
 655          // If there is a monitor registered, let it know we are starting this query
 656          if ($this->monitor)
 657          {
 658              // Take a local copy so that we don't modify the original query and cause issues later
 659              $sql = $this->replacePrefix((string) $this->sql);
 660  
 661              $this->monitor->startQuery($sql, $bounded);
 662          }
 663  
 664          // Execute the query.
 665          $this->executed = false;
 666  
 667          // Bind the variables
 668          foreach ($bounded as $key => $obj)
 669          {
 670              $this->statement->bindParam($key, $obj->value, $obj->dataType);
 671          }
 672  
 673          try
 674          {
 675              $this->executed = $this->statement->execute();
 676  
 677              // If there is a monitor registered, let it know we have finished this query
 678              if ($this->monitor)
 679              {
 680                  $this->monitor->stopQuery();
 681              }
 682  
 683              return true;
 684          }
 685          catch (ExecutionFailureException $exception)
 686          {
 687              // If there is a monitor registered, let it know we have finished this query
 688              if ($this->monitor)
 689              {
 690                  $this->monitor->stopQuery();
 691              }
 692  
 693              // Check if the server was disconnected.
 694              if (!$this->connected())
 695              {
 696                  try
 697                  {
 698                      // Attempt to reconnect.
 699                      $this->connection = null;
 700                      $this->connect();
 701                  }
 702                  catch (ConnectionFailureException $e)
 703                  {
 704                      // If connect fails, ignore that exception and throw the normal exception.
 705                      throw $exception;
 706                  }
 707  
 708                  // Since we were able to reconnect, run the query again.
 709                  return $this->execute();
 710              }
 711  
 712              // Throw the normal query exception.
 713              throw $exception;
 714          }
 715      }
 716  
 717      /**
 718       * Method to fetch a row from the result set cursor as an array.
 719       *
 720       * @return  mixed  Either the next row from the result set or false if there are no more rows.
 721       *
 722       * @since   1.0
 723       */
 724  	protected function fetchArray()
 725      {
 726          if ($this->statement)
 727          {
 728              return $this->statement->fetch(FetchMode::NUMERIC);
 729          }
 730      }
 731  
 732      /**
 733       * Method to fetch a row from the result set cursor as an associative array.
 734       *
 735       * @return  mixed  Either the next row from the result set or false if there are no more rows.
 736       *
 737       * @since   1.0
 738       */
 739  	protected function fetchAssoc()
 740      {
 741          if ($this->statement)
 742          {
 743              return $this->statement->fetch(FetchMode::ASSOCIATIVE);
 744          }
 745      }
 746  
 747      /**
 748       * Method to fetch a row from the result set cursor as an object.
 749       *
 750       * Note, the fetch mode should be configured before calling this method using `StatementInterface::setFetchMode()`.
 751       *
 752       * @return  mixed   Either the next row from the result set or false if there are no more rows.
 753       *
 754       * @since   1.0
 755       */
 756  	protected function fetchObject()
 757      {
 758          if ($this->statement)
 759          {
 760              return $this->statement->fetch();
 761          }
 762      }
 763  
 764      /**
 765       * Method to free up the memory used for the result set.
 766       *
 767       * @return  void
 768       *
 769       * @since   1.0
 770       */
 771  	protected function freeResult()
 772      {
 773          $this->executed = false;
 774  
 775          if ($this->statement)
 776          {
 777              $this->statement->closeCursor();
 778          }
 779      }
 780  
 781      /**
 782       * Get the number of affected rows for the previous executed SQL statement.
 783       *
 784       * @return  integer  The number of affected rows in the previous operation
 785       *
 786       * @since   2.0.0
 787       */
 788  	public function getAffectedRows()
 789      {
 790          $this->connect();
 791  
 792          if ($this->statement)
 793          {
 794              return $this->statement->rowCount();
 795          }
 796  
 797          return 0;
 798      }
 799  
 800      /**
 801       * Method that provides access to the underlying database connection.
 802       *
 803       * @return  resource  The underlying database connection resource.
 804       *
 805       * @since   1.0
 806       */
 807  	public function getConnection()
 808      {
 809          return $this->connection;
 810      }
 811  
 812      /**
 813       * Get the total number of SQL statements executed by the database driver.
 814       *
 815       * @return  integer
 816       *
 817       * @since   1.0
 818       */
 819  	public function getCount()
 820      {
 821          return $this->count;
 822      }
 823  
 824      /**
 825       * Return the query string to alter the database character set.
 826       *
 827       * @param   string  $dbName  The database name
 828       *
 829       * @return  string  The query that alter the database query string
 830       *
 831       * @since   1.6.0
 832       */
 833  	protected function getAlterDbCharacterSet($dbName)
 834      {
 835          return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET ' . $this->quote('UTF8');
 836      }
 837  
 838      /**
 839       * Return the query string to create new Database.
 840       *
 841       * @param   stdClass  $options  Object used to pass user and database name to database driver. This object must have "db_name" and "db_user" set.
 842       * @param   boolean   $utf      True if the database supports the UTF-8 character set.
 843       *
 844       * @return  string  The query that creates database
 845       *
 846       * @since   2.0.0
 847       */
 848  	protected function getCreateDatabaseQuery($options, $utf)
 849      {
 850          return 'CREATE DATABASE ' . $this->quoteName($options->db_name);
 851      }
 852  
 853      /**
 854       * Gets the name of the database used by this connection.
 855       *
 856       * @return  string
 857       *
 858       * @since   1.0
 859       */
 860  	protected function getDatabase()
 861      {
 862          return $this->database;
 863      }
 864  
 865      /**
 866       * Returns a PHP date() function compliant date format for the database driver.
 867       *
 868       * @return  string
 869       *
 870       * @since   1.0
 871       */
 872  	public function getDateFormat()
 873      {
 874          return 'Y-m-d H:i:s';
 875      }
 876  
 877      /**
 878       * Get the minimum supported database version.
 879       *
 880       * @return  string
 881       *
 882       * @since   1.0
 883       */
 884  	public function getMinimum()
 885      {
 886          return static::$dbMinimum;
 887      }
 888  
 889      /**
 890       * Get the name of the database driver.
 891       *
 892       * If $this->name is not set it will try guessing the driver name from the class name.
 893       *
 894       * @return  string
 895       *
 896       * @since   1.4.0
 897       */
 898  	public function getName()
 899      {
 900          if (empty($this->name))
 901          {
 902              $reflect = new \ReflectionClass($this);
 903  
 904              $this->name = strtolower(str_replace('Driver', '', $reflect->getShortName()));
 905          }
 906  
 907          return $this->name;
 908      }
 909  
 910      /**
 911       * Get the number of returned rows for the previous executed SQL statement.
 912       *
 913       * @return  integer   The number of returned rows.
 914       *
 915       * @since   2.0.0
 916       */
 917  	public function getNumRows()
 918      {
 919          $this->connect();
 920  
 921          if ($this->statement)
 922          {
 923              return $this->statement->rowCount();
 924          }
 925  
 926          return 0;
 927      }
 928  
 929      /**
 930       * Get the server family type.
 931       *
 932       * If $this->serverType is not set it will attempt guessing the server family type from the driver name. If this is not possible the driver
 933       * name will be returned instead.
 934       *
 935       * @return  string
 936       *
 937       * @since   1.4.0
 938       */
 939  	public function getServerType()
 940      {
 941          if (empty($this->serverType))
 942          {
 943              $name = $this->getName();
 944  
 945              if (stristr($name, 'mysql') !== false)
 946              {
 947                  $this->serverType = 'mysql';
 948              }
 949              elseif (stristr($name, 'postgre') !== false)
 950              {
 951                  $this->serverType = 'postgresql';
 952              }
 953              elseif (stristr($name, 'pgsql') !== false)
 954              {
 955                  $this->serverType = 'postgresql';
 956              }
 957              elseif (stristr($name, 'oracle') !== false)
 958              {
 959                  $this->serverType = 'oracle';
 960              }
 961              elseif (stristr($name, 'sqlite') !== false)
 962              {
 963                  $this->serverType = 'sqlite';
 964              }
 965              elseif (stristr($name, 'sqlsrv') !== false)
 966              {
 967                  $this->serverType = 'mssql';
 968              }
 969              elseif (stristr($name, 'sqlazure') !== false)
 970              {
 971                  $this->serverType = 'mssql';
 972              }
 973              elseif (stristr($name, 'mssql') !== false)
 974              {
 975                  $this->serverType = 'mssql';
 976              }
 977              else
 978              {
 979                  $this->serverType = $name;
 980              }
 981          }
 982  
 983          return $this->serverType;
 984      }
 985  
 986      /**
 987       * Get the null or zero representation of a timestamp for the database driver.
 988       *
 989       * @return  string
 990       *
 991       * @since   1.0
 992       */
 993  	public function getNullDate()
 994      {
 995          return $this->nullDate;
 996      }
 997  
 998      /**
 999       * Get the common table prefix for the database driver.
1000       *
1001       * @return  string  The common database table prefix.
1002       *
1003       * @since   1.0
1004       */
1005  	public function getPrefix()
1006      {
1007          return $this->tablePrefix;
1008      }
1009  
1010      /**
1011       * Gets an exporter class object.
1012       *
1013       * @return  DatabaseExporter  An exporter object.
1014       *
1015       * @since   1.0
1016       * @throws  \RuntimeException
1017       */
1018  	public function getExporter()
1019      {
1020          return $this->factory->getExporter($this->name, $this);
1021      }
1022  
1023      /**
1024       * Gets an importer class object.
1025       *
1026       * @return  DatabaseImporter
1027       *
1028       * @since   1.0
1029       */
1030  	public function getImporter()
1031      {
1032          return $this->factory->getImporter($this->name, $this);
1033      }
1034  
1035      /**
1036       * Get the current query object or a new DatabaseQuery object.
1037       *
1038       * @param   boolean  $new  False to return the current query object, True to return a new DatabaseQuery object.
1039       *
1040       * @return  DatabaseQuery
1041       *
1042       * @since   1.0
1043       */
1044  	public function getQuery($new = false)
1045      {
1046          if ($new)
1047          {
1048              return $this->factory->getQuery($this->name, $this);
1049          }
1050  
1051          return $this->sql;
1052      }
1053  
1054      /**
1055       * Get a new iterator on the current query.
1056       *
1057       * @param   string  $column  An option column to use as the iterator key.
1058       * @param   string  $class   The class of object that is returned.
1059       *
1060       * @return  DatabaseIterator
1061       *
1062       * @since   1.0
1063       */
1064  	public function getIterator($column = null, $class = \stdClass::class)
1065      {
1066          if (!$this->executed)
1067          {
1068              $this->execute();
1069          }
1070  
1071          /**
1072           * Calling setQuery free's the statement from the iterator which will break the iterator.
1073           * So we set statement to null so that freeResult on the statement here has no affect.
1074           * If you unset the iterator object then that will close the cursor and free the result.
1075           */
1076          $iterator = $this->factory->getIterator($this->name, $this->statement, $column, $class);
1077          $this->statement = null;
1078  
1079          return $iterator;
1080      }
1081  
1082      /**
1083       * Shows the table CREATE statement that creates the given tables.
1084       *
1085       * @param   mixed  $tables  A table name or a list of table names.
1086       *
1087       * @return  array  A list of the create SQL for the tables.
1088       *
1089       * @since   1.0
1090       * @throws  \RuntimeException
1091       */
1092      abstract public function getTableCreate($tables);
1093  
1094      /**
1095       * Determine whether or not the database engine supports UTF-8 character encoding.
1096       *
1097       * @return  boolean  True if the database engine supports UTF-8 character encoding.
1098       *
1099       * @since   1.0
1100       */
1101  	public function hasUtfSupport()
1102      {
1103          return $this->utf;
1104      }
1105  
1106      /**
1107       * Inserts a row into a table based on an object's properties.
1108       *
1109       * @param   string  $table   The name of the database table to insert into.
1110       * @param   object  $object  A reference to an object whose public properties match the table fields.
1111       * @param   string  $key     The name of the primary key. If provided the object property is updated.
1112       *
1113       * @return  boolean
1114       *
1115       * @since   1.0
1116       * @throws  \RuntimeException
1117       */
1118  	public function insertObject($table, &$object, $key = null)
1119      {
1120          $fields       = [];
1121          $values       = [];
1122          $tableColumns = $this->getTableColumns($table);
1123  
1124          // Iterate over the object variables to build the query fields and values.
1125          foreach (get_object_vars($object) as $k => $v)
1126          {
1127              // Skip columns that don't exist in the table.
1128              if (!\array_key_exists($k, $tableColumns))
1129              {
1130                  continue;
1131              }
1132  
1133              // Only process non-null scalars.
1134              if (\is_array($v) || \is_object($v) || $v === null)
1135              {
1136                  continue;
1137              }
1138  
1139              // Ignore any internal fields.
1140              if ($k[0] === '_')
1141              {
1142                  continue;
1143              }
1144  
1145              // Prepare and sanitize the fields and values for the database query.
1146              $fields[] = $this->quoteName($k);
1147              $values[] = $this->quote($v);
1148          }
1149  
1150          // Create the base insert statement.
1151          $query = $this->getQuery(true)
1152              ->insert($this->quoteName($table))
1153              ->columns($fields)
1154              ->values(implode(',', $values));
1155  
1156          // Set the query and execute the insert.
1157          $this->setQuery($query)->execute();
1158  
1159          // Update the primary key if it exists.
1160          $id = $this->insertid();
1161  
1162          if ($key && $id && \is_string($key))
1163          {
1164              $object->$key = $id;
1165          }
1166  
1167          return true;
1168      }
1169  
1170      /**
1171       * Method to check whether the installed database version is supported by the database driver
1172       *
1173       * @return  boolean  True if the database version is supported
1174       *
1175       * @since   1.0
1176       */
1177  	public function isMinimumVersion()
1178      {
1179          return version_compare($this->getVersion(), $this->getMinimum()) >= 0;
1180      }
1181  
1182      /**
1183       * Method to get the first row of the result set from the database query as an associative array of ['field_name' => 'row_value'].
1184       *
1185       * @return  mixed  The return value or null if the query failed.
1186       *
1187       * @since   1.0
1188       * @throws  \RuntimeException
1189       */
1190  	public function loadAssoc()
1191      {
1192          $this->connect();
1193  
1194          $ret = null;
1195  
1196          // Execute the query and get the result set cursor.
1197          $this->execute();
1198  
1199          // Get the first row from the result set as an associative array.
1200          $array = $this->fetchAssoc();
1201  
1202          if ($array)
1203          {
1204              $ret = $array;
1205          }
1206  
1207          // Free up system resources and return.
1208          $this->freeResult();
1209  
1210          return $ret;
1211      }
1212  
1213      /**
1214       * Method to get an array of the result set rows from the database query where each row is an associative array
1215       * of ['field_name' => 'row_value'].  The array of rows can optionally be keyed by a field name, but defaults to
1216       * a sequential numeric array.
1217       *
1218       * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted
1219       * behavior and should be avoided.
1220       *
1221       * @param   string  $key     The name of a field on which to key the result array.
1222       * @param   string  $column  An optional column name. Instead of the whole row, only this column value will be in
1223       *                           the result array.
1224       *
1225       * @return  mixed   The return value or null if the query failed.
1226       *
1227       * @since   1.0
1228       * @throws  \RuntimeException
1229       */
1230  	public function loadAssocList($key = null, $column = null)
1231      {
1232          $this->connect();
1233  
1234          $array = [];
1235  
1236          // Execute the query and get the result set cursor.
1237          $this->execute();
1238  
1239          // Get all of the rows from the result set.
1240          while ($row = $this->fetchAssoc())
1241          {
1242              $value = $column ? ($row[$column] ?? $row) : $row;
1243  
1244              if ($key)
1245              {
1246                  $array[$row[$key]] = $value;
1247              }
1248              else
1249              {
1250                  $array[] = $value;
1251              }
1252          }
1253  
1254          // Free up system resources and return.
1255          $this->freeResult();
1256  
1257          return $array;
1258      }
1259  
1260      /**
1261       * Method to get an array of values from the <var>$offset</var> field in each row of the result set from the database query.
1262       *
1263       * @param   integer  $offset  The row offset to use to build the result array.
1264       *
1265       * @return  mixed  The return value or null if the query failed.
1266       *
1267       * @since   1.0
1268       * @throws  \RuntimeException
1269       */
1270  	public function loadColumn($offset = 0)
1271      {
1272          $this->connect();
1273  
1274          $array = [];
1275  
1276          // Execute the query and get the result set cursor.
1277          $this->execute();
1278  
1279          // Get all of the rows from the result set as arrays.
1280          while ($row = $this->fetchArray())
1281          {
1282              $array[] = $row[$offset];
1283          }
1284  
1285          // Free up system resources and return.
1286          $this->freeResult();
1287  
1288          return $array;
1289      }
1290  
1291      /**
1292       * Method to get the first row of the result set from the database query as an object.
1293       *
1294       * @param   string  $class  The class name to use for the returned row object.
1295       *
1296       * @return  mixed  The return value or null if the query failed.
1297       *
1298       * @since   1.0
1299       * @throws  \RuntimeException
1300       */
1301  	public function loadObject($class = \stdClass::class)
1302      {
1303          $this->connect();
1304  
1305          $ret = null;
1306  
1307          if ($this->statement)
1308          {
1309              $fetchMode = $class === \stdClass::class ? FetchMode::STANDARD_OBJECT : FetchMode::CUSTOM_OBJECT;
1310  
1311              // PDO doesn't allow extra arguments for \PDO::FETCH_CLASS, so only forward the class for the custom object mode
1312              if ($fetchMode === FetchMode::STANDARD_OBJECT)
1313              {
1314                  $this->statement->setFetchMode($fetchMode);
1315              }
1316              else
1317              {
1318                  $this->statement->setFetchMode($fetchMode, $class);
1319              }
1320          }
1321  
1322          // Execute the query and get the result set cursor.
1323          $this->execute();
1324  
1325          // Get the first row from the result set as an object of type $class.
1326          $object = $this->fetchObject();
1327  
1328          if ($object)
1329          {
1330              $ret = $object;
1331          }
1332  
1333          // Free up system resources and return.
1334          $this->freeResult();
1335  
1336          return $ret;
1337      }
1338  
1339      /**
1340       * Method to get an array of the result set rows from the database query where each row is an object.  The array
1341       * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array.
1342       *
1343       * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted behavior and should be avoided.
1344       *
1345       * @param   string  $key    The name of a field on which to key the result array.
1346       * @param   string  $class  The class name to use for the returned row objects.
1347       *
1348       * @return  mixed  The return value or null if the query failed.
1349       *
1350       * @since   1.0
1351       * @throws  \RuntimeException
1352       */
1353  	public function loadObjectList($key = '', $class = \stdClass::class)
1354      {
1355          $this->connect();
1356  
1357          $array = [];
1358  
1359          if ($this->statement)
1360          {
1361              $fetchMode = $class === \stdClass::class ? FetchMode::STANDARD_OBJECT : FetchMode::CUSTOM_OBJECT;
1362  
1363              // PDO doesn't allow extra arguments for \PDO::FETCH_CLASS, so only forward the class for the custom object mode
1364              if ($fetchMode === FetchMode::STANDARD_OBJECT)
1365              {
1366                  $this->statement->setFetchMode($fetchMode);
1367              }
1368              else
1369              {
1370                  $this->statement->setFetchMode($fetchMode, $class);
1371              }
1372          }
1373  
1374          // Execute the query and get the result set cursor.
1375          $this->execute();
1376  
1377          // Get all of the rows from the result set as objects of type $class.
1378          while ($row = $this->fetchObject())
1379          {
1380              if ($key)
1381              {
1382                  $array[$row->$key] = $row;
1383              }
1384              else
1385              {
1386                  $array[] = $row;
1387              }
1388          }
1389  
1390          // Free up system resources and return.
1391          $this->freeResult();
1392  
1393          return $array;
1394      }
1395  
1396      /**
1397       * Method to get the first field of the first row of the result set from the database query.
1398       *
1399       * @return  mixed  The return value or null if the query failed.
1400       *
1401       * @since   1.0
1402       * @throws  \RuntimeException
1403       */
1404  	public function loadResult()
1405      {
1406          $this->connect();
1407  
1408          $ret = null;
1409  
1410          // Execute the query and get the result set cursor.
1411          $this->execute();
1412  
1413          // Get the first row from the result set as an array.
1414          $row = $this->fetchArray();
1415  
1416          if ($row)
1417          {
1418              $ret = $row[0];
1419          }
1420  
1421          // Free up system resources and return.
1422          $this->freeResult();
1423  
1424          return $ret;
1425      }
1426  
1427      /**
1428       * Method to get the first row of the result set from the database query as an array.
1429       *
1430       * Columns are indexed numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc.
1431       *
1432       * @return  mixed  The return value or null if the query failed.
1433       *
1434       * @since   1.0
1435       * @throws  \RuntimeException
1436       */
1437  	public function loadRow()
1438      {
1439          $this->connect();
1440  
1441          $ret = null;
1442  
1443          // Execute the query and get the result set cursor.
1444          $this->execute();
1445  
1446          // Get the first row from the result set as an array.
1447          $row = $this->fetchArray();
1448  
1449          if ($row)
1450          {
1451              $ret = $row;
1452          }
1453  
1454          // Free up system resources and return.
1455          $this->freeResult();
1456  
1457          return $ret;
1458      }
1459  
1460      /**
1461       * Method to get an array of the result set rows from the database query where each row is an array.  The array
1462       * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array.
1463       *
1464       * NOTE: Choosing to key the result array by a non-unique field can result in unwanted behavior and should be avoided.
1465       *
1466       * @param   string  $key  The name of a field on which to key the result array.
1467       *
1468       * @return  array   An array of results.
1469       *
1470       * @since   1.0
1471       * @throws  \RuntimeException
1472       */
1473  	public function loadRowList($key = null)
1474      {
1475          $this->connect();
1476  
1477          $array = [];
1478  
1479          // Execute the query and get the result set cursor.
1480          $this->execute();
1481  
1482          // Get all of the rows from the result set as arrays.
1483          while ($row = $this->fetchArray())
1484          {
1485              if ($key !== null)
1486              {
1487                  $array[$row[$key]] = $row;
1488              }
1489              else
1490              {
1491                  $array[] = $row;
1492              }
1493          }
1494  
1495          // Free up system resources and return.
1496          $this->freeResult();
1497  
1498          return $array;
1499      }
1500  
1501      /**
1502       * Prepares a SQL statement for execution
1503       *
1504       * @param   string  $query  The SQL query to be prepared.
1505       *
1506       * @return  StatementInterface
1507       *
1508       * @since   2.0.0
1509       * @throws  PrepareStatementFailureException
1510       */
1511      abstract protected function prepareStatement(string $query): StatementInterface;
1512  
1513      /**
1514       * Alias for quote method
1515       *
1516       * @param   array|string  $text    A string or an array of strings to quote.
1517       * @param   boolean       $escape  True (default) to escape the string, false to leave it unchanged.
1518       *
1519       * @return  string  The quoted input string.
1520       *
1521       * @since   1.0
1522       */
1523      public function q($text, $escape = true)
1524      {
1525          return $this->quote($text, $escape);
1526      }
1527  
1528      /**
1529       * Quotes and optionally escapes a string to database requirements for use in database queries.
1530       *
1531       * @param   array|string  $text    A string or an array of strings to quote.
1532       * @param   boolean       $escape  True (default) to escape the string, false to leave it unchanged.
1533       *
1534       * @return  string  The quoted input string.
1535       *
1536       * @since   1.0
1537       */
1538  	public function quote($text, $escape = true)
1539      {
1540          if (\is_array($text))
1541          {
1542              foreach ($text as $k => $v)
1543              {
1544                  $text[$k] = $this->quote($v, $escape);
1545              }
1546  
1547              return $text;
1548          }
1549  
1550          return '\'' . ($escape ? $this->escape($text) : $text) . '\'';
1551      }
1552  
1553      /**
1554       * Quotes a binary string to database requirements for use in database queries.
1555       *
1556       * @param   string  $data  A binary string to quote.
1557       *
1558       * @return  string  The binary quoted input string.
1559       *
1560       * @since   1.7.0
1561       */
1562  	public function quoteBinary($data)
1563      {
1564          // SQL standard syntax for hexadecimal literals
1565          return "X'" . bin2hex($data) . "'";
1566      }
1567  
1568      /**
1569       * Replace special placeholder representing binary field with the original string.
1570       *
1571       * @param   string|resource  $data  Encoded string or resource.
1572       *
1573       * @return  string  The original string.
1574       *
1575       * @since   1.7.0
1576       */
1577  	public function decodeBinary($data)
1578      {
1579          return $data;
1580      }
1581  
1582      /**
1583       * Alias for quoteName method
1584       *
1585       * @param   array|string  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
1586       *                               Each type supports dot-notation name.
1587       * @param   array|string  $as    The AS query part associated to $name. It can be string or array, in latter case it has to be
1588       *                               same length of $name; if is null there will not be any AS part for string or array element.
1589       *
1590       * @return  array|string  The quote wrapped name, same type of $name.
1591       *
1592       * @since   1.0
1593       */
1594      public function qn($name, $as = null)
1595      {
1596          return $this->quoteName($name, $as);
1597      }
1598  
1599      /**
1600       * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
1601       * risks and reserved word conflicts.
1602       *
1603       * @param   array|string  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
1604       *                               Each type supports dot-notation name.
1605       * @param   array|string  $as    The AS query part associated to $name. It can be string or array, in latter case it has to be
1606       *                               same length of $name; if is null there will not be any AS part for string or array element.
1607       *
1608       * @return  array|string  The quote wrapped name, same type of $name.
1609       *
1610       * @since   1.0
1611       */
1612  	public function quoteName($name, $as = null)
1613      {
1614          if (\is_string($name))
1615          {
1616              $name = $this->quoteNameString($name);
1617  
1618              if ($as !== null)
1619              {
1620                  $name .= ' AS ' . $this->quoteNameString($as, true);
1621              }
1622  
1623              return $name;
1624          }
1625  
1626          $fin = [];
1627  
1628          if ($as === null)
1629          {
1630              foreach ($name as $str)
1631              {
1632                  $fin[] = $this->quoteName($str);
1633              }
1634          }
1635          elseif (\is_array($name) && (\count($name) === \count($as)))
1636          {
1637              $count = \count($name);
1638  
1639              for ($i = 0; $i < $count; $i++)
1640              {
1641                  $fin[] = $this->quoteName($name[$i], $as[$i]);
1642              }
1643          }
1644  
1645          return $fin;
1646      }
1647  
1648      /**
1649       * Quote string coming from quoteName call.
1650       *
1651       * @param   string   $name          Identifier name to be quoted.
1652       * @param   boolean  $asSinglePart  Treat the name as a single part of the identifier.
1653       *
1654       * @return  string  Quoted identifier string.
1655       *
1656       * @since   1.7.0
1657       */
1658  	protected function quoteNameString($name, $asSinglePart = false)
1659      {
1660          $q = $this->nameQuote . $this->nameQuote;
1661  
1662          // Double quote reserved keyword
1663          $name = str_replace($q[1], $q[1] . $q[1], $name);
1664  
1665          if ($asSinglePart)
1666          {
1667              return $q[0] . $name . $q[1];
1668          }
1669  
1670          return $q[0] . str_replace('.', "$q[1].$q[0]", $name) . $q[1];
1671      }
1672  
1673      /**
1674       * Quote strings coming from quoteName call.
1675       *
1676       * @param   array  $strArr  Array of strings coming from quoteName dot-explosion.
1677       *
1678       * @return  string  Dot-imploded string of quoted parts.
1679       *
1680       * @since   1.0
1681       * @deprecated  2.0  Use quoteNameString instead
1682       */
1683  	protected function quoteNameStr($strArr)
1684      {
1685          $parts = [];
1686          $q     = $this->nameQuote;
1687  
1688          foreach ($strArr as $part)
1689          {
1690              if ($part === null)
1691              {
1692                  continue;
1693              }
1694  
1695              if (\strlen($q) === 1)
1696              {
1697                  $parts[] = $q . $part . $q;
1698              }
1699              else
1700              {
1701                  $parts[] = $q[0] . $part . $q[1];
1702              }
1703          }
1704  
1705          return implode('.', $parts);
1706      }
1707  
1708      /**
1709       * This function replaces a string identifier with the configured table prefix.
1710       *
1711       * @param   string  $sql     The SQL statement to prepare.
1712       * @param   string  $prefix  The table prefix.
1713       *
1714       * @return  string  The processed SQL statement.
1715       *
1716       * @since   1.0
1717       */
1718  	public function replacePrefix($sql, $prefix = '#__')
1719      {
1720          $escaped   = false;
1721          $startPos  = 0;
1722          $quoteChar = '';
1723          $literal   = '';
1724  
1725          $sql = trim($sql);
1726          $n   = \strlen($sql);
1727  
1728          while ($startPos < $n)
1729          {
1730              $ip = strpos($sql, $prefix, $startPos);
1731  
1732              if ($ip === false)
1733              {
1734                  break;
1735              }
1736  
1737              $j = strpos($sql, "'", $startPos);
1738              $k = strpos($sql, '"', $startPos);
1739  
1740              if (($k !== false) && (($k < $j) || ($j === false)))
1741              {
1742                  $quoteChar = '"';
1743                  $j         = $k;
1744              }
1745              else
1746              {
1747                  $quoteChar = "'";
1748              }
1749  
1750              if ($j === false)
1751              {
1752                  $j = $n;
1753              }
1754  
1755              $literal .= str_replace($prefix, $this->tablePrefix, substr($sql, $startPos, $j - $startPos));
1756              $startPos = $j;
1757  
1758              $j = $startPos + 1;
1759  
1760              if ($j >= $n)
1761              {
1762                  break;
1763              }
1764  
1765              // Quote comes first, find end of quote
1766              while (true)
1767              {
1768                  $k       = strpos($sql, $quoteChar, $j);
1769                  $escaped = false;
1770  
1771                  if ($k === false)
1772                  {
1773                      break;
1774                  }
1775  
1776                  $l = $k - 1;
1777  
1778                  while ($l >= 0 && $sql[$l] === '\\')
1779                  {
1780                      $l--;
1781                      $escaped = !$escaped;
1782                  }
1783  
1784                  if ($escaped)
1785                  {
1786                      $j = $k + 1;
1787  
1788                      continue;
1789                  }
1790  
1791                  break;
1792              }
1793  
1794              if ($k === false)
1795              {
1796                  // Error in the query - no end quote; ignore it
1797                  break;
1798              }
1799  
1800              $literal .= substr($sql, $startPos, $k - $startPos + 1);
1801              $startPos = $k + 1;
1802          }
1803  
1804          if ($startPos < $n)
1805          {
1806              $literal .= substr($sql, $startPos, $n - $startPos);
1807          }
1808  
1809          return $literal;
1810      }
1811  
1812      /**
1813       * Get the query monitor.
1814       *
1815       * @return  QueryMonitorInterface|null  The query monitor or null if not set.
1816       *
1817       * @since   2.0.0
1818       */
1819  	public function getMonitor()
1820      {
1821          return $this->monitor;
1822      }
1823  
1824      /**
1825       * Set a query monitor.
1826       *
1827       * @param   QueryMonitorInterface|null  $monitor  The query monitor.
1828       *
1829       * @return  $this
1830       *
1831       * @since   2.0.0
1832       */
1833  	public function setMonitor(QueryMonitorInterface $monitor = null)
1834      {
1835          $this->monitor = $monitor;
1836  
1837          return $this;
1838      }
1839  
1840      /**
1841       * Sets the SQL statement string for later execution.
1842       *
1843       * @param   string|QueryInterface  $query   The SQL statement to set either as a Query object or a string.
1844       * @param   integer                $offset  The affected row offset to set. {@deprecated 3.0 Use LimitableInterface::setLimit() instead}
1845       * @param   integer                $limit   The maximum affected rows to set. {@deprecated 3.0 Use LimitableInterface::setLimit() instead}
1846       *
1847       * @return  $this
1848       *
1849       * @since   1.0
1850       * @throws  \InvalidArgumentException
1851       */
1852  	public function setQuery($query, $offset = 0, $limit = 0)
1853      {
1854          $this->connect();
1855  
1856          $this->freeResult();
1857  
1858          if (\is_string($query))
1859          {
1860              // Allows taking advantage of bound variables in a direct query:
1861              $query = $this->getQuery(true)->setQuery($query);
1862          }
1863          elseif (!($query instanceof QueryInterface))
1864          {
1865              throw new \InvalidArgumentException(
1866                  sprintf(
1867                      'A query must be a string or a %s instance, a %s was given.',
1868                      QueryInterface::class,
1869                      \gettype($query) === 'object' ? (\get_class($query) . ' instance') : \gettype($query)
1870                  )
1871              );
1872          }
1873  
1874          if ($offset > 0 || $limit > 0)
1875          {
1876              trigger_deprecation(
1877                  'joomla/database',
1878                  '2.0.0',
1879                  'The "$offset" and "$limit" arguments of %s() are deprecated and will be removed in 3.0, use %s::setLimit() instead.',
1880                  __METHOD__,
1881                  QueryInterface::class
1882              );
1883          }
1884  
1885          // Check for values set on the query object and use those if there is a zero value passed here
1886          if ($limit === 0 && $query->limit > 0)
1887          {
1888              $limit = $query->limit;
1889          }
1890  
1891          if ($offset === 0 && $query->offset > 0)
1892          {
1893              $offset = $query->offset;
1894          }
1895  
1896          $query->setLimit($limit, $offset);
1897  
1898          $sql = $this->replacePrefix((string) $query);
1899  
1900          $this->statement = $this->prepareStatement($sql);
1901  
1902          $this->sql    = $query;
1903          $this->limit  = (int) max(0, $limit);
1904          $this->offset = (int) max(0, $offset);
1905  
1906          return $this;
1907      }
1908  
1909      /**
1910       * Set the connection to use UTF-8 character encoding.
1911       *
1912       * @return  boolean  True on success.
1913       *
1914       * @since   1.0
1915       */
1916      abstract public function setUtf();
1917  
1918      /**
1919       * Method to truncate a table.
1920       *
1921       * @param   string  $table  The table to truncate
1922       *
1923       * @return  void
1924       *
1925       * @since   1.0
1926       * @throws  \RuntimeException
1927       */
1928  	public function truncateTable($table)
1929      {
1930          $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table))
1931              ->execute();
1932      }
1933  
1934      /**
1935       * Updates a row in a table based on an object's properties.
1936       *
1937       * @param   string        $table   The name of the database table to update.
1938       * @param   object        $object  A reference to an object whose public properties match the table fields.
1939       * @param   array|string  $key     The name of the primary key.
1940       * @param   boolean       $nulls   True to update null fields or false to ignore them.
1941       *
1942       * @return  boolean  True on success.
1943       *
1944       * @since   1.0
1945       * @throws  \RuntimeException
1946       */
1947  	public function updateObject($table, &$object, $key, $nulls = false)
1948      {
1949          $fields       = [];
1950          $where        = [];
1951          $tableColumns = $this->getTableColumns($table);
1952  
1953          if (\is_string($key))
1954          {
1955              $key = [$key];
1956          }
1957  
1958          if (\is_object($key))
1959          {
1960              $key = (array) $key;
1961          }
1962  
1963          // Create the base update statement.
1964          $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s';
1965  
1966          // Iterate over the object variables to build the query fields/value pairs.
1967          foreach (get_object_vars($object) as $k => $v)
1968          {
1969              // Skip columns that don't exist in the table.
1970              if (!\array_key_exists($k, $tableColumns))
1971              {
1972                  continue;
1973              }
1974  
1975              // Only process scalars that are not internal fields.
1976              if (\is_array($v) || \is_object($v) || $k[0] === '_')
1977              {
1978                  continue;
1979              }
1980  
1981              // Set the primary key to the WHERE clause instead of a field to update.
1982              if (\in_array($k, $key, true))
1983              {
1984                  $where[] = $this->quoteName($k) . ($v === null ? ' IS NULL' : ' = ' . $this->quote($v));
1985  
1986                  continue;
1987              }
1988  
1989              // Prepare and sanitize the fields and values for the database query.
1990              if ($v === null)
1991              {
1992                  // If the value is null and we want to update nulls then set it.
1993                  if ($nulls)
1994                  {
1995                      $val = 'NULL';
1996                  }
1997                  else
1998                  {
1999                      // If the value is null and we do not want to update nulls then ignore this field.
2000                      continue;
2001                  }
2002              }
2003              else
2004              {
2005                  // The field is not null so we prep it for update.
2006                  $val = $this->quote($v);
2007              }
2008  
2009              // Add the field to be updated.
2010              $fields[] = $this->quoteName($k) . '=' . $val;
2011          }
2012  
2013          // We don't have any fields to update.
2014          if (empty($fields))
2015          {
2016              return true;
2017          }
2018  
2019          // Set the query and execute the update.
2020          $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where)))->execute();
2021  
2022          return true;
2023      }
2024  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer