Viewing file: DatabaseManager.php (12.78 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
namespace Illuminate\Database;
use Doctrine\DBAL\Types\Type; use Illuminate\Database\Connectors\ConnectionFactory; use Illuminate\Database\Events\ConnectionEstablished; use Illuminate\Support\Arr; use Illuminate\Support\ConfigurationUrlParser; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; use PDO; use RuntimeException;
/** * @mixin \Illuminate\Database\Connection */ class DatabaseManager implements ConnectionResolverInterface { use Macroable { __call as macroCall; }
/** * The application instance. * * @var \Illuminate\Contracts\Foundation\Application */ protected $app;
/** * The database connection factory instance. * * @var \Illuminate\Database\Connectors\ConnectionFactory */ protected $factory;
/** * The active connection instances. * * @var array<string, \Illuminate\Database\Connection> */ protected $connections = [];
/** * The custom connection resolvers. * * @var array<string, callable> */ protected $extensions = [];
/** * The callback to be executed to reconnect to a database. * * @var callable */ protected $reconnector;
/** * The custom Doctrine column types. * * @var array<string, array> */ protected $doctrineTypes = [];
/** * Create a new database manager instance. * * @param \Illuminate\Contracts\Foundation\Application $app * @param \Illuminate\Database\Connectors\ConnectionFactory $factory * @return void */ public function __construct($app, ConnectionFactory $factory) { $this->app = $app; $this->factory = $factory;
$this->reconnector = function ($connection) { $this->reconnect($connection->getNameWithReadWriteType()); }; }
/** * Get a database connection instance. * * @param string|null $name * @return \Illuminate\Database\Connection */ public function connection($name = null) { [$database, $type] = $this->parseConnectionName($name);
$name = $name ?: $database;
// If we haven't created this connection, we'll create it based on the config // provided in the application. Once we've created the connections we will // set the "fetch mode" for PDO which determines the query return types. if (! isset($this->connections[$name])) { $this->connections[$name] = $this->configure( $this->makeConnection($database), $type );
if ($this->app->bound('events')) { $this->app['events']->dispatch( new ConnectionEstablished($this->connections[$name]) ); } }
return $this->connections[$name]; }
/** * Parse the connection into an array of the name and read / write type. * * @param string $name * @return array */ protected function parseConnectionName($name) { $name = $name ?: $this->getDefaultConnection();
return Str::endsWith($name, ['::read', '::write']) ? explode('::', $name, 2) : [$name, null]; }
/** * Make the database connection instance. * * @param string $name * @return \Illuminate\Database\Connection */ protected function makeConnection($name) { $config = $this->configuration($name);
// First we will check by the connection name to see if an extension has been // registered specifically for that connection. If it has we will call the // Closure and pass it the config allowing it to resolve the connection. if (isset($this->extensions[$name])) { return call_user_func($this->extensions[$name], $config, $name); }
// Next we will check to see if an extension has been registered for a driver // and will call the Closure if so, which allows us to have a more generic // resolver for the drivers themselves which applies to all connections. if (isset($this->extensions[$driver = $config['driver']])) { return call_user_func($this->extensions[$driver], $config, $name); }
return $this->factory->make($config, $name); }
/** * Get the configuration for a connection. * * @param string $name * @return array * * @throws \InvalidArgumentException */ protected function configuration($name) { $name = $name ?: $this->getDefaultConnection();
// To get the database connection configuration, we will just pull each of the // connection configurations and get the configurations for the given name. // If the configuration doesn't exist, we'll throw an exception and bail. $connections = $this->app['config']['database.connections'];
if (is_null($config = Arr::get($connections, $name))) { throw new InvalidArgumentException("Database connection [{$name}] not configured."); }
return (new ConfigurationUrlParser) ->parseConfiguration($config); }
/** * Prepare the database connection instance. * * @param \Illuminate\Database\Connection $connection * @param string $type * @return \Illuminate\Database\Connection */ protected function configure(Connection $connection, $type) { $connection = $this->setPdoForType($connection, $type)->setReadWriteType($type);
// First we'll set the fetch mode and a few other dependencies of the database // connection. This method basically just configures and prepares it to get // used by the application. Once we're finished we'll return it back out. if ($this->app->bound('events')) { $connection->setEventDispatcher($this->app['events']); }
if ($this->app->bound('db.transactions')) { $connection->setTransactionManager($this->app['db.transactions']); }
// Here we'll set a reconnector callback. This reconnector can be any callable // so we will set a Closure to reconnect from this manager with the name of // the connection, which will allow us to reconnect from the connections. $connection->setReconnector($this->reconnector);
$this->registerConfiguredDoctrineTypes($connection);
return $connection; }
/** * Prepare the read / write mode for database connection instance. * * @param \Illuminate\Database\Connection $connection * @param string|null $type * @return \Illuminate\Database\Connection */ protected function setPdoForType(Connection $connection, $type = null) { if ($type === 'read') { $connection->setPdo($connection->getReadPdo()); } elseif ($type === 'write') { $connection->setReadPdo($connection->getPdo()); }
return $connection; }
/** * Register custom Doctrine types with the connection. * * @param \Illuminate\Database\Connection $connection * @return void */ protected function registerConfiguredDoctrineTypes(Connection $connection): void { foreach ($this->app['config']->get('database.dbal.types', []) as $name => $class) { $this->registerDoctrineType($class, $name, $name); }
foreach ($this->doctrineTypes as $name => [$type, $class]) { $connection->registerDoctrineType($class, $name, $type); } }
/** * Register a custom Doctrine type. * * @param string $class * @param string $name * @param string $type * @return void * * @throws \Doctrine\DBAL\Exception * @throws \RuntimeException */ public function registerDoctrineType(string $class, string $name, string $type): void { if (! class_exists('Doctrine\DBAL\Connection')) { throw new RuntimeException( 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' ); }
if (! Type::hasType($name)) { Type::addType($name, $class); }
$this->doctrineTypes[$name] = [$type, $class]; }
/** * Disconnect from the given database and remove from local cache. * * @param string|null $name * @return void */ public function purge($name = null) { $name = $name ?: $this->getDefaultConnection();
$this->disconnect($name);
unset($this->connections[$name]); }
/** * Disconnect from the given database. * * @param string|null $name * @return void */ public function disconnect($name = null) { if (isset($this->connections[$name = $name ?: $this->getDefaultConnection()])) { $this->connections[$name]->disconnect(); } }
/** * Reconnect to the given database. * * @param string|null $name * @return \Illuminate\Database\Connection */ public function reconnect($name = null) { $this->disconnect($name = $name ?: $this->getDefaultConnection());
if (! isset($this->connections[$name])) { return $this->connection($name); }
return $this->refreshPdoConnections($name); }
/** * Set the default database connection for the callback execution. * * @param string $name * @param callable $callback * @return mixed */ public function usingConnection($name, callable $callback) { $previousName = $this->getDefaultConnection();
$this->setDefaultConnection($name);
return tap($callback(), function () use ($previousName) { $this->setDefaultConnection($previousName); }); }
/** * Refresh the PDO connections on a given connection. * * @param string $name * @return \Illuminate\Database\Connection */ protected function refreshPdoConnections($name) { [$database, $type] = $this->parseConnectionName($name);
$fresh = $this->configure( $this->makeConnection($database), $type );
return $this->connections[$name] ->setPdo($fresh->getRawPdo()) ->setReadPdo($fresh->getRawReadPdo()); }
/** * Get the default connection name. * * @return string */ public function getDefaultConnection() { return $this->app['config']['database.default']; }
/** * Set the default connection name. * * @param string $name * @return void */ public function setDefaultConnection($name) { $this->app['config']['database.default'] = $name; }
/** * Get all of the support drivers. * * @return string[] */ public function supportedDrivers() { return ['mysql', 'pgsql', 'sqlite', 'sqlsrv']; }
/** * Get all of the drivers that are actually available. * * @return string[] */ public function availableDrivers() { return array_intersect( $this->supportedDrivers(), str_replace('dblib', 'sqlsrv', PDO::getAvailableDrivers()) ); }
/** * Register an extension connection resolver. * * @param string $name * @param callable $resolver * @return void */ public function extend($name, callable $resolver) { $this->extensions[$name] = $resolver; }
/** * Remove an extension connection resolver. * * @param string $name * @return void */ public function forgetExtension($name) { unset($this->extensions[$name]); }
/** * Return all of the created connections. * * @return array<string, \Illuminate\Database\Connection> */ public function getConnections() { return $this->connections; }
/** * Set the database reconnector callback. * * @param callable $reconnector * @return void */ public function setReconnector(callable $reconnector) { $this->reconnector = $reconnector; }
/** * Set the application instance used by the manager. * * @param \Illuminate\Contracts\Foundation\Application $app * @return $this */ public function setApplication($app) { $this->app = $app;
return $this; }
/** * Dynamically pass methods to the default connection. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); }
return $this->connection()->$method(...$parameters); } }
|