Initiale Version

This commit is contained in:
Jan Unger
2016-08-16 21:20:53 +02:00
commit 88cf71d772
10930 changed files with 1708903 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: deps=low SYMFONY_DEPRECATIONS_HELPER=weak
- php: 5.6
env: SYMFONY_VERSION="2.3.*"
- php: 5.6
env: SYMFONY_VERSION="2.8.*@dev"
- php: 5.6
env: SYMFONY_VERSION="3.0.*@dev"
env:
global:
- deps=no
before_install:
- composer self-update
- if [ "$SYMFONY_VERSION" != "" ]; then composer require --no-update symfony/symfony:${SYMFONY_VERSION}; fi;
install:
- if [ "$deps" = "no" ]; then composer update; fi;
- if [ "$deps" = "low" ]; then composer --prefer-lowest --prefer-stable update; fi;
script:
- ./vendor/bin/phpunit -v --coverage-clover ./build/logs/clover.xml
after_script:
- php ./vendor/bin/coveralls -v

View File

@@ -0,0 +1,141 @@
## 1.5.2 (2015-08-31)
Security:
* Fix Security Misconfiguration Vulnerability, allowing potential local arbitrary code execution.
## 1.5.1 (2015-08-12)
Bugfix:
* Fixed the JS expanding all queries in the profiler in case of multiple connections
* Fixed the retrieval of the namespace in DisconnectedMetadataFactory
* Changed the composer constraint to allow Symfony 3.0 for people wanting to do early testing
## 1.5.0 (2015-05-28)
Features:
* Added the possibility to configure the quote strategy in the configuration
* Improved the rendering of the query explanation for Postgres
* Added support for tagging entity listeners, without the need to map the listener
Bugfix:
* Fixed the serverVersion configuration for master slave connections
* Fixed the enabling of the profiler to avoid automatically enabling the logger
* Fixed the detection of existing databases when quoted names are used
* Fixed the profiler template when having a manager without any loaded metadata
## 1.4.0 (2015-02-28)
Features:
* Added the ``--if-not-exists`` option in the ``doctrine:database:create`` command
* Added the ``--if-exists`` option in the ``doctrine:database:drop`` command
* Added the support for the ``url`` DBAL setting
* Added profiling ability for SQL Server
Bugfix:
* Fixed the cache configuration when using ``cache_provider``
* Removed usage of deprecated DI features when using Symfony 2.6+
* Close connections and clear managers on shutdown. This avoids leaking connections in tests.
* Added an exception when the ORM layer is configured but not DBAL
## 1.3.0 (2014-11-28)
Features:
* add support for bundle namespace alias in the mapping compiler pass
* Added support for server_version connection parameter
* Added a way to enable auto_mapping option using multiple entity managers
Bugfix:
* Inlined the profiler picto images instead of getting from FrameworkBundle (where they are gone)
* Remove duplicates in the list of mapped entities in the profile
* Fixed the compatibility with PHP 5.3 (broken in 1.3.0-beta1)
## 1.3.0-beta2 (2014-07-09)
Feature:
* add auto-commit DBAL configuration option
* Use DoctrineCacheBundle to create cache drivers, supporting more configuration
* Added sorting by time in DB panel
Bugfix:
* Fixed the compatibility of the DataCollector with Doctrine 2.4 (bug introduced in 1.3.0-beta1)
* Fixed the exit code of commands on failure
* Fixed the replacement of query parameters in the profiler
## 1.3.0-beta1 (2014-01-26)
Features:
* Added option to configure entity listener resolver service
* add compiler pass for bundles to register mappings
* Added a button to expand/collapse all queries in the profiler
* Added configuration for sharding
* Added support for the new ways to generate proxies in Doctrine Common
* Added configuration for the second-level cache
Bugfix:
* Removed deprecated call
* fix drop and create command for connections with master slave configuration
* Remove usage of deprecated Twig features
## 1.2.0 (2013-03-25)
* Bumped the requirement to Symfony 2.2
* Updated the profiler templates for Symfony 2.2
## 1.1.0 (2013-01-12)
* Added syntax highlighting for queries in teh profiler
* Added the validation of the mapping in the profiler panel
* Added return codes for doctrine:database:[create|drop] commands
## 1.0.0 (2012-09-07)
* Removed the mysql charset hack for 5.3.6+ as PDO has been fixed
* Implement "keep_slave"/"keepSlave" configuration
* Added missing Redis cache class mapping.
* Fixed the XSD schema for the configuration.
* Added support for schema assets filter configuration
* integrate naming_strategy into config
## 1.0.0-RC1 (2012-07-04)
* Add support for targetEntity resolving through the ORM 2.2 listener.
* Fixed quote database name in doctrine:database:create and doctrine:database:drop commands
* added a way to use cache services
* Added a way to configure the default entity repository class
* Added the support for SQL filters
* Removed the InfoCommand and proxy the ORM command instead
* Made the ORM fully optional by avoiding breaking the console
* Added support for master_slave connections
* Fixed xml config for proxy parameters
* Fixes doctrine:generate:entities when called with the --path argument
* Added missing Memcached cache driver
* Fix memory leak in Doctrine Autoload Proxy Magic
* adds lazy-loading event manager, improved listener registration
* Added a configuration setting for commented types
* Fixed bug with MetadataFactory having problem when the StaticReflection is used.
* Added the possibility to explain queries in the profiler
* Splitted the configuration for the logging and the profiling of the connection
## 1.0.0-beta1 (2011-12-15)
* [BC break] Changed the namespace from Symfony\Bundle to Doctrine\Bundle
* Enhance error reporting during mapping validation when nested exceptions occur.
* Add DoctrineValidationPass to load validation files conditionally
* Moved the entity provider service to DoctrineBundle
* Added Stopwatch support in debug mode to integrate in the profiler timeline
* Removed the IndexedReader
* Added the implementation of the ManagerRegistry to replace the symfony 2.0 registry
* Added access to Doctrine's ValidateSchema command from the console. See symfony/symfony#2200.
* Extracted the bundle from Symfony 2.0

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\DriverManager;
/**
* Database tool allows you to easily drop and create your configured databases.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class CreateDatabaseDoctrineCommand extends DoctrineCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:database:create')
->setDescription('Creates the configured database')
->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) === true) {
$connectionName = $this->getContainer()->get('doctrine')->getDefaultConnectionName();
}
$connection = $this->getDoctrineConnection($connectionName);
$ifNotExists = $input->getOption('if-not-exists');
$params = $connection->getParams();
if (isset($params['master'])) {
$params = $params['master'];
}
$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 dropped.");
}
// 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);
$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,68 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Doctrine\ORM\Tools\EntityGenerator;
/**
* Base class for Doctrine console commands to extend from.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class DoctrineCommand extends ContainerAwareCommand
{
/**
* 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
*
* @return \Doctrine\ORM\EntityManager
*/
protected function getEntityManager($name)
{
return $this->getContainer()->get('doctrine')->getManager($name);
}
/**
* Get a doctrine dbal connection by symfony name.
*
* @param string $name
*
* @return \Doctrine\DBAL\Connection
*/
protected function getDoctrineConnection($name)
{
return $this->getContainer()->get('doctrine')->getConnection($name);
}
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Doctrine\DBAL\DriverManager;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Database tool allows you to easily drop and create your configured databases.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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('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)
{
$connection = $this->getDoctrineConnection($input->getOption('connection'));
$ifExists = $input->getOption('if-exists');
$params = $connection->getParams();
if (isset($params['master'])) {
$params = $params['master'];
}
$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']);
if ($input->getOption('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 for connection named <comment>%s</comment></info>', $name));
} else {
$output->writeln(sprintf('<info>Database for connection named <comment>%s</comment> doesn\'t exist. Skipped.</info>', $name));
}
} catch (\Exception $e) {
$output->writeln(sprintf('<error>Could not drop database for connection named <comment>%s</comment></error>', $name));
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
return self::RETURN_CODE_NOT_DROP;
}
} else {
$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 named <comment>%s</comment>.</info>', $name));
$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;
}
}
}

View File

@@ -0,0 +1,140 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory;
/**
* Generate entity classes from mapping information
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class GenerateEntitiesDoctrineCommand extends DoctrineCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:generate:entities')
->setAliases(array('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)
{
$manager = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine'));
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'), '/', '\\');
if (false !== $pos = strpos($name, ':')) {
$name = $this->getContainer()->get('doctrine')->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(array($m), $entityMetadata->getPath());
if ($m->customRepositoryClassName && false !== strpos($m->customRepositoryClassName, $metadata->getNamespace())) {
$repoGenerator->writeEntityRepositoryClass($m->customRepositoryClassName, $metadata->getPath());
}
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Mapping\Driver\DatabaseDriver;
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
use Doctrine\ORM\Tools\Console\MetadataFilter;
/**
* Import Doctrine ORM metadata mapping information from an existing database.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class ImportMappingDoctrineCommand extends DoctrineCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('doctrine:mapping:import')
->addArgument('bundle', InputArgument::REQUIRED, 'The bundle 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('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.')
->setDescription('Imports mapping information from an existing database')
->setHelp(<<<EOT
The <info>%command.name%</info> command imports mapping information
from an existing database:
<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)
{
$bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('bundle'));
$destPath = $bundle->getPath();
$type = $input->getArgument('mapping-type') ? $input->getArgument('mapping-type') : 'xml';
if ('annotation' === $type) {
$destPath .= '/Entity';
} else {
$destPath .= '/Resources/config/doctrine';
}
if ('yaml' === $type) {
$type = 'yml';
}
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter($type);
$exporter->setOverwriteExistingFiles($input->getOption('force'));
if ('annotation' === $type) {
$entityGenerator = $this->getEntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
}
$em = $this->getEntityManager($input->getOption('em'));
$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 = $bundle->getNamespace().'\\Entity\\'.$className;
if ('annotation' === $type) {
$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);
if (!is_dir($dir = dirname($path))) {
mkdir($dir, 0775, true);
}
file_put_contents($path, $code);
chmod($path, 0664);
}
return 0;
} else {
$output->writeln('Database does not have any mapping information.', 'ERROR');
$output->writeln('', 'ERROR');
return 1;
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
/**
* Command to clear the metadata cache of the various cache drivers.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
/**
* Command to clear the query cache of the various cache drivers.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
/**
* Command to clear the result cache of the various cache drivers.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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,51 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand;
/**
* Command to clear a collection cache region.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
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,72 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
use Doctrine\ORM\Tools\Export\Driver\XmlExporter;
use Doctrine\ORM\Tools\Export\Driver\YamlExporter;
/**
* Convert Doctrine ORM metadata mapping information between the various supported
* formats.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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 \Doctrine\ORM\Tools\Export\Driver\AbstractExporter
*/
protected function getExporter($toType, $destPath)
{
/** @var $exporter \Doctrine\ORM\Tools\Export\Driver\AbstractExporter */
$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,53 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
/**
* Command to execute the SQL needed to generate the database schema for
* a given entity manager.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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'));
parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command Delegate.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
abstract class DelegateCommand extends Command
{
/**
* @var \Symfony\Component\Console\Command\Command
*/
protected $command;
/**
* @return \Symfony\Component\Console\Command\Command
*/
abstract protected function createCommand();
/**
* @return string
*/
protected function getMinimalVersion()
{
return '2.3.0-DEV';
}
/**
* @return boolean
*/
private function isVersionCompatible()
{
return (version_compare(\Doctrine\ORM\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,55 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
/**
* Provides some helper and convenience methods to configure doctrine commands in the context of bundles
* and multiple connections/entity managers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class DoctrineCommandHelper
{
/**
* Convenience method to push the helper sets of a given entity manager into the application.
*
* @param Application $application
* @param string $emName
*/
public static function setApplicationEntityManager(Application $application, $emName)
{
/** @var $em \Doctrine\ORM\EntityManager */
$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 Application $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,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand;
/**
* Command to drop the database schema for a set of classes based on their mappings.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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'));
parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand;
/**
* Ensure the Doctrine ORM is configured properly for a production environment.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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'));
parent::execute($input, $output);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand;
/**
* Command to clear a entity cache region.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
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,48 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\InfoCommand;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Show information about mapped entities
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
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,51 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand;
/**
* Command to clear a query cache region.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
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,68 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\RunDqlCommand;
/**
* Execute a Doctrine DQL query and output the results.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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,58 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand;
/**
* Execute a SQL query and output the results.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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 user"</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,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
/**
* Command to generate the SQL needed to update the database schema to match
* the current mapping information.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
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,52 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand as DoctrineValidateSchemaCommand;
/**
* Command to run Doctrine ValidateSchema() on the current mappings.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Neil Katin <symfony@askneil.com>
*/
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,89 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Types\Type;
/**
* Connection
*/
class ConnectionFactory
{
private $typesConfig = array();
private $commentedTypes = array();
private $initialized = false;
/**
* Construct.
*
* @param array $typesConfig
*/
public function __construct(array $typesConfig)
{
$this->typesConfig = $typesConfig;
}
/**
* Create a connection by name.
*
* @param array $params
* @param Configuration $config
* @param EventManager $eventManager
* @param array $mappingTypes
*
* @return \Doctrine\DBAL\Connection
*/
public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array())
{
if (!$this->initialized) {
$this->initializeTypes();
$this->initialized = true;
}
$connection = DriverManager::getConnection($params, $config, $eventManager);
if (!empty($mappingTypes)) {
$platform = $connection->getDatabasePlatform();
foreach ($mappingTypes as $dbType => $doctrineType) {
$platform->registerDoctrineTypeMapping($dbType, $doctrineType);
}
foreach ($this->commentedTypes as $type) {
$platform->markDoctrineTypeCommented(Type::getType($type));
}
}
return $connection;
}
/**
* 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']) {
$this->commentedTypes[] = $type;
}
}
}
}

View File

@@ -0,0 +1,105 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Controller;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
/**
* ProfilerController.
*
* @author Christophe Coevoet <stof@notk.org>
*/
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 integer $query
*
* @return Response A Response instance
*/
public function explainAction($token, $connectionName, $query)
{
/** @var $profiler \Symfony\Component\HttpKernel\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 \Doctrine\DBAL\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 $this->container->get('templating')->renderResponse('@Doctrine/Collector/explain.html.twig', array(
'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;';
}
$stmt = $connection->executeQuery($sql, $query['params'], $query['types']);
$stmt->nextRowset();
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
private function explainOtherPlatform(Connection $connection, $query)
{
return $connection->executeQuery('EXPLAIN '.$query['sql'], $query['params'], $query['types'])
->fetchAll(\PDO::FETCH_ASSOC);
}
}

View File

@@ -0,0 +1,192 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\DataCollector;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Tools\SchemaValidator;
use Doctrine\ORM\Version;
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* DoctrineDataCollector.
*
* @author Christophe Coevoet <stof@notk.org>
*/
class DoctrineDataCollector extends BaseCollector
{
private $registry;
private $invalidEntityCount;
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 = array();
$entities = array();
$caches = array(
'enabled' => false,
'log_enabled' => false,
'counts' => array(
'puts' => 0,
'hits' => 0,
'misses' => 0,
),
'regions' => array(
'puts' => array(),
'hits' => array(),
'misses' => array(),
),
);
foreach ($this->registry->getManagers() as $name => $em) {
$entities[$name] = array();
/** @var $factory \Doctrine\ORM\Mapping\ClassMetadataFactory */
$factory = $em->getMetadataFactory();
$validator = new SchemaValidator($em);
/** @var $class \Doctrine\ORM\Mapping\ClassMetadataInfo */
foreach ($factory->getLoadedMetadata() as $class) {
if (!isset($entities[$name][$class->getName()])) {
$classErrors = $validator->validateClass($class);
$entities[$name][$class->getName()] = $class->getName();
if (!empty($classErrors)) {
$errors[$name][$class->getName()] = $classErrors;
}
}
}
if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
continue;
}
/** @var $emConfig \Doctrine\ORM\Configuration */
$emConfig = $em->getConfiguration();
$slcEnabled = $emConfig->isSecondLevelCacheEnabled();
if (!$slcEnabled) {
continue;
}
$caches['enabled'] = true;
/** @var $cacheConfiguration \Doctrine\ORM\Cache\CacheConfiguration */
/** @var $cacheLoggerChain \Doctrine\ORM\Cache\Logging\CacheLoggerChain */
$cacheConfiguration = $emConfig->getSecondLevelCacheConfiguration();
$cacheLoggerChain = $cacheConfiguration->getCacheLogger();
if (!$cacheLoggerChain || !$cacheLoggerChain->getLogger('statistics')) {
continue;
}
/** @var $cacheLoggerStats \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger */
$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;
}
}
$this->data['entities'] = $entities;
$this->data['errors'] = $errors;
$this->data['caches'] = $caches;
}
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 (null === $this->invalidEntityCount) {
$this->invalidEntityCount = array_sum(array_map('count', $this->data['errors']));
}
return $this->invalidEntityCount;
}
}

View File

@@ -0,0 +1,167 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
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.
*
* @author David Buchmann <david@liip.ch>
*/
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 = array())
{
$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 = array(), $enabledParameter = false, array $aliasMap = array())
{
$arguments = array($namespaces, '.orm.xml');
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', array($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 = array(), $enabledParameter = false, array $aliasMap = array())
{
$arguments = array($namespaces, '.orm.yml');
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\ORM\Mapping\Driver\YamlDriver', array($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 = array(), $enabledParameter = false, array $aliasMap = array())
{
$arguments = array($namespaces, '.php');
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments);
$driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\PHPDriver', array($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 = array(), $enabledParameter = false, array $aliasMap = array())
{
$reader = new Reference('annotation_reader');
$driver = new Definition('Doctrine\ORM\Mapping\Driver\AnnotationDriver', array($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 = array(), $enabledParameter = false, array $aliasMap = array())
{
$driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver', array($directories));
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class for Symfony bundles to register entity listeners
*
* @author Sander Marechal <s.marechal@jejik.com>
*/
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;
}
$resolver = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
if ($container->hasAlias($resolver)) {
$resolver = (string) $container->getAlias($resolver);
}
if (!$container->hasDefinition($resolver)) {
continue;
}
if (isset($attributes['entity']) && isset($attributes['event'])) {
$this->attachToListener($container, $name, $id, $attributes);
}
$container->getDefinition($resolver)->addMethodCall('register', array(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 = array(
$attributes['entity'],
$serviceDef->getClass(),
$attributes['event'],
);
if (isset($attributes['method'])) {
$args[] = $attributes['method'];
}
$container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
}
}

View File

@@ -0,0 +1,679 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This class contains the configuration information for the bundle
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*
* @author Christophe Coevoet <stof@notk.org>
*/
class Configuration implements ConfigurationInterface
{
private $debug;
/**
* Constructor
*
* @param Boolean $debug Whether to use the debug mode
*/
public function __construct($debug)
{
$this->debug = (Boolean) $debug;
}
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('doctrine');
$this->addDbalSection($rootNode);
$this->addOrmSection($rootNode);
return $treeBuilder;
}
/**
* Add DBAL section to configuration tree
*
* @param ArrayNodeDefinition $node
*/
private function addDbalSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('dbal')
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v) && !array_key_exists('connection', $v); })
->then(function ($v) {
// Key that should not be rewritten to the connection config
$excludedKeys = array('default_connection' => true, 'types' => true, 'type' => true);
$connection = array();
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'] = array($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(function ($v) { return array('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();
$node = $treeBuilder->root('connections');
/** @var $connectionNode ArrayNodeDefinition */
$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_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.
*
* @param ArrayNodeDefinition $node
*/
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('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()
->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(function ($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);})
->then(function ($v) {
$v['sessionMode'] = $v['session_mode'];
unset($v['session_mode']);
return $v;
})
->end()
->beforeNormalization()
->ifTrue(function ($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);})
->then(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
*
* @param ArrayNodeDefinition $node
*/
private function addOrmSection(ArrayNodeDefinition $node)
{
$generationModes = $this->getAutoGenerateModes();
$node
->children()
->arrayNode('orm')
->beforeNormalization()
->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); })
->then(function ($v) {
$v = (array) $v;
// Key that should not be rewritten to the connection config
$excludedKeys = array(
'default_entity_manager' => true, 'auto_generate_proxy_classes' => true,
'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true,
'resolve_target_entity' => true,
);
$entityManager = array();
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'] = array($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) use ($generationModes) {
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(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 \Symfony\Component\Config\Definition\Builder\NodeDefinition
*/
private function getOrmTargetEntityResolverNode()
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root('resolve_target_entities');
$node
->useAttributeAsKey('interface')
->prototype('scalar')
->cannotBeEmpty()
->end()
;
return $node;
}
/**
* Return ORM entity listener node
*
* @return \Symfony\Component\Config\Definition\Builder\NodeDefinition
*/
private function getOrmEntityListenersNode()
{
$builder = new TreeBuilder();
$node = $builder->root('entity_listeners');
$normalizer = function ($mappings) {
$entities = array();
foreach ($mappings as $entityClass => $mapping) {
$listeners = array();
foreach ($mapping as $listenerClass => $listenerEvent) {
$events = array();
foreach ($listenerEvent as $eventType => $eventMapping) {
if ($eventMapping === null) {
$eventMapping = array(null);
}
foreach ($eventMapping as $method) {
$events[] = array(
'type' => $eventType,
'method' => $method,
);
}
}
$listeners[] = array(
'class' => $listenerClass,
'event' => $events,
);
}
$entities[] = array(
'class' => $entityClass,
'listener' => $listeners,
);
}
return array('entities' => $entities);
};
$node
->beforeNormalization()
// Yaml normalization
->ifTrue(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();
$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')->defaultNull()->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(function ($v) { return array('type' => $v); })
->end()
->treatNullLike(array())
->treatFalseLike(array('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(function ($v) { return array('class' => $v); })
->end()
->beforeNormalization()
// The content of the XML node is returned as the "value" key so we need to rename it
->ifTrue(function ($v) {
return is_array($v) && isset($v['value']);
})
->then(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();
$node = $treeBuilder->root($name);
$node
->addDefaultsIfNotSet()
->beforeNormalization()
->ifString()
->then(function ($v) { return array('type' => $v); })
->end()
->children()
->scalarNode('type')->defaultValue('array')->end()
->scalarNode('host')->end()
->scalarNode('port')->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 = array();
$valuesArray = array();
foreach ($constsArray as $key => $value) {
if (strpos($key, $constPrefix) === 0) {
$namesArray[] = substr($key, $prefixLen);
$valuesArray[] = (int) $value;
}
}
return array(
'names' => $namesArray,
'values' => $valuesArray,
);
}
}

View File

@@ -0,0 +1,824 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
use Doctrine\ORM\Version;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
use Symfony\Component\Config\FileLocator;
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
/**
* DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Kinn Coelho Julião <kinncj@php.net>
*/
class DoctrineExtension extends AbstractDoctrineExtension
{
/**
* @var string
*/
private $defaultConnection;
/**
* @var array
*/
private $entityManagers;
/**
* @var SymfonyBridgeAdapter
*/
private $adapter;
/**
* @param SymfonyBridgeAdapter $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'])) {
if (empty($config['dbal'])) {
throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
}
$this->ormLoad($config['orm'], $container);
}
$this->addClassesToCompile(array(
'Doctrine\\Common\\Annotations\\DocLexer',
'Doctrine\\Common\\Annotations\\FileCacheReader',
'Doctrine\\Common\\Annotations\\PhpParser',
'Doctrine\\Common\\Annotations\\Reader',
'Doctrine\\Common\\Lexer',
'Doctrine\\Common\\Persistence\\ConnectionRegistry',
'Doctrine\\Common\\Persistence\\Proxy',
'Doctrine\\Common\\Util\\ClassUtils',
'Doctrine\\Bundle\\DoctrineBundle\\Registry',
));
}
/**
* 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->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 = array();
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);
$def = $container->getDefinition('doctrine.dbal.connection');
if (method_exists($def, 'setFactory')) {
// to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
$def->setFactory(array(new Reference('doctrine.dbal.connection_factory'), 'createConnection'));
} else {
// to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
$def->setFactoryService('doctrine.dbal.connection_factory');
$def->setFactoryMethod('createConnection');
}
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
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('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 DefinitionDecorator('doctrine.dbal.logger.profiling'));
$profilingLogger = new Reference($profilingLoggerId);
$container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
if (null !== $logger) {
$chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain');
$chainLogger->addMethodCall('addLogger', array($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', array($connection['auto_commit']));
}
unset($connection['auto_commit']);
if (isset($connection['schema_filter']) && $connection['schema_filter']) {
$configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
}
unset($connection['schema_filter']);
if ($logger) {
$configuration->addMethodCall('setSQLLogger', array($logger));
}
// event manager
$container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager'));
// connection
// PDO ignores the charset property before 5.3.6 so the init listener has to be used instead.
if (isset($connection['charset']) && version_compare(PHP_VERSION, '5.3.6', '<')) {
if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) ||
(isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) {
$mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%');
$mysqlSessionInit->setArguments(array($connection['charset']));
$mysqlSessionInit->setPublic(false);
$mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name));
$container->setDefinition(
sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name),
$mysqlSessionInit
);
unset($connection['charset']);
}
}
$options = $this->getConnectionOptions($connection);
$def = $container
->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection'))
->setArguments(array(
$options,
new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
$connection['mapping_types'],
))
;
if (!empty($connection['use_savepoints'])) {
$def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
}
}
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 (array(
'options' => 'driverOptions',
'driver_class' => 'driverClass',
'wrapper_class' => 'wrapperClass',
'keep_slave' => 'keepSlave',
'shard_choser' => 'shardChoser',
'server_version' => 'serverVersion',
'default_table_options' => 'defaultTableOptions',
) as $old => $new) {
if (isset($options[$old])) {
$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 = array(
'driver' => true, 'driverOptions' => true, 'driverClass' => true,
'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
'serverVersion' => 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 = array(
'driver' => true, 'driverOptions' => true, 'driverClass' => true,
'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
'platform' => true, 'slaves' => true, 'global' => true, 'shards' => 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';
}
} 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');
$this->entityManagers = array();
foreach (array_keys($config['entity_managers']) as $name) {
$this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
}
$container->setParameter('doctrine.entity_managers', $this->entityManagers);
if (empty($config['default_entity_manager'])) {
$tmp = array_keys($this->entityManagers);
$config['default_entity_manager'] = reset($tmp);
}
$container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
$options = array('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']));
// BC logic to handle DoctrineBridge < 2.6
if (!method_exists($this, 'fixManagersAutoMappings')) {
foreach ($config['entity_managers'] as $entityManager) {
if ($entityManager['auto_mapping'] && count($config['entity_managers']) > 1) {
throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.');
}
}
} else {
$config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
}
$def = $container->getDefinition('doctrine.orm.entity_manager.abstract');
if (method_exists($def, 'setFactory')) {
// to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
$def->setFactory(array('%doctrine.orm.entity_manager.class%', 'create'));
} else {
// to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
$def->setFactoryClass('%doctrine.orm.entity_manager.class%');
$def->setFactoryMethod('create');
}
$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) {
$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', array(
$name, $implementation, array(),
));
}
// 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', array('event' => 'loadClassMetadata'));
} else {
$def->addTag('doctrine.event_subscriber');
}
}
}
/**
* 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)
{
$ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('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 {
$container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), new Definition('%doctrine.orm.entity_listener_resolver.class%'));
}
$methods = array(
'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, array(
'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, array(
'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%'));
$listenerDef->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
}
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, array($arg));
}
foreach ($entityManager['hydrators'] as $name => $class) {
$ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class));
}
if (!empty($entityManager['dql'])) {
foreach ($entityManager['dql']['string_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
}
foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
}
foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
$ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
}
}
$enabledFilters = array();
$filtersParameters = array();
foreach ($entityManager['filters'] as $name => $filter) {
$ormConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
if ($filter['enabled']) {
$enabledFilters[] = $name;
}
if ($filter['parameters']) {
$filtersParameters[$name] = $filter['parameters'];
}
}
$managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
$container
->setDefinition($managerConfiguratorName, new DefinitionDecorator('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 DefinitionDecorator('doctrine.orm.entity_manager.abstract'))
->setArguments(array(
new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
))
->setConfigurator(array(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'])) {
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', array(
$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.
*
* @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.
*
* @param array $entityManager A configured ORM entity manager
* @param Definition $ormConfigDef A Definition instance
* @param ContainerBuilder $container A ContainerBuilder instance
*/
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 = array();
$this->aliasMap = array();
$this->loadMappingInformation($entityManager, $container);
$this->registerMappingDrivers($entityManager, $container);
$ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap));
}
/**
* Loads an ORM second level cache bundle mapping information.
*
* @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
*
* @param array $entityManager A configured ORM entity manager
* @param Definition $ormConfigDef A Definition instance
* @param ContainerBuilder $container A ContainerBuilder instance
*/
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']);
$slcFactoryDef = $container
->setDefinition($slcFactoryId, new Definition('%doctrine.orm.second_level_cache.default_cache_factory.class%'))
->setArguments(array(new Reference($regionsId), new Reference($driverId)));
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(array($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(array($regionRef, $region['lock_path'], $region['lock_lifetime']));
$regionRef = new Reference($regionId);
$regionsDef->addMethodCall('getLockLifetime', array($name, $region['lock_lifetime']));
}
$regionsDef->addMethodCall('setLifetime', array($name, $region['lifetime']));
$slcFactoryDef->addMethodCall('setRegion', array($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', array('statistics', $loggerStatsDef));
$configDef->addMethodCall('setCacheLogger', array($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', array($name, $loggerRef));
}
}
$configDef->addMethodCall('setCacheFactory', array($slcFactoryDef));
$configDef->addMethodCall('setRegionsConfiguration', array($regionsDef));
$ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', array($enabled));
$ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', array($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
* @param ContainerBuilder $container
*/
private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
{
$metadataFactoryService = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
$metadataFactoryDefinition = $container->register($metadataFactoryService, 'Doctrine\Common\Persistence\Mapping\ClassMetadataFactory');
$metadataFactoryDefinition->setFactory(array(
new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
'getMetadataFactory'
));
$metadataFactoryDefinition->setPublic(false);
$propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), 'Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
$propertyExtractorDefinition->addArgument(new Reference($metadataFactoryService));
$propertyExtractorDefinition->addTag('property_info.list_extractor', array('priority' => -1001));
$propertyExtractorDefinition->addTag('property_info.type_extractor', array('priority' => -999));
}
/**
* @param array $objectManager
* @param ContainerBuilder $container
* @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'));
}
}

View File

@@ -0,0 +1,155 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunSqlDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\ORM\Proxy\Autoloader;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\IntrospectableContainerInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass;
use Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider\EntityFactory;
/**
* Bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class DoctrineBundle extends Bundle
{
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());
}
/**
* {@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')) {
$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 = 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 $em \Doctrine\ORM\EntityManager */
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(array($classMetadata));
clearstatcache(true, Autoloader::resolveFile($proxyDir, $proxyNamespace, $class));
break;
}
};
}
$this->autoloader = Autoloader::register($dir, $namespace, $proxyGenerator);
}
}
/**
* {@inheritDoc}
*/
public function shutdown()
{
if (null !== $this->autoloader) {
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 (!$this->container instanceof IntrospectableContainerInterface || $this->container->initialized($id)) {
$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')) {
foreach ($this->container->getParameter('doctrine.connections') as $id) {
if (!$this->container instanceof IntrospectableContainerInterface || $this->container->initialized($id)) {
$this->container->get($id)->close();
}
}
}
}
/**
* {@inheritDoc}
*/
public function registerCommands(Application $application)
{
// Use the default logic when the ORM is available.
// This avoids listing all ORM commands by hand.
if (class_exists('Doctrine\\ORM\\Version')) {
parent::registerCommands($application);
return;
}
// Register only the DBAL commands if the ORM is not available.
$application->add(new CreateDatabaseDoctrineCommand());
$application->add(new DropDatabaseDoctrineCommand());
$application->add(new RunSqlDoctrineCommand());
}
}

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,81 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Query\Filter\SQLFilter;
/**
* Configurator for an EntityManager
*
* @author Christophe Coevoet <stof@notk.org>
*/
class ManagerConfigurator
{
private $enabledFilters = array();
private $filtersParameters = array();
public function __construct(array $enabledFilters, array $filtersParameters)
{
$this->enabledFilters = $enabledFilters;
$this->filtersParameters = $filtersParameters;
}
/**
* Create a connection by name.
*
* @param EntityManager $entityManager
*/
public function configure(EntityManager $entityManager)
{
$this->enableFilters($entityManager);
}
/**
* Enables filters for a given entity manager
*
* @param EntityManager $entityManager
*/
private function enableFilters(EntityManager $entityManager)
{
if (empty($this->enabledFilters)) {
return;
}
$filterCollection = $entityManager->getFilters();
foreach ($this->enabledFilters as $filter) {
$filterObject = $filterCollection->enable($filter);
if (null !== $filterObject) {
$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])) {
$parameters = $this->filtersParameters[$name];
foreach ($parameters as $paramName => $paramValue) {
$filter->setParameter($paramName, $paramValue);
}
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class ClassMetadataCollection
{
private $path;
private $namespace;
private $metadata;
/**
* Constructor
*
* @param array $metadata
*/
public function __construct(array $metadata)
{
$this->metadata = $metadata;
}
/**
* @return array
*/
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,216 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
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
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DisconnectedMetadataFactory
{
private $registry;
/**
* Constructor.
*
* @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 ClassMetadataCollection $metadata
* @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 = array();
foreach ($this->getAllMetadata() as $m) {
if (strpos($m->name, $namespace) === 0) {
$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(array($cmf->getMetadataFor($entity)));
}
}
return new ClassMetadataCollection(array());
}
/**
* @return array
*/
private function getAllMetadata()
{
$metadata = array();
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,24 @@
# Doctrine Bundle
Doctrine DBAL & ORM Bundle for the Symfony Framework.
Build Status: [![Build Status](https://secure.travis-ci.org/doctrine/DoctrineBundle.png?branch=master)](http://travis-ci.org/doctrine/DoctrineBundle)
## What is Doctrine?
The Doctrine Project is the home of a selected set of PHP libraries primarily focused on providing persistence
services and related functionality. Its prize projects are a Object Relational Mapper and the Database Abstraction
Layer it is built on top of. You can read more about the projects below or view a list of all projects.
Object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL).
One of its key features is the option to write database queries in a proprietary object oriented SQL dialect
called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful
alternative to SQL that maintains flexibility without requiring unnecessary code duplication.
DBAL is a powerful database abstraction layer with many features for database schema introspection,
schema management and PDO abstraction.
## Documentation
The documentation is rendered on [the symfony.com website](http://symfony.com/doc/master/bundles/DoctrineBundle/index.html).
The source of the documentation is available in the Resources/docs folder.

View File

@@ -0,0 +1,186 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\EntityManager;
/**
* References all Doctrine connections and entity managers in a given Container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Registry extends ManagerRegistry implements RegistryInterface
{
/**
* Construct.
*
* @param ContainerInterface $container
* @param array $connections
* @param array $entityManagers
* @param string $defaultConnection
* @param string $defaultEntityManager
*/
public function __construct(ContainerInterface $container, array $connections, array $entityManagers, $defaultConnection, $defaultEntityManager)
{
$this->setContainer($container);
parent::__construct('ORM', $connections, $entityManagers, $defaultConnection, $defaultEntityManager, 'Doctrine\ORM\Proxy\Proxy');
}
/**
* Gets the default entity manager name.
*
* @return string The default entity manager name
*
* @deprecated
*/
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.
*
* @param string $name The entity manager name (null for the default one)
*
* @return EntityManager
*
* @deprecated
*/
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
*
* @return EntityManager[] an array of all EntityManager instances
*
* @deprecated
*/
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 useable 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.
*
* @param string $name The entity manager name (null for the default one)
*
* @return EntityManager
*
* @deprecated
*/
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.
*
* @param string $alias The alias
*
* @return string The full namespace
*
* @deprecated
*/
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.
*
* @param string $alias The alias
*
* @return string The full namespace
*
* @see Configuration::getEntityNamespace
*/
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.
*
* @return array An array of connection names
*
* @deprecated
*/
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.
*
* @param string $class A Doctrine Entity class name
*
* @return EntityManager|null
*
* @deprecated
*/
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,67 @@
<?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.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" />
<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%">
<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.twig.doctrine_extension" class="Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension" public="false">
<tag name="twig.extension" />
</service>
</services>
</container>

View File

@@ -0,0 +1,133 @@
<?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\ORM\Mapping\DefaultEntityListenerResolver</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>
<!--- 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" />
<!-- 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" />
</services>
</container>

View File

@@ -0,0 +1,273 @@
<?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="sslmode" type="xsd:string" />
<xsd:attribute name="sslrootcert" 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="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,432 @@
{% extends app.request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %}
{% 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: app.request.query.get('connection'),
query: app.request.query.get('query')
})) }}
{% else %}
{{ block('queries') }}
{% endif %}
{% endblock %}
{% block queries %}
{% if profiler_markup_version == 1 %}
<style>
.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; }
</style>
{% endif %}
{% 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">{{ '%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 %}
<h2>Queries</h2>
{% 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 %}
<table class="alt queries-table">
<thead>
<tr>
<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>
<th style="width: 100%;">Info</th>
</tr>
</thead>
<tbody id="queries-{{ loop.index }}">
{% for i, query in queries %}
<tr id="queryNo-{{ i }}-{{ loop.parent.loop.index }}">
<td class="nowrap">{{ loop.index }}</td>
<td class="nowrap">{{ '%0.2f'|format(query.executionMS * 1000) }}&nbsp;ms</td>
<td>
{{ query.sql|doctrine_pretty_query(highlight_only = true) }}
<div>
<strong class="font-normal text-small">Parameters</strong>: {{ query.params|yaml_encode }}
</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 }}"></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 %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.connections, labels: ['Name', 'Service'] }, with_context = false ) }}
{% endif %}
<h2>Entity Managers</h2>
{% if not collector.managers %}
<div class="empty">
<p>There are no configured entity managers.</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.managers, labels: ['Name', 'Service'] }, with_context = false ) }}
{% 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 %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheCounts }, with_context = false) }}
{% 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>
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.hits }, with_context = false) }}
{% endif %}
{% if collector.cacheRegions.misses %}
<h3>Number of cache misses</h3>
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.misses }, with_context = false) }}
{% endif %}
{% if collector.cacheRegions.puts %}
<h3>Number of cache puts</h3>
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.puts }, with_context = false) }}
{% 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 %}

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" 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: 647 B

View File

@@ -0,0 +1,351 @@
<?php
/*
* This file is part of the Doctrine Bundle
*
* The code was originally distributed inside the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
* (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Doctrine\Bundle\DoctrineBundle\Twig;
/**
* This class contains the needed functions in order to do the query highlighting
*
* @author Florin Patan <florinpatan@gmail.com>
* @author Christophe Coevoet <stof@notk.org>
*/
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 array
*/
public function getFilters()
{
return array(
new \Twig_SimpleFilter('doctrine_minify_query', array($this, 'minifyQuery'), array('deprecated' => true)),
new \Twig_SimpleFilter('doctrine_pretty_query', array($this, 'formatQuery'), array('is_safe' => array('html'))),
new \Twig_SimpleFilter('doctrine_replace_query_parameters', array($this, 'replaceQueryParameters')),
);
}
/**
* Get the possible combinations of elements from the given array
*
* @param array $elements
* @param integer $combinationsLevel
*
* @return array
*/
private function getPossibleCombinations(array $elements, $combinationsLevel)
{
$baseCount = count($elements);
$result = array();
if (1 === $combinationsLevel) {
foreach ($elements as $element) {
$result[] = array($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) {
$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 = $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 integer $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 = array();
$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)) {
$result = $this->shrinkParameters($matches, $combination);
return $result;
}
}
// 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)) {
$result = $this->shrinkParameters($matches, $mandatoryKeywords);
return $result;
}
// Fallback in case we didn't managed to find any good match (can we actually have that happen?!)
$result = substr($query, 0, $this->maxCharWidth);
return $result;
}
/**
* Minify the query
*
* @param string $query
*
* @return string
*/
public function minifyQuery($query)
{
$result = '';
$keywords = array();
$required = 1;
// Check if we can match the query against any of the major types
switch (true) {
case stripos($query, 'SELECT') !== false:
$keywords = array('SELECT', 'FROM', 'WHERE', 'HAVING', 'ORDER BY', 'LIMIT');
$required = 2;
break;
case stripos($query, 'DELETE') !== false:
$keywords = array('DELETE', 'FROM', 'WHERE', 'ORDER BY', 'LIMIT');
$required = 2;
break;
case stripos($query, 'UPDATE') !== false:
$keywords = array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT');
$required = 2;
break;
case stripos($query, 'INSERT') !== false:
$keywords = array('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) {
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 null === $result:
$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 $parameters
*
* @return string
*/
public function replaceQueryParameters($query, array $parameters)
{
$i = 0;
$result = preg_replace_callback(
'/\?|((?<!:):[a-z0-9_]+)/i',
function ($matches) use ($parameters, &$i) {
$key = substr($matches[0], 1);
if (!array_key_exists($i, $parameters) && (false === $key || !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
);
return $result;
}
/**
* 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,57 @@
{
"name": "doctrine/doctrine-bundle",
"type": "symfony-bundle",
"description": "Symfony DoctrineBundle",
"keywords": ["DBAL", "ORM", "Database", "Persistence"],
"homepage": "http://www.doctrine-project.org",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Doctrine Project",
"homepage": "http://www.doctrine-project.org/"
}
],
"require": {
"php": ">=5.3.2",
"symfony/framework-bundle": "~2.3|~3.0",
"symfony/console": "~2.3|~3.0",
"doctrine/dbal": "~2.3",
"jdorn/sql-formatter": "~1.1",
"symfony/doctrine-bridge": "~2.2|~3.0",
"doctrine/doctrine-cache-bundle": "~1.0"
},
"require-dev": {
"doctrine/orm": "~2.3",
"symfony/yaml": "~2.2|~3.0",
"symfony/validator": "~2.2|~3.0",
"symfony/property-info": "~2.8|~3.0",
"symfony/phpunit-bridge": "~2.7|~3.0",
"twig/twig": "~1.10",
"satooshi/php-coveralls": "~0.6.1",
"phpunit/phpunit": "~4"
},
"suggest": {
"symfony/web-profiler-bundle": "To use the data collector.",
"doctrine/orm": "The Doctrine ORM integration is optional in the bundle."
},
"autoload": {
"psr-4": { "Doctrine\\Bundle\\DoctrineBundle\\": "" }
},
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="DoctrineBundle for the Symfony Framework">
<directory>./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>.</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>