297 lines
9.8 KiB
PHP
297 lines
9.8 KiB
PHP
<?php
|
|
// {{{ICINGA_LICENSE_HEADER}}}
|
|
/**
|
|
* This file is part of Icinga 2 Web.
|
|
*
|
|
* Icinga 2 Web - Head for multiple monitoring backends.
|
|
* Copyright (C) 2013 Icinga Development Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
* @author Icinga Development Team <info@icinga.org>
|
|
*/
|
|
// {{{ICINGA_LICENSE_HEADER}}}
|
|
|
|
namespace Icinga\Application;
|
|
|
|
use \PDO;
|
|
use \Zend_Config;
|
|
use \Zend_Db;
|
|
use \Zend_Db_Adapter_Abstract;
|
|
use \Icinga\Application\Logger;
|
|
use \Icinga\Util\ConfigAwareFactory;
|
|
use \Icinga\Exception\ConfigurationError;
|
|
use \Icinga\Exception\ProgrammingError;
|
|
|
|
/**
|
|
* Create resources using short identifiers referring to configuration entries
|
|
*/
|
|
class DbAdapterFactory implements ConfigAwareFactory
|
|
{
|
|
/**
|
|
* Resource definitions
|
|
*
|
|
* @var Zend_Config
|
|
*/
|
|
private static $resources;
|
|
|
|
/**
|
|
* The factory class used to create instances of Zend_Db_Adapter
|
|
*
|
|
* @var String
|
|
*/
|
|
private static $factoryClass = 'Zend_Db';
|
|
|
|
/**
|
|
* Resource cache to allow multiple use
|
|
*
|
|
* @var array
|
|
*/
|
|
private static $resourceCache = array();
|
|
|
|
/**
|
|
* Array of PDO driver options
|
|
*
|
|
* @see http://www.php.net/manual/en/pdo.constants.php
|
|
* @var array
|
|
*/
|
|
private static $defaultPdoDriverOptions = array(
|
|
PDO::ATTR_TIMEOUT => 2,
|
|
PDO::ATTR_CASE => PDO::CASE_LOWER
|
|
);
|
|
|
|
/**
|
|
* Array of Zend_Db adapter options
|
|
*
|
|
* @see http://framework.zend.com/manual/1.12/en/zend.db.html
|
|
* @var array
|
|
*/
|
|
private static $defaultZendDbAdapterOptions = array(
|
|
Zend_Db::AUTO_QUOTE_IDENTIFIERS => false,
|
|
Zend_Db::CASE_FOLDING => Zend_Db::CASE_LOWER,
|
|
Zend_Db::FETCH_MODE => Zend_Db::FETCH_OBJ
|
|
);
|
|
|
|
/**
|
|
* Set the configuration that stores the available resources
|
|
*
|
|
* @param mixed $config The configuration containing the resources
|
|
*
|
|
* @param array $options Additional options that affect the factories behaviour:
|
|
* * factory : Set the factory class that creates instances
|
|
* of Zend_Db_Adapter for the different database types
|
|
* (used for testing)
|
|
*/
|
|
public static function setConfig($config, array $options = null)
|
|
{
|
|
if (is_array($config)) {
|
|
$config = new Zend_Config($config);
|
|
}
|
|
self::$resources = $config;
|
|
if (isset($options['factory'])) {
|
|
self::$factoryClass = $options['factory'];
|
|
} else {
|
|
self::$factoryClass = 'Zend_Db';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the factory configuration back to the default state
|
|
*/
|
|
public static function resetConfig()
|
|
{
|
|
self::$resources = null;
|
|
self::$factoryClass = 'Zend_Db';
|
|
}
|
|
|
|
/**
|
|
* Get a list of all resources available to this factory
|
|
*
|
|
* @return array An array containing all resources compatible to this factory
|
|
*/
|
|
public static function getResources()
|
|
{
|
|
$resources = self::$resources->toArray();
|
|
foreach ($resources as $identifier => $resource) {
|
|
if ($resource['type'] !== 'db') {
|
|
unset($resources[$identifier]);
|
|
}
|
|
}
|
|
return $resources;
|
|
}
|
|
|
|
/**
|
|
* Return if a resource with the given identifier exists
|
|
*
|
|
* @param $identifier The name of the resource
|
|
*
|
|
* @return boolean If the resource exists and is compatible
|
|
*/
|
|
public static function resourceExists($identifier)
|
|
{
|
|
return isset(self::$resources->{$identifier})
|
|
&& (self::$resources->{$identifier}->type === 'db');
|
|
}
|
|
|
|
/**
|
|
* Get the resource with the given $identifier
|
|
*
|
|
* @param string $identifier The name of the resource
|
|
*
|
|
* @return Zend_Db_Adapter_Abstract
|
|
* @throws ConfigurationError
|
|
* @throws ProgrammingError
|
|
*/
|
|
public static function getDbAdapter($identifier)
|
|
{
|
|
if (!isset(self::$resources)) {
|
|
$msg = 'Creation of resource ' . $identifier . ' not possible, because there is no configuration present.'
|
|
. ' Make shure this factory class was initialised correctly during the application bootstrap.';
|
|
Logger::error($msg);
|
|
throw new ProgrammingError($msg);
|
|
}
|
|
if (!isset(self::$resources->{$identifier})) {
|
|
$msg = 'Creation of resource "'
|
|
. $identifier
|
|
. '" not possible, because there is no matching resource present in the configuration ';
|
|
Logger::error($msg);
|
|
throw new ConfigurationError($msg);
|
|
}
|
|
if (array_key_exists($identifier, self::$resourceCache)) {
|
|
return self::$resourceCache[$identifier];
|
|
} else {
|
|
$res = self::createDbAdapter(self::$resources->{$identifier});
|
|
self::$resourceCache[$identifier] = $res;
|
|
return $res;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a db adapter directly from a configuration instead of a resource identifier
|
|
*
|
|
* @param Zend_Config $config The resource configuration that will be used.
|
|
*
|
|
* @return Zend_Db_Adapter_Abstract The created DbAdapter
|
|
* @throws ProgrammingError
|
|
*/
|
|
public static function createDbAdapterFromConfig(Zend_Config $config)
|
|
{
|
|
return self::createDbAdapter($config);
|
|
}
|
|
|
|
/**
|
|
* Create the Db_Adapter for the given configuration section
|
|
*
|
|
* @param Zend_Config $config The configuration section containing the db information
|
|
*
|
|
* @return Zend_Db_Adapter_Abstract The created Zend_Db_Adapter
|
|
* @throws ConfigurationError When the specified db type is invalid
|
|
*/
|
|
public static function createDbAdapter(Zend_Config $config)
|
|
{
|
|
if ($config->type !== 'db') {
|
|
$msg = 'Resource type must be "db" but is "' . $config->type . '"';
|
|
Logger::error($msg);
|
|
throw new ConfigurationError($msg);
|
|
}
|
|
$options = array(
|
|
'dbname' => $config->dbname,
|
|
'host' => $config->host,
|
|
'username' => $config->username,
|
|
'password' => $config->password,
|
|
'options' => self::$defaultZendDbAdapterOptions,
|
|
'driver_options' => self::$defaultPdoDriverOptions
|
|
);
|
|
switch ($config->db) {
|
|
case 'mysql':
|
|
return self::callFactory('Pdo_Mysql', $options);
|
|
case 'pgsql':
|
|
return self::callFactory('Pdo_Pgsql', $options);
|
|
default:
|
|
if (!$config->db) {
|
|
$msg = 'Database type is missing (e.g. db=mysql).';
|
|
} else {
|
|
$msg = 'Unsupported db type ' . $config->db . '.';
|
|
}
|
|
Logger::error($msg);
|
|
throw new ConfigurationError($msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Call the currently set factory class
|
|
*
|
|
* @param string $adapter The name of the used db adapter
|
|
* @param mixed $options An array or Zend_Config object with adapter
|
|
* parameters
|
|
*
|
|
* @return Zend_Db_Adapter_Abstract The created adapter
|
|
*/
|
|
private static function callFactory($adapter, $options)
|
|
{
|
|
$factory = self::$factoryClass;
|
|
$optionModifierCallback = __CLASS__ . '::get' . ucfirst(str_replace('_', '', $adapter)) . 'Options';
|
|
if (is_callable($optionModifierCallback)) {
|
|
$options = call_user_func($optionModifierCallback, $options);
|
|
}
|
|
return $factory::factory($adapter, $options);
|
|
}
|
|
|
|
/**
|
|
* Get modified attributes for driver PDO_Mysql
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getPdoMysqlOptions(array $options)
|
|
{
|
|
/*
|
|
* Set MySQL server SQL modes to behave as closely as possible to Oracle and PostgreSQL. Note that the
|
|
* ONLY_FULL_GROUP_BY mode is left on purpose because MySQL requires you to specify all non-aggregate columns
|
|
* in the group by list even if the query is grouped by the master table's primary key which is valid
|
|
* ANSI SQL though. Further in that case the query plan would suffer if you add more columns to the group by
|
|
* list.
|
|
*/
|
|
$options['driver_options'][PDO::MYSQL_ATTR_INIT_COMMAND] =
|
|
'SET SESSION SQL_MODE=\'STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,'
|
|
. 'NO_AUTO_CREATE_USER,ANSI_QUOTES,PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\';';
|
|
|
|
if (!isset($options['port'])) {
|
|
$options['port'] = 3306;
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Get modified attributes for driver PDO_PGSQL
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getPdoPgsqlOptions(array $options)
|
|
{
|
|
if (!isset($options['port'])) {
|
|
$options['port'] = 5432;
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
}
|