master

laravel/framework

Last updated at: 29/12/2023 09:22

DbCommand.php

TLDR

The DbCommand.php file in the Illuminate\Database\Console namespace is a class that extends the Command class. It provides a console command to start a new database CLI session. The command accepts an optional argument for specifying the database connection and options for connecting to either a read or write connection.

Methods

handle

This method executes the console command. It retrieves the connection configuration and checks if the host is specified. If not, it displays an error message and instructions for using the --read and --write options. It then creates a new Process instance and runs the database client command with the appropriate arguments and environment variables. The output of the command is written to the console.

getConnection

This method retrieves the database connection configuration based on the provided connection argument or the default connection. It checks if the connection configuration is empty and throws an UnexpectedValueException if it is. It also parses the connection URL if present. If the --read option is used, it merges the read connection configuration. If the --write option is used, it merges the write connection configuration. The resulting connection configuration is returned.

commandArguments

This method takes the connection configuration as an argument and returns the arguments for the database client command based on the driver. The driver name is converted to uppercase and appended with "Arguments". For example, if the driver is "mysql", the method that will be called is getMysqlArguments.

commandEnvironment

This method takes the connection configuration as an argument and returns the environment variables for the database client command based on the driver. The driver name is converted to uppercase and appended with "Environment". For example, if the driver is "pgsql", the method that will be called is getPgsqlEnvironment.

getCommand

This method takes the connection configuration as an argument and returns the database client command based on the driver. It uses an array mapping of drivers to command names.

getMysqlArguments

This method takes the connection configuration as an argument and returns the arguments for the MySQL CLI. It merges the host, port, and username arguments, and includes optional arguments such as password, unix_socket, and charset.

getPgsqlArguments

This method takes the connection configuration as an argument and returns the arguments for the Postgres CLI. It only includes the database argument.

getSqliteArguments

This method takes the connection configuration as an argument and returns the arguments for the SQLite CLI. It only includes the database argument.

getSqlsrvArguments

This method takes the connection configuration as an argument and returns the arguments for the SQL Server CLI. It merges the optional arguments such as database, username, password, and host.

getPgsqlEnvironment

This method takes the connection configuration as an argument and returns the environment variables for the Postgres CLI. It merges the optional arguments such as username, host, port, and password.

getOptionalArguments

This method takes an array of arguments and the connection configuration as arguments and returns an array of optional arguments based on the connection configuration. It filters out the arguments that are empty.

<?php

namespace Illuminate\Database\Console;

use Illuminate\Console\Command;
use Illuminate\Support\ConfigurationUrlParser;
use Symfony\Component\Process\Process;
use UnexpectedValueException;

class DbCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'db {connection? : The database connection that should be used}
               {--read : Connect to the read connection}
               {--write : Connect to the write connection}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Start a new database CLI session';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $connection = $this->getConnection();

        if (! isset($connection['host']) && $connection['driver'] !== 'sqlite') {
            $this->components->error('No host specified for this database connection.');
            $this->line('  Use the <options=bold>[--read]</> and <options=bold>[--write]</> options to specify a read or write connection.');
            $this->newLine();

            return Command::FAILURE;
        }

        (new Process(
            array_merge([$this->getCommand($connection)], $this->commandArguments($connection)),
            null,
            $this->commandEnvironment($connection)
        ))->setTimeout(null)->setTty(true)->mustRun(function ($type, $buffer) {
            $this->output->write($buffer);
        });

        return 0;
    }

    /**
     * Get the database connection configuration.
     *
     * @return array
     *
     * @throws \UnexpectedValueException
     */
    public function getConnection()
    {
        $connection = $this->laravel['config']['database.connections.'.
            (($db = $this->argument('connection')) ?? $this->laravel['config']['database.default'])
        ];

        if (empty($connection)) {
            throw new UnexpectedValueException("Invalid database connection [{$db}].");
        }

        if (! empty($connection['url'])) {
            $connection = (new ConfigurationUrlParser)->parseConfiguration($connection);
        }

        if ($this->option('read')) {
            if (is_array($connection['read']['host'])) {
                $connection['read']['host'] = $connection['read']['host'][0];
            }

            $connection = array_merge($connection, $connection['read']);
        } elseif ($this->option('write')) {
            if (is_array($connection['write']['host'])) {
                $connection['write']['host'] = $connection['write']['host'][0];
            }

            $connection = array_merge($connection, $connection['write']);
        }

        return $connection;
    }

    /**
     * Get the arguments for the database client command.
     *
     * @param  array  $connection
     * @return array
     */
    public function commandArguments(array $connection)
    {
        $driver = ucfirst($connection['driver']);

        return $this->{"get{$driver}Arguments"}($connection);
    }

    /**
     * Get the environment variables for the database client command.
     *
     * @param  array  $connection
     * @return array|null
     */
    public function commandEnvironment(array $connection)
    {
        $driver = ucfirst($connection['driver']);

        if (method_exists($this, "get{$driver}Environment")) {
            return $this->{"get{$driver}Environment"}($connection);
        }

        return null;
    }

    /**
     * Get the database client command to run.
     *
     * @param  array  $connection
     * @return string
     */
    public function getCommand(array $connection)
    {
        return [
            'mysql' => 'mysql',
            'pgsql' => 'psql',
            'sqlite' => 'sqlite3',
            'sqlsrv' => 'sqlcmd',
        ][$connection['driver']];
    }

    /**
     * Get the arguments for the MySQL CLI.
     *
     * @param  array  $connection
     * @return array
     */
    protected function getMysqlArguments(array $connection)
    {
        return array_merge([
            '--host='.$connection['host'],
            '--port='.$connection['port'],
            '--user='.$connection['username'],
        ], $this->getOptionalArguments([
            'password' => '--password='.$connection['password'],
            'unix_socket' => '--socket='.($connection['unix_socket'] ?? ''),
            'charset' => '--default-character-set='.($connection['charset'] ?? ''),
        ], $connection), [$connection['database']]);
    }

    /**
     * Get the arguments for the Postgres CLI.
     *
     * @param  array  $connection
     * @return array
     */
    protected function getPgsqlArguments(array $connection)
    {
        return [$connection['database']];
    }

    /**
     * Get the arguments for the SQLite CLI.
     *
     * @param  array  $connection
     * @return array
     */
    protected function getSqliteArguments(array $connection)
    {
        return [$connection['database']];
    }

    /**
     * Get the arguments for the SQL Server CLI.
     *
     * @param  array  $connection
     * @return array
     */
    protected function getSqlsrvArguments(array $connection)
    {
        return array_merge(...$this->getOptionalArguments([
            'database' => ['-d', $connection['database']],
            'username' => ['-U', $connection['username']],
            'password' => ['-P', $connection['password']],
            'host' => ['-S', 'tcp:'.$connection['host']
                        .($connection['port'] ? ','.$connection['port'] : ''), ],
        ], $connection));
    }

    /**
     * Get the environment variables for the Postgres CLI.
     *
     * @param  array  $connection
     * @return array|null
     */
    protected function getPgsqlEnvironment(array $connection)
    {
        return array_merge(...$this->getOptionalArguments([
            'username' => ['PGUSER' => $connection['username']],
            'host' => ['PGHOST' => $connection['host']],
            'port' => ['PGPORT' => $connection['port']],
            'password' => ['PGPASSWORD' => $connection['password']],
        ], $connection));
    }

    /**
     * Get the optional arguments based on the connection configuration.
     *
     * @param  array  $args
     * @param  array  $connection
     * @return array
     */
    protected function getOptionalArguments(array $args, array $connection)
    {
        return array_values(array_filter($args, function ($key) use ($connection) {
            return ! empty($connection[$key]);
        }, ARRAY_FILTER_USE_KEY));
    }
}