Файловый менеджер - Редактировать - /home/lmsyaran/public_html/joomla4/fof.zip
Назад
PK ! �x&�zN zN autoloader/component.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage autoloader * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ defined('FOF_INCLUDED') or die(); /** * An autoloader for FOF-powered components. It allows the autoloading of * various classes related to the operation of a component, from Controllers * and Models to Helpers and Fields. If a class doesn't exist, it will be * created on the fly. * * @package FrameworkOnFramework * @subpackage autoloader * @since 2.1 */ class FOFAutoloaderComponent { /** * An instance of this autoloader * * @var FOFAutoloaderComponent */ public static $autoloader = null; /** * The path to the FOF root directory * * @var string */ public static $fofPath = null; /** * An array holding component names and their FOF-ness status * * @var array */ protected static $fofComponents = array(); /** * Initialise this autoloader * * @return FOFAutoloaderComponent */ public static function init() { if (self::$autoloader == null) { self::$autoloader = new self; } return self::$autoloader; } /** * Public constructor. Registers the autoloader with PHP. */ public function __construct() { self::$fofPath = realpath(__DIR__ . '/../'); spl_autoload_register(array($this,'autoload_fof_controller')); spl_autoload_register(array($this,'autoload_fof_model')); spl_autoload_register(array($this,'autoload_fof_view')); spl_autoload_register(array($this,'autoload_fof_table')); spl_autoload_register(array($this,'autoload_fof_helper')); spl_autoload_register(array($this,'autoload_fof_toolbar')); spl_autoload_register(array($this,'autoload_fof_field')); } /** * Returns true if this is a FOF-powered component, i.e. if it has a fof.xml * file in its main directory. * * @param string $component The component's name * * @return boolean */ public function isFOFComponent($component) { if (!isset($fofComponents[$component])) { $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $fofComponents[$component] = file_exists($componentPaths['admin'] . '/fof.xml'); } return $fofComponents[$component]; } /** * Creates class aliases. On systems where eval() is enabled it creates a * real class. On other systems it merely creates an alias. The eval() * method is preferred as class_aliases result in the name of the class * being instantiated not being available, making it impossible to create * a class instance without passing a $config array :( * * @param string $original The name of the original (existing) class * @param string $alias The name of the new (aliased) class * @param boolean $autoload Should I try to autoload the $original class? * * @return void */ private function class_alias($original, $alias, $autoload = true) { static $hasEval = null; if (is_null($hasEval)) { $hasEval = false; if (function_exists('ini_get')) { $disabled_functions = ini_get('disabled_functions'); if (!is_string($disabled_functions)) { $hasEval = true; } else { $disabled_functions = explode(',', $disabled_functions); $hasEval = !in_array('eval', $disabled_functions); } } } if (!class_exists($original, $autoload)) { return; } if ($hasEval) { $phpCode = "class $alias extends $original {}"; eval($phpCode); } else { class_alias($original, $alias, $autoload); } } /** * Autoload Controllers * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_controller($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'Controller') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need three parts in the name if (count($parts) != 3) { return; } // We need the second part to be "controller" if ($parts[1] != 'controller') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $view = $parts[2]; // Is this an FOF 2.1 or later component? if (!$this->isFOFComponent($component)) { return; } // Get the alternate view and class name (opposite singular/plural name) $alt_view = FOFInflector::isSingular($view) ? FOFInflector::pluralize($view) : FOFInflector::singularize($view); $alt_class = FOFInflector::camelize($component_raw . '_controller_' . $alt_view); // Get the component's paths $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); // Get the proper and alternate paths and file names $file = "/controllers/$view.php"; $altFile = "/controllers/$alt_view.php"; $path = $componentPaths['main']; $altPath = $componentPaths['alt']; // Try to find the proper class in the proper path if (file_exists($path . $file)) { @include_once $path . $file; } // Try to find the proper class in the alternate path if (!class_exists($class_name) && file_exists($altPath . $file)) { @include_once $altPath . $file; } // Try to find the alternate class in the proper path if (!class_exists($alt_class) && file_exists($path . $altFile)) { @include_once $path . $altFile; } // Try to find the alternate class in the alternate path if (!class_exists($alt_class) && file_exists($altPath . $altFile)) { @include_once $altPath . $altFile; } // If the alternate class exists just map the class to the alternate if (!class_exists($class_name) && class_exists($alt_class)) { $this->class_alias($alt_class, $class_name); } // No class found? Map to FOFController elseif (!class_exists($class_name)) { if ($view != 'default') { $defaultClass = FOFInflector::camelize($component_raw . '_controller_default'); $this->class_alias($defaultClass, $class_name); } else { $this->class_alias('FOFController', $class_name); } } } /** * Autoload Models * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_model($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'Model') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need three parts in the name if (count($parts) != 3) { return; } // We need the second part to be "model" if ($parts[1] != 'model') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $view = $parts[2]; // Is this an FOF 2.1 or later component? if (!$this->isFOFComponent($component)) { return; } // Get the alternate view and class name (opposite singular/plural name) $alt_view = FOFInflector::isSingular($view) ? FOFInflector::pluralize($view) : FOFInflector::singularize($view); $alt_class = FOFInflector::camelize($component_raw . '_model_' . $alt_view); // Get the proper and alternate paths and file names $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $file = "/models/$view.php"; $altFile = "/models/$alt_view.php"; $path = $componentPaths['main']; $altPath = $componentPaths['alt']; // Try to find the proper class in the proper path if (file_exists($path . $file)) { @include_once $path . $file; } // Try to find the proper class in the alternate path if (!class_exists($class_name) && file_exists($altPath . $file)) { @include_once $altPath . $file; } // Try to find the alternate class in the proper path if (!class_exists($alt_class) && file_exists($path . $altFile)) { @include_once $path . $altFile; } // Try to find the alternate class in the alternate path if (!class_exists($alt_class) && file_exists($altPath . $altFile)) { @include_once $altPath . $altFile; } // If the alternate class exists just map the class to the alternate if (!class_exists($class_name) && class_exists($alt_class)) { $this->class_alias($alt_class, $class_name); } // No class found? Map to FOFModel elseif (!class_exists($class_name)) { if ($view != 'default') { $defaultClass = FOFInflector::camelize($component_raw . '_model_default'); $this->class_alias($defaultClass, $class_name); } else { $this->class_alias('FOFModel', $class_name, true); } } } /** * Autoload Views * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_view($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'View') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need at least three parts in the name if (count($parts) < 3) { return; } // We need the second part to be "view" if ($parts[1] != 'view') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $view = $parts[2]; if (count($parts) > 3) { $format = $parts[3]; } else { $input = new FOFInput; $format = $input->getCmd('format', 'html', 'cmd'); } // Is this an FOF 2.1 or later component? if (!$this->isFOFComponent($component)) { return; } // Get the alternate view and class name (opposite singular/plural name) $alt_view = FOFInflector::isSingular($view) ? FOFInflector::pluralize($view) : FOFInflector::singularize($view); $alt_class = FOFInflector::camelize($component_raw . '_view_' . $alt_view); // Get the proper and alternate paths and file names $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $protoFile = "/models/$view"; $protoAltFile = "/models/$alt_view"; $path = $componentPaths['main']; $altPath = $componentPaths['alt']; $formats = array($format); if ($format != 'html') { $formats[] = 'raw'; } foreach ($formats as $currentFormat) { $file = $protoFile . '.' . $currentFormat . '.php'; $altFile = $protoAltFile . '.' . $currentFormat . '.php'; // Try to find the proper class in the proper path if (!class_exists($class_name) && file_exists($path . $file)) { @include_once $path . $file; } // Try to find the proper class in the alternate path if (!class_exists($class_name) && file_exists($altPath . $file)) { @include_once $altPath . $file; } // Try to find the alternate class in the proper path if (!class_exists($alt_class) && file_exists($path . $altFile)) { @include_once $path . $altFile; } // Try to find the alternate class in the alternate path if (!class_exists($alt_class) && file_exists($altPath . $altFile)) { @include_once $altPath . $altFile; } } // If the alternate class exists just map the class to the alternate if (!class_exists($class_name) && class_exists($alt_class)) { $this->class_alias($alt_class, $class_name); } // No class found? Map to FOFModel elseif (!class_exists($class_name)) { if ($view != 'default') { $defaultClass = FOFInflector::camelize($component_raw . '_view_default'); $this->class_alias($defaultClass, $class_name); } else { if (!file_exists(self::$fofPath . '/view/' . $format . '.php')) { $default_class = 'FOFView'; } else { $default_class = 'FOFView' . ucfirst($format); } $this->class_alias($default_class, $class_name, true); } } } /** * Autoload Tables * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_table($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'Table') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need three parts in the name if (count($parts) != 3) { return; } // We need the second part to be "model" if ($parts[1] != 'table') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $view = $parts[2]; // Is this an FOF 2.1 or later component? if (!$this->isFOFComponent($component)) { return; } // Get the alternate view and class name (opposite singular/plural name) $alt_view = FOFInflector::isSingular($view) ? FOFInflector::pluralize($view) : FOFInflector::singularize($view); $alt_class = FOFInflector::camelize($component_raw . '_table_' . $alt_view); // Get the proper and alternate paths and file names $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $file = "/tables/$view.php"; $altFile = "/tables/$alt_view.php"; $path = $componentPaths['admin']; // Try to find the proper class in the proper path if (file_exists($path . $file)) { @include_once $path . $file; } // Try to find the alternate class in the proper path if (!class_exists($alt_class) && file_exists($path . $altFile)) { @include_once $path . $altFile; } // If the alternate class exists just map the class to the alternate if (!class_exists($class_name) && class_exists($alt_class)) { $this->class_alias($alt_class, $class_name); } // No class found? Map to FOFModel elseif (!class_exists($class_name)) { if ($view != 'default') { $defaultClass = FOFInflector::camelize($component_raw . '_table_default'); $this->class_alias($defaultClass, $class_name); } else { $this->class_alias('FOFTable', $class_name, true); } } } /** * Autoload Helpers * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_helper($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'Helper') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need three parts in the name if (count($parts) != 3) { return; } // We need the second part to be "model" if ($parts[1] != 'helper') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $view = $parts[2]; // Is this an FOF 2.1 or later component? if (!$this->isFOFComponent($component)) { return; } // Get the alternate view and class name (opposite singular/plural name) $alt_view = FOFInflector::isSingular($view) ? FOFInflector::pluralize($view) : FOFInflector::singularize($view); $alt_class = FOFInflector::camelize($component_raw . '_helper_' . $alt_view); // Get the proper and alternate paths and file names $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $file = "/helpers/$view.php"; $altFile = "/helpers/$alt_view.php"; $path = $componentPaths['main']; $altPath = $componentPaths['alt']; // Try to find the proper class in the proper path if (file_exists($path . $file)) { @include_once $path . $file; } // Try to find the proper class in the alternate path if (!class_exists($class_name) && file_exists($altPath . $file)) { @include_once $altPath . $file; } // Try to find the alternate class in the proper path if (!class_exists($alt_class) && file_exists($path . $altFile)) { @include_once $path . $altFile; } // Try to find the alternate class in the alternate path if (!class_exists($alt_class) && file_exists($altPath . $altFile)) { @include_once $altPath . $altFile; } // If the alternate class exists just map the class to the alternate if (!class_exists($class_name) && class_exists($alt_class)) { $this->class_alias($alt_class, $class_name); } } /** * Autoload Toolbars * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_toolbar($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); static $isCli = null, $isAdmin = null; if (is_null($isCli) && is_null($isAdmin)) { list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin(); } if (strpos($class_name, 'Toolbar') === false) { return; } // Change from camel cased into a lowercase array $class_modified = preg_replace('/(\s)+/', '_', $class_name); $class_modified = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class_modified)); $parts = explode('_', $class_modified); // We need two parts in the name if (count($parts) != 2) { return; } // We need the second part to be "model" if ($parts[1] != 'toolbar') { return; } // Get the information about this class $component_raw = $parts[0]; $component = 'com_' . $parts[0]; $platformDirs = FOFPlatform::getInstance()->getPlatformBaseDirs(); // Get the proper and alternate paths and file names $file = "/components/$component/toolbar.php"; $path = ($isAdmin || $isCli) ? $platformDirs['admin'] : $platformDirs['public']; $altPath = ($isAdmin || $isCli) ? $platformDirs['public'] : $platformDirs['admin']; // Try to find the proper class in the proper path if (file_exists($path . $file)) { @include_once $path . $file; } // Try to find the proper class in the alternate path if (!class_exists($class_name) && file_exists($altPath . $file)) { @include_once $altPath . $file; } // No class found? Map to FOFToolbar if (!class_exists($class_name)) { $this->class_alias('FOFToolbar', $class_name, true); } } /** * Autoload Fields * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_field($class_name) { FOFPlatform::getInstance()->logDebug(__METHOD__ . "() autoloading $class_name"); // @todo } } PK ! &/g!� � autoloader/fof.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage autoloader * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ defined('FOF_INCLUDED') or die(); /** * The main class autoloader for FOF itself * * @package FrameworkOnFramework * @subpackage autoloader * @since 2.1 */ class FOFAutoloaderFof { /** * An instance of this autoloader * * @var FOFAutoloaderFof */ public static $autoloader = null; /** * The path to the FOF root directory * * @var string */ public static $fofPath = null; /** * Initialise this autoloader * * @return FOFAutoloaderFof */ public static function init() { if (self::$autoloader == null) { self::$autoloader = new self; } return self::$autoloader; } /** * Public constructor. Registers the autoloader with PHP. */ public function __construct() { self::$fofPath = realpath(__DIR__ . '/../'); spl_autoload_register(array($this,'autoload_fof_core')); } /** * The actual autoloader * * @param string $class_name The name of the class to load * * @return void */ public function autoload_fof_core($class_name) { // Make sure the class has a FOF prefix if (substr($class_name, 0, 3) != 'FOF') { return; } // Remove the prefix $class = substr($class_name, 3); // Change from camel cased (e.g. ViewHtml) into a lowercase array (e.g. 'view','html') $class = preg_replace('/(\s)+/', '_', $class); $class = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $class)); $class = explode('_', $class); // First try finding in structured directory format (preferred) $path = self::$fofPath . '/' . implode('/', $class) . '.php'; if (@file_exists($path)) { include_once $path; } // Then try the duplicate last name structured directory format (not recommended) if (!class_exists($class_name, false)) { reset($class); $lastPart = end($class); $path = self::$fofPath . '/' . implode('/', $class) . '/' . $lastPart . '.php'; if (@file_exists($path)) { include_once $path; } } // If it still fails, try looking in the legacy folder (used for backwards compatibility) if (!class_exists($class_name, false)) { $path = self::$fofPath . '/legacy/' . implode('/', $class) . '.php'; if (@file_exists($path)) { include_once $path; } } } } PK ! s�_�� � config/domain/dispatcher.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage config * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ defined('FOF_INCLUDED') or die(); /** * Configuration parser for the dispatcher-specific settings * * @package FrameworkOnFramework * @since 2.1 */ class FOFConfigDomainDispatcher implements FOFConfigDomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['dispatcher'] = array(); // Parse the dispatcher configuration $dispatcherData = $xml->dispatcher; // Sanity check if (empty($dispatcherData)) { return; } $options = $xml->xpath('dispatcher/option'); if (!empty($options)) { foreach ($options as $option) { $key = (string) $option['name']; $ret['dispatcher'][$key] = (string) $option; } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { if (isset($configuration['dispatcher'][$var])) { return $configuration['dispatcher'][$var]; } else { return $default; } } } PK ! I{.�^ ^ config/domain/interface.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage config * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ defined('FOF_INCLUDED') or die(); /** * The Interface of an FOFConfigDomain class. The methods are used to parse and * provision sensible information to consumers. FOFConfigProvider acts as an * adapter to the FOFConfigDomain classes. * * @package FrameworkOnFramework * @since 2.1 */ interface FOFConfigDomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret); /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default); } PK ! Y�Ć � config/domain/tables.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage config * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ defined('FOF_INCLUDED') or die(); /** * Configuration parser for the tables-specific settings * * @package FrameworkOnFramework * @since 2.1 */ class FOFConfigDomainTables implements FOFConfigDomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['tables'] = array(); // Parse table configuration $tableData = $xml->xpath('table'); // Sanity check if (empty($tableData)) { return; } foreach ($tableData as $aTable) { $key = (string) $aTable['name']; $ret['tables'][$key]['behaviors'] = (string) $aTable->behaviors; $ret['tables'][$key]['tablealias'] = $aTable->xpath('tablealias'); $ret['tables'][$key]['fields'] = array(); $ret['tables'][$key]['relations'] = array(); $fieldData = $aTable->xpath('field'); if (!empty($fieldData)) { foreach ($fieldData as $field) { $k = (string) $field['name']; $ret['tables'][$key]['fields'][$k] = (string) $field; } } $relationsData = $aTable->xpath('relation'); if (!empty($relationsData)) { foreach ($relationsData as $relationData) { $type = (string)$relationData['type']; $itemName = (string)$relationData['name']; if (empty($type) || empty($itemName)) { continue; } $tableClass = (string)$relationData['tableClass']; $localKey = (string)$relationData['localKey']; $remoteKey = (string)$relationData['remoteKey']; $ourPivotKey = (string)$relationData['ourPivotKey']; $theirPivotKey = (string)$relationData['theirPivotKey']; $pivotTable = (string)$relationData['pivotTable']; $default = (string)$relationData['default']; $default = !in_array($default, array('no', 'false', 0)); $relation = array( 'type' => $type, 'itemName' => $itemName, 'tableClass' => empty($tableClass) ? null : $tableClass, 'localKey' => empty($localKey) ? null : $localKey, 'remoteKey' => empty($remoteKey) ? null : $remoteKey, 'default' => $default, ); if (!empty($ourPivotKey) || !empty($theirPivotKey) || !empty($pivotTable)) { $relation['ourPivotKey'] = empty($ourPivotKey) ? null : $ourPivotKey; $relation['theirPivotKey'] = empty($theirPivotKey) ? null : $theirPivotKey; $relation['pivotTable'] = empty($pivotTable) ? null : $pivotTable; } $ret['tables'][$key]['relations'][] = $relation; } } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); $ret = $this->$method($view, $configuration, $parts, $default); return $ret; } /** * Internal method to return the magic field mapping * * @param string $table The table for which we will be fetching a field map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the table we want to fetch * @param string $default Default magic field mapping; empty if not defined * * @return array Field map */ protected function getField($table, &$configuration, $params, $default = '') { $fieldmap = array(); if (isset($configuration['tables']['*']) && isset($configuration['tables']['*']['fields'])) { $fieldmap = $configuration['tables']['*']['fields']; } if (isset($configuration['tables'][$table]) && isset($configuration['tables'][$table]['fields'])) { $fieldmap = array_merge($fieldmap, $configuration['tables'][$table]['fields']); } $map = $default; if (empty($params[0])) { $map = $fieldmap; } elseif (isset($fieldmap[$params[0]])) { $map = $fieldmap[$params[0]]; } return $map; } /** * Internal method to get table alias * * @param string $table The table for which we will be fetching table alias * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the table we want to fetch * @param string $default Default table alias * * @return string Table alias */ protected function getTablealias($table, &$configuration, $params, $default = '') { $tablealias = $default; if (isset($configuration['tables']['*']) && isset($configuration['tables']['*']['tablealias']) && isset($configuration['tables']['*']['tablealias'][0])) { $tablealias = (string) $configuration['tables']['*']['tablealias'][0]; } if (isset($configuration['tables'][$table]) && isset($configuration['tables'][$table]['tablealias']) && isset($configuration['tables'][$table]['tablealias'][0])) { $tablealias = (string) $configuration['tables'][$table]['tablealias'][0]; } return $tablealias; } /** * Internal method to get table behaviours * * @param string $table The table for which we will be fetching table alias * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the table we want to fetch * @param string $default Default table alias * * @return string Table behaviours */ protected function getBehaviors($table, &$configuration, $params, $default = '') { $behaviors = $default; if (isset($configuration['tables']['*']) && isset($configuration['tables']['*']['behaviors'])) { $behaviors = (string) $configuration['tables']['*']['behaviors']; } if (isset($configuration['tables'][$table]) && isset($configuration['tables'][$table]['behaviors'])) { $behaviors = (string) $configuration['tables'][$table]['behaviors']; } return $behaviors; } /** * Internal method to get table relations * * @param string $table The table for which we will be fetching table alias * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the table we want to fetch * @param string $default Default table alias * * @return array Table relations */ protected function getRelations($table, &$configuration, $params, $default = '') { $relations = $default; if (isset($configuration['tables']['*']) && isset($configuration['tables']['*']['relations'])) { $relations = $configuration['tables']['*']['relations']; } if (isset($configuration['tables'][$table]) && isset($configuration['tables'][$table]['relations'])) { $relations = $configuration['tables'][$table]['relations']; } return $relations; } } PK ! �H_� � config/domain/views.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage config * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ defined('FOF_INCLUDED') or die(); /** * Configuration parser for the view-specific settings * * @package FrameworkOnFramework * @since 2.1 */ class FOFConfigDomainViews implements FOFConfigDomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['views'] = array(); // Parse view configuration $viewData = $xml->xpath('view'); // Sanity check if (empty($viewData)) { return; } foreach ($viewData as $aView) { $key = (string) $aView['name']; // Parse ACL options $ret['views'][$key]['acl'] = array(); $aclData = $aView->xpath('acl/task'); if (!empty($aclData)) { foreach ($aclData as $acl) { $k = (string) $acl['name']; $ret['views'][$key]['acl'][$k] = (string) $acl; } } // Parse taskmap $ret['views'][$key]['taskmap'] = array(); $taskmapData = $aView->xpath('taskmap/task'); if (!empty($taskmapData)) { foreach ($taskmapData as $map) { $k = (string) $map['name']; $ret['views'][$key]['taskmap'][$k] = (string) $map; } } // Parse controller configuration $ret['views'][$key]['config'] = array(); $optionData = $aView->xpath('config/option'); if (!empty($optionData)) { foreach ($optionData as $option) { $k = (string) $option['name']; $ret['views'][$key]['config'][$k] = (string) $option; } } // Parse the toolbar $ret['views'][$key]['toolbar'] = array(); $toolBars = $aView->xpath('toolbar'); if (!empty($toolBars)) { foreach ($toolBars as $toolBar) { $taskName = isset($toolBar['task']) ? (string) $toolBar['task'] : '*'; // If a toolbar title is specified, create a title element. if (isset($toolBar['title'])) { $ret['views'][$key]['toolbar'][$taskName]['title'] = array( 'value' => (string) $toolBar['title'] ); } // Parse the toolbar buttons data $toolbarData = $toolBar->xpath('button'); if (!empty($toolbarData)) { foreach ($toolbarData as $button) { $k = (string) $button['type']; $ret['views'][$key]['toolbar'][$taskName][$k] = current($button->attributes()); $ret['views'][$key]['toolbar'][$taskName][$k]['value'] = (string) $button; } } } } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); $ret = $this->$method($view, $configuration, $parts, $default); return $ret; } /** * Internal function to return the task map for a view * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options (not used) * @param array $default ßDefault task map; empty array if not provided * * @return array The task map as a hash array in the format task => method */ protected function getTaskmap($view, &$configuration, $params, $default = array()) { $taskmap = array(); if (isset($configuration['views']['*']) && isset($configuration['views']['*']['taskmap'])) { $taskmap = $configuration['views']['*']['taskmap']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['taskmap'])) { $taskmap = array_merge($taskmap, $configuration['views'][$view]['taskmap']); } if (empty($taskmap)) { return $default; } return $taskmap; } /** * Internal method to return the ACL mapping (privilege required to access * a specific task) for the given view's tasks * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the task we want to fetch * @param string $default Default ACL option; empty (no ACL check) if not defined * * @return string The privilege required to access this view */ protected function getAcl($view, &$configuration, $params, $default = '') { $aclmap = array(); if (isset($configuration['views']['*']) && isset($configuration['views']['*']['acl'])) { $aclmap = $configuration['views']['*']['acl']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['acl'])) { $aclmap = array_merge($aclmap, $configuration['views'][$view]['acl']); } $acl = $default; if (isset($aclmap['*'])) { $acl = $aclmap['*']; } if (isset($aclmap[$params[0]])) { $acl = $aclmap[$params[0]]; } return $acl; } /** * Internal method to return the a configuration option for the view. These * are equivalent to $config array options passed to the Controller * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the option variable we want to fetch * @param mixed $default Default option; null if not defined * * @return string The setting for the requested option */ protected function getConfig($view, &$configuration, $params, $default = null) { $ret = $default; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['config']) && isset($configuration['views']['*']['config'][$params[0]])) { $ret = $configuration['views']['*']['config'][$params[0]]; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['config']) && isset($configuration['views'][$view]['config'][$params[0]])) { $ret = $configuration['views'][$view]['config'][$params[0]]; } return $ret; } /** * Internal method to return the toolbar infos. * * @param string $view The view for which we will be fetching buttons * @param array &$configuration The configuration parameters hash array * @param array $params Extra options * @param string $default Default option * * @return string The toolbar data for this view */ protected function getToolbar($view, &$configuration, $params, $default = '') { $toolbar = array(); if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar']['*'])) { $toolbar = $configuration['views']['*']['toolbar']['*']; } if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views']['*']['toolbar'][$params[0]]); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar']['*'])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar']['*']); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar'][$params[0]]); } if (empty($toolbar)) { return $default; } return $toolbar; } } PK ! W�5� config/provider.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage config * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ defined('FOF_INCLUDED') or die(); /** * Reads and parses the fof.xml file in the back-end of a FOF-powered component, * provisioning the data to the rest of the FOF framework * * @package FrameworkOnFramework * @since 2.1 */ class FOFConfigProvider { /** * Cache of FOF components' configuration variables * * @var array */ public static $configurations = array(); /** * Parses the configuration of the specified component * * @param string $component The name of the component, e.g. com_foobar * @param boolean $force Force reload even if it's already parsed? * * @return void */ public function parseComponent($component, $force = false) { if (!$force && isset(self::$configurations[$component])) { return; } if (FOFPlatform::getInstance()->isCli()) { $order = array('cli', 'backend'); } elseif (FOFPlatform::getInstance()->isBackend()) { $order = array('backend'); } else { $order = array('frontend'); } $order[] = 'common'; $order = array_reverse($order); self::$configurations[$component] = array(); foreach ($order as $area) { $config = $this->parseComponentArea($component, $area); self::$configurations[$component] = array_merge_recursive(self::$configurations[$component], $config); } } /** * Returns the value of a variable. Variables use a dot notation, e.g. * view.config.whatever where the first part is the domain, the rest of the * parts specify the path to the variable. * * @param string $variable The variable name * @param mixed $default The default value, or null if not specified * * @return mixed The value of the variable */ public function get($variable, $default = null) { static $domains = null; if (is_null($domains)) { $domains = $this->getDomains(); } list($component, $domain, $var) = explode('.', $variable, 3); if (!isset(self::$configurations[$component])) { $this->parseComponent($component); } if (!in_array($domain, $domains)) { return $default; } $class = 'FOFConfigDomain' . ucfirst($domain); $o = new $class; return $o->get(self::$configurations[$component], $var, $default); } /** * Parses the configuration options of a specific component area * * @param string $component Which component's configuration to parse * @param string $area Which area to parse (frontend, backend, cli) * * @return array A hash array with the configuration data */ protected function parseComponentArea($component, $area) { // Initialise the return array $ret = array(); // Get the folders of the component $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($component); $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem'); // Check that the path exists $path = $componentPaths['admin']; $path = $filesystem->pathCheck($path); if (!$filesystem->folderExists($path)) { return $ret; } // Read the filename if it exists $filename = $path . '/fof.xml'; if (!$filesystem->fileExists($filename)) { return $ret; } $data = file_get_contents($filename); // Load the XML data in a SimpleXMLElement object $xml = simplexml_load_string($data); if (!($xml instanceof SimpleXMLElement)) { return $ret; } // Get this area's data $areaData = $xml->xpath('//' . $area); if (empty($areaData)) { return $ret; } $xml = array_shift($areaData); // Parse individual configuration domains $domains = $this->getDomains(); foreach ($domains as $dom) { $class = 'FOFConfigDomain' . ucfirst($dom); if (class_exists($class, true)) { $o = new $class; $o->parseDomain($xml, $ret); } } // Finally, return the result return $ret; } /** * Gets a list of the available configuration domain adapters * * @return array A list of the available domains */ protected function getDomains() { static $domains = array(); if (empty($domains)) { $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem'); $files = $filesystem->folderFiles(__DIR__ . '/domain', '.php'); if (!empty($files)) { foreach ($files as $file) { $domain = basename($file, '.php'); if ($domain == 'interface') { continue; } $domain = preg_replace('/[^A-Za-z0-9]/', '', $domain); $domains[] = $domain; } $domains = array_unique($domains); } } return $domains; } } PK ! �=O%R/ R/ controller/controller.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage controller * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * FrameworkOnFramework controller class. FOF is based on the thin controller * paradigm, where the controller is mainly used to set up the model state and * spawn the view. * * @package FrameworkOnFramework * @since 1.0 */ class FOFController extends FOFUtilsObject { /** * @var int Bit mask to enable Routing on redirects. * 0 = never * 1 = frontend only * 2 = backend only * 3 = always */ protected $autoRouting = 0; /** * The current component's name without the com_ prefix * * @var string */ protected $bareComponent = 'foobar'; /** * The base path of the controller * * @var string */ protected $basePath; /** * The tasks for which caching should be enabled by default * * @var array */ protected $cacheableTasks = array('browse', 'read'); /** * The current component's name; you can override it in the configuration * * @var string */ protected $component = 'com_foobar'; /** * A cached copy of the class configuration parameter passed during initialisation * * @var array */ protected $config = array(); /** * An instance of FOFConfigProvider to provision configuration overrides * * @var FOFConfigProvider */ protected $configProvider = null; /** * Set to true to enable CSRF protection on selected tasks. The possible * values are: * 0 Disabled; no token checks are performed * 1 Enabled; token checks are always performed * 2 Only on HTML requests and backend; token checks are always performed in the back-end and in the front-end only when format is 'html' * 3 Only on back-end; token checks are performer only in the back-end * * @var integer */ protected $csrfProtection = 2; /** * The default view for the display method. * * @var string */ protected $default_view; /** * The mapped task that was performed. * * @var string */ protected $doTask; /** * The input object for this MVC triad; you can override it in the configuration * * @var FOFInput */ protected $input = array(); /** * Redirect message. * * @var string */ protected $message; /** * Redirect message type. * * @var string */ protected $messageType; /** * The current layout; you can override it in the configuration * * @var string */ protected $layout = null; /** * Array of class methods * * @var array */ protected $methods; /** * The prefix of the models * * @var string */ protected $model_prefix; /** * Overrides the name of the view's default model * * @var string */ protected $modelName = null; /** * The set of search directories for resources (views). * * @var array */ protected $paths; /** * URL for redirection. * * @var string */ protected $redirect; /** * Current or most recently performed task. * * @var string */ protected $task; /** * Array of class methods to call for a given task. * * @var array */ protected $taskMap; /** * The name of the controller * * @var array */ protected $name; /** * The current view name; you can override it in the configuration * * @var string */ protected $view = ''; /** * Overrides the name of the view's default view * * @var string */ protected $viewName = null; /** * A copy of the FOFView object used in this triad * * @var FOFView */ private $_viewObject = null; /** * A cache for the view item objects created in this controller * * @var array */ protected $viewsCache = array(); /** * A copy of the FOFModel object used in this triad * * @var FOFModel */ private $_modelObject = null; /** * Does this tried have a FOFForm which will be used to render it? * * @var boolean */ protected $hasForm = false; /** * Gets a static (Singleton) instance of a controller class. It loads the * relevant controller file from the component's directory or, if it doesn't * exist, creates a new controller object out of thin air. * * @param string $option Component name, e.g. com_foobar * @param string $view The view name, also used for the controller name * @param array $config Configuration parameters * * @return FOFController */ public static function &getAnInstance($option = null, $view = null, $config = array()) { static $instances = array(); // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } $hash = $option . $view; if (!array_key_exists($hash, $instances)) { $instances[$hash] = self::getTmpInstance($option, $view, $config); } return $instances[$hash]; } /** * Gets a temporary instance of a controller object. A temporary instance is * not a Singleton and can be disposed off after use. * * @param string $option The component name, e.g. com_foobar * @param string $view The view name, e.g. cpanel * @param array $config Configuration parameters * * @return \FOFController A disposable class instance */ public static function &getTmpInstance($option = null, $view = null, $config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } // Get an input object if (array_key_exists('input', $config)) { $input = $config['input']; } else { $input = null; } if (array_key_exists('input_options', $config)) { $input_options = $config['input_options']; } else { $input_options = array(); } if (!($input instanceof FOFInput)) { $input = new FOFInput($input, $input_options); } // Determine the option (component name) and view $config['option'] = !is_null($option) ? $option : $input->getCmd('option', 'com_foobar'); $config['view'] = !is_null($view) ? $view : $input->getCmd('view', 'cpanel'); // Get the class base name, e.g. FoobarController $classBaseName = ucfirst(str_replace('com_', '', $config['option'])) . 'Controller'; // Get the class name suffixes, in the order to be searched for: plural, singular, 'default' $classSuffixes = array( FOFInflector::pluralize($config['view']), FOFInflector::singularize($config['view']), 'default' ); // Get the path names for the component $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($config['option']); $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem'); // Look for the best classname match foreach ($classSuffixes as $suffix) { $className = $classBaseName . ucfirst($suffix); if (class_exists($className)) { // The class is already loaded. We have a match! break; } // The class is not already loaded. Try to find and load it. $searchPaths = array( $componentPaths['main'] . '/controllers', $componentPaths['admin'] . '/controllers' ); // If we have a searchpath in the configuration please search it first if (array_key_exists('searchpath', $config)) { array_unshift($searchPaths, $config['searchpath']); } else { $configProvider = new FOFConfigProvider; $searchPath = $configProvider->get($config['option'] . '.views.' . FOFInflector::singularize($config['view']) . '.config.searchpath', null); if ($searchPath) { array_unshift($searchPaths, $componentPaths['admin'] . '/' . $searchPath); array_unshift($searchPaths, $componentPaths['main'] . '/' . $searchPath); } } /** * Try to find the path to this file. First try to find the * format-specific controller file, e.g. foobar.json.php for * format=json, then the regular one-size-fits-all controller */ $format = $input->getCmd('format', 'html'); $path = null; if (!empty($format)) { $path = $filesystem->pathFind( $searchPaths, strtolower($suffix) . '.' . strtolower($format) . '.php' ); } if (!$path) { $path = $filesystem->pathFind( $searchPaths, strtolower($suffix) . '.php' ); } // The path is found. Load the file and make sure the expected class name exists. if ($path) { require_once $path; if (class_exists($className)) { // The class was loaded successfully. We have a match! break; } } } if (!class_exists($className)) { // If no specialised class is found, instantiate the generic FOFController $className = 'FOFController'; } $instance = new $className($config); return $instance; } /** * Public constructor of the Controller class * * @param array $config Optional configuration parameters */ public function __construct($config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } $this->methods = array(); $this->message = null; $this->messageType = 'message'; $this->paths = array(); $this->redirect = null; $this->taskMap = array(); // Cache the config $this->config = $config; // Get the input for this MVC triad if (array_key_exists('input', $config)) { $input = $config['input']; } else { $input = null; } if (array_key_exists('input_options', $config)) { $input_options = $config['input_options']; } else { $input_options = array(); } if ($input instanceof FOFInput) { $this->input = $input; } else { $this->input = new FOFInput($input, $input_options); } // Load the configuration provider $this->configProvider = new FOFConfigProvider; // Determine the methods to exclude from the base class. $xMethods = get_class_methods('FOFController'); // Some methods must always be considered valid tasks $iMethods = array('accesspublic', 'accessregistered', 'accessspecial', 'add', 'apply', 'browse', 'cancel', 'copy', 'edit', 'orderdown', 'orderup', 'publish', 'read', 'remove', 'save', 'savenew', 'saveorder', 'unpublish', 'display', 'archive', 'trash', 'loadhistory'); // Get the public methods in this class using reflection. $r = new ReflectionClass($this); $rMethods = $r->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($rMethods as $rMethod) { $mName = $rMethod->getName(); // If the developer screwed up and declared one of the helper method public do NOT make them available as // tasks. if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0, 7) == 'onAfter') || substr($mName, 0, 1) == '_') { continue; } // Add default display method if not explicitly declared. if (!in_array($mName, $xMethods) || in_array($mName, $iMethods)) { $this->methods[] = strtolower($mName); // Auto register the methods as tasks. $this->taskMap[strtolower($mName)] = $mName; } } // Get the default values for the component and view names $classNameParts = FOFInflector::explode(get_class($this)); if (count($classNameParts) == 3) { $defComponent = "com_" . $classNameParts[0]; $defView = $classNameParts[2]; } else { $defComponent = 'com_foobar'; $defView = 'cpanel'; } $this->component = $this->input->get('option', $defComponent, 'cmd'); $this->view = $this->input->get('view', $defView, 'cmd'); $this->layout = $this->input->get('layout', null, 'cmd'); // Overrides from the config if (array_key_exists('option', $config)) { $this->component = $config['option']; } if (array_key_exists('view', $config)) { $this->view = $config['view']; } if (array_key_exists('layout', $config)) { $this->layout = $config['layout']; } $this->layout = $this->configProvider->get($this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.layout', $this->layout); $this->input->set('option', $this->component); // Set the bareComponent variable $this->bareComponent = str_replace('com_', '', strtolower($this->component)); // Set the $name variable $this->name = $this->bareComponent; // Set the basePath variable $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($this->component); $basePath = $componentPaths['main']; if (array_key_exists('base_path', $config)) { $basePath = $config['base_path']; } $altBasePath = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.base_path', null ); if (!is_null($altBasePath)) { $platformDirs = FOFPlatform::getInstance()->getPlatformBaseDirs(); $basePath = $platformDirs['public'] . '/' . $altBasePath; } $this->basePath = $basePath; // If the default task is set, register it as such $defaultTask = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.default_task', 'display' ); if (array_key_exists('default_task', $config)) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask($defaultTask); } // Set the models prefix if (empty($this->model_prefix)) { if (array_key_exists('model_prefix', $config)) { // User-defined prefix $this->model_prefix = $config['model_prefix']; } else { $this->model_prefix = $this->name . 'Model'; $this->model_prefix = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.model_prefix', $this->model_prefix ); } } // Set the default model search path if (array_key_exists('model_path', $config)) { // User-defined dirs $this->addModelPath($config['model_path'], $this->model_prefix); } else { $modelPath = $this->basePath . '/models'; $altModelPath = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.model_path', null ); if (!is_null($altModelPath)) { $modelPath = $this->basePath . '/' . $altModelPath; } $this->addModelPath($modelPath, $this->model_prefix); } // Set the default view search path if (array_key_exists('view_path', $config)) { // User-defined dirs $this->setPath('view', $config['view_path']); } else { $viewPath = $this->basePath . '/views'; $altViewPath = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.view_path', null ); if (!is_null($altViewPath)) { $viewPath = $this->basePath . '/' . $altViewPath; } $this->setPath('view', $viewPath); } // Set the default view. if (array_key_exists('default_view', $config)) { $this->default_view = $config['default_view']; } else { if (empty($this->default_view)) { $this->default_view = $this->getName(); } $this->default_view = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.default_view', $this->default_view ); } // Set the CSRF protection if (array_key_exists('csrf_protection', $config)) { $this->csrfProtection = $config['csrf_protection']; } $this->csrfProtection = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.csrf_protection', $this->csrfProtection ); // Set any model/view name overrides if (array_key_exists('viewName', $config)) { $this->setThisViewName($config['viewName']); } else { $overrideViewName = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.viewName', null ); if ($overrideViewName) { $this->setThisViewName($overrideViewName); } } if (array_key_exists('modelName', $config)) { $this->setThisModelName($config['modelName']); } else { $overrideModelName = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.modelName', null ); if ($overrideModelName) { $this->setThisModelName($overrideModelName); } } // Caching if (array_key_exists('cacheableTasks', $config)) { if (is_array($config['cacheableTasks'])) { $this->cacheableTasks = $config['cacheableTasks']; } } else { $cacheableTasks = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.cacheableTasks', null ); if ($cacheableTasks) { $cacheableTasks = explode(',', $cacheableTasks); if (count($cacheableTasks)) { $temp = array(); foreach ($cacheableTasks as $t) { $temp[] = trim($t); } $temp = array_unique($temp); $this->cacheableTasks = $temp; } } } // Bit mask for auto routing on setRedirect $this->autoRouting = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.config.autoRouting', $this->autoRouting ); if (array_key_exists('autoRouting', $config)) { $this->autoRouting = $config['autoRouting']; } // Apply task map $taskmap = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.taskmap' ); if (is_array($taskmap) && !empty($taskmap)) { foreach ($taskmap as $aliasedtask => $realmethod) { $this->registerTask($aliasedtask, $realmethod); } } } /** * Adds to the stack of model paths in LIFO order. * * @param mixed $path The directory (string) , or list of directories (array) to add. * @param string $prefix A prefix for models * * @return void */ public static function addModelPath($path, $prefix = '') { FOFModel::addIncludePath($path, $prefix); } /** * Adds to the search path for templates and resources. * * @param string $type The path type (e.g. 'model', 'view'). * @param mixed $path The directory string or stream array to search. * * @return FOFController A FOFController object to support chaining. */ protected function addPath($type, $path) { // Just force path to array settype($path, 'array'); $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem'); if (!isset($this->paths[$type])) { $this->paths[$type] = array(); } // Loop through the path directories foreach ($path as $dir) { // No surrounding spaces allowed! $dir = rtrim($filesystem->pathCheck($dir, '/'), '/') . '/'; // Add to the top of the search dirs array_unshift($this->paths[$type], $dir); } return $this; } /** * Add one or more view paths to the controller's stack, in LIFO order. * * @param mixed $path The directory (string) or list of directories (array) to add. * * @return FOFController This object to support chaining. */ public function addViewPath($path) { $this->addPath('view', $path); return $this; } /** * Authorisation check * * @param string $task The ACO Section Value to check access on. * * @return boolean True if authorised * * @deprecated 2.0 Use JAccess instead. */ public function authorise($task) { FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .__METHOD__ . ' is deprecated. Use checkACL() instead.'); return true; } /** * Create the filename for a resource. * * @param string $type The resource type to create the filename for. * @param array $parts An associative array of filename information. Optional. * * @return string The filename. */ protected static function createFileName($type, $parts = array()) { $filename = ''; switch ($type) { case 'controller': if (!empty($parts['format'])) { if ($parts['format'] == 'html') { $parts['format'] = ''; } else { $parts['format'] = '.' . $parts['format']; } } else { $parts['format'] = ''; } $filename = strtolower($parts['name'] . $parts['format'] . '.php'); break; case 'view': if (!empty($parts['type'])) { $parts['type'] = '.' . $parts['type']; } else { $parts['type'] = ''; } $filename = strtolower($parts['name'] . '/view' . $parts['type'] . '.php'); break; } return $filename; } /** * Executes a given controller task. The onBefore<task> and onAfter<task> * methods are called automatically if they exist. * * @param string $task The task to execute, e.g. "browse" * * @throws Exception Exception thrown if the onBefore<task> returns false * * @return null|bool False on execution failure */ public function execute($task) { $this->task = $task; $method_name = 'onBefore' . ucfirst($task); if (!method_exists($this, $method_name)) { $result = $this->onBeforeGenericTask($task); } elseif (method_exists($this, $method_name)) { $result = $this->$method_name(); } else { $result = true; } if ($result) { $plugin_event = FOFInflector::camelize('on before ' . $this->bareComponent . ' controller ' . $this->view . ' ' . $task); $plugin_result = FOFPlatform::getInstance()->runPlugins($plugin_event, array(&$this, &$this->input)); if (in_array(false, $plugin_result, true)) { $result = false; } } if (!$result) { throw new Exception(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); } // Do not allow the display task to be directly called $task = strtolower($task); if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { $doTask = null; } if ($doTask == 'display') { FOFPlatform::getInstance()->setHeader('Status', '400 Bad Request', true); throw new Exception('Bad Request', 400); } $this->doTask = $doTask; $ret = $this->$doTask(); $method_name = 'onAfter' . ucfirst($task); if (method_exists($this, $method_name)) { $result = $this->$method_name(); } else { $result = true; } if ($result) { $plugin_event = FOFInflector::camelize('on after ' . $this->bareComponent . ' controller ' . $this->view . ' ' . $task); $plugin_result = FOFPlatform::getInstance()->runPlugins($plugin_event, array(&$this, &$this->input, &$ret)); if (in_array(false, $plugin_result, true)) { $result = false; } } if (!$result) { throw new Exception(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); } return $ret; } /** * Default task. Assigns a model to the view and asks the view to render * itself. * * YOU MUST NOT USETHIS TASK DIRECTLY IN A URL. It is supposed to be * used ONLY inside your code. In the URL, use task=browse instead. * * @param bool $cachable Is this view cacheable? * @param bool $urlparams Add your safe URL parameters (see further down in the code) * @param string $tpl The name of the template file to parse * * @return bool */ public function display($cachable = false, $urlparams = false, $tpl = null) { $document = FOFPlatform::getInstance()->getDocument(); if ($document instanceof JDocument) { $viewType = $document->getType(); } else { $viewType = $this->input->getCmd('format', 'html'); } $view = $this->getThisView(); // Get/Create the model if ($model = $this->getThisModel()) { // Push the model into the view (as default) $view->setModel($model, true); } // Set the layout $view->setLayout(is_null($this->layout) ? 'default' : $this->layout); // Display the view $conf = FOFPlatform::getInstance()->getConfig(); if (FOFPlatform::getInstance()->isFrontend() && $cachable && ($viewType != 'feed') && $conf->get('caching') >= 1) { // Get a JCache object $option = $this->input->get('option', 'com_foobar', 'cmd'); $cache = JFactory::getCache($option, 'view'); // Set up a cache ID based on component, view, task and user group assignment $user = FOFPlatform::getInstance()->getUser(); if ($user->guest) { $groups = array(); } else { $groups = $user->groups; } $importantParameters = array(); // Set up safe URL parameters if (!is_array($urlparams)) { $urlparams = array( 'option' => 'CMD', 'view' => 'CMD', 'task' => 'CMD', 'format' => 'CMD', 'layout' => 'CMD', 'id' => 'INT', ); } if (is_array($urlparams)) { $app = JFactory::getApplication(); $registeredurlparams = null; if (version_compare(JVERSION, '3.0', 'ge')) { if (property_exists($app, 'registeredurlparams')) { $registeredurlparams = $app->registeredurlparams; } } else { $registeredurlparams = $app->get('registeredurlparams'); } if (empty($registeredurlparams)) { $registeredurlparams = new stdClass; } foreach ($urlparams AS $key => $value) { // Add your safe url parameters with variable type as value {@see JFilterInput::clean()}. $registeredurlparams->$key = $value; // Add the URL-important parameters into the array $importantParameters[$key] = $this->input->get($key, null, $value); } if (version_compare(JVERSION, '3.0', 'ge')) { $app->registeredurlparams = $registeredurlparams; } else { $app->set('registeredurlparams', $registeredurlparams); } } // Create the cache ID after setting the registered URL params, as they are used to generate the ID $cacheId = md5(serialize(array(JCache::makeId(), $view->getName(), $this->doTask, $groups, $importantParameters))); // Get the cached view or cache the current view $cache->get($view, 'display', $cacheId); } else { // Display without caching $view->display($tpl); } return true; } /** * Implements a default browse task, i.e. read a bunch of records and send * them to the browser. * * @return boolean */ public function browse() { if ($this->input->get('savestate', -999, 'int') == -999) { $this->input->set('savestate', true); } // Do I have a form? $model = $this->getThisModel(); if (empty($this->layout)) { $formname = 'form.default'; } else { $formname = 'form.' . $this->layout; } $model->setState('form_name', $formname); $form = $model->getForm(); if ($form !== false) { $this->hasForm = true; } $this->display(in_array('browse', $this->cacheableTasks)); return true; } /** * Single record read. The id set in the request is passed to the model and * then the item layout is used to render the result. * * @return bool */ public function read() { // Load the model $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } // Set the layout to item, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'item'; } // Do I have a form? $model->setState('form_name', 'form.' . $this->layout); $item = $model->getItem(); if (!($item instanceof FOFTable)) { return false; } $itemKey = $item->getKeyName(); if ($item->$itemKey != $model->getId()) { return false; } $formData = is_object($item) ? $item->getData() : array(); $form = $model->getForm($formData); if ($form !== false) { $this->hasForm = true; } // Display $this->display(in_array('read', $this->cacheableTasks)); return true; } /** * Single record add. The form layout is used to present a blank page. * * @return false|void */ public function add() { // Load and reset the model $model = $this->getThisModel(); $model->reset(); // Set the layout to form, if it's not set in the URL if (!$this->layout) { $this->layout = 'form'; } // Do I have a form? $model->setState('form_name', 'form.' . $this->layout); $item = $model->getItem(); if (!($item instanceof FOFTable)) { return false; } $formData = is_object($item) ? $item->getData() : array(); $form = $model->getForm($formData); if ($form !== false) { $this->hasForm = true; } // Display $this->display(in_array('add', $this->cacheableTasks)); } /** * Single record edit. The ID set in the request is passed to the model, * then the form layout is used to edit the result. * * @return bool */ public function edit() { // Load the model $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->checkout(); if (!$status) { // Redirect on error if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $model->getError(), 'error'); return false; } // Set the layout to form, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'form'; } // Do I have a form? $model->setState('form_name', 'form.' . $this->layout); $item = $model->getItem(); if (!($item instanceof FOFTable)) { return false; } $itemKey = $item->getKeyName(); if ($item->$itemKey != $model->getId()) { return false; } $formData = is_object($item) ? $item->getData() : array(); $form = $model->getForm($formData); if ($form !== false) { $this->hasForm = true; } // Display $this->display(in_array('edit', $this->cacheableTasks)); return true; } /** * Save the incoming data and then return to the Edit task * * @return bool */ public function apply() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); $result = $this->applySave(); // Redirect to the edit task if ($result) { $id = $this->input->get('id', 0, 'int'); $textkey = strtoupper($this->component) . '_LBL_' . strtoupper($this->view) . '_SAVED'; if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); $this->setRedirect($url, JText::_($textkey)); } return $result; } /** * Duplicates selected items * * @return bool */ public function copy() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->copy(); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); return false; } else { if(!FOFPlatform::getInstance()->isCli()) { FOFPlatform::getInstance()->setHeader('Status', '201 Created', true); } $this->setRedirect($url); return true; } } /** * Save the incoming data and then return to the Browse task * * @return bool */ public function save() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $result = $this->applySave(); // Redirect to the display task if ($result) { $textkey = strtoupper($this->component) . '_LBL_' . strtoupper($this->view) . '_SAVED'; if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, JText::_($textkey)); } return $result; } /** * Save the incoming data and then return to the Add task * * @return bool */ public function savenew() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $result = $this->applySave(); // Redirect to the display task if ($result) { $textkey = strtoupper($this->component) . '_LBL_' . strtoupper($this->view) . '_SAVED'; if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=add' . $this->getItemidURLSuffix(); $this->setRedirect($url, JText::_($textkey)); } return $result; } /** * Cancel the edit, check in the record and return to the Browse task * * @return bool */ public function cancel() { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $model->checkin(); // Remove any saved data JFactory::getSession()->set($model->getHash() . 'savedata', null); // Redirect to the display task if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); return true; } /** * Method to load a row from version history * * @return boolean True if the content history is reverted, false otherwise * * @since 2.2 */ public function loadhistory() { $app = JFactory::getApplication(); $lang = JFactory::getLanguage(); $model = $this->getThisModel(); $table = $model->getTable(); $historyId = $app->input->get('version_id', null, 'integer'); $status = $model->checkout(); $alias = $this->component . '.' . $this->view; if (!$model->loadhistory($historyId, $table, $alias)) { $this->setMessage($model->getError(), 'error'); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); return false; } // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } $recordId = $table->$key; // To avoid data collisions the urlVar may be different from the primary key. $urlVar = empty($this->urlVar) ? $key : $this->urlVar; // Access check. $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.edit', 'core.edit' ); if (!$this->checkACL($privilege)) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED')); $this->setMessage($this->getError(), 'error'); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); $table->checkin(); return false; } $table->store(); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); $this->setMessage(JText::sprintf('JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note'))); return true; } /** * Sets the access to public. Joomla! 1.5 compatibility. * * @return bool * * @deprecated since 2.0 */ public function accesspublic() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setaccess(0); } /** * Sets the access to registered. Joomla! 1.5 compatibility. * * @return bool * * @deprecated since 2.0 */ public function accessregistered() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setaccess(1); } /** * Sets the access to special. Joomla! 1.5 compatibility. * * @return bool * * @deprecated since 2.0 */ public function accessspecial() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setaccess(2); } /** * Publish (set enabled = 1) an item. * * @return bool */ public function publish() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setstate(1); } /** * Unpublish (set enabled = 0) an item. * * @return bool */ public function unpublish() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setstate(0); } /** * Archive (set enabled = 2) an item. * * @return bool */ public function archive() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setstate(2); } /** * Trash (set enabled = -2) an item. * * @return bool */ public function trash() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } return $this->setstate(-2); } /** * Saves the order of the items * * @return bool */ public function saveorder() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $ordering = $model->getTable()->getColumnAlias('ordering'); $ids = $model->getIds(); $orders = $this->input->get('order', array(), 'array'); if ($n = count($ids)) { for ($i = 0; $i < $n; $i++) { $model->setId($ids[$i]); $neworder = (int) $orders[$i]; $item = $model->getItem(); if (!($item instanceof FOFTable)) { return false; } $key = $item->getKeyName(); if ($item->$key == $ids[$i]) { $item->$ordering = $neworder; $model->save($item); } } } $status = $model->reorder(); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); return $status; } /** * Moves selected items one position down the ordering list * * @return bool */ public function orderdown() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->move(1); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); } else { $this->setRedirect($url); } return $status; } /** * Moves selected items one position up the ordering list * * @return bool */ public function orderup() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->move(-1); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); } else { $this->setRedirect($url); } return $status; } /** * Delete selected item(s) * * @return bool */ public function remove() { // CSRF prevention if ($this->csrfProtection) { $this->_csrfProtection(); } $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->delete(); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); } else { $this->setRedirect($url); } return $status; } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. */ public function redirect() { if ($this->redirect) { $app = JFactory::getApplication(); $app->enqueueMessage($this->message, $this->messageType); $app->redirect($this->redirect); return true; } return false; } /** * Returns true if there is a redirect set in the controller * * @return boolean */ public function hasRedirect() { return !empty($this->redirect); } /** * Register the default task to perform if a mapping is not found. * * @param string $method The name of the method in the derived class to perform if a named task is not found. * * @return FOFController A FOFController object to support chaining. */ public function registerDefaultTask($method) { $this->registerTask('__default', $method); return $this; } /** * Register (map) a task to a method in the class. * * @param string $task The task. * @param string $method The name of the method in the derived class to perform for this task. * * @return FOFController A FOFController object to support chaining. */ public function registerTask($task, $method) { if (in_array(strtolower($method), $this->methods)) { $this->taskMap[strtolower($task)] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return FOFController This object to support chaining. */ public function unregisterTask($task) { unset($this->taskMap[strtolower($task)]); return $this; } /** * Sets the internal message that is passed with a redirect * * @param string $text Message to display on redirect. * @param string $type Message type. Optional, defaults to 'message'. * * @return string Previous message */ public function setMessage($text, $type = 'message') { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Sets an entire array of search paths for resources. * * @param string $type The type of path to set, typically 'view' or 'model'. * @param string $path The new set of search paths. If null or false, resets to the current directory only. * * @return void */ protected function setPath($type, $path) { // Clear out the prior search dirs $this->paths[$type] = array(); // Actually add the user-specified directories $this->addPath($type, $path); } /** * Registers a redirection with an optional message. The redirection is * carried out when you use the redirect method. * * @param string $url The URL to redirect to * @param string $msg The message to be pushed to the application * @param string $type The message type to be pushed to the application, e.g. 'error' * * @return FOFController This object to support chaining */ public function setRedirect($url, $msg = null, $type = null) { // Do the logic only if we're parsing a raw url (index.php?foo=bar&etc=etc) if (strpos($url, 'index.php') === 0) { $isAdmin = FOFPlatform::getInstance()->isBackend(); $auto = false; if (($this->autoRouting == 2 || $this->autoRouting == 3) && $isAdmin) { $auto = true; } elseif (($this->autoRouting == 1 || $this->autoRouting == 3) && !$isAdmin) { $auto = true; } if ($auto) { $url = JRoute::_($url, false); } } $this->redirect = $url; if ($msg !== null) { // Controller may have set this directly $this->message = $msg; } // Ensure the type is not overwritten by a previous call to setMessage. if (empty($type)) { if (empty($this->messageType)) { $this->messageType = 'message'; } } // If the type is explicitly set, set it. else { $this->messageType = $type; } return $this; } /** * Sets the published state (the enabled field) of the selected item(s) * * @param integer $state The desired state. 0 is unpublished, 1 is published. * * @return bool */ protected function setstate($state = 0) { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $status = $model->publish($state); // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); } else { $this->setRedirect($url); } return $status; } /** * Sets the access level of the selected item(s). * * @param integer $level The desired viewing access level ID * * @return bool */ protected function setaccess($level = 0) { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $id = $model->getId(); $item = $model->getItem(); if (!($item instanceof FOFTable)) { return false; } $accessField = $item->getColumnAlias('access'); $key = $item->getKeyName(); $loadedid = $item->$key; if ($id == $loadedid) { $item->$accessField = $level; $status = $model->save($item); } else { $status = false; } // Redirect if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $model->getError(), 'error'); } else { $this->setRedirect($url); } return $status; } /** * Common method to handle apply and save tasks * * @return boolean Returns true on success */ private function applySave() { // Load the model $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $id = $model->getId(); $data = $this->input->getData(); if (!$this->onBeforeApplySave($data)) { return false; } // Set the layout to form, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'form'; } // Do I have a form? $model->setState('form_name', 'form.' . $this->layout); $status = $model->save($data); if ($status && ($id != 0)) { FOFPlatform::getInstance()->setHeader('Status', '201 Created', true); // Try to check-in the record if it's not a new one $status = $model->checkin(); } if ($status) { $status = $this->onAfterApplySave(); } $this->input->set('id', $model->getId()); if (!$status) { // Redirect on error $id = $model->getId(); if ($customURL = $this->input->get('returnurl', '', 'string')) { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $url = $customURL; } elseif ($id != 0) { $url = 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); } else { $url = 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=add' . $this->getItemidURLSuffix(); } $this->setRedirect($url, '<li>' . implode('</li><li>', $model->getErrors()) . '</li>', 'error'); return false; } else { $session = JFactory::getSession(); $session->set($model->getHash() . 'savedata', null); return true; } } /** * Returns the default model associated with the current view * * @param array $config Configuration variables for the model * * @return FOFModel The global instance of the model (singleton) */ final public function getThisModel($config = array()) { if (!is_object($this->_modelObject)) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } if (!empty($this->modelName)) { $parts = FOFInflector::explode($this->modelName); $modelName = ucfirst(array_pop($parts)); $prefix = FOFInflector::implode($parts); } else { $prefix = ucfirst($this->bareComponent) . 'Model'; $modelName = ucfirst(FOFInflector::pluralize($this->view)); } if (!array_key_exists('input', $config) || !($config['input'] instanceof FOFInput)) { $config['input'] = $this->input; } $this->_modelObject = $this->getModel($modelName, $prefix, $config); } return $this->_modelObject; } /** * Method to get a model object, loading it if required. * * @param string $name The model name. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for model. Optional. * * @return object The model. */ public function getModel($name = '', $prefix = '', $config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config) || empty($config)) { // array_merge is required to create a copy instead of assigning by reference $config = array_merge($this->config); } if (empty($name)) { $name = $this->getName(); } if (empty($prefix)) { $prefix = $this->model_prefix; } if ($model = $this->createModel($name, $prefix, $config)) { // Task is a reserved state $model->setState('task', $this->task); // Let's get the application object and set menu information if it's available if (!FOFPlatform::getInstance()->isCli()) { $app = JFactory::getApplication(); $menu = $app->getMenu(); if (is_object($menu)) { if ($item = $menu->getActive()) { $params = $menu->getParams($item->id); // Set default state data $model->setState('parameters.menu', $params); } } } } return $model; } /** * Returns current view object * * @param array $config Configuration variables for the model * * @return FOFView The global instance of the view object (singleton) */ final public function getThisView($config = array()) { if (!is_object($this->_viewObject)) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config) || empty($config)) { // array_merge is required to create a copy instead of assigning by reference $config = array_merge($this->config); } $prefix = null; $viewName = null; $viewType = null; if (!empty($this->viewName)) { $parts = FOFInflector::explode($this->viewName); $viewName = ucfirst(array_pop($parts)); $prefix = FOFInflector::implode($parts); } else { $prefix = ucfirst($this->bareComponent) . 'View'; $viewName = ucfirst($this->view); } $document = FOFPlatform::getInstance()->getDocument(); if ($document instanceof JDocument) { $viewType = $document->getType(); } else { $viewType = $this->input->getCmd('format', 'html'); } if (($viewType == 'html') && $this->hasForm) { $viewType = 'form'; } if (!array_key_exists('input', $config) || !($config['input'] instanceof FOFInput)) { $config['input'] = $this->input; } $config['input']->set('base_path', $this->basePath); $this->_viewObject = $this->getView($viewName, $viewType, $prefix, $config); } return $this->_viewObject; } /** * Method to get the controller name * * The dispatcher name is set by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @throws Exception * * @return string The name of the dispatcher */ public function getName() { if (empty($this->name)) { if (empty($this->bareComponent)) { $r = null; if (!preg_match('/(.*)Controller/i', get_class($this), $r)) { throw new Exception(JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'), 500); } $this->name = strtolower($r[1]); } else { $this->name = $this->bareComponent; } } return $this->name; } /** * Get the last task that is being performed or was most recently performed. * * @return string The task that is being performed or was most recently performed. */ public function getTask() { return $this->task; } /** * Gets the available tasks in the controller. * * @return array Array[i] of task names. */ public function getTasks() { return $this->methods; } /** * Method to get a reference to the current view and load it if necessary. * * @param string $name The view name. Optional, defaults to the controller name. * @param string $type The view type. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for view. Optional. * * @throws Exception * * @return FOFView Reference to the view or an error. */ public function getView($name = '', $type = '', $prefix = '', $config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } if (empty($name)) { $name = $this->getName(); } if (empty($prefix)) { $prefix = $this->getName() . 'View'; } $signature = md5($name . $type . $prefix . serialize($config)); if (empty($this->viewsCache[$signature])) { if ($view = $this->createView($name, $prefix, $type, $config)) { $this->viewsCache[$signature] = & $view; } else { throw new Exception(JText::sprintf('JLIB_APPLICATION_ERROR_VIEW_NOT_FOUND', $name, $type, $prefix), 500); } } return $this->viewsCache[$signature]; } /** * Creates a new model object * * @param string $name The name of the model class, e.g. Items * @param string $prefix The prefix of the model class, e.g. FoobarModel * @param array $config The configuration parameters for the model class * * @return FOFModel The model object */ protected function createModel($name, $prefix = '', $config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } $result = null; // Clean the model name $modelName = preg_replace('/[^A-Z0-9_]/i', '', $name); $classPrefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); $result = FOFModel::getAnInstance($modelName, $classPrefix, $config); return $result; } /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Configuration array for the model. Optional. * * @return mixed Model object on success; otherwise null */ protected function &_createModel($name, $prefix = '', $config = array()) { FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .__METHOD__ . ' is deprecated. Use createModel() instead.'); return $this->createModel($name, $prefix, $config); } /** * Creates a View object instance and returns it * * @param string $name The name of the view, e.g. Items * @param string $prefix The prefix of the view, e.g. FoobarView * @param string $type The type of the view, usually one of Html, Raw, Json or Csv * @param array $config The configuration variables to use for creating the view * * @return FOFView */ protected function createView($name, $prefix = '', $type = '', $config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } $result = null; // Clean the view name $viewName = preg_replace('/[^A-Z0-9_]/i', '', $name); $classPrefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); $viewType = preg_replace('/[^A-Z0-9_]/i', '', $type); if (!isset($config['input'])) { $config['input'] = $this->input; } if (($config['input'] instanceof FOFInput)) { $tmpInput = $config['input']; } else { $tmpInput = new FOFInput($config['input']); } // Guess the component name and view if (!empty($prefix)) { preg_match('/(.*)View$/', $prefix, $m); $component = 'com_' . strtolower($m[1]); } else { $component = ''; } if (empty($component) && array_key_exists('input', $config)) { $component = $tmpInput->get('option', $component, 'cmd'); } if (array_key_exists('option', $config)) { if ($config['option']) { $component = $config['option']; } } $config['option'] = $component; $view = strtolower($viewName); if (empty($view) && array_key_exists('input', $config)) { $view = $tmpInput->get('view', $view, 'cmd'); } if (array_key_exists('view', $config)) { if ($config['view']) { $view = $config['view']; } } $config['view'] = $view; if (array_key_exists('input', $config)) { $tmpInput->set('option', $config['option']); $tmpInput->set('view', $config['view']); $config['input'] = $tmpInput; } // Get the component directories $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($config['option']); // Get the base paths where the view class files are expected to live $basePaths = array( $componentPaths['main'], $componentPaths['alt'] ); $basePaths = array_merge($this->paths['view']); // Get the alternate (singular/plural) view name $altViewName = FOFInflector::isPlural($viewName) ? FOFInflector::singularize($viewName) : FOFInflector::pluralize($viewName); $suffixes = array( $viewName, $altViewName, 'default' ); $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem'); foreach ($suffixes as $suffix) { // Build the view class name $viewClass = $classPrefix . ucfirst($suffix); if (class_exists($viewClass)) { // The class is already loaded break; } // The class is not loaded. Let's load it! $viewPath = $this->createFileName('view', array('name' => $suffix, 'type' => $viewType)); $path = $filesystem->pathFind($basePaths, $viewPath); if ($path) { require_once $path; } if (class_exists($viewClass)) { // The class was loaded successfully break; } } if (!class_exists($viewClass)) { $viewClass = 'FOFView' . ucfirst($type); } $templateOverridePath = FOFPlatform::getInstance()->getTemplateOverridePath($config['option']); // Setup View configuration options if (!array_key_exists('template_path', $config)) { $config['template_path'][] = $componentPaths['main'] . '/views/' . FOFInflector::pluralize($config['view']) . '/tmpl'; if ($templateOverridePath) { $config['template_path'][] = $templateOverridePath . '/' . FOFInflector::pluralize($config['view']); } $config['template_path'][] = $componentPaths['main'] . '/views/' . FOFInflector::singularize($config['view']) . '/tmpl'; if ($templateOverridePath) { $config['template_path'][] = $templateOverridePath . '/' . FOFInflector::singularize($config['view']); } $config['template_path'][] = $componentPaths['main'] . '/views/' . $config['view'] . '/tmpl'; if ($templateOverridePath) { $config['template_path'][] = $templateOverridePath . '/' . $config['view']; } } $extraTemplatePath = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.template_path', null); if ($extraTemplatePath) { array_unshift($config['template_path'], $componentPaths['main'] . '/' . $extraTemplatePath); } if (!array_key_exists('helper_path', $config)) { $config['helper_path'] = array( $componentPaths['main'] . '/helpers', $componentPaths['admin'] . '/helpers' ); } $extraHelperPath = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.helper_path', null); if ($extraHelperPath) { $config['helper_path'][] = $componentPaths['main'] . '/' . $extraHelperPath; } // Set up the page title $setFrontendPageTitle = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.setFrontendPageTitle', null); if ($setFrontendPageTitle) { $setFrontendPageTitle = strtolower($setFrontendPageTitle); $config['setFrontendPageTitle'][] = in_array($setFrontendPageTitle, array('1', 'yes', 'true', 'on')); } $defaultPageTitle = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.defaultPageTitle', null); if ($defaultPageTitle) { $config['defaultPageTitle'][] = in_array($defaultPageTitle, array('1', 'yes', 'true', 'on')); } // Set the use_hypermedia flag in $config if it's not already set if (!isset($config['use_hypermedia'])) { $config['use_hypermedia'] = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.use_hypermedia', false); } // Set also the linkbar_style if (!isset($config['linkbar_style'])) { $style = $this->configProvider->get($config['option'] . '.views.' . $config['view'] . '.config.linkbar_style', false); if ($style) { $config['linkbar_style'] = $style; } } /** * Some administrative templates force format=utf (yeah, I know, what the heck, right?) when a format * URL parameter does not exist in the URL. Of course there is no such thing as FOFViewUtf (why the heck would * it be, there is no such thing as a format=utf in Joomla! for crying out loud) which causes a Fatal Error. So * we have to detect that and force $type='html'... */ if (!class_exists($viewClass) && ($type != 'html')) { $type = 'html'; $result = $this->createView($name, $prefix, $type, $config); } else { $result = new $viewClass($config); } return $result; } /** * Deprecated function to create a View object instance * * @param string $name The name of the view, e.g. 'Items' * @param string $prefix The prefix of the view, e.g. 'FoobarView' * @param string $type The view type, e.g. 'html' * @param array $config The configuration array for the view * * @return FOFView * * @see FOFController::createView * * @deprecated since version 2.0 */ protected function &_createView($name, $prefix = '', $type = '', $config = array()) { FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' . __METHOD__ . ' is deprecated. Use createView() instead.'); return $this->createView($name, $prefix, $type, $config); } /** * Set the name of the view to be used by this Controller * * @param string $viewName The name of the view * * @return void */ public function setThisViewName($viewName) { $this->viewName = $viewName; } /** * Set the name of the model to be used by this Controller * * @param string $modelName The name of the model * * @return void */ public function setThisModelName($modelName) { $this->modelName = $modelName; } /** * Checks if the current user has enough privileges for the requested ACL * area. * * @param string $area The ACL area, e.g. core.manage. * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { if (in_array(strtolower($area), array('false','0','no','403'))) { return false; } if (in_array(strtolower($area), array('true','1','yes'))) { return true; } elseif (empty($area)) { return true; } else { // Check if we're dealing with ids $ids = null; // First, check if there is an asset for this record $table = $this->getThisModel()->getTable(); if ($table && $table->isAssetsTracked()) { $ids = $this->getThisModel()->getId() ? $this->getThisModel()->getId() : null; } // Generic or Asset tracking if (empty($ids)) { return FOFPlatform::getInstance()->authorise($area, $this->component); } else { if (!is_array($ids)) { $ids = array($ids); } $resource = FOFInflector::singularize($this->view); $isEditState = ($area == 'core.edit.state'); foreach ($ids as $id) { $asset = $this->component . '.' . $resource . '.' . $id; // Dedicated permission found, check it! if (FOFPlatform::getInstance()->authorise($area, $asset) ) { return true; } // Fallback on edit.own, if not edit.state. First test if the permission is available. if ((!$isEditState) && (FOFPlatform::getInstance()->authorise('core.edit.own', $asset))) { $table = $this->getThisModel()->getTable(); $table->load($id); $created_by = $table->getColumnAlias('created_by'); if ($table && isset($table->$created_by)) { // Now test the owner is the user. $owner_id = (int) $table->$created_by; // If the owner matches 'me' then do the test. if ($owner_id == FOFPlatform::getInstance()->getUser()->id) { return true; } else { return false; } } else { return false; } } } } } return false; } /** * A catch-all method for all tasks without a corresponding onBefore * method. Applies the ACL preferences defined in fof.xml. * * @param string $task The task being executed * * @return boolean True to allow execution of the task */ protected function onBeforeGenericTask($task) { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.' . $task, '' ); return $this->checkACL($privilege); } /** * Execute something before applySave is called. Return false to prevent * applySave from executing. * * @param array &$data The data upon which applySave will act * * @return boolean True to allow applySave to run */ protected function onBeforeApplySave(&$data) { return true; } /** * Execute something after applySave has run. * * @return boolean True to allow normal return, false to cause a 403 error */ protected function onAfterApplySave() { return true; } /** * ACL check before changing the access level; override to customise * * @return boolean True to allow accesspublic() to run */ protected function onBeforeAccesspublic() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.accesspublic', 'core.edit.state'); return $this->checkACL($privilege); } /** * ACL check before changing the access level; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeAccessregistered() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.accessregistered', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before changing the access level; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeAccessspecial() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.accessspecial', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before adding a new record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeAdd() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.add', 'core.create' ); return $this->checkACL($privilege); } /** * ACL check before saving a new/modified record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeApply() { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $id = $model->getId(); if(!$id) { $defaultPrivilege = 'core.create'; } else { $defaultPrivilege = 'core.edit'; } $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.apply', $defaultPrivilege ); return $this->checkACL($privilege); } /** * ACL check before allowing someone to browse * * @return boolean True to allow the method to run */ protected function onBeforeBrowse() { $defaultPrivilege = ''; $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.browse', $defaultPrivilege ); return $this->checkACL($privilege); } /** * ACL check before cancelling an edit * * @return boolean True to allow the method to run */ protected function onBeforeCancel() { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $id = $model->getId(); if(!$id) { $defaultPrivilege = 'core.create'; } else { $defaultPrivilege = 'core.edit'; } $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.cancel', $defaultPrivilege ); return $this->checkACL($privilege); } /** * ACL check before editing a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeEdit() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.edit', 'core.edit' ); return $this->checkACL($privilege); } /** * ACL check before changing the ordering of a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeOrderdown() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.orderdown', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before changing the ordering of a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeOrderup() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.orderup', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before changing the publish status of a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforePublish() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.publish', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before removing a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeRemove() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.remove', 'core.delete' ); return $this->checkACL($privilege); } /** * ACL check before saving a new/modified record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeSave() { $model = $this->getThisModel(); if (!$model->getId()) { $model->setIDsFromRequest(); } $id = $model->getId(); if(!$id) { $defaultPrivilege = 'core.create'; } else { $defaultPrivilege = 'core.edit'; } $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.save', $defaultPrivilege ); return $this->checkACL($privilege); } /** * ACL check before saving a new/modified record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeSavenew() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.savenew', 'core.create' ); return $this->checkACL($privilege); } /** * ACL check before changing the ordering of a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeSaveorder() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.saveorder', 'core.edit.state' ); return $this->checkACL($privilege); } /** * ACL check before changing the publish status of a record; override to customise * * @return boolean True to allow the method to run */ protected function onBeforeUnpublish() { $privilege = $this->configProvider->get( $this->component . '.views.' . FOFInflector::singularize($this->view) . '.acl.unpublish', 'core.edit.state' ); return $this->checkACL($privilege); } /** * Gets a URL suffix with the Itemid parameter. If it's not the front-end of the site, or if * there is no Itemid set it returns an empty string. * * @return string The &Itemid=123 URL suffix, or an empty string if Itemid is not applicable */ public function getItemidURLSuffix() { if (FOFPlatform::getInstance()->isFrontend() && ($this->input->getCmd('Itemid', 0) != 0)) { return '&Itemid=' . $this->input->getInt('Itemid', 0); } else { return ''; } } /** * Applies CSRF protection by means of a standard Joomla! token (nonce) check. * Raises a 403 Access Forbidden error through the platform if the check fails. * * TODO Move this check inside the platform * * @return boolean True if the CSRF check is successful * * @throws Exception */ protected function _csrfProtection() { static $isCli = null, $isAdmin = null; if (is_null($isCli)) { $isCli = FOFPlatform::getInstance()->isCli(); $isAdmin = FOFPlatform::getInstance()->isBackend(); } switch ($this->csrfProtection) { // Never case 0: return true; break; // Always case 1: break; // Only back-end and HTML format case 2: if ($isCli) { return true; } elseif (!$isAdmin && ($this->input->get('format', 'html', 'cmd') != 'html')) { return true; } break; // Only back-end case 3: if (!$isAdmin) { return true; } break; } $hasToken = false; $session = JFactory::getSession(); // Joomla! 1.5/1.6/1.7/2.5 (classic Joomla! API) method if (method_exists('JUtility', 'getToken')) { $token = JUtility::getToken(); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } } // Joomla! 2.5+ (Platform 12.1+) method if (!$hasToken) { if (method_exists($session, 'getToken')) { $token = $session->getToken(); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } } } // Joomla! 2.5+ formToken method if (!$hasToken) { if (method_exists($session, 'getFormToken')) { $token = $session->getFormToken(); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } } } if (!$hasToken) { FOFPlatform::getInstance()->raiseError(403, JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN')); return false; } } } PK ! c�� � database/database.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Database connector class. * * @since 11.1 * @deprecated 13.3 (Platform) & 4.0 (CMS) */ abstract class FOFDatabase { /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 11.1 * @throws RuntimeException * @deprecated 13.1 (Platform) & 4.0 (CMS) */ public function query() { if (class_exists('JLog')) { JLog::add('FOFDatabase::query() is deprecated, use FOFDatabaseDriver::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 11.1 * @deprecated 13.1 (Platform) & 4.0 (CMS) */ public static function getConnectors() { if (class_exists('JLog')) { JLog::add('FOFDatabase::getConnectors() is deprecated, use FOFDatabaseDriver::getConnectors() instead.', JLog::WARNING, 'deprecated'); } return FOFDatabaseDriver::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 13.3 (Platform) & 4.0 (CMS) * @since 11.1 */ public function getErrorMsg($escaped = false) { if (class_exists('JLog')) { JLog::add('FOFDatabase::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 11.1 * @deprecated 13.3 (Platform) & 4.0 (CMS) */ public function getErrorNum() { if (class_exists('JLog')) { JLog::add('FOFDatabase::getErrorNum() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); } return $this->errorNum; } /** * Method to return a FOFDatabaseDriver 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 FOFDatabaseDriver 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 FOFDatabaseDriver A database object. * * @since 11.1 * @deprecated 13.1 (Platform) & 4.0 (CMS) */ public static function getInstance($options = array()) { if (class_exists('JLog')) { JLog::add('FOFDatabase::getInstance() is deprecated, use FOFDatabaseDriver::getInstance() instead.', JLog::WARNING, 'deprecated'); } return FOFDatabaseDriver::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 11.1 * @deprecated 13.1 (Platform) & 4.0 (CMS) */ public static function splitSql($query) { if (class_exists('JLog')) { JLog::add('FOFDatabase::splitSql() is deprecated, use FOFDatabaseDriver::splitSql() instead.', JLog::WARNING, 'deprecated'); } return FOFDatabaseDriver::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 11.1 * @deprecated 13.3 (Platform) & 4.0 (CMS) */ public function stderr($showSQL = false) { if (class_exists('JLog')) { JLog::add('FOFDatabase::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 11.1 * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use FOFDatabaseDriver::isSupported() instead. */ public static function test() { if (class_exists('JLog')) { JLog::add('FOFDatabase::test() is deprecated. Use FOFDatabaseDriver::isSupported() instead.', JLog::WARNING, 'deprecated'); } return static::isSupported(); } } PK ! �Q= = database/driver/joomla.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * This crazy three line bit is required to convince Joomla! to load JDatabaseInterface which is on the same file as the * abstract JDatabaseDriver class for reasons that beat me. It makes no sense. Furthermore, jimport on Joomla! 3.4 * doesn't seem to actually load the file, merely registering the association in the autoloader. Hence the class_exists * in here. */ jimport('joomla.database.driver'); jimport('joomla.database.driver.mysqli'); class_exists('JDatabaseDriver', true); /** * Joomla! pass-through database driver. */ class FOFDatabaseDriverJoomla extends FOFDatabase implements FOFDatabaseInterface { /** @var FOFDatabase The real database connection object */ private $dbo; /** * @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 11.1 */ protected $nameQuote = ''; /** * Is this driver supported * * @since 11.2 */ public static function isSupported() { return true; } /** * Database object constructor * * @param array $options List of options used to configure the connection */ public function __construct($options = array()) { // Get best matching Akeeba Backup driver instance $this->dbo = JFactory::getDbo(); $reflection = new ReflectionClass($this->dbo); try { $refProp = $reflection->getProperty('nameQuote'); $refProp->setAccessible(true); $this->nameQuote = $refProp->getValue($this->dbo); } catch (Exception $e) { $this->nameQuote = '`'; } } public function close() { if (method_exists($this->dbo, 'close')) { $this->dbo->close(); } elseif (method_exists($this->dbo, 'disconnect')) { $this->dbo->disconnect(); } } public function disconnect() { $this->close(); } public function open() { if (method_exists($this->dbo, 'open')) { $this->dbo->open(); } elseif (method_exists($this->dbo, 'connect')) { $this->dbo->connect(); } } public function connect() { $this->open(); } public function connected() { if (method_exists($this->dbo, 'connected')) { return $this->dbo->connected(); } return true; } public function escape($text, $extra = false) { return $this->dbo->escape($text, $extra); } public function execute() { if (method_exists($this->dbo, 'execute')) { return $this->dbo->execute(); } return $this->dbo->query(); } public function getAffectedRows() { if (method_exists($this->dbo, 'getAffectedRows')) { return $this->dbo->getAffectedRows(); } return 0; } public function getCollation() { if (method_exists($this->dbo, 'getCollation')) { return $this->dbo->getCollation(); } return 'utf8_general_ci'; } public function getConnection() { if (method_exists($this->dbo, 'getConnection')) { return $this->dbo->getConnection(); } return null; } public function getCount() { if (method_exists($this->dbo, 'getCount')) { return $this->dbo->getCount(); } return 0; } public function getDateFormat() { if (method_exists($this->dbo, 'getDateFormat')) { return $this->dbo->getDateFormat(); } return 'Y-m-d H:i:s';; } public function getMinimum() { if (method_exists($this->dbo, 'getMinimum')) { return $this->dbo->getMinimum(); } return '5.0.40'; } public function getNullDate() { if (method_exists($this->dbo, 'getNullDate')) { return $this->dbo->getNullDate(); } return '0000-00-00 00:00:00'; } public function getNumRows($cursor = null) { if (method_exists($this->dbo, 'getNumRows')) { return $this->dbo->getNumRows($cursor); } return 0; } public function getQuery($new = false) { if (method_exists($this->dbo, 'getQuery')) { return $this->dbo->getQuery($new); } return null; } public function getTableColumns($table, $typeOnly = true) { if (method_exists($this->dbo, 'getTableColumns')) { return $this->dbo->getTableColumns($table, $typeOnly); } $result = $this->dbo->getTableFields(array($table), $typeOnly); return $result[$table]; } public function getTableKeys($tables) { if (method_exists($this->dbo, 'getTableKeys')) { return $this->dbo->getTableKeys($tables); } return array(); } public function getTableList() { if (method_exists($this->dbo, 'getTableList')) { return $this->dbo->getTableList(); } return array(); } public function getVersion() { if (method_exists($this->dbo, 'getVersion')) { return $this->dbo->getVersion(); } return '5.0.40'; } public function insertid() { if (method_exists($this->dbo, 'insertid')) { return $this->dbo->insertid(); } return null; } public function insertObject($table, &$object, $key = null) { if (method_exists($this->dbo, 'insertObject')) { return $this->dbo->insertObject($table, $object, $key); } return null; } public function loadAssoc() { if (method_exists($this->dbo, 'loadAssoc')) { return $this->dbo->loadAssoc(); } return null; } public function loadAssocList($key = null, $column = null) { if (method_exists($this->dbo, 'loadAssocList')) { return $this->dbo->loadAssocList($key, $column); } return null; } public function loadObject($class = 'stdClass') { if (method_exists($this->dbo, 'loadObject')) { return $this->dbo->loadObject($class); } return null; } public function loadObjectList($key = '', $class = 'stdClass') { if (method_exists($this->dbo, 'loadObjectList')) { return $this->dbo->loadObjectList($key, $class); } return null; } public function loadResult() { if (method_exists($this->dbo, 'loadResult')) { return $this->dbo->loadResult(); } return null; } public function loadRow() { if (method_exists($this->dbo, 'loadRow')) { return $this->dbo->loadRow(); } return null; } public function loadRowList($key = null) { if (method_exists($this->dbo, 'loadRowList')) { return $this->dbo->loadRowList($key); } return null; } public function lockTable($tableName) { if (method_exists($this->dbo, 'lockTable')) { return $this->dbo->lockTable($this); } return $this; } public function quote($text, $escape = true) { if (method_exists($this->dbo, 'quote')) { return $this->dbo->quote($text, $escape); } return $text; } public function select($database) { if (method_exists($this->dbo, 'select')) { return $this->dbo->select($database); } return false; } public function setQuery($query, $offset = 0, $limit = 0) { if (method_exists($this->dbo, 'setQuery')) { return $this->dbo->setQuery($query, $offset, $limit); } return false; } public function transactionCommit($toSavepoint = false) { if (method_exists($this->dbo, 'transactionCommit')) { $this->dbo->transactionCommit($toSavepoint); } } public function transactionRollback($toSavepoint = false) { if (method_exists($this->dbo, 'transactionRollback')) { $this->dbo->transactionRollback($toSavepoint); } } public function transactionStart($asSavepoint = false) { if (method_exists($this->dbo, 'transactionStart')) { $this->dbo->transactionStart($asSavepoint); } } public function unlockTables() { if (method_exists($this->dbo, 'unlockTables')) { return $this->dbo->unlockTables(); } return $this; } public function updateObject($table, &$object, $key, $nulls = false) { if (method_exists($this->dbo, 'updateObject')) { return $this->dbo->updateObject($table, $object, $key, $nulls); } return false; } public function getLog() { if (method_exists($this->dbo, 'getLog')) { return $this->dbo->getLog(); } return array(); } public function dropTable($table, $ifExists = true) { if (method_exists($this->dbo, 'dropTable')) { return $this->dbo->dropTable($table, $ifExists); } return $this; } public function getTableCreate($tables) { if (method_exists($this->dbo, 'getTableCreate')) { return $this->dbo->getTableCreate($tables); } return array(); } public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { if (method_exists($this->dbo, 'renameTable')) { return $this->dbo->renameTable($oldTable, $newTable, $backup, $prefix); } return $this; } public function setUtf() { if (method_exists($this->dbo, 'setUtf')) { return $this->dbo->setUtf(); } return false; } protected function freeResult($cursor = null) { return false; } /** * 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 11.1 * @throws RuntimeException */ public function loadColumn($offset = 0) { if (method_exists($this->dbo, 'loadColumn')) { return $this->dbo->loadColumn($offset); } return $this->dbo->loadResultArray($offset); } /** * 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 11.1 */ 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 11.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 . $part . $q; } else { $parts[] = $q[0] . $part . $q[1]; } } return implode('.', $parts); } /** * 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. * * @since 11.1 */ public function getErrorMsg($escaped = false) { if (method_exists($this->dbo, 'getErrorMsg')) { $errorMessage = $this->dbo->getErrorMsg(); } else { $errorMessage = $this->errorMsg; } if ($escaped) { return addslashes($errorMessage); } return $errorMessage; } /** * Gets the error number from the database connection. * * @return integer The error number for the most recent query. * * @since 11.1 * @deprecated 13.3 (Platform) & 4.0 (CMS) */ public function getErrorNum() { if (method_exists($this->dbo, 'getErrorNum')) { $errorNum = $this->dbo->getErrorNum(); } else { $errorNum = $this->getErrorNum; } return $errorNum; } /** * 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. */ public function stderr($showSQL = false) { if (method_exists($this->dbo, 'stderr')) { return $this->dbo->stderr($showSQL); } return parent::stderr($showSQL); } /** * Magic method to proxy all calls to the loaded database driver object */ public function __call($name, array $arguments) { if (is_null($this->dbo)) { throw new Exception('FOF database driver is not loaded'); } if (method_exists($this->dbo, $name) || in_array($name, array('q', 'nq', 'qn', 'query'))) { switch ($name) { case 'execute': $name = 'query'; break; case 'q': $name = 'quote'; break; case 'qn': case 'nq': switch (count($arguments)) { case 0 : $result = $this->quoteName(); break; case 1 : $result = $this->quoteName($arguments[0]); break; case 2: default: $result = $this->quoteName($arguments[0], $arguments[1]); break; } return $result; break; } switch (count($arguments)) { case 0 : $result = $this->dbo->$name(); break; case 1 : $result = $this->dbo->$name($arguments[0]); break; case 2: $result = $this->dbo->$name($arguments[0], $arguments[1]); break; case 3: $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2]); break; case 4: $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: // Resort to using call_user_func_array for many segments $result = call_user_func_array(array($this->dbo, $name), $arguments); } if (class_exists('JDatabase') && is_object($result) && ($result instanceof JDatabase)) { return $this; } return $result; } else { throw new \Exception('Method ' . $name . ' not found in FOFDatabase'); } } public function __get($name) { if (isset($this->dbo->$name) || property_exists($this->dbo, $name)) { return $this->dbo->$name; } else { $this->dbo->$name = null; user_error('Database driver does not support property ' . $name); } } public function __set($name, $value) { if (isset($this->dbo->name) || property_exists($this->dbo, $name)) { $this->dbo->$name = $value; } else { $this->dbo->$name = null; user_error('Database driver not support property ' . $name); } } } PK ! qri N8 N8 database/driver/mysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQL database driver * * @see http://dev.mysql.com/doc/ * @since 12.1 * @deprecated Will be removed when the minimum supported PHP version no longer includes the deprecated PHP `mysql` extension */ class FOFDatabaseDriverMysql extends FOFDatabaseDriverMysqli { /** * The name of the database driver. * * @var string * @since 12.1 */ public $name = 'mysql'; /** * Constructor. * * @param array $options Array of database options with keys: host, user, password, database, select. * * @since 12.1 */ 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 RuntimeException( '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'] : 'root'; $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); } /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->disconnect(); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @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 RuntimeException('Could not connect to MySQL.'); } // Attempt to connect to the server. if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true))) { throw new RuntimeException('Could not connect to MySQL.'); } // 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(); // Turn MySQL profiling ON in debug mode: if ($this->debug && $this->hasProfiling()) { mysql_query("SET profiling = 1;", $this->connection); } } /** * Disconnects the database. * * @return void * * @since 12.1 */ 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 12.1 */ public function escape($text, $extra = false) { $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 12.1 */ public static function isSupported() { return (function_exists('mysql_connect')); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 12.1 */ 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 12.1 */ 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 a 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 * @throws RuntimeException */ public function execute() { $this->connect(); if (!is_resource($this->connection)) { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } // 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 FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } // 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; if (class_exists('JLog')) { 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($query); // 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($query); // Throw the normal query exception. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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 12.1 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysql_select_db($database, $this->connection)) { throw new RuntimeException('Could not connect to database'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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(); 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 * * @param string $query The SQL Query that fails * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage($query) { $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); $query = str_replace($this->tablePrefix, '#__', $query); } return $errorMessage . ' SQL=' . $query; } } PK ! �]�&Da Da database/driver/mysqli.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQLi database driver * * @see http://php.net/manual/en/book.mysqli.php * @since 12.1 */ class FOFDatabaseDriverMysqli extends FOFDatabaseDriver { /** * The name of the database driver. * * @var string * @since 12.1 */ 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 11.1 */ 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 12.2 */ 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 12.2 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var string The minimum supported database version. * @since 12.2 */ protected static $dbMinimum = '5.0.4'; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 12.1 */ 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'] : 'root'; $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'] = null; $options['socket'] = null; // Finalize initialisation. parent::__construct($options); } /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->disconnect(); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @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 ot 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 domain.com or localhost) with ot 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 (!function_exists('mysqli_connect')) { throw new RuntimeException('The MySQL adapter mysqli is not available'); } $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 RuntimeException('Could not connect to MySQL.'); } // 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(); // Turn MySQL profiling ON in debug mode: if ($this->debug && $this->hasProfiling()) { mysqli_query($this->connection, "SET profiling_history_size = 100;"); mysqli_query($this->connection, "SET profiling = 1;"); } } /** * Disconnects the database. * * @return void * * @since 12.1 */ public function disconnect() { // Close the connection. if ($this->connection) { 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 12.1 */ public function escape($text, $extra = false) { $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 12.1 */ 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 12.1 */ 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 FOFDatabaseDriverMysqli Returns this object to support chaining. * * @since 12.2 * @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 12.1 */ 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 12.2 * @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 a 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 12.1 */ 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 12.1 * @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 12.2 * @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 12.2 * @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 12.2 * @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 12.1 */ 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 12.1 */ 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 FOFDatabaseDriverMysqli Returns this object to support chaining. * * @since 12.2 * @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 12.1 * @throws RuntimeException */ public function execute() { $this->connect(); if (!is_object($this->connection)) { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } // 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 FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } // 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; if (class_exists('JLog')) { 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($query); // 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($query); if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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 FOFDatabaseDriverMysqli Returns this object to support chaining. * * @since 12.2 * @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 12.1 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysqli_select_db($this->connection, $database)) { throw new RuntimeException('Could not connect to database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 12.1 */ 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 12.2 * @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 12.2 * @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 12.2 * @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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 FOFDatabaseDriverMysqli Returns this object to support chaining. * * @since 12.1 * @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(); 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 * * @param string $query The SQL Query that fails * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage($query) { $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); $query = str_replace($this->tablePrefix, '#__', $query); } return $errorMessage . ' SQL=' . $query; } } PK ! 2� 4y; y; database/driver/oracle.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Oracle database driver * * @see http://php.net/pdo * @since 12.1 */ class FOFDatabaseDriverOracle extends FOFDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 12.1 */ 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 12.1 */ protected $nameQuote = '"'; /** * Returns the current dateformat * * @var string * @since 12.1 */ protected $dateformat; /** * Returns the current character set * * @var string * @since 12.1 */ protected $charset; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 12.1 */ 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); } /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->freeResult(); unset($this->connection); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @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 12.1 */ public function disconnect() { // Close the connection. $this->freeResult(); unset($this->connection); } /** * 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 FOFDatabaseDriverOracle Returns this object to support chaining. * * @since 12.1 */ 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 12.1 */ 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 12.2 */ 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 12.1 */ 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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 * @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 12.1 */ 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 12.1 */ public function setUtf() { return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return FOFDatabaseDriverOracle Returns this object to support chaining. * * @since 12.1 * @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 FOFDatabaseDriverOracle Returns this object to support chaining. * * @since 12.1 * @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 FOFDatabaseDriverOracle Returns this object to support chaining. * * @since 12.1 * @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 12.1 */ 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 11.1 */ 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 12.3 * @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 12.3 * @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 12.3 * @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++; } } } PK ! ��<`�i �i database/driver/pdo.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Joomla Platform PDO Database Driver Class * * @see http://php.net/pdo * @since 12.1 */ abstract class FOFDatabaseDriverPdo extends FOFDatabaseDriver { /** * The name of the database driver. * * @var string * @since 12.1 */ public $name = 'pdo'; /** * @var PDO The database connection resource. * @since 12.1 */ 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 12.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 12.1 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var resource The prepared statement. * @since 12.1 */ protected $prepared; /** * Contains the current query execution status * * @var array * @since 12.1 */ protected $executed = false; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 12.1 */ 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); } /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->disconnect(); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @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 RuntimeException('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 RuntimeException('Could not connect to PDO: ' . $e->getMessage(), 2, $e); } } /** * Disconnects the database. * * @return void * * @since 12.1 */ public function disconnect() { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } $this->freeResult(); unset($this->connection); } /** * 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 12.1 */ public function escape($text, $extra = false) { if (is_int($text) || is_float($text)) { return $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 12.1 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); if (!is_object($this->connection)) { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } // 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 FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { // @TODO $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } // 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; if (class_exists('JLog')) { 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 FOFDatabaseQueryPreparable) { $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($query); // 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($query); // Throw the normal query exception. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } } return $this->prepared; } /** * Retrieve a PDO database connection attribute * http://www.php.net/manual/en/pdo.getattribute.php * * Usage: $db->getOption(PDO::ATTR_CASE); * * @param mixed $key One of the PDO::ATTR_* Constants * * @return mixed * * @since 12.1 */ 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 12.2 */ public function getConnectedQuery() { return 'SELECT 1'; } /** * Sets an attribute on the PDO database handle. * http://www.php.net/manual/en/pdo.setattribute.php * * 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 * * @since 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 * @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 FOFDatabaseQuery 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 FOFDatabaseDriver This object to support method chaining. * * @since 12.1 */ 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 FOFDatabaseQueryLimitable && !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 FOFDatabaseQuery 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 12.1 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { if (class_exists('JLog')) { JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::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 12.1 * @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 12.1 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextRow() { if (class_exists('JLog')) { JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::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 12.3 */ 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)) { array_push($serializedProperties, $property->name); } } return $serializedProperties; } /** * Wake up after serialization * * @return array * * @since 12.3 */ 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 * * @param string $query The SQL Query that fails * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage($query) { // Note we ignoring $query here as it not used in the original code. // 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 'SQL: ' . $errorInfo; } } PK ! 1��:�4 �4 database/driver/pdomysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQL database driver supporting PDO based connections * * @package Joomla.Platform * @subpackage Database * @see http://php.net/manual/en/ref.pdo-mysql.php * @since 3.4 */ class FOFDatabaseDriverPdomysql extends FOFDatabaseDriverPdo { /** * 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) { /** * 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. Between this chicken and egg issue we _assume_ it's supported and we'll just * catch any problems at connection time. */ $this->utf8mb4 = true; // Get some basic values from the options. $options['driver'] = 'mysql'; $options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'utf8'; if ($this->utf8mb4 && ($options['charset'] == 'utf8')) { $options['charset'] = 'utf8mb4'; } $this->charset = $options['charset']; // 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() { try { // Try to connect to MySQL parent::connect(); } catch (\RuntimeException $e) { // If the connection failed but not because of the wrong character set bubble up the exception if (!$this->utf8mb4 || ($this->options['charset'] != 'utf8mb4')) { throw $e; } /** * If the connection failed and I was trying to use the utf8mb4 charset then it is likely that 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! */ $this->utf8mb4 = false; $this->options['charset'] = 'utf8'; parent::connect(); } $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); } /** * 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 FOFDatabaseDriverPdomysql 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; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.4 */ public function getVersion() { $this->connect(); return $this->getOption(PDO::ATTR_SERVER_VERSION); } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return FOFDatabaseDriverPdomysql 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 FOFDatabaseDriverPdomysql 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) { $this->connect(); if (is_int($text) || is_float($text)) { return $text; } $result = substr($this->connection->quote($text), 1, -1); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Unlocks tables in the database. * * @return FOFDatabaseDriverPdomysql 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++; } } } } PK ! ��nSǔ ǔ database/driver/postgresql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * PostgreSQL database driver * * @since 12.1 */ class FOFDatabaseDriverPostgresql extends FOFDatabaseDriver { /** * The database driver name * * @var string * @since 12.1 */ 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 12.1 */ protected $nameQuote = '"'; /** * The null/zero date string * * @var string * @since 12.1 */ protected $nullDate = '1970-01-01 00:00:00'; /** * The minimum supported database version. * * @var string * @since 12.1 */ protected static $dbMinimum = '8.3.18'; /** * Operator used for concatenation * * @var string * @since 12.1 */ protected $concat_operator = '||'; /** * FOFDatabaseDriverPostgresqlQuery object returned by getQuery * * @var FOFDatabaseDriverPostgresqlQuery * @since 12.1 */ protected $queryObject = null; /** * Database object constructor * * @param array $options List of options used to configure the connection * * @since 12.1 */ 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'] : ''; // Finalize initialization parent::__construct($options); } /** * Database object destructor * * @since 12.1 */ public function __destruct() { $this->disconnect(); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the postgresql extension for PHP is installed and enabled. if (!function_exists('pg_connect')) { throw new RuntimeException('PHP extension pg_connect is not available.'); } // Build the DSN for the connection. $dsn = ''; if (!empty($this->options['host'])) { $dsn .= "host={$this->options['host']} "; } $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 RuntimeException('Error connecting to PGSQL database.'); } pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT); pg_query('SET standard_conforming_strings=off'); pg_query('SET escape_string_warning=off'); } /** * Disconnects the database. * * @return void * * @since 12.1 */ 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 12.1 */ public function escape($text, $extra = false) { $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 12.1 */ public static function test() { return (function_exists('pg_connect')); } /** * Determines if the connection to the server is active. * * @return boolean * * @since 12.1 */ 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 12.1 * @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 12.1 */ 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 12.1 * @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 a 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 12.1 */ public function getNumRows($cur = null) { $this->connect(); return pg_num_rows((int) $cur ? $cur : $this->cursor); } /** * Get the current or query, or new FOFDatabaseQuery object. * * @param boolean $new False to return the last query set, True to return a new FOFDatabaseQuery object. * @param boolean $asObj False to return last query as string, true to get FOFDatabaseQueryPostgresql object. * * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. * * @since 12.1 * @throws RuntimeException */ public function getQuery($new = false, $asObj = false) { if ($new) { // Make sure we have a query class for this driver. if (!class_exists('FOFDatabaseQueryPostgresql')) { throw new RuntimeException('FOFDatabaseQueryPostgresql Class not found.'); } $this->queryObject = new FOFDatabaseQueryPostgresql($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 12.1 */ 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 12.1 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); $tableSub = $this->replacePrefix($table); $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 = \'public\') ) 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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 */ 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 FOFDatabaseDriverPostgresql Returns this object to support chaining. * * @since 12.1 * @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 12.1 * @throws RuntimeException */ public function execute() { $this->connect(); if (!is_resource($this->connection)) { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } // 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 FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->limit . ' OFFSET ' . $this->offset; } // 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; if (class_exists('JLog')) { 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 = @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(); } } // 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($query); // 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($query); // Throw the normal query exception. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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 FOFDatabaseDriverPostgresql Returns this object to support chaining. * * @since 12.1 * @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 12.1 */ public function select($database) { return true; } /** * Custom settings for UTF support * * @return integer Zero on success, -1 on failure * * @since 12.1 */ public function setUtf() { $this->connect(); 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 $field_name The table field's name. * @param string $field_value The variable value to quote and return. * * @return string The quoted string. * * @since 12.1 */ public function sqlValue($columns, $field_name, $field_value) { switch ($columns[$field_name]) { case 'boolean': $val = 'NULL'; if ($field_value == 't') { $val = 'TRUE'; } elseif ($field_value == 'f') { $val = 'FALSE'; } break; case 'bigint': case 'bigserial': case 'integer': case 'money': case 'numeric': case 'real': case 'smallint': case 'serial': case 'numeric,': $val = strlen($field_value) == 0 ? 'NULL' : $field_value; break; case 'date': case 'timestamp without time zone': if (empty($field_value)) { $field_value = $this->getNullDate(); } $val = $this->quote($field_value); break; default: $val = $this->quote($field_value); break; } return $val; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 12.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 12.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--; $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 12.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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 * @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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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; $nIndex < count($query); $nIndex = $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; $nIndex < count($query); $nIndex = $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; $nIndex < count($query); $nIndex = $nIndex + 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('setval', $query); } $explodedQuery = explode('\'', $query); for ($nIndex = 0; $nIndex < count($explodedQuery); $nIndex = $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 12.1 */ 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 12.1 */ 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 FOFDatabaseDriverPostgresql Returns this object to support chaining. * * @since 12.1 * @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 12.1 * @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 */ protected function getErrorNumber() { return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE) . ' '; } /** * Return the actual SQL Error message * * @param string $query The SQL Query that fails * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage($query) { $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); $query = str_replace($this->tablePrefix, '#__', $query); } return $errorMessage . "SQL=" . $query; } } PK ! W|�D database/driver/sqlazure.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQL Server database driver * * @see http://msdn.microsoft.com/en-us/library/ee336279.aspx * @since 12.1 */ class FOFDatabaseDriverSqlazure extends FOFDatabaseDriverSqlsrv { /** * The name of the database driver. * * @var string * @since 12.1 */ public $name = 'sqlazure'; } PK ! ۵NQ ) ) database/driver/sqlite.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQLite database driver * * @see http://php.net/pdo * @since 12.1 */ class FOFDatabaseDriverSqlite extends FOFDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 12.1 */ 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 12.1 */ protected $nameQuote = '`'; /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->freeResult(); unset($this->connection); } /** * Disconnects the database. * * @return void * * @since 12.1 */ public function disconnect() { $this->freeResult(); unset($this->connection); } /** * 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 FOFDatabaseDriverSqlite Returns this object to support chaining. * * @since 12.1 */ 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 12.1 */ public function escape($text, $extra = false) { if (is_int($text) || is_float($text)) { return $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 12.1 */ 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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 * @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 12.1 */ public function setUtf() { $this->connect(); return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return FOFDatabaseDriverSqlite Returns this object to support chaining. * * @since 12.1 * @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 FOFDatabaseDriverSqlite Returns this object to support chaining. * * @since 12.1 * @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 FOFDatabaseDriverSqlite Returns this object to support chaining. * * @since 12.1 * @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 12.1 */ 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 12.3 * @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 12.3 * @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 12.3 * @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++; } } } PK ! ^�j j database/driver/sqlsrv.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQL Server database driver * * @see http://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx * @since 12.1 */ class FOFDatabaseDriverSqlsrv extends FOFDatabaseDriver { /** * The name of the database driver. * * @var string * @since 12.1 */ 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 12.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 12.1 */ protected $nullDate = '1900-01-01 00:00:00'; /** * @var string The minimum supported database version. * @since 12.1 */ protected static $dbMinimum = '10.50.1600.1'; /** * Test to see if the SQLSRV connector is available. * * @return boolean True on success, false otherwise. * * @since 12.1 */ public static function isSupported() { return (function_exists('sqlsrv_connect')); } /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 12.1 */ 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); } /** * Destructor. * * @since 12.1 */ public function __destruct() { $this->disconnect(); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 12.1 * @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 (!function_exists('sqlsrv_connect')) { throw new RuntimeException('PHP extension sqlsrv_connect is not available.'); } // Attempt to connect to the server. if (!($this->connection = @ sqlsrv_connect($this->options['host'], $config))) { throw new RuntimeException('Database sqlsrv_connect failed'); } // 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(); } /** * Disconnects the database. * * @return void * * @since 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ public function escape($text, $extra = false) { $result = addslashes($text); $result = str_replace("\'", "''", $result); $result = str_replace('\"', '"', $result); $result = str_replace('\/', '/', $result); if ($extra) { // We need the below str_replace since the search in sql server doesn't recognize _ character. $result = str_replace('_', '[_]', $result); } return $result; } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 12.1 */ 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 FOFDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 */ 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 12.1 * @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) { if (stristr(strtolower($field->Type), "nvarchar")) { $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 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 * @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 12.1 */ public function insertid() { $this->connect(); // TODO: SELECT IDENTITY $this->setQuery('SELECT @@IDENTITY'); return (int) $this->loadResult(); } /** * 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 12.1 * @throws RuntimeException */ public function loadResult() { $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return null; } // Get the first row from the result set as an array. if ($row = sqlsrv_fetch_array($cursor, SQLSRV_FETCH_NUMERIC)) { $ret = $row[0]; } // Free up system resources and return. $this->freeResult($cursor); // For SQLServer - we need to strip slashes $ret = stripslashes($ret); return $ret; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 12.1 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); if (!is_resource($this->connection)) { if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); } throw new RuntimeException($this->errorMsg, $this->errorNum); } // 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 FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query = $this->limit($query, $this->limit, $this->offset); } // 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; if (class_exists('JLog')) { 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($query); // 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($query); // Throw the normal query exception. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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. if (class_exists('JLog')) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); } throw new RuntimeException($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 12.1 */ public function replacePrefix($query, $prefix = '#__') { $startPos = 0; $literal = ''; $query = trim($query); $n = strlen($query); while ($startPos < $n) { $ip = strpos($query, $prefix, $startPos); if ($ip === false) { break; } $j = strpos($query, "N'", $startPos); $k = strpos($query, '"', $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($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; } /** * 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 12.1 * @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 RuntimeException('Could not connect to database'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 12.1 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 12.1 * @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 12.1 * @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 12.1 * @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 12.1 */ 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 12.1 */ 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 12.1 */ protected function fetchObject($cursor = null, $class = 'stdClass') { 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 12.1 */ 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 12.1 */ 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 12.1 */ protected function limit($query, $limit, $offset) { if ($limit == 0 && $offset == 0) { return $query; } $start = $offset + 1; $end = $offset + $limit; $orderBy = stristr($query, 'ORDER BY'); if (is_null($orderBy) || empty($orderBy)) { $orderBy = 'ORDER BY (select 0)'; } $query = str_ireplace($orderBy, '', $query); $rowNumberText = ', ROW_NUMBER() OVER (' . $orderBy . ') AS RowNumber FROM '; $query = preg_replace('/\sFROM\s/i', $rowNumberText, $query, 1); return $query; } /** * 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 FOFDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 12.1 * @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 FOFDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 12.1 * @throws RuntimeException */ public function lockTable($tableName) { return $this; } /** * Unlocks tables in the database. * * @return FOFDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 12.1 * @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]['SQLSTATE']; } /** * Return the actual SQL Error message * * @param string $query The SQL Query that fails * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage($query) { $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); $query = str_replace($this->tablePrefix, '#__', $query); } return $errorMessage . ' SQL=' . $query; } } PK ! _�Sl�� �� database/driver.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Joomla Platform Database Driver Class * * @since 12.1 * * @method string q() q($text, $escape = true) Alias for quote method * @method string qn() qn($name, $as = null) Alias for quoteName method */ abstract class FOFDatabaseDriver extends FOFDatabase implements FOFDatabaseInterface { /** * The name of the database. * * @var string * @since 11.4 */ private $_database; /** * The name of the database driver. * * @var string * @since 11.1 */ 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 11.1 */ protected $connection; /** * @var integer The number of SQL statements executed by the database driver. * @since 11.1 */ protected $count = 0; /** * @var resource The database connection cursor from the last query. * @since 11.1 */ protected $cursor; /** * @var boolean The database driver debugging state. * @since 11.1 */ protected $debug = false; /** * @var integer The affected row limit for the current SQL statement. * @since 11.1 */ protected $limit = 0; /** * @var array The log of executed SQL statements by the database driver. * @since 11.1 */ 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 11.1 */ 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 11.1 */ protected $nullDate; /** * @var integer The affected row offset to apply for the current SQL statement. * @since 11.1 */ protected $offset = 0; /** * @var array Passed in upon instantiation and saved. * @since 11.1 */ protected $options; /** * @var mixed The current SQL statement to execute. * @since 11.1 */ protected $sql; /** * @var string The common database table prefix. * @since 11.1 */ protected $tablePrefix; /** * @var boolean True if the database engine supports UTF-8 character encoding. * @since 11.1 */ 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 11.1 * @deprecated 12.1 */ protected $errorNum = 0; /** * @var string The database error message * @since 11.1 * @deprecated 12.1 */ protected $errorMsg; /** * @var array FOFDatabaseDriver instances container. * @since 11.1 */ protected static $instances = array(); /** * @var string The minimum supported database version. * @since 12.1 */ protected static $dbMinimum; /** * @var integer The depth of the current transaction. * @since 12.3 */ 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 11.1 */ 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; } // Block the ext/mysql driver for PHP 7 if ($fileName === 'mysql.php' && PHP_MAJOR_VERSION >= 7) { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', 'FOFDatabaseDriver' . 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 FOFDatabaseDriver 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 FOFDatabaseDriver 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 FOFDatabaseDriver A database object. * * @since 11.1 * @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 = FOFDatabaseDriverMysqli::isSupported(); $pdoMysqlSupported = FOFDatabaseDriverPdomysql::isSupported(); // If neither is supported, then the user cannot use MySQL; throw an exception if (!$mysqliSupported && !$pdoMysqlSupported) { throw new RuntimeException( '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) { if (class_exists('JLog')) { 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 { if (class_exists('JLog')) { 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 = 'FOFDatabaseDriver' . 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 RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver'])); } // Create our new FOFDatabaseDriver connector based on the options given. try { $instance = new $class($options); } catch (RuntimeException $e) { throw new RuntimeException(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. * * @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 11.1 */ public static function splitSql($sql) { $start = 0; $open = false; $char = ''; $end = strlen($sql); $queries = array(); for ($i = 0; $i < $end; $i++) { $current = substr($sql, $i, 1); if (($current == '"' || $current == '\'')) { $n = 2; while (substr($sql, $i - $n + 1, 1) == '\\' && $n < $i) { $n++; } if ($n % 2 == 0) { if ($open) { if ($current == $char) { $open = false; $char = ''; } } else { $open = true; $char = $current; } } } if (($current == ';' && !$open) || $i == $end - 1) { $queries[] = substr($sql, $start, ($i - $start + 1)); $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 11.1 */ 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 11.1 */ public function __construct($options) { // Initialise object variables. $this->_database = (isset($options['database'])) ? $options['database'] : ''; $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_'; $this->connection = array_key_exists('connection', $options) ? $options['connection'] : null; $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 12.2 * @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 12.1 * @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 11.1 */ 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 12.2 * @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(); } /** * Disconnects the database. * * @return void * * @since 12.1 */ abstract public function disconnect(); /** * Adds a function callable just before disconnecting the database. Parameter of the callable is $this FOFDatabaseDriver * * @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 FOFDatabaseDriver Returns this object to support chaining. * * @since 11.4 * @throws RuntimeException */ public abstract 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 11.1 */ 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 11.1 */ 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 11.1 */ 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 11.1 */ 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 11.1 */ 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 11.1 */ 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 12.2 */ 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 . '_general_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_general_ci or utf8mb4_general_ci $newCollation = $charset . '_general_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 $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) { return $query; } // Replace utf8mb4 with utf8 return str_replace('utf8mb4', '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 12.2 */ protected function getCreateDatabaseQuery($options, $utf) { if ($utf) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `' . $charset . '`'; } 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 11.1 */ 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 11.1 */ public function getConnection() { return $this->connection; } /** * Get the total number of SQL statements executed by the database driver. * * @return integer * * @since 11.1 */ public function getCount() { return $this->count; } /** * Gets the name of the database used by this connection. * * @return string * * @since 11.4 */ protected function getDatabase() { return $this->_database; } /** * Returns a PHP date() function compliant date format for the database driver. * * @return string The format string. * * @since 11.1 */ 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 11.1 */ 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 12.1 */ 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 11.1 */ 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 11.1 */ abstract public function getNumRows($cursor = null); /** * Get the common table prefix for the database driver. * * @return string The common database table prefix. * * @since 11.1 */ public function getPrefix() { return $this->tablePrefix; } /** * Gets an exporter class object. * * @return FOFDatabaseExporter An exporter object. * * @since 12.1 * @throws RuntimeException */ public function getExporter() { // Derive the class name from the driver. $class = 'FOFDatabaseExporter' . 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 RuntimeException('Database Exporter not found.'); } $o = new $class; $o->setDbo($this); return $o; } /** * Gets an importer class object. * * @return FOFDatabaseImporter An importer object. * * @since 12.1 * @throws RuntimeException */ public function getImporter() { // Derive the class name from the driver. $class = 'FOFDatabaseImporter' . 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 RuntimeException('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('FOFDatabaseDriver', '', $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, '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 FOFDatabaseQuery object. * * @param boolean $new False to return the current query object, True to return a new FOFDatabaseQuery object. * * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. * * @since 11.1 * @throws RuntimeException */ public function getQuery($new = false) { if ($new) { // Derive the class name from the driver. $class = 'FOFDatabaseQuery' . 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 RuntimeException('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 FOFDatabaseIterator A new database iterator. * * @since 12.1 * @throws RuntimeException */ public function getIterator($column = null, $class = 'stdClass') { // Derive the class name from the driver. $iteratorClass = 'FOFDatabaseIterator' . 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 RuntimeException(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 11.1 * @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 11.1 * @throws RuntimeException */ abstract public function getTableCreate($tables); /** * Retrieves field information about the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array An array of keys for the table(s). * * @since 11.1 * @throws RuntimeException */ abstract public function getTableKeys($tables); /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 11.1 * @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 11.1 * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use hasUTFSupport() instead */ public function getUTFSupport() { if (class_exists('JLog')) { JLog::add('FOFDatabaseDriver::getUTFSupport() is deprecated. Use FOFDatabaseDriver::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 12.1 */ 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 11.1 */ 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 11.1 */ 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 11.1 * @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 12.1 */ 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 11.1 * @throws RuntimeException */ public function loadAssoc() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return null; } // 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 11.1 * @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 null; } // 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 11.1 * @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 null; } // 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 11.1 * @throws RuntimeException * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { if (class_exists('JLog')) { JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::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 11.1 * @throws RuntimeException * @deprecated 4.0 (CMS) Use FOFDatabaseDriver::getIterator() instead */ public function loadNextRow() { if (class_exists('JLog')) { JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::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 11.1 * @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 null; } // 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 11.1 * @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 null; } // 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 11.1 * @throws RuntimeException */ public function loadResult() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return null; } // 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 11.1 * @throws RuntimeException */ public function loadRow() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return null; } // 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 string $key The name of a field on which to key the result array. * * @return mixed The return value or null if the query failed. * * @since 11.1 * @throws RuntimeException */ public function loadRowList($key = null) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return null; } // Get all of the rows from the result set as arrays. while ($row = $this->fetchArray($cursor)) { if ($key !== null) { $array[$row[$key]] = $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 FOFDatabaseDriver Returns this object to support chaining. * * @since 11.4 * @throws RuntimeException */ public abstract 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 The quoted input string. * * @note Accepting an array of strings was added in 12.3. * @since 11.1 */ 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) . '\''; } } /** * 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 11.1 */ 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 11.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 . $part . $q; } else { $parts[] = $q[0] . $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 11.1 */ 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 FOFDatabaseDriver Returns this object to support chaining. * * @since 11.4 * @throws RuntimeException */ public abstract 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 11.1 * @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 11.1 */ 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 FOFDatabaseQuery object or a string. * @param integer $offset The affected row offset to set. * @param integer $limit The maximum affected rows to set. * * @return FOFDatabaseDriver This object to support method chaining. * * @since 11.1 */ public function setQuery($query, $offset = 0, $limit = 0) { $this->sql = $query; if ($query instanceof FOFDatabaseQueryLimitable) { 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 11.1 */ abstract public function setUtf(); /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 11.1 * @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 11.1 * @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 11.1 * @throws RuntimeException */ abstract public function transactionStart($asSavepoint = false); /** * Method to truncate a table. * * @param string $table The table to truncate * * @return void * * @since 11.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 $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 11.1 * @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) 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)) { $where[] = $this->quoteName($k) . '=' . $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 12.1 * @throws RuntimeException */ abstract public function execute(); /** * Unlocks tables in the database. * * @return FOFDatabaseDriver Returns this object to support chaining. * * @since 11.4 * @throws RuntimeException */ public abstract function unlockTables(); } PK ! �RNĺ � database/factory.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Joomla Platform Database Factory class * * @since 12.1 */ class FOFDatabaseFactory { /** * Contains the current FOFDatabaseFactory instance * * @var FOFDatabaseFactory * @since 12.1 */ private static $_instance = null; /** * Method to return a FOFDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. 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 string $name Name of the database driver you'd like to instantiate * @param array $options Parameters to be passed to the database driver. * * @return FOFDatabaseDriver A database driver object. * * @since 12.1 * @throws RuntimeException */ public function getDriver($name = 'joomla', $options = array()) { // Sanitize the database connector options. $options['driver'] = preg_replace('/[^A-Z0-9_\.-]/i', '', $name); $options['database'] = (isset($options['database'])) ? $options['database'] : null; $options['select'] = (isset($options['select'])) ? $options['select'] : true; // Derive the class name from the driver. $class = 'FOFDatabaseDriver' . 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 RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver'])); } // Create our new FOFDatabaseDriver connector based on the options given. try { $instance = new $class($options); } catch (RuntimeException $e) { throw new RuntimeException(sprintf('Unable to connect to the Database: %s', $e->getMessage()), $e->getCode(), $e); } return $instance; } /** * Gets an instance of the factory object. * * @return FOFDatabaseFactory * * @since 12.1 */ public static function getInstance() { return self::$_instance ? self::$_instance : new FOFDatabaseFactory; } /** * Get the current query object or a new FOFDatabaseQuery object. * * @param string $name Name of the driver you want an query object for. * @param FOFDatabaseDriver $db Optional FOFDatabaseDriver instance * * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. * * @since 12.1 * @throws RuntimeException */ public function getQuery($name, FOFDatabaseDriver $db = null) { // Derive the class name from the driver. $class = 'FOFDatabaseQuery' . ucfirst(strtolower($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 RuntimeException('Database Query class not found'); } return new $class($db); } /** * Gets an instance of a factory object to return on subsequent calls of getInstance. * * @param FOFDatabaseFactory $instance A FOFDatabaseFactory object. * * @return void * * @since 12.1 */ public static function setInstance(FOFDatabaseFactory $instance = null) { self::$_instance = $instance; } } PK ! ��GD�[ �[ database/installer.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ class FOFDatabaseInstaller { /** @var FOFDatabase The database connector object */ private $db = null; /** * @var FOFInput Input variables */ protected $input = array(); /** @var string The directory where the XML schema files are stored */ private $xmlDirectory = null; /** @var array A list of the base names of the XML schema files */ public $xmlFiles = array('mysql', 'mysqli', 'pdomysql', 'postgresql', 'sqlsrv', 'mssql'); /** @var array Internal cache for table list */ protected static $allTables = array(); /** * Public constructor * * @param array $config The configuration array */ public function __construct($config = array()) { // Make sure $config is an array if (is_object($config)) { $config = (array) $config; } elseif (!is_array($config)) { $config = array(); } // Get the input if (array_key_exists('input', $config)) { if ($config['input'] instanceof FOFInput) { $this->input = $config['input']; } else { $this->input = new FOFInput($config['input']); } } else { $this->input = new FOFInput; } // Set the database object if (array_key_exists('dbo', $config)) { $this->db = $config['dbo']; } else { $this->db = FOFPlatform::getInstance()->getDbo(); } // Set the $name/$_name variable $component = $this->input->getCmd('option', 'com_foobar'); if (array_key_exists('option', $config)) { $component = $config['option']; } // Figure out where the XML schema files are stored if (array_key_exists('dbinstaller_directory', $config)) { $this->xmlDirectory = $config['dbinstaller_directory']; } else { // Nothing is defined, assume the files are stored in the sql/xml directory inside the component's administrator section $directories = FOFPlatform::getInstance()->getComponentBaseDirs($component); $this->setXmlDirectory($directories['admin'] . '/sql/xml'); } // Do we have a set of XML files to look for? if (array_key_exists('dbinstaller_files', $config)) { $files = $config['dbinstaller_files']; if (!is_array($files)) { $files = explode(',', $files); } $this->xmlFiles = $files; } } /** * Sets the directory where XML schema files are stored * * @param string $xmlDirectory */ public function setXmlDirectory($xmlDirectory) { $this->xmlDirectory = $xmlDirectory; } /** * Returns the directory where XML schema files are stored * * @return string */ public function getXmlDirectory() { return $this->xmlDirectory; } /** * Creates or updates the database schema * * @return void * * @throws Exception When a database query fails and it doesn't have the canfail flag */ public function updateSchema() { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** * The meta/autocollation node defines if I should automatically apply the correct collation (utf8 or utf8mb4) * to the database tables managed by the schema updater. When enabled (default) the queries are automatically * converted to the correct collation (utf8mb4_unicode_ci or utf8_general_ci) depending on whether your Joomla! * and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if UTF8MB4 is supported, all CREATE TABLE * queries are analyzed and the tables referenced in them are auto-converted to the proper utf8mb4 collation. */ $autoCollationConversion = true; if ($xml->meta->autocollation) { $value = (string) $xml->meta->autocollation; $value = trim($value); $value = strtolower($value); $autoCollationConversion = in_array($value, array('true', '1', 'on', 'yes')); } try { $hasUtf8mb4Support = $this->db->hasUTF8mb4Support(); } catch (\Exception $e) { $hasUtf8mb4Support = false; } $tablesToConvert = array(); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { // Get the attributes $attributes = $action->attributes(); // Get the table / view name $table = $attributes->table ? (string)$attributes->table : ''; if (empty($table)) { continue; } // Am I allowed to let this action fail? $canFailAction = $attributes->canfail ? $attributes->canfail : 0; // Evaluate conditions $shouldExecute = true; /** @var SimpleXMLElement $node */ foreach ($action->children() as $node) { if ($node->getName() == 'condition') { // Get the operator $operator = $node->attributes()->operator ? (string)$node->attributes()->operator : 'and'; $operator = empty($operator) ? 'and' : $operator; $condition = $this->conditionMet($table, $node); switch ($operator) { case 'not': $shouldExecute = $shouldExecute && !$condition; break; case 'or': $shouldExecute = $shouldExecute || $condition; break; case 'nor': $shouldExecute = !$shouldExecute && !$condition; break; case 'xor': $shouldExecute = ($shouldExecute xor $condition); break; case 'maybe': $shouldExecute = $condition ? true : $shouldExecute; break; default: $shouldExecute = $shouldExecute && $condition; break; } } // DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION! // if (!$shouldExecute) break; } // Do I have to only collect the tables from CREATE TABLE queries? $onlyCollectTables = !$shouldExecute && $autoCollationConversion && $hasUtf8mb4Support; // Make sure all conditions are met OR I have to collect tables from CREATE TABLE queries. if (!$shouldExecute && !$onlyCollectTables) { continue; } // Execute queries foreach ($action->children() as $node) { if ($node->getName() == 'query') { $query = (string) $node; if ($autoCollationConversion && $hasUtf8mb4Support) { $this->extractTablesToConvert($query, $tablesToConvert); } // If we're only collecting tables do not run the queries if ($onlyCollectTables) { continue; } $canFail = $node->attributes->canfail ? (string)$node->attributes->canfail : $canFailAction; if (is_string($canFail)) { $canFail = strtoupper($canFail); } $canFail = (in_array($canFail, array(true, 1, 'YES', 'TRUE'))); // Do I need to automatically convert the collation of all CREATE / ALTER queries? if ($autoCollationConversion) { if ($hasUtf8mb4Support) { // We have UTF8MB4 support. Convert all queries to UTF8MB4. $query = $this->convertUtf8QueryToUtf8mb4($query); } else { // We do not have UTF8MB4 support. Convert all queries to plain old UTF8. $query = $this->convertUtf8mb4QueryToUtf8($query); } } $this->db->setQuery($query); try { $this->db->execute(); } catch (Exception $e) { // If we are not allowed to fail, throw back the exception we caught if (!$canFail) { throw $e; } } } } } // Auto-convert the collation of tables if we are told to do so, have utf8mb4 support and a list of tables. if ($autoCollationConversion && $hasUtf8mb4Support && !empty($tablesToConvert)) { $this->convertTablesToUtf8mb4($tablesToConvert); } } /** * Uninstalls the database schema * * @return void */ public function removeSchema() { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables $tables = array(); /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { $attributes = $action->attributes(); $tables[] = (string)$attributes->table; } // Simplify the tables list $tables = array_unique($tables); // Start dropping tables foreach ($tables as $table) { try { $this->db->dropTable($table); } catch (Exception $e) { // Do not fail if I can't drop the table } } } /** * Find an suitable schema XML file for this database type and return the SimpleXMLElement holding its information * * @return null|SimpleXMLElement Null if no suitable schema XML file is found */ protected function findSchemaXml() { $driverType = $this->db->name; $xml = null; // And now look for the file foreach ($this->xmlFiles as $baseName) { // Remove any accidental whitespace $baseName = trim($baseName); // Get the full path to the file $fileName = $this->xmlDirectory . '/' . $baseName . '.xml'; // Make sure the file exists if (!@file_exists($fileName)) { continue; } // Make sure the file is a valid XML document try { $xml = new SimpleXMLElement($fileName, LIBXML_NONET, true); } catch (Exception $e) { $xml = null; continue; } // Make sure the file is an XML schema file if ($xml->getName() != 'schema') { $xml = null; continue; } if (!$xml->meta) { $xml = null; continue; } if (!$xml->meta->drivers) { $xml = null; continue; } /** @var SimpleXMLElement $drivers */ $drivers = $xml->meta->drivers; // Strict driver name match foreach ($drivers->children() as $driverTypeTag) { $thisDriverType = (string)$driverTypeTag; if ($thisDriverType == $driverType) { return $xml; } } // Some custom database drivers use a non-standard $name variable. Let try a relaxed match. foreach ($drivers->children() as $driverTypeTag) { $thisDriverType = (string)$driverTypeTag; if ( // e.g. $driverType = 'mysqlistupid', $thisDriverType = 'mysqli' => driver matched strpos($driverType, $thisDriverType) === 0 // e.g. $driverType = 'stupidmysqli', $thisDriverType = 'mysqli' => driver matched || (substr($driverType, -strlen($thisDriverType)) == $thisDriverType) ) { return $xml; } } $xml = null; } return $xml; } /** * Checks if a condition is met * * @param string $table The table we're operating on * @param SimpleXMLElement $node The condition definition node * * @return bool */ protected function conditionMet($table, SimpleXMLElement $node) { if (empty(static::$allTables)) { static::$allTables = $this->db->getTableList(); } // Does the table exist? $tableNormal = $this->db->replacePrefix($table); $tableExists = in_array($tableNormal, static::$allTables); // Initialise $condition = false; // Get the condition's attributes $attributes = $node->attributes(); $type = $attributes->type ? $attributes->type : null; $value = $attributes->value ? (string) $attributes->value : null; switch ($type) { // Check if a table or column is missing case 'missing': $fieldName = (string)$value; if (empty($fieldName)) { $condition = !$tableExists; } else { try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (\Exception $e) { $tableColumns = array(); } $condition = !array_key_exists($fieldName, $tableColumns); } break; // Check if a column type matches the "coltype" attribute case 'type': try { $tableColumns = $this->db->getTableColumns($tableNormal, false); } catch (\Exception $e) { $tableColumns = array(); } $condition = false; if (array_key_exists($value, $tableColumns)) { $coltype = $attributes->coltype ? $attributes->coltype : null; if (!empty($coltype)) { $coltype = strtolower($coltype); $currentType = strtolower($tableColumns[$value]->Type); $condition = ($coltype == $currentType); } } break; // Check if a (named) index exists on the table. Currently only supported on MySQL. case 'index': $indexName = (string) $value; $condition = true; if (!empty($indexName)) { $indexName = str_replace('#__', $this->db->getPrefix(), $indexName); $condition = $this->hasIndex($tableNormal, $indexName); } break; // Check if a table or column needs to be upgraded to utf8mb4 case 'utf8mb4upgrade': $condition = false; // Check if the driver and the database connection have UTF8MB4 support try { $hasUtf8mb4Support = $this->db->hasUTF8mb4Support(); } catch (\Exception $e) { $hasUtf8mb4Support = false; } if ($hasUtf8mb4Support) { $fieldName = (string)$value; if (empty($fieldName)) { $collation = $this->getTableCollation($tableNormal); } else { $collation = $this->getColumnCollation($tableNormal, $fieldName); } $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); $condition = $encoding != 'utf8mb4'; } break; // Check if the result of a query matches our expectation case 'equals': $query = (string)$node; $this->db->setQuery($query); try { $result = $this->db->loadResult(); $condition = ($result == $value); } catch (Exception $e) { return false; } break; // Always returns true case 'true': return true; break; default: return false; break; } return $condition; } /** * Get the collation of a table. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function getTableCollation($tableName) { static $cache = array(); $tableName = $this->db->replacePrefix($tableName); if (!isset($cache[$tableName])) { $cache[$tableName] = $this->realGetTableCollation($tableName); } return $cache[$tableName]; } /** * Get the collation of a table. This is the internal method used by getTableCollation. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetTableCollation($tableName) { try { $utf8Support = $this->db->hasUTFSupport(); } catch (\Exception $e) { $utf8Support = false; } try { $utf8mb4Support = $utf8Support && $this->db->hasUTF8mb4Support(); } catch (\Exception $e) { $utf8mb4Support = false; } $collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' : ($utf8Support ? 'utf_general_ci' : 'latin1_swedish_ci'); $query = 'SHOW TABLE STATUS LIKE ' . $this->db->q($tableName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (\Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Get the collation of a column. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function getColumnCollation($tableName, $columnName) { static $cache = array(); $tableName = $this->db->replacePrefix($tableName); $columnName = $this->db->replacePrefix($columnName); if (!isset($cache[$tableName])) { $cache[$tableName] = array(); } if (!isset($cache[$tableName][$columnName])) { $cache[$tableName][$columnName] = $this->realGetColumnCollation($tableName, $columnName); } return $cache[$tableName][$columnName]; } /** * Get the collation of a column. This is the internal method used by getColumnCollation. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetColumnCollation($tableName, $columnName) { $collation = $this->getTableCollation($tableName); $query = 'SHOW FULL COLUMNS FROM ' . $this->db->qn($tableName) . ' LIKE ' . $this->db->q($columnName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (\Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. * * We use our own method so we can be site it works even on Joomla! 3.4 or earlier, where UTF8MB4 support is not * implemented. * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8mb4QueryToUtf8($query) { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) { return $query; } // Replace utf8mb4 with utf8 $from = array( 'utf8mb4_unicode_ci', 'utf8mb4_', 'utf8mb4', ); $to = array( 'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to utf8_general_ci per Joomla!'s conventions 'utf8_', 'utf8', ); return str_replace($from, $to, $query); } /** * Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain utf8 to utf8mb4 (UTF-8 Multibyte). * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8QueryToUtf8mb4($query) { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) { return $query; } // Replace utf8 with utf8mb4 $from = array( 'utf8_general_ci', 'utf8_', 'utf8', ); $to = array( 'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to utf8mb4_unicode_ci per Joomla!'s conventions 'utf8mb4_', 'utf8mb4', ); return str_replace($from, $to, $query); } /** * Analyzes a query. If it's a CREATE TABLE query the table is added to the $tables array. * * @param string $query The query to analyze * @param string $tables The array where the name of the detected table is added * * @return void */ private function extractTablesToConvert($query, &$tables) { // Normalize the whitespace of the query $query = trim($query); $query = str_replace(array("\r\n", "\r", "\n"), ' ', $query); while (strstr($query, ' ') !== false) { $query = str_replace(' ', ' ', $query); } // Is it a create table query? $queryStart = substr($query, 0, 12); $queryStart = strtoupper($queryStart); if ($queryStart != 'CREATE TABLE') { return; } // Remove the CREATE TABLE keyword. Also, If there's an IF NOT EXISTS clause remove it. $query = substr($query, 12); $query = str_ireplace('IF NOT EXISTS', '', $query); $query = trim($query); // Make sure there is a space between the table name and its definition, denoted by an open parenthesis $query = str_replace('(', ' (', $query); // Now we should have the name of the table, a space and the rest of the query. Extract the table name. $parts = explode(' ', $query, 2); $tableName = $parts[0]; /** * The table name may be quoted. Since UTF8MB4 is only supported in MySQL, the table name can only be * quoted with surrounding backticks. Therefore we can trim backquotes from the table name to unquote it! **/ $tableName = trim($tableName, '`'); // Finally, add the table name to $tables if it doesn't already exist. if (!in_array($tableName, $tables)) { $tables[] = $tableName; } } /** * Converts the collation of tables listed in $tablesToConvert to utf8mb4_unicode_ci * * @param array $tablesToConvert The list of tables to convert * * @return void */ private function convertTablesToUtf8mb4($tablesToConvert) { try { $utf8mb4Support = $this->db->hasUTF8mb4Support(); } catch (\Exception $e) { $utf8mb4Support = false; } // Make sure the database driver REALLY has support for converting character sets if (!$utf8mb4Support) { return; } asort($tablesToConvert); foreach ($tablesToConvert as $tableName) { $collation = $this->getTableCollation($tableName); $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); if ($encoding != 'utf8mb4') { $queries = $this->db->getAlterTableCharacterSet($tableName); try { foreach ($queries as $query) { $this->db->setQuery($query)->execute(); } } catch (\Exception $e) { // We ignore failed conversions. Remember, you MUST change your indices MANUALLY. } } } } /** * Returns true if table $tableName has an index named $indexName or if it's impossible to retrieve index names for * the table (not enough privileges, not a MySQL database, ...) * * @param string $tableName The name of the table * @param string $indexName The name of the index * * @return bool */ private function hasIndex($tableName, $indexName) { static $isMySQL = null; static $cache = array(); if (is_null($isMySQL)) { $driverType = $this->db->name; $driverType = strtolower($driverType); $isMySQL = true; if ( !strpos($driverType, 'mysql') === 0 && !(substr($driverType, -5) == 'mysql') && !(substr($driverType, -6) == 'mysqli') ) { $isMySQL = false; } } // Not MySQL? Lie and return true. if (!$isMySQL) { return true; } if (!isset($cache[$tableName])) { $cache[$tableName] = array(); } if (!isset($cache[$tableName][$indexName])) { $cache[$tableName][$indexName] = true; try { $indices = array(); $query = 'SHOW INDEXES FROM ' . $this->db->qn($tableName); $indexDefinitions = $this->db->setQuery($query)->loadAssocList(); if (!empty($indexDefinitions) && is_array($indexDefinitions)) { foreach ($indexDefinitions as $def) { $indices[] = $def['Key_name']; } $indices = array_unique($indices); } $cache[$tableName][$indexName] = in_array($indexName, $indices); } catch (\Exception $e) { // Ignore errors } } return $cache[$tableName][$indexName]; } } PK ! �ox x database/interface.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; if (!interface_exists('JDatabaseInterface')) { /** * Joomla Platform Database Interface * * @since 11.2 */ interface JDatabaseInterface { /** * Test to see if the connector is available. * * @return boolean True on success, false otherwise. * * @since 11.2 */ public static function isSupported(); } } interface FOFDatabaseInterface extends JDatabaseInterface { } PK ! � �JQ Q database/iterator/azure.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQL azure database iterator. */ class FOFDatabaseIteratorAzure extends FOFDatabaseIteratorSqlsrv { } PK ! ��� database/iterator/mysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQL database iterator. */ class FOFDatabaseIteratorMysql extends FOFDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @see Countable::count() */ public function count() { return @mysql_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ protected function fetchObject() { return @mysql_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void */ protected function freeResult() { @mysql_free_result($this->cursor); } } PK ! '�B database/iterator/mysqli.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQLi database iterator. */ class FOFDatabaseIteratorMysqli extends FOFDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @see Countable::count() */ public function count() { return @mysqli_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ protected function fetchObject() { return @mysqli_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void */ protected function freeResult() { @mysqli_free_result($this->cursor); } } PK ! (ݫ�L L database/iterator/oracle.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQLi database iterator. */ class FOFDatabaseIteratorOracle extends FOFDatabaseIteratorPdo { } PK ! ���' ' database/iterator/pdo.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * PDO database iterator. */ class FOFDatabaseIteratorPdo extends FOFDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @see Countable::count() */ public function count() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { return @$this->cursor->rowCount(); } else { return 0; } } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ protected function fetchObject() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { return @$this->cursor->fetchObject($this->class); } else { return false; } } /** * Method to free up the memory used for the result set. * * @return void */ protected function freeResult() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { @$this->cursor->closeCursor(); } } } PK ! ݚ�N N database/iterator/pdomysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQLi database iterator. */ class FOFDatabaseIteratorPdomysql extends FOFDatabaseIteratorPdo { } PK ! Y��5 database/iterator/postgresql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * PostgreSQL database iterator. */ class FOFDatabaseIteratorPostgresql extends FOFDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @see Countable::count() */ public function count() { return @pg_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ protected function fetchObject() { return @pg_fetch_object($this->cursor, null, $this->class); } /** * Method to free up the memory used for the result set. * * @return void */ protected function freeResult() { @pg_free_result($this->cursor); } } PK ! m7�L L database/iterator/sqlite.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * MySQLi database iterator. */ class FOFDatabaseIteratorSqlite extends FOFDatabaseIteratorPdo { } PK ! ��e! database/iterator/sqlsrv.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQL server database iterator. */ class FOFDatabaseIteratorSqlsrv extends FOFDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @see Countable::count() */ public function count() { return @sqlsrv_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ protected function fetchObject() { return @sqlsrv_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void */ protected function freeResult() { @sqlsrv_free_stmt($this->cursor); } } PK ! �^Z+� � database/iterator.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Database iterator */ abstract class FOFDatabaseIterator implements Iterator { /** * The database cursor. * * @var mixed */ protected $cursor; /** * The class of object to create. * * @var string */ protected $class; /** * The name of the column to use for the key of the database record. * * @var mixed */ private $_column; /** * The current database record. * * @var mixed */ private $_current; /** * The current database record as a FOFTable object. * * @var FOFTable */ private $_currentTable; /** * A numeric or string key for the current database record. * * @var scalar */ private $_key; /** * The number of fetched records. * * @var integer */ private $_fetched = 0; /** * A FOFTable object created using the class type $class, used by getTable * * @var FOFTable */ private $_tableObject = null; /** * Returns an iterator object for a specific database type * * @param string $dbName The database type, e.g. mysql, mysqli, sqlazure etc. * @param mixed $cursor The database cursor * @param string $column An option column to use as the iterator key * @param string $class The table class of the returned objects * @param array $config Configuration parameters to push to the table class * * @return FOFDatabaseIterator * * @throws InvalidArgumentException */ public static function &getIterator($dbName, $cursor, $column, $class, $config = array()) { $className = 'FOFDatabaseIterator' . ucfirst($dbName); $object = new $className($cursor, $column, $class, $config); return $object; } /** * Database iterator constructor. * * @param mixed $cursor The database cursor. * @param string $column An option column to use as the iterator key. * @param string $class The table class of the returned objects. * @param array $config Configuration parameters to push to the table class * * @throws InvalidArgumentException */ public function __construct($cursor, $column, $class, $config = array()) { // Figure out the type and prefix of the class by the class name $parts = FOFInflector::explode($class); if(count($parts) != 3) { throw new InvalidArgumentException('Invalid table name, expected a pattern like ComponentTableFoobar got '.$class); } $this->_tableObject = FOFTable::getInstance($parts[2], ucfirst($parts[0]) . ucfirst($parts[1]))->getClone(); $this->cursor = $cursor; $this->class = 'stdClass'; $this->_column = $column; $this->_fetched = 0; $this->next(); } /** * Database iterator destructor. */ public function __destruct() { if ($this->cursor) { $this->freeResult($this->cursor); } } /** * The current element in the iterator. * * @return object * * @see Iterator::current() */ public function current() { return $this->_currentTable; } /** * The key of the current element in the iterator. * * @return scalar * * @see Iterator::key() */ public function key() { return $this->_key; } /** * Moves forward to the next result from the SQL query. * * @return void * * @see Iterator::next() */ public function next() { // Set the default key as being the number of fetched object $this->_key = $this->_fetched; // Try to get an object $this->_current = $this->fetchObject(); // If an object has been found if ($this->_current) { $this->_currentTable = $this->getTable(); // Set the key as being the indexed column (if it exists) if (isset($this->_current->{$this->_column})) { $this->_key = $this->_current->{$this->_column}; } // Update the number of fetched object $this->_fetched++; } } /** * Rewinds the iterator. * * This iterator cannot be rewound. * * @return void * * @see Iterator::rewind() */ public function rewind() { } /** * Checks if the current position of the iterator is valid. * * @return boolean * * @see Iterator::valid() */ public function valid() { return (boolean) $this->_current; } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. */ abstract protected function fetchObject(); /** * Method to free up the memory used for the result set. * * @return void */ abstract protected function freeResult(); /** * Returns the data in $this->_current as a FOFTable instance * * @return FOFTable * * @throws OutOfBoundsException */ protected function getTable() { if (!$this->valid()) { throw new OutOfBoundsException('Cannot get item past iterator\'s bounds', 500); } $this->_tableObject->bind($this->_current); return $this->_tableObject; } } PK ! V=F] ] database/query/element.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Element Class. * * @property-read string $name The name of the element. * @property-read array $elements An array of elements. * @property-read string $glue Glue piece. * * @since 11.1 */ class FOFDatabaseQueryElement { /** * @var string The name of the element. * @since 11.1 */ protected $name = null; /** * @var array An array of elements. * @since 11.1 */ protected $elements = null; /** * @var string Glue piece. * @since 11.1 */ protected $glue = null; /** * Constructor. * * @param string $name The name of the element. * @param mixed $elements String or array. * @param string $glue The glue for elements. * * @since 11.1 */ public function __construct($name, $elements, $glue = ',') { $this->elements = array(); $this->name = $name; $this->glue = $glue; $this->append($elements); } /** * Magic function to convert the query element to a string. * * @return string * * @since 11.1 */ public function __toString() { if (substr($this->name, -2) == '()') { return PHP_EOL . substr($this->name, 0, -2) . '(' . implode($this->glue, $this->elements) . ')'; } else { return PHP_EOL . $this->name . ' ' . implode($this->glue, $this->elements); } } /** * Appends element parts to the internal list. * * @param mixed $elements String or array. * * @return void * * @since 11.1 */ public function append($elements) { if (is_array($elements)) { $this->elements = array_merge($this->elements, $elements); } else { $this->elements = array_merge($this->elements, array($elements)); } } /** * Gets the elements of this element. * * @return array * * @since 11.1 */ public function getElements() { return $this->elements; } /** * Method to provide deep copy support to nested objects and arrays * when cloning. * * @return void * * @since 11.3 */ public function __clone() { foreach ($this as $k => $v) { if (is_object($v) || is_array($v)) { $this->{$k} = unserialize(serialize($v)); } } } } PK ! ^}R� � database/query/limitable.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; if (!interface_exists('JDatabaseQueryLimitable')) { /** * Joomla Database Query Limitable Interface. * Adds bind/unbind methods as well as a getBounded() method * to retrieve the stored bounded variables on demand prior to * query execution. * * @since 12.1 */ interface JDatabaseQueryLimitable { /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the FOFDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 12.1 */ public function processLimit($query, $limit, $offset = 0); /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return FOFDatabaseQuery Returns this object to allow chaining. * * @since 12.1 */ public function setLimit($limit = 0, $offset = 0); } } /** * Joomla Database Query Limitable Interface. * Adds bind/unbind methods as well as a getBounded() method * to retrieve the stored bounded variables on demand prior to * query execution. * * @since 12.1 */ interface FOFDatabaseQueryLimitable extends JDatabaseQueryLimitable { } PK ! |���� � database/query/mysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Building Class. * * @since 11.1 * @deprecated Will be removed when the minimum supported PHP version no longer includes the deprecated PHP `mysql` extension */ class FOFDatabaseQueryMysql extends FOFDatabaseQueryMysqli { } PK ! ��ya� � database/query/mysqli.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Building Class. * * @since 11.1 */ class FOFDatabaseQueryMysqli extends FOFDatabaseQuery implements FOFDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 12.1 */ protected $offset; /** * @var integer The limit for the result set. * @since 12.1 */ protected $limit; /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 12.1 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0 || $offset > 0) { $query .= ' LIMIT ' . $offset . ', ' . $limit; } return $query; } /** * Concatenates an array of column names or values. * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 11.1 */ public function concatenate($values, $separator = null) { if ($separator) { $concat_string = 'CONCAT_WS(' . $this->quote($separator); foreach ($values as $value) { $concat_string .= ', ' . $value; } return $concat_string . ')'; } else { return 'CONCAT(' . implode(',', $values) . ')'; } } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return FOFDatabaseQuery Returns this object to allow chaining. * * @since 12.1 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Return correct regexp operator for mysqli. * * Ensure that the regexp operator is mysqli compatible. * * Usage: * $query->where('field ' . $query->regexp($search)); * * @param string $value The regex pattern. * * @return string Returns the regex operator. * * @since 11.3 */ public function regexp($value) { return ' REGEXP ' . $value; } /** * Return correct rand() function for Mysql. * * Ensure that the rand() function is Mysql compatible. * * Usage: * $query->Rand(); * * @return string The correct rand function. * * @since 3.5 */ public function Rand() { return ' RAND() '; } } PK ! =��*� � database/query/oracle.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Oracle Query Building Class. * * @since 12.1 */ class FOFDatabaseQueryOracle extends FOFDatabaseQueryPdo implements FOFDatabaseQueryPreparable, FOFDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 12.1 */ protected $offset; /** * @var integer The limit for the result set. * @since 12.1 */ protected $limit; /** * @var array Bounded object array * @since 12.1 */ protected $bounded = array(); /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return FOFDatabaseQueryOracle * * @since 12.1 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()) { // Case 1: Empty Key (reset $bounded array) if (empty($key)) { $this->bounded = array(); return $this; } // Case 2: Key Provided, null value (unset key from $bounded array) if (is_null($value)) { if (isset($this->bounded[$key])) { unset($this->bounded[$key]); } return $this; } $obj = new stdClass; $obj->value = &$value; $obj->dataType = $dataType; $obj->length = $length; $obj->driverOptions = $driverOptions; // Case 3: Simply add the Key/Value into the bounded array $this->bounded[$key] = $obj; return $this; } /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 12.1 */ public function &getBounded($key = null) { if (empty($key)) { return $this->bounded; } else { if (isset($this->bounded[$key])) { return $this->bounded[$key]; } } } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return FOFDatabaseQueryOracle Returns this object to allow chaining. * * @since 12.1 */ public function clear($clause = null) { switch ($clause) { case null: $this->bounded = array(); break; } parent::clear($clause); return $this; } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the FOFDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 12.1 */ public function processLimit($query, $limit, $offset = 0) { // Check if we need to mangle the query. if ($limit || $offset) { $query = "SELECT joomla2.* FROM ( SELECT joomla1.*, ROWNUM AS joomla_db_rownum FROM ( " . $query . " ) joomla1 ) joomla2"; // Check if the limit value is greater than zero. if ($limit > 0) { $query .= ' WHERE joomla2.joomla_db_rownum BETWEEN ' . ($offset + 1) . ' AND ' . ($offset + $limit); } else { // Check if there is an offset and then use this. if ($offset) { $query .= ' WHERE joomla2.joomla_db_rownum > ' . ($offset + 1); } } } return $query; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return FOFDatabaseQueryOracle Returns this object to allow chaining. * * @since 12.1 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } } PK ! �v�bS S database/query/pdo.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * PDO Query Building Class. * * @since 12.1 */ class FOFDatabaseQueryPdo extends FOFDatabaseQuery { } PK ! V��!� � database/query/pdomysql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Building Class. * * @package Joomla.Platform * @subpackage Database * @since 3.4 */ class FOFDatabaseQueryPdomysql extends FOFDatabaseQueryMysqli { } PK ! �Ϻ�/7 /7 database/query/postgresql.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Building Class. * * @since 11.3 */ class FOFDatabaseQueryPostgresql extends FOFDatabaseQuery implements FOFDatabaseQueryLimitable { /** * @var object The FOR UPDATE element used in "FOR UPDATE" lock * @since 11.3 */ protected $forUpdate = null; /** * @var object The FOR SHARE element used in "FOR SHARE" lock * @since 11.3 */ protected $forShare = null; /** * @var object The NOWAIT element used in "FOR SHARE" and "FOR UPDATE" lock * @since 11.3 */ protected $noWait = null; /** * @var object The LIMIT element * @since 11.3 */ protected $limit = null; /** * @var object The OFFSET element * @since 11.3 */ protected $offset = null; /** * @var object The RETURNING element of INSERT INTO * @since 11.3 */ protected $returning = null; /** * Magic function to convert the query to a string, only for postgresql specific query * * @return string The completed query. * * @since 11.3 */ public function __toString() { $query = ''; switch ($this->type) { case 'select': $query .= (string) $this->select; $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->group) { $query .= (string) $this->group; } if ($this->having) { $query .= (string) $this->having; } if ($this->order) { $query .= (string) $this->order; } if ($this->forUpdate) { $query .= (string) $this->forUpdate; } else { if ($this->forShare) { $query .= (string) $this->forShare; } } if ($this->noWait) { $query .= (string) $this->noWait; } break; case 'update': $query .= (string) $this->update; $query .= (string) $this->set; if ($this->join) { $onWord = ' ON '; // Workaround for special case of JOIN with UPDATE foreach ($this->join as $join) { $joinElem = $join->getElements(); $joinArray = explode($onWord, $joinElem[0]); $this->from($joinArray[0]); $this->where($joinArray[1]); } $query .= (string) $this->from; } if ($this->where) { $query .= (string) $this->where; } break; case 'insert': $query .= (string) $this->insert; if ($this->values) { if ($this->columns) { $query .= (string) $this->columns; } $elements = $this->values->getElements(); if (!($elements[0] instanceof $this)) { $query .= ' VALUES '; } $query .= (string) $this->values; if ($this->returning) { $query .= (string) $this->returning; } } break; default: $query = parent::__toString(); break; } if ($this instanceof FOFDatabaseQueryLimitable) { $query = $this->processLimit($query, $this->limit, $this->offset); } return $query; } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 11.3 */ public function clear($clause = null) { switch ($clause) { case 'limit': $this->limit = null; break; case 'offset': $this->offset = null; break; case 'forUpdate': $this->forUpdate = null; break; case 'forShare': $this->forShare = null; break; case 'noWait': $this->noWait = null; break; case 'returning': $this->returning = null; break; case 'select': case 'update': case 'delete': case 'insert': case 'from': case 'join': case 'set': case 'where': case 'group': case 'having': case 'order': case 'columns': case 'values': parent::clear($clause); break; default: $this->type = null; $this->limit = null; $this->offset = null; $this->forUpdate = null; $this->forShare = null; $this->noWait = null; $this->returning = null; parent::clear($clause); break; } return $this; } /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * Usage: * $query->select($query->castAsChar('a')); * * @param string $value The value to cast as a char. * * @return string Returns the cast value. * * @since 11.3 */ public function castAsChar($value) { return $value . '::text'; } /** * Concatenates an array of column names or values. * * Usage: * $query->select($query->concatenate(array('a', 'b'))); * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 11.3 */ public function concatenate($values, $separator = null) { if ($separator) { return implode(' || ' . $this->quote($separator) . ' || ', $values); } else { return implode(' || ', $values); } } /** * Gets the current date and time. * * @return string Return string used in query to obtain * * @since 11.3 */ public function currentTimestamp() { return 'NOW()'; } /** * Sets the FOR UPDATE lock on select's output row * * @param string $table_name The table to lock * @param string $glue The glue by which to join the conditions. Defaults to ',' . * * @return FOFDatabaseQueryPostgresql FOR UPDATE query element * * @since 11.3 */ public function forUpdate($table_name, $glue = ',') { $this->type = 'forUpdate'; if (is_null($this->forUpdate)) { $glue = strtoupper($glue); $this->forUpdate = new FOFDatabaseQueryElement('FOR UPDATE', 'OF ' . $table_name, "$glue "); } else { $this->forUpdate->append($table_name); } return $this; } /** * Sets the FOR SHARE lock on select's output row * * @param string $table_name The table to lock * @param string $glue The glue by which to join the conditions. Defaults to ',' . * * @return FOFDatabaseQueryPostgresql FOR SHARE query element * * @since 11.3 */ public function forShare($table_name, $glue = ',') { $this->type = 'forShare'; if (is_null($this->forShare)) { $glue = strtoupper($glue); $this->forShare = new FOFDatabaseQueryElement('FOR SHARE', 'OF ' . $table_name, "$glue "); } else { $this->forShare->append($table_name); } return $this; } /** * Used to get a string to extract year from date column. * * Usage: * $query->select($query->year($query->quoteName('dateColumn'))); * * @param string $date Date column containing year to be extracted. * * @return string Returns string to extract year from a date. * * @since 12.1 */ public function year($date) { return 'EXTRACT (YEAR FROM ' . $date . ')'; } /** * Used to get a string to extract month from date column. * * Usage: * $query->select($query->month($query->quoteName('dateColumn'))); * * @param string $date Date column containing month to be extracted. * * @return string Returns string to extract month from a date. * * @since 12.1 */ public function month($date) { return 'EXTRACT (MONTH FROM ' . $date . ')'; } /** * Used to get a string to extract day from date column. * * Usage: * $query->select($query->day($query->quoteName('dateColumn'))); * * @param string $date Date column containing day to be extracted. * * @return string Returns string to extract day from a date. * * @since 12.1 */ public function day($date) { return 'EXTRACT (DAY FROM ' . $date . ')'; } /** * Used to get a string to extract hour from date column. * * Usage: * $query->select($query->hour($query->quoteName('dateColumn'))); * * @param string $date Date column containing hour to be extracted. * * @return string Returns string to extract hour from a date. * * @since 12.1 */ public function hour($date) { return 'EXTRACT (HOUR FROM ' . $date . ')'; } /** * Used to get a string to extract minute from date column. * * Usage: * $query->select($query->minute($query->quoteName('dateColumn'))); * * @param string $date Date column containing minute to be extracted. * * @return string Returns string to extract minute from a date. * * @since 12.1 */ public function minute($date) { return 'EXTRACT (MINUTE FROM ' . $date . ')'; } /** * Used to get a string to extract seconds from date column. * * Usage: * $query->select($query->second($query->quoteName('dateColumn'))); * * @param string $date Date column containing second to be extracted. * * @return string Returns string to extract second from a date. * * @since 12.1 */ public function second($date) { return 'EXTRACT (SECOND FROM ' . $date . ')'; } /** * Sets the NOWAIT lock on select's output row * * @return FOFDatabaseQueryPostgresql NO WAIT query element * * @since 11.3 */ public function noWait () { $this->type = 'noWait'; if (is_null($this->noWait)) { $this->noWait = new FOFDatabaseQueryElement('NOWAIT', null); } return $this; } /** * Set the LIMIT clause to the query * * @param integer $limit An int of how many row will be returned * * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 11.3 */ public function limit($limit = 0) { if (is_null($this->limit)) { $this->limit = new FOFDatabaseQueryElement('LIMIT', (int) $limit); } return $this; } /** * Set the OFFSET clause to the query * * @param integer $offset An int for skipping row * * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 11.3 */ public function offset($offset = 0) { if (is_null($this->offset)) { $this->offset = new FOFDatabaseQueryElement('OFFSET', (int) $offset); } return $this; } /** * Add the RETURNING element to INSERT INTO statement. * * @param mixed $pkCol The name of the primary key column. * * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 11.3 */ public function returning($pkCol) { if (is_null($this->returning)) { $this->returning = new FOFDatabaseQueryElement('RETURNING', $pkCol); } return $this; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 12.1 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 12.1 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0) { $query .= ' LIMIT ' . $limit; } if ($offset > 0) { $query .= ' OFFSET ' . $offset; } return $query; } /** * Add to the current date and time in Postgresql. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * * @param datetime $date The date to add to * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @since 13.1 * @note Not all drivers support all units. Check appropriate references * @link http://www.postgresql.org/docs/9.0/static/functions-datetime.html. */ public function dateAdd($date, $interval, $datePart) { if (substr($interval, 0, 1) != '-') { return "timestamp '" . $date . "' + interval '" . $interval . " " . $datePart . "'"; } else { return "timestamp '" . $date . "' - interval '" . ltrim($interval, '-') . " " . $datePart . "'"; } } /** * Return correct regexp operator for Postgresql. * * Ensure that the regexp operator is Postgresql compatible. * * Usage: * $query->where('field ' . $query->regexp($search)); * * @param string $value The regex pattern. * * @return string Returns the regex operator. * * @since 11.3 */ public function regexp($value) { return ' ~* ' . $value; } /** * Return correct rand() function for Postgresql. * * Ensure that the rand() function is Postgresql compatible. * * Usage: * $query->Rand(); * * @return string The correct rand function. * * @since 3.5 */ public function Rand() { return ' RANDOM() '; } } PK ! -� �? ? database/query/preparable.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; if (!interface_exists('JDatabaseQueryPreparable')) { /** * Joomla Database Query Preparable Interface. * Adds bind/unbind methods as well as a getBounded() method * to retrieve the stored bounded variables on demand prior to * query execution. * * @since 12.1 */ interface JDatabaseQueryPreparable { /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return FOFDatabaseQuery * * @since 12.1 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()); /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 12.1 */ public function &getBounded($key = null); } } interface FOFDatabaseQueryPreparable extends JDatabaseQueryPreparable { }PK ! =�q database/query/sqlazure.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * Query Building Class. * * @since 11.1 */ class FOFDatabaseQuerySqlazure extends FOFDatabaseQuerySqlsrv { /** * 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 11.1 */ protected $name_quotes = ''; } PK ! �VPK� � database/query/sqlite.phpnu �[��� <?php /** * @package FrameworkOnFramework * @subpackage database * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects * instead of plain stdClass objects */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; /** * SQLite Query Building Class. * * @since 12.1 */ class FOFDatabaseQuerySqlite extends FOFDatabaseQueryPdo implements FOFDatabaseQueryPreparable, FOFDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 12.1 */ protected $offset; /** * @var integer The limit for the result set. * @since 12.1 */ protected $limit; /** * @var array Bounded object array * @since 12.1 */ protected $bounded = array(); /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return FOFDatabaseQuerySqlite * * @since 12.1 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()) { // Case 1: Empty Key (reset $bounded array) if (empty($key)) { $this->bounded = array(); return $this; } // Case 2: Key Provided, null value (unset key from $bounded array) if (is_null($value)) { if (isset($this->bounded[$key])) { unset($this->bounded[$key]); } return $this; } $obj = new stdClass; $obj->value = &$value; $obj->dataType = $dataType; $obj->length = $length; $obj->driverOptions = $driverOptions; // Case 3: Simply add the Key/Value into the bounded array $this->bounded[$key] = $obj; return $this; } /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 12.1 */ public function &getBounded($key = null) { if (empty($key)) { return $this->bounded; } else { if (isset($this->bounded[$key])) { return $this->bounded[$key]; } } } /** * Gets the number of characters in a string. * * Note, use 'length' to find the number of bytes in a string. * * Usage: * $query->select($query->charLength('a')); * * @param string $field A value. * @param string $operator Comparison operator between charLength integer value and $condition * @param string $condition Integer value to compare charLength with. * * @return string The required char length call. * * @since 13.1 */ public function charLength($field, $operator = null, $condition = null) { return 'length(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return FOFDatabaseQuerySqlite Returns this object to allow chaining. * * @since 12.1 */ public function clear($clause = null) { switch ($clause) { case null: $this->bounded = array(); break; } parent::clear($clause); return $this; } /** * Concatenates an array of column names or values. * * Usage: * $query->select($query->concatenate(array('a', 'b'))); * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 11.1 */ public function concatenate($values, $separator = null) { if ($separator) { return implode(' || ' . $this->quote($separator) . ' || ', $values); } else { return implode(' || ', $values); } } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the FOFDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 12.1 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0 || $offset > 0) { $query .= ' LIMIT ' . $offset . ', ' . $limit; } return $query; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return FOFDatabaseQuerySqlite Returns this object to allow chaining. * * @since 12.1 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Add to the current date and time. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * * @param datetime $date The date or datetime to add to * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @since 13.1 * @link http://www.sqlite.org/lang_datefunc.html */ public function dateAdd($date, $interval, $datePart) { // SQLite does not support microseconds as a separate unit. Convert the interval to seconds if (strcasecmp($datePart, 'microseconds') == 0) { $interval = .001 * $interval; $datePart = 'seconds'; } if (substr($interval, 0, 1) != '-') { return "datetime('" . $date . "', '+" . $interval . " " . $datePart . "')"; } else { return "datetime('" . $date . "', '" . $interval . " " . $datePart . "')"; } } /** * Gets the current date and time. * * Usage: * $query->where('published_up < '.$query->currentTimestamp()); * * @return string * * @since 3.4 */ public function currentTimestamp() { return 'CURRENT_TIMESTAMP'; } } PK ! Q�\"