Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\DBAL\DriverManager;
use Exception;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Database tool allows you to easily create your configured databases.
*
* @final
*/
class CreateDatabaseDoctrineCommand extends DoctrineCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:database:create')
->setDescription('Creates the configured database')
->addOption('shard', null, InputOption::VALUE_REQUIRED, 'The shard connection to use for this command')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
->addOption('if-not-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database already exists')
->setHelp(<<<EOT
The <info>%command.name%</info> command creates the default connections database:
<info>php %command.full_name%</info>
You can also optionally specify the name of a connection to create the database for:
<info>php %command.full_name% --connection=default</info>
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$connectionName = $input->getOption('connection');
if (empty($connectionName)) {
$connectionName = $this->getDoctrine()->getDefaultConnectionName();
}
$connection = $this->getDoctrineConnection($connectionName);
$ifNotExists = $input->getOption('if-not-exists');
$params = $connection->getParams();
if (isset($params['master'])) {
$params = $params['master'];
}
// Cannot inject `shard` option in parent::getDoctrineConnection
// cause it will try to connect to a non-existing database
if (isset($params['shards'])) {
$shards = $params['shards'];
// Default select global
$params = array_merge($params, $params['global']);
unset($params['global']['dbname'], $params['global']['path'], $params['global']['url']);
if ($input->getOption('shard')) {
foreach ($shards as $i => $shard) {
if ($shard['id'] === (int) $input->getOption('shard')) {
// Select sharded database
$params = array_merge($params, $shard);
unset($params['shards'][$i]['dbname'], $params['shards'][$i]['path'], $params['shards'][$i]['url'], $params['id']);
break;
}
}
}
}
$hasPath = isset($params['path']);
$name = $hasPath ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
if (! $name) {
throw new InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be created.");
}
// Need to get rid of _every_ occurrence of dbname from connection configuration and we have already extracted all relevant info from url
unset($params['dbname'], $params['path'], $params['url']);
$tmpConnection = DriverManager::getConnection($params);
$tmpConnection->connect($input->getOption('shard'));
$shouldNotCreateDatabase = $ifNotExists && in_array($name, $tmpConnection->getSchemaManager()->listDatabases());
// Only quote if we don't have a path
if (! $hasPath) {
$name = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($name);
}
$error = false;
try {
if ($shouldNotCreateDatabase) {
$output->writeln(sprintf('<info>Database <comment>%s</comment> for connection named <comment>%s</comment> already exists. Skipped.</info>', $name, $connectionName));
} else {
$tmpConnection->getSchemaManager()->createDatabase($name);
$output->writeln(sprintf('<info>Created database <comment>%s</comment> for connection named <comment>%s</comment></info>', $name, $connectionName));
}
} catch (Exception $e) {
$output->writeln(sprintf('<error>Could not create database <comment>%s</comment> for connection named <comment>%s</comment></error>', $name, $connectionName));
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
$error = true;
}
$tmpConnection->close();
return $error ? 1 : 0;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Sharding\PoolingShardConnection;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\EntityGenerator;
use LogicException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base class for Doctrine console commands to extend from.
*
* @internal
*/
abstract class DoctrineCommand extends Command
{
/** @var ManagerRegistry|null */
private $doctrine;
/** @var ContainerInterface|null */
private $container;
public function __construct(ManagerRegistry $doctrine = null)
{
parent::__construct();
$this->doctrine = $doctrine;
}
/**
* @deprecated
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @deprecated
*
* @return ContainerInterface
*
* @throws LogicException
*/
protected function getContainer()
{
if ($this->container === null) {
$application = $this->getApplication();
if ($application === null) {
throw new LogicException('The container cannot be retrieved as the application instance is not yet set.');
}
$this->container = $application->getKernel()->getContainer();
}
return $this->container;
}
/**
* get a doctrine entity generator
*
* @return EntityGenerator
*/
protected function getEntityGenerator()
{
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(false);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->setNumSpaces(4);
$entityGenerator->setAnnotationPrefix('ORM\\');
return $entityGenerator;
}
/**
* Get a doctrine entity manager by symfony name.
*
* @param string $name
* @param int|null $shardId
*
* @return EntityManager
*/
protected function getEntityManager($name, $shardId = null)
{
$manager = $this->getDoctrine()->getManager($name);
if ($shardId) {
if (! $manager->getConnection() instanceof PoolingShardConnection) {
throw new LogicException(sprintf("Connection of EntityManager '%s' must implement shards configuration.", $name));
}
$manager->getConnection()->connect($shardId);
}
return $manager;
}
/**
* Get a doctrine dbal connection by symfony name.
*
* @param string $name
*
* @return Connection
*/
protected function getDoctrineConnection($name)
{
return $this->getDoctrine()->getConnection($name);
}
/**
* @return ManagerRegistry
*/
protected function getDoctrine()
{
return $this->doctrine ?: $this->doctrine = $this->getContainer()->get('doctrine');
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\DBAL\DriverManager;
use Exception;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Database tool allows you to easily drop your configured databases.
*
* @final
*/
class DropDatabaseDoctrineCommand extends DoctrineCommand
{
const RETURN_CODE_NOT_DROP = 1;
const RETURN_CODE_NO_FORCE = 2;
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:database:drop')
->setDescription('Drops the configured database')
->addOption('shard', null, InputOption::VALUE_REQUIRED, 'The shard connection to use for this command')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
->addOption('if-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database doesn\'t exist')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action')
->setHelp(<<<EOT
The <info>%command.name%</info> command drops the default connections database:
<info>php %command.full_name%</info>
The <info>--force</info> parameter has to be used to actually drop the database.
You can also optionally specify the name of a connection to drop the database for:
<info>php %command.full_name% --connection=default</info>
<error>Be careful: All data in a given database will be lost when executing this command.</error>
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$connectionName = $input->getOption('connection');
if (empty($connectionName)) {
$connectionName = $this->getDoctrine()->getDefaultConnectionName();
}
$connection = $this->getDoctrineConnection($connectionName);
$ifExists = $input->getOption('if-exists');
$params = $connection->getParams();
if (isset($params['master'])) {
$params = $params['master'];
}
if (isset($params['shards'])) {
$shards = $params['shards'];
// Default select global
$params = array_merge($params, $params['global']);
if ($input->getOption('shard')) {
foreach ($shards as $shard) {
if ($shard['id'] === (int) $input->getOption('shard')) {
// Select sharded database
$params = $shard;
unset($params['id']);
break;
}
}
}
}
$name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
if (! $name) {
throw new InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
}
unset($params['dbname'], $params['url']);
if (! $input->getOption('force')) {
$output->writeln('<error>ATTENTION:</error> This operation should not be executed in a production environment.');
$output->writeln('');
$output->writeln(sprintf('<info>Would drop the database <comment>%s</comment> for connection named <comment>%s</comment>.</info>', $name, $connectionName));
$output->writeln('Please run the operation with --force to execute');
$output->writeln('<error>All data will be lost!</error>');
return self::RETURN_CODE_NO_FORCE;
}
// Reopen connection without database name set
// as some vendors do not allow dropping the database connected to.
$connection->close();
$connection = DriverManager::getConnection($params);
$shouldDropDatabase = ! $ifExists || in_array($name, $connection->getSchemaManager()->listDatabases());
// Only quote if we don't have a path
if (! isset($params['path'])) {
$name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name);
}
try {
if ($shouldDropDatabase) {
$connection->getSchemaManager()->dropDatabase($name);
$output->writeln(sprintf('<info>Dropped database <comment>%s</comment> for connection named <comment>%s</comment></info>', $name, $connectionName));
} else {
$output->writeln(sprintf('<info>Database <comment>%s</comment> for connection named <comment>%s</comment> doesn\'t exist. Skipped.</info>', $name, $connectionName));
}
} catch (Exception $e) {
$output->writeln(sprintf('<error>Could not drop database <comment>%s</comment> for connection named <comment>%s</comment></error>', $name, $connectionName));
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
return self::RETURN_CODE_NOT_DROP;
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory;
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Generate entity classes from mapping information
*
* @final
*/
class GenerateEntitiesDoctrineCommand extends DoctrineCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:generate:entities')
->setAliases(['generate:doctrine:entities'])
->setDescription('Generates entity classes and method stubs from your mapping information')
->addArgument('name', InputArgument::REQUIRED, 'A bundle name, a namespace, or a class name')
->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path where to generate entities when it cannot be guessed')
->addOption('no-backup', null, InputOption::VALUE_NONE, 'Do not backup existing entities files.')
->setHelp(<<<EOT
The <info>%command.name%</info> command generates entity classes
and method stubs from your mapping information:
You have to limit generation of entities:
* To a bundle:
<info>php %command.full_name% MyCustomBundle</info>
* To a single entity:
<info>php %command.full_name% MyCustomBundle:User</info>
<info>php %command.full_name% MyCustomBundle/Entity/User</info>
* To a namespace
<info>php %command.full_name% MyCustomBundle/Entity</info>
If the entities are not stored in a bundle, and if the classes do not exist,
the command has no way to guess where they should be generated. In this case,
you must provide the <comment>--path</comment> option:
<info>php %command.full_name% Blog/Entity --path=src/</info>
By default, the unmodified version of each entity is backed up and saved
(e.g. Product.php~). To prevent this task from creating the backup file,
pass the <comment>--no-backup</comment> option:
<info>php %command.full_name% Blog/Entity --no-backup</info>
<error>Important:</error> Even if you specified Inheritance options in your
XML or YAML Mapping files the generator cannot generate the base and
child classes for you correctly, because it doesn't know which
class is supposed to extend which. You have to adjust the entity
code manually for inheritance to work!
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
trigger_error('The doctrine:generate:entity command has been deprecated.', E_USER_DEPRECATED);
$output->writeln([
' <comment>NOTE:</comment> The <info>doctrine:generate:entities</info> command has been deprecated.',
' To read more about the differences between anemic and rich models go here <info>http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html#adding-behavior-to-entities</info>.',
' If you wish to generate your entities, use <info>make:entity --regenerate</info> from MakerBundle instead.',
]);
$manager = new DisconnectedMetadataFactory($this->getDoctrine());
try {
$bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('name'));
$output->writeln(sprintf('Generating entities for bundle "<info>%s</info>"', $bundle->getName()));
$metadata = $manager->getBundleMetadata($bundle);
} catch (InvalidArgumentException $e) {
$name = strtr($input->getArgument('name'), '/', '\\');
$pos = strpos($name, ':');
if ($pos !== false) {
$name = $this->getDoctrine()->getAliasNamespace(substr($name, 0, $pos)) . '\\' . substr($name, $pos + 1);
}
if (class_exists($name)) {
$output->writeln(sprintf('Generating entity "<info>%s</info>"', $name));
$metadata = $manager->getClassMetadata($name, $input->getOption('path'));
} else {
$output->writeln(sprintf('Generating entities for namespace "<info>%s</info>"', $name));
$metadata = $manager->getNamespaceMetadata($name, $input->getOption('path'));
}
}
$generator = $this->getEntityGenerator();
$backupExisting = ! $input->getOption('no-backup');
$generator->setBackupExisting($backupExisting);
$repoGenerator = new EntityRepositoryGenerator();
foreach ($metadata->getMetadata() as $m) {
if ($backupExisting) {
$basename = substr($m->name, strrpos($m->name, '\\') + 1);
$output->writeln(sprintf(' > backing up <comment>%s.php</comment> to <comment>%s.php~</comment>', $basename, $basename));
}
// Getting the metadata for the entity class once more to get the correct path if the namespace has multiple occurrences
try {
$entityMetadata = $manager->getClassMetadata($m->getName(), $input->getOption('path'));
} catch (RuntimeException $e) {
// fall back to the bundle metadata when no entity class could be found
$entityMetadata = $metadata;
}
$output->writeln(sprintf(' > generating <comment>%s</comment>', $m->name));
$generator->generate([$m], $entityMetadata->getPath());
if (! $m->customRepositoryClassName || strpos($m->customRepositoryClassName, $metadata->getNamespace()) === false) {
continue;
}
$repoGenerator->writeEntityRepositoryClass($m->customRepositoryClassName, $metadata->getPath());
}
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping\Driver\DatabaseDriver;
use Doctrine\ORM\Tools\Console\MetadataFilter;
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Import Doctrine ORM metadata mapping information from an existing database.
*
* @final
*/
class ImportMappingDoctrineCommand extends DoctrineCommand
{
/** @var string[] */
private $bundles;
/**
* @param string[] $bundles
*/
public function __construct(ManagerRegistry $doctrine, array $bundles)
{
parent::__construct($doctrine);
$this->bundles = $bundles;
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:mapping:import')
->addArgument('name', InputArgument::REQUIRED, 'The bundle or namespace to import the mapping information to')
->addArgument('mapping-type', InputArgument::OPTIONAL, 'The mapping type to export the imported mapping information to')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command')
->addOption('shard', null, InputOption::VALUE_REQUIRED, 'The shard connection to use for this command')
->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be mapped.')
->addOption('force', null, InputOption::VALUE_NONE, 'Force to overwrite existing mapping files.')
->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path where the files would be generated (not used when a bundle is passed).')
->setDescription('Imports mapping information from an existing database')
->setHelp(<<<EOT
The <info>%command.name%</info> command imports mapping information
from an existing database:
Generate annotation mappings into the src/ directory using App as the namespace:
<info>php %command.full_name% App\\\Entity annotation --path=src/Entity</info>
Generate xml mappings into the config/doctrine/ directory using App as the namespace:
<info>php %command.full_name% App\\\Entity xml --path=config/doctrine</info>
Generate XML mappings into a bundle:
<info>php %command.full_name% "MyCustomBundle" xml</info>
You can also optionally specify which entity manager to import from with the
<info>--em</info> option:
<info>php %command.full_name% "MyCustomBundle" xml --em=default</info>
If you don't want to map every entity that can be found in the database, use the
<info>--filter</info> option. It will try to match the targeted mapped entity with the
provided pattern string.
<info>php %command.full_name% "MyCustomBundle" xml --filter=MyMatchedEntity</info>
Use the <info>--force</info> option, if you want to override existing mapping files:
<info>php %command.full_name% "MyCustomBundle" xml --force</info>
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$type = $input->getArgument('mapping-type') ?: 'xml';
if ($type === 'yaml') {
$type = 'yml';
}
$namespaceOrBundle = $input->getArgument('name');
if (isset($this->bundles[$namespaceOrBundle])) {
$bundle = $this->getApplication()->getKernel()->getBundle($namespaceOrBundle);
$namespace = $bundle->getNamespace() . '\Entity';
$destPath = $bundle->getPath();
if ($type === 'annotation') {
$destPath .= '/Entity';
} else {
$destPath .= '/Resources/config/doctrine';
}
} else {
// assume a namespace has been passed
$namespace = $namespaceOrBundle;
$destPath = $input->getOption('path');
if ($destPath === null) {
throw new InvalidArgumentException('The --path option is required when passing a namespace (e.g. --path=src). If you intended to pass a bundle name, check your spelling.');
}
}
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter($type);
$exporter->setOverwriteExistingFiles($input->getOption('force'));
if ($type === 'annotation') {
$entityGenerator = $this->getEntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
}
$em = $this->getEntityManager($input->getOption('em'), $input->getOption('shard'));
$databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager());
$em->getConfiguration()->setMetadataDriverImpl($databaseDriver);
$emName = $input->getOption('em');
$emName = $emName ? $emName : 'default';
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));
if ($metadata) {
$output->writeln(sprintf('Importing mapping information from "<info>%s</info>" entity manager', $emName));
foreach ($metadata as $class) {
$className = $class->name;
$class->name = $namespace . '\\' . $className;
if ($type === 'annotation') {
$path = $destPath . '/' . str_replace('\\', '.', $className) . '.php';
} else {
$path = $destPath . '/' . str_replace('\\', '.', $className) . '.orm.' . $type;
}
$output->writeln(sprintf(' > writing <comment>%s</comment>', $path));
$code = $exporter->exportClassMetadata($class);
$dir = dirname($path);
if (! is_dir($dir)) {
mkdir($dir, 0775, true);
}
file_put_contents($path, $code);
chmod($path, 0664);
}
return 0;
}
$output->writeln('Database does not have any mapping information.');
$output->writeln('');
return 1;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to clear the metadata cache of the various cache drivers.
*/
class ClearMetadataCacheDoctrineCommand extends MetadataCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:cache:clear-metadata')
->setDescription('Clears all metadata cache for an entity manager')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to clear the query cache of the various cache drivers.
*/
class ClearQueryCacheDoctrineCommand extends QueryCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:cache:clear-query')
->setDescription('Clears all query cache for an entity manager')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to clear the result cache of the various cache drivers.
*/
class ClearResultCacheDoctrineCommand extends ResultCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:cache:clear-result')
->setDescription('Clears result cache for an entity manager')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand;
/**
* Command to clear a collection cache region.
*/
class CollectionRegionDoctrineCommand extends DelegateCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->setName('doctrine:cache:clear-collection-region');
}
/**
* {@inheritDoc}
*/
protected function createCommand()
{
return new CollectionRegionCommand();
}
/**
* {@inheritDoc}
*/
protected function getMinimalVersion()
{
return '2.5.0-DEV';
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
use Doctrine\ORM\Tools\Export\Driver\AbstractExporter;
use Doctrine\ORM\Tools\Export\Driver\XmlExporter;
use Doctrine\ORM\Tools\Export\Driver\YamlExporter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Convert Doctrine ORM metadata mapping information between the various supported
* formats.
*/
class ConvertMappingDoctrineCommand extends ConvertMappingCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:mapping:convert')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
/**
* @param string $toType
* @param string $destPath
*
* @return AbstractExporter
*/
protected function getExporter($toType, $destPath)
{
/** @var AbstractExporter $exporter */
$exporter = parent::getExporter($toType, $destPath);
if ($exporter instanceof XmlExporter) {
$exporter->setExtension('.orm.xml');
} elseif ($exporter instanceof YamlExporter) {
$exporter->setExtension('.orm.yml');
}
return $exporter;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to execute the SQL needed to generate the database schema for
* a given entity manager.
*/
class CreateSchemaDoctrineCommand extends CreateCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:schema:create')
->setDescription('Executes (or dumps) the SQL needed to generate the database schema')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Version;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command Delegate.
*/
abstract class DelegateCommand extends Command
{
/** @var Command */
protected $command;
/**
* @return Command
*/
abstract protected function createCommand();
/**
* @return string
*/
protected function getMinimalVersion()
{
return '2.3.0-DEV';
}
/**
* @return bool
*/
private function isVersionCompatible()
{
return version_compare(Version::VERSION, $this->getMinimalVersion()) >= 0;
}
/**
* {@inheritDoc}
*/
public function isEnabled()
{
return $this->isVersionCompatible();
}
/**
* @param string $entityManagerName
*
* @return Command
*/
protected function wrapCommand($entityManagerName)
{
if (! $this->isVersionCompatible()) {
throw new RuntimeException(sprintf('"%s" requires doctrine-orm "%s" or newer', $this->getName(), $this->getMinimalVersion()));
}
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $entityManagerName);
$this->command->setApplication($this->getApplication());
return $this->command;
}
/**
* {@inheritDoc}
*/
protected function configure()
{
if ($this->isVersionCompatible()) {
$this->command = $this->createCommand();
$this->setHelp($this->command->getHelp());
$this->setDefinition($this->command->getDefinition());
$this->setDescription($this->command->getDescription());
}
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
return $this->wrapCommand($input->getOption('em'))->execute($input, $output);
}
/**
* {@inheritDoc}
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$this->wrapCommand($input->getOption('em'))->interact($input, $output);
}
/**
* {@inheritDoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->wrapCommand($input->getOption('em'))->initialize($input, $output);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Symfony\Bundle\FrameworkBundle\Console\Application;
/**
* Provides some helper and convenience methods to configure doctrine commands in the context of bundles
* and multiple connections/entity managers.
*/
abstract class DoctrineCommandHelper
{
/**
* Convenience method to push the helper sets of a given entity manager into the application.
*
* @param string $emName
*/
public static function setApplicationEntityManager(Application $application, $emName)
{
/** @var EntityManager $em */
$em = $application->getKernel()->getContainer()->get('doctrine')->getManager($emName);
$helperSet = $application->getHelperSet();
$helperSet->set(new ConnectionHelper($em->getConnection()), 'db');
$helperSet->set(new EntityManagerHelper($em), 'em');
}
/**
* Convenience method to push the helper sets of a given connection into the application.
*
* @param string $connName
*/
public static function setApplicationConnection(Application $application, $connName)
{
$connection = $application->getKernel()->getContainer()->get('doctrine')->getConnection($connName);
$helperSet = $application->getHelperSet();
$helperSet->set(new ConnectionHelper($connection), 'db');
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to drop the database schema for a set of classes based on their mappings.
*/
class DropSchemaDoctrineCommand extends DropCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:schema:drop')
->setDescription('Executes (or dumps) the SQL needed to drop the current database schema')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Ensure the Doctrine ORM is configured properly for a production environment.
*/
class EnsureProductionSettingsDoctrineCommand extends EnsureProductionSettingsCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:ensure-production-settings')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand;
/**
* Command to clear a entity cache region.
*/
class EntityRegionCacheDoctrineCommand extends DelegateCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->setName('doctrine:cache:clear-entity-region');
}
/**
* {@inheritDoc}
*/
protected function createCommand()
{
return new EntityRegionCommand();
}
/**
* {@inheritDoc}
*/
protected function getMinimalVersion()
{
return '2.5.0-DEV';
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Loads an SQL file and executes it.
*/
class ImportDoctrineCommand extends ImportCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:database:import')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationConnection($this->getApplication(), $input->getOption('connection'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\InfoCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Show information about mapped entities
*/
class InfoDoctrineCommand extends InfoCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:mapping:info')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand;
/**
* Command to clear a query cache region.
*/
class QueryRegionCacheDoctrineCommand extends DelegateCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->setName('doctrine:cache:clear-query-region');
}
/**
* {@inheritDoc}
*/
protected function createCommand()
{
return new QueryRegionCommand();
}
/**
* {@inheritDoc}
*/
protected function getMinimalVersion()
{
return '2.5.0-DEV';
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\RunDqlCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Execute a Doctrine DQL query and output the results.
*/
class RunDqlDoctrineCommand extends RunDqlCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:query:dql')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command')
->setHelp(<<<EOT
The <info>%command.name%</info> command executes the given DQL query and
outputs the results:
<info>php %command.full_name% "SELECT u FROM UserBundle:User u"</info>
You can also optional specify some additional options like what type of
hydration to use when executing the query:
<info>php %command.full_name% "SELECT u FROM UserBundle:User u" --hydrate=array</info>
Additionally you can specify the first result and maximum amount of results to
show:
<info>php %command.full_name% "SELECT u FROM UserBundle:User u" --first-result=0 --max-result=30</info>
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Execute a SQL query and output the results.
*/
class RunSqlDoctrineCommand extends RunSqlCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:query:sql')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
->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}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationConnection($this->getApplication(), $input->getOption('connection'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to generate the SQL needed to update the database schema to match
* the current mapping information.
*/
class UpdateSchemaDoctrineCommand extends UpdateCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:schema:update')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand as DoctrineValidateSchemaCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to run Doctrine ValidateSchema() on the current mappings.
*/
class ValidateSchemaCommand extends DoctrineValidateSchemaCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('doctrine:schema:validate')
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
return parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
/**
* Connection
*/
class ConnectionFactory
{
/** @var mixed[][] */
private $typesConfig = [];
/** @var string[] */
private $commentedTypes = [];
/** @var bool */
private $initialized = false;
/**
* Construct.
*
* @param mixed[][] $typesConfig
*/
public function __construct(array $typesConfig)
{
$this->typesConfig = $typesConfig;
}
/**
* Create a connection by name.
*
* @param mixed[] $params
* @param string[]|Type[] $mappingTypes
*
* @return Connection
*/
public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = [])
{
if (! $this->initialized) {
$this->initializeTypes();
}
$connection = DriverManager::getConnection($params, $config, $eventManager);
if (! empty($mappingTypes)) {
$platform = $this->getDatabasePlatform($connection);
foreach ($mappingTypes as $dbType => $doctrineType) {
$platform->registerDoctrineTypeMapping($dbType, $doctrineType);
}
}
if (! empty($this->commentedTypes)) {
$platform = $this->getDatabasePlatform($connection);
foreach ($this->commentedTypes as $type) {
$platform->markDoctrineTypeCommented(Type::getType($type));
}
}
return $connection;
}
/**
* Try to get the database platform.
*
* This could fail if types should be registered to an predefined/unused connection
* and the platform version is unknown.
* For details have a look at DoctrineBundle issue #673.
*
* @return AbstractPlatform
*
* @throws DBALException
*/
private function getDatabasePlatform(Connection $connection)
{
try {
return $connection->getDatabasePlatform();
} catch (DriverException $driverException) {
throw new DBALException(
'An exception occured while establishing a connection to figure out your platform version.' . PHP_EOL .
"You can circumvent this by setting a 'server_version' configuration value" . PHP_EOL . PHP_EOL .
'For further information have a look at:' . PHP_EOL .
'https://github.com/doctrine/DoctrineBundle/issues/673',
0,
$driverException
);
}
}
/**
* initialize the types
*/
private function initializeTypes()
{
foreach ($this->typesConfig as $type => $typeConfig) {
if (Type::hasType($type)) {
Type::overrideType($type, $typeConfig['class']);
} else {
Type::addType($type, $typeConfig['class']);
}
if (! $typeConfig['commented']) {
continue;
}
$this->commentedTypes[] = $type;
}
$this->initialized = true;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Controller;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Exception;
use PDO;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\VarDumper\Cloner\Data;
/**
* ProfilerController.
*/
class ProfilerController implements ContainerAwareInterface
{
/** @var ContainerInterface */
private $container;
/**
* {@inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* Renders the profiler panel for the given token.
*
* @param string $token The profiler token
* @param string $connectionName
* @param int $query
*
* @return Response A Response instance
*/
public function explainAction($token, $connectionName, $query)
{
/** @var Profiler $profiler */
$profiler = $this->container->get('profiler');
$profiler->disable();
$profile = $profiler->loadProfile($token);
$queries = $profile->getCollector('db')->getQueries();
if (! isset($queries[$connectionName][$query])) {
return new Response('This query does not exist.');
}
$query = $queries[$connectionName][$query];
if (! $query['explainable']) {
return new Response('This query cannot be explained.');
}
/** @var Connection $connection */
$connection = $this->container->get('doctrine')->getConnection($connectionName);
try {
if ($connection->getDatabasePlatform() instanceof SQLServerPlatform) {
$results = $this->explainSQLServerPlatform($connection, $query);
} else {
$results = $this->explainOtherPlatform($connection, $query);
}
} catch (Exception $e) {
return new Response('This query cannot be explained.');
}
return new Response($this->container->get('twig')->render('@Doctrine/Collector/explain.html.twig', [
'data' => $results,
'query' => $query,
]));
}
private function explainSQLServerPlatform(Connection $connection, $query)
{
if (stripos($query['sql'], 'SELECT') === 0) {
$sql = 'SET STATISTICS PROFILE ON; ' . $query['sql'] . '; SET STATISTICS PROFILE OFF;';
} else {
$sql = 'SET SHOWPLAN_TEXT ON; GO; SET NOEXEC ON; ' . $query['sql'] . '; SET NOEXEC OFF; GO; SET SHOWPLAN_TEXT OFF;';
}
$params = $query['params'];
if ($params instanceof Data) {
$params = $params->getValue(true);
}
$stmt = $connection->executeQuery($sql, $params, $query['types']);
$stmt->nextRowset();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function explainOtherPlatform(Connection $connection, $query)
{
$params = $query['params'];
if ($params instanceof Data) {
$params = $params->getValue(true);
}
return $connection->executeQuery('EXPLAIN ' . $query['sql'], $params, $query['types'])
->fetchAll(PDO::FETCH_ASSOC);
}
}

View File

@@ -0,0 +1,268 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DataCollector;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Cache\Logging\CacheLoggerChain;
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Tools\SchemaValidator;
use Doctrine\ORM\Version;
use Exception;
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* DoctrineDataCollector.
*/
class DoctrineDataCollector extends BaseCollector
{
/** @var ManagerRegistry */
private $registry;
/** @var int|null */
private $invalidEntityCount;
/** @var string[] */
private $groupedQueries;
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
parent::__construct($registry);
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, Exception $exception = null)
{
parent::collect($request, $response, $exception);
$errors = [];
$entities = [];
$caches = [
'enabled' => false,
'log_enabled' => false,
'counts' => [
'puts' => 0,
'hits' => 0,
'misses' => 0,
],
'regions' => [
'puts' => [],
'hits' => [],
'misses' => [],
],
];
/** @var EntityManager $em */
foreach ($this->registry->getManagers() as $name => $em) {
$entities[$name] = [];
/** @var ClassMetadataFactory $factory */
$factory = $em->getMetadataFactory();
$validator = new SchemaValidator($em);
/** @var ClassMetadataInfo $class */
foreach ($factory->getLoadedMetadata() as $class) {
if (isset($entities[$name][$class->getName()])) {
continue;
}
$classErrors = $validator->validateClass($class);
$entities[$name][$class->getName()] = $class->getName();
if (empty($classErrors)) {
continue;
}
$errors[$name][$class->getName()] = $classErrors;
}
if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
continue;
}
/** @var Configuration $emConfig */
$emConfig = $em->getConfiguration();
$slcEnabled = $emConfig->isSecondLevelCacheEnabled();
if (! $slcEnabled) {
continue;
}
$caches['enabled'] = true;
/** @var $cacheConfiguration \Doctrine\ORM\Cache\CacheConfiguration */
/** @var CacheLoggerChain $cacheLoggerChain */
$cacheConfiguration = $emConfig->getSecondLevelCacheConfiguration();
$cacheLoggerChain = $cacheConfiguration->getCacheLogger();
if (! $cacheLoggerChain || ! $cacheLoggerChain->getLogger('statistics')) {
continue;
}
/** @var StatisticsCacheLogger $cacheLoggerStats */
$cacheLoggerStats = $cacheLoggerChain->getLogger('statistics');
$caches['log_enabled'] = true;
$caches['counts']['puts'] += $cacheLoggerStats->getPutCount();
$caches['counts']['hits'] += $cacheLoggerStats->getHitCount();
$caches['counts']['misses'] += $cacheLoggerStats->getMissCount();
foreach ($cacheLoggerStats->getRegionsPut() as $key => $value) {
if (! isset($caches['regions']['puts'][$key])) {
$caches['regions']['puts'][$key] = 0;
}
$caches['regions']['puts'][$key] += $value;
}
foreach ($cacheLoggerStats->getRegionsHit() as $key => $value) {
if (! isset($caches['regions']['hits'][$key])) {
$caches['regions']['hits'][$key] = 0;
}
$caches['regions']['hits'][$key] += $value;
}
foreach ($cacheLoggerStats->getRegionsMiss() as $key => $value) {
if (! isset($caches['regions']['misses'][$key])) {
$caches['regions']['misses'][$key] = 0;
}
$caches['regions']['misses'][$key] += $value;
}
}
// HttpKernel < 3.2 compatibility layer
if (method_exists($this, 'cloneVar')) {
// Might be good idea to replicate this block in doctrine bridge so we can drop this from here after some time.
// This code is compatible with such change, because cloneVar is supposed to check if input is already cloned.
foreach ($this->data['queries'] as &$queries) {
foreach ($queries as &$query) {
$query['params'] = $this->cloneVar($query['params']);
}
}
}
$this->data['entities'] = $entities;
$this->data['errors'] = $errors;
$this->data['caches'] = $caches;
$this->groupedQueries = null;
}
public function getEntities()
{
return $this->data['entities'];
}
public function getMappingErrors()
{
return $this->data['errors'];
}
public function getCacheHitsCount()
{
return $this->data['caches']['counts']['hits'];
}
public function getCachePutsCount()
{
return $this->data['caches']['counts']['puts'];
}
public function getCacheMissesCount()
{
return $this->data['caches']['counts']['misses'];
}
public function getCacheEnabled()
{
return $this->data['caches']['enabled'];
}
public function getCacheRegions()
{
return $this->data['caches']['regions'];
}
public function getCacheCounts()
{
return $this->data['caches']['counts'];
}
public function getInvalidEntityCount()
{
if ($this->invalidEntityCount === null) {
$this->invalidEntityCount = array_sum(array_map('count', $this->data['errors']));
}
return $this->invalidEntityCount;
}
public function getGroupedQueries()
{
if ($this->groupedQueries !== null) {
return $this->groupedQueries;
}
$this->groupedQueries = [];
$totalExecutionMS = 0;
foreach ($this->data['queries'] as $connection => $queries) {
$connectionGroupedQueries = [];
foreach ($queries as $i => $query) {
$key = $query['sql'];
if (! isset($connectionGroupedQueries[$key])) {
$connectionGroupedQueries[$key] = $query;
$connectionGroupedQueries[$key]['executionMS'] = 0;
$connectionGroupedQueries[$key]['count'] = 0;
$connectionGroupedQueries[$key]['index'] = $i; // "Explain query" relies on query index in 'queries'.
}
$connectionGroupedQueries[$key]['executionMS'] += $query['executionMS'];
$connectionGroupedQueries[$key]['count']++;
$totalExecutionMS += $query['executionMS'];
}
usort($connectionGroupedQueries, static function ($a, $b) {
if ($a['executionMS'] === $b['executionMS']) {
return 0;
}
return $a['executionMS'] < $b['executionMS'] ? 1 : -1;
});
$this->groupedQueries[$connection] = $connectionGroupedQueries;
}
foreach ($this->groupedQueries as $connection => $queries) {
foreach ($queries as $i => $query) {
$this->groupedQueries[$connection][$i]['executionPercent'] =
$this->executionTimePercentage($query['executionMS'], $totalExecutionMS);
}
}
return $this->groupedQueries;
}
private function executionTimePercentage($executionTimeMS, $totalExecutionTimeMS)
{
if ($totalExecutionTimeMS === 0.0 || $totalExecutionTimeMS === 0) {
return 0;
}
return $executionTimeMS / $totalExecutionTimeMS * 100;
}
public function getGroupedQueryCount()
{
$count = 0;
foreach ($this->getGroupedQueries() as $connectionGroupedQueries) {
$count += count($connectionGroupedQueries);
}
return $count;
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterMappingsPass;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class for Symfony bundles to configure mappings for model classes not in the
* auto-mapped folder.
*
* NOTE: alias is only supported by Symfony 2.6+ and will be ignored with older versions.
*/
class DoctrineOrmMappingsPass extends RegisterMappingsPass
{
/**
* You should not directly instantiate this class but use one of the
* factory methods.
*
* @param Definition|Reference $driver Driver DI definition or reference.
* @param array $namespaces List of namespaces handled by $driver.
* @param string[] $managerParameters Ordered list of container parameters that
* could hold the manager name.
* doctrine.default_entity_manager is appended
* automatically.
* @param string|false $enabledParameter If specified, the compiler pass only executes
* if this parameter is defined in the service
* container.
* @param array $aliasMap Map of alias to namespace.
*/
public function __construct($driver, array $namespaces, array $managerParameters, $enabledParameter = false, array $aliasMap = [])
{
$managerParameters[] = 'doctrine.default_entity_manager';
parent::__construct(
$driver,
$namespaces,
$managerParameters,
'doctrine.orm.%s_metadata_driver',
$enabledParameter,
'doctrine.orm.%s_configuration',
'addEntityNamespace',
$aliasMap
);
}
/**
* @param array $namespaces Hashmap of directory path to namespace.
* @param string[] $managerParameters List of parameters that could which object manager name
* your bundle uses. This compiler pass will automatically
* append the parameter name for the default entity manager
* to this list.
* @param string|false $enabledParameter Service container parameter that must be present to
* enable the mapping. Set to false to not do any check,
* optional.
* @param string[] $aliasMap Map of alias to namespace.
*
* @return self
*/
public static function createXmlMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
{
$arguments = [$namespaces, '.orm.xml'];
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', [$locator]);
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
/**
* @param array $namespaces Hashmap of directory path to namespace
* @param string[] $managerParameters List of parameters that could which object manager name
* your bundle uses. This compiler pass will automatically
* append the parameter name for the default entity manager
* to this list.
* @param string|false $enabledParameter Service container parameter that must be present to
* enable the mapping. Set to false to not do any check,
* optional.
* @param string[] $aliasMap Map of alias to namespace.
*
* @return self
*/
public static function createYamlMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
{
$arguments = [$namespaces, '.orm.yml'];
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\ORM\Mapping\Driver\YamlDriver', [$locator]);
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
/**
* @param array $namespaces Hashmap of directory path to namespace
* @param string[] $managerParameters List of parameters that could which object manager name
* your bundle uses. This compiler pass will automatically
* append the parameter name for the default entity manager
* to this list.
* @param string $enabledParameter Service container parameter that must be present to
* enable the mapping. Set to false to not do any check,
* optional.
* @param string[] $aliasMap Map of alias to namespace.
*
* @return self
*/
public static function createPhpMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
{
$arguments = [$namespaces, '.php'];
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\PHPDriver', [$locator]);
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
/**
* @param array $namespaces List of namespaces that are handled with annotation mapping
* @param array $directories List of directories to look for annotated classes
* @param string[] $managerParameters List of parameters that could which object manager name
* your bundle uses. This compiler pass will automatically
* append the parameter name for the default entity manager
* to this list.
* @param string|false $enabledParameter Service container parameter that must be present to
* enable the mapping. Set to false to not do any check,
* optional.
* @param string[] $aliasMap Map of alias to namespace.
*
* @return self
*/
public static function createAnnotationMappingDriver(array $namespaces, array $directories, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
{
$reader = new Reference('annotation_reader');
$driver = new Definition('Doctrine\ORM\Mapping\Driver\AnnotationDriver', [$reader, $directories]);
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
/**
* @param array $namespaces List of namespaces that are handled with static php mapping
* @param array $directories List of directories to look for static php mapping files
* @param string[] $managerParameters List of parameters that could which object manager name
* your bundle uses. This compiler pass will automatically
* append the parameter name for the default entity manager
* to this list.
* @param string|false $enabledParameter Service container parameter that must be present to
* enable the mapping. Set to false to not do any check,
* optional.
* @param string[] $aliasMap Map of alias to namespace.
*
* @return self
*/
public static function createStaticPhpMappingDriver(array $namespaces, array $directories, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
{
$driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver', [$directories]);
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class for Symfony bundles to register entity listeners
*/
class EntityListenerPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
$resolvers = $container->findTaggedServiceIds('doctrine.orm.entity_listener');
foreach ($resolvers as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
$name = isset($attributes['entity_manager']) ? $attributes['entity_manager'] : $container->getParameter('doctrine.default_entity_manager');
$entityManager = sprintf('doctrine.orm.%s_entity_manager', $name);
if (! $container->hasDefinition($entityManager)) {
continue;
}
$resolverId = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
if (! $container->has($resolverId)) {
continue;
}
$resolver = $container->findDefinition($resolverId);
$resolver->setPublic(true);
if (isset($attributes['entity']) && isset($attributes['event'])) {
$this->attachToListener($container, $name, $id, $attributes);
}
if (isset($attributes['lazy']) && $attributes['lazy']) {
$listener = $container->findDefinition($id);
if ($listener->isAbstract()) {
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as this entity listener is lazy-loaded.', $id));
}
$interface = 'Doctrine\\Bundle\\DoctrineBundle\\Mapping\\EntityListenerServiceResolver';
$class = $resolver->getClass();
if (substr($class, 0, 1) === '%') {
// resolve container parameter first
$class = $container->getParameterBag()->resolveValue($resolver->getClass());
}
if (! is_a($class, $interface, true)) {
throw new InvalidArgumentException(
sprintf('Lazy-loaded entity listeners can only be resolved by a resolver implementing %s.', $interface)
);
}
$listener->setPublic(true);
$resolver->addMethodCall('registerService', [$listener->getClass(), $id]);
} else {
$resolver->addMethodCall('register', [new Reference($id)]);
}
}
}
}
private function attachToListener(ContainerBuilder $container, $name, $id, array $attributes)
{
$listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
if (! $container->has($listenerId)) {
return;
}
$serviceDef = $container->getDefinition($id);
$args = [
$attributes['entity'],
$serviceDef->getClass(),
$attributes['event'],
];
if (isset($attributes['method'])) {
$args[] = $attributes['method'];
}
$container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
final class ServiceRepositoryCompilerPass implements CompilerPassInterface
{
const REPOSITORY_SERVICE_TAG = 'doctrine.repository_service';
public function process(ContainerBuilder $container)
{
// when ORM is not enabled
if (! $container->hasDefinition('doctrine.orm.container_repository_factory')) {
return;
}
$locatorDef = $container->getDefinition('doctrine.orm.container_repository_factory');
$repoServiceIds = array_keys($container->findTaggedServiceIds(self::REPOSITORY_SERVICE_TAG));
// Symfony 3.2 and lower sanity check
if (! class_exists(ServiceLocatorTagPass::class)) {
if (! empty($repoServiceIds)) {
throw new RuntimeException(sprintf('The "%s" tag can only be used with Symfony 3.3 or higher. Remove the tag from the following services (%s) or upgrade to Symfony 3.3 or higher.', self::REPOSITORY_SERVICE_TAG, implode(', ', $repoServiceIds)));
}
return;
}
$repoReferences = array_map(static function ($id) {
return new Reference($id);
}, $repoServiceIds);
$ref = ServiceLocatorTagPass::register($container, array_combine($repoServiceIds, $repoReferences));
$locatorDef->replaceArgument(0, $ref);
}
}

View File

@@ -0,0 +1,739 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
use Doctrine\ORM\EntityManager;
use ReflectionClass;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* This class contains the configuration information for the bundle
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*/
class Configuration implements ConfigurationInterface
{
/** @var bool */
private $debug;
/**
* Constructor
*
* @param bool $debug Whether to use the debug mode
*/
public function __construct($debug)
{
$this->debug = (bool) $debug;
}
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('doctrine');
if (method_exists($treeBuilder, 'getRootNode')) {
$rootNode = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$rootNode = $treeBuilder->root('doctrine');
}
$this->addDbalSection($rootNode);
$this->addOrmSection($rootNode);
return $treeBuilder;
}
/**
* Add DBAL section to configuration tree
*/
private function addDbalSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('dbal')
->beforeNormalization()
->ifTrue(static function ($v) {
return is_array($v) && ! array_key_exists('connections', $v) && ! array_key_exists('connection', $v);
})
->then(static function ($v) {
// Key that should not be rewritten to the connection config
$excludedKeys = ['default_connection' => true, 'types' => true, 'type' => true];
$connection = [];
foreach ($v as $key => $value) {
if (isset($excludedKeys[$key])) {
continue;
}
$connection[$key] = $v[$key];
unset($v[$key]);
}
$v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default';
$v['connections'] = [$v['default_connection'] => $connection];
return $v;
})
->end()
->children()
->scalarNode('default_connection')->end()
->end()
->fixXmlConfig('type')
->children()
->arrayNode('types')
->useAttributeAsKey('name')
->prototype('array')
->beforeNormalization()
->ifString()
->then(static function ($v) {
return ['class' => $v];
})
->end()
->children()
->scalarNode('class')->isRequired()->end()
->booleanNode('commented')->defaultTrue()->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('connection')
->append($this->getDbalConnectionsNode())
->end();
}
/**
* Return the dbal connections node
*
* @return ArrayNodeDefinition
*/
private function getDbalConnectionsNode()
{
$treeBuilder = new TreeBuilder('connections');
if (method_exists($treeBuilder, 'getRootNode')) {
$node = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$node = $treeBuilder->root('connections');
}
/** @var ArrayNodeDefinition $connectionNode */
$connectionNode = $node
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array');
$this->configureDbalDriverNode($connectionNode);
$connectionNode
->fixXmlConfig('option')
->fixXmlConfig('mapping_type')
->fixXmlConfig('slave')
->fixXmlConfig('shard')
->fixXmlConfig('default_table_option')
->children()
->scalarNode('driver')->defaultValue('pdo_mysql')->end()
->scalarNode('platform_service')->end()
->booleanNode('auto_commit')->end()
->scalarNode('schema_filter')->end()
->booleanNode('logging')->defaultValue($this->debug)->end()
->booleanNode('profiling')->defaultValue($this->debug)->end()
->scalarNode('server_version')->end()
->scalarNode('driver_class')->end()
->scalarNode('wrapper_class')->end()
->scalarNode('shard_manager_class')->end()
->scalarNode('shard_choser')->end()
->scalarNode('shard_choser_service')->end()
->booleanNode('keep_slave')->end()
->arrayNode('options')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->arrayNode('mapping_types')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->arrayNode('default_table_options')
->info("This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','collate', and 'engine'.")
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->end();
$slaveNode = $connectionNode
->children()
->arrayNode('slaves')
->useAttributeAsKey('name')
->prototype('array');
$this->configureDbalDriverNode($slaveNode);
$shardNode = $connectionNode
->children()
->arrayNode('shards')
->prototype('array')
->children()
->integerNode('id')
->min(1)
->isRequired()
->end()
->end();
$this->configureDbalDriverNode($shardNode);
return $node;
}
/**
* Adds config keys related to params processed by the DBAL drivers
*
* These keys are available for slave configurations too.
*/
private function configureDbalDriverNode(ArrayNodeDefinition $node)
{
$node
->children()
->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
->scalarNode('dbname')->end()
->scalarNode('host')->defaultValue('localhost')->end()
->scalarNode('port')->defaultNull()->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultNull()->end()
->scalarNode('application_name')->end()
->scalarNode('charset')->end()
->scalarNode('path')->end()
->booleanNode('memory')->end()
->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end()
->booleanNode('service')
->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
->end()
->scalarNode('servicename')
->info(
'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter ' .
'for Oracle depending on the service parameter.'
)
->end()
->scalarNode('sessionMode')
->info('The session mode to use for the oci8 driver')
->end()
->scalarNode('server')
->info('The name of a running database server to connect to for SQL Anywhere.')
->end()
->scalarNode('default_dbname')
->info(
'Override the default database (postgres) to connect to for PostgreSQL connexion.'
)
->end()
->scalarNode('sslmode')
->info(
'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with ' .
'the server for PostgreSQL.'
)
->end()
->scalarNode('sslrootcert')
->info(
'The name of a file containing SSL certificate authority (CA) certificate(s). ' .
'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.'
)
->end()
->scalarNode('sslcert')
->info(
'The path to the SSL client certificate file for PostgreSQL.'
)
->end()
->scalarNode('sslkey')
->info(
'The path to the SSL client key file for PostgreSQL.'
)
->end()
->scalarNode('sslcrl')
->info(
'The file name of the SSL certificate revocation list for PostgreSQL.'
)
->end()
->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
->scalarNode('instancename')
->info(
'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.' .
' It is generally used to connect to an Oracle RAC server to select the name' .
' of a particular instance.'
)
->end()
->scalarNode('connectstring')
->info(
'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.' .
'When using this option, you will still need to provide the user and password parameters, but the other ' .
'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods' .
' from Doctrine\DBAL\Connection will no longer function as expected.'
)
->end()
->end()
->beforeNormalization()
->ifTrue(static function ($v) {
return ! isset($v['sessionMode']) && isset($v['session_mode']);
})
->then(static function ($v) {
$v['sessionMode'] = $v['session_mode'];
unset($v['session_mode']);
return $v;
})
->end()
->beforeNormalization()
->ifTrue(static function ($v) {
return ! isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);
})
->then(static function ($v) {
$v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
unset($v['multiple_active_result_sets']);
return $v;
})
->end();
}
/**
* Add the ORM section to configuration tree
*/
private function addOrmSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('orm')
->beforeNormalization()
->ifTrue(static function ($v) {
if (! empty($v) && ! class_exists(EntityManager::class)) {
throw new LogicException('The doctrine/orm package is required when the doctrine.orm config is set.');
}
return $v === null || (is_array($v) && ! array_key_exists('entity_managers', $v) && ! array_key_exists('entity_manager', $v));
})
->then(static function ($v) {
$v = (array) $v;
// Key that should not be rewritten to the connection config
$excludedKeys = [
'default_entity_manager' => true,
'auto_generate_proxy_classes' => true,
'proxy_dir' => true,
'proxy_namespace' => true,
'resolve_target_entities' => true,
'resolve_target_entity' => true,
];
$entityManager = [];
foreach ($v as $key => $value) {
if (isset($excludedKeys[$key])) {
continue;
}
$entityManager[$key] = $v[$key];
unset($v[$key]);
}
$v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
$v['entity_managers'] = [$v['default_entity_manager'] => $entityManager];
return $v;
})
->end()
->children()
->scalarNode('default_entity_manager')->end()
->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"')
->validate()
->ifTrue(function ($v) {
$generationModes = $this->getAutoGenerateModes();
if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) {
return false;
}
if (is_bool($v)) {
return false;
}
if (is_string($v)) {
if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL')*/)) {
return false;
}
}
return true;
})
->thenInvalid('Invalid auto generate mode value %s')
->end()
->validate()
->ifString()
->then(static function ($v) {
return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_' . strtoupper($v));
})
->end()
->end()
->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
->end()
->fixXmlConfig('entity_manager')
->append($this->getOrmEntityManagersNode())
->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
->append($this->getOrmTargetEntityResolverNode())
->end()
->end();
}
/**
* Return ORM target entity resolver node
*
* @return NodeDefinition
*/
private function getOrmTargetEntityResolverNode()
{
$treeBuilder = new TreeBuilder('resolve_target_entities');
if (method_exists($treeBuilder, 'getRootNode')) {
$node = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$node = $treeBuilder->root('resolve_target_entities');
}
$node
->useAttributeAsKey('interface')
->prototype('scalar')
->cannotBeEmpty()
->end();
return $node;
}
/**
* Return ORM entity listener node
*
* @return NodeDefinition
*/
private function getOrmEntityListenersNode()
{
$treeBuilder = new TreeBuilder('entity_listeners');
if (method_exists($treeBuilder, 'getRootNode')) {
$node = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$node = $treeBuilder->root('entity_listeners');
}
$normalizer = static function ($mappings) {
$entities = [];
foreach ($mappings as $entityClass => $mapping) {
$listeners = [];
foreach ($mapping as $listenerClass => $listenerEvent) {
$events = [];
foreach ($listenerEvent as $eventType => $eventMapping) {
if ($eventMapping === null) {
$eventMapping = [null];
}
foreach ($eventMapping as $method) {
$events[] = [
'type' => $eventType,
'method' => $method,
];
}
}
$listeners[] = [
'class' => $listenerClass,
'event' => $events,
];
}
$entities[] = [
'class' => $entityClass,
'listener' => $listeners,
];
}
return ['entities' => $entities];
};
$node
->beforeNormalization()
// Yaml normalization
->ifTrue(static function ($v) {
return is_array(reset($v)) && is_string(key(reset($v)));
})
->then($normalizer)
->end()
->fixXmlConfig('entity', 'entities')
->children()
->arrayNode('entities')
->useAttributeAsKey('class')
->prototype('array')
->fixXmlConfig('listener')
->children()
->arrayNode('listeners')
->useAttributeAsKey('class')
->prototype('array')
->fixXmlConfig('event')
->children()
->arrayNode('events')
->prototype('array')
->children()
->scalarNode('type')->end()
->scalarNode('method')->defaultNull()->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end();
return $node;
}
/**
* Return ORM entity manager node
*
* @return ArrayNodeDefinition
*/
private function getOrmEntityManagersNode()
{
$treeBuilder = new TreeBuilder('entity_managers');
if (method_exists($treeBuilder, 'getRootNode')) {
$node = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$node = $treeBuilder->root('entity_managers');
}
$node
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->addDefaultsIfNotSet()
->append($this->getOrmCacheDriverNode('query_cache_driver'))
->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
->append($this->getOrmCacheDriverNode('result_cache_driver'))
->append($this->getOrmEntityListenersNode())
->children()
->scalarNode('connection')->end()
->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end()
->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end()
->scalarNode('auto_mapping')->defaultFalse()->end()
->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
->scalarNode('entity_listener_resolver')->defaultNull()->end()
->scalarNode('repository_factory')->defaultValue('doctrine.orm.container_repository_factory')->end()
->end()
->children()
->arrayNode('second_level_cache')
->children()
->append($this->getOrmCacheDriverNode('region_cache_driver'))
->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
->booleanNode('log_enabled')->defaultValue($this->debug)->end()
->scalarNode('region_lifetime')->defaultValue(0)->end()
->booleanNode('enabled')->defaultValue(true)->end()
->scalarNode('factory')->end()
->end()
->fixXmlConfig('region')
->children()
->arrayNode('regions')
->useAttributeAsKey('name')
->prototype('array')
->children()
->append($this->getOrmCacheDriverNode('cache_driver'))
->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
->scalarNode('lock_lifetime')->defaultValue(60)->end()
->scalarNode('type')->defaultValue('default')->end()
->scalarNode('lifetime')->defaultValue(0)->end()
->scalarNode('service')->end()
->scalarNode('name')->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('logger')
->children()
->arrayNode('loggers')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('name')->end()
->scalarNode('service')->end()
->end()
->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('hydrator')
->children()
->arrayNode('hydrators')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig('mapping')
->children()
->arrayNode('mappings')
->useAttributeAsKey('name')
->prototype('array')
->beforeNormalization()
->ifString()
->then(static function ($v) {
return ['type' => $v];
})
->end()
->treatNullLike([])
->treatFalseLike(['mapping' => false])
->performNoDeepMerging()
->children()
->scalarNode('mapping')->defaultValue(true)->end()
->scalarNode('type')->end()
->scalarNode('dir')->end()
->scalarNode('alias')->end()
->scalarNode('prefix')->end()
->booleanNode('is_bundle')->end()
->end()
->end()
->end()
->arrayNode('dql')
->fixXmlConfig('string_function')
->fixXmlConfig('numeric_function')
->fixXmlConfig('datetime_function')
->children()
->arrayNode('string_functions')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->arrayNode('numeric_functions')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->arrayNode('datetime_functions')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('filter')
->children()
->arrayNode('filters')
->info('Register SQL Filters in the entity manager')
->useAttributeAsKey('name')
->prototype('array')
->beforeNormalization()
->ifString()
->then(static function ($v) {
return ['class' => $v];
})
->end()
->beforeNormalization()
// The content of the XML node is returned as the "value" key so we need to rename it
->ifTrue(static function ($v) {
return is_array($v) && isset($v['value']);
})
->then(static function ($v) {
$v['class'] = $v['value'];
unset($v['value']);
return $v;
})
->end()
->fixXmlConfig('parameter')
->children()
->scalarNode('class')->isRequired()->end()
->booleanNode('enabled')->defaultFalse()->end()
->arrayNode('parameters')
->useAttributeAsKey('name')
->prototype('variable')->end()
->end()
->end()
->end()
->end()
->end()
->end();
return $node;
}
/**
* Return a ORM cache driver node for an given entity manager
*
* @param string $name
*
* @return ArrayNodeDefinition
*/
private function getOrmCacheDriverNode($name)
{
$treeBuilder = new TreeBuilder($name);
if (method_exists($treeBuilder, 'getRootNode')) {
$node = $treeBuilder->getRootNode();
} else {
// BC layer for symfony/config 4.1 and older
$node = $treeBuilder->root($name);
}
$node
->addDefaultsIfNotSet()
->beforeNormalization()
->ifString()
->then(static function ($v) {
return ['type' => $v];
})
->end()
->children()
->scalarNode('type')->defaultValue('array')->end()
->scalarNode('host')->end()
->scalarNode('port')->end()
->scalarNode('database')->end()
->scalarNode('instance_class')->end()
->scalarNode('class')->end()
->scalarNode('id')->end()
->scalarNode('namespace')->defaultNull()->end()
->scalarNode('cache_provider')->defaultNull()->end()
->end();
return $node;
}
/**
* Find proxy auto generate modes for their names and int values
*
* @return array
*/
private function getAutoGenerateModes()
{
$constPrefix = 'AUTOGENERATE_';
$prefixLen = strlen($constPrefix);
$refClass = new ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory');
$constsArray = $refClass->getConstants();
$namesArray = [];
$valuesArray = [];
foreach ($constsArray as $key => $value) {
if (strpos($key, $constPrefix) !== 0) {
continue;
}
$namesArray[] = substr($key, $prefixLen);
$valuesArray[] = (int) $value;
}
return [
'names' => $namesArray,
'values' => $valuesArray,
];
}
}

View File

@@ -0,0 +1,861 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Version;
use LogicException;
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
use Symfony\Bridge\Doctrine\Form\Type\DoctrineType;
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Messenger\MessageBusInterface;
/**
* DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
*/
class DoctrineExtension extends AbstractDoctrineExtension
{
/** @var string */
private $defaultConnection;
/** @var SymfonyBridgeAdapter */
private $adapter;
public function __construct(SymfonyBridgeAdapter $adapter = null)
{
$this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
}
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$this->adapter->loadServicesConfiguration($container);
if (! empty($config['dbal'])) {
$this->dbalLoad($config['dbal'], $container);
}
if (empty($config['orm'])) {
return;
}
if (empty($config['dbal'])) {
throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
}
if (! class_exists('Doctrine\ORM\Version')) {
throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
}
$this->ormLoad($config['orm'], $container);
}
/**
* Loads the DBAL configuration.
*
* Usage example:
*
* <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function dbalLoad(array $config, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('dbal.xml');
if (empty($config['default_connection'])) {
$keys = array_keys($config['connections']);
$config['default_connection'] = reset($keys);
}
$this->defaultConnection = $config['default_connection'];
$container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
$container->getAlias('database_connection')->setPublic(true);
$container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
$container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
$connections = [];
foreach (array_keys($config['connections']) as $name) {
$connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
}
$container->setParameter('doctrine.connections', $connections);
$container->setParameter('doctrine.default_connection', $this->defaultConnection);
foreach ($config['connections'] as $name => $connection) {
$this->loadDbalConnection($name, $connection, $container);
}
}
/**
* Loads a configured DBAL connection.
*
* @param string $name The name of the connection
* @param array $connection A dbal connection configuration.
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
{
// configuration
$definitionClassname = $this->getDefinitionClassname();
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new $definitionClassname('doctrine.dbal.connection.configuration'));
$logger = null;
if ($connection['logging']) {
$logger = new Reference('doctrine.dbal.logger');
}
unset($connection['logging']);
if ($connection['profiling']) {
$profilingLoggerId = 'doctrine.dbal.logger.profiling.' . $name;
$container->setDefinition($profilingLoggerId, new $definitionClassname('doctrine.dbal.logger.profiling'));
$profilingLogger = new Reference($profilingLoggerId);
$container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', [$name, $profilingLogger]);
if ($logger !== null) {
$chainLogger = new $definitionClassname('doctrine.dbal.logger.chain');
$chainLogger->addMethodCall('addLogger', [$profilingLogger]);
$loggerId = 'doctrine.dbal.logger.chain.' . $name;
$container->setDefinition($loggerId, $chainLogger);
$logger = new Reference($loggerId);
} else {
$logger = $profilingLogger;
}
}
unset($connection['profiling']);
if (isset($connection['auto_commit'])) {
$configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
}
unset($connection['auto_commit']);
if (isset($connection['schema_filter']) && $connection['schema_filter']) {
$configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
}
unset($connection['schema_filter']);
if ($logger) {
$configuration->addMethodCall('setSQLLogger', [$logger]);
}
// event manager
$container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new $definitionClassname('doctrine.dbal.connection.event_manager'));
// connection
$options = $this->getConnectionOptions($connection);
$def = $container
->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new $definitionClassname('doctrine.dbal.connection'))
->setPublic(true)
->setArguments([
$options,
new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
$connection['mapping_types'],
]);
// Set class in case "wrapper_class" option was used to assist IDEs
if (isset($options['wrapperClass'])) {
$def->setClass($options['wrapperClass']);
}
if (! empty($connection['use_savepoints'])) {
$def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
}
// Create a shard_manager for this connection
if (! isset($options['shards'])) {
return;
}
$shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
$container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
}
protected function getConnectionOptions($connection)
{
$options = $connection;
if (isset($options['platform_service'])) {
$options['platform'] = new Reference($options['platform_service']);
unset($options['platform_service']);
}
unset($options['mapping_types']);
if (isset($options['shard_choser_service'])) {
$options['shard_choser'] = new Reference($options['shard_choser_service']);
unset($options['shard_choser_service']);
}
foreach ([
'options' => 'driverOptions',
'driver_class' => 'driverClass',
'wrapper_class' => 'wrapperClass',
'keep_slave' => 'keepSlave',
'shard_choser' => 'shardChoser',
'shard_manager_class' => 'shardManagerClass',
'server_version' => 'serverVersion',
'default_table_options' => 'defaultTableOptions',
] as $old => $new) {
if (! isset($options[$old])) {
continue;
}
$options[$new] = $options[$old];
unset($options[$old]);
}
if (! empty($options['slaves']) && ! empty($options['shards'])) {
throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
}
if (! empty($options['slaves'])) {
$nonRewrittenKeys = [
'driver' => true,
'driverOptions' => true,
'driverClass' => true,
'wrapperClass' => true,
'keepSlave' => true,
'shardChoser' => true,
'platform' => true,
'slaves' => true,
'master' => true,
'shards' => true,
'serverVersion' => true,
'defaultTableOptions' => true,
// included by safety but should have been unset already
'logging' => true,
'profiling' => true,
'mapping_types' => true,
'platform_service' => true,
];
foreach ($options as $key => $value) {
if (isset($nonRewrittenKeys[$key])) {
continue;
}
$options['master'][$key] = $value;
unset($options[$key]);
}
if (empty($options['wrapperClass'])) {
// Change the wrapper class only if the user does not already forced using a custom one.
$options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
}
} else {
unset($options['slaves']);
}
if (! empty($options['shards'])) {
$nonRewrittenKeys = [
'driver' => true,
'driverOptions' => true,
'driverClass' => true,
'wrapperClass' => true,
'keepSlave' => true,
'shardChoser' => true,
'platform' => true,
'slaves' => true,
'global' => true,
'shards' => true,
'serverVersion' => true,
'defaultTableOptions' => true,
// included by safety but should have been unset already
'logging' => true,
'profiling' => true,
'mapping_types' => true,
'platform_service' => true,
];
foreach ($options as $key => $value) {
if (isset($nonRewrittenKeys[$key])) {
continue;
}
$options['global'][$key] = $value;
unset($options[$key]);
}
if (empty($options['wrapperClass'])) {
// Change the wrapper class only if the user does not already forced using a custom one.
$options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
}
if (empty($options['shardManagerClass'])) {
// Change the shard manager class only if the user does not already forced using a custom one.
$options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
}
} else {
unset($options['shards']);
}
return $options;
}
/**
* Loads the Doctrine ORM configuration.
*
* Usage example:
*
* <doctrine:orm id="mydm" connection="myconn" />
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function ormLoad(array $config, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('orm.xml');
if (class_exists(AbstractType::class) && method_exists(DoctrineType::class, 'reset')) {
$container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']);
}
$entityManagers = [];
foreach (array_keys($config['entity_managers']) as $name) {
$entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
}
$container->setParameter('doctrine.entity_managers', $entityManagers);
if (empty($config['default_entity_manager'])) {
$tmp = array_keys($entityManagers);
$config['default_entity_manager'] = reset($tmp);
}
$container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
$options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
foreach ($options as $key) {
$container->setParameter('doctrine.orm.' . $key, $config[$key]);
}
$container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
$container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
$config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
$loadPropertyInfoExtractor = interface_exists('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface')
&& class_exists('Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
foreach ($config['entity_managers'] as $name => $entityManager) {
$entityManager['name'] = $name;
$this->loadOrmEntityManager($entityManager, $container);
if (! $loadPropertyInfoExtractor) {
continue;
}
$this->loadPropertyInfoExtractor($name, $container);
}
if ($config['resolve_target_entities']) {
$def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
foreach ($config['resolve_target_entities'] as $name => $implementation) {
$def->addMethodCall('addResolveTargetEntity', [
$name,
$implementation,
[],
]);
}
// BC: ResolveTargetEntityListener implements the subscriber interface since
// v2.5.0-beta1 (Commit 437f812)
if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
$def->addTag('doctrine.event_listener', ['event' => 'loadClassMetadata']);
} else {
$def->addTag('doctrine.event_subscriber');
}
}
// if is for Symfony 3.2 and lower compat
if (method_exists($container, 'registerForAutoconfiguration')) {
$container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
}
// If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
if (interface_exists(MessageBusInterface::class) && class_exists(DoctrineTransactionMiddleware::class)) {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('messenger.xml');
}
/*
* Compatibility for Symfony 3.2 and lower: gives the service a default argument.
* When DoctrineBundle requires 3.3 or higher, this can be moved to an anonymous
* service in orm.xml.
*
* This is replaced with a true locator by ServiceRepositoryCompilerPass.
* This makes that pass technically optional (good for tests).
*/
if (! class_exists(ServiceLocator::class)) {
return;
}
$container->getDefinition('doctrine.orm.container_repository_factory')
->replaceArgument(0, (new Definition(ServiceLocator::class))->setArgument(0, []));
}
/**
* Loads a configured ORM entity manager.
*
* @param array $entityManager A configured ORM entity manager.
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
{
$definitionClassname = $this->getDefinitionClassname();
$ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new $definitionClassname('doctrine.orm.configuration'));
$this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
$this->loadOrmCacheDrivers($entityManager, $container);
if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
$container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
} else {
$definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
$definition->addArgument(new Reference('service_container'));
$container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
}
$methods = [
'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
'setProxyDir' => '%doctrine.orm.proxy_dir%',
'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
];
// check for version to keep BC
if (version_compare(Version::VERSION, '2.3.0-DEV') >= 0) {
$methods = array_merge($methods, [
'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
]);
}
if (version_compare(Version::VERSION, '2.4.0-DEV') >= 0) {
$methods = array_merge($methods, [
'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
]);
}
if (version_compare(Version::VERSION, '2.5.0-DEV') >= 0) {
$listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
$listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
$listenerTagParams = ['event' => 'loadClassMetadata'];
if (isset($entityManager['connection'])) {
$listenerTagParams['connection'] = $entityManager['connection'];
}
$listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
}
if (isset($entityManager['second_level_cache'])) {
$this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
}
if ($entityManager['repository_factory']) {
$methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
}
foreach ($methods as $method => $arg) {
$ormConfigDef->addMethodCall($method, [$arg]);
}
foreach ($entityManager['hydrators'] as $name => $class) {
$ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
}
if (! empty($entityManager['dql'])) {
foreach ($entityManager['dql']['string_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
}
foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
}
foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
}
}
$enabledFilters = [];
$filtersParameters = [];
foreach ($entityManager['filters'] as $name => $filter) {
$ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
if ($filter['enabled']) {
$enabledFilters[] = $name;
}
if (! $filter['parameters']) {
continue;
}
$filtersParameters[$name] = $filter['parameters'];
}
$managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
$container
->setDefinition($managerConfiguratorName, new $definitionClassname('doctrine.orm.manager_configurator.abstract'))
->replaceArgument(0, $enabledFilters)
->replaceArgument(1, $filtersParameters);
if (! isset($entityManager['connection'])) {
$entityManager['connection'] = $this->defaultConnection;
}
$container
->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new $definitionClassname('doctrine.orm.entity_manager.abstract'))
->setPublic(true)
->setArguments([
new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
])
->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
$container->setAlias(
sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
);
if (! isset($entityManager['entity_listeners'])) {
return;
}
if (! isset($listenerDef)) {
throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
}
$entities = $entityManager['entity_listeners']['entities'];
foreach ($entities as $entityListenerClass => $entity) {
foreach ($entity['listeners'] as $listenerClass => $listener) {
foreach ($listener['events'] as $listenerEvent) {
$listenerEventName = $listenerEvent['type'];
$listenerMethod = $listenerEvent['method'];
$listenerDef->addMethodCall('addEntityListener', [
$entityListenerClass,
$listenerClass,
$listenerEventName,
$listenerMethod,
]);
}
}
}
}
/**
* Loads an ORM entity managers bundle mapping information.
*
* There are two distinct configuration possibilities for mapping information:
*
* 1. Specify a bundle and optionally details where the entity and mapping information reside.
* 2. Specify an arbitrary mapping location.
*
* @param array $entityManager A configured ORM entity manager
* @param Definition $ormConfigDef A Definition instance
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @example
*
* doctrine.orm:
* mappings:
* MyBundle1: ~
* MyBundle2: yml
* MyBundle3: { type: annotation, dir: Entities/ }
* MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
* MyBundle5:
* type: yml
* dir: bundle-mappings/
* alias: BundleAlias
* arbitrary_key:
* type: xml
* dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
* prefix: DoctrineExtensions\Entities\
* alias: DExt
*
* In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
* in the mappings key everything except alias is a required argument.
*/
protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
{
// reset state of drivers and alias map. They are only used by this methods and children.
$this->drivers = [];
$this->aliasMap = [];
$this->loadMappingInformation($entityManager, $container);
$this->registerMappingDrivers($entityManager, $container);
$ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
}
/**
* Loads an ORM second level cache bundle mapping information.
*
* @param array $entityManager A configured ORM entity manager
* @param Definition $ormConfigDef A Definition instance
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @example
* entity_managers:
* default:
* second_level_cache:
* region_cache_driver: apc
* log_enabled: true
* regions:
* my_service_region:
* type: service
* service : "my_service_region"
*
* my_query_region:
* lifetime: 300
* cache_driver: array
* type: filelock
*
* my_entity_region:
* lifetime: 600
* cache_driver:
* type: apc
*/
protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
{
if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer');
}
$driverId = null;
$enabled = $entityManager['second_level_cache']['enabled'];
if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
$driverName = 'second_level_cache.region_cache_driver';
$driverMap = $entityManager['second_level_cache']['region_cache_driver'];
$driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
}
$configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
$regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
$driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
$configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
$regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
$slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
$factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
$definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
$slcFactoryDef = $container
->setDefinition($slcFactoryId, $definition);
if (isset($entityManager['second_level_cache']['regions'])) {
foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
$regionRef = null;
$regionType = $region['type'];
if ($regionType === 'service') {
$regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
$regionRef = new Reference($region['service']);
$container->setAlias($regionId, new Alias($region['service'], false));
}
if ($regionType === 'default' || $regionType === 'filelock') {
$regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
$driverName = sprintf('second_level_cache.region.%s_driver', $name);
$driverMap = $region['cache_driver'];
$driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
$regionRef = new Reference($regionId);
$container
->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
->setArguments([$name, new Reference($driverId), $region['lifetime']]);
}
if ($regionType === 'filelock') {
$regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
$container
->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
$regionRef = new Reference($regionId);
$regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
}
$regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
$slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
}
}
if ($entityManager['second_level_cache']['log_enabled']) {
$loggerChainId = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
$loggerStatsId = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
$loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
$loggerStatsDef = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
$loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
$configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
$loggerId = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
$loggerRef = new Reference($logger['service']);
$container->setAlias($loggerId, new Alias($logger['service'], false));
$loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
}
}
$configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
$configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
$ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
$ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
}
/**
* {@inheritDoc}
*/
protected function getObjectManagerElementName($name)
{
return 'doctrine.orm.' . $name;
}
protected function getMappingObjectDefaultName()
{
return 'Entity';
}
/**
* {@inheritDoc}
*/
protected function getMappingResourceConfigDirectory()
{
return 'Resources/config/doctrine';
}
/**
* {@inheritDoc}
*/
protected function getMappingResourceExtension()
{
return 'orm';
}
/**
* {@inheritDoc}
*/
protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
{
if (! empty($driverMap['cache_provider'])) {
$aliasId = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
$serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
$container->setAlias($aliasId, new Alias($serviceId, false));
return $aliasId;
}
return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
}
/**
* Loads a configured entity managers cache drivers.
*
* @param array $entityManager A configured ORM entity manager.
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
{
$this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
$this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
$this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
}
/**
* Loads a property info extractor for each defined entity manager.
*
* @param string $entityManagerName
*/
private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
{
$propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
if (property_exists(DoctrineExtractor::class, 'entityManager')) {
$argumentId = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
} else {
$argumentId = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
$metadataFactoryDefinition = $container->register($argumentId, ClassMetadataFactory::class);
$metadataFactoryDefinition->setFactory([
new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
'getMetadataFactory',
]);
$metadataFactoryDefinition->setPublic(false);
}
$propertyExtractorDefinition->addArgument(new Reference($argumentId));
$propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
$propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
}
/**
* @param array $objectManager
* @param string $cacheName
*/
public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
{
$this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
}
/**
* {@inheritDoc}
*/
public function getXsdValidationBasePath()
{
return __DIR__ . '/../Resources/config/schema';
}
/**
* {@inheritDoc}
*/
public function getNamespace()
{
return 'http://symfony.com/schema/dic/doctrine';
}
/**
* {@inheritDoc}
*/
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($container->getParameter('kernel.debug'));
}
/**
* @return string
*/
private function getDefinitionClassname()
{
return class_exists(ChildDefinition::class) ? ChildDefinition::class : DefinitionDecorator::class;
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Proxy\Autoloader;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass;
use Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider\EntityFactory;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Bundle.
*/
class DoctrineBundle extends Bundle
{
/** @var callable|null */
private $autoloader;
/**
* {@inheritDoc}
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine.connections', 'doctrine.dbal.%s_connection.event_manager', 'doctrine'), PassConfig::TYPE_BEFORE_OPTIMIZATION);
if ($container->hasExtension('security')) {
$container->getExtension('security')->addUserProviderFactory(new EntityFactory('entity', 'doctrine.orm.security.user.provider'));
}
$container->addCompilerPass(new DoctrineValidationPass('orm'));
$container->addCompilerPass(new EntityListenerPass());
$container->addCompilerPass(new ServiceRepositoryCompilerPass());
}
/**
* {@inheritDoc}
*/
public function boot()
{
// Register an autoloader for proxies to avoid issues when unserializing them
// when the ORM is used.
if (! $this->container->hasParameter('doctrine.orm.proxy_namespace')) {
return;
}
$namespace = $this->container->getParameter('doctrine.orm.proxy_namespace');
$dir = $this->container->getParameter('doctrine.orm.proxy_dir');
$proxyGenerator = null;
if ($this->container->getParameter('doctrine.orm.auto_generate_proxy_classes')) {
// See https://github.com/symfony/symfony/pull/3419 for usage of references
$container = &$this->container;
$proxyGenerator = static function ($proxyDir, $proxyNamespace, $class) use (&$container) {
$originalClassName = ClassUtils::getRealClass($class);
/** @var Registry $registry */
$registry = $container->get('doctrine');
// Tries to auto-generate the proxy file
/** @var EntityManager $em */
foreach ($registry->getManagers() as $em) {
if (! $em->getConfiguration()->getAutoGenerateProxyClasses()) {
continue;
}
$metadataFactory = $em->getMetadataFactory();
if ($metadataFactory->isTransient($originalClassName)) {
continue;
}
$classMetadata = $metadataFactory->getMetadataFor($originalClassName);
$em->getProxyFactory()->generateProxyClasses([$classMetadata]);
clearstatcache(true, Autoloader::resolveFile($proxyDir, $proxyNamespace, $class));
break;
}
};
}
$this->autoloader = Autoloader::register($dir, $namespace, $proxyGenerator);
}
/**
* {@inheritDoc}
*/
public function shutdown()
{
if ($this->autoloader !== null) {
spl_autoload_unregister($this->autoloader);
$this->autoloader = null;
}
// Clear all entity managers to clear references to entities for GC
if ($this->container->hasParameter('doctrine.entity_managers')) {
foreach ($this->container->getParameter('doctrine.entity_managers') as $id) {
if (method_exists($this->container, 'initialized') && ! $this->container->initialized($id)) {
continue;
}
$this->container->get($id)->clear();
}
}
// Close all connections to avoid reaching too many connections in the process when booting again later (tests)
if (! $this->container->hasParameter('doctrine.connections')) {
return;
}
foreach ($this->container->getParameter('doctrine.connections') as $id) {
if (method_exists($this->container, 'initialized') && ! $this->container->initialized($id)) {
continue;
}
$this->container->get($id)->close();
}
}
/**
* {@inheritDoc}
*/
public function registerCommands(Application $application)
{
}
}

13
vendor/doctrine/doctrine-bundle/LICENSE vendored Normal file
View File

@@ -0,0 +1,13 @@
Copyright (c) 2011 Fabien Potencier, Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,74 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Filter\SQLFilter;
/**
* Configurator for an EntityManager
*/
class ManagerConfigurator
{
/** @var string[] */
private $enabledFilters = [];
/** @var string[] */
private $filtersParameters = [];
/**
* @param string[] $enabledFilters
* @param string[] $filtersParameters
*/
public function __construct(array $enabledFilters, array $filtersParameters)
{
$this->enabledFilters = $enabledFilters;
$this->filtersParameters = $filtersParameters;
}
/**
* Create a connection by name.
*/
public function configure(EntityManagerInterface $entityManager)
{
$this->enableFilters($entityManager);
}
/**
* Enables filters for a given entity manager
*/
private function enableFilters(EntityManagerInterface $entityManager)
{
if (empty($this->enabledFilters)) {
return;
}
$filterCollection = $entityManager->getFilters();
foreach ($this->enabledFilters as $filter) {
$filterObject = $filterCollection->enable($filter);
if ($filterObject === null) {
continue;
}
$this->setFilterParameters($filter, $filterObject);
}
}
/**
* Sets default parameters for a given filter
*
* @param string $name Filter name
* @param SQLFilter $filter Filter object
*/
private function setFilterParameters($name, SQLFilter $filter)
{
if (empty($this->filtersParameters[$name])) {
return;
}
$parameters = $this->filtersParameters[$name];
foreach ($parameters as $paramName => $paramValue) {
$filter->setParameter($paramName, $paramValue);
}
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
class ClassMetadataCollection
{
/** @var string */
private $path;
/** @var string */
private $namespace;
/** @var ClassMetadata[] */
private $metadata;
/**
* Constructor
*
* @param ClassMetadata[] $metadata
*/
public function __construct(array $metadata)
{
$this->metadata = $metadata;
}
/**
* @return ClassMetadata[]
*/
public function getMetadata()
{
return $this->metadata;
}
/**
* @param string $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @param string $namespace
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ContainerAwareEntityListenerResolver implements EntityListenerServiceResolver
{
/** @var ContainerInterface */
private $container;
/** @var object[] Map to store entity listener instances. */
private $instances = [];
/** @var string[] Map to store registered service ids */
private $serviceIds = [];
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* {@inheritdoc}
*/
public function clear($className = null)
{
if ($className === null) {
$this->instances = [];
return;
}
$className = $this->normalizeClassName($className);
if (! isset($this->instances[$className])) {
return;
}
unset($this->instances[$className]);
}
/**
* {@inheritdoc}
*/
public function register($object)
{
if (! is_object($object)) {
throw new InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object)));
}
$className = $this->normalizeClassName(get_class($object));
$this->instances[$className] = $object;
}
/**
* {@inheritdoc}
*/
public function registerService($className, $serviceId)
{
$this->serviceIds[$this->normalizeClassName($className)] = $serviceId;
}
/**
* {@inheritdoc}
*/
public function resolve($className)
{
$className = $this->normalizeClassName($className);
if (! isset($this->instances[$className])) {
if (isset($this->serviceIds[$className])) {
$this->instances[$className] = $this->resolveService($this->serviceIds[$className]);
} else {
$this->instances[$className] = new $className();
}
}
return $this->instances[$className];
}
/**
* @param string $serviceId
*
* @return object
*/
private function resolveService($serviceId)
{
if (! $this->container->has($serviceId)) {
throw new RuntimeException(sprintf('There is no service named "%s"', $serviceId));
}
return $this->container->get($serviceId);
}
/**
* @param string $className
*
* @return string
*/
private function normalizeClassName($className)
{
return trim($className, '\\');
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
use ReflectionClass;
use RuntimeException;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* This class provides methods to access Doctrine entity class metadata for a
* given bundle, namespace or entity class, for generation purposes
*/
class DisconnectedMetadataFactory
{
/** @var ManagerRegistry */
private $registry;
/**
* @param ManagerRegistry $registry A ManagerRegistry instance
*/
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
}
/**
* Gets the metadata of all classes of a bundle.
*
* @param BundleInterface $bundle A BundleInterface instance
*
* @return ClassMetadataCollection A ClassMetadataCollection instance
*
* @throws RuntimeException When bundle does not contain mapped entities.
*/
public function getBundleMetadata(BundleInterface $bundle)
{
$namespace = $bundle->getNamespace();
$metadata = $this->getMetadataForNamespace($namespace);
if (! $metadata->getMetadata()) {
throw new RuntimeException(sprintf('Bundle "%s" does not contain any mapped entities.', $bundle->getName()));
}
$path = $this->getBasePathForClass($bundle->getName(), $bundle->getNamespace(), $bundle->getPath());
$metadata->setPath($path);
$metadata->setNamespace($bundle->getNamespace());
return $metadata;
}
/**
* Gets the metadata of a class.
*
* @param string $class A class name
* @param string $path The path where the class is stored (if known)
*
* @return ClassMetadataCollection A ClassMetadataCollection instance
*
* @throws MappingException When class is not valid entity or mapped superclass.
*/
public function getClassMetadata($class, $path = null)
{
$metadata = $this->getMetadataForClass($class);
if (! $metadata->getMetadata()) {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($class);
}
$this->findNamespaceAndPathForMetadata($metadata, $path);
return $metadata;
}
/**
* Gets the metadata of all classes of a namespace.
*
* @param string $namespace A namespace name
* @param string $path The path where the class is stored (if known)
*
* @return ClassMetadataCollection A ClassMetadataCollection instance
*
* @throws RuntimeException When namespace not contain mapped entities.
*/
public function getNamespaceMetadata($namespace, $path = null)
{
$metadata = $this->getMetadataForNamespace($namespace);
if (! $metadata->getMetadata()) {
throw new RuntimeException(sprintf('Namespace "%s" does not contain any mapped entities.', $namespace));
}
$this->findNamespaceAndPathForMetadata($metadata, $path);
return $metadata;
}
/**
* Find and configure path and namespace for the metadata collection.
*
* @param string|null $path
*
* @throws RuntimeException When unable to determine the path.
*/
public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadata, $path = null)
{
$all = $metadata->getMetadata();
if (class_exists($all[0]->name)) {
$r = new ReflectionClass($all[0]->name);
$path = $this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename()));
$ns = $r->getNamespaceName();
} elseif ($path) {
// Get namespace by removing the last component of the FQCN
$nsParts = explode('\\', $all[0]->name);
array_pop($nsParts);
$ns = implode('\\', $nsParts);
} else {
throw new RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name));
}
$metadata->setPath($path);
$metadata->setNamespace($ns);
}
/**
* Get a base path for a class
*
* @param string $name class name
* @param string $namespace class namespace
* @param string $path class path
*
* @return string
*
* @throws RuntimeException When base path not found.
*/
private function getBasePathForClass($name, $namespace, $path)
{
$namespace = str_replace('\\', '/', $namespace);
$search = str_replace('\\', '/', $path);
$destination = str_replace('/' . $namespace, '', $search, $c);
if ($c !== 1) {
throw new RuntimeException(sprintf('Can\'t find base path for "%s" (path: "%s", destination: "%s").', $name, $path, $destination));
}
return $destination;
}
/**
* @param string $namespace
*
* @return ClassMetadataCollection
*/
private function getMetadataForNamespace($namespace)
{
$metadata = [];
foreach ($this->getAllMetadata() as $m) {
if (strpos($m->name, $namespace) !== 0) {
continue;
}
$metadata[] = $m;
}
return new ClassMetadataCollection($metadata);
}
/**
* @param string $entity
*
* @return ClassMetadataCollection
*/
private function getMetadataForClass($entity)
{
foreach ($this->registry->getManagers() as $em) {
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
if (! $cmf->isTransient($entity)) {
return new ClassMetadataCollection([$cmf->getMetadataFor($entity)]);
}
}
return new ClassMetadataCollection([]);
}
/**
* @return ClassMetadata[]
*/
private function getAllMetadata()
{
$metadata = [];
foreach ($this->registry->getManagers() as $em) {
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
foreach ($cmf->getAllMetadata() as $m) {
$metadata[] = $m;
}
}
return $metadata;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
use Doctrine\ORM\Mapping\EntityListenerResolver;
interface EntityListenerServiceResolver extends EntityListenerResolver
{
/**
* @param string $className
* @param string $serviceId
*/
public function registerService($className, $serviceId);
}

View File

@@ -0,0 +1,179 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\ORMException;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* References all Doctrine connections and entity managers in a given Container.
*/
class Registry extends ManagerRegistry implements RegistryInterface
{
/**
* Construct.
*
* @param Connection[] $connections
* @param EntityManagerInterface[] $entityManagers
* @param string $defaultConnection
* @param string $defaultEntityManager
*/
public function __construct(ContainerInterface $container, array $connections, array $entityManagers, $defaultConnection, $defaultEntityManager)
{
$parentTraits = class_uses(parent::class);
if (isset($parentTraits[ContainerAwareTrait::class])) {
// this case should be removed when Symfony 3.4 becomes the lowest supported version
// and then also, the constructor should type-hint Psr\Container\ContainerInterface
$this->setContainer($container);
} else {
$this->container = $container;
}
parent::__construct('ORM', $connections, $entityManagers, $defaultConnection, $defaultEntityManager, 'Doctrine\ORM\Proxy\Proxy');
}
/**
* Gets the default entity manager name.
*
* @deprecated
*
* @return string The default entity manager name
*/
public function getDefaultEntityManagerName()
{
@trigger_error('getDefaultEntityManagerName is deprecated since Symfony 2.1. Use getDefaultManagerName instead', E_USER_DEPRECATED);
return $this->getDefaultManagerName();
}
/**
* Gets a named entity manager.
*
* @deprecated
*
* @param string $name The entity manager name (null for the default one)
*
* @return EntityManager
*/
public function getEntityManager($name = null)
{
@trigger_error('getEntityManager is deprecated since Symfony 2.1. Use getManager instead', E_USER_DEPRECATED);
return $this->getManager($name);
}
/**
* Gets an array of all registered entity managers
*
* @deprecated
*
* @return EntityManager[] an array of all EntityManager instances
*/
public function getEntityManagers()
{
@trigger_error('getEntityManagers is deprecated since Symfony 2.1. Use getManagers instead', E_USER_DEPRECATED);
return $this->getManagers();
}
/**
* Resets a named entity manager.
*
* This method is useful when an entity manager has been closed
* because of a rollbacked transaction AND when you think that
* it makes sense to get a new one to replace the closed one.
*
* Be warned that you will get a brand new entity manager as
* the existing one is not usable anymore. This means that any
* other object with a dependency on this entity manager will
* hold an obsolete reference. You can inject the registry instead
* to avoid this problem.
*
* @deprecated
*
* @param string $name The entity manager name (null for the default one)
*/
public function resetEntityManager($name = null)
{
@trigger_error('resetEntityManager is deprecated since Symfony 2.1. Use resetManager instead', E_USER_DEPRECATED);
$this->resetManager($name);
}
/**
* Resolves a registered namespace alias to the full namespace.
*
* This method looks for the alias in all registered entity managers.
*
* @deprecated
*
* @param string $alias The alias
*
* @return string The full namespace
*/
public function getEntityNamespace($alias)
{
@trigger_error('getEntityNamespace is deprecated since Symfony 2.1. Use getAliasNamespace instead', E_USER_DEPRECATED);
return $this->getAliasNamespace($alias);
}
/**
* Resolves a registered namespace alias to the full namespace.
*
* This method looks for the alias in all registered entity managers.
*
* @see Configuration::getEntityNamespace
*
* @param string $alias The alias
*
* @return string The full namespace
*/
public function getAliasNamespace($alias)
{
foreach (array_keys($this->getManagers()) as $name) {
try {
return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias);
} catch (ORMException $e) {
}
}
throw ORMException::unknownEntityNamespace($alias);
}
/**
* Gets all connection names.
*
* @deprecated
*
* @return string[] An array of connection names
*/
public function getEntityManagerNames()
{
@trigger_error('getEntityManagerNames is deprecated since Symfony 2.1. Use getManagerNames instead', E_USER_DEPRECATED);
return $this->getManagerNames();
}
/**
* Gets the entity manager associated with a given class.
*
* @deprecated
*
* @param string $class A Doctrine Entity class name
*
* @return EntityManager|null
*/
public function getEntityManagerForClass($class)
{
@trigger_error('getEntityManagerForClass is deprecated since Symfony 2.1. Use getManagerForClass instead', E_USER_DEPRECATED);
return $this->getManagerForClass($class);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Repository;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Repository\RepositoryFactory;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
/**
* Fetches repositories from the container or falls back to normal creation.
*/
final class ContainerRepositoryFactory implements RepositoryFactory
{
/** @var ObjectRepository[] */
private $managedRepositories = [];
/** @var ContainerInterface|null */
private $container;
/**
* @param ContainerInterface $container A service locator containing the repositories
*/
public function __construct(ContainerInterface $container = null)
{
// When DoctrineBundle requires Symfony 3.3+, this can be removed
// and the $container argument can become required.
if ($container === null && class_exists(ServiceLocatorTagPass::class)) {
throw new InvalidArgumentException(sprintf('The first argument of %s::__construct() is required on Symfony 3.3 or higher.', self::class));
}
$this->container = $container;
}
/**
* {@inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName)
{
$metadata = $entityManager->getClassMetadata($entityName);
$repositoryServiceId = $metadata->customRepositoryClassName;
$customRepositoryName = $metadata->customRepositoryClassName;
if ($customRepositoryName !== null) {
// fetch from the container
if ($this->container && $this->container->has($customRepositoryName)) {
$repository = $this->container->get($customRepositoryName);
if (! $repository instanceof ObjectRepository) {
throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).', $repositoryServiceId));
}
return $repository;
}
// if not in the container but the class/id implements the interface, throw an error
if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
// can be removed when DoctrineBundle requires Symfony 3.3
if ($this->container === null) {
throw new RuntimeException(sprintf('Support for loading entities from the service container only works for Symfony 3.3 or higher. Upgrade your version of Symfony or make sure your "%s" class does not implement "%s"', $customRepositoryName, ServiceEntityRepositoryInterface::class));
}
throw new RuntimeException(sprintf('The "%s" entity repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".', $customRepositoryName, ServiceEntityRepositoryInterface::class, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
}
if (! class_exists($customRepositoryName)) {
throw new RuntimeException(sprintf('The "%s" entity has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".', $metadata->name, $customRepositoryName, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
}
// allow the repository to be created below
}
return $this->getOrCreateRepository($entityManager, $metadata);
}
private function getOrCreateRepository(EntityManagerInterface $entityManager, ClassMetadata $metadata)
{
$repositoryHash = $metadata->getName() . spl_object_hash($entityManager);
if (isset($this->managedRepositories[$repositoryHash])) {
return $this->managedRepositories[$repositoryHash];
}
$repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Repository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityRepository;
use LogicException;
/**
* Optional EntityRepository base class with a simplified constructor (for autowiring).
*
* To use in your class, inject the "registry" service and call
* the parent constructor. For example:
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(RegistryInterface $registry)
* {
* parent::__construct($registry, YourEntity::class);
* }
* }
*/
class ServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
/**
* @param string $entityClass The class name of the entity this repository manages
*/
public function __construct(ManagerRegistry $registry, $entityClass)
{
$manager = $registry->getManagerForClass($entityClass);
if ($manager === null) {
throw new LogicException(sprintf(
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entitys metadata.',
$entityClass
));
}
parent::__construct($manager, $manager->getClassMetadata($entityClass));
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Repository;
/**
* This interface signals that your repository should be loaded from the container.
*/
interface ServiceEntityRepositoryInterface
{
}

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="doctrine.dbal.logger.chain.class">Doctrine\DBAL\Logging\LoggerChain</parameter>
<parameter key="doctrine.dbal.logger.profiling.class">Doctrine\DBAL\Logging\DebugStack</parameter>
<parameter key="doctrine.dbal.logger.class">Symfony\Bridge\Doctrine\Logger\DbalLogger</parameter>
<parameter key="doctrine.dbal.configuration.class">Doctrine\DBAL\Configuration</parameter>
<parameter key="doctrine.data_collector.class">Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector</parameter>
<parameter key="doctrine.dbal.connection.event_manager.class">Symfony\Bridge\Doctrine\ContainerAwareEventManager</parameter>
<parameter key="doctrine.dbal.connection_factory.class">Doctrine\Bundle\DoctrineBundle\ConnectionFactory</parameter>
<parameter key="doctrine.dbal.events.mysql_session_init.class">Doctrine\DBAL\Event\Listeners\MysqlSessionInit</parameter>
<parameter key="doctrine.dbal.events.oracle_session_init.class">Doctrine\DBAL\Event\Listeners\OracleSessionInit</parameter>
<parameter key="doctrine.class">Doctrine\Bundle\DoctrineBundle\Registry</parameter>
<parameter key="doctrine.entity_managers" type="collection"></parameter>
<parameter key="doctrine.default_entity_manager"></parameter>
</parameters>
<services>
<service id="Doctrine\DBAL\Driver\Connection" alias="database_connection" public="false" />
<service id="Doctrine\DBAL\Connection" alias="database_connection" public="false" />
<service id="doctrine.dbal.logger.chain" class="%doctrine.dbal.logger.chain.class%" public="false" abstract="true">
<call method="addLogger">
<argument type="service" id="doctrine.dbal.logger" />
</call>
</service>
<service id="doctrine.dbal.logger.profiling" class="%doctrine.dbal.logger.profiling.class%" public="false" abstract="true" />
<service id="doctrine.dbal.logger" class="%doctrine.dbal.logger.class%" public="false">
<tag name="monolog.logger" channel="doctrine" />
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</service>
<service id="data_collector.doctrine" class="%doctrine.data_collector.class%" public="false">
<tag name="data_collector" template="@Doctrine/Collector/db.html.twig" id="db" priority="250" />
<argument type="service" id="doctrine" />
</service>
<service id="doctrine.dbal.connection_factory" class="%doctrine.dbal.connection_factory.class%">
<argument>%doctrine.dbal.connection_factory.types%</argument>
</service>
<service id="doctrine.dbal.connection" class="Doctrine\DBAL\Connection" abstract="true">
<factory service="doctrine.dbal.connection_factory" method="createConnection" />
</service>
<service id="doctrine.dbal.connection.event_manager" class="%doctrine.dbal.connection.event_manager.class%" public="false" abstract="true">
<argument type="service" id="service_container" />
</service>
<service id="doctrine.dbal.connection.configuration" class="%doctrine.dbal.configuration.class%" public="false" abstract="true" />
<service id="doctrine" class="%doctrine.class%" public="true">
<argument type="service" id="service_container" />
<argument>%doctrine.connections%</argument>
<argument>%doctrine.entity_managers%</argument>
<argument>%doctrine.default_connection%</argument>
<argument>%doctrine.default_entity_manager%</argument>
</service>
<service id="Doctrine\Common\Persistence\ManagerRegistry" alias="doctrine" public="false" />
<service id="Symfony\Bridge\Doctrine\RegistryInterface" alias="doctrine" public="false" />
<service id="doctrine.twig.doctrine_extension" class="Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension" public="false">
<tag name="twig.extension" />
</service>
<!-- commands -->
<service id="doctrine.database_create_command" class="Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand">
<argument type="service" id="doctrine" />
<tag name="console.command" command="doctrine:database:create" />
</service>
<service id="doctrine.database_drop_command" class="Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand">
<argument type="service" id="doctrine" />
<tag name="console.command" command="doctrine:database:drop" />
</service>
<service id="doctrine.generate_entities_command" class="Doctrine\Bundle\DoctrineBundle\Command\GenerateEntitiesDoctrineCommand">
<tag name="console.command" command="doctrine:generate:entities" />
</service>
<service id="doctrine.query_sql_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunSqlDoctrineCommand">
<tag name="console.command" command="doctrine:query:sql" />
</service>
</services>
</container>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!--
The following service isn't prefixed by the "doctrine.orm" namespace in order for end-users to just use
the "doctrine_transaction" shortcut in message buses middleware config
-->
<service id="messenger.middleware.doctrine_transaction" class="Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware" abstract="true" public="false">
<argument type="service" id="doctrine" />
</service>
</services>
</container>

View File

@@ -0,0 +1,217 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="doctrine.orm.configuration.class">Doctrine\ORM\Configuration</parameter>
<parameter key="doctrine.orm.entity_manager.class">Doctrine\ORM\EntityManager</parameter>
<parameter key="doctrine.orm.manager_configurator.class">Doctrine\Bundle\DoctrineBundle\ManagerConfigurator</parameter>
<!-- cache -->
<parameter key="doctrine.orm.cache.array.class">Doctrine\Common\Cache\ArrayCache</parameter>
<parameter key="doctrine.orm.cache.apc.class">Doctrine\Common\Cache\ApcCache</parameter>
<parameter key="doctrine.orm.cache.memcache.class">Doctrine\Common\Cache\MemcacheCache</parameter>
<parameter key="doctrine.orm.cache.memcache_host">localhost</parameter>
<parameter key="doctrine.orm.cache.memcache_port">11211</parameter>
<parameter key="doctrine.orm.cache.memcache_instance.class">Memcache</parameter>
<parameter key="doctrine.orm.cache.memcached.class">Doctrine\Common\Cache\MemcachedCache</parameter>
<parameter key="doctrine.orm.cache.memcached_host">localhost</parameter>
<parameter key="doctrine.orm.cache.memcached_port">11211</parameter>
<parameter key="doctrine.orm.cache.memcached_instance.class">Memcached</parameter>
<parameter key="doctrine.orm.cache.redis.class">Doctrine\Common\Cache\RedisCache</parameter>
<parameter key="doctrine.orm.cache.redis_host">localhost</parameter>
<parameter key="doctrine.orm.cache.redis_port">6379</parameter>
<parameter key="doctrine.orm.cache.redis_instance.class">Redis</parameter>
<parameter key="doctrine.orm.cache.xcache.class">Doctrine\Common\Cache\XcacheCache</parameter>
<parameter key="doctrine.orm.cache.wincache.class">Doctrine\Common\Cache\WinCacheCache</parameter>
<parameter key="doctrine.orm.cache.zenddata.class">Doctrine\Common\Cache\ZendDataCache</parameter>
<!-- metadata -->
<parameter key="doctrine.orm.metadata.driver_chain.class">Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain</parameter>
<parameter key="doctrine.orm.metadata.annotation.class">Doctrine\ORM\Mapping\Driver\AnnotationDriver</parameter>
<parameter key="doctrine.orm.metadata.xml.class">Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver</parameter>
<parameter key="doctrine.orm.metadata.yml.class">Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver</parameter>
<parameter key="doctrine.orm.metadata.php.class">Doctrine\ORM\Mapping\Driver\PHPDriver</parameter>
<parameter key="doctrine.orm.metadata.staticphp.class">Doctrine\ORM\Mapping\Driver\StaticPHPDriver</parameter>
<!-- cache warmer -->
<parameter key="doctrine.orm.proxy_cache_warmer.class">Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer</parameter>
<!-- form field factory guesser -->
<parameter key="form.type_guesser.doctrine.class">Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser</parameter>
<!-- validator -->
<parameter key="doctrine.orm.validator.unique.class">Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator</parameter>
<parameter key="doctrine.orm.validator_initializer.class">Symfony\Bridge\Doctrine\Validator\DoctrineInitializer</parameter>
<!-- security -->
<parameter key="doctrine.orm.security.user.provider.class">Symfony\Bridge\Doctrine\Security\User\EntityUserProvider</parameter>
<!-- listeners -->
<parameter key="doctrine.orm.listeners.resolve_target_entity.class">Doctrine\ORM\Tools\ResolveTargetEntityListener</parameter>
<parameter key="doctrine.orm.listeners.attach_entity_listeners.class">Doctrine\ORM\Tools\AttachEntityListenersListener</parameter>
<!-- naming strategy -->
<parameter key="doctrine.orm.naming_strategy.default.class">Doctrine\ORM\Mapping\DefaultNamingStrategy</parameter>
<parameter key="doctrine.orm.naming_strategy.underscore.class">Doctrine\ORM\Mapping\UnderscoreNamingStrategy</parameter>
<!-- quote strategy -->
<parameter key="doctrine.orm.quote_strategy.default.class">Doctrine\ORM\Mapping\DefaultQuoteStrategy</parameter>
<parameter key="doctrine.orm.quote_strategy.ansi.class">Doctrine\ORM\Mapping\AnsiQuoteStrategy</parameter>
<!-- entity listener resolver -->
<parameter key="doctrine.orm.entity_listener_resolver.class">Doctrine\Bundle\DoctrineBundle\Mapping\ContainerAwareEntityListenerResolver</parameter>
<!-- second level cache -->
<parameter key="doctrine.orm.second_level_cache.default_cache_factory.class">Doctrine\ORM\Cache\DefaultCacheFactory</parameter>
<parameter key="doctrine.orm.second_level_cache.default_region.class">Doctrine\ORM\Cache\Region\DefaultRegion</parameter>
<parameter key="doctrine.orm.second_level_cache.filelock_region.class">Doctrine\ORM\Cache\Region\FileLockRegion</parameter>
<parameter key="doctrine.orm.second_level_cache.logger_chain.class">Doctrine\ORM\Cache\Logging\CacheLoggerChain</parameter>
<parameter key="doctrine.orm.second_level_cache.logger_statistics.class">Doctrine\ORM\Cache\Logging\StatisticsCacheLogger</parameter>
<parameter key="doctrine.orm.second_level_cache.cache_configuration.class">Doctrine\ORM\Cache\CacheConfiguration</parameter>
<parameter key="doctrine.orm.second_level_cache.regions_configuration.class">Doctrine\ORM\Cache\RegionsConfiguration</parameter>
</parameters>
<services>
<service id="Doctrine\Common\Persistence\ObjectManager" alias="doctrine.orm.entity_manager" public="false" />
<service id="Doctrine\ORM\EntityManagerInterface" alias="doctrine.orm.entity_manager" public="false" />
<!--- Deprecated Annotation Metadata Reader Service alias, use annotation_reader service -->
<service id="doctrine.orm.metadata.annotation_reader" alias="annotation_reader" public="false" />
<service id="doctrine.orm.proxy_cache_warmer" class="%doctrine.orm.proxy_cache_warmer.class%" public="false">
<tag name="kernel.cache_warmer" />
<argument type="service" id="doctrine" />
</service>
<service id="form.type_guesser.doctrine" class="%form.type_guesser.doctrine.class%">
<tag name="form.type_guesser" />
<argument type="service" id="doctrine" />
</service>
<service id="form.type.entity" class="Symfony\Bridge\Doctrine\Form\Type\EntityType">
<tag name="form.type" alias="entity" />
<argument type="service" id="doctrine" />
</service>
<service id="doctrine.orm.configuration" class="%doctrine.orm.configuration.class%" abstract="true" public="false" />
<service id="doctrine.orm.entity_manager.abstract" class="%doctrine.orm.entity_manager.class%" abstract="true" lazy="true">
<factory class="%doctrine.orm.entity_manager.class%" method="create" />
</service>
<service id="doctrine.orm.container_repository_factory" class="Doctrine\Bundle\DoctrineBundle\Repository\ContainerRepositoryFactory" public="false">
<argument>null</argument> <!-- repository locator -->
</service>
<!-- The configurator cannot be a private service -->
<service id="doctrine.orm.manager_configurator.abstract" class="%doctrine.orm.manager_configurator.class%" abstract="true">
<argument type="collection" />
<argument type="collection" />
</service>
<!-- validator -->
<service id="doctrine.orm.validator.unique" class="%doctrine.orm.validator.unique.class%">
<tag name="validator.constraint_validator" alias="doctrine.orm.validator.unique" />
<argument type="service" id="doctrine" />
</service>
<service id="doctrine.orm.validator_initializer" class="%doctrine.orm.validator_initializer.class%">
<tag name="validator.initializer" />
<argument type="service" id="doctrine" />
</service>
<!-- security -->
<service id="doctrine.orm.security.user.provider" class="%doctrine.orm.security.user.provider.class%" abstract="true" public="false">
<argument type="service" id="doctrine" />
</service>
<!-- listeners -->
<service id="doctrine.orm.listeners.resolve_target_entity" class="%doctrine.orm.listeners.resolve_target_entity.class%" public="false" />
<!-- naming strategy -->
<service id="doctrine.orm.naming_strategy.default" class="%doctrine.orm.naming_strategy.default.class%" public="false" />
<service id="doctrine.orm.naming_strategy.underscore" class="%doctrine.orm.naming_strategy.underscore.class%" public="false" />
<!-- quote strategy -->
<service id="doctrine.orm.quote_strategy.default" class="%doctrine.orm.quote_strategy.default.class%" public="false" />
<service id="doctrine.orm.quote_strategy.ansi" class="%doctrine.orm.quote_strategy.ansi.class%" public="false" />
<!-- commands -->
<service id="doctrine.cache_clear_metadata_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ClearMetadataCacheDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-metadata" />
</service>
<service id="doctrine.cache_clear_query_cache_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ClearQueryCacheDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-query" />
</service>
<service id="doctrine.cache_clear_result_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ClearResultCacheDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-result" />
</service>
<service id="doctrine.cache_collection_region_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\CollectionRegionDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-collection-region" />
</service>
<service id="doctrine.mapping_convert_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ConvertMappingDoctrineCommand">
<tag name="console.command" command="doctrine:mapping:convert" />
</service>
<service id="doctrine.schema_create_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\CreateSchemaDoctrineCommand">
<tag name="console.command" command="doctrine:schema:create" />
</service>
<service id="doctrine.schema_drop_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\DropSchemaDoctrineCommand">
<tag name="console.command" command="doctrine:schema:drop" />
</service>
<service id="doctrine.ensure_production_settings_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\EnsureProductionSettingsDoctrineCommand">
<tag name="console.command" command="doctrine:ensure-production-settings" />
</service>
<service id="doctrine.clear_entity_region_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\EntityRegionCacheDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-entity-region" />
</service>
<service id="doctrine.database_import_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ImportDoctrineCommand">
<tag name="console.command" command="doctrine:database:import" />
</service>
<service id="doctrine.mapping_info_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\InfoDoctrineCommand">
<tag name="console.command" command="doctrine:mapping:info" />
</service>
<service id="doctrine.clear_query_region_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\QueryRegionCacheDoctrineCommand">
<tag name="console.command" command="doctrine:cache:clear-query-region" />
</service>
<service id="doctrine.query_dql_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunDqlDoctrineCommand">
<tag name="console.command" command="doctrine:query:dql" />
</service>
<service id="doctrine.schema_update_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand">
<tag name="console.command" command="doctrine:schema:update" />
</service>
<service id="doctrine.schema_validate_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\ValidateSchemaCommand">
<tag name="console.command" command="doctrine:schema:validate" />
</service>
<service id="doctrine.mapping_import_command" class="Doctrine\Bundle\DoctrineBundle\Command\ImportMappingDoctrineCommand">
<argument type="service" id="doctrine" />
<argument>%kernel.bundles%</argument>
<tag name="console.command" command="doctrine:mapping:import" />
</service>
<service id="doctrine.generate_entities_command" class="Doctrine\Bundle\DoctrineBundle\Command\GenerateEntitiesDoctrineCommand">
<argument type="service" id="doctrine" />
<tag name="console.command" command="doctrine:generate:entities" />
</service>
</services>
</container>

View File

@@ -0,0 +1,278 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema/dic/doctrine"
elementFormDefault="qualified">
<xsd:element name="config">
<xsd:complexType>
<xsd:all>
<xsd:element name="dbal" type="dbal" minOccurs="0" maxOccurs="1" />
<xsd:element name="orm" type="orm" minOccurs="0" maxOccurs="1" />
</xsd:all>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="named_scalar">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<!-- DBAL configuration -->
<xsd:attributeGroup name="connection-config">
<xsd:attribute name="driver" type="xsd:string" />
<xsd:attribute name="driver-class" type="xsd:string" />
<xsd:attribute name="wrapper-class" type="xsd:string" />
<xsd:attribute name="keep-slave" type="xsd:string" />
<xsd:attribute name="platform-service" type="xsd:string" />
<xsd:attribute name="shard-choser" type="xsd:string" />
<xsd:attribute name="shard-choser-service" type="xsd:string" />
<xsd:attribute name="auto-commit" type="xsd:string" />
<xsd:attribute name="schema-filter" type="xsd:string" />
<xsd:attribute name="logging" type="xsd:string" default="false" />
<xsd:attribute name="profiling" type="xsd:string" default="false" />
<xsd:attribute name="server-version" type="xsd:string" />
<xsd:attribute name="use-savepoints" type="xsd:boolean" />
<xsd:attributeGroup ref="driver-config" />
</xsd:attributeGroup>
<xsd:attributeGroup name="driver-config">
<xsd:attribute name="url" type="xsd:string" />
<xsd:attribute name="dbname" type="xsd:string" />
<xsd:attribute name="host" type="xsd:string" />
<xsd:attribute name="port" type="xsd:string" />
<xsd:attribute name="user" type="xsd:string" />
<xsd:attribute name="password" type="xsd:string" />
<xsd:attribute name="application-name" type="xsd:string" />
<xsd:attribute name="path" type="xsd:string" />
<xsd:attribute name="unix-socket" type="xsd:string" />
<xsd:attribute name="memory" type="xsd:string" />
<xsd:attribute name="charset" type="xsd:string" />
<xsd:attribute name="persistent" type="xsd:string" />
<xsd:attribute name="protocol" type="xsd:string" />
<xsd:attribute name="server" type="xsd:string" />
<xsd:attribute name="service" type="xsd:string" />
<xsd:attribute name="servicename" type="xsd:string" />
<xsd:attribute name="session-mode" type="xsd:string" />
<xsd:attribute name="default_dbname" type="xsd:string" />
<xsd:attribute name="sslmode" type="xsd:string" />
<xsd:attribute name="sslrootcert" type="xsd:string" />
<xsd:attribute name="sslcert" type="xsd:string" />
<xsd:attribute name="sslkey" type="xsd:string" />
<xsd:attribute name="sslcrl" type="xsd:string" />
<xsd:attribute name="pooled" type="xsd:string" />
<xsd:attribute name="multiple-active-result-sets" type="xsd:string" />
<xsd:attribute name="connectstring" type="xsd:string" />
<xsd:attribute name="instancename" type="xsd:string" />
</xsd:attributeGroup>
<xsd:group name="connection-child-config">
<xsd:choice>
<xsd:element name="option" type="option" />
<xsd:element name="mapping-type" type="named_scalar" />
<xsd:element name="slave" type="slave" />
<xsd:element name="shard" type="shard" />
<xsd:element name="default-table-option" type="named_scalar" />
</xsd:choice>
</xsd:group>
<xsd:complexType name="dbal">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="connection" type="connection" />
<xsd:element name="type" type="named_scalar" />
<xsd:group ref="connection-child-config" />
</xsd:choice>
<xsd:attribute name="default-connection" type="xsd:string" />
<xsd:attributeGroup ref="connection-config" />
</xsd:complexType>
<xsd:complexType name="option">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="key" type="xsd:string" use="required" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="connection">
<xsd:group ref="connection-child-config" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attributeGroup ref="connection-config" />
</xsd:complexType>
<xsd:complexType name="slave">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attributeGroup ref="driver-config" />
</xsd:complexType>
<xsd:complexType name="shard">
<xsd:attribute name="id" type="xsd:integer" use="required" />
<xsd:attributeGroup ref="driver-config" />
</xsd:complexType>
<!-- ORM configuration -->
<xsd:complexType name="mapping">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="dir" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="prefix" type="xsd:string" />
<xsd:attribute name="is-bundle" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="orm">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="entity-manager" type="entity_manager" />
<xsd:element name="resolve-target-entity" type="resolve_target_entity" minOccurs="0" maxOccurs="unbounded" />
<xsd:group ref="entity-manager-child-config" />
</xsd:choice>
<xsd:attribute name="default-entity-manager" type="xsd:string" />
<xsd:attribute name="proxy-dir" type="xsd:string" />
<xsd:attribute name="proxy-namespace" type="xsd:string" />
<xsd:attribute name="auto-generate-proxy-classes" type="xsd:string" default="false" />
<xsd:attributeGroup ref="entity-manager-config" />
</xsd:complexType>
<xsd:complexType name="resolve_target_entity">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="interface" type="xsd:string" use="required" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="cache_driver">
<!-- This is not consistent with the Symfony config. These string elements should be attributes.
TODO: change it for 2.0
-->
<xsd:all>
<xsd:element name="class" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="host" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="port" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="database" type="xsd:integer" minOccurs="0" maxOccurs="1" />
<xsd:element name="instance-class" type="xsd:string" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="namespace" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="entity_listeners">
<xsd:choice minOccurs="1">
<xsd:element name="entity" type="entity_listeners_entity" minOccurs="1" maxOccurs="unbounded" />
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="entity_listeners_entity">
<xsd:choice minOccurs="1">
<xsd:element name="listener" type="entity_listeners_listener" minOccurs="1" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="class" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="entity_listeners_listener">
<xsd:choice minOccurs="1">
<xsd:element name="event" type="entity_listeners_event" minOccurs="1" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="class" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="entity_listeners_event">
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="method" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="entity_manager">
<xsd:group ref="entity-manager-child-config" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attributeGroup ref="entity-manager-config" />
</xsd:complexType>
<xsd:group name="entity-manager-child-config">
<xsd:choice>
<xsd:element name="mapping" type="mapping" />
<xsd:element name="metadata-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
<xsd:element name="result-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
<xsd:element name="query-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
<xsd:element name="dql" type="dql" minOccurs="0" maxOccurs="1" />
<xsd:element name="hydrator" type="named_scalar" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="filter" type="filter" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="entity-listeners" type="entity_listeners" minOccurs="0" maxOccurs="1" />
<xsd:element name="second-level-cache" type="second-level-cache" minOccurs="0" maxOccurs="1" />
</xsd:choice>
</xsd:group>
<xsd:attributeGroup name="entity-manager-config">
<xsd:attribute name="auto-mapping" type="xsd:string" />
<xsd:attribute name="connection" type="xsd:string" />
<xsd:attribute name="default-repository-class" type="xsd:string" />
<xsd:attribute name="class-metadata-factory-name" type="xsd:string" />
<xsd:attribute name="naming-strategy" type="xsd:string" />
<xsd:attribute name="quote-strategy" type="xsd:string" />
<xsd:attribute name="entity-listener-resolver" type="xsd:string" />
<xsd:attribute name="repository-factory" type="xsd:string" />
<!-- deprecated attributes, use the child element instead -->
<xsd:attribute name="result-cache-driver" type="xsd:string" />
<xsd:attribute name="metadata-cache-driver" type="xsd:string" />
<xsd:attribute name="query-cache-driver" type="xsd:string" />
</xsd:attributeGroup>
<xsd:complexType name="filter" mixed="true">
<xsd:choice minOccurs="0">
<xsd:element name="parameter" type="named_scalar" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="class" type="xsd:string" />
<xsd:attribute name="enabled" type="xsd:boolean" />
</xsd:complexType>
<xsd:complexType name="second-level-cache-region" mixed="true">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="service" type="xsd:string" />
<xsd:attribute name="lifetime" type="xsd:integer" />
<xsd:attribute name="lock-lifetime" type="xsd:integer" />
<xsd:attribute name="cache-driver" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="second-level-cache-logger" mixed="true">
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="service" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="second-level-cache" mixed="true">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="logger" type="second-level-cache-logger" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="region" type="second-level-cache-region" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="region-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="enabled" type="xsd:boolean" default="true"/>
<xsd:attribute name="log-enabled" type="xsd:boolean" default="true"/>
<xsd:attribute name="factory" type="xsd:string" />
<xsd:attribute name="query-validator" type="xsd:string" />
<xsd:attribute name="region-lifetime" type="xsd:integer" />
<xsd:attribute name="region-lock-lifetime" type="xsd:integer" />
</xsd:complexType>
<xsd:complexType name="dql">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="string-function" type="named_scalar" />
<xsd:element name="numeric-function" type="named_scalar" />
<xsd:element name="datetime-function" type="named_scalar" />
</xsd:choice>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,486 @@
{% extends request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %}
{% import _self as helper %}
{% block toolbar %}
{% if collector.querycount > 0 or collector.invalidEntityCount > 0 %}
{% set profiler_markup_version = profiler_markup_version|default(1) %}
{% set icon %}
{% if profiler_markup_version == 1 %}
<img width="20" height="28" alt="Database" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//PwM1ARMDlcGogZQDlpMnT7pxc3NbA9nhQKxOpL5rQLwJiPeBsI6Ozl+YBOOOHTv+AOllQNwtLS39F2owKYZ/gRq8G4i3ggxEToggWzvc3d2Pk+1lNL4fFAs6ODi8JzdS7mMRVyDVoAMHDsANdAPiOCC+jCQvQKqBQB/BDbwBxK5AHA3E/kB8nKJkA8TMQBwLxaBIKQbi70AvTADSBiSadwFXpCikpKQU8PDwkGTaly9fHFigkaKIJid4584dkiMFFI6jkTJII0WVmpHCAixZQEXWYhDeuXMnyLsVlEQKI45qFBQZ8eRECi4DBaAlDqle/8A48ip6gAADANdQY88Uc0oGAAAAAElFTkSuQmCC" />
<span class="sf-toolbar-value sf-toolbar-status {% if collector.querycount > 50 %}sf-toolbar-status-yellow{% endif %}">{{ collector.querycount }}</span>
{% if collector.querycount > 0 %}
<span class="sf-toolbar-info-piece-additional-detail">in {{ '%0.2f'|format(collector.time * 1000) }} ms</span>
{% endif %}
{% if collector.invalidEntityCount > 0 %}
<span class="sf-toolbar-info-piece-additional sf-toolbar-status sf-toolbar-status-red">{{ collector.invalidEntityCount }}</span>
{% endif %}
{% else %}
{% set status = collector.invalidEntityCount > 0 ? 'red' : collector.querycount > 50 ? 'yellow' %}
{{ include('@Doctrine/Collector/icon.svg') }}
{% if collector.querycount == 0 and collector.invalidEntityCount > 0 %}
<span class="sf-toolbar-value">{{ collector.invalidEntityCount }}</span>
<span class="sf-toolbar-label">errors</span>
{% else %}
<span class="sf-toolbar-value">{{ collector.querycount }}</span>
<span class="sf-toolbar-info-piece-additional-detail">
<span class="sf-toolbar-label">in</span>
<span class="sf-toolbar-value">{{ '%0.2f'|format(collector.time * 1000) }}</span>
<span class="sf-toolbar-label">ms</span>
</span>
{% endif %}
{% endif %}
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Database Queries</b>
<span class="sf-toolbar-status {{ collector.querycount > 50 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.querycount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Query time</b>
<span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Invalid entities</b>
<span class="sf-toolbar-status {{ collector.invalidEntityCount > 0 ? 'sf-toolbar-status-red' : '' }}">{{ collector.invalidEntityCount }}</span>
</div>
{% if collector.cacheEnabled %}
<div class="sf-toolbar-info-piece">
<b>Cache hits</b>
<span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.cacheHitsCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache misses</b>
<span class="sf-toolbar-status {{ collector.cacheMissesCount > 0 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.cacheMissesCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache puts</b>
<span class="sf-toolbar-status {{ collector.cachePutsCount > 0 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.cachePutsCount }}</span>
</div>
{% else %}
<div class="sf-toolbar-info-piece">
<b>Second Level Cache</b>
<span class="sf-toolbar-status">disabled</span>
</div>
{% endif %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status|default('') }) }}
{% endif %}
{% endblock %}
{% block menu %}
{% set profiler_markup_version = profiler_markup_version|default(1) %}
{% if profiler_markup_version == 1 %}
<span class="label">
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAABLUlEQVR42u3TP0vDQBiA8UK/gDiLzi0IhU4OEunk5OQUAhGSOBUCzqWfIKSzX8DRySF0URCcMjWLIJjFD9Cpk/D6HITecEPUuzhIAz8CIdyTP/f2iqI4qaqqDx8l5Ic2uIeP/bquezCokOAFF+oCN3t4gPzSEjc4NEPaCldQbzjELTYW0RJzHDchwwem+ons6ZBpLSJ7nueJC22h0V+FzmwWV0ee59vQNV67CGVZJmEYbkNjfpY6X6I0Qo4/3RMmTdDDspuQVsJvgkP3IdMbIkIjLPBoadG2646iKJI0Ta2wxm6OdnP0/Tk6DYJgHcfxpw21RtscDTDDnaVZ26474GkkSRIrrPEv5sgMTfHe+cA2O6wPH6vOBpYQNALneHb96XTEDI6dzpEZ0VzO0Rf3pP5LMLI4tAAAAABJRU5ErkJggg==" alt="" /></span>
<strong>Doctrine</strong>
<span class="count">
<span>{{ collector.querycount }}</span>
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
</span>
</span>
{% else %}
<span class="label {{ collector.invalidEntityCount > 0 ? 'label-status-error' }} {{ collector.querycount == 0 ? 'disabled' }}">
<span class="icon">{{ include('@Doctrine/Collector/icon.svg') }}</span>
<strong>Doctrine</strong>
{% if collector.invalidEntityCount %}
<span class="count">
<span>{{ collector.invalidEntityCount }}</span>
</span>
{% endif %}
</span>
{% endif %}
{% endblock %}
{% block panel %}
{% set profiler_markup_version = profiler_markup_version|default(1) %}
{% if 'explain' == page %}
{{ render(controller('DoctrineBundle:Profiler:explain', {
token: token,
panel: 'db',
connectionName: request.query.get('connection'),
query: request.query.get('query')
})) }}
{% else %}
{{ block('queries') }}
{% endif %}
{% endblock %}
{% block queries %}
<style>
{% if profiler_markup_version == 1 %}
.hidden { display: none; }
.queries-table td, .queries-table th { vertical-align: top; }
.queries-table td > div { margin-bottom: 6px; }
.highlight pre { margin: 0; white-space: pre-wrap; }
.highlight .keyword { color: #8959A8; font-weight: bold; }
.highlight .word { color: #222222; }
.highlight .variable { color: #916319; }
.highlight .symbol { color: #222222; }
.highlight .comment { color: #999999; }
.highlight .backtick { color: #718C00; }
.highlight .string { color: #718C00; }
.highlight .number { color: #F5871F; font-weight: bold; }
.highlight .error { color: #C82829; }
{% endif %}
.time-container { position: relative; }
.time-container .nowrap { position: relative; z-index: 1; text-shadow: 0 0 2px #fff; }
.time-bar { display: block; position: absolute; top: 0; left: 0; bottom: 0; background: #e0e0e0; }
</style>
{% if profiler_markup_version > 1 %}
<h2>Query Metrics</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.querycount }}</span>
<span class="label">Database Queries</span>
</div>
<div class="metric">
<span class="value">{{ collector.groupedQueryCount }}</span>
<span class="label">Different statements</span>
</div>
<div class="metric">
<span class="value">{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
<span class="label">Query time</span>
</div>
<div class="metric">
<span class="value">{{ collector.invalidEntityCount }}</span>
<span class="label">Invalid entities</span>
</div>
{% if collector.cacheEnabled %}
<div class="metric">
<span class="value">{{ collector.cacheHitsCount }}</span>
<span class="label">Cache hits</span>
</div>
<div class="metric">
<span class="value">{{ collector.cacheMissesCount }}</span>
<span class="label">Cache misses</span>
</div>
<div class="metric">
<span class="value">{{ collector.cachePutsCount }}</span>
<span class="label">Cache puts</span>
</div>
{% endif %}
</div>
{% endif %}
{% set group_queries = request.query.getBoolean('group') %}
{% if group_queries %}
<h2>Grouped Statements</h2>
<p><a href="{{ path('_profiler', { panel: 'db', token: token }) }}">Show all queries</a></p>
{% else %}
<h2>Queries</h2>
<p><a href="{{ path('_profiler', { panel: 'db', token: token, group: true }) }}">Group similar statements</a></p>
{% endif %}
{% for connection, queries in collector.queries %}
{% if collector.connections|length > 1 %}
<h3>{{ connection }} <small>connection</small></h3>
{% endif %}
{% if queries is empty %}
<div class="empty">
<p>No database queries were performed.</p>
</div>
{% else %}
{% if group_queries %}
{% set queries = collector.groupedQueries[connection] %}
{% endif %}
<table class="alt queries-table">
<thead>
<tr>
{% if group_queries %}
<th class="nowrap" onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="1" style="cursor: pointer;">Time<span class="text-muted">&#9660;</span></th>
<th class="nowrap" onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Count<span></span></th>
{% else %}
<th class="nowrap" onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="-1" style="cursor: pointer;">#<span class="text-muted">&#9650;</span></th>
<th class="nowrap" onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Time<span></span></th>
{% endif %}
<th style="width: 100%;">Info</th>
</tr>
</thead>
<tbody id="queries-{{ loop.index }}">
{% for i, query in queries %}
{% set i = group_queries ? query.index : i %}
<tr id="queryNo-{{ i }}-{{ loop.parent.loop.index }}">
{% if group_queries %}
<td class="time-container">
<span class="time-bar" style="width:{{ '%0.2f'|format(query.executionPercent) }}%"></span>
<span class="nowrap">{{ '%0.2f'|format(query.executionMS * 1000) }}&nbsp;ms<br />({{ '%0.2f'|format(query.executionPercent) }}%)</span>
</td>
<td class="nowrap">{{ query.count }}</td>
{% else %}
<td class="nowrap">{{ loop.index }}</td>
<td class="nowrap">{{ '%0.2f'|format(query.executionMS * 1000) }}&nbsp;ms</td>
{% endif %}
<td>
{{ query.sql|doctrine_pretty_query(highlight_only = true) }}
<div>
<strong class="font-normal text-small">Parameters</strong>: {{ profiler_dump(query.params, 2) }}
</div>
<div class="text-small font-normal">
<a href="#" {{ profiler_markup_version == 1 ? 'onclick="return toggleRunnableQuery(this);"' }} class="sf-toggle link-inverse" data-toggle-selector="#formatted-query-{{ i }}-{{ loop.parent.loop.index }}" data-toggle-alt-content="Hide formatted query">View formatted query</a>
&nbsp;&nbsp;
<a href="#" {{ profiler_markup_version == 1 ? 'onclick="return toggleRunnableQuery(this);"' }} class="sf-toggle link-inverse" data-toggle-selector="#original-query-{{ i }}-{{ loop.parent.loop.index }}" data-toggle-alt-content="Hide runnable query">View runnable query</a>
{% if query.explainable %}
&nbsp;&nbsp;
<a class="link-inverse" href="{{ path('_profiler', { panel: 'db', token: token, page: 'explain', connection: connection, query: i }) }}" onclick="return explain(this);" data-target-id="explain-{{ i }}-{{ loop.parent.loop.index }}">Explain query</a>
{% endif %}
</div>
<div id="formatted-query-{{ i }}-{{ loop.parent.loop.index }}" class="sql-runnable hidden">
{{ query.sql|doctrine_pretty_query }}
</div>
<div id="original-query-{{ i }}-{{ loop.parent.loop.index }}" class="sql-runnable hidden">
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query(highlight_only = true) }}
</div>
{% if query.explainable %}
<div id="explain-{{ i }}-{{ loop.parent.loop.index }}" class="sql-explain"></div>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
<h2>Database Connections</h2>
{% if not collector.connections %}
<div class="empty">
<p>There are no configured database connections.</p>
</div>
{% else %}
{{ helper.render_simple_table('Name', 'Service', collector.connections) }}
{% endif %}
<h2>Entity Managers</h2>
{% if not collector.managers %}
<div class="empty">
<p>There are no configured entity managers.</p>
</div>
{% else %}
{{ helper.render_simple_table('Name', 'Service', collector.managers) }}
{% endif %}
<h2>Second Level Cache</h2>
{% if not collector.cacheEnabled %}
<div class="empty">
<p>Second Level Cache is not enabled.</p>
</div>
{% else %}
{% if not collector.cacheCounts %}
<div class="empty">
<p>Second level cache information is not available.</p>
</div>
{% else %}
{% if profiler_markup_version == 1 %}
{{ helper.render_simple_table('Key', 'Value', collector.cacheCounts) }}
{% else %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.cacheCounts.hits }}</span>
<span class="label">Hits</span>
</div>
<div class="metric">
<span class="value">{{ collector.cacheCounts.misses }}</span>
<span class="label">Misses</span>
</div>
<div class="metric">
<span class="value">{{ collector.cacheCounts.puts }}</span>
<span class="label">Puts</span>
</div>
</div>
{% endif %}
{% if collector.cacheRegions.hits %}
<h3>Number of cache hits</h3>
{{ helper.render_simple_table('Region', 'Hits', collector.cacheRegions.hits) }}
{% endif %}
{% if collector.cacheRegions.misses %}
<h3>Number of cache misses</h3>
{{ helper.render_simple_table('Region', 'Misses', collector.cacheRegions.misses) }}
{% endif %}
{% if collector.cacheRegions.puts %}
<h3>Number of cache puts</h3>
{{ helper.render_simple_table('Region', 'Puts', collector.cacheRegions.puts) }}
{% endif %}
{% endif %}
{% endif %}
<h2>Entities Mapping</h2>
{% for manager, classes in collector.entities %}
{% if collector.managers|length > 1 %}
<h3>{{ manager }} <small>entity manager</small></h3>
{% endif %}
{% if classes is empty %}
<div class="empty">
<p>No loaded entities.</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th scope="col">Class</th>
<th scope="col">Mapping errors</th>
</tr>
</thead>
<tbody>
{% for class in classes %}
{% set contains_errors = collector.mappingErrors[manager] is defined and collector.mappingErrors[manager][class] is defined %}
<tr class="{{ contains_errors ? 'status-error' }}">
<td>{{ class }}</td>
<td class="font-normal">
{% if contains_errors %}
<ul>
{% for error in collector.mappingErrors[manager][class] %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% else %}
No errors.
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
<script type="text/javascript">//<![CDATA[
function explain(link) {
"use strict";
var targetId = link.getAttribute('data-target-id');
var targetElement = document.getElementById(targetId);
if (targetElement.style.display != 'block') {
Sfjs.load(targetId, link.href, null, function(xhr, el) {
el.innerHTML = 'An error occurred while loading the query explanation.';
});
targetElement.style.display = 'block';
link.innerHTML = 'Hide query explanation';
} else {
targetElement.style.display = 'none';
link.innerHTML = 'Explain query';
}
return false;
}
function sortTable(header, column, targetId) {
"use strict";
var direction = parseInt(header.getAttribute('data-sort-direction')) || 1,
items = [],
target = document.getElementById(targetId),
rows = target.children,
headers = header.parentElement.children,
i;
for (i = 0; i < rows.length; ++i) {
items.push(rows[i]);
}
for (i = 0; i < headers.length; ++i) {
headers[i].removeAttribute('data-sort-direction');
if (headers[i].children.length > 0) {
headers[i].children[0].innerHTML = '';
}
}
header.setAttribute('data-sort-direction', (-1*direction).toString());
header.children[0].innerHTML = direction > 0 ? '<span class="text-muted">&#9650;</span>' : '<span class="text-muted">&#9660;</span>';
items.sort(function(a, b) {
return direction * (parseFloat(a.children[column].innerHTML) - parseFloat(b.children[column].innerHTML));
});
for (i = 0; i < items.length; ++i) {
Sfjs.removeClass(items[i], i % 2 ? 'even' : 'odd');
Sfjs.addClass(items[i], i % 2 ? 'odd' : 'even');
target.appendChild(items[i]);
}
}
{% if profiler_markup_version == 1 %}
function toggleRunnableQuery(target) {
var targetSelector = target.getAttribute('data-toggle-selector');
var targetDataAltContent = target.getAttribute('data-toggle-alt-content');
var targetElement = document.querySelector(targetSelector);
target.setAttribute('data-toggle-alt-content', target.innerHTML);
if (targetElement.style.display != 'block') {
targetElement.style.display = 'block';
target.innerHTML = targetDataAltContent;
} else {
targetElement.style.display = 'none';
target.innerHTML = targetDataAltContent;
}
return false;
}
{% endif %}
//]]></script>
{% endblock %}
{% macro render_simple_table(label1, label2, data) %}
<table>
<thead>
<tr>
<th scope="col" class="key">{{ label1 }}</th>
<th scope="col">{{ label2 }}</th>
</tr>
</thead>
<tbody>
{% for key, value in data %}
<tr>
<th scope="row">{{ key }}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}

View File

@@ -0,0 +1,28 @@
{% if data[0]|length > 1 %}
{# The platform returns a table for the explanation (e.g. MySQL), display all columns #}
<table style="margin: 5px 0;">
<thead>
<tr>
{% for label in data[0]|keys %}
<th>{{ label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
{% for key, item in row %}
<td>{{ item|replace({',': ', '}) }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
{# The Platform returns a single column for a textual explanation (e.g. PostgreSQL), display all lines #}
<pre style="margin: 5px 0;">
{%- for row in data -%}
{{ row|first }}{{ "\n" }}
{%- endfor -%}
</pre>
{% endif %}

View File

@@ -0,0 +1,4 @@
<svg version="1.1"xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#AAAAAA" d="M5,8h14c1.7,0,3-1.3,3-3s-1.3-3-3-3H5C3.3,2,2,3.3,2,5S3.3,8,5,8z M18,3.6c0.8,0,1.5,0.7,1.5,1.5S18.8,6.6,18,6.6s-1.5-0.7-1.5-1.5S17.2,3.6,18,3.6z M19,9H5c-1.7,0-3,1.3-3,3s1.3,3,3,3h14c1.7,0,3-1.3,3-3S20.7,9,19,9z M18,13.6
c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S18.8,13.6,18,13.6z M19,16H5c-1.7,0-3,1.3-3,3s1.3,3,3,3h14c1.7,0,3-1.3,3-3S20.7,16,19,16z M18,20.6c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S18.8,20.6,18,20.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View File

@@ -0,0 +1,349 @@
<?php
namespace Doctrine\Bundle\DoctrineBundle\Twig;
use SqlFormatter;
use Symfony\Component\VarDumper\Cloner\Data;
use Twig_Extension;
use Twig_SimpleFilter;
/**
* This class contains the needed functions in order to do the query highlighting
*/
class DoctrineExtension extends Twig_Extension
{
/**
* Number of maximum characters that one single line can hold in the interface
*
* @var int
*/
private $maxCharWidth = 100;
/**
* Define our functions
*
* @return Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new Twig_SimpleFilter('doctrine_minify_query', [$this, 'minifyQuery'], ['deprecated' => true]),
new Twig_SimpleFilter('doctrine_pretty_query', [$this, 'formatQuery'], ['is_safe' => ['html']]),
new Twig_SimpleFilter('doctrine_replace_query_parameters', [$this, 'replaceQueryParameters']),
];
}
/**
* Get the possible combinations of elements from the given array
*
* @param array $elements
* @param int $combinationsLevel
*
* @return array
*/
private function getPossibleCombinations(array $elements, $combinationsLevel)
{
$baseCount = count($elements);
$result = [];
if ($combinationsLevel === 1) {
foreach ($elements as $element) {
$result[] = [$element];
}
return $result;
}
$nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1);
foreach ($nextLevelElements as $nextLevelElement) {
$lastElement = $nextLevelElement[$combinationsLevel - 2];
$found = false;
foreach ($elements as $key => $element) {
if ($element === $lastElement) {
$found = true;
continue;
}
if ($found !== true || $key >= $baseCount) {
continue;
}
$tmp = $nextLevelElement;
$newCombination = array_slice($tmp, 0);
$newCombination[] = $element;
$result[] = array_slice($newCombination, 0);
}
}
return $result;
}
/**
* Shrink the values of parameters from a combination
*
* @param array $parameters
* @param array $combination
*
* @return string
*/
private function shrinkParameters(array $parameters, array $combination)
{
array_shift($parameters);
$result = '';
$maxLength = $this->maxCharWidth;
$maxLength -= count($parameters) * 5;
$maxLength /= count($parameters);
foreach ($parameters as $key => $value) {
$isLarger = false;
if (strlen($value) > $maxLength) {
$value = wordwrap($value, $maxLength, "\n", true);
$value = explode("\n", $value);
$value = $value[0];
$isLarger = true;
}
$value = self::escapeFunction($value);
if (! is_numeric($value)) {
$value = substr($value, 1, -1);
}
if ($isLarger) {
$value .= ' [...]';
}
$result .= ' ' . $combination[$key] . ' ' . $value;
}
return trim($result);
}
/**
* Attempt to compose the best scenario minified query so that a user could find it without expanding it
*
* @param string $query
* @param array $keywords
* @param int $required
*
* @return string
*/
private function composeMiniQuery($query, array $keywords, $required)
{
// Extract the mandatory keywords and consider the rest as optional keywords
$mandatoryKeywords = array_splice($keywords, 0, $required);
$combinations = [];
$combinationsCount = count($keywords);
// Compute all the possible combinations of keywords to match the query for
while ($combinationsCount > 0) {
$combinations = array_merge($combinations, $this->getPossibleCombinations($keywords, $combinationsCount));
$combinationsCount--;
}
// Try and match the best case query pattern
foreach ($combinations as $combination) {
$combination = array_merge($mandatoryKeywords, $combination);
$regexp = implode('(.*) ', $combination) . ' (.*)';
$regexp = '/^' . $regexp . '/is';
if (preg_match($regexp, $query, $matches)) {
return $this->shrinkParameters($matches, $combination);
}
}
// Try and match the simplest query form that contains only the mandatory keywords
$regexp = implode(' (.*)', $mandatoryKeywords) . ' (.*)';
$regexp = '/^' . $regexp . '/is';
if (preg_match($regexp, $query, $matches)) {
return $this->shrinkParameters($matches, $mandatoryKeywords);
}
// Fallback in case we didn't managed to find any good match (can we actually have that happen?!)
return substr($query, 0, $this->maxCharWidth);
}
/**
* Minify the query
*
* @param string $query
*
* @return string
*/
public function minifyQuery($query)
{
$result = '';
$keywords = [];
$required = 1;
// Check if we can match the query against any of the major types
switch (true) {
case stripos($query, 'SELECT') !== false:
$keywords = ['SELECT', 'FROM', 'WHERE', 'HAVING', 'ORDER BY', 'LIMIT'];
$required = 2;
break;
case stripos($query, 'DELETE') !== false:
$keywords = ['DELETE', 'FROM', 'WHERE', 'ORDER BY', 'LIMIT'];
$required = 2;
break;
case stripos($query, 'UPDATE') !== false:
$keywords = ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'];
$required = 2;
break;
case stripos($query, 'INSERT') !== false:
$keywords = ['INSERT', 'INTO', 'VALUE', 'VALUES'];
$required = 2;
break;
// If there's no match so far just truncate it to the maximum allowed by the interface
default:
$result = substr($query, 0, $this->maxCharWidth);
}
// If we had a match then we should minify it
if ($result === '') {
$result = $this->composeMiniQuery($query, $keywords, $required);
}
return $result;
}
/**
* Escape parameters of a SQL query
* DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE
*
* @internal
*
* @param mixed $parameter
*
* @return string
*/
public static function escapeFunction($parameter)
{
$result = $parameter;
switch (true) {
// Check if result is non-unicode string using PCRE_UTF8 modifier
case is_string($result) && ! preg_match('//u', $result):
$result = '0x' . strtoupper(bin2hex($result));
break;
case is_string($result):
$result = "'" . addslashes($result) . "'";
break;
case is_array($result):
foreach ($result as &$value) {
$value = static::escapeFunction($value);
}
$result = implode(', ', $result);
break;
case is_object($result):
$result = addslashes((string) $result);
break;
case $result === null:
$result = 'NULL';
break;
case is_bool($result):
$result = $result ? '1' : '0';
break;
}
return $result;
}
/**
* Return a query with the parameters replaced
*
* @param string $query
* @param array|Data $parameters
*
* @return string
*/
public function replaceQueryParameters($query, $parameters)
{
if ($parameters instanceof Data) {
// VarDumper < 3.3 compatibility layer
$parameters = method_exists($parameters, 'getValue') ? $parameters->getValue(true) : $parameters->getRawData();
}
$i = 0;
if (! array_key_exists(0, $parameters) && array_key_exists(1, $parameters)) {
$i = 1;
}
return preg_replace_callback(
'/\?|((?<!:):[a-z0-9_]+)/i',
static function ($matches) use ($parameters, &$i) {
$key = substr($matches[0], 1);
if (! array_key_exists($i, $parameters) && ($key === false || ! array_key_exists($key, $parameters))) {
return $matches[0];
}
$value = array_key_exists($i, $parameters) ? $parameters[$i] : $parameters[$key];
$result = DoctrineExtension::escapeFunction($value);
$i++;
return $result;
},
$query
);
}
/**
* Formats and/or highlights the given SQL statement.
*
* @param string $sql
* @param bool $highlightOnly If true the query is not formatted, just highlighted
*
* @return string
*/
public function formatQuery($sql, $highlightOnly = false)
{
SqlFormatter::$pre_attributes = 'class="highlight highlight-sql"';
SqlFormatter::$quote_attributes = 'class="string"';
SqlFormatter::$backtick_quote_attributes = 'class="string"';
SqlFormatter::$reserved_attributes = 'class="keyword"';
SqlFormatter::$boundary_attributes = 'class="symbol"';
SqlFormatter::$number_attributes = 'class="number"';
SqlFormatter::$word_attributes = 'class="word"';
SqlFormatter::$error_attributes = 'class="error"';
SqlFormatter::$comment_attributes = 'class="comment"';
SqlFormatter::$variable_attributes = 'class="variable"';
if ($highlightOnly) {
$html = SqlFormatter::highlight($sql);
$html = preg_replace('/<pre class=".*">([^"]*+)<\/pre>/Us', '\1', $html);
} else {
$html = SqlFormatter::format($sql);
$html = preg_replace('/<pre class="(.*)">([^"]*+)<\/pre>/Us', '<div class="\1"><pre>\2</pre></div>', $html);
}
return $html;
}
/**
* Get the name of the extension
*
* @return string
*/
public function getName()
{
return 'doctrine_extension';
}
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors"/>
<!-- Ignore warnings, show progress of the run and show sniff names -->
<arg value="nps"/>
<file>.</file>
<exclude-pattern>vendor/*</exclude-pattern>
<rule ref="Doctrine">
<exclude name="SlevomatCodingStandard.Classes.ClassConstantVisibility"/>
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly"/>
<exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFallbackGlobalName"/>
<exclude name="SlevomatCodingStandard.TypeHints.DeclareStrictTypes"/>
<exclude name="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/>
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint"/>
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint"/>
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint"/>
<exclude name="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator.NullCoalesceOperatorNotUsed"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix"/>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>Tests/*</exclude-pattern>
</rule>
<rule ref="Squiz.Classes.ClassFileName.NoMatch">
<exclude-pattern>Tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedProperty">
<exclude-pattern>Tests/DependencyInjection/Fixtures/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableParameterTypeHintSpecification">
<exclude-pattern>DependencyInjection/*</exclude-pattern>
<exclude-pattern>Twig/DoctrineExtension.php</exclude-pattern>
<exclude-pattern>Tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversablePropertyTypeHintSpecification">
<exclude-pattern>DependencyInjection/*</exclude-pattern>
<exclude-pattern>Twig/DoctrineExtension.php</exclude-pattern>
<exclude-pattern>Tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification">
<exclude-pattern>DependencyInjection/*</exclude-pattern>
<exclude-pattern>Twig/DoctrineExtension.php</exclude-pattern>
<exclude-pattern>Tests/*</exclude-pattern>
</rule>
</ruleset>