221 lines
7.3 KiB
PHP
221 lines
7.3 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* 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; under version 2
|
||
|
* of the License (non-upgradable).
|
||
|
*
|
||
|
* 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 (c) 2019-2020 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
|
||
|
*/
|
||
|
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
namespace oat\generis\persistence;
|
||
|
|
||
|
use oat\oatbox\service\ConfigurableService;
|
||
|
use oat\generis\persistence\sql\SchemaCollection;
|
||
|
use common_persistence_SqlPersistence;
|
||
|
use oat\generis\persistence\sql\SchemaProviderInterface;
|
||
|
use oat\oatbox\service\ServiceNotFoundException;
|
||
|
|
||
|
/**
|
||
|
* The PersistenceManager is responsible for initializing all persistences
|
||
|
*
|
||
|
* @author Lionel Lecaque <lionel@taotesting.com>
|
||
|
* @license GPLv2
|
||
|
*/
|
||
|
class PersistenceManager extends ConfigurableService
|
||
|
{
|
||
|
|
||
|
public const SERVICE_ID = 'generis/persistences';
|
||
|
|
||
|
public const OPTION_PERSISTENCES = 'persistences';
|
||
|
|
||
|
/**
|
||
|
* Mapping of drivers to implementations.
|
||
|
* All SQL drivers except 'dbal' are deprecated
|
||
|
* @var array
|
||
|
*/
|
||
|
private const DRIVER_MAP = [
|
||
|
'dbal' => 'common_persistence_sql_dbal_Driver',
|
||
|
'dbal_pdo_mysql' => 'common_persistence_sql_dbal_Driver',
|
||
|
'dbal_pdo_sqlite' => 'common_persistence_sql_dbal_Driver',
|
||
|
'dbal_pdo_pgsql' => 'common_persistence_sql_dbal_Driver',
|
||
|
'dbal_pdo_ibm' => 'common_persistence_sql_dbal_Driver',
|
||
|
'phpredis' => 'common_persistence_PhpRedisDriver',
|
||
|
'phpfile' => 'common_persistence_PhpFileDriver',
|
||
|
'SqlKvWrapper' => 'common_persistence_SqlKvDriver',
|
||
|
'no_storage' => 'common_persistence_InMemoryKvDriver',
|
||
|
'no_storage_adv' => 'common_persistence_InMemoryAdvKvDriver'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private $persistences = [];
|
||
|
|
||
|
/**
|
||
|
* Returns TRUE if the requested persistence exist, otherwise FALSE.
|
||
|
*
|
||
|
* @param string $persistenceId
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function hasPersistence($persistenceId)
|
||
|
{
|
||
|
$persistenceList = $this->getOption(static::OPTION_PERSISTENCES);
|
||
|
return isset($persistenceList[$persistenceId]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers a new persistence.
|
||
|
*
|
||
|
* @param string $persistenceId
|
||
|
* @param array $persistenceConf
|
||
|
*/
|
||
|
public function registerPersistence($persistenceId, array $persistenceConf)
|
||
|
{
|
||
|
// wrap pdo drivers in dbal
|
||
|
if (strpos($persistenceConf['driver'], 'pdo_') === 0) {
|
||
|
$persistenceConf = [
|
||
|
'driver' => 'dbal',
|
||
|
'connection' => $persistenceConf
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
isset($persistenceConf['connection']['driver'])
|
||
|
&& $persistenceConf['connection']['driver'] === 'pdo_mysql'
|
||
|
) {
|
||
|
$persistenceConf['connection']['charset'] = 'utf8';
|
||
|
}
|
||
|
|
||
|
$configs = $this->getOption(self::OPTION_PERSISTENCES);
|
||
|
$configs[$persistenceId] = $persistenceConf;
|
||
|
$this->setOption(self::OPTION_PERSISTENCES, $configs);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @return \common_persistence_Persistence
|
||
|
*/
|
||
|
public function getPersistenceById($persistenceId)
|
||
|
{
|
||
|
if (!isset($this->persistences[$persistenceId])) {
|
||
|
$this->persistences[$persistenceId] = $this->createPersistence($persistenceId);
|
||
|
}
|
||
|
return $this->persistences[$persistenceId];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param string $persistenceId
|
||
|
* @return \common_persistence_Persistence
|
||
|
* @throws \common_Exception
|
||
|
*/
|
||
|
private function createPersistence($persistenceId)
|
||
|
{
|
||
|
$configs = $this->getOption(self::OPTION_PERSISTENCES);
|
||
|
if (!isset($configs[$persistenceId])) {
|
||
|
throw new \common_Exception('Persistence Configuration for persistence ' . $persistenceId . ' not found');
|
||
|
}
|
||
|
$config = $configs[$persistenceId];
|
||
|
$driverString = $config['driver'];
|
||
|
|
||
|
$driverClassName = isset(self::DRIVER_MAP[$driverString]) ? self::DRIVER_MAP[$driverString] : $driverString;
|
||
|
|
||
|
if (!class_exists($driverClassName)) {
|
||
|
throw new \common_exception_Error(
|
||
|
'Driver ' . $driverString . ' not found, check your database configuration'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$driver = $this->propagate(new $driverClassName());
|
||
|
|
||
|
$driverOptionsFeeder = $this->getDriverConfigFeeder();
|
||
|
|
||
|
if ($driverOptionsFeeder !== null) {
|
||
|
$config = $driverOptionsFeeder->feed($config);
|
||
|
}
|
||
|
|
||
|
return $driver->connect($persistenceId, $config);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a collection of all SQL schemas
|
||
|
*/
|
||
|
public function getSqlSchemas()
|
||
|
{
|
||
|
$schemas = new SchemaCollection();
|
||
|
foreach (array_keys($this->getOption(self::OPTION_PERSISTENCES)) as $id) {
|
||
|
$persistence = $this->getPersistenceById($id);
|
||
|
if ($persistence instanceof common_persistence_SqlPersistence) {
|
||
|
$schemas->addSchema($id, $persistence->getSchemaManager()->createSchema());
|
||
|
}
|
||
|
}
|
||
|
return $schemas;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adapt the databases to the SQL schemas
|
||
|
*/
|
||
|
public function applySchemas(SchemaCollection $schemaCollection)
|
||
|
{
|
||
|
$this->logInfo('Applying schame changes');
|
||
|
foreach ($schemaCollection as $id => $schema) {
|
||
|
$persistence = $this->getPersistenceById($id);
|
||
|
$fromSchema = $schemaCollection->getOriginalSchema($id);
|
||
|
$queries = $persistence->getPlatForm()->getMigrateSchemaSql($fromSchema, $schema);
|
||
|
foreach ($queries as $query) {
|
||
|
$persistence->exec($query);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add the schema of a single service, if needed
|
||
|
*/
|
||
|
public function applySchemaProvider($service): void
|
||
|
{
|
||
|
if ($service instanceof SchemaProviderInterface) {
|
||
|
$schemaCollection = $this->getSqlSchemas();
|
||
|
$service->provideSchema($schemaCollection);
|
||
|
$this->applySchemas($schemaCollection);
|
||
|
$this->logInfo('Applied schema for ' . get_class($service));
|
||
|
} else {
|
||
|
$this->logDebug('No schema found for ' . get_class($service));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function getDriverConfigFeeder(): ?DriverConfigurationFeeder
|
||
|
{
|
||
|
/**
|
||
|
* This class is directly instantiated directly in some installation process, so the ServiceLocator
|
||
|
* might not be available, therefore this if is necessary.
|
||
|
*/
|
||
|
if (!$this->getServiceLocator()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
return $this->getServiceLocator()->get(DriverConfigurationFeeder::SERVICE_ID);
|
||
|
} catch (ServiceNotFoundException $exception) {
|
||
|
/**
|
||
|
* This is because PersistenceManager is registered in tao-core and while doing the generis update
|
||
|
* before the update of tao (which had this class in the migrations).
|
||
|
*/
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|