DatabaseManager.php
TLDR
This file, DatabaseManager.php
, is part of the Illuminate\Database
namespace in the Laravel framework. It contains the DatabaseManager
class, which is responsible for managing database connections in the application. The class implements the ConnectionResolverInterface
interface and includes methods for retrieving, creating, configuring, and disconnecting from database connections.
Methods
__construct
This method is the constructor of the DatabaseManager
class. It initializes the instance with the given application instance and a ConnectionFactory
object.
connection
This method returns a Connection
object for the specified database connection name. If the connection has not been created yet, it creates a new connection based on the configuration and sets event dispatchers if the application has an events
container.
parseConnectionName
This method parses the given connection name into an array of database name and read/write type.
makeConnection
This method creates a new Connection
object for the specified database connection name. It first checks if there is an extension registered for the connection or the driver, and if so, calls the corresponding closure to resolve the connection. If not, it creates a new connection using the ConnectionFactory
object.
configuration
This method retrieves the configuration for the specified database connection name. It retrieves the database.connections
configuration array from the application configuration and returns the configuration for the given name. If the configuration does not exist, it throws an InvalidArgumentException
.
configure
This method prepares the given Connection
object by setting the event dispatcher, transaction manager, reconnector callback, and registering custom Doctrine types.
setPdoForType
This method sets the PDO object for the specified mode (read or write) on the given Connection
object.
registerConfiguredDoctrineTypes
This method registers the custom Doctrine types specified in the application configuration with the given Connection
object.
registerDoctrineType
This method registers a custom Doctrine type with the given class, name, and type. It checks if Doctrine DBAL is installed, adds the type if it does not exist, and stores the type in the $doctrineTypes
property.
purge
This method disconnects from the specified database connection and removes it from the list of active connections.
disconnect
This method disconnects from the specified database connection.
reconnect
This method reconnects to the specified database connection by disconnecting and creating a new connection.
usingConnection
This method sets the default database connection to the specified name and executes the provided callback. After the callback is executed, it resets the default connection to the previous one.
refreshPdoConnections
This method refreshes the PDO connections on the specified connection by disconnecting and creating a new connection.
getDefaultConnection
This method returns the default database connection name.
setDefaultConnection
This method sets the default database connection name.
supportedDrivers
This method returns an array of supported database drivers.
availableDrivers
This method returns an array of available database drivers by intersecting the supported drivers with the ones available in the PDO.
extend
This method registers an extension connection resolver for the specified connection name.
forgetExtension
This method removes the extension connection resolver for the specified connection name.
getConnections
This method returns an array of all the created connections.
setReconnector
This method sets the database reconnector callback.
setApplication
This method sets the application instance used by the manager.
__call
This method is a magic method that allows calling methods on the default database connection. It first checks if the called method is a macro and handles it accordingly. If not, it delegates the method call to the default connection.
<?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);
}
}