Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
<?php
namespace Doctrine\DBAL\Tools\Console\Command;
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\PDO\Statement as PDOStatement;
use InvalidArgumentException;
use PDOException;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function assert;
use function error_get_last;
use function file_exists;
use function file_get_contents;
use function is_readable;
use function realpath;
use function sprintf;
use const PHP_EOL;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
* the command line.
*
* @deprecated Use a database client application instead
*/
class ImportCommand extends Command
{
/** @return void */
protected function configure()
{
$this
->setName('dbal:import')
->setDescription('Import SQL file(s) directly to Database.')
->setDefinition([new InputArgument(
'file',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'File path(s) of SQL to be executed.'
),
])
->setHelp(<<<EOT
Import SQL file(s) directly to Database.
EOT
);
}
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$conn = $this->getHelper('db')->getConnection();
$fileNames = $input->getArgument('file');
if ($fileNames === null) {
return 0;
}
foreach ((array) $fileNames as $fileName) {
$filePath = realpath($fileName);
// Phar compatibility.
if ($filePath === false) {
$filePath = $fileName;
}
if (! file_exists($filePath)) {
throw new InvalidArgumentException(
sprintf("SQL file '<info>%s</info>' does not exist.", $filePath)
);
}
if (! is_readable($filePath)) {
throw new InvalidArgumentException(
sprintf("SQL file '<info>%s</info>' does not have read permissions.", $filePath)
);
}
$output->write(sprintf("Processing file '<info>%s</info>'... ", $filePath));
$sql = @file_get_contents($filePath);
if ($sql === false) {
$message = sprintf("Unable to read SQL file '<info>%s</info>'", $filePath);
$error = error_get_last();
if ($error !== null) {
$message .= ': ' . $error['message'];
}
throw new RuntimeException($message);
}
if ($conn instanceof PDOConnection) {
// PDO Drivers
try {
$lines = 0;
$stmt = $conn->prepare($sql);
assert($stmt instanceof PDOStatement);
$stmt->execute();
do {
// Required due to "MySQL has gone away!" issue
$stmt->fetch();
$stmt->closeCursor();
$lines++;
} while ($stmt->nextRowset());
$output->write(sprintf('%d statements executed!', $lines) . PHP_EOL);
} catch (PDOException $e) {
$output->write('error!' . PHP_EOL);
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}
} else {
// Non-PDO Drivers (ie. OCI8 driver)
$stmt = $conn->prepare($sql);
$rs = $stmt->execute();
if (! $rs) {
$error = $stmt->errorInfo();
$output->write('error!' . PHP_EOL);
throw new RuntimeException($error[2], $error[0]);
}
$output->writeln('OK!' . PHP_EOL);
$stmt->closeCursor();
}
}
return 0;
}
}

View File

@@ -0,0 +1,244 @@
<?php
namespace Doctrine\DBAL\Tools\Console\Command;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\Keywords\DB2Keywords;
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
use Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQL80Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQLKeywords;
use Doctrine\DBAL\Platforms\Keywords\OracleKeywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords;
use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator;
use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords;
use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\Deprecations\Deprecation;
use Exception;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use function array_keys;
use function assert;
use function count;
use function implode;
use function is_array;
use function is_string;
class ReservedWordsCommand extends Command
{
/** @var array<string,class-string<KeywordList>> */
private $keywordListClasses = [
'mysql' => MySQLKeywords::class,
'mysql57' => MySQL57Keywords::class,
'mysql80' => MySQL80Keywords::class,
'sqlserver' => SQLServerKeywords::class,
'sqlserver2005' => SQLServer2005Keywords::class,
'sqlserver2008' => SQLServer2008Keywords::class,
'sqlserver2012' => SQLServer2012Keywords::class,
'sqlite' => SQLiteKeywords::class,
'pgsql' => PostgreSQLKeywords::class,
'pgsql91' => PostgreSQL91Keywords::class,
'pgsql92' => PostgreSQL92Keywords::class,
'oracle' => OracleKeywords::class,
'db2' => DB2Keywords::class,
'sqlanywhere' => SQLAnywhereKeywords::class,
'sqlanywhere11' => SQLAnywhere11Keywords::class,
'sqlanywhere12' => SQLAnywhere12Keywords::class,
'sqlanywhere16' => SQLAnywhere16Keywords::class,
];
/** @var ConnectionProvider|null */
private $connectionProvider;
public function __construct(?ConnectionProvider $connectionProvider = null)
{
parent::__construct();
$this->connectionProvider = $connectionProvider;
if ($connectionProvider !== null) {
return;
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3956',
'Not passing a connection provider as the first constructor argument is deprecated'
);
}
/**
* If you want to add or replace a keywords list use this command.
*
* @param string $name
* @param class-string<KeywordList> $class
*
* @return void
*/
public function setKeywordListClass($name, $class)
{
$this->keywordListClasses[$name] = $class;
}
/** @return void */
protected function configure()
{
$this
->setName('dbal:reserved-words')
->setDescription('Checks if the current database contains identifiers that are reserved.')
->setDefinition([
new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'),
new InputOption(
'list',
'l',
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Keyword-List name.'
),
])
->setHelp(<<<EOT
Checks if the current database contains tables and columns
with names that are identifiers in this dialect or in other SQL dialects.
By default SQLite, MySQL, PostgreSQL, Microsoft SQL Server, Oracle
and SQL Anywhere keywords are checked:
<info>%command.full_name%</info>
If you want to check against specific dialects you can
pass them to the command:
<info>%command.full_name% -l mysql -l pgsql</info>
The following keyword lists are currently shipped with Doctrine:
* mysql
* mysql57
* mysql80
* pgsql
* pgsql92
* sqlite
* oracle
* sqlserver
* sqlserver2005
* sqlserver2008
* sqlserver2012
* sqlanywhere
* sqlanywhere11
* sqlanywhere12
* sqlanywhere16
* db2 (Not checked by default)
EOT
);
}
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$conn = $this->getConnection($input);
$keywordLists = $input->getOption('list');
if (is_string($keywordLists)) {
$keywordLists = [$keywordLists];
} elseif (! is_array($keywordLists)) {
$keywordLists = [];
}
if (! $keywordLists) {
$keywordLists = [
'mysql',
'mysql57',
'mysql80',
'pgsql',
'pgsql92',
'sqlite',
'oracle',
'sqlserver',
'sqlserver2005',
'sqlserver2008',
'sqlserver2012',
'sqlanywhere',
'sqlanywhere11',
'sqlanywhere12',
'sqlanywhere16',
];
}
$keywords = [];
foreach ($keywordLists as $keywordList) {
if (! isset($this->keywordListClasses[$keywordList])) {
throw new InvalidArgumentException(
"There exists no keyword list with name '" . $keywordList . "'. " .
'Known lists: ' . implode(', ', array_keys($this->keywordListClasses))
);
}
$class = $this->keywordListClasses[$keywordList];
$keywords[] = new $class();
}
$output->write(
'Checking keyword violations for <comment>' . implode(', ', $keywordLists) . '</comment>...',
true
);
$schema = $conn->getSchemaManager()->createSchema();
$visitor = new ReservedKeywordsValidator($keywords);
$schema->visit($visitor);
$violations = $visitor->getViolations();
if (count($violations) !== 0) {
$output->write(
'There are <error>' . count($violations) . '</error> reserved keyword violations'
. ' in your database schema:',
true
);
foreach ($violations as $violation) {
$output->write(' - ' . $violation, true);
}
return 1;
}
$output->write('No reserved keywords violations have been found!', true);
return 0;
}
private function getConnection(InputInterface $input): Connection
{
$connectionName = $input->getOption('connection');
assert(is_string($connectionName) || $connectionName === null);
if ($this->connectionProvider === null) {
if ($connectionName !== null) {
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
}
return $this->getHelper('db')->getConnection();
}
if ($connectionName !== null) {
return $this->connectionProvider->getConnection($connectionName);
}
return $this->connectionProvider->getDefaultConnection();
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Doctrine\DBAL\Tools\Console\Command;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\DBAL\Tools\Dumper;
use Doctrine\Deprecations\Deprecation;
use Exception;
use LogicException;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use function assert;
use function is_numeric;
use function is_string;
use function stripos;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
* the command line.
*/
class RunSqlCommand extends Command
{
/** @var ConnectionProvider|null */
private $connectionProvider;
public function __construct(?ConnectionProvider $connectionProvider = null)
{
parent::__construct();
$this->connectionProvider = $connectionProvider;
if ($connectionProvider !== null) {
return;
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3956',
'Not passing a connection provider as the first constructor argument is deprecated'
);
}
/** @return void */
protected function configure()
{
$this
->setName('dbal:run-sql')
->setDescription('Executes arbitrary SQL directly from the command line.')
->setDefinition([
new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'),
new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'),
new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', '7'),
new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'),
])
->setHelp(<<<EOT
The <info>%command.name%</info> command executes the given SQL query and
outputs the results:
<info>php %command.full_name% "SELECT * FROM users"</info>
EOT
);
}
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$conn = $this->getConnection($input);
$sql = $input->getArgument('sql');
if ($sql === null) {
throw new RuntimeException("Argument 'SQL' is required in order to execute this command correctly.");
}
assert(is_string($sql));
$depth = $input->getOption('depth');
if (! is_numeric($depth)) {
throw new LogicException("Option 'depth' must contains an integer value");
}
if (stripos($sql, 'select') === 0 || $input->getOption('force-fetch')) {
$resultSet = $conn->fetchAllAssociative($sql);
} else {
$resultSet = $conn->executeStatement($sql);
}
$output->write(Dumper::dump($resultSet, (int) $depth));
return 0;
}
private function getConnection(InputInterface $input): Connection
{
$connectionName = $input->getOption('connection');
assert(is_string($connectionName) || $connectionName === null);
if ($this->connectionProvider === null) {
if ($connectionName !== null) {
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
}
return $this->getHelper('db')->getConnection();
}
if ($connectionName !== null) {
return $this->connectionProvider->getConnection($connectionName);
}
return $this->connectionProvider->getDefaultConnection();
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Tools\Console;
use OutOfBoundsException;
final class ConnectionNotFound extends OutOfBoundsException
{
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\DBAL\Tools\Console;
use Doctrine\DBAL\Connection;
interface ConnectionProvider
{
public function getDefaultConnection(): Connection;
/**
* @throws ConnectionNotFound in case a connection with the given name does not exist.
*/
public function getConnection(string $name): Connection;
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Tools\Console\ConnectionNotFound;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use function sprintf;
class SingleConnectionProvider implements ConnectionProvider
{
/** @var Connection */
private $connection;
/** @var string */
private $defaultConnectionName;
public function __construct(Connection $connection, string $defaultConnectionName = 'default')
{
$this->connection = $connection;
$this->defaultConnectionName = $defaultConnectionName;
}
public function getDefaultConnection(): Connection
{
return $this->connection;
}
public function getConnection(string $name): Connection
{
if ($name !== $this->defaultConnectionName) {
throw new ConnectionNotFound(sprintf('Connection with name "%s" does not exist.', $name));
}
return $this->connection;
}
}

View File

@@ -0,0 +1,118 @@
<?php
namespace Doctrine\DBAL\Tools\Console;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand;
use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\DBAL\Version;
use Doctrine\Deprecations\Deprecation;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\HelperSet;
use TypeError;
use function sprintf;
/**
* Handles running the Console Tools inside Symfony Console context.
*/
class ConsoleRunner
{
/**
* Create a Symfony Console HelperSet
*
* @deprecated use a ConnectionProvider instead.
*
* @return HelperSet
*/
public static function createHelperSet(Connection $connection)
{
return new HelperSet([
'db' => new ConnectionHelper($connection),
]);
}
/**
* Runs console with the given connection provider or helperset (deprecated).
*
* @param ConnectionProvider|HelperSet $helperSetOrConnectionProvider
* @param Command[] $commands
*
* @return void
*/
public static function run($helperSetOrConnectionProvider, $commands = [])
{
$cli = new Application('Doctrine Command Line Interface', Version::VERSION);
$cli->setCatchExceptions(true);
$connectionProvider = null;
if ($helperSetOrConnectionProvider instanceof HelperSet) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3956',
'Passing an instance of "%s" as the first argument is deprecated. Pass an instance of "%s" instead.',
HelperSet::class,
ConnectionProvider::class
);
$connectionProvider = null;
$cli->setHelperSet($helperSetOrConnectionProvider);
} elseif ($helperSetOrConnectionProvider instanceof ConnectionProvider) {
$connectionProvider = $helperSetOrConnectionProvider;
} else {
throw new TypeError(sprintf(
'First argument must be an instance of "%s" or "%s"',
HelperSet::class,
ConnectionProvider::class
));
}
self::addCommands($cli, $connectionProvider);
$cli->addCommands($commands);
$cli->run();
}
/**
* @return void
*/
public static function addCommands(Application $cli, ?ConnectionProvider $connectionProvider = null)
{
$cli->addCommands([
new RunSqlCommand($connectionProvider),
new ImportCommand(),
new ReservedWordsCommand($connectionProvider),
]);
}
/**
* Prints the instructions to create a configuration file
*
* @return void
*/
public static function printCliConfigTemplate()
{
echo <<<'HELP'
You are missing a "cli-config.php" or "config/cli-config.php" file in your
project, which is required to get the Doctrine-DBAL Console working. You can use the
following sample as a template:
<?php
use Doctrine\DBAL\Tools\Console\ConnectionProvider\SingleConnectionProvider;
// You can append new commands to $commands array, if needed
// replace with the mechanism to retrieve DBAL connection(s) in your app
// and return a Doctrine\DBAL\Tools\Console\ConnectionProvider instance.
$connection = getDBALConnection();
// in case you have a single connection you can use SingleConnectionProvider
// otherwise you need to implement the Doctrine\DBAL\Tools\Console\ConnectionProvider interface with your custom logic
return new SingleConnectionProvider($connection);
HELP;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Doctrine\DBAL\Tools\Console\Helper;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Helper\Helper;
/**
* Doctrine CLI Connection Helper.
*
* @deprecated use a ConnectionProvider instead.
*/
class ConnectionHelper extends Helper
{
/**
* The Doctrine database Connection.
*
* @var Connection
*/
protected $_connection;
/**
* @param Connection $connection The Doctrine database Connection.
*/
public function __construct(Connection $connection)
{
$this->_connection = $connection;
}
/**
* Retrieves the Doctrine database Connection.
*
* @return Connection
*/
public function getConnection()
{
return $this->_connection;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'connection';
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace Doctrine\DBAL\Tools;
use ArrayIterator;
use ArrayObject;
use DateTimeInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Persistence\Proxy;
use stdClass;
use function array_keys;
use function assert;
use function class_exists;
use function count;
use function end;
use function explode;
use function extension_loaded;
use function get_class;
use function html_entity_decode;
use function ini_set;
use function is_array;
use function is_object;
use function is_string;
use function ob_get_clean;
use function ob_start;
use function strip_tags;
use function strlen;
use function strrpos;
use function substr;
use function var_dump;
/**
* Static class used to dump the variable to be used on output.
* Simplified port of Util\Debug from doctrine/common.
*
* @internal
*/
final class Dumper
{
/**
* Private constructor (prevents instantiation).
*
* @codeCoverageIgnore
*/
private function __construct()
{
}
/**
* Returns a dump of the public, protected and private properties of $var.
*
* @link https://xdebug.org/
*
* @param mixed $var The variable to dump.
* @param int $maxDepth The maximum nesting level for object properties.
*/
public static function dump($var, int $maxDepth = 2): string
{
$html = ini_set('html_errors', '1');
assert(is_string($html));
if (extension_loaded('xdebug')) {
ini_set('xdebug.var_display_max_depth', (string) $maxDepth);
}
$var = self::export($var, $maxDepth);
ob_start();
var_dump($var);
try {
$output = ob_get_clean();
assert(is_string($output));
return strip_tags(html_entity_decode($output));
} finally {
ini_set('html_errors', $html);
}
}
/**
* @param mixed $var
*
* @return mixed
*/
public static function export($var, int $maxDepth)
{
$return = null;
$isObj = is_object($var);
if ($var instanceof Collection) {
$var = $var->toArray();
}
if ($maxDepth === 0) {
return is_object($var) ? get_class($var)
: (is_array($var) ? 'Array(' . count($var) . ')' : $var);
}
if (is_array($var)) {
$return = [];
foreach ($var as $k => $v) {
$return[$k] = self::export($v, $maxDepth - 1);
}
return $return;
}
if (! $isObj) {
return $var;
}
$return = new stdClass();
if ($var instanceof DateTimeInterface) {
$return->__CLASS__ = get_class($var);
$return->date = $var->format('c');
$return->timezone = $var->getTimezone()->getName();
return $return;
}
$return->__CLASS__ = self::getClass($var);
if ($var instanceof Proxy) {
$return->__IS_PROXY__ = true;
$return->__PROXY_INITIALIZED__ = $var->__isInitialized();
}
if ($var instanceof ArrayObject || $var instanceof ArrayIterator) {
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);
}
return self::fillReturnWithClassAttributes($var, $return, $maxDepth);
}
/**
* Fill the $return variable with class attributes
* Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075}
*
* @param object $var
*
* @return mixed
*/
private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth)
{
$clone = (array) $var;
foreach (array_keys($clone) as $key) {
$aux = explode("\0", $key);
$name = end($aux);
if ($aux[0] === '') {
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private');
}
$return->$name = self::export($clone[$key], $maxDepth - 1);
}
return $return;
}
/**
* @param object $object
*/
private static function getClass($object): string
{
$class = get_class($object);
if (! class_exists(Proxy::class)) {
return $class;
}
$pos = strrpos($class, '\\' . Proxy::MARKER . '\\');
if ($pos === false) {
return $class;
}
return substr($class, $pos + strlen(Proxy::MARKER) + 2);
}
}