Файловый менеджер - Редактировать - /home/lmsyaran/public_html/joomla4/joomla.zip
Назад
PK �c�[�@�y7 7 application/web/router/base.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Application * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Basic Web application router class for the Joomla Platform. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ class JApplicationWebRouterBase extends JApplicationWebRouter { /** * @var array An array of rules, each rule being an associative array('regex'=> $regex, 'vars' => $vars, 'controller' => $controller) * for routing the request. * @since 3.0 */ protected $maps = array(); /** * Add a route map to the router. If the pattern already exists it will be overwritten. * * @param string $pattern The route pattern to use for matching. * @param string $controller The controller name to map to the given pattern. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function addMap($pattern, $controller) { // Sanitize and explode the pattern. $pattern = explode('/', trim(parse_url((string) $pattern, PHP_URL_PATH), ' /')); // Prepare the route variables $vars = array(); // Initialize regular expression $regex = array(); // Loop on each segment foreach ($pattern as $segment) { // Match a splat with no variable. if ($segment == '*') { $regex[] = '.*'; } // Match a splat and capture the data to a named variable. elseif ($segment[0] == '*') { $vars[] = substr($segment, 1); $regex[] = '(.*)'; } // Match an escaped splat segment. elseif ($segment[0] == '\\' && $segment[1] == '*') { $regex[] = '\*' . preg_quote(substr($segment, 2)); } // Match an unnamed variable without capture. elseif ($segment == ':') { $regex[] = '[^/]*'; } // Match a named variable and capture the data. elseif ($segment[0] == ':') { $vars[] = substr($segment, 1); $regex[] = '([^/]*)'; } // Match a segment with an escaped variable character prefix. elseif ($segment[0] == '\\' && $segment[1] == ':') { $regex[] = preg_quote(substr($segment, 1)); } // Match the standard segment. else { $regex[] = preg_quote($segment); } } $this->maps[] = array( 'regex' => chr(1) . '^' . implode('/', $regex) . '$' . chr(1), 'vars' => $vars, 'controller' => (string) $controller, ); return $this; } /** * Add a route map to the router. If the pattern already exists it will be overwritten. * * @param array $maps A list of route maps to add to the router as $pattern => $controller. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function addMaps($maps) { foreach ($maps as $pattern => $controller) { $this->addMap($pattern, $controller); } return $this; } /** * Parse the given route and return the name of a controller mapped to the given route. * * @param string $route The route string for which to find and execute a controller. * * @return string The controller name for the given route excluding prefix. * * @since 3.0 * @throws InvalidArgumentException */ protected function parseRoute($route) { $controller = false; // Trim the query string off. $route = preg_replace('/([^?]*).*/u', '\1', $route); // Sanitize and explode the route. $route = trim(parse_url($route, PHP_URL_PATH), ' /'); // If the route is empty then simply return the default route. No parsing necessary. if ($route == '') { return $this->default; } // Iterate through all of the known route maps looking for a match. foreach ($this->maps as $rule) { if (preg_match($rule['regex'], $route, $matches)) { // If we have gotten this far then we have a positive match. $controller = $rule['controller']; // Time to set the input variables. // We are only going to set them if they don't already exist to avoid overwriting things. foreach ($rule['vars'] as $i => $var) { $this->input->def($var, $matches[$i + 1]); // Don't forget to do an explicit set on the GET superglobal. $this->input->get->def($var, $matches[$i + 1]); } $this->input->def('_rawRoute', $route); break; } } // We were unable to find a route match for the request. Panic. if (!$controller) { throw new InvalidArgumentException(sprintf('Unable to handle request for route `%s`.', $route), 404); } return $controller; } } PK �c�[���� application/web/router/rest.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Application * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * RESTful Web application router class for the Joomla Platform. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ class JApplicationWebRouterRest extends JApplicationWebRouterBase { /** * @var boolean A boolean allowing to pass _method as parameter in POST requests * @since 3.0 */ protected $methodInPostRequest = false; /** * @var array An array of HTTP Method => controller suffix pairs for routing the request. * @since 3.0 */ protected $suffixMap = array( 'GET' => 'Get', 'POST' => 'Create', 'PUT' => 'Update', 'PATCH' => 'Update', 'DELETE' => 'Delete', 'HEAD' => 'Head', 'OPTIONS' => 'Options', ); /** * Find and execute the appropriate controller based on a given route. * * @param string $route The route string for which to find and execute a controller. * * @return void * * @since 3.0 * @throws InvalidArgumentException * @throws RuntimeException */ public function execute($route) { // Get the controller name based on the route patterns and requested route. $name = $this->parseRoute($route); // Append the HTTP method based suffix. $name .= $this->fetchControllerSuffix(); // Get the controller object by name. $controller = $this->fetchController($name); // Execute the controller. $controller->execute(); } /** * Set a controller class suffix for a given HTTP method. * * @param string $method The HTTP method for which to set the class suffix. * @param string $suffix The class suffix to use when fetching the controller name for a given request. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setHttpMethodSuffix($method, $suffix) { $this->suffixMap[strtoupper((string) $method)] = (string) $suffix; return $this; } /** * Set to allow or not method in POST request * * @param boolean $value A boolean to allow or not method in POST request * * @return void * * @since 3.0 */ public function setMethodInPostRequest($value) { $this->methodInPostRequest = $value; } /** * Get the property to allow or not method in POST request * * @return boolean * * @since 3.0 */ public function isMethodInPostRequest() { return $this->methodInPostRequest; } /** * Get the controller class suffix string. * * @return string * * @since 3.0 * @throws RuntimeException */ protected function fetchControllerSuffix() { // Validate that we have a map to handle the given HTTP method. if (!isset($this->suffixMap[$this->input->getMethod()])) { throw new RuntimeException(sprintf('Unable to support the HTTP method `%s`.', $this->input->getMethod()), 404); } // Check if request method is POST if ($this->methodInPostRequest == true && strcmp(strtoupper($this->input->server->getMethod()), 'POST') === 0) { // Get the method from input $postMethod = $this->input->get->getWord('_method'); // Validate that we have a map to handle the given HTTP method from input if ($postMethod && isset($this->suffixMap[strtoupper($postMethod)])) { return ucfirst($this->suffixMap[strtoupper($postMethod)]); } } return ucfirst($this->suffixMap[$this->input->getMethod()]); } } PK �c�[f)�t t application/web/router.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Application * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Class to define an abstract Web application router. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ abstract class JApplicationWebRouter { /** * @var JApplicationWeb The web application on whose behalf we are routing the request. * @since 3.0 */ protected $app; /** * @var string The default page controller name for an empty route. * @since 3.0 */ protected $default; /** * @var string Controller class name prefix for creating controller objects by name. * @since 3.0 */ protected $controllerPrefix; /** * @var JInput An input object from which to derive the route. * @since 3.0 */ protected $input; /** * Constructor. * * @param JApplicationWeb $app The web application on whose behalf we are routing the request. * @param JInput $input An optional input object from which to derive the route. If none * is given than the input from the application object will be used. * * @since 3.0 */ public function __construct(JApplicationWeb $app, JInput $input = null) { $this->app = $app; $this->input = ($input === null) ? $this->app->input : $input; } /** * Find and execute the appropriate controller based on a given route. * * @param string $route The route string for which to find and execute a controller. * * @return mixed The return value of the controller executed * * @since 3.0 * @throws InvalidArgumentException * @throws RuntimeException */ public function execute($route) { // Get the controller name based on the route patterns and requested route. $name = $this->parseRoute($route); // Get the controller object by name. $controller = $this->fetchController($name); // Execute the controller. return $controller->execute(); } /** * Set the controller name prefix. * * @param string $prefix Controller class name prefix for creating controller objects by name. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setControllerPrefix($prefix) { $this->controllerPrefix = (string) $prefix; return $this; } /** * Set the default controller name. * * @param string $name The default page controller name for an empty route. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setDefaultController($name) { $this->default = (string) $name; return $this; } /** * Parse the given route and return the name of a controller mapped to the given route. * * @param string $route The route string for which to find and execute a controller. * * @return string The controller name for the given route excluding prefix. * * @since 3.0 * @throws InvalidArgumentException */ abstract protected function parseRoute($route); /** * Get a JController object for a given name. * * @param string $name The controller name (excluding prefix) for which to fetch and instance. * * @return JController * * @since 3.0 * @throws RuntimeException */ protected function fetchController($name) { // Derive the controller class name. $class = $this->controllerPrefix . ucfirst($name); // If the controller class does not exist panic. if (!class_exists($class) || !is_subclass_of($class, 'JController')) { throw new RuntimeException(sprintf('Unable to locate controller `%s`.', $class), 404); } // Instantiate the controller. $controller = new $class($this->input, $this->app); return $controller; } } PK �c�[ʭ�� � archive/archive.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); use Joomla\Archive\Archive; /** * An Archive handling class * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ class JArchive { /** * The array of instantiated archive adapters. * * @var JArchiveExtractable[] * @since 3.0.0 */ protected static $adapters = array(); /** * Extract an archive file to a directory. * * @param string $archivename The name of the archive file * @param string $extractdir Directory to unpack into * * @return boolean True for success * * @since 1.5 * @throws InvalidArgumentException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public static function extract($archivename, $extractdir) { // The archive instance $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path'))); // Extract the archive return $archive->extract($archivename, $extractdir); } /** * Get a file compression adapter. * * @param string $type The type of adapter (bzip2|gzip|tar|zip). * * @return JArchiveExtractable Adapter for the requested type * * @since 1.5 * @throws UnexpectedValueException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public static function getAdapter($type) { if (!isset(self::$adapters[$type])) { // Try to load the adapter object $class = 'JArchive' . ucfirst($type); if (!class_exists($class)) { throw new UnexpectedValueException('Unable to load archive', 500); } self::$adapters[$type] = new $class; } return self::$adapters[$type]; } } PK �c�[ �M� archive/bzip2.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.stream'); /** * Bzip2 format adapter for the JArchive class * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Bzip2 class instead */ class JArchiveBzip2 implements JArchiveExtractable { /** * Bzip2 file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Extract a Bzip2 compressed file to a given path * * @param string $archive Path to Bzip2 archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; if (!extension_loaded('bz2')) { $this->raiseWarning(100, 'The bz2 extension is not available.'); } if (isset($options['use_streams']) && $options['use_streams'] != false) { return $this->extractStream($archive, $destination, $options); } // Old style: read the whole file and then parse it $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive'); } $buffer = bzdecompress($this->_data); unset($this->_data); if (empty($buffer)) { return $this->raiseWarning(100, 'Unable to decompress data'); } if (JFile::write($destination, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write archive'); } return true; } /** * Method to extract archive using stream objects * * @param string $archive Path to Bzip2 archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 3.6.0 */ protected function extractStream($archive, $destination, $options = array()) { // New style! streams! $input = JFactory::getStream(); // Use bzip $input->set('processingmethod', 'bz'); if (!$input->open($archive)) { return $this->raiseWarning(100, 'Unable to read archive (bz2)'); } $output = JFactory::getStream(); if (!$output->open($destination, 'w')) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (bz2)'); } do { $this->_data = $input->read($input->get('chunksize', 8196)); if ($this->_data && !$output->write($this->_data)) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (bz2)'); } } while ($this->_data); $output->close(); $input->close(); return true; } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return extension_loaded('bz2'); } } PK �c�[��� archive/extractable.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Archieve class interface * * @since 3.0.0 * @deprecated 4.0 use the Joomla\Archive\ExtractableInterface interface instead */ interface JArchiveExtractable { /** * Extract a compressed file to a given path * * @param string $archive Path to archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [may be unused] * * @return boolean True if successful * * @since 3.0.0 */ public function extract($archive, $destination, array $options = array()); /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 3.0.0 */ public static function isSupported(); } PK �c�[��{� � archive/gzip.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); /** * Gzip format adapter for the JArchive class * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Gzip class instead */ class JArchiveGzip implements JArchiveExtractable { /** * Gzip file flags. * * @var array * @since 1.5 */ private $_flags = array('FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10); /** * Gzip file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Extract a Gzip compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; if (!extension_loaded('zlib')) { return $this->raiseWarning(100, 'The zlib extension is not available.'); } if (isset($options['use_streams']) && $options['use_streams'] != false) { return $this->extractStream($archive, $destination, $options); } $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive'); } $position = $this->_getFilePosition(); $buffer = gzinflate(substr($this->_data, $position, strlen($this->_data) - $position)); if (empty($buffer)) { return $this->raiseWarning(100, 'Unable to decompress data'); } if (JFile::write($destination, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write archive'); } return true; } /** * Method to extract archive using stream objects * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 3.6.0 */ protected function extractStream($archive, $destination, $options = array()) { // New style! streams! $input = JFactory::getStream(); // Use gz $input->set('processingmethod', 'gz'); if (!$input->open($archive)) { return $this->raiseWarning(100, 'Unable to read archive (gz)'); } $output = JFactory::getStream(); if (!$output->open($destination, 'w')) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (gz)'); } do { $this->_data = $input->read($input->get('chunksize', 8196)); if ($this->_data && !$output->write($this->_data)) { $input->close(); return $this->raiseWarning(100, 'Unable to write file (gz)'); } } while ($this->_data); $output->close(); $input->close(); return true; } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return extension_loaded('zlib'); } /** * Get file data offset for archive * * @return integer Data position marker for archive * * @since 1.5 * @throws RuntimeException */ public function _getFilePosition() { // Gzipped file... unpack it first $position = 0; $info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->_data, $position + 2)); if (!$info) { return $this->raiseWarning(100, 'Unable to decompress data.'); } $position += 10; if ($info['FLG'] & $this->_flags['FEXTRA']) { $XLEN = unpack('vLength', substr($this->_data, $position + 0, 2)); $XLEN = $XLEN['Length']; $position += $XLEN + 2; } if ($info['FLG'] & $this->_flags['FNAME']) { $filenamePos = strpos($this->_data, "\x0", $position); $position = $filenamePos + 1; } if ($info['FLG'] & $this->_flags['FCOMMENT']) { $commentPos = strpos($this->_data, "\x0", $position); $position = $commentPos + 1; } if ($info['FLG'] & $this->_flags['FHCRC']) { $hcrc = unpack('vCRC', substr($this->_data, $position + 0, 2)); $hcrc = $hcrc['CRC']; $position += 2; } return $position; } } PK �c�[_��� � archive/tar.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); jimport('joomla.filesystem.path'); /** * Tar format adapter for the JArchive class * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Tar class instead */ class JArchiveTar implements JArchiveExtractable { /** * Tar file types. * * @var array * @since 1.5 */ private $_types = array( 0x0 => 'Unix file', 0x30 => 'File', 0x31 => 'Link', 0x32 => 'Symbolic link', 0x33 => 'Character special file', 0x34 => 'Block special file', 0x35 => 'Directory', 0x36 => 'FIFO special file', 0x37 => 'Contiguous file', ); /** * Tar file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Tar file metadata array * * @var array * @since 1.5 */ private $_metadata = null; /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * @param array $options Extraction options [unused] * * @return boolean|JException True on success, JException instance on failure if JError class exists * * @since 1.5 * @throws RuntimeException if JError class does not exist */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; $this->_metadata = null; $this->_data = file_get_contents($archive); if (!$this->_data) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to read archive'); } else { throw new RuntimeException('Unable to read archive'); } } $this->_getTarInfo($this->_data); for ($i = 0, $n = count($this->_metadata); $i < $n; $i++) { $type = strtolower($this->_metadata[$i]['type']); if ($type == 'file' || $type == 'unix file') { $buffer = $this->_metadata[$i]['data']; $path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']); // Make sure the destination folder exists if (!JFolder::create(dirname($path))) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to create destination'); } else { throw new RuntimeException('Unable to create destination'); } } if (JFile::write($path, $buffer) === false) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to write entry'); } else { throw new RuntimeException('Unable to write entry'); } } } } return true; } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return true; } /** * Get the list of files/data from a Tar archive buffer. * * @param string &$data The Tar archive buffer. * * @return boolean|JException True on success, JException instance on failure if JError class exists * * @since 1.5 * @throws RuntimeException if JError class does not exist */ protected function _getTarInfo(& $data) { $position = 0; $return_array = array(); while ($position < strlen($data)) { if (version_compare(PHP_VERSION, '5.5', '>=')) { $info = @unpack( 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor', substr($data, $position) ); } else { $info = @unpack( 'a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/Ctypeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor', substr($data, $position) ); } /** * This variable has been set in the previous loop, * meaning that the filename was present in the previous block * to allow more than 100 characters - see below */ if (isset($longlinkfilename)) { $info['filename'] = $longlinkfilename; unset($longlinkfilename); } if (!$info) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to decompress data'); } else { throw new RuntimeException('Unable to decompress data'); } } $position += 512; $contents = substr($data, $position, octdec($info['size'])); $position += ceil(octdec($info['size']) / 512) * 512; if ($info['filename']) { $file = array( 'attr' => null, 'data' => null, 'date' => octdec($info['mtime']), 'name' => trim($info['filename']), 'size' => octdec($info['size']), 'type' => isset($this->_types[$info['typeflag']]) ? $this->_types[$info['typeflag']] : null, ); if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) { // File or folder. $file['data'] = $contents; $mode = hexdec(substr($info['mode'], 4, 3)); $file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-') . (($mode & 0x400) ? 'r' : '-') . (($mode & 0x200) ? 'w' : '-') . (($mode & 0x100) ? 'x' : '-') . (($mode & 0x040) ? 'r' : '-') . (($mode & 0x020) ? 'w' : '-') . (($mode & 0x010) ? 'x' : '-') . (($mode & 0x004) ? 'r' : '-') . (($mode & 0x002) ? 'w' : '-') . (($mode & 0x001) ? 'x' : '-'); } elseif (chr($info['typeflag']) == 'L' && $info['filename'] == '././@LongLink') { // GNU tar ././@LongLink support - the filename is actually in the contents, // setting a variable here so we can test in the next loop $longlinkfilename = $contents; // And the file contents are in the next block so we'll need to skip this continue; } else { // Some other type. } $return_array[] = $file; } } $this->_metadata = $return_array; return true; } } PK �c�[�6__ _ archive/wrapper/archive.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for JArchive * * @package Joomla.Platform * @subpackage Archive * @since 3.4 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ class JArchiveWrapperArchive { /** * Helper wrapper method for extract * * @param string $archivename The name of the archive file * @param string $extractdir Directory to unpack into * * @return boolean True for success * * @see JArchive::extract() * @since 3.4 * @throws InvalidArgumentException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public function extract($archivename, $extractdir) { return JArchive::extract($archivename, $extractdir); } /** * Helper wrapper method for getAdapter * * @param string $type The type of adapter (bzip2|gzip|tar|zip). * * @return JArchiveExtractable Adapter for the requested type * * @see JUserHelper::getAdapter() * @since 3.4 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public function getAdapter($type) { return JArchive::getAdapter($type); } } PK �c�[�RX.�A �A archive/zip.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); /** * ZIP format adapter for the JArchive class * * The ZIP compression code is partially based on code from: * Eric Mueller <eric@themepark.com> * http://www.zend.com/codex.php?id=535&single=1 * * Deins125 <webmaster@atlant.ru> * http://www.zend.com/codex.php?id=470&single=1 * * The ZIP compression date code is partially based on code from * Peter Listiak <mlady@users.sourceforge.net> * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Chuck Hagenbuch <chuck@horde.org> * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Zip class instead */ class JArchiveZip implements JArchiveExtractable { /** * ZIP compression methods. * * @var array * @since 1.5 */ private $_methods = array( 0x0 => 'None', 0x1 => 'Shrunk', 0x2 => 'Super Fast', 0x3 => 'Fast', 0x4 => 'Normal', 0x5 => 'Maximum', 0x6 => 'Imploded', 0x8 => 'Deflated', ); /** * Beginning of central directory record. * * @var string * @since 1.5 */ private $_ctrlDirHeader = "\x50\x4b\x01\x02"; /** * End of central directory record. * * @var string * @since 1.5 */ private $_ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00"; /** * Beginning of file contents. * * @var string * @since 1.5 */ private $_fileHeader = "\x50\x4b\x03\x04"; /** * ZIP file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * ZIP file metadata array * * @var array * @since 1.5 */ private $_metadata = null; /** * Create a ZIP compressed file from an array of file data. * * @param string $archive Path to save archive. * @param array $files Array of files to add to archive. * * @return boolean True if successful. * * @since 1.5 * * @todo Finish Implementation */ public function create($archive, $files) { $contents = array(); $ctrldir = array(); foreach ($files as $file) { $this->_addToZIPFile($file, $contents, $ctrldir); } return $this->_createZIPFile($contents, $ctrldir, $archive); } /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { if (!is_file($archive)) { return $this->raiseWarning(100, 'Archive does not exist'); } if ($this->hasNativeSupport()) { return $this->extractNative($archive, $destination); } return $this->extractCustom($archive, $destination); } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return self::hasNativeSupport() || extension_loaded('zlib'); } /** * Method to determine if the server has native zip support for faster handling * * @return boolean True if php has native ZIP support * * @since 1.5 */ public static function hasNativeSupport() { return extension_loaded('zip'); } /** * Checks to see if the data is a valid ZIP file. * * @param string &$data ZIP archive data buffer. * * @return boolean True if valid, false if invalid. * * @since 1.5 */ public function checkZipData(&$data) { if (strpos($data, $this->_fileHeader) === false) { return false; } return true; } /** * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support * * @param string $archive Path to ZIP archive to extract. * @param string $destination Path to extract archive into. * * @return mixed True if successful * * @since 3.0 * @throws RuntimeException */ protected function extractCustom($archive, $destination) { $this->_data = null; $this->_metadata = null; if (!extension_loaded('zlib')) { return $this->raiseWarning(100, 'Zlib not supported'); } $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive (zip)'); } if (!$this->_readZipInfo($this->_data)) { return $this->raiseWarning(100, 'Get ZIP Information failed'); } for ($i = 0, $n = count($this->_metadata); $i < $n; $i++) { $lastPathCharacter = substr($this->_metadata[$i]['name'], -1, 1); if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\') { $buffer = $this->_getFileData($i); $path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']); // Make sure the destination folder exists if (!JFolder::create(dirname($path))) { return $this->raiseWarning(100, 'Unable to create destination'); } if (JFile::write($path, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write entry'); } } } return true; } /** * Extract a ZIP compressed file to a given path using native php api calls for speed * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * * @return boolean True on success * * @since 3.0 * @throws RuntimeException */ protected function extractNative($archive, $destination) { $zip = new \ZipArchive; if ($zip->open($archive) !== true) { return $this->raiseWarning(100, 'Unable to open archive'); } // Make sure the destination folder exists if (!JFolder::create($destination)) { return $this->raiseWarning(100, 'Unable to create destination'); } // Read files in the archive for ($index = 0; $index < $zip->numFiles; $index++) { $file = $zip->getNameIndex($index); if (substr($file, -1) === '/') { continue; } $buffer = $zip->getFromIndex($index); if ($buffer === false) { return $this->raiseWarning(100, 'Unable to read entry'); } if (JFile::write($destination . '/' . $file, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write entry'); } } $zip->close(); return true; } /** * Get the list of files/data from a ZIP archive buffer. * * <pre> * KEY: Position in zipfile * VALUES: 'attr' -- File attributes * 'crc' -- CRC checksum * 'csize' -- Compressed file size * 'date' -- File modification time * 'name' -- Filename * 'method'-- Compression method * 'size' -- Original file size * 'type' -- File type * </pre> * * @param string &$data The ZIP archive buffer. * * @return boolean True on success * * @since 2.5.0 * @throws RuntimeException */ private function _readZipInfo(&$data) { $entries = array(); // Find the last central directory header entry $fhLast = strpos($data, $this->_ctrlDirEnd); do { $last = $fhLast; } while (($fhLast = strpos($data, $this->_ctrlDirEnd, $fhLast + 1)) !== false); // Find the central directory offset $offset = 0; if ($last) { $endOfCentralDirectory = unpack( 'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' . 'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength', substr($data, $last + 4) ); $offset = $endOfCentralDirectory['CentralDirectoryOffset']; } // Get details from central directory structure. $fhStart = strpos($data, $this->_ctrlDirHeader, $offset); $dataLength = strlen($data); do { if ($dataLength < $fhStart + 31) { return $this->raiseWarning(100, 'Invalid Zip Data'); } $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20)); $name = substr($data, $fhStart + 46, $info['Length']); $entries[$name] = array( 'attr' => null, 'crc' => sprintf('%08s', dechex($info['CRC32'])), 'csize' => $info['Compressed'], 'date' => null, '_dataStart' => null, 'name' => $name, 'method' => $this->_methods[$info['Method']], '_method' => $info['Method'], 'size' => $info['Uncompressed'], 'type' => null, ); $entries[$name]['date'] = mktime( (($info['Time'] >> 11) & 0x1f), (($info['Time'] >> 5) & 0x3f), (($info['Time'] << 1) & 0x3e), (($info['Time'] >> 21) & 0x07), (($info['Time'] >> 16) & 0x1f), ((($info['Time'] >> 25) & 0x7f) + 1980) ); if ($dataLength < $fhStart + 43) { return $this->raiseWarning(100, 'Invalid ZIP data'); } $info = unpack('vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10)); $entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary'; $entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-') . (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-'); $entries[$name]['offset'] = $info['Offset']; // Get details from local file header since we have the offset $lfhStart = strpos($data, $this->_fileHeader, $entries[$name]['offset']); if ($dataLength < $lfhStart + 34) { return $this->raiseWarning(100, 'Invalid Zip Data'); } $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25)); $name = substr($data, $lfhStart + 30, $info['Length']); $entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength']; // Bump the max execution time because not using the built in php zip libs makes this process slow. @set_time_limit(ini_get('max_execution_time')); } while ((($fhStart = strpos($data, $this->_ctrlDirHeader, $fhStart + 46)) !== false)); $this->_metadata = array_values($entries); return true; } /** * Returns the file data for a file by offset in the ZIP archive * * @param integer $key The position of the file in the archive. * * @return string Uncompressed file data buffer. * * @since 1.5 */ private function _getFileData($key) { $method = $this->_metadata[$key]['_method']; if ($method == 0x12 && !extension_loaded('bz2')) { return ''; } switch ($method) { case 0x8: return gzinflate(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize'])); case 0x0: // Files that aren't compressed. return substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']); case 0x12: return bzdecompress(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize'])); } return ''; } /** * Converts a UNIX timestamp to a 4-byte DOS date and time format * (date in high 2-bytes, time in low 2-bytes allowing magnitude * comparison). * * @param int $unixtime The current UNIX timestamp. * * @return int The current date in a 4-byte DOS format. * * @since 1.5 */ protected function _unix2DOSTime($unixtime = null) { $timearray = (is_null($unixtime)) ? getdate() : getdate($unixtime); if ($timearray['year'] < 1980) { $timearray['year'] = 1980; $timearray['mon'] = 1; $timearray['mday'] = 1; $timearray['hours'] = 0; $timearray['minutes'] = 0; $timearray['seconds'] = 0; } return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); } /** * Adds a "file" to the ZIP archive. * * @param array &$file File data array to add * @param array &$contents An array of existing zipped files. * @param array &$ctrldir An array of central directory information. * * @return void * * @since 1.5 * * @todo Review and finish implementation */ private function _addToZIPFile(array &$file, array &$contents, array &$ctrldir) { $data = &$file['data']; $name = str_replace('\\', '/', $file['name']); /* See if time/date information has been provided. */ $ftime = null; if (isset($file['time'])) { $ftime = $file['time']; } // Get the hex time. $dtime = dechex($this->_unix2DosTime($ftime)); $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3])) . chr(hexdec($dtime[0] . $dtime[1])); /* Begin creating the ZIP data. */ $fr = $this->_fileHeader; /* Version needed to extract. */ $fr .= "\x14\x00"; /* General purpose bit flag. */ $fr .= "\x00\x00"; /* Compression method. */ $fr .= "\x08\x00"; /* Last modification time/date. */ $fr .= $hexdtime; /* "Local file header" segment. */ $unc_len = strlen($data); $crc = crc32($data); $zdata = gzcompress($data); $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); $c_len = strlen($zdata); /* CRC 32 information. */ $fr .= pack('V', $crc); /* Compressed filesize. */ $fr .= pack('V', $c_len); /* Uncompressed filesize. */ $fr .= pack('V', $unc_len); /* Length of filename. */ $fr .= pack('v', strlen($name)); /* Extra field length. */ $fr .= pack('v', 0); /* File name. */ $fr .= $name; /* "File data" segment. */ $fr .= $zdata; /* Add this entry to array. */ $old_offset = strlen(implode('', $contents)); $contents[] = &$fr; /* Add to central directory record. */ $cdrec = $this->_ctrlDirHeader; /* Version made by. */ $cdrec .= "\x00\x00"; /* Version needed to extract */ $cdrec .= "\x14\x00"; /* General purpose bit flag */ $cdrec .= "\x00\x00"; /* Compression method */ $cdrec .= "\x08\x00"; /* Last mod time/date. */ $cdrec .= $hexdtime; /* CRC 32 information. */ $cdrec .= pack('V', $crc); /* Compressed filesize. */ $cdrec .= pack('V', $c_len); /* Uncompressed filesize. */ $cdrec .= pack('V', $unc_len); /* Length of filename. */ $cdrec .= pack('v', strlen($name)); /* Extra field length. */ $cdrec .= pack('v', 0); /* File comment length. */ $cdrec .= pack('v', 0); /* Disk number start. */ $cdrec .= pack('v', 0); /* Internal file attributes. */ $cdrec .= pack('v', 0); /* External file attributes -'archive' bit set. */ $cdrec .= pack('V', 32); /* Relative offset of local header. */ $cdrec .= pack('V', $old_offset); /* File name. */ $cdrec .= $name; /* Optional extra field, file comment goes here. */ /* Save to central directory array. */ $ctrldir[] = &$cdrec; } /** * Creates the ZIP file. * * Official ZIP file format: https://support.pkware.com/display/PKZIP/APPNOTE * * @param array &$contents An array of existing zipped files. * @param array &$ctrlDir An array of central directory information. * @param string $path The path to store the archive. * * @return boolean True if successful * * @since 1.5 * * @todo Review and finish implementation */ private function _createZIPFile(array &$contents, array &$ctrlDir, $path) { $data = implode('', $contents); $dir = implode('', $ctrlDir); $buffer = $data . $dir . $this->_ctrlDirEnd . /* Total # of entries "on this disk". */ pack('v', count($ctrlDir)) . /* Total # of entries overall. */ pack('v', count($ctrlDir)) . /* Size of central directory. */ pack('V', strlen($dir)) . /* Offset to start of central dir. */ pack('V', strlen($data)) . /* ZIP file comment length. */ "\x00\x00"; if (JFile::write($path, $buffer) === false) { return false; } return true; } } PK �c�[\#?ō � base/adapter.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Base * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Adapter Class * Retains common adapter pattern functions * Class harvested from joomla.installer.installer * * @since 1.6 * @deprecated 5.0 Will be removed without replacement */ class JAdapter extends JObject { /** * Associative array of adapters * * @var JAdapterInstance[] * @since 1.6 */ protected $_adapters = array(); /** * Adapter Folder * * @var string * @since 1.6 */ protected $_adapterfolder = 'adapters'; /** * Adapter Class Prefix * * @var string * @since 1.6 */ protected $_classprefix = 'J'; /** * Base Path for the adapter instance * * @var string * @since 1.6 */ protected $_basepath = null; /** * Database Connector Object * * @var JDatabaseDriver * @since 1.6 */ protected $_db; /** * Constructor * * @param string $basepath Base Path of the adapters * @param string $classprefix Class prefix of adapters * @param string $adapterfolder Name of folder to append to base path * * @since 1.6 */ public function __construct($basepath, $classprefix = null, $adapterfolder = null) { $this->_basepath = $basepath; $this->_classprefix = $classprefix ? $classprefix : 'J'; $this->_adapterfolder = $adapterfolder ? $adapterfolder : 'adapters'; $this->_db = JFactory::getDbo(); } /** * Get the database connector object * * @return JDatabaseDriver Database connector object * * @since 1.6 */ public function getDbo() { return $this->_db; } /** * Return an adapter. * * @param string $name Name of adapter to return * @param array $options Adapter options * * @return JAdapterInstance|boolean Adapter of type 'name' or false * * @since 1.6 */ public function getAdapter($name, $options = array()) { if (array_key_exists($name, $this->_adapters)) { return $this->_adapters[$name]; } if ($this->setAdapter($name, $options)) { return $this->_adapters[$name]; } return false; } /** * Set an adapter by name * * @param string $name Adapter name * @param object &$adapter Adapter object * @param array $options Adapter options * * @return boolean True if successful * * @since 1.6 */ public function setAdapter($name, &$adapter = null, $options = array()) { if (is_object($adapter)) { $this->_adapters[$name] = &$adapter; return true; } $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name); if (class_exists($class)) { $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name) . 'Adapter'; if (class_exists($class)) { $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } $fullpath = $this->_basepath . '/' . $this->_adapterfolder . '/' . strtolower($name) . '.php'; if (!file_exists($fullpath)) { return false; } // Try to load the adapter object $class = $this->_classprefix . ucfirst($name); JLoader::register($class, $fullpath); if (!class_exists($class)) { return false; } $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } /** * Loads all adapters. * * @param array $options Adapter options * * @return void * * @since 1.6 */ public function loadAllAdapters($options = array()) { $files = new DirectoryIterator($this->_basepath . '/' . $this->_adapterfolder); /* @type $file DirectoryIterator */ foreach ($files as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } // Try to load the adapter object require_once $this->_basepath . '/' . $this->_adapterfolder . '/' . $fileName; // Derive the class name from the filename. $name = str_ireplace('.php', '', ucfirst(trim($fileName))); $class = $this->_classprefix . ucfirst($name); if (!class_exists($class)) { // Skip to next one continue; } $adapter = new $class($this, $this->_db, $options); $this->_adapters[$name] = clone $adapter; } } } PK �c�[���"l l base/adapterinstance.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Base * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Adapter Instance Class * * @since 1.6 * @deprecated 5.0 Will be removed without replacement */ class JAdapterInstance extends JObject { /** * Parent * * @var JAdapter * @since 1.6 */ protected $parent = null; /** * Database * * @var JDatabaseDriver * @since 1.6 */ protected $db = null; /** * Constructor * * @param JAdapter $parent Parent object * @param JDatabaseDriver $db Database object * @param array $options Configuration Options * * @since 1.6 */ public function __construct(JAdapter $parent, JDatabaseDriver $db, array $options = array()) { // Set the properties from the options array that is passed in $this->setProperties($options); // Set the parent and db in case $options for some reason overrides it. $this->parent = $parent; // Pull in the global dbo in case something happened to it. $this->db = $db ?: JFactory::getDbo(); } /** * Retrieves the parent object * * @return JAdapter * * @since 1.6 */ public function getParent() { return $this->parent; } } PK �c�[h1*�q q controller/base.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Controller * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Application\AbstractApplication; /** * Joomla Platform Base Controller Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JControllerBase implements JController { /** * The application object. * * @var AbstractApplication * @since 3.0.0 */ protected $app; /** * The input object. * * @var JInput * @since 3.0.0 */ protected $input; /** * Instantiate the controller. * * @param JInput $input The input object. * @param AbstractApplication $app The application object. * * @since 3.0.0 */ public function __construct(JInput $input = null, AbstractApplication $app = null) { // Setup dependencies. $this->app = isset($app) ? $app : $this->loadApplication(); $this->input = isset($input) ? $input : $this->loadInput(); } /** * Get the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ public function getApplication() { return $this->app; } /** * Get the input object. * * @return JInput The input object. * * @since 3.0.0 */ public function getInput() { return $this->input; } /** * Serialize the controller. * * @return string The serialized controller. * * @since 3.0.0 */ public function serialize() { return serialize($this->input); } /** * Unserialize the controller. * * @param string $input The serialized controller. * * @return JController Supports chaining. * * @since 3.0.0 * @throws UnexpectedValueException if input is not the right class. */ public function unserialize($input) { // Setup dependencies. $this->app = $this->loadApplication(); // Unserialize the input. $this->input = unserialize($input); if (!($this->input instanceof JInput)) { throw new UnexpectedValueException(sprintf('%s::unserialize would not accept a `%s`.', get_class($this), gettype($this->input))); } return $this; } /** * Load the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ protected function loadApplication() { return JFactory::getApplication(); } /** * Load the input object. * * @return JInput The input object. * * @since 3.0.0 */ protected function loadInput() { return $this->app->input; } } PK �c�[�A� � controller/controller.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Controller * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Application\AbstractApplication; /** * Joomla Platform Controller Interface * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ interface JController extends Serializable { /** * Execute the controller. * * @return boolean True if controller finished execution, false if the controller did not * finish execution. A controller might return false if some precondition for * the controller to run has not been satisfied. * * @since 3.0.0 * @throws LogicException * @throws RuntimeException */ public function execute(); /** * Get the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ public function getApplication(); /** * Get the input object. * * @return JInput The input object. * * @since 3.0.0 */ public function getInput(); } PK �c�[�� database/database.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Database connector class. * * @since 1.7.0 * @deprecated 4.0 */ abstract class JDatabase { /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 */ public function query() { JLog::add('JDatabase::query() is deprecated, use JDatabaseDriver::execute() instead.', JLog::WARNING, 'deprecated'); return $this->execute(); } /** * Get a list of available database connectors. The list will only be populated with connectors that both * the class exists and the static test method returns true. This gives us the ability to have a multitude * of connector classes that are self-aware as to whether or not they are able to be used on a given system. * * @return array An array of available database connectors. * * @since 1.7.0 * @deprecated 4.0 */ public static function getConnectors() { JLog::add('JDatabase::getConnectors() is deprecated, use JDatabaseDriver::getConnectors() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::getConnectors(); } /** * Gets the error message from the database connection. * * @param boolean $escaped True to escape the message string for use in JavaScript. * * @return string The error message for the most recent query. * * @deprecated 4.0 * @since 1.7.0 */ public function getErrorMsg($escaped = false) { JLog::add('JDatabase::getErrorMsg() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); if ($escaped) { return addslashes($this->errorMsg); } else { return $this->errorMsg; } } /** * Gets the error number from the database connection. * * @return integer The error number for the most recent query. * * @since 1.7.0 * @deprecated 4.0 */ public function getErrorNum() { JLog::add('JDatabase::getErrorNum() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); return $this->errorNum; } /** * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to * be used for the connection. The 'select' option determines whether the connector should automatically select * the chosen database. * * Instances are unique to the given options and new objects are only created when a unique options array is * passed into the method. This ensures that we don't end up with unnecessary database connection resources. * * @param array $options Parameters to be passed to the database driver. * * @return JDatabaseDriver A database object. * * @since 1.7.0 * @deprecated 4.0 */ public static function getInstance($options = array()) { JLog::add('JDatabase::getInstance() is deprecated, use JDatabaseDriver::getInstance() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::getInstance($options); } /** * Splits a string of multiple queries into an array of individual queries. * * @param string $query Input SQL string with which to split into individual queries. * * @return array The queries from the input string separated into an array. * * @since 1.7.0 * @deprecated 4.0 */ public static function splitSql($query) { JLog::add('JDatabase::splitSql() is deprecated, use JDatabaseDriver::splitSql() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::splitSql($query); } /** * Return the most recent error message for the database connector. * * @param boolean $showSQL True to display the SQL statement sent to the database as well as the error. * * @return string The error message for the most recent query. * * @since 1.7.0 * @deprecated 4.0 */ public function stderr($showSQL = false) { JLog::add('JDatabase::stderr() is deprecated.', JLog::WARNING, 'deprecated'); if ($this->errorNum != 0) { return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $this->errorNum, $this->errorMsg) . ($showSQL ? "<br />SQL = <pre>$this->sql</pre>" : ''); } else { return JText::_('JLIB_DATABASE_FUNCTION_NOERROR'); } } /** * Test to see if the connector is available. * * @return boolean True on success, false otherwise. * * @since 1.7.0 * @deprecated 4.0 - Use JDatabaseDriver::isSupported() instead. */ public static function test() { JLog::add('JDatabase::test() is deprecated. Use JDatabaseDriver::isSupported() instead.', JLog::WARNING, 'deprecated'); return static::isSupported(); } } PK �c�[�kp0�7 �7 database/driver/mysql.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database driver * * @link https://dev.mysql.com/doc/ * @since 3.0.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseDriverMysql extends JDatabaseDriverMysqli { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'mysql'; /** * Constructor. * * @param array $options Array of database options with keys: host, user, password, database, select. * * @since 3.0.0 */ public function __construct($options) { // PHP's `mysql` extension is not present in PHP 7, block instantiation in this environment if (PHP_MAJOR_VERSION >= 7) { throw new JDatabaseExceptionUnsupported( 'This driver is unsupported in PHP 7, please use the MySQLi or PDO MySQL driver instead.' ); } // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the MySQL extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('Make sure the MySQL extension for PHP is installed and enabled.'); } // Attempt to connect to the server. if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true))) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL server.'); } // Set sql_mode to non_strict mode mysql_query("SET @@SESSION.sql_mode = '';", $this->connection); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Pre-populate the UTF-8 Multibyte compatibility flag based on server version $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); // Set the character set (needed for MySQL 4.1.2+). $this->utf = $this->setUtf(); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { mysql_query('SET query_cache_type = 0;', $this->connection); if ($this->hasProfiling()) { mysql_query('SET profiling_history_size = 100, profiling = 1;', $this->connection); } } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } mysql_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $this->connect(); $result = mysql_real_escape_string($text, $this->getConnection()); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return PHP_MAJOR_VERSION < 7 && function_exists('mysql_connect'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { if (is_resource($this->connection)) { return @mysql_ping($this->connection); } return false; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return mysql_affected_rows($this->connection); } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); return mysql_num_rows($cursor ? $cursor : $this->cursor); } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); return mysql_get_server_info($this->connection); } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); return mysql_insert_id($this->connection); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @mysql_query($query, $this->connection); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysql_select_db($database, $this->connection)) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { // If UTF is not supported return false immediately if (!$this->utf) { return false; } // Make sure we're connected to the server $this->connect(); // Which charset should I use, plain utf8 or multibyte utf8mb4? $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $result = @mysql_set_charset($charset, $this->connection); /** * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd * masks the server version and reports only its own we can not be sure if the server actually does support * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we * catch the error and determine that utf8mb4 is not supported! */ if (!$result && $this->utf8mb4) { $this->utf8mb4 = false; $result = @mysql_set_charset('utf8', $this->connection); } return $result; } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return mysql_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return mysql_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { mysql_free_result($cursor ? $cursor : $this->cursor); } /** * Internal function to check if profiling is available * * @return boolean * * @since 3.1.3 */ private function hasProfiling() { try { $res = mysql_query("SHOW VARIABLES LIKE 'have_profiling'", $this->connection); $row = mysql_fetch_assoc($res); return isset($row); } catch (Exception $e) { return false; } } /** * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? * * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. * * @return boolean * * @since CMS 3.5.0 */ private function serverClaimsUtf8mb4Support() { $client_version = mysql_get_client_info(); $server_version = $this->getVersion(); if (version_compare($server_version, '5.5.3', '<')) { return false; } else { if (strpos($client_version, 'mysqlnd') !== false) { $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); return version_compare($client_version, '5.0.9', '>='); } else { return version_compare($client_version, '5.5.3', '>='); } } } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) mysql_errno($this->connection); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) mysql_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } } PK �c�[�'�Ha Ha database/driver/mysqli.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQLi database driver * * @link https://www.php.net/manual/en/book.mysqli.php * @since 3.0.0 */ class JDatabaseDriverMysqli extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'mysqli'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mysql'; /** * @var mysqli The database connection resource. * @since 1.7.0 */ protected $connection; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.0.1 */ protected $nameQuote = '`'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.1 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var string The minimum supported database version. * @since 3.0.1 */ protected static $dbMinimum = '5.0.4'; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; $options['port'] = (isset($options['port'])) ? (int) $options['port'] : null; $options['socket'] = (isset($options['socket'])) ? $options['socket'] : null; // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } /* * Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we * have to extract them from the host string. */ $port = isset($this->options['port']) ? $this->options['port'] : 3306; $regex = '/^(?P<host>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P<port>.+))?$/'; if (preg_match($regex, $this->options['host'], $matches)) { // It's an IPv4 address with or without port $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^(?P<host>\[.*\])(:(?P<port>.+))?$/', $this->options['host'], $matches)) { // We assume square-bracketed IPv6 address with or without port, e.g. [fe80:102::2%eth1]:3306 $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^(?P<host>(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P<port>[^:]+))?$/i', $this->options['host'], $matches)) { // Named host (e.g example.com or localhost) with or without port $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^:(?P<port>[^:]+)$/', $this->options['host'], $matches)) { // Empty host, just port, e.g. ':3306' $this->options['host'] = 'localhost'; $port = $matches['port']; } // ... else we assume normal (naked) IPv6 address, so host and port stay as they are or default // Get the port number or socket name if (is_numeric($port)) { $this->options['port'] = (int) $port; } else { $this->options['socket'] = $port; } // Make sure the MySQLi extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The MySQLi extension for PHP is not installed or enabled.'); } $this->connection = @mysqli_connect( $this->options['host'], $this->options['user'], $this->options['password'], null, $this->options['port'], $this->options['socket'] ); // Attempt to connect to the server. if (!$this->connection) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL server.'); } // Set sql_mode to non_strict mode mysqli_query($this->connection, "SET @@SESSION.sql_mode = '';"); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Pre-populate the UTF-8 Multibyte compatibility flag based on server version $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); // Set the character set (needed for MySQL 4.1.2+). $this->utf = $this->setUtf(); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { mysqli_query($this->connection, 'SET query_cache_type = 0;'); if ($this->hasProfiling()) { mysqli_query($this->connection, 'SET profiling_history_size = 100, profiling = 1;'); } } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if ($this->connection instanceof mysqli && $this->connection->stat() !== false) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } mysqli_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $this->connect(); $result = mysqli_real_escape_string($this->getConnection(), $text); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('mysqli_connect'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { if (is_object($this->connection)) { return mysqli_ping($this->connection); } return false; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); $this->execute(); return $this; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return mysqli_affected_rows($this->connection); } /** * Method to get the database collation. * * @return mixed The collation in use by the database (string) or boolean false if not supported. * * @since 3.0.1 * @throws RuntimeException */ public function getCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { return mysqli_num_rows($cursor ? $cursor : $this->cursor); } /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); $result = array(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { // Set the query to get the table CREATE statement. $this->setQuery('SHOW CREATE table ' . $this->quoteName($this->escape($table))); $row = $this->loadRow(); // Populate the result array based on the create statements. $result[$table] = $row[1]; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.1 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); // Set the query to get the table fields statement. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table))); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $result[$field->Field] = $field; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.1 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // Get the details columns information. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); $keys = $this->loadObjectList(); return $keys; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.1 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SHOW TABLES'); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); return mysqli_get_server_info($this->connection); } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return mixed The value of the auto-increment field from the last inserted row. * If the value is greater than maximal int value, it will return a string. * * @since 3.0.0 */ public function insertid() { $this->connect(); return mysqli_insert_id($this->connection); } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); return $this; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } if (!is_object($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; $memoryBefore = null; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); if (is_object($this->cursor)) { // Avoid warning if result already freed by third-party library @$this->freeResult(); } $memoryBefore = memory_get_usage(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @mysqli_query($this->connection, $query); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } $this->callStacks[count($this->callStacks) - 1][0]['memory'] = array( $memoryBefore, memory_get_usage(), is_object($this->cursor) ? $this->getNumRows() : null, ); } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by MySQL. * @param string $prefix Not used by MySQL. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute(); return $this; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysqli_select_db($this->connection, $database)) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { // If UTF is not supported return false immediately if (!$this->utf) { return false; } // Make sure we're connected to the server $this->connect(); // Which charset should I use, plain utf8 or multibyte utf8mb4? $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $result = @$this->connection->set_charset($charset); /** * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd * masks the server version and reports only its own we can not be sure if the server actually does support * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we * catch the error and determine that utf8mb4 is not supported! */ if (!$result && $this->utf8mb4) { $this->utf8mb4 = false; $result = @$this->connection->set_charset('utf8'); } return $result; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return mysqli_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { mysqli_free_result($cursor ? $cursor : $this->cursor); if ((! $cursor) || ($cursor === $this->cursor)) { $this->cursor = null; } } /** * Unlocks tables in the database. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('UNLOCK TABLES')->execute(); return $this; } /** * Internal function to check if profiling is available * * @return boolean * * @since 3.1.3 */ private function hasProfiling() { try { $res = mysqli_query($this->connection, "SHOW VARIABLES LIKE 'have_profiling'"); $row = mysqli_fetch_assoc($res); return isset($row); } catch (Exception $e) { return false; } } /** * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? * * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. * * @return boolean * * @since CMS 3.5.0 */ private function serverClaimsUtf8mb4Support() { $client_version = mysqli_get_client_info(); $server_version = $this->getVersion(); if (version_compare($server_version, '5.5.3', '<')) { return false; } else { if (strpos($client_version, 'mysqlnd') !== false) { $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); return version_compare($client_version, '5.0.9', '>='); } else { return version_compare($client_version, '5.5.3', '>='); } } } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) mysqli_errno($this->connection); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) mysqli_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } } PK �c�[����= �= database/driver/oracle.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Oracle database driver * * @link https://www.php.net/pdo * @since 3.0.0 */ class JDatabaseDriverOracle extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'oracle'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'oracle'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '"'; /** * Returns the current dateformat * * @var string * @since 3.0.0 */ protected $dateformat; /** * Returns the current character set * * @var string * @since 3.0.0 */ protected $charset; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { $options['driver'] = 'oci'; $options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'AL32UTF8'; $options['dateformat'] = (isset($options['dateformat'])) ? $options['dateformat'] : 'RRRR-MM-DD HH24:MI:SS'; $this->charset = $options['charset']; $this->dateformat = $options['dateformat']; // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } parent::connect(); if (isset($this->options['schema'])) { $this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' . $this->quoteName($this->options['schema']))->execute(); } $this->setDateFormat($this->dateformat); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. $this->freeResult(); $this->connection = null; } /** * Drops a table from the database. * * Note: The IF EXISTS flag is unused in the Oracle driver. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true) ->setQuery('DROP TABLE :tableName'); $query->bind(':tableName', $tableName); $this->setQuery($query); $this->execute(); return $this; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { return $this->charset; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return $this->charset; } /** * Get a query to run and verify the database is operational. * * @return string The query to check the health of the DB. * * @since 3.0.1 */ public function getConnectedQuery() { return 'SELECT 1 FROM dual'; } /** * Returns the current date format * This method should be useful in the case that * somebody actually wants to use a different * date format and needs to check what the current * one is to see if it needs to be changed. * * @return string The current date format * * @since 3.0.0 */ public function getDateFormat() { return $this->dateformat; } /** * Shows the table CREATE statement that creates the given tables. * * Note: You must have the correct privileges before this method * will return usable results! * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); $result = array(); $query = $this->getQuery(true) ->select('dbms_metadata.get_ddl(:type, :tableName)') ->from('dual') ->bind(':type', 'TABLE'); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { $query->bind(':tableName', $table); $this->setQuery($query); $statement = (string) $this->loadResult(); $result[$table] = $statement; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $columns = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->select('*'); $query->from('ALL_TAB_COLUMNS'); $query->where('table_name = :tableName'); $prefixedTable = str_replace('#__', strtoupper($this->tablePrefix), $table); $query->bind(':tableName', $prefixedTable); $this->setQuery($query); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $columns[$field->COLUMN_NAME] = $field->DATA_TYPE; } } else { foreach ($fields as $field) { $columns[$field->COLUMN_NAME] = $field; $columns[$field->COLUMN_NAME]->Default = null; } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $columns; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->select('*') ->from('ALL_CONSTRAINTS') ->where('table_name = :tableName') ->bind(':tableName', $table); $this->setQuery($query); $keys = $this->loadObjectList(); $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $keys; } /** * Method to get an array of all tables in the database (schema). * * @param string $databaseName The database (schema) name * @param boolean $includeDatabaseName Whether to include the schema name in the results * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList($databaseName = null, $includeDatabaseName = false) { $this->connect(); $query = $this->getQuery(true); if ($includeDatabaseName) { $query->select('owner, table_name'); } else { $query->select('table_name'); } $query->from('all_tables'); if ($databaseName) { $query->where('owner = :database') ->bind(':database', $databaseName); } $query->order('table_name'); $this->setQuery($query); if ($includeDatabaseName) { $tables = $this->loadAssocList(); } else { $tables = $this->loadColumn(); } return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $this->setQuery("select value from nls_database_parameters where parameter = 'NLS_RDBMS_VERSION'"); return $this->loadResult(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Sets the Oracle Date Format for the session * Default date format for Oracle is = DD-MON-RR * The default date format for this driver is: * 'RRRR-MM-DD HH24:MI:SS' since it is the format * that matches the MySQL one used within most Joomla * tables. * * @param string $dateFormat Oracle Date Format String * * @return boolean * * @since 3.0.0 */ public function setDateFormat($dateFormat = 'DD-MON-RR') { $this->connect(); $this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT = '$dateFormat'"); if (!$this->execute()) { return false; } $this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = '$dateFormat'"); if (!$this->execute()) { return false; } $this->dateformat = $dateFormat; return true; } /** * Set the connection to use UTF-8 character encoding. * * Returns false automatically for the Oracle driver since * you can only set the character set when the connection * is created. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLE ' . $this->quoteName($table) . ' IN EXCLUSIVE MODE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by Oracle. * @param string $prefix Not used by Oracle. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME ' . $oldTable . ' TO ' . $newTable)->execute(); return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('COMMIT')->execute(); return $this; } /** * Test to see if the PDO ODBC connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return class_exists('PDO') && in_array('oci', PDO::getAvailableDrivers()); } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 1.7.0 */ public function replacePrefix($query, $prefix = '#__') { $startPos = 0; $quoteChar = "'"; $literal = ''; $query = trim($query); $n = strlen($query); while ($startPos < $n) { $ip = strpos($query, $prefix, $startPos); if ($ip === false) { break; } $j = strpos($query, "'", $startPos); if ($j === false) { $j = $n; } $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos)); $startPos = $j; $j = $startPos + 1; if ($j >= $n) { break; } // Quote comes first, find end of quote while (true) { $k = strpos($query, $quoteChar, $j); $escaped = false; if ($k === false) { break; } $l = $k - 1; while ($l >= 0 && $query[$l] == '\\') { $l--; $escaped = !$escaped; } if ($escaped) { $j = $k + 1; continue; } break; } if ($k === false) { // Error in the query - no end quote; ignore it break; } $literal .= substr($query, $startPos, $k - $startPos + 1); $startPos = $k + 1; } if ($startPos < $n) { $literal .= substr($query, $startPos, $n - $startPos); } return $literal; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { return parent::transactionStart($asSavepoint); } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK �c�[:�֍�g �g database/driver/pdo.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform PDO Database Driver Class * * @link https://www.php.net/pdo * @since 3.0.0 */ abstract class JDatabaseDriverPdo extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'pdo'; /** * @var PDO The database connection resource. * @since 3.0.0 */ protected $connection; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = "'"; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.0 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var resource The prepared statement. * @since 3.0.0 */ protected $prepared; /** * Contains the current query execution status * * @var array * @since 3.0.0 */ protected $executed = false; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['driver'] = (isset($options['driver'])) ? $options['driver'] : 'odbc'; $options['dsn'] = (isset($options['dsn'])) ? $options['dsn'] : ''; $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['driverOptions'] = (isset($options['driverOptions'])) ? $options['driverOptions'] : array(); $hostParts = explode(':', $options['host']); if (!empty($hostParts[1])) { $options['host'] = $hostParts[0]; $options['port'] = $hostParts[1]; } // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the PDO extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('PDO Extension is not available.', 1); } $replace = array(); $with = array(); // Find the correct PDO DSN Format to use: switch ($this->options['driver']) { case 'cubrid': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 33000; $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'dblib': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'firebird': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3050; $format = 'firebird:dbname=#DBNAME#'; $replace = array('#DBNAME#'); $with = array($this->options['database']); break; case 'ibm': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 56789; if (!empty($this->options['dsn'])) { $format = 'ibm:DSN=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); } break; case 'informix': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1526; $this->options['protocol'] = (isset($this->options['protocol'])) ? $this->options['protocol'] : 'onsoctcp'; if (!empty($this->options['dsn'])) { $format = 'informix:DSN=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#SERVER#', '#PROTOCOL#'); $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['server'], $this->options['protocol']); } break; case 'mssql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; // The pdomysql case is a special case within the CMS environment case 'pdomysql': case 'mysql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3306; $format = 'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#CHARSET#'); $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['charset']); break; case 'oci': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1521; $this->options['charset'] = (isset($this->options['charset'])) ? $this->options['charset'] : 'AL32UTF8'; if (!empty($this->options['dsn'])) { $format = 'oci:dbname=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); } $format .= ';charset=' . $this->options['charset']; break; case 'odbc': $format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#'; $replace = array('#DSN#', '#USER#', '#PASSWORD#'); $with = array($this->options['dsn'], $this->options['user'], $this->options['password']); break; case 'pgsql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 5432; $format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'sqlite': if (isset($this->options['version']) && $this->options['version'] == 2) { $format = 'sqlite2:#DBNAME#'; } else { $format = 'sqlite:#DBNAME#'; } $replace = array('#DBNAME#'); $with = array($this->options['database']); break; case 'sybase': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; } // Create the connection string: $connectionString = str_replace($replace, $with, $format); try { $this->connection = new PDO( $connectionString, $this->options['user'], $this->options['password'], $this->options['driverOptions'] ); } catch (PDOException $e) { throw new JDatabaseExceptionConnecting('Could not connect to PDO: ' . $e->getMessage(), 2, $e); } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } $this->freeResult(); $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * Oracle escaping reference: * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F * * SQLite escaping notes: * http://www.sqlite.org/faq.html#q14 * * Method body is as implemented by the Zend Framework * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $text = str_replace("'", "''", $text); return addcslashes($text, "\000\n\r\\\032"); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!is_object($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // Execute the query. $this->executed = false; if ($this->prepared instanceof PDOStatement) { // Bind the variables: if ($this->sql instanceof JDatabaseQueryPreparable) { $bounded = $this->sql->getBounded(); foreach ($bounded as $key => $obj) { $this->prepared->bindParam($key, $obj->value, $obj->dataType, $obj->length, $obj->driverOptions); } } $this->executed = $this->prepared->execute(); } if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->executed) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->prepared; } /** * Retrieve a PDO database connection attribute * * Usage: $db->getOption(PDO::ATTR_CASE); * * @param mixed $key One of the PDO::ATTR_* Constants * * @return mixed * * @link https://www.php.net/manual/en/pdo.getattribute.php * @since 3.0.0 */ public function getOption($key) { $this->connect(); return $this->connection->getAttribute($key); } /** * Get a query to run and verify the database is operational. * * @return string The query to check the health of the DB. * * @since 3.0.1 */ public function getConnectedQuery() { return 'SELECT 1'; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.9.0 */ public function getVersion() { $this->connect(); return $this->getOption(PDO::ATTR_SERVER_VERSION); } /** * Sets an attribute on the PDO database handle. * * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); * * @param integer $key One of the PDO::ATTR_* Constants * @param mixed $value One of the associated PDO Constants related to the particular attribute key. * * @return boolean * * @link https://www.php.net/manual/en/pdo.setattribute.php * @since 3.0.0 */ public function setOption($key, $value) { $this->connect(); return $this->connection->setAttribute($key, $value); } /** * Test to see if the PDO extension is available. * Override as needed to check for specific PDO Drivers. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return defined('PDO::ATTR_DRIVER_NAME'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { // Flag to prevent recursion into this function. static $checkingConnected = false; if ($checkingConnected) { // Reset this flag and throw an exception. $checkingConnected = true; die('Recursion trying to check if connected.'); } // Backup the query state. $query = $this->sql; $limit = $this->limit; $offset = $this->offset; $prepared = $this->prepared; try { // Set the checking connection flag. $checkingConnected = true; // Run a simple query to check the connection. $this->setQuery($this->getConnectedQuery()); $status = (bool) $this->loadResult(); } // If we catch an exception here, we must not be connected. catch (Exception $e) { $status = false; } // Restore the query state. $this->sql = $query; $this->limit = $limit; $this->offset = $offset; $this->prepared = $prepared; $checkingConnected = false; return $status; } /** * Get the number of affected rows for the previous executed SQL statement. * Only applicable for DELETE, INSERT, or UPDATE statements. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); if ($this->prepared instanceof PDOStatement) { return $this->prepared->rowCount(); } else { return 0; } } /** * Get the number of returned rows for the previous executed SQL statement. * Only applicable for DELETE, INSERT, or UPDATE statements. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); if ($cursor instanceof PDOStatement) { return $cursor->rowCount(); } elseif ($this->prepared instanceof PDOStatement) { return $this->prepared->rowCount(); } else { return 0; } } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return string The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); // Error suppress this to prevent PDO warning us that the driver doesn't support this operation. return @$this->connection->lastInsertId(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Sets the SQL statement string for later execution. * * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string. * @param integer $offset The affected row offset to set. * @param integer $limit The maximum affected rows to set. * @param array $driverOptions The optional PDO driver options. * * @return JDatabaseDriver This object to support method chaining. * * @since 3.0.0 */ public function setQuery($query, $offset = null, $limit = null, $driverOptions = array()) { $this->connect(); $this->freeResult(); if (is_string($query)) { // Allows taking advantage of bound variables in a direct query: $query = $this->getQuery(true)->setQuery($query); } if ($query instanceof JDatabaseQueryLimitable && !is_null($offset) && !is_null($limit)) { $query = $query->processLimit($query, $limit, $offset); } // Create a stringified version of the query (with prefixes replaced): $sql = $this->replacePrefix((string) $query); // Use the stringified version in the prepare call: $this->prepared = $this->connection->prepare($sql, $driverOptions); // Store reference to the original JDatabaseQuery instance within the class. // This is important since binding variables depends on it within execute(): parent::setQuery($query, $offset, $limit); return $this; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth == 1) { $this->connection->commit(); } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth == 1) { $this->connection->rollBack(); } $this->transactionDepth--; } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { $this->connection->beginTransaction(); } $this->transactionDepth++; } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetch(PDO::FETCH_NUM); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetch(PDO::FETCH_NUM); } } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetch(PDO::FETCH_ASSOC); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetch(PDO::FETCH_ASSOC); } } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class Unused, only necessary so method signature will be the same as parent. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetchObject($class); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetchObject($class); } } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { $this->executed = false; if ($cursor instanceof PDOStatement) { $cursor->closeCursor(); $cursor = null; } if ($this->prepared instanceof PDOStatement) { $this->prepared->closeCursor(); $this->prepared = null; } } /** * Method to get the next row in the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchObject(null, $class)) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException */ public function loadNextAssoc() { $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchAssoc()) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextRow() { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchArray()) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * PDO does not support serialize * * @return array * * @since 3.1.4 */ public function __sleep() { $serializedProperties = array(); $reflect = new ReflectionClass($this); // Get properties of the current class $properties = $reflect->getProperties(); foreach ($properties as $property) { // Do not serialize properties that are PDO if ($property->isStatic() == false && !($this->{$property->name} instanceof PDO)) { $serializedProperties[] = $property->name; } } return $serializedProperties; } /** * Wake up after serialization * * @return array * * @since 3.1.4 */ public function __wakeup() { // Get connection back $this->__construct($this->options); } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) $this->connection->errorCode(); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { // The SQL Error Information $errorInfo = implode(', ', $this->connection->errorInfo()); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorInfo = str_replace($this->tablePrefix, '#__', $errorInfo); } return $errorInfo; } } PK �c�[ �CQ5 Q5 database/driver/pdomysql.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database driver supporting PDO based connections * * @link https://www.php.net/manual/en/ref.pdo-mysql.php * @since 3.4 */ class JDatabaseDriverPdomysql extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.4 */ public $name = 'pdomysql'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mysql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.4 */ protected $nameQuote = '`'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.4 */ protected $nullDate = '0000-00-00 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.4 */ protected static $dbMinimum = '5.0.4'; /** * Constructor. * * @param array $options Array of database options with keys: host, user, password, database, select. * * @since 3.4 */ public function __construct($options) { // Get some basic values from the options. $options['driver'] = 'mysql'; if (!isset($options['charset']) || $options['charset'] == 'utf8') { $options['charset'] = 'utf8mb4'; } /** * Pre-populate the UTF-8 Multibyte compatibility flag. Unfortunately PDO won't report the server version * unless we're connected to it, and we cannot connect to it unless we know if it supports utf8mb4, which requires * us knowing the server version. Because of this chicken and egg issue, we _assume_ it's supported and we'll just * catch any problems at connection time. */ $this->utf8mb4 = ($options['charset'] == 'utf8mb4'); // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.4 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } try { // Try to connect to MySQL parent::connect(); } catch (\RuntimeException $e) { // If the connection failed, but not because of the wrong character set, then bubble up the exception. if (!$this->utf8mb4) { throw $e; } /* * Otherwise, try connecting again without using * utf8mb4 and see if maybe that was the problem. If the * connection succeeds, then we will have learned that the * client end of the connection does not support utf8mb4. */ $this->utf8mb4 = false; $this->options['charset'] = 'utf8'; parent::connect(); } if ($this->utf8mb4) { /* * At this point we know the client supports utf8mb4. Now * we must check if the server supports utf8mb4 as well. */ $serverVersion = $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); $this->utf8mb4 = version_compare($serverVersion, '5.5.3', '>='); if (!$this->utf8mb4) { // Reconnect with the utf8 character set. parent::disconnect(); $this->options['charset'] = 'utf8'; parent::connect(); } } $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); // Set sql_mode to non_strict mode $this->connection->query("SET @@SESSION.sql_mode = '';"); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { $this->connection->query('SET query_cache_type = 0;'); if ($this->hasProfiling()) { $this->connection->query('SET profiling_history_size = 100, profiling = 1;'); } } } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.4 */ public static function isSupported() { return class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers()); } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); $this->setQuery($query); $this->execute(); return $this; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.4 * @throws RuntimeException */ public function select($database) { $this->connect(); $this->setQuery('USE ' . $this->quoteName($database)); $this->execute(); return $this; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database (string) or boolean false if not supported. * * @since 3.4 * @throws RuntimeException */ public function getCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.4 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); // Initialise variables. $result = array(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table)); $row = $this->loadRow(); // Populate the result array based on the create statements. $result[$table] = $row[1]; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.4 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); // Set the query to get the table fields statement. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table)); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $result[$field->Field] = $field; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.4 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // Get the details columns information. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); $keys = $this->loadObjectList(); return $keys; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.4 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SHOW TABLES'); $tables = $this->loadColumn(); return $tables; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by MySQL. * @param string $prefix Not used by MySQL. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable)); $this->execute(); return $this; } /** * Method to escape a string for usage in an SQL statement. * * Oracle escaping reference: * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F * * SQLite escaping notes: * http://www.sqlite.org/faq.html#q14 * * Method body is as implemented by the Zend Framework * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.4 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $this->connect(); $result = substr($this->connection->quote($text), 1, -1); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Unlocks tables in the database. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('UNLOCK TABLES')->execute(); return $this; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { parent::transactionStart($asSavepoint); } else { $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } } /** * Internal function to check if profiling is available. * * @return boolean * * @since 3.9.1 */ private function hasProfiling() { $result = $this->setQuery("SHOW VARIABLES LIKE 'have_profiling'")->loadAssoc(); return isset($result); } } PK �c�[��g g database/driver/pgsql.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL PDO Database Driver * * @link https://www.php.net/manual/en/ref.pdo-mysql.php * @since 3.9.0 */ class JDatabaseDriverPgsql extends JDatabaseDriverPdo { /** * The database driver name * * @var string * @since 3.9.0 */ public $name = 'pgsql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.9.0 */ protected $nameQuote = '"'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.9.0 */ protected $nullDate = '1970-01-01 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.9.0 */ protected static $dbMinimum = '8.3.18'; /** * Operator used for concatenation * * @var string * @since 3.9.0 */ protected $concat_operator = '||'; /** * Database object constructor * * @param array $options List of options used to configure the connection * * @since 3.9.0 */ public function __construct($options) { $options['driver'] = 'pgsql'; $options['host'] = isset($options['host']) ? $options['host'] : 'localhost'; $options['user'] = isset($options['user']) ? $options['user'] : ''; $options['password'] = isset($options['password']) ? $options['password'] : ''; $options['database'] = isset($options['database']) ? $options['database'] : ''; $options['port'] = isset($options['port']) ? $options['port'] : null; // Finalize initialization parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.9.0 * @throws \RuntimeException */ public function connect() { if ($this->getConnection()) { return; } parent::connect(); $this->setQuery('SET standard_conforming_strings = off')->execute(); } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return boolean true * * @since 3.9.0 * @throws \RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName))->execute(); return true; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.9.0 * @throws \RuntimeException */ public function getCollation() { $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Method to get the database connection collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database connection (string) or boolean false if not supported. * * @since 3.9.0 * @throws \RuntimeException */ public function getConnectionCollation() { $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Internal function to get the name of the default schema for the current PostgreSQL connection. * That is the schema where tables are created by Joomla. * * @return string * * @since 3.9.24 */ private function getDefaultSchema() { // Supported since PostgreSQL 7.3 $this->setQuery('SELECT (current_schemas(false))[1]'); return $this->loadResult(); } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by PostgreSQL. * * @param mixed $tables A table name or a list of table names. * * @return string An empty string because this function is not supported by PostgreSQL. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableCreate($tables) { return ''; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); $tableSub = $this->replacePrefix($table); $defaultSchema = $this->getDefaultSchema(); $this->setQuery(' SELECT a.attname AS "column_name", pg_catalog.format_type(a.atttypid, a.atttypmod) as "type", CASE WHEN a.attnotnull IS TRUE THEN \'NO\' ELSE \'YES\' END AS "null", CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) END as "Default", CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL THEN \'\' ELSE pg_catalog.col_description(a.attrelid, a.attnum) END AS "comments" FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid WHERE a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . ' AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ' . $this->quote($defaultSchema) . ') ) AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum' ); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $result[$field->column_name] = preg_replace('/[(0-9)]/', '', $field->type); } } else { foreach ($fields as $field) { if (stristr(strtolower($field->type), 'character varying')) { $field->Default = ''; } if (stristr(strtolower($field->type), 'text')) { $field->Default = ''; } // Do some dirty translation to MySQL output. // @todo: Come up with and implement a standard across databases. $result[$field->column_name] = (object) array( 'column_name' => $field->column_name, 'type' => $field->type, 'null' => $field->null, 'Default' => $field->Default, 'comments' => '', 'Field' => $field->column_name, 'Type' => $field->type, 'Null' => $field->null, // @todo: Improve query above to return primary key info as well // 'Key' => ($field->PK == '1' ? 'PRI' : '') ); } } // Change Postgresql's NULL::* type with PHP's null one foreach ($fields as $field) { if (preg_match('/^NULL::*/', $field->Default)) { $field->Default = null; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableKeys($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList, true)) { // Get the details columns information. $this->setQuery(' SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", CASE WHEN indisprimary = true THEN ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true) FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname ) ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true) END AS "Query" FROM pg_indexes LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey' ); return $this->loadObjectList(); } return false; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableList() { $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type = ' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')') ->order('table_name ASC'); $this->setQuery($query); return $this->loadColumn(); } /** * Get the details list of sequences for a table. * * @param string $table The name of the table. * * @return array An array of sequences specification for the table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableSequences($table) { // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList, true)) { $name = array( 's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type', 'info.minimum_value', 'info.maximum_value', 'info.increment', 'info.cycle_option' ); $as = array('sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option'); if (version_compare($this->getVersion(), '9.1.0') >= 0) { $name[] .= 'info.start_value'; $as[] .= 'start_value'; } // Get the details columns information. $query = $this->getQuery(true) ->select($this->quoteName($name, $as)) ->from('pg_class AS s') ->leftJoin("pg_depend d ON d.objid = s.oid AND d.classid = 'pg_class'::regclass AND d.refclassid = 'pg_class'::regclass") ->leftJoin('pg_class t ON t.oid = d.refobjid') ->leftJoin('pg_namespace n ON n.oid = t.relnamespace') ->leftJoin('pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubid') ->leftJoin('information_schema.sequences AS info ON info.sequence_name = s.relname') ->where('s.relkind = ' . $this->quote('S') . ' AND d.deptype = ' . $this->quote('a') . ' AND t.relname = ' . $this->quote($table)); $this->setQuery($query); return $this->loadObjectList(); } return false; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function lockTable($tableName) { $this->transactionStart(); $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by PostgreSQL. * @param string $prefix Not used by PostgreSQL. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); // Origin Table does not exist if (!in_array($oldTable, $tableList, true)) { // Origin Table not found throw new \RuntimeException('Table not found in Postgresql database.'); } // Rename indexes $subQuery = $this->getQuery(true) ->select('indexrelid') ->from('pg_index, pg_class') ->where('pg_class.relname = ' . $this->quote($oldTable)) ->where('pg_class.oid = pg_index.indrelid'); $this->setQuery( $this->getQuery(true) ->select('relname') ->from('pg_class') ->where('oid IN (' . (string) $subQuery . ')') ); $oldIndexes = $this->loadColumn(); foreach ($oldIndexes as $oldIndex) { $changedIdxName = str_replace($oldTable, $newTable, $oldIndex); $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName))->execute(); } // Rename sequences $subQuery = $this->getQuery(true) ->select('oid') ->from('pg_namespace') ->where('nspname NOT LIKE ' . $this->quote('pg_%')) ->where('nspname != ' . $this->quote('information_schema')); $this->setQuery( $this->getQuery(true) ->select('relname') ->from('pg_class') ->where('relkind = ' . $this->quote('S')) ->where('relnamespace IN (' . (string) $subQuery . ')') ->where('relname LIKE ' . $this->quote("%$oldTable%")) ); $oldSequences = $this->loadColumn(); foreach ($oldSequences as $oldSequence) { $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence); $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName))->execute(); } // Rename table $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable))->execute(); return true; } /** * This function return a field value as a prepared string to be used in a SQL statement. * * @param array $columns Array of table's column returned by ::getTableColumns. * @param string $fieldName The table field's name. * @param string $fieldValue The variable value to quote and return. * * @return string The quoted string. * * @since 3.9.0 */ public function sqlValue($columns, $fieldName, $fieldValue) { switch ($columns[$fieldName]) { case 'boolean': $val = 'NULL'; if ($fieldValue === 't' || $fieldValue === true || $fieldValue === 1 || $fieldValue === '1') { $val = 'TRUE'; } elseif ($fieldValue === 'f' || $fieldValue === false || $fieldValue === 0 || $fieldValue === '0') { $val = 'FALSE'; } break; case 'bigint': case 'bigserial': case 'integer': case 'money': case 'numeric': case 'real': case 'smallint': case 'serial': case 'numeric,': $val = $fieldValue === '' ? 'NULL' : $fieldValue; break; case 'date': case 'timestamp without time zone': if (empty($fieldValue)) { $fieldValue = $this->getNullDate(); } $val = $this->quote($fieldValue); break; default: $val = $this->quote($fieldValue); break; } return $val; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute(); } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.9.0 * @throws \RuntimeException */ public function insertObject($table, &$object, $key = null) { $columns = $this->getTableColumns($table); $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Skip columns that don't exist in the table. if (!array_key_exists($k, $columns)) { continue; } // Only process non-null scalars. if (is_array($v) || is_object($v) || $v === null) { continue; } // Ignore any internal fields or primary keys with value 0. if (($k[0] === '_') || ($k == $key && (($v === 0) || ($v === '0')))) { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->sqlValue($columns, $k, $v); } // Create the base insert statement. $query = $this->getQuery(true); $query->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); $retVal = false; if ($key) { $query->returning($key); // Set the query and execute the insert. $this->setQuery($query); $id = $this->loadResult(); if ($id) { $object->$key = $id; $retVal = true; } } else { // Set the query and execute the insert. $this->setQuery($query); if ($this->execute()) { $retVal = true; } } return $retVal; } /** * Test to see if the PostgreSQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.9.0 */ public static function isSupported() { return class_exists('PDO') && in_array('pgsql', PDO::getAvailableDrivers(), true); } /** * Returns an array containing database's table list. * * @return array The database's table list. * * @since 3.9.0 */ public function showTables() { $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type=' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )'); $this->setQuery($query); return $this->loadColumn(); } /** * Get the substring position inside a string * * @param string $substring The string being sought * @param string $string The string/column being searched * * @return integer The position of $substring in $string * * @since 3.9.0 */ public function getStringPositionSql($substring, $string) { $this->setQuery("SELECT POSITION($substring IN $string)"); $position = $this->loadRow(); return $position['position']; } /** * Generate a random value * * @return float The random generated number * * @since 3.9.0 */ public function getRandom() { $this->setQuery('SELECT RANDOM()'); $random = $this->loadAssoc(); return $random['random']; } /** * Get the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.9.0 */ public function getAlterDbCharacterSet($dbName) { return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8'); } /** * Get the query string to create new Database in correct PostgreSQL syntax. * * @param object $options object coming from "initialise" function to pass user and database name to database driver. * @param boolean $utf True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query. * * @return string The query that creates database, owned by $options['user'] * * @since 3.9.0 */ public function getCreateDbQuery($options, $utf) { $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user); if ($utf) { $query .= ' ENCODING ' . $this->quote('UTF-8'); } return $query; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the <var>tablePrefix</var> class variable. * * @param string $sql The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.9.0 */ public function replacePrefix($sql, $prefix = '#__') { $sql = trim($sql); if (strpos($sql, '\'')) { // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'currval')) { $sql = explode('currval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('currval', $sql); } // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'nextval')) { $sql = explode('nextval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('nextval', $sql); } // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'setval')) { $sql = explode('setval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('setval', $sql); } $explodedQuery = explode('\'', $sql); for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex < $nIndexMax; $nIndex += 2) { if (strpos($explodedQuery[$nIndex], $prefix)) { $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]); } } $replacedQuery = implode('\'', $explodedQuery); } else { $replacedQuery = str_replace($prefix, $this->tablePrefix, $sql); } return $replacedQuery; } /** * Unlocks tables in the database, this command does not exist in PostgreSQL, it is automatically done on commit or rollback. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function unlockTables() { $this->transactionCommit(); return $this; } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean * * @since 3.9.0 * @throws \RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $columns = $this->getTableColumns($table); $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Skip columns that don't exist in the table. if (! array_key_exists($k, $columns)) { continue; } // Only process scalars that are not internal fields. if (is_array($v) || is_object($v) || $k[0] === '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key, true)) { $key_val = $this->sqlValue($columns, $k, $v); $where[] = $this->quoteName($k) . '=' . $key_val; continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we do not want to update nulls then ignore this field. if (!$nulls) { continue; } // If the value is null and we want to update nulls then set it. $val = 'NULL'; } else // The field is not null so we prep it for update. { $val = $this->sqlValue($columns, $k, $v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { return "decode('" . bin2hex($data) . "', 'hex')"; } } PK �c�[�t�֠ ֠ database/driver/postgresql.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL database driver * * @since 3.0.0 * @deprecated 4.0 Use PDO PostgreSQL instead */ class JDatabaseDriverPostgresql extends JDatabaseDriver { /** * The database driver name * * @var string * @since 3.0.0 */ public $name = 'postgresql'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'postgresql'; /** * Quote for named objects * * @var string * @since 3.0.0 */ protected $nameQuote = '"'; /** * The null/zero date string * * @var string * @since 3.0.0 */ protected $nullDate = '1970-01-01 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.0.0 */ protected static $dbMinimum = '8.3.18'; /** * Operator used for concatenation * * @var string * @since 3.0.0 */ protected $concat_operator = '||'; /** * JDatabaseDriverPostgresqlQuery object returned by getQuery * * @var JDatabaseQueryPostgresql * @since 3.0.0 */ protected $queryObject = null; /** * Database object constructor * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['port'] = (isset($options['port'])) ? $options['port'] : null; // Finalize initialization parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the postgresql extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The pgsql extension for PHP is not installed or enabled.'); } /* * pg_connect() takes the port as separate argument. Therefore, we * have to extract it from the host string (if provided). */ // Check for empty port if (!$this->options['port']) { // Port is empty or not set via options, check for port annotation (:) in the host string $tmp = substr(strstr($this->options['host'], ':'), 1); if (!empty($tmp)) { // Get the port number if (is_numeric($tmp)) { $this->options['port'] = $tmp; } // Extract the host name $this->options['host'] = substr($this->options['host'], 0, strlen($this->options['host']) - (strlen($tmp) + 1)); // This will take care of the following notation: ":5432" if ($this->options['host'] === '') { $this->options['host'] = 'localhost'; } } // No port annotation (:) found, setting port to default PostgreSQL port 5432 else { $this->options['port'] = '5432'; } } // Build the DSN for the connection. $dsn = ''; if (!empty($this->options['host'])) { $dsn .= "host={$this->options['host']} port={$this->options['port']} "; } $dsn .= "dbname={$this->options['database']} user={$this->options['user']} password={$this->options['password']}"; // Attempt to connect to the server. if (!($this->connection = @pg_connect($dsn))) { throw new JDatabaseExceptionConnecting('Error connecting to PGSQL database.'); } pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT); pg_query($this->connection, 'SET standard_conforming_strings=off'); pg_query($this->connection, 'SET escape_string_warning=off'); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } pg_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $this->connect(); $result = pg_escape_string($this->connection, $text); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the PostgreSQL connector is available * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function test() { return function_exists('pg_connect'); } /** * Determines if the connection to the server is active. * * @return boolean * * @since 3.0.0 */ public function connected() { $this->connect(); if (is_resource($this->connection)) { return pg_ping($this->connection); } return false; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return boolean * * @since 3.0.0 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); $this->execute(); return true; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows in the previous operation * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return pg_affected_rows($this->cursor); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 * @throws RuntimeException */ public function getCollation() { $this->connect(); $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return pg_client_encoding($this->connection); } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cur An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cur = null) { $this->connect(); return pg_num_rows((int) $cur ? $cur : $this->cursor); } /** * Get the current or query, or new JDatabaseQuery object. * * @param boolean $new False to return the last query set, True to return a new JDatabaseQuery object. * @param boolean $asObj False to return last query as string, true to get JDatabaseQueryPostgresql object. * * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class. * * @since 3.0.0 * @throws RuntimeException */ public function getQuery($new = false, $asObj = false) { if ($new) { $this->queryObject = new JDatabaseQueryPostgresql($this); return $this->queryObject; } else { if ($asObj) { return $this->queryObject; } else { return $this->sql; } } } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by PostgreSQL. * * @param mixed $tables A table name or a list of table names. * * @return string An empty char because this function is not supported by PostgreSQL. * * @since 3.0.0 */ public function getTableCreate($tables) { return ''; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. For PostgreSQL may start with a schema. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); $tableSub = $this->replacePrefix($table); $fn = explode('.', $tableSub); if (count($fn) === 2) { $schema = $fn[0]; $tableSub = $fn[1]; } else { $schema = $this->getDefaultSchema(); } $this->setQuery(' SELECT a.attname AS "column_name", pg_catalog.format_type(a.atttypid, a.atttypmod) as "type", CASE WHEN a.attnotnull IS TRUE THEN \'NO\' ELSE \'YES\' END AS "null", CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) END as "Default", CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL THEN \'\' ELSE pg_catalog.col_description(a.attrelid, a.attnum) END AS "comments" FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid WHERE a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . ' AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ' . $this->quote($schema) . ') ) AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum' ); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $result[$field->column_name] = preg_replace('/[(0-9)]/', '', $field->type); } } else { foreach ($fields as $field) { if (stristr(strtolower($field->type), 'character varying')) { $field->Default = ''; } if (stristr(strtolower($field->type), 'text')) { $field->Default = ''; } // Do some dirty translation to MySQL output. // TODO: Come up with and implement a standard across databases. $result[$field->column_name] = (object) array( 'column_name' => $field->column_name, 'type' => $field->type, 'null' => $field->null, 'Default' => $field->Default, 'comments' => '', 'Field' => $field->column_name, 'Type' => $field->type, 'Null' => $field->null, // TODO: Improve query above to return primary key info as well // 'Key' => ($field->PK == '1' ? 'PRI' : '') ); } } /* Change Postgresql's NULL::* type with PHP's null one */ foreach ($fields as $field) { if (preg_match('/^NULL::*/', $field->Default)) { $field->Default = null; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList)) { // Get the details columns information. $this->setQuery(' SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", CASE WHEN indisprimary = true THEN ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true) FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname ) ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true) END AS "Query" FROM pg_indexes LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey' ); $keys = $this->loadObjectList(); return $keys; } return false; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type=' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')') ->order('table_name ASC'); $this->setQuery($query); $tables = $this->loadColumn(); return $tables; } /** * Get the details list of sequences for a table. * * @param string $table The name of the table. * * @return array An array of sequences specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableSequences($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList)) { $name = array( 's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type', 'info.minimum_value', 'info.maximum_value', 'info.increment', 'info.cycle_option', ); $as = array('sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option'); if (version_compare($this->getVersion(), '9.1.0') >= 0) { $name[] .= 'info.start_value'; $as[] .= 'start_value'; } // Get the details columns information. $query = $this->getQuery(true) ->select($this->quoteName($name, $as)) ->from('pg_class AS s') ->join('LEFT', "pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass") ->join('LEFT', 'pg_class t ON t.oid=d.refobjid') ->join('LEFT', 'pg_namespace n ON n.oid=t.relnamespace') ->join('LEFT', 'pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid') ->join('LEFT', 'information_schema.sequences AS info ON info.sequence_name=s.relname') ->where("s.relkind='S' AND d.deptype='a' AND t.relname=" . $this->quote($table)); $this->setQuery($query); $seq = $this->loadObjectList(); return $seq; } return false; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $version = pg_version($this->connection); return $version['server']; } /** * Method to get the auto-incremented value from the last INSERT statement. * To be called after the INSERT statement, it's MANDATORY to have a sequence on * every primary key table. * * To get the auto incremented value it's possible to call this function after * INSERT INTO query, or use INSERT INTO with RETURNING clause. * * @example with insertid() call: * $query = $this->getQuery(true) * ->insert('jos_dbtest') * ->columns('title,start_date,description') * ->values("'testTitle2nd','1971-01-01','testDescription2nd'"); * $this->setQuery($query); * $this->execute(); * $id = $this->insertid(); * * @example with RETURNING clause: * $query = $this->getQuery(true) * ->insert('jos_dbtest') * ->columns('title,start_date,description') * ->values("'testTitle2nd','1971-01-01','testDescription2nd'") * ->returning('id'); * $this->setQuery($query); * $id = $this->loadResult(); * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); $insertQuery = $this->getQuery(false, true); $table = $insertQuery->__get('insert')->getElements(); /* find sequence column name */ $colNameQuery = $this->getQuery(true); $colNameQuery->select('column_default') ->from('information_schema.columns') ->where('table_name=' . $this->quote($this->replacePrefix(str_replace('"', '', $table[0]))), 'AND') ->where("column_default LIKE '%nextval%'"); $this->setQuery($colNameQuery); $colName = $this->loadRow(); $changedColName = str_replace('nextval', 'currval', $colName); $insertidQuery = $this->getQuery(true); $insertidQuery->select($changedColName); $this->setQuery($insertidQuery); $insertVal = $this->loadRow(); return $insertVal[0]; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($tableName) { $this->transactionStart(); $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute(); return $this; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->limit . ' OFFSET ' . $this->offset; } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); if (is_object($this->cursor)) { // Avoid warning if result already freed by third-party library @$this->freeResult(); } $memoryBefore = memory_get_usage(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @pg_query($this->connection, $query); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } $this->callStacks[count($this->callStacks) - 1][0]['memory'] = array( $memoryBefore, memory_get_usage(), is_resource($this->cursor) ? $this->getNumRows($this->cursor) : null, ); } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, null, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg); } } return $this->cursor; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by PostgreSQL. * @param string $prefix Not used by PostgreSQL. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); // Origin Table does not exist if (!in_array($oldTable, $tableList)) { // Origin Table not found throw new RuntimeException('Table not found in Postgresql database.'); } else { /* Rename indexes */ $this->setQuery( 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname=' . $this->quote($oldTable, true) . ' AND pg_class.oid=pg_index.indrelid );' ); $oldIndexes = $this->loadColumn(); foreach ($oldIndexes as $oldIndex) { $changedIdxName = str_replace($oldTable, $newTable, $oldIndex); $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName)); $this->execute(); } /* Rename sequence */ $this->setQuery( 'SELECT relname FROM pg_class WHERE relkind = \'S\' AND relnamespace IN ( SELECT oid FROM pg_namespace WHERE nspname NOT LIKE \'pg_%\' AND nspname != \'information_schema\' ) AND relname LIKE \'%' . $oldTable . '%\' ;' ); $oldSequences = $this->loadColumn(); foreach ($oldSequences as $oldSequence) { $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence); $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName)); $this->execute(); } /* Rename table */ $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable)); $this->execute(); } return true; } /** * Selects the database, but redundant for PostgreSQL * * @param string $database Database name to select. * * @return boolean Always true * * @since 3.0.0 */ public function select($database) { return true; } /** * Custom settings for UTF support * * @return integer Zero on success, -1 on failure * * @since 3.0.0 */ public function setUtf() { $this->connect(); if (!function_exists('pg_set_client_encoding')) { return -1; } return pg_set_client_encoding($this->connection, 'UTF8'); } /** * This function return a field value as a prepared string to be used in a SQL statement. * * @param array $columns Array of table's column returned by ::getTableColumns. * @param string $fieldName The table field's name. * @param string $fieldValue The variable value to quote and return. * * @return string The quoted string. * * @since 3.0.0 */ public function sqlValue($columns, $fieldName, $fieldValue) { switch ($columns[$fieldName]) { case 'boolean': $val = 'NULL'; if ($fieldValue == 't') { $val = 'TRUE'; } elseif ($fieldValue == 'f') { $val = 'FALSE'; } break; case 'bigint': case 'bigserial': case 'integer': case 'money': case 'numeric': case 'real': case 'smallint': case 'serial': case 'numeric,': $val = strlen($fieldValue) == 0 ? 'NULL' : $fieldValue; break; case 'date': case 'timestamp without time zone': if (empty($fieldValue)) { $fieldValue = $this->getNullDate(); } $val = $this->quote($fieldValue); break; default: $val = $this->quote($fieldValue); break; } return $val; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute(); } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return pg_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return pg_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return pg_fetch_object(is_null($cursor) ? $this->cursor : $cursor, null, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { pg_free_result($cursor ? $cursor : $this->cursor); } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $columns = $this->getTableColumns($table); $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } // Ignore any internal fields or primary keys with value 0. if (($k[0] == '_') || ($k == $key && (($v === 0) || ($v === '0')))) { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->sqlValue($columns, $k, $v); } // Create the base insert statement. $query = $this->getQuery(true) ->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); $retVal = false; if ($key) { $query->returning($key); // Set the query and execute the insert. $this->setQuery($query); $id = $this->loadResult(); if ($id) { $object->$key = $id; $retVal = true; } } else { // Set the query and execute the insert. $this->setQuery($query); if ($this->execute()) { $retVal = true; } } return $retVal; } /** * Test to see if the PostgreSQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('pg_connect'); } /** * Returns an array containing database's table list. * * @return array The database's table list. * * @since 3.0.0 */ public function showTables() { $this->connect(); $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type = ' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )'); $this->setQuery($query); $tableList = $this->loadColumn(); return $tableList; } /** * Get the substring position inside a string * * @param string $substring The string being sought * @param string $string The string/column being searched * * @return integer The position of $substring in $string * * @since 3.0.0 */ public function getStringPositionSql($substring, $string) { $this->connect(); $query = "SELECT POSITION( $substring IN $string )"; $this->setQuery($query); $position = $this->loadRow(); return $position['position']; } /** * Generate a random value * * @return float The random generated number * * @since 3.0.0 */ public function getRandom() { $this->connect(); $this->setQuery('SELECT RANDOM()'); $random = $this->loadAssoc(); return $random['random']; } /** * Get the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.0.0 */ public function getAlterDbCharacterSet($dbName) { $query = 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8'); return $query; } /** * Get the query string to create new Database in correct PostgreSQL syntax. * * @param object $options object coming from "initialise" function to pass user and database name to database driver. * @param boolean $utf True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query. * * @return string The query that creates database, owned by $options['user'] * * @since 3.0.0 */ public function getCreateDbQuery($options, $utf) { $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user); if ($utf) { $query .= ' ENCODING ' . $this->quote('UTF-8'); } return $query; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.0.0 */ public function replacePrefix($query, $prefix = '#__') { $query = trim($query); if (strpos($query, '\'')) { // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'currval')) { $query = explode('currval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('currval', $query); } // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'nextval')) { $query = explode('nextval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('nextval', $query); } // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'setval')) { $query = explode('setval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('setval', $query); } $explodedQuery = explode('\'', $query); for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex < $nIndexMax; $nIndex += 2) { if (strpos($explodedQuery[$nIndex], $prefix)) { $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]); } } $replacedQuery = implode('\'', $explodedQuery); } else { $replacedQuery = str_replace($prefix, $this->tablePrefix, $query); } return $replacedQuery; } /** * Method to release a savepoint. * * @param string $savepointName Savepoint's name to release * * @return void * * @since 3.0.0 */ public function releaseTransactionSavepoint($savepointName) { $this->connect(); $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); $this->execute(); } /** * Method to create a savepoint. * * @param string $savepointName Savepoint's name to create * * @return void * * @since 3.0.0 */ public function transactionSavepoint($savepointName) { $this->connect(); $this->setQuery('SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); $this->execute(); } /** * Unlocks tables in the database, this command does not exist in PostgreSQL, * it is automatically done on commit or rollback. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->transactionCommit(); return $this; } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $columns = $this->getTableColumns($table); $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Only process scalars that are not internal fields. if (is_array($v) or is_object($v) or $k[0] == '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key)) { $key_val = $this->sqlValue($columns, $k, $v); $where[] = $this->quoteName($k) . '=' . $key_val; continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we want to update nulls then set it. if ($nulls) { $val = 'NULL'; } // If the value is null and we do not want to update nulls then ignore this field. else { continue; } } // The field is not null so we prep it for update. else { $val = $this->sqlValue($columns, $k, $v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 * * @throws \JDatabaseExceptionExecuting Thrown if the global cursor is false indicating a query failed */ protected function getErrorNumber() { if ($this->cursor === false) { $this->errorMsg = pg_last_error($this->connection); throw new JDatabaseExceptionExecuting($this->sql, $this->errorMsg); } return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE) . ' '; } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) pg_last_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { return "decode('" . bin2hex($data) . "', 'hex')"; } /** * Internal function to get the name of the default schema for the current PostgreSQL connection. * That is the schema where tables are created by Joomla. * * @return string * * @since 3.9.24 */ private function getDefaultSchema() { // Supported since PostgreSQL 7.3 $this->setQuery('SELECT (current_schemas(false))[1]'); return $this->loadResult(); } } PK �c�[�KKXR R database/driver/sqlazure.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL Server database driver * * @link https://azure.microsoft.com/en-us/documentation/services/sql-database/ * @since 3.0.0 */ class JDatabaseDriverSqlazure extends JDatabaseDriverSqlsrv { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlazure'; } PK �c�[�s��/ / database/driver/sqlite.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQLite database driver * * @link https://www.php.net/pdo * @since 3.0.0 */ class JDatabaseDriverSqlite extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlite'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'sqlite'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '`'; /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } parent::connect(); $this->connection->sqliteCreateFunction( 'ROW_NUMBER', function($init = null) { static $rownum, $partition; if ($init !== null) { $rownum = $init; $partition = null; return $rownum; } $args = func_get_args(); array_shift($args); $partitionBy = $args ? implode(',', $args) : null; if ($partitionBy === null || $partitionBy === $partition) { $rownum++; } else { $rownum = 1; $partition = $partitionBy; } return $rownum; } ); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { $this->freeResult(); $this->connection = null; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); $this->execute(); return $this; } /** * Method to escape a string for usage in an SQLite statement. * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } return SQLite3::escapeString($text); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { return $this->charset; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return $this->charset; } /** * Shows the table CREATE statement that creates the given tables. * * Note: Doesn't appear to have support in SQLite * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); return $tables; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $columns = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->setQuery('pragma table_info(' . $table . ')'); $this->setQuery($query); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $columns[$field->NAME] = $field->TYPE; } } else { foreach ($fields as $field) { // Do some dirty translation to MySQL output. // TODO: Come up with and implement a standard across databases. $columns[$field->NAME] = (object) array( 'Field' => $field->NAME, 'Type' => $field->TYPE, 'Null' => ($field->NOTNULL == '1' ? 'NO' : 'YES'), 'Default' => $field->DFLT_VALUE, 'Key' => ($field->PK != '0' ? 'PRI' : ''), ); } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $columns; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); $keys = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->setQuery('pragma table_info( ' . $table . ')'); // $query->bind(':tableName', $table); $this->setQuery($query); $rows = $this->loadObjectList(); foreach ($rows as $column) { if ($column->PK == 1) { $keys[$column->NAME] = $column; } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $keys; } /** * Method to get an array of all tables in the database (schema). * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); $type = 'table'; $query = $this->getQuery(true) ->select('name') ->from('sqlite_master') ->where('type = :type') ->bind(':type', $type) ->order('name'); $this->setQuery($query); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $this->setQuery('SELECT sqlite_version()'); return $this->loadResult(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Set the connection to use UTF-8 character encoding. * * Returns false automatically for the Oracle driver since * you can only set the character set when the connection * is created. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { $this->connect(); return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($table) { return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by Sqlite. * @param string $prefix Not used by Sqlite. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME TO ' . $newTable)->execute(); return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { return $this; } /** * Test to see if the PDO ODBC connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return class_exists('PDO') && in_array('sqlite', PDO::getAvailableDrivers()); } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { parent::transactionStart($asSavepoint); } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK �c�[�)�$l $l database/driver/sqlsrv.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL Server database driver * * @link https://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx * @since 3.0.0 */ class JDatabaseDriverSqlsrv extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlsrv'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mssql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '[]'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.0 */ protected $nullDate = '1900-01-01 00:00:00'; /** * @var string The minimum supported database version. * @since 3.0.0 */ protected static $dbMinimum = '10.50.1600.1'; /** * Test to see if the SQLSRV connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('sqlsrv_connect'); } /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Build the connection configuration array. $config = array( 'Database' => $this->options['database'], 'uid' => $this->options['user'], 'pwd' => $this->options['password'], 'CharacterSet' => 'UTF-8', 'ReturnDatesAsStrings' => true, ); // Make sure the SQLSRV extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The sqlsrv extension for PHP is not installed or enabled..'); } // Attempt to connect to the server. if (!($this->connection = @ sqlsrv_connect($this->options['host'], $config))) { throw new JDatabaseExceptionConnecting('Database sqlsrv_connect failed, ' . print_r(sqlsrv_errors(), true)); } // Make sure that DB warnings are not returned as errors. sqlsrv_configure('WarningsReturnAsErrors', 0); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Set charactersets. $this->utf = $this->setUtf(); // Set QUOTED_IDENTIFIER always ON sqlsrv_query($this->connection, 'SET QUOTED_IDENTIFIER ON'); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } sqlsrv_close($this->connection); } $this->connection = null; } /** * Get table constraints * * @param string $tableName The name of the database table. * * @return array Any constraints available for the table. * * @since 3.0.0 */ protected function getTableConstraints($tableName) { $this->connect(); $query = $this->getQuery(true); $this->setQuery( 'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = ' . $query->quote($tableName) ); return $this->loadColumn(); } /** * Rename constraints. * * @param array $constraints Array(strings) of table constraints * @param string $prefix A string * @param string $backup A string * * @return void * * @since 3.0.0 */ protected function renameConstraints($constraints = array(), $prefix = null, $backup = null) { $this->connect(); foreach ($constraints as $constraint) { $this->setQuery('sp_rename ' . $constraint . ',' . str_replace($prefix, $backup, $constraint)); $this->execute(); } } /** * Method to escape a string for usage in an SQL statement. * * The escaping for MSSQL isn't handled in the driver though that would be nice. Because of this we need * to handle the escaping ourselves. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $result = str_replace("'", "''", $text); // SQL Server does not accept NULL byte in query string $result = str_replace("\0", "' + CHAR(0) + N'", $result); // Fix for SQL Server escape sequence, see https://support.microsoft.com/en-us/kb/164291 $result = str_replace( array("\\\n", "\\\r", "\\\\\r\r\n"), array("\\\\\n\n", "\\\\\r\r", "\\\\\r\n\r\n"), $result ); if ($extra) { // Escape special chars $result = str_replace( array('[', '_', '%'), array('[[]', '[_]', '[%]'), $result ); } return $result; } /** * Quotes and optionally escapes a string to database requirements for use in database queries. * * @param mixed $text A string or an array of strings to quote. * @param boolean $escape True (default) to escape the string, false to leave it unchanged. * * @return string The quoted input string. * * @note Accepting an array of strings was added in 3.1.4. * @since 1.7.0 */ public function quote($text, $escape = true) { if (is_array($text)) { return parent::quote($text, $escape); } // To support unicode on MSSQL we have to add prefix N return 'N\'' . ($escape ? $this->escape($text) : $text) . '\''; } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { // ODBC syntax for hexadecimal literals return '0x' . bin2hex($data); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { // TODO: Run a blank query here return true; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); if ($ifExists) { $this->setQuery( 'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ' . $query->quote($tableName) . ') DROP TABLE ' . $tableName ); } else { $this->setQuery('DROP TABLE ' . $tableName); } $this->execute(); return $this; } /** * Get the number of affected rows for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return sqlsrv_rows_affected($this->cursor); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { // TODO: Not fake this return 'MSSQL UTF-8 (UCS2)'; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { // TODO: Not fake this return 'MSSQL UTF-8 (UCS2)'; } /** * Get the number of returned rows for the previous executed SQL statement. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); return sqlsrv_num_rows($cursor ? $cursor : $this->cursor); } /** * Retrieves field information about the given tables. * * @param mixed $table A table name * @param boolean $typeOnly True to only return field types. * * @return array An array of fields. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $result = array(); $table_temp = $this->replacePrefix((string) $table); // Set the query to get the table fields statement. $this->setQuery( 'SELECT column_name as Field, data_type as Type, is_nullable as \'Null\', column_default as \'Default\'' . ' FROM information_schema.columns WHERE table_name = ' . $this->quote($table_temp) ); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $field->Default = preg_replace("/(^(\(\(|\('|\(N'|\()|(('\)|(?<!\()\)\)|\))$))/i", '', $field->Default); $result[$field->Field] = $field; } } return $result; } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by MSSQL. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); return ''; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // TODO To implement. return array(); } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SELECT name FROM ' . $this->getDatabase() . '.sys.Tables WHERE type = \'U\';'); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $version = sqlsrv_server_info($this->connection); return $version['SQLServerVersion']; } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $fields = array(); $values = array(); $statement = 'INSERT INTO ' . $this->quoteName($table) . ' (%s) VALUES (%s)'; foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } if (!$this->checkFieldExists($table, $k)) { continue; } if ($k[0] == '_') { // Internal field continue; } if ($k == $key && $key == 0) { continue; } $fields[] = $this->quoteName($k); $values[] = $this->Quote($v); } // Set the query and execute the insert. $this->setQuery(sprintf($statement, implode(',', $fields), implode(',', $values))); if (!$this->execute()) { return false; } $id = $this->insertid(); if ($key && $id) { $object->$key = $id; } return true; } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); // TODO: SELECT IDENTITY $this->setQuery('SELECT @@IDENTITY'); return (int) $this->loadResult(); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query = $this->limit($query, $this->limit, $this->offset); } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // SQLSrv_num_rows requires a static or keyset cursor. if (strncmp(ltrim(strtoupper($query)), 'SELECT', strlen('SELECT')) == 0) { $array = array('Scrollable' => SQLSRV_CURSOR_KEYSET); } else { $array = array(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @sqlsrv_query($this->connection, $query, array(), $array); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.0.0 */ public function replacePrefix($query, $prefix = '#__') { $query = trim($query); if (strpos($query, "'")) { $parts = explode("'", $query); for ($nIndex = 0, $size = count($parts); $nIndex < $size; $nIndex = $nIndex + 2) { if (strpos($parts[$nIndex], $prefix) !== false) { $parts[$nIndex] = str_replace($prefix, $this->tablePrefix, $parts[$nIndex]); } } $query = implode("'", $parts); } else { $query = str_replace($prefix, $this->tablePrefix, $query); } return $query; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!sqlsrv_query($this->connection, 'USE ' . $database, null, array('scrollable' => SQLSRV_CURSOR_STATIC))) { throw new JDatabaseExceptionConnecting('Could not connect to SQL Server database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT TRANSACTION')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK TRANSACTION')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TRANSACTION ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('BEGIN TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('BEGIN TRANSACTION ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_NUMERIC); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_ASSOC); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { // Class has to be loaded for sqlsrv on windows platform class_exists($class); return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { sqlsrv_free_stmt($cursor ? $cursor : $this->cursor); } /** * Method to check and see if a field exists in a table. * * @param string $table The table in which to verify the field. * @param string $field The field to verify. * * @return boolean True if the field exists in the table. * * @since 3.0.0 */ protected function checkFieldExists($table, $field) { $this->connect(); $table = $this->replacePrefix((string) $table); $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field' ORDER BY ORDINAL_POSITION"; $this->setQuery($query); if ($this->loadResult()) { return true; } else { return false; } } /** * Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior for scrolling through a result set. * * @param string $query The SQL statement to process. * @param integer $limit The maximum affected rows to set. * @param integer $offset The affected row offset to set. * * @return string The processed SQL statement. * * @since 3.0.0 */ protected function limit($query, $limit, $offset) { if ($limit) { $total = $offset + $limit; $position = stripos($query, 'SELECT'); $distinct = stripos($query, 'SELECT DISTINCT'); if ($position === $distinct) { $query = substr_replace($query, 'SELECT DISTINCT TOP ' . (int) $total, $position, 15); } else { $query = substr_replace($query, 'SELECT TOP ' . (int) $total, $position, 6); } } if (!$offset) { return $query; } return PHP_EOL . 'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber FROM (' . $query . PHP_EOL . ') AS A) AS A WHERE RowNumber > ' . (int) $offset; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Table prefix * @param string $prefix For the table - used to rename constraints in non-mysql databases * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $constraints = array(); if (!is_null($prefix) && !is_null($backup)) { $constraints = $this->getTableConstraints($oldTable); } if (!empty($constraints)) { $this->renameConstraints($constraints, $prefix, $backup); } $this->setQuery("sp_rename '" . $oldTable . "', '" . $newTable . "'"); return $this->execute(); } /** * Locks a table in the database. * * @param string $tableName The name of the table to lock. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($tableName) { return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { return $this; } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { $errors = sqlsrv_errors(); return $errors[0]['code']; } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errors = sqlsrv_errors(); $errorMessage = (string) $errors[0]['message']; // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK �c�[��UD� D� database/driver.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Driver Class * * @since 3.0.0 * * @method string|array q() q($text, $escape = true) Alias for quote method * @method string|array qn() qn($name, $as = null) Alias for quoteName method */ abstract class JDatabaseDriver extends JDatabase implements JDatabaseInterface { /** * The name of the database. * * @var string * @since 2.5.0 */ private $_database; /** * The name of the database driver. * * @var string * @since 1.7.0 */ public $name; /** * The type of the database server family supported by this driver. Examples: mysql, oracle, postgresql, mssql, * sqlite. * * @var string * @since CMS 3.5.0 */ public $serverType; /** * @var resource The database connection resource. * @since 1.7.0 */ protected $connection; /** * @var integer The number of SQL statements executed by the database driver. * @since 1.7.0 */ protected $count = 0; /** * @var resource The database connection cursor from the last query. * @since 1.7.0 */ protected $cursor; /** * @var boolean The database driver debugging state. * @since 1.7.0 */ protected $debug = false; /** * @var integer The affected row limit for the current SQL statement. * @since 1.7.0 */ protected $limit = 0; /** * @var array The log of executed SQL statements by the database driver. * @since 1.7.0 */ protected $log = array(); /** * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver. * @since CMS 3.1.2 */ protected $timings = array(); /** * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver. * @since CMS 3.1.2 */ protected $callStacks = array(); /** * @var string The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. 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 opening quote and the second for the closing quote. * @since 1.7.0 */ protected $nameQuote; /** * @var string The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * @since 1.7.0 */ protected $nullDate; /** * @var integer The affected row offset to apply for the current SQL statement. * @since 1.7.0 */ protected $offset = 0; /** * @var array Passed in upon instantiation and saved. * @since 1.7.0 */ protected $options; /** * @var JDatabaseQuery|string The current SQL statement to execute. * @since 1.7.0 */ protected $sql; /** * @var string The common database table prefix. * @since 1.7.0 */ protected $tablePrefix; /** * @var boolean True if the database engine supports UTF-8 character encoding. * @since 1.7.0 */ protected $utf = true; /** * @var boolean True if the database engine supports UTF-8 Multibyte (utf8mb4) character encoding. * @since CMS 3.5.0 */ protected $utf8mb4 = false; /** * @var integer The database error number * @since 1.7.0 * @deprecated 3.0.0 */ protected $errorNum = 0; /** * @var string The database error message * @since 1.7.0 * @deprecated 3.0.0 */ protected $errorMsg; /** * @var array JDatabaseDriver instances container. * @since 1.7.0 */ protected static $instances = array(); /** * @var string The minimum supported database version. * @since 3.0.0 */ protected static $dbMinimum; /** * @var integer The depth of the current transaction. * @since 3.1.4 */ protected $transactionDepth = 0; /** * @var callable[] List of callables to call just before disconnecting database * @since CMS 3.1.2 */ protected $disconnectHandlers = array(); /** * Get a list of available database connectors. The list will only be populated with connectors that both * the class exists and the static test method returns true. This gives us the ability to have a multitude * of connector classes that are self-aware as to whether or not they are able to be used on a given system. * * @return array An array of available database connectors. * * @since 1.7.0 */ public static function getConnectors() { $connectors = array(); // Get an iterator and loop trough the driver classes. $iterator = new DirectoryIterator(__DIR__ . '/driver'); /* @type $file DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', 'JDatabaseDriver' . ucfirst(trim($fileName))); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } // Sweet! Our class exists, so now we just need to know if it passes its test method. if ($class::isSupported()) { // Connector names should not have file extensions. $connectors[] = str_ireplace('.php', '', $fileName); } } return $connectors; } /** * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to * be used for the connection. The 'select' option determines whether the connector should automatically select * the chosen database. * * Instances are unique to the given options and new objects are only created when a unique options array is * passed into the method. This ensures that we don't end up with unnecessary database connection resources. * * @param array $options Parameters to be passed to the database driver. * * @return JDatabaseDriver A database object. * * @since 1.7.0 * @throws RuntimeException */ public static function getInstance($options = array()) { // Sanitize the database connector options. $options['driver'] = (isset($options['driver'])) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli'; $options['database'] = (isset($options['database'])) ? $options['database'] : null; $options['select'] = (isset($options['select'])) ? $options['select'] : true; // If the selected driver is `mysql` and we are on PHP 7 or greater, switch to the `mysqli` driver. if ($options['driver'] === 'mysql' && PHP_MAJOR_VERSION >= 7) { // Check if we have support for the other MySQL drivers $mysqliSupported = JDatabaseDriverMysqli::isSupported(); $pdoMysqlSupported = JDatabaseDriverPdomysql::isSupported(); // If neither is supported, then the user cannot use MySQL; throw an exception if (!$mysqliSupported && !$pdoMysqlSupported) { throw new JDatabaseExceptionUnsupported( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver.' . ' Also, this system does not support MySQLi or PDO MySQL. Cannot instantiate database driver.' ); } // Prefer MySQLi as it is a closer replacement for the removed MySQL driver, otherwise use the PDO driver if ($mysqliSupported) { JLog::add( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `mysqli` instead.', JLog::WARNING, 'deprecated' ); $options['driver'] = 'mysqli'; } else { JLog::add( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `pdomysql` instead.', JLog::WARNING, 'deprecated' ); $options['driver'] = 'pdomysql'; } } // Get the options signature for the database connector. $signature = md5(serialize($options)); // If we already have a database connector instance for these options then just use that. if (empty(self::$instances[$signature])) { // Derive the class name from the driver. $class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver'])); // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best. if (!class_exists($class)) { throw new JDatabaseExceptionUnsupported(sprintf('Unable to load Database Driver: %s', $options['driver'])); } // Create our new JDatabaseDriver connector based on the options given. try { $instance = new $class($options); } catch (RuntimeException $e) { throw new JDatabaseExceptionConnecting(sprintf('Unable to connect to the Database: %s', $e->getMessage()), $e->getCode(), $e); } // Set the new connector to the global instances based on signature. self::$instances[$signature] = $instance; } return self::$instances[$signature]; } /** * Splits a string of multiple queries into an array of individual queries. * Single line or line end comments and multi line comments are stripped off. * * @param string $sql Input SQL string with which to split into individual queries. * * @return array The queries from the input string separated into an array. * * @since 1.7.0 */ public static function splitSql($sql) { $start = 0; $open = false; $comment = false; $endString = ''; $end = strlen($sql); $queries = array(); $query = ''; for ($i = 0; $i < $end; $i++) { $current = substr($sql, $i, 1); $current2 = substr($sql, $i, 2); $current3 = substr($sql, $i, 3); $lenEndString = strlen($endString); $testEnd = substr($sql, $i, $lenEndString); if ($current == '"' || $current == "'" || $current2 == '--' || ($current2 == '/*' && $current3 != '/*!' && $current3 != '/*+') || ($current == '#' && $current3 != '#__') || ($comment && $testEnd == $endString)) { // Check if quoted with previous backslash $n = 2; while (substr($sql, $i - $n + 1, 1) == '\\' && $n < $i) { $n++; } // Not quoted if ($n % 2 == 0) { if ($open) { if ($testEnd == $endString) { if ($comment) { $comment = false; if ($lenEndString > 1) { $i += ($lenEndString - 1); $current = substr($sql, $i, 1); } $start = $i + 1; } $open = false; $endString = ''; } } else { $open = true; if ($current2 == '--') { $endString = "\n"; $comment = true; } elseif ($current2 == '/*') { $endString = '*/'; $comment = true; } elseif ($current == '#') { $endString = "\n"; $comment = true; } else { $endString = $current; } if ($comment && $start < $i) { $query = $query . substr($sql, $start, ($i - $start)); } } } } if ($comment) { $start = $i + 1; } if (($current == ';' && !$open) || $i == $end - 1) { if ($start <= $i) { $query = $query . substr($sql, $start, ($i - $start + 1)); } $query = trim($query); if ($query) { if (($i == $end - 1) && ($current != ';')) { $query = $query . ';'; } $queries[] = $query; } $query = ''; $start = $i + 1; } } return $queries; } /** * Magic method to provide method alias support for quote() and quoteName(). * * @param string $method The called method. * @param array $args The array of arguments passed to the method. * * @return mixed The aliased method's return value or null. * * @since 1.7.0 */ public function __call($method, $args) { if (empty($args)) { return; } switch ($method) { case 'q': return $this->quote($args[0], isset($args[1]) ? $args[1] : true); break; case 'qn': return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null); break; } } /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 1.7.0 */ public function __construct($options) { // Initialise object variables. $this->_database = (isset($options['database'])) ? $options['database'] : ''; $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_'; $this->count = 0; $this->errorNum = 0; $this->log = array(); // Set class options. $this->options = $options; } /** * Alter database's character set, obtaining query string from protected member. * * @param string $dbName The database name that will be altered * * @return string The query that alter the database query string * * @since 3.0.1 * @throws RuntimeException */ public function alterDbCharacterSet($dbName) { if (is_null($dbName)) { throw new RuntimeException('Database name must not be null.'); } $this->setQuery($this->getAlterDbCharacterSet($dbName)); return $this->execute(); } /** * Alter a table's character set, obtaining an array of queries to do so from a protected method. The conversion is * wrapped in a transaction, if supported by the database driver. Otherwise the table will be locked before the * conversion. This prevents data corruption. * * @param string $tableName The name of the table to alter * @param boolean $rethrow True to rethrow database exceptions. Default: false (exceptions are suppressed) * * @return boolean True if successful * * @since CMS 3.5.0 * @throws RuntimeException If the table name is empty * @throws Exception Relayed from the database layer if a database error occurs and $rethrow == true */ public function alterTableCharacterSet($tableName, $rethrow = false) { if (is_null($tableName)) { throw new RuntimeException('Table name must not be null.'); } $queries = $this->getAlterTableCharacterSet($tableName); if (empty($queries)) { return false; } $hasTransaction = true; try { $this->transactionStart(); } catch (Exception $e) { $hasTransaction = false; $this->lockTable($tableName); } foreach ($queries as $query) { try { $this->setQuery($query)->execute(); } catch (Exception $e) { if ($hasTransaction) { $this->transactionRollback(); } else { $this->unlockTables(); } if ($rethrow) { throw $e; } return false; } } if ($hasTransaction) { try { $this->transactionCommit(); } catch (Exception $e) { $this->transactionRollback(); if ($rethrow) { throw $e; } return false; } } else { $this->unlockTables(); } return true; } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ abstract public function connect(); /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 1.7.0 */ abstract public function connected(); /** * Create a new database using information from $options object, obtaining query string * from protected member. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 * @throws RuntimeException */ public function createDatabase($options, $utf = true) { if (is_null($options)) { throw new RuntimeException('$options object must not be null.'); } elseif (empty($options->db_name)) { throw new RuntimeException('$options object must have db_name set.'); } elseif (empty($options->db_user)) { throw new RuntimeException('$options object must have db_user set.'); } $this->setQuery($this->getCreateDatabaseQuery($options, $utf)); return $this->execute(); } /** * Destructor. * * @since 3.8.0 */ public function __destruct() { $this->disconnect(); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ abstract public function disconnect(); /** * Adds a function callable just before disconnecting the database. Parameter of the callable is $this JDatabaseDriver * * @param callable $callable Function to call in disconnect() method just before disconnecting from database * * @return void * * @since CMS 3.1.2 */ public function addDisconnectHandler($callable) { $this->disconnectHandlers[] = $callable; } /** * Drops a table from the database. * * @param string $table The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function dropTable($table, $ifExists = true); /** * Escapes a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 1.7.0 */ abstract public function escape($text, $extra = false); /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchArray($cursor = null); /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchAssoc($cursor = null); /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchObject($cursor = null, $class = 'stdClass'); /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 1.7.0 */ abstract protected function freeResult($cursor = null); /** * Get the number of affected rows for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 1.7.0 */ abstract public function getAffectedRows(); /** * Return the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.0.1 */ public function getAlterDbCharacterSet($dbName) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `' . $charset . '`'; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $collation = $charset . '_unicode_ci'; $quotedTableName = $this->quoteName($tableName); $queries = array(); $queries[] = "ALTER TABLE $quotedTableName CONVERT TO CHARACTER SET $charset COLLATE $collation"; /** * We also need to convert each text column, modifying their character set and collation. This allows us to * change, for example, a utf8_bin collated column to a utf8mb4_bin collated column. */ $sql = "SHOW FULL COLUMNS FROM $quotedTableName"; $this->setQuery($sql); $columns = $this->loadAssocList(); $columnMods = array(); if (is_array($columns)) { foreach ($columns as $column) { // Make sure we are redefining only columns which do support a collation $col = (object) $column; if (empty($col->Collation)) { continue; } // Default new collation: utf8_unicode_ci or utf8mb4_unicode_ci $newCollation = $charset . '_unicode_ci'; $collationParts = explode('_', $col->Collation); /** * If the collation is in the form charset_collationType_ci or charset_collationType we have to change * the charset but leave the collationType intact (e.g. utf8_bin must become utf8mb4_bin, NOT * utf8mb4_general_ci). */ if (count($collationParts) >= 2) { $ci = array_pop($collationParts); $collationType = array_pop($collationParts); $newCollation = $charset . '_' . $collationType . '_' . $ci; /** * When the last part of the old collation is not _ci we have a charset_collationType format, * something like utf8_bin. Therefore the new collation only has *two* parts. */ if ($ci != 'ci') { $newCollation = $charset . '_' . $ci; } } // If the old and new collation is the same we don't have to change the collation type if (strtolower($newCollation) == strtolower($col->Collation)) { continue; } $null = $col->Null == 'YES' ? 'NULL' : 'NOT NULL'; $default = is_null($col->Default) ? '' : "DEFAULT '" . $this->q($col->Default) . "'"; $columnMods[] = "MODIFY COLUMN `{$col->Field}` {$col->Type} CHARACTER SET $charset COLLATE $newCollation $null $default"; } } if (count($columnMods)) { $queries[] = "ALTER TABLE $quotedTableName " . implode(',', $columnMods) . " CHARACTER SET $charset COLLATE $collation"; } return $queries; } /** * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. Used * when the server doesn't support UTF-8 Multibyte. * * @param string $query The query to convert * * @return string The converted query */ public function convertUtf8mb4QueryToUtf8($query) { if ($this->hasUTF8mb4Support()) { return $query; } // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert if (!preg_match('/^(ALTER|CREATE)\s+TABLE\s+/i', $query)) { return $query; } // Don't do preg replacement if string does not exist if (stripos($query, 'utf8mb4') === false) { return $query; } // Replace utf8mb4 with utf8 if not within single or double quotes or name quotes return preg_replace('/[`"\'][^`"\']*[`"\'](*SKIP)(*FAIL)|utf8mb4/i', 'utf8', $query); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { if ($utf) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $collation = $charset . '_unicode_ci'; return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `' . $charset . '` COLLATE `' . $collation . '`'; } return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 1.7.0 */ abstract public function getCollation(); /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return ''; } /** * Method that provides access to the underlying database connection. Useful for when you need to call a * proprietary method such as postgresql's lo_* methods. * * @return resource The underlying database connection resource. * * @since 1.7.0 */ public function getConnection() { return $this->connection; } /** * Get the total number of SQL statements executed by the database driver. * * @return integer * * @since 1.7.0 */ public function getCount() { return $this->count; } /** * Gets the name of the database used by this connection. * * @return string * * @since 2.5.0 */ protected function getDatabase() { return $this->_database; } /** * Returns a PHP date() function compliant date format for the database driver. * * @return string The format string. * * @since 1.7.0 */ public function getDateFormat() { return 'Y-m-d H:i:s'; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since 1.7.0 */ public function getLog() { return $this->log; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since CMS 3.1.2 */ public function getTimings() { return $this->timings; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since CMS 3.1.2 */ public function getCallStacks() { return $this->callStacks; } /** * Get the minimum supported database version. * * @return string The minimum version number for the database driver. * * @since 3.0.0 */ public function getMinimum() { return static::$dbMinimum; } /** * Get the null or zero representation of a timestamp for the database driver. * * @return string Null or zero representation of a timestamp. * * @since 1.7.0 */ public function getNullDate() { return $this->nullDate; } /** * Get the number of returned rows for the previous executed SQL statement. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 1.7.0 */ abstract public function getNumRows($cursor = null); /** * Get the common table prefix for the database driver. * * @return string The common database table prefix. * * @since 1.7.0 */ public function getPrefix() { return $this->tablePrefix; } /** * Gets an exporter class object. * * @return JDatabaseExporter An exporter object. * * @since 3.0.0 * @throws RuntimeException */ public function getExporter() { // Derive the class name from the driver. $class = 'JDatabaseExporter' . ucfirst($this->name); // Make sure we have an exporter class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Exporter not found.'); } $o = new $class; $o->setDbo($this); return $o; } /** * Gets an importer class object. * * @return JDatabaseImporter An importer object. * * @since 3.0.0 * @throws RuntimeException */ public function getImporter() { // Derive the class name from the driver. $class = 'JDatabaseImporter' . ucfirst($this->name); // Make sure we have an importer class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Importer not found'); } $o = new $class; $o->setDbo($this); return $o; } /** * Get the name of the database driver. If $this->name is not set it will try guessing the driver name from the * class name. * * @return string * * @since CMS 3.5.0 */ public function getName() { if (empty($this->name)) { $className = get_class($this); $className = str_replace('JDatabaseDriver', '', $className); $this->name = strtolower($className); } return $this->name; } /** * Get the server family type, e.g. mysql, postgresql, oracle, sqlite, mssql. 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 name will * be returned instead. * * @return string * * @since CMS 3.5.0 */ public function getServerType() { if (empty($this->serverType)) { $name = $this->getName(); if (stristr($name, 'mysql') !== false) { $this->serverType = 'mysql'; } elseif (stristr($name, 'postgre') !== false) { $this->serverType = 'postgresql'; } elseif (stristr($name, 'pgsql') !== false) { $this->serverType = 'postgresql'; } elseif (stristr($name, 'oracle') !== false) { $this->serverType = 'oracle'; } elseif (stristr($name, 'sqlite') !== false) { $this->serverType = 'sqlite'; } elseif (stristr($name, 'sqlsrv') !== false) { $this->serverType = 'mssql'; } elseif (stristr($name, 'mssql') !== false) { $this->serverType = 'mssql'; } else { $this->serverType = $name; } } return $this->serverType; } /** * Get the current query object or a new JDatabaseQuery object. * * @param boolean $new False to return the current query object, True to return a new JDatabaseQuery object. * * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class. * * @since 1.7.0 * @throws RuntimeException */ public function getQuery($new = false) { if ($new) { // Derive the class name from the driver. $class = 'JDatabaseQuery' . ucfirst($this->name); // Make sure we have a query class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Query Class not found.'); } return new $class($this); } else { return $this->sql; } } /** * Get a new iterator on the current query. * * @param string $column An option column to use as the iterator key. * @param string $class The class of object that is returned. * * @return JDatabaseIterator A new database iterator. * * @since 3.0.0 * @throws RuntimeException */ public function getIterator($column = null, $class = 'stdClass') { // Derive the class name from the driver. $iteratorClass = 'JDatabaseIterator' . ucfirst($this->name); // Make sure we have an iterator class for this driver. if (!class_exists($iteratorClass)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported(sprintf('class *%s* is not defined', $iteratorClass)); } // Return a new iterator return new $iteratorClass($this->execute(), $column, $class); } /** * Retrieves field information about the given tables. * * @param string $table The name of the database table. * @param boolean $typeOnly True (default) to only return field types. * * @return array An array of fields by table. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableColumns($table, $typeOnly = true); /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableCreate($tables); /** * Retrieves keys information about the given table. * * @param string $table The name of the table. * * @return array An array of keys for the table. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableKeys($table); /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableList(); /** * Determine whether or not the database engine supports UTF-8 character encoding. * * @return boolean True if the database engine supports UTF-8 character encoding. * * @since 1.7.0 * @deprecated 4.0 - Use hasUTFSupport() instead */ public function getUTFSupport() { JLog::add('JDatabaseDriver::getUTFSupport() is deprecated. Use JDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING, 'deprecated'); return $this->hasUTFSupport(); } /** * Determine whether or not the database engine supports UTF-8 character encoding. * * @return boolean True if the database engine supports UTF-8 character encoding. * * @since 3.0.0 */ public function hasUTFSupport() { return $this->utf; } /** * Determine whether the database engine support the UTF-8 Multibyte (utf8mb4) character encoding. This applies to * MySQL databases. * * @return boolean True if the database engine supports UTF-8 Multibyte. * * @since CMS 3.5.0 */ public function hasUTF8mb4Support() { return $this->utf8mb4; } /** * Get the version of the database connector * * @return string The database connector version. * * @since 1.7.0 */ abstract public function getVersion(); /** * Method to get the auto-incremented value from the last INSERT statement. * * @return mixed The value of the auto-increment field from the last inserted row. * * @since 1.7.0 */ abstract public function insertid(); /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 1.7.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } // Ignore any internal fields. if ($k[0] == '_') { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->quote($v); } // Create the base insert statement. $query = $this->getQuery(true) ->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); // Set the query and execute the insert. $this->setQuery($query); if (!$this->execute()) { return false; } // Update the primary key if it exists. $id = $this->insertid(); if ($key && $id && is_string($key)) { $object->$key = $id; } return true; } /** * Method to check whether the installed database version is supported by the database driver * * @return boolean True if the database version is supported * * @since 3.0.0 */ public function isMinimumVersion() { return version_compare($this->getVersion(), static::$dbMinimum) >= 0; } /** * Method to get the first row of the result set from the database query as an associative array * of ['field_name' => 'row_value']. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadAssoc() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an associative array. if ($array = $this->fetchAssoc($cursor)) { $ret = $array; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an associative array * of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to * a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted * behavior and should be avoided. * * @param string $key The name of a field on which to key the result array. * @param string $column An optional column name. Instead of the whole row, only this column value will be in * the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadAssocList($key = null, $column = null) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set. while ($row = $this->fetchAssoc($cursor)) { $value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row; if ($key) { $array[$row[$key]] = $value; } else { $array[] = $value; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get an array of values from the <var>$offset</var> field in each row of the result set from * the database query. * * @param integer $offset The row offset to use to build the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadColumn($offset = 0) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as arrays. while ($row = $this->fetchArray($cursor)) { $array[] = $row[$offset]; } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get the next row in the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 - Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); static $cursor = null; // Execute the query and get the result set cursor. if (is_null($cursor)) { if (!($cursor = $this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchObject($cursor, $class)) { return $row; } // Free up system resources and return. $this->freeResult($cursor); $cursor = null; return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use JDatabaseDriver::getIterator() instead */ public function loadNextRow() { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); static $cursor = null; // Execute the query and get the result set cursor. if (is_null($cursor)) { if (!($cursor = $this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchArray($cursor)) { return $row; } // Free up system resources and return. $this->freeResult($cursor); $cursor = null; return false; } /** * Method to get the first row of the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadObject($class = 'stdClass') { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an object of type $class. if ($object = $this->fetchObject($cursor, $class)) { $ret = $object; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an object. The array * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted * behavior and should be avoided. * * @param string $key The name of a field on which to key the result array. * @param string $class The class name to use for the returned row objects. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadObjectList($key = '', $class = 'stdClass') { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as objects of type $class. while ($row = $this->fetchObject($cursor, $class)) { if ($key) { $array[$row->$key] = $row; } else { $array[] = $row; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get the first field of the first row of the result set from the database query. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadResult() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an array. if ($row = $this->fetchArray($cursor)) { $ret = $row[0]; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get the first row of the result set from the database query as an array. Columns are indexed * numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadRow() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an array. if ($row = $this->fetchArray($cursor)) { $ret = $row; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an array. The array * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field can result in unwanted * behavior and should be avoided. * * @param integer $index The index of a field on which to key the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadRowList($index = null) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as arrays. while ($row = $this->fetchArray($cursor)) { if ($index !== null) { $array[$row[$index]] = $row; } else { $array[] = $row; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function lockTable($tableName); /** * Quotes and optionally escapes a string to database requirements for use in database queries. * * @param mixed $text A string or an array of strings to quote. * @param boolean $escape True (default) to escape the string, false to leave it unchanged. * * @return string|array The quoted input. * * @note Accepting an array of strings was added in 12.3. * @since 1.7.0 */ public function quote($text, $escape = true) { if (is_array($text)) { foreach ($text as $k => $v) { $text[$k] = $this->quote($v, $escape); } return $text; } else { return '\'' . ($escape ? $this->escape($text) : $text) . '\''; } } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { // SQL standard syntax for hexadecimal literals return "X'" . bin2hex($data) . "'"; } /** * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection * risks and reserved word conflicts. * * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. * Each type supports dot-notation name. * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be * same length of $name; if is null there will not be any AS part for string or array element. * * @return mixed The quote wrapped name, same type of $name. * * @since 1.7.0 */ public function quoteName($name, $as = null) { if (is_string($name)) { $quotedName = $this->quoteNameStr(explode('.', $name)); $quotedAs = ''; if (!is_null($as)) { settype($as, 'array'); $quotedAs .= ' AS ' . $this->quoteNameStr($as); } return $quotedName . $quotedAs; } else { $fin = array(); if (is_null($as)) { foreach ($name as $str) { $fin[] = $this->quoteName($str); } } elseif (is_array($name) && (count($name) == count($as))) { $count = count($name); for ($i = 0; $i < $count; $i++) { $fin[] = $this->quoteName($name[$i], $as[$i]); } } return $fin; } } /** * Quote strings coming from quoteName call. * * @param array $strArr Array of strings coming from quoteName dot-explosion. * * @return string Dot-imploded string of quoted parts. * * @since 1.7.3 */ protected function quoteNameStr($strArr) { $parts = array(); $q = $this->nameQuote; foreach ($strArr as $part) { if (is_null($part)) { continue; } if (strlen($q) == 1) { $parts[] = $q . str_replace($q, $q . $q, $part) . $q; } else { $parts[] = $q[0] . str_replace($q[1], $q[1] . $q[1], $part) . $q[1]; } } return implode('.', $parts); } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $sql The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 1.7.0 */ public function replacePrefix($sql, $prefix = '#__') { $startPos = 0; $literal = ''; $sql = trim($sql); $n = strlen($sql); while ($startPos < $n) { $ip = strpos($sql, $prefix, $startPos); if ($ip === false) { break; } $j = strpos($sql, "'", $startPos); $k = strpos($sql, '"', $startPos); if (($k !== false) && (($k < $j) || ($j === false))) { $quoteChar = '"'; $j = $k; } else { $quoteChar = "'"; } if ($j === false) { $j = $n; } $literal .= str_replace($prefix, $this->tablePrefix, substr($sql, $startPos, $j - $startPos)); $startPos = $j; $j = $startPos + 1; if ($j >= $n) { break; } // Quote comes first, find end of quote while (true) { $k = strpos($sql, $quoteChar, $j); $escaped = false; if ($k === false) { break; } $l = $k - 1; while ($l >= 0 && $sql[$l] == '\\') { $l--; $escaped = !$escaped; } if ($escaped) { $j = $k + 1; continue; } break; } if ($k === false) { // Error in the query - no end quote; ignore it break; } $literal .= substr($sql, $startPos, $k - $startPos + 1); $startPos = $k + 1; } if ($startPos < $n) { $literal .= substr($sql, $startPos, $n - $startPos); } return $literal; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Table prefix * @param string $prefix For the table - used to rename constraints in non-mysql databases * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function renameTable($oldTable, $newTable, $backup = null, $prefix = null); /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 1.7.0 * @throws RuntimeException */ abstract public function select($database); /** * Sets the database debugging state for the driver. * * @param boolean $level True to enable debugging. * * @return boolean The old debugging level. * * @since 1.7.0 * @deprecated 4.0 This will be removed in Joomla 4 without replacement */ public function setDebug($level) { $previous = $this->debug; $this->debug = (bool) $level; return $previous; } /** * Sets the SQL statement string for later execution. * * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string. * @param integer $offset The affected row offset to set. * @param integer $limit The maximum affected rows to set. * * @return JDatabaseDriver This object to support method chaining. * * @since 1.7.0 */ public function setQuery($query, $offset = 0, $limit = 0) { $this->sql = $query; if ($query instanceof JDatabaseQueryLimitable) { if (!$limit && $query->limit) { $limit = $query->limit; } if (!$offset && $query->offset) { $offset = $query->offset; } $query->setLimit($limit, $offset); } else { $this->limit = (int) max(0, $limit); $this->offset = (int) max(0, $offset); } return $this; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 1.7.0 */ abstract public function setUtf(); /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionCommit($toSavepoint = false); /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionRollback($toSavepoint = false); /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionStart($asSavepoint = false); /** * Method to truncate a table. * * @param string $table The table to truncate * * @return void * * @since 1.7.3 * @throws RuntimeException */ public function truncateTable($table) { $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table)); $this->execute(); } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array|string $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean True on success. * * @since 1.7.0 * @throws RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Only process scalars that are not internal fields. if (is_array($v) || is_object($v) || $k[0] === '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key)) { $where[] = $this->quoteName($k) . ($v === null ? ' IS NULL' : ' = ' . $this->quote($v)); continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we want to update nulls then set it. if ($nulls) { $val = 'NULL'; } // If the value is null and we do not want to update nulls then ignore this field. else { continue; } } // The field is not null so we prep it for update. else { $val = $this->quote($v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ abstract public function execute(); /** * Unlocks tables in the database. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function unlockTables(); } PK �c�[�ۄ�� � ! database/exception/connecting.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Exception class defining an error connecting to the database platform * * @since 3.6 */ class JDatabaseExceptionConnecting extends RuntimeException { } PK �c�[>���� � database/exception/executing.phpnu �[��� <?php /** * @package Joomla.Platform * @subpackage Database * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Exception class defining an error executing a statement * * @since 3.6 */ class JDatabaseExceptionExecuting extends RuntimeException { /** * The SQL statement that was executed. * * @var string * @since 3.6 */ private $query; /** * Construct the exception * * @param string $query The SQL statement that was executed. * @param string $message The Exception message to throw. [optional] * @param integer $code The Exception code. [optional] * @param Exception $previous The previous exception used for the exception chaining. [optional] * * @since 3.6 */ public function __construct($query, $message = '', $code = 0, Exception $previous = null) { parent::__construct($message, $code, $previous); $this->query = $query; } /** * Get the SQL statement that was executed * * @return string * * @since 3.6 */ public function getQuery() { return $this->query; } } PK �c�[n��u� � "