ÿØÿà JFIF    ÿÛ „ ( %!1!%*+...983,7(-.- ManagerRegistry.php000064400000002250152406467650010376 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Doctrine\Common\Persistence\AbstractManagerRegistry; /** * References Doctrine connections and entity/document managers. * * @author Lukas Kahwe Smith */ abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface { /** * @var ContainerInterface */ protected $container; /** * @inheritdoc */ protected function getService($name) { return $this->container->get($name); } /** * @inheritdoc */ protected function resetService($name) { $this->container->set($name, null); } /** * @inheritdoc */ public function setContainer(ContainerInterface $container = null) { $this->container = $container; } } Test/DoctrineTestHelper.php000064400000003350152406467650011763 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Test; use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\EntityManager; /** * Provides utility functions needed in tests. * * @author Bernhard Schussek */ class DoctrineTestHelper { /** * Returns an entity manager for testing. * * @return EntityManager */ public static function createTestEntityManager() { if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) { \PHPUnit_Framework_TestCase::markTestSkipped('This test requires SQLite support in your environment'); } $config = new \Doctrine\ORM\Configuration(); $config->setEntityNamespaces(array('SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures')); $config->setAutoGenerateProxyClasses(true); $config->setProxyDir(\sys_get_temp_dir()); $config->setProxyNamespace('SymfonyTests\Doctrine'); $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); $config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache()); $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache()); $params = array( 'driver' => 'pdo_sqlite', 'memory' => true, ); return EntityManager::create($params, $config); } /** * This class cannot be instantiated. */ private function __construct() { } } DependencyInjection/AbstractDoctrineExtension.php000064400000045233152406467650016354 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DependencyInjection; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Config\Resource\FileResource; /** * This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need. * * @author Benjamin Eberlei */ abstract class AbstractDoctrineExtension extends Extension { /** * Used inside metadata driver method to simplify aggregation of data. * * @var array */ protected $aliasMap = array(); /** * Used inside metadata driver method to simplify aggregation of data. * * @var array */ protected $drivers = array(); /** * @param array $objectManager A configured object manager. * @param ContainerBuilder $container A ContainerBuilder instance * * @throws \InvalidArgumentException */ protected function loadMappingInformation(array $objectManager, ContainerBuilder $container) { if ($objectManager['auto_mapping']) { // automatically register bundle mappings foreach (array_keys($container->getParameter('kernel.bundles')) as $bundle) { if (!isset($objectManager['mappings'][$bundle])) { $objectManager['mappings'][$bundle] = array( 'mapping' => true, 'is_bundle' => true, ); } } } foreach ($objectManager['mappings'] as $mappingName => $mappingConfig) { if (null !== $mappingConfig && false === $mappingConfig['mapping']) { continue; } $mappingConfig = array_replace(array( 'dir' => false, 'type' => false, 'prefix' => false, ), (array) $mappingConfig); $mappingConfig['dir'] = $container->getParameterBag()->resolveValue($mappingConfig['dir']); // a bundle configuration is detected by realizing that the specified dir is not absolute and existing if (!isset($mappingConfig['is_bundle'])) { $mappingConfig['is_bundle'] = !is_dir($mappingConfig['dir']); } if ($mappingConfig['is_bundle']) { $bundle = null; foreach ($container->getParameter('kernel.bundles') as $name => $class) { if ($mappingName === $name) { $bundle = new \ReflectionClass($class); break; } } if (null === $bundle) { throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $mappingName)); } $mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container); if (!$mappingConfig) { continue; } } $this->assertValidMappingConfiguration($mappingConfig, $objectManager['name']); $this->setMappingDriverConfig($mappingConfig, $mappingName); $this->setMappingDriverAlias($mappingConfig, $mappingName); } } /** * Register the alias for this mapping driver. * * Aliases can be used in the Query languages of all the Doctrine object managers to simplify writing tasks. * * @param array $mappingConfig * @param string $mappingName */ protected function setMappingDriverAlias($mappingConfig, $mappingName) { if (isset($mappingConfig['alias'])) { $this->aliasMap[$mappingConfig['alias']] = $mappingConfig['prefix']; } else { $this->aliasMap[$mappingName] = $mappingConfig['prefix']; } } /** * Register the mapping driver configuration for later use with the object managers metadata driver chain. * * @param array $mappingConfig * @param string $mappingName * * @throws \InvalidArgumentException */ protected function setMappingDriverConfig(array $mappingConfig, $mappingName) { if (is_dir($mappingConfig['dir'])) { $this->drivers[$mappingConfig['type']][$mappingConfig['prefix']] = realpath($mappingConfig['dir']); } else { throw new \InvalidArgumentException(sprintf('Invalid Doctrine mapping path given. Cannot load Doctrine mapping/bundle named "%s".', $mappingName)); } } /** * If this is a bundle controlled mapping all the missing information can be autodetected by this method. * * Returns false when autodetection failed, an array of the completed information otherwise. * * @param array $bundleConfig * @param \ReflectionClass $bundle * @param ContainerBuilder $container A ContainerBuilder instance * * @return array|false */ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container) { $bundleDir = dirname($bundle->getFilename()); if (!$bundleConfig['type']) { $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container); } if (!$bundleConfig['type']) { // skip this bundle, no mapping information was found. return false; } if (!$bundleConfig['dir']) { if (in_array($bundleConfig['type'], array('annotation', 'staticphp'))) { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName(); } else { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory(); } } else { $bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir']; } if (!$bundleConfig['prefix']) { $bundleConfig['prefix'] = $bundle->getNamespaceName().'\\'.$this->getMappingObjectDefaultName(); } return $bundleConfig; } /** * Register all the collected mapping information with the object manager by registering the appropriate mapping drivers. * * @param array $objectManager * @param ContainerBuilder $container A ContainerBuilder instance */ protected function registerMappingDrivers($objectManager, ContainerBuilder $container) { // configure metadata driver for each bundle based on the type of mapping files found if ($container->hasDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'))) { $chainDriverDef = $container->getDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver')); } else { $chainDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.driver_chain.class%')); $chainDriverDef->setPublic(false); } foreach ($this->drivers as $driverType => $driverPaths) { $mappingService = $this->getObjectManagerElementName($objectManager['name'].'_'.$driverType.'_metadata_driver'); if ($container->hasDefinition($mappingService)) { $mappingDriverDef = $container->getDefinition($mappingService); $args = $mappingDriverDef->getArguments(); if ($driverType == 'annotation') { $args[1] = array_merge(array_values($driverPaths), $args[1]); } else { $args[0] = array_merge(array_values($driverPaths), $args[0]); } $mappingDriverDef->setArguments($args); } elseif ($driverType == 'annotation') { $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( new Reference($this->getObjectManagerElementName('metadata.annotation_reader')), array_values($driverPaths) )); } else { $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( array_values($driverPaths) )); } $mappingDriverDef->setPublic(false); if (false !== strpos($mappingDriverDef->getClass(), 'yml') || false !== strpos($mappingDriverDef->getClass(), 'xml')) { $mappingDriverDef->setArguments(array(array_flip($driverPaths))); $mappingDriverDef->addMethodCall('setGlobalBasename', array('mapping')); } $container->setDefinition($mappingService, $mappingDriverDef); foreach ($driverPaths as $prefix => $driverPath) { $chainDriverDef->addMethodCall('addDriver', array(new Reference($mappingService), $prefix)); } } $container->setDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'), $chainDriverDef); } /** * Assertion if the specified mapping information is valid. * * @param array $mappingConfig * @param string $objectManagerName * * @throws \InvalidArgumentException */ protected function assertValidMappingConfiguration(array $mappingConfig, $objectManagerName) { if (!$mappingConfig['type'] || !$mappingConfig['dir'] || !$mappingConfig['prefix']) { throw new \InvalidArgumentException(sprintf('Mapping definitions for Doctrine manager "%s" require at least the "type", "dir" and "prefix" options.', $objectManagerName)); } if (!is_dir($mappingConfig['dir'])) { throw new \InvalidArgumentException(sprintf('Specified non-existing directory "%s" as Doctrine mapping source.', $mappingConfig['dir'])); } if (!in_array($mappingConfig['type'], array('xml', 'yml', 'annotation', 'php', 'staticphp'))) { throw new \InvalidArgumentException(sprintf('Can only configure "xml", "yml", "annotation", "php" or '. '"staticphp" through the DoctrineBundle. Use your own bundle to configure other metadata drivers. '. 'You can register them by adding a new driver to the '. '"%s" service definition.', $this->getObjectManagerElementName($objectManagerName.'.metadata_driver') )); } } /** * Detects what metadata driver to use for the supplied directory. * * @param string $dir A directory path * @param ContainerBuilder $container A ContainerBuilder instance * * @return string|null A metadata driver short name, if one can be detected */ protected function detectMetadataDriver($dir, ContainerBuilder $container) { // add the closest existing directory as a resource $configPath = $this->getMappingResourceConfigDirectory(); $resource = $dir.'/'.$configPath; while (!is_dir($resource)) { $resource = dirname($resource); } $container->addResource(new FileResource($resource)); $extension = $this->getMappingResourceExtension(); if (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) && count($files)) { return 'xml'; } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) && count($files)) { return 'yml'; } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) && count($files)) { return 'php'; } // add the directory itself as a resource $container->addResource(new FileResource($dir)); if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) { return 'annotation'; } return null; } /** * Loads a configured object manager metadata, query or result cache driver. * * @param array $objectManager A configured object manager. * @param ContainerBuilder $container A ContainerBuilder instance. * @param string $cacheName * * @throws \InvalidArgumentException In case of unknown driver type. */ protected function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) { $cacheDriver = $objectManager[$cacheName.'_driver']; $cacheDriverService = $this->getObjectManagerElementName($objectManager['name'].'_'.$cacheName); switch ($cacheDriver['type']) { case 'service': $container->setAlias($cacheDriverService, new Alias($cacheDriver['id'], false)); return; case 'memcache': $memcacheClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcache.class').'%'; $memcacheInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcache_instance.class').'%'; $memcacheHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcache_host').'%'; $memcachePort = !empty($cacheDriver['port']) || (isset($cacheDriver['port']) && $cacheDriver['port'] === 0) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcache_port').'%'; $cacheDef = new Definition($memcacheClass); $memcacheInstance = new Definition($memcacheInstanceClass); $memcacheInstance->addMethodCall('connect', array( $memcacheHost, $memcachePort )); $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManager['name'])), $memcacheInstance); $cacheDef->addMethodCall('setMemcache', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManager['name']))))); break; case 'memcached': $memcachedClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcached.class').'%'; $memcachedInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcached_instance.class').'%'; $memcachedHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcached_host').'%'; $memcachedPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcached_port').'%'; $cacheDef = new Definition($memcachedClass); $memcachedInstance = new Definition($memcachedInstanceClass); $memcachedInstance->addMethodCall('addServer', array( $memcachedHost, $memcachedPort )); $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManager['name'])), $memcachedInstance); $cacheDef->addMethodCall('setMemcached', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManager['name']))))); break; case 'redis': $redisClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.redis.class').'%'; $redisInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.redis_instance.class').'%'; $redisHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.redis_host').'%'; $redisPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.redis_port').'%'; $cacheDef = new Definition($redisClass); $redisInstance = new Definition($redisInstanceClass); $redisInstance->addMethodCall('connect', array( $redisHost, $redisPort )); $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManager['name'])), $redisInstance); $cacheDef->addMethodCall('setRedis', array(new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManager['name']))))); break; case 'apc': case 'array': case 'xcache': case 'wincache': case 'zenddata': $cacheDef = new Definition('%'.$this->getObjectManagerElementName(sprintf('cache.%s.class', $cacheDriver['type'])).'%'); break; default: throw new \InvalidArgumentException(sprintf('"%s" is an unrecognized Doctrine cache driver.', $cacheDriver['type'])); } $cacheDef->setPublic(false); if (!isset($cacheDriver['namespace'])) { // generate a unique namespace for the given application $cacheDriver['namespace'] = 'sf2'.$this->getMappingResourceExtension().'_'.$objectManager['name'].'_'.hash('sha256',($container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment'))); } $cacheDef->addMethodCall('setNamespace', array($cacheDriver['namespace'])); $container->setDefinition($cacheDriverService, $cacheDef); } /** * Prefixes the relative dependency injection container path with the object manager prefix. * * @example $name is 'entity_manager' then the result would be 'doctrine.orm.entity_manager' * * @param string $name * * @return string */ abstract protected function getObjectManagerElementName($name); /** * Noun that describes the mapped objects such as Entity or Document. * * Will be used for autodetection of persistent objects directory. * * @return string */ abstract protected function getMappingObjectDefaultName(); /** * Relative path from the bundle root to the directory where mapping files reside. * * @return string */ abstract protected function getMappingResourceConfigDirectory(); /** * Extension used by the mapping files. * * @return string */ abstract protected function getMappingResourceExtension(); } DependencyInjection/CompilerPass/DoctrineValidationPass.php000064400000004377152406467650020242 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\Config\Resource\FileResource; /** * Registers additional validators * * @author Benjamin Eberlei */ class DoctrineValidationPass implements CompilerPassInterface { /** * @var string */ private $managerType; public function __construct($managerType) { $this->managerType = $managerType; } /** * {@inheritDoc} */ public function process(ContainerBuilder $container) { $this->updateValidatorMappingFiles($container, 'xml', 'xml'); $this->updateValidatorMappingFiles($container, 'yaml', 'yml'); } /** * Gets the validation mapping files for the format and extends them with * files matching a doctrine search pattern (Resources/config/validation.orm.xml) * * @param ContainerBuilder $container * @param string $mapping * @param string $extension */ private function updateValidatorMappingFiles(ContainerBuilder $container, $mapping, $extension) { if (!$container->hasParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files')) { return; } $files = $container->getParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files'); $validationPath = 'Resources/config/validation.'.$this->managerType.'.'.$extension; foreach ($container->getParameter('kernel.bundles') as $bundle) { $reflection = new \ReflectionClass($bundle); if (is_file($file = dirname($reflection->getFilename()).'/'.$validationPath)) { $files[] = realpath($file); $container->addResource(new FileResource($file)); } } $container->setParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files', $files); } } DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php000064400000012501152406467650023755 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; /** * Registers event listeners and subscribers to the available doctrine connections. * * @author Jeremy Mikola * @author Alexander */ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface { private $connections; private $container; private $eventManagers; private $managerTemplate; private $tagPrefix; /** * Constructor. * * @param string $connections Parameter ID for connections * @param string $managerTemplate sprintf() template for generating the event * manager's service ID for a connection name * @param string $tagPrefix Tag prefix for listeners and subscribers */ public function __construct($connections, $managerTemplate, $tagPrefix) { $this->connections = $connections; $this->managerTemplate = $managerTemplate; $this->tagPrefix = $tagPrefix; } /** * {@inheritDoc} */ public function process(ContainerBuilder $container) { if (!$container->hasParameter($this->connections)) { return; } $taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber'); $taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener'); if (empty($taggedSubscribers) && empty($taggedListeners)) { return; } $this->container = $container; $this->connections = $container->getParameter($this->connections); $sortFunc = function ($a, $b) { $a = isset($a['priority']) ? $a['priority'] : 0; $b = isset($b['priority']) ? $b['priority'] : 0; return $a > $b ? -1 : 1; }; if (!empty($taggedSubscribers)) { $subscribersPerCon = $this->groupByConnection($taggedSubscribers); foreach ($subscribersPerCon as $con => $subscribers) { $em = $this->getEventManager($con); uasort($subscribers, $sortFunc); foreach ($subscribers as $id => $instance) { $em->addMethodCall('addEventSubscriber', array(new Reference($id))); } } } if (!empty($taggedListeners)) { $listenersPerCon = $this->groupByConnection($taggedListeners, true); foreach ($listenersPerCon as $con => $listeners) { $em = $this->getEventManager($con); uasort($listeners, $sortFunc); foreach ($listeners as $id => $instance) { $em->addMethodCall('addEventListener', array( array_unique($instance['event']), isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id), )); } } } } private function groupByConnection(array $services, $isListener = false) { $grouped = array(); foreach ($allCons = array_keys($this->connections) as $con) { $grouped[$con] = array(); } foreach ($services as $id => $instances) { foreach ($instances as $instance) { if ($isListener) { if (!isset($instance['event'])) { throw new \InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); } $instance['event'] = array($instance['event']); if (isset($instance['lazy']) && $instance['lazy']) { $this->container->getDefinition($id)->setPublic(true); } } $cons = isset($instance['connection']) ? array($instance['connection']) : $allCons; foreach ($cons as $con) { if (!isset($grouped[$con])) { throw new \RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); } if ($isListener && isset($grouped[$con][$id])) { $grouped[$con][$id]['event'] = array_merge($grouped[$con][$id]['event'], $instance['event']); } else { $grouped[$con][$id] = $instance; } } } } return $grouped; } private function getEventManager($name) { if (null === $this->eventManagers) { $this->eventManagers = array(); foreach ($this->connections as $n => $id) { $this->eventManagers[$n] = $this->container->getDefinition(sprintf($this->managerTemplate, $n)); } } return $this->eventManagers[$name]; } } DependencyInjection/CompilerPass/RegisterMappingsPass.php000064400000013625152406467650017737 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; /** * Base class for the doctrine bundles to provide a compiler pass class that * helps to register doctrine mappings. * * The compiler pass is meant to register the mappings with the metadata * chain driver corresponding to one of the object managers. * * For concrete implementations that are easy to use, see the * RegisterXyMappingsPass classes in the DoctrineBundle resp. * DoctrineMongodbBundle, DoctrineCouchdbBundle and DoctrinePhpcrBundle. * * @author David Buchmann */ abstract class RegisterMappingsPass implements CompilerPassInterface { /** * DI object for the driver to use, either a service definition for a * private service or a reference for a public service. * @var Definition|Reference */ protected $driver; /** * List of namespaces handled by the driver * @var string[] */ protected $namespaces; /** * List of potential container parameters that hold the object manager name * to register the mappings with the correct metadata driver, for example * array('acme.manager', 'doctrine.default_entity_manager') * @var string[] */ protected $managerParameters; /** * Naming pattern of the metadata chain driver service ids, for example * 'doctrine.orm.%s_metadata_driver' * @var string */ protected $driverPattern; /** * A name for a parameter in the container. If set, this compiler pass will * only do anything if the parameter is present. (But regardless of the * value of that parameter. * @var string */ protected $enabledParameter; /** * @param Definition|Reference $driver driver DI definition or reference * @param string[] $namespaces list of namespaces handled by $driver * @param string[] $managerParameters list of container parameters * that could hold the manager name * @param string $driverPattern pattern to get the metadata driver service names * @param string $enabledParameter service container parameter that must be * present to enable the mapping. Set to false * to not do any check, optional. */ public function __construct($driver, array $namespaces, array $managerParameters, $driverPattern, $enabledParameter = false) { $this->driver = $driver; $this->namespaces = $namespaces; $this->managerParameters = $managerParameters; $this->driverPattern = $driverPattern; $this->enabledParameter = $enabledParameter; } /** * Register mappings with the metadata drivers. * * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { if (!$this->enabled($container)) { return; } $mappingDriverDef = $this->getDriver($container); $chainDriverDefService = $this->getChainDriverServiceName($container); $chainDriverDef = $container->getDefinition($chainDriverDefService); foreach ($this->namespaces as $namespace) { $chainDriverDef->addMethodCall('addDriver', array($mappingDriverDef, $namespace)); } } /** * Get the service name of the metadata chain driver that the mappings * should be registered with. The default implementation loops over the * managerParameters and applies the first non-empty parameter it finds to * the driverPattern. * * @param ContainerBuilder $container * * @return string a service definition name * * @throws ParameterNotFoundException if non of the managerParameters has a * non-empty value. */ protected function getChainDriverServiceName(ContainerBuilder $container) { foreach ($this->managerParameters as $param) { if ($container->hasParameter($param)) { $name = $container->getParameter($param); if ($name) { return sprintf($this->driverPattern, $name); } } } throw new ParameterNotFoundException('None of the managerParameters resulted in a valid name'); } /** * Create the service definition for the metadata driver. * * @param ContainerBuilder $container passed on in case an extending class * needs access to the container. * * @return Definition|Reference the metadata driver to add to all chain drivers */ protected function getDriver(ContainerBuilder $container) { return $this->driver; } /** * Determine whether this mapping should be activated or not. This allows * to take this decision with the container builder available. * * This default implementation checks if the class has the enabledParameter * configured and if so if that parameter is present in the container. * * @param ContainerBuilder $container * * @return boolean whether this compiler pass really should register the mappings */ protected function enabled(ContainerBuilder $container) { return !$this->enabledParameter || $container->hasParameter($this->enabledParameter); } } DependencyInjection/Security/UserProvider/EntityFactory.php000064400000003345152406467650020266 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider; use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * EntityFactory creates services for Doctrine user provider. * * @author Fabien Potencier * @author Christophe Coevoet */ class EntityFactory implements UserProviderFactoryInterface { private $key; private $providerId; public function __construct($key, $providerId) { $this->key = $key; $this->providerId = $providerId; } public function create(ContainerBuilder $container, $id, $config) { $container ->setDefinition($id, new DefinitionDecorator($this->providerId)) ->addArgument($config['class']) ->addArgument($config['property']) ->addArgument($config['manager_name']) ; } public function getKey() { return $this->key; } public function addConfiguration(NodeDefinition $node) { $node ->children() ->scalarNode('class')->isRequired()->cannotBeEmpty()->end() ->scalarNode('property')->defaultNull()->end() ->scalarNode('manager_name')->defaultNull()->end() ->end() ; } } DataCollector/DoctrineDataCollector.php000064400000011101152406467650014216 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DataCollector; use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\DBAL\Logging\DebugStack; use Doctrine\DBAL\Types\Type; use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** * DoctrineDataCollector. * * @author Fabien Potencier */ class DoctrineDataCollector extends DataCollector { private $registry; private $connections; private $managers; private $loggers = array(); public function __construct(ManagerRegistry $registry) { $this->registry = $registry; $this->connections = $registry->getConnectionNames(); $this->managers = $registry->getManagerNames(); } /** * Adds the stack logger for a connection. * * @param string $name * @param DebugStack $logger */ public function addLogger($name, DebugStack $logger) { $this->loggers[$name] = $logger; } /** * {@inheritdoc} */ public function collect(Request $request, Response $response, \Exception $exception = null) { $queries = array(); foreach ($this->loggers as $name => $logger) { $queries[$name] = $this->sanitizeQueries($name, $logger->queries); } $this->data = array( 'queries' => $queries, 'connections' => $this->connections, 'managers' => $this->managers, ); } public function getManagers() { return $this->data['managers']; } public function getConnections() { return $this->data['connections']; } public function getQueryCount() { return array_sum(array_map('count', $this->data['queries'])); } public function getQueries() { return $this->data['queries']; } public function getTime() { $time = 0; foreach ($this->data['queries'] as $queries) { foreach ($queries as $query) { $time += $query['executionMS']; } } return $time; } /** * {@inheritdoc} */ public function getName() { return 'db'; } private function sanitizeQueries($connectionName, $queries) { foreach ($queries as $i => $query) { $queries[$i] = $this->sanitizeQuery($connectionName, $query); } return $queries; } private function sanitizeQuery($connectionName, $query) { $query['explainable'] = true; $query['params'] = (array) $query['params']; foreach ($query['params'] as $j => &$param) { if (isset($query['types'][$j])) { // Transform the param according to the type $type = $query['types'][$j]; if (is_string($type)) { $type = Type::getType($type); } if ($type instanceof Type) { $query['types'][$j] = $type->getBindingType(); $param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform()); } } list($param, $explainable) = $this->sanitizeParam($param); if (!$explainable) { $query['explainable'] = false; } } return $query; } /** * Sanitizes a param. * * The return value is an array with the sanitized value and a boolean * indicating if the original value was kept (allowing to use the sanitized * value to explain the query). * * @param mixed $var * * @return array */ private function sanitizeParam($var) { if (is_object($var)) { return array(sprintf('Object(%s)', get_class($var)), false); } if (is_array($var)) { $a = array(); $original = true; foreach ($var as $k => $v) { list($value, $orig) = $this->sanitizeParam($v); $original = $original && $orig; $a[$k] = $value; } return array($a, $original); } if (is_resource($var)) { return array(sprintf('Resource(%s)', get_resource_type($var)), false); } return array($var, true); } } CacheWarmer/ProxyCacheWarmer.php000064400000004372152406467650012706 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\CacheWarmer; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; /** * The proxy generator cache warmer generates all entity proxies. * * In the process of generating proxies the cache for all the metadata is primed also, * since this information is necessary to build the proxies in the first place. * * @author Benjamin Eberlei */ class ProxyCacheWarmer implements CacheWarmerInterface { private $registry; /** * Constructor. * * @param ManagerRegistry $registry A ManagerRegistry instance */ public function __construct(ManagerRegistry $registry) { $this->registry = $registry; } /** * This cache warmer is not optional, without proxies fatal error occurs! * * @return false */ public function isOptional() { return false; } /** * {@inheritdoc} */ public function warmUp($cacheDir) { foreach ($this->registry->getManagers() as $em) { // we need the directory no matter the proxy cache generation strategy if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) { if (false === @mkdir($proxyCacheDir, 0777, true)) { throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory "%s".', $proxyCacheDir)); } } elseif (!is_writable($proxyCacheDir)) { throw new \RuntimeException(sprintf('The Doctrine Proxy directory "%s" is not writeable for the current system user.', $proxyCacheDir)); } // if proxies are autogenerated we don't need to generate them in the cache warmer if ($em->getConfiguration()->getAutoGenerateProxyClasses()) { continue; } $classes = $em->getMetadataFactory()->getAllMetadata(); $em->getProxyFactory()->generateProxyClasses($classes); } } } ExpressionLanguage/DoctrineParserCache.php000064400000002075152406467650014753 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\ExpressionLanguage; use Doctrine\Common\Cache\Cache; use Symfony\Component\ExpressionLanguage\ParsedExpression; use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; /** * @author Adrien Brault */ class DoctrineParserCache implements ParserCacheInterface { /** * @var Cache */ private $cache; public function __construct(Cache $cache) { $this->cache = $cache; } /** * {@inheritdoc} */ public function fetch($key) { if (false === $value = $this->cache->fetch($key)) { return null; } return $value; } /** * {@inheritdoc} */ public function save($key, ParsedExpression $expression) { $this->cache->save($key, $expression); } } Form/Type/DoctrineType.php000064400000014766152406467650011547 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\Type; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Form\Exception\RuntimeException; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface; use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener; use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer; use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; abstract class DoctrineType extends AbstractType { /** * @var ManagerRegistry */ protected $registry; /** * @var array */ private $choiceListCache = array(); /** * @var PropertyAccessorInterface */ private $propertyAccessor; public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null) { $this->registry = $registry; $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); } public function buildForm(FormBuilderInterface $builder, array $options) { if ($options['multiple']) { $builder ->addEventSubscriber(new MergeDoctrineCollectionListener()) ->addViewTransformer(new CollectionToArrayTransformer(), true) ; } } public function setDefaultOptions(OptionsResolverInterface $resolver) { $choiceListCache =& $this->choiceListCache; $registry = $this->registry; $propertyAccessor = $this->propertyAccessor; $type = $this; $loader = function (Options $options) use ($type) { if (null !== $options['query_builder']) { return $type->getLoader($options['em'], $options['query_builder'], $options['class']); } return null; }; $choiceList = function (Options $options) use (&$choiceListCache, $propertyAccessor) { // Support for closures $propertyHash = is_object($options['property']) ? spl_object_hash($options['property']) : $options['property']; $choiceHashes = $options['choices']; // Support for recursive arrays if (is_array($choiceHashes)) { // A second parameter ($key) is passed, so we cannot use // spl_object_hash() directly (which strictly requires // one parameter) array_walk_recursive($choiceHashes, function (&$value) { $value = spl_object_hash($value); }); } elseif ($choiceHashes instanceof \Traversable) { $hashes = array(); foreach ($choiceHashes as $value) { $hashes[] = spl_object_hash($value); } $choiceHashes = $hashes; } $preferredChoiceHashes = $options['preferred_choices']; if (is_array($preferredChoiceHashes)) { array_walk_recursive($preferredChoiceHashes, function (&$value) { $value = spl_object_hash($value); }); } // Support for custom loaders (with query builders) $loaderHash = is_object($options['loader']) ? spl_object_hash($options['loader']) : $options['loader']; // Support for closures $groupByHash = is_object($options['group_by']) ? spl_object_hash($options['group_by']) : $options['group_by']; $hash = hash('sha256', json_encode(array( spl_object_hash($options['em']), $options['class'], $propertyHash, $loaderHash, $choiceHashes, $preferredChoiceHashes, $groupByHash ))); if (!isset($choiceListCache[$hash])) { $choiceListCache[$hash] = new EntityChoiceList( $options['em'], $options['class'], $options['property'], $options['loader'], $options['choices'], $options['preferred_choices'], $options['group_by'], $propertyAccessor ); } return $choiceListCache[$hash]; }; $emNormalizer = function (Options $options, $em) use ($registry) { /* @var ManagerRegistry $registry */ if (null !== $em) { return $registry->getManager($em); } $em = $registry->getManagerForClass($options['class']); if (null === $em) { throw new RuntimeException(sprintf( 'Class "%s" seems not to be a managed Doctrine entity. ' . 'Did you forget to map it?', $options['class'] )); } return $em; }; $resolver->setDefaults(array( 'em' => null, 'property' => null, 'query_builder' => null, 'loader' => $loader, 'choices' => null, 'choice_list' => $choiceList, 'group_by' => null, )); $resolver->setRequired(array('class')); $resolver->setNormalizers(array( 'em' => $emNormalizer, )); $resolver->setAllowedTypes(array( 'loader' => array('null', 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'), )); } /** * Return the default loader object. * * @param ObjectManager $manager * @param mixed $queryBuilder * @param string $class * * @return EntityLoaderInterface */ abstract public function getLoader(ObjectManager $manager, $queryBuilder, $class); public function getParent() { return 'choice'; } } Form/Type/EntityType.php000064400000001646152406467650011245 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\Type; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; class EntityType extends DoctrineType { /** * Return the default loader object. * * @param ObjectManager $manager * @param mixed $queryBuilder * @param string $class * @return ORMQueryBuilderLoader */ public function getLoader(ObjectManager $manager, $queryBuilder, $class) { return new ORMQueryBuilderLoader( $queryBuilder, $manager, $class ); } public function getName() { return 'entity'; } } Form/DataTransformer/CollectionToArrayTransformer.php000064400000003520152406467650017113 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\DataTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\DataTransformerInterface; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\ArrayCollection; /** * @author Bernhard Schussek */ class CollectionToArrayTransformer implements DataTransformerInterface { /** * Transforms a collection into an array. * * @param Collection $collection A collection of entities * * @return mixed An array of entities * * @throws TransformationFailedException */ public function transform($collection) { if (null === $collection) { return array(); } // For cases when the collection getter returns $collection->toArray() // in order to prevent modifications of the returned collection if (is_array($collection)) { return $collection; } if (!$collection instanceof Collection) { throw new TransformationFailedException('Expected a Doctrine\Common\Collections\Collection object.'); } return $collection->toArray(); } /** * Transforms choice keys into entities. * * @param mixed $array An array of entities * * @return Collection A collection of entities */ public function reverseTransform($array) { if ('' === $array || null === $array) { $array = array(); } else { $array = (array) $array; } return new ArrayCollection($array); } } Form/DoctrineOrmTypeGuesser.php000064400000013560152406467650012631 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form; use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\Common\Persistence\Mapping\MappingException; use Doctrine\ORM\Mapping\MappingException as LegacyMappingException; use Symfony\Component\Form\FormTypeGuesserInterface; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\TypeGuess; use Symfony\Component\Form\Guess\ValueGuess; use Doctrine\Common\Util\ClassUtils; class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface { protected $registry; private $cache = array(); public function __construct(ManagerRegistry $registry) { $this->registry = $registry; } /** * {@inheritDoc} */ public function guessType($class, $property) { if (!$ret = $this->getMetadata($class)) { return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); } list($metadata, $name) = $ret; if ($metadata->hasAssociation($property)) { $multiple = $metadata->isCollectionValuedAssociation($property); $mapping = $metadata->getAssociationMapping($property); return new TypeGuess('entity', array('em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple), Guess::HIGH_CONFIDENCE); } switch ($metadata->getTypeOfField($property)) { case 'array': return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE); case 'boolean': return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE); case 'datetime': case 'vardatetime': case 'datetimetz': return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE); case 'date': return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE); case 'time': return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE); case 'decimal': case 'float': return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); case 'integer': case 'bigint': case 'smallint': return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); case 'string': return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE); case 'text': return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE); default: return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); } } /** * {@inheritDoc} */ public function guessRequired($class, $property) { $classMetadatas = $this->getMetadata($class); if (!$classMetadatas) { return null; } /** @var ClassMetadataInfo $classMetadata */ $classMetadata = $classMetadatas[0]; // Check whether the field exists and is nullable or not if ($classMetadata->hasField($property)) { if (!$classMetadata->isNullable($property)) { return new ValueGuess(true, Guess::HIGH_CONFIDENCE); } return new ValueGuess(false, Guess::MEDIUM_CONFIDENCE); } // Check whether the association exists, is a to-one association and its // join column is nullable or not if ($classMetadata->isAssociationWithSingleJoinColumn($property)) { $mapping = $classMetadata->getAssociationMapping($property); if (!isset($mapping['joinColumns'][0]['nullable'])) { // The "nullable" option defaults to true, in that case the // field should not be required. return new ValueGuess(false, Guess::HIGH_CONFIDENCE); } return new ValueGuess(!$mapping['joinColumns'][0]['nullable'], Guess::HIGH_CONFIDENCE); } return null; } /** * {@inheritDoc} */ public function guessMaxLength($class, $property) { $ret = $this->getMetadata($class); if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { $mapping = $ret[0]->getFieldMapping($property); if (isset($mapping['length'])) { return new ValueGuess($mapping['length'], Guess::HIGH_CONFIDENCE); } if (in_array($ret[0]->getTypeOfField($property), array('decimal', 'float'))) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } } /** * {@inheritDoc} */ public function guessPattern($class, $property) { $ret = $this->getMetadata($class); if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { if (in_array($ret[0]->getTypeOfField($property), array('decimal', 'float'))) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } } protected function getMetadata($class) { // normalize class name $class = ClassUtils::getRealClass(ltrim($class, '\\')); if (array_key_exists($class, $this->cache)) { return $this->cache[$class]; } $this->cache[$class] = null; foreach ($this->registry->getManagers() as $name => $em) { try { return $this->cache[$class] = array($em->getClassMetadata($class), $name); } catch (MappingException $e) { // not an entity or mapped super class } catch (LegacyMappingException $e) { // not an entity or mapped super class, using Doctrine ORM 2.2 } } } } Form/ChoiceList/EntityChoiceList.php000064400000033634152406467650013461 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; use Symfony\Component\Form\Exception\RuntimeException; use Symfony\Component\Form\Exception\StringCastException; use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; /** * A choice list presenting a list of Doctrine entities as choices * * @author Bernhard Schussek */ class EntityChoiceList extends ObjectChoiceList { /** * @var ObjectManager */ private $em; /** * @var string */ private $class; /** * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata */ private $classMetadata; /** * Contains the query builder that builds the query for fetching the * entities * * This property should only be accessed through queryBuilder. * * @var EntityLoaderInterface */ private $entityLoader; /** * The identifier field, if the identifier is not composite * * @var array */ private $idField = null; /** * Whether to use the identifier for index generation * * @var Boolean */ private $idAsIndex = false; /** * Whether to use the identifier for value generation * * @var Boolean */ private $idAsValue = false; /** * Whether the entities have already been loaded. * * @var Boolean */ private $loaded = false; /** * The preferred entities. * * @var array */ private $preferredEntities = array(); /** * Creates a new entity choice list. * * @param ObjectManager $manager An EntityManager instance * @param string $class The class name * @param string $labelPath The property path used for the label * @param EntityLoaderInterface $entityLoader An optional query builder * @param array $entities An array of choices * @param array $preferredEntities An array of preferred choices * @param string $groupPath A property path pointing to the property used * to group the choices. Only allowed if * the choices are given as flat array. * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. */ public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null, array $preferredEntities = array(), $groupPath = null, PropertyAccessorInterface $propertyAccessor = null) { $this->em = $manager; $this->entityLoader = $entityLoader; $this->classMetadata = $manager->getClassMetadata($class); $this->class = $this->classMetadata->getName(); $this->loaded = is_array($entities) || $entities instanceof \Traversable; $this->preferredEntities = $preferredEntities; $identifier = $this->classMetadata->getIdentifierFieldNames(); if (1 === count($identifier)) { $this->idField = $identifier[0]; $this->idAsValue = true; if (in_array($this->classMetadata->getTypeOfField($this->idField), array('integer', 'smallint', 'bigint'))) { $this->idAsIndex = true; } } if (!$this->loaded) { // Make sure the constraints of the parent constructor are // fulfilled $entities = array(); } parent::__construct($entities, $labelPath, $preferredEntities, $groupPath, null, $propertyAccessor); } /** * Returns the list of entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getChoices() { if (!$this->loaded) { $this->load(); } return parent::getChoices(); } /** * Returns the values for the entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getValues() { if (!$this->loaded) { $this->load(); } return parent::getValues(); } /** * Returns the choice views of the preferred choices as nested array with * the choice groups as top-level keys. * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getPreferredViews() { if (!$this->loaded) { $this->load(); } return parent::getPreferredViews(); } /** * Returns the choice views of the choices that are not preferred as nested * array with the choice groups as top-level keys. * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getRemainingViews() { if (!$this->loaded) { $this->load(); } return parent::getRemainingViews(); } /** * Returns the entities corresponding to the given values. * * @param array $values * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getChoicesForValues(array $values) { // Performance optimization // Also prevents the generation of "WHERE id IN ()" queries through the // entity loader. At least with MySQL and on the development machine // this was tested on, no exception was thrown for such invalid // statements, consequently no test fails when this code is removed. // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557 if (empty($values)) { return array(); } if (!$this->loaded) { // Optimize performance in case we have an entity loader and // a single-field identifier if ($this->idAsValue && $this->entityLoader) { $unorderedEntities = $this->entityLoader->getEntitiesByIds($this->idField, $values); $entitiesByValue = array(); $entities = array(); // Maintain order and indices from the given $values // An alternative approach to the following loop is to add the // "INDEX BY" clause to the Doctrine query in the loader, // but I'm not sure whether that's doable in a generic fashion. foreach ($unorderedEntities as $entity) { $value = $this->fixValue(current($this->getIdentifierValues($entity))); $entitiesByValue[$value] = $entity; } foreach ($values as $i => $value) { if (isset($entitiesByValue[$value])) { $entities[$i] = $entitiesByValue[$value]; } } return $entities; } $this->load(); } return parent::getChoicesForValues($values); } /** * Returns the values corresponding to the given entities. * * @param array $entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getValuesForChoices(array $entities) { // Performance optimization if (empty($entities)) { return array(); } if (!$this->loaded) { // Optimize performance for single-field identifiers. We already // know that the IDs are used as values // Attention: This optimization does not check choices for existence if ($this->idAsValue) { $values = array(); foreach ($entities as $i => $entity) { if ($entity instanceof $this->class) { // Make sure to convert to the right format $values[$i] = $this->fixValue(current($this->getIdentifierValues($entity))); } } return $values; } $this->load(); } return parent::getValuesForChoices($entities); } /** * Returns the indices corresponding to the given entities. * * @param array $entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface * * @deprecated Deprecated since version 2.4, to be removed in 3.0. */ public function getIndicesForChoices(array $entities) { // Performance optimization if (empty($entities)) { return array(); } if (!$this->loaded) { // Optimize performance for single-field identifiers. We already // know that the IDs are used as indices // Attention: This optimization does not check choices for existence if ($this->idAsIndex) { $indices = array(); foreach ($entities as $i => $entity) { if ($entity instanceof $this->class) { // Make sure to convert to the right format $indices[$i] = $this->fixIndex(current($this->getIdentifierValues($entity))); } } return $indices; } $this->load(); } return parent::getIndicesForChoices($entities); } /** * Returns the entities corresponding to the given values. * * @param array $values * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface * * @deprecated Deprecated since version 2.4, to be removed in 3.0. */ public function getIndicesForValues(array $values) { // Performance optimization if (empty($values)) { return array(); } if (!$this->loaded) { // Optimize performance for single-field identifiers. // Attention: This optimization does not check values for existence if ($this->idAsIndex && $this->idAsValue) { return $this->fixIndices($values); } $this->load(); } return parent::getIndicesForValues($values); } /** * Creates a new unique index for this entity. * * If the entity has a single-field identifier, this identifier is used. * * Otherwise a new integer is generated. * * @param mixed $entity The choice to create an index for * * @return integer|string A unique index containing only ASCII letters, * digits and underscores. */ protected function createIndex($entity) { if ($this->idAsIndex) { return $this->fixIndex(current($this->getIdentifierValues($entity))); } return parent::createIndex($entity); } /** * Creates a new unique value for this entity. * * If the entity has a single-field identifier, this identifier is used. * * Otherwise a new integer is generated. * * @param mixed $entity The choice to create a value for * * @return integer|string A unique value without character limitations. */ protected function createValue($entity) { if ($this->idAsValue) { return (string) current($this->getIdentifierValues($entity)); } return parent::createValue($entity); } /** * {@inheritdoc} */ protected function fixIndex($index) { $index = parent::fixIndex($index); // If the ID is a single-field integer identifier, it is used as // index. Replace any leading minus by underscore to make it a valid // form name. if ($this->idAsIndex && $index < 0) { $index = strtr($index, '-', '_'); } return $index; } /** * Loads the list with entities. */ private function load() { if ($this->entityLoader) { $entities = $this->entityLoader->getEntities(); } else { $entities = $this->em->getRepository($this->class)->findAll(); } try { // The second parameter $labels is ignored by ObjectChoiceList parent::initialize($entities, array(), $this->preferredEntities); } catch (StringCastException $e) { throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e); } $this->loaded = true; } /** * Returns the values of the identifier fields of an entity. * * Doctrine must know about this entity, that is, the entity must already * be persisted or added to the identity map before. Otherwise an * exception is thrown. * * @param object $entity The entity for which to get the identifier * * @return array The identifier values * * @throws RuntimeException If the entity does not exist in Doctrine's identity map */ private function getIdentifierValues($entity) { if (!$this->em->contains($entity)) { throw new RuntimeException( 'Entities passed to the choice field must be managed. Maybe ' . 'persist them in the entity manager?' ); } $this->em->initializeObject($entity); return $this->classMetadata->getIdentifierValues($entity); } } Form/ChoiceList/ORMQueryBuilderLoader.php000064400000005726152406467650014360 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Doctrine\ORM\QueryBuilder; use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManager; /** * Getting Entities through the ORM QueryBuilder */ class ORMQueryBuilderLoader implements EntityLoaderInterface { /** * Contains the query builder that builds the query for fetching the * entities * * This property should only be accessed through queryBuilder. * * @var QueryBuilder */ private $queryBuilder; /** * Construct an ORM Query Builder Loader * * @param QueryBuilder|\Closure $queryBuilder * @param EntityManager $manager * @param string $class * * @throws UnexpectedTypeException */ public function __construct($queryBuilder, $manager = null, $class = null) { // If a query builder was passed, it must be a closure or QueryBuilder // instance if (!($queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure'); } if ($queryBuilder instanceof \Closure) { if (!$manager instanceof EntityManager) { throw new UnexpectedTypeException($manager, 'Doctrine\ORM\EntityManager'); } $queryBuilder = $queryBuilder($manager->getRepository($class)); if (!$queryBuilder instanceof QueryBuilder) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); } } $this->queryBuilder = $queryBuilder; } /** * {@inheritDoc} */ public function getEntities() { return $this->queryBuilder->getQuery()->execute(); } /** * {@inheritDoc} */ public function getEntitiesByIds($identifier, array $values) { $qb = clone ($this->queryBuilder); $alias = current($qb->getRootAliases()); $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; $where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter); // Guess type $entity = current($qb->getRootEntities()); $metadata = $qb->getEntityManager()->getClassMetadata($entity); if (in_array($metadata->getTypeOfField($identifier), array('integer', 'bigint', 'smallint'))) { $parameterType = Connection::PARAM_INT_ARRAY; } else { $parameterType = Connection::PARAM_STR_ARRAY; } return $qb->andWhere($where) ->getQuery() ->setParameter($parameter, $values, $parameterType) ->getResult(); } } Form/ChoiceList/EntityLoaderInterface.php000064400000002075152406467650014455 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; /** * Custom loader for entities in the choice list. * * @author Benjamin Eberlei */ interface EntityLoaderInterface { /** * Returns an array of entities that are valid choices in the corresponding choice list. * * @return array The entities. */ public function getEntities(); /** * Returns an array of entities matching the given identifiers. * * @param string $identifier The identifier field of the object. This method * is not applicable for fields with multiple * identifiers. * @param array $values The values of the identifiers. * * @return array The entities. */ public function getEntitiesByIds($identifier, array $values); } Form/EventListener/MergeDoctrineCollectionListener.php000064400000002660152406467650017243 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\EventListener; use Doctrine\Common\Collections\Collection; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Merge changes from the request to a Doctrine\Common\Collections\Collection instance. * * This works with ORM, MongoDB and CouchDB instances of the collection interface. * * @author Bernhard Schussek * * @see Doctrine\Common\Collections\Collection */ class MergeDoctrineCollectionListener implements EventSubscriberInterface { public static function getSubscribedEvents() { // Higher priority than core MergeCollectionListener so that this one // is called before return array(FormEvents::SUBMIT => array('onBind', 10)); } public function onBind(FormEvent $event) { $collection = $event->getForm()->getData(); $data = $event->getData(); // If all items were removed, call clear which has a higher // performance on persistent collections if ($collection instanceof Collection && count($data) === 0) { $collection->clear(); } } } Form/DoctrineOrmExtension.php000064400000001612152406467650012321 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Form\AbstractExtension; use Symfony\Component\PropertyAccess\PropertyAccess; class DoctrineOrmExtension extends AbstractExtension { protected $registry; public function __construct(ManagerRegistry $registry) { $this->registry = $registry; } protected function loadTypes() { return array( new Type\EntityType($this->registry, PropertyAccess::createPropertyAccessor()), ); } protected function loadTypeGuesser() { return new DoctrineOrmTypeGuesser($this->registry); } } DataFixtures/ContainerAwareLoader.php000064400000002561152406467650013734 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\DataFixtures; use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\Common\DataFixtures\Loader; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Doctrine data fixtures loader that injects the service container into * fixture objects that implement ContainerAwareInterface. * * Note: Use of this class requires the Doctrine data fixtures extension, which * is a suggested dependency for Symfony. */ class ContainerAwareLoader extends Loader { /** * @var ContainerInterface */ private $container; /** * Constructor. * * @param ContainerInterface $container A ContainerInterface instance */ public function __construct(ContainerInterface $container) { $this->container = $container; } /** * {@inheritdoc} */ public function addFixture(FixtureInterface $fixture) { if ($fixture instanceof ContainerAwareInterface) { $fixture->setContainer($this->container); } parent::addFixture($fixture); } } HttpFoundation/DbalSessionHandler.php000064400000011407152406467650013751 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\HttpFoundation; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Driver\Connection; /** * DBAL based session storage. * * @author Fabien Potencier * @author Johannes M. Schmitt */ class DbalSessionHandler implements \SessionHandlerInterface { /** * @var Connection */ private $con; /** * @var string */ private $tableName; /** * Constructor. * * @param Connection $con An instance of Connection. * @param string $tableName Table name. */ public function __construct(Connection $con, $tableName = 'sessions') { $this->con = $con; $this->tableName = $tableName; } /** * {@inheritdoc} */ public function open($path = null, $name = null) { return true; } /** * {@inheritdoc} */ public function close() { // do nothing return true; } /** * {@inheritdoc} */ public function destroy($id) { try { $this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_id = :id", array( 'id' => $id, )); } catch (\PDOException $e) { throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); } return true; } /** * {@inheritdoc} */ public function gc($lifetime) { try { $this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array( 'time' => time() - $lifetime, )); } catch (\PDOException $e) { throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); } return true; } /** * {@inheritdoc} */ public function read($id) { try { $data = $this->con->executeQuery("SELECT sess_data FROM {$this->tableName} WHERE sess_id = :id", array( 'id' => $id, ))->fetchColumn(); if (false !== $data) { return base64_decode($data); } // session does not exist, create it $this->createNewSession($id); return ''; } catch (\PDOException $e) { throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); } } /** * {@inheritdoc} */ public function write($id, $data) { $platform = $this->con->getDatabasePlatform(); // this should maybe be abstracted in Doctrine DBAL if ($platform instanceof MySqlPlatform) { $sql = "INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%1\$s, %2\$s, %3\$d) " ."ON DUPLICATE KEY UPDATE sess_data = VALUES(sess_data), sess_time = CASE WHEN sess_time = %3\$d THEN (VALUES(sess_time) + 1) ELSE VALUES(sess_time) END"; } else { $sql = "UPDATE {$this->tableName} SET sess_data = %2\$s, sess_time = %3\$d WHERE sess_id = %1\$s"; } try { $rowCount = $this->con->exec(sprintf( $sql, $this->con->quote($id), //session data can contain non binary safe characters so we need to encode it $this->con->quote(base64_encode($data)), time() )); if (!$rowCount) { // No session exists in the database to update. This happens when we have called // session_regenerate_id() $this->createNewSession($id, $data); } } catch (\PDOException $e) { throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); } return true; } /** * Creates a new session with the given $id and $data * * @param string $id * @param string $data * * @return Boolean */ private function createNewSession($id, $data = '') { $this->con->exec(sprintf("INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%s, %s, %d)", $this->con->quote($id), //session data can contain non binary safe characters so we need to encode it $this->con->quote(base64_encode($data)), time() )); return true; } } HttpFoundation/DbalSessionHandlerSchema.php000064400000002251152406467650015067 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\HttpFoundation; use Doctrine\DBAL\Schema\Schema; /** * DBAL Session Storage Schema. * * @author Johannes M. Schmitt */ final class DbalSessionHandlerSchema extends Schema { private $tableName; public function __construct($tableName = 'sessions') { parent::__construct(); $this->tableName = $tableName; $this->addSessionTable(); } public function addToSchema(Schema $schema) { foreach ($this->getTables() as $table) { $schema->_addTable($table); } } private function addSessionTable() { $table = $this->createTable($this->tableName); $table->addColumn('sess_id', 'string'); $table->addColumn('sess_data', 'text')->setNotNull(true); $table->addColumn('sess_time', 'integer')->setNotNull(true)->setUnsigned(true); $table->setPrimaryKey(array('sess_id')); } } RegistryInterface.php000064400000005155152406467650010733 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine; use Doctrine\Common\Persistence\ManagerRegistry as ManagerRegistryInterface; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; /** * References Doctrine connections and entity managers. * * @author Fabien Potencier */ interface RegistryInterface extends ManagerRegistryInterface { /** * Gets the default entity manager name. * * @return string The default entity manager name */ public function getDefaultEntityManagerName(); /** * Gets a named entity manager. * * @param string $name The entity manager name (null for the default one) * * @return EntityManager */ public function getEntityManager($name = null); /** * Gets an array of all registered entity managers * * @return array An array of EntityManager instances */ public function getEntityManagers(); /** * 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 */ public function resetEntityManager($name = null); /** * 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 getEntityNamespace($alias); /** * Gets all connection names. * * @return array An array of connection names */ public function getEntityManagerNames(); /** * Gets the entity manager associated with a given class. * * @param string $class A Doctrine Entity class name * * @return EntityManager|null */ public function getEntityManagerForClass($class); } ContainerAwareEventManager.php000064400000010471152406467650012476 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine; use Doctrine\Common\EventArgs; use Doctrine\Common\EventManager; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Allows lazy loading of listener services. * * @author Johannes M. Schmitt */ class ContainerAwareEventManager extends EventManager { /** * Map of registered listeners. * => * * @var array */ private $listeners = array(); private $initialized = array(); private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } /** * Dispatches an event to all registered listeners. * * @param string $eventName The name of the event to dispatch. The name of the event is * the name of the method that is invoked on listeners. * @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners. * If not supplied, the single empty EventArgs instance is used. * @return boolean */ public function dispatchEvent($eventName, EventArgs $eventArgs = null) { if (isset($this->listeners[$eventName])) { $eventArgs = $eventArgs === null ? EventArgs::getEmptyInstance() : $eventArgs; $initialized = isset($this->initialized[$eventName]); foreach ($this->listeners[$eventName] as $hash => $listener) { if (!$initialized && is_string($listener)) { $this->listeners[$eventName][$hash] = $listener = $this->container->get($listener); } $listener->$eventName($eventArgs); } $this->initialized[$eventName] = true; } } /** * Gets the listeners of a specific event or all listeners. * * @param string $event The name of the event. * * @return array The event listeners for the specified event, or all event listeners. */ public function getListeners($event = null) { return $event ? $this->listeners[$event] : $this->listeners; } /** * Checks whether an event has any registered listeners. * * @param string $event * * @return boolean TRUE if the specified event has any listeners, FALSE otherwise. */ public function hasListeners($event) { return isset($this->listeners[$event]) && $this->listeners[$event]; } /** * Adds an event listener that listens on the specified events. * * @param string|array $events The event(s) to listen on. * @param object|string $listener The listener object. * * @throws \RuntimeException */ public function addEventListener($events, $listener) { if (is_string($listener)) { if ($this->initialized) { throw new \RuntimeException('Adding lazy-loading listeners after construction is not supported.'); } $hash = '_service_'.$listener; } else { // Picks the hash code related to that listener $hash = spl_object_hash($listener); } foreach ((array) $events as $event) { // Overrides listener if a previous one was associated already // Prevents duplicate listeners on same event (same instance only) $this->listeners[$event][$hash] = $listener; } } /** * Removes an event listener from the specified events. * * @param string|array $events * @param object|string $listener */ public function removeEventListener($events, $listener) { if (is_string($listener)) { $hash = '_service_'.$listener; } else { // Picks the hash code related to that listener $hash = spl_object_hash($listener); } foreach ((array) $events as $event) { // Check if actually have this listener associated if (isset($this->listeners[$event][$hash])) { unset($this->listeners[$event][$hash]); } } } } Logger/DbalLogger.php000064400000005633152406467650010524 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Logger; use Psr\Log\LoggerInterface; use Symfony\Component\Stopwatch\Stopwatch; use Doctrine\DBAL\Logging\SQLLogger; /** * DbalLogger. * * @author Fabien Potencier */ class DbalLogger implements SQLLogger { const MAX_STRING_LENGTH = 32; const BINARY_DATA_VALUE = '(binary value)'; protected $logger; protected $stopwatch; /** * Constructor. * * @param LoggerInterface $logger A LoggerInterface instance * @param Stopwatch $stopwatch A Stopwatch instance */ public function __construct(LoggerInterface $logger = null, Stopwatch $stopwatch = null) { $this->logger = $logger; $this->stopwatch = $stopwatch; } /** * {@inheritdoc} */ public function startQuery($sql, array $params = null, array $types = null) { if (null !== $this->stopwatch) { $this->stopwatch->start('doctrine', 'doctrine'); } if (is_array($params)) { foreach ($params as $index => $param) { if (!is_string($params[$index])) { continue; } // non utf-8 strings break json encoding if (!preg_match('#[\p{L}\p{N} ]#u', $params[$index])) { $params[$index] = self::BINARY_DATA_VALUE; continue; } // detect if the too long string must be shorten if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($params[$index])) { if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], $encoding)) { $params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, $encoding).' [...]'; continue; } } else { if (self::MAX_STRING_LENGTH < strlen($params[$index])) { $params[$index] = substr($params[$index], 0, self::MAX_STRING_LENGTH - 6).' [...]'; continue; } } } } if (null !== $this->logger) { $this->log($sql, null === $params ? array() : $params); } } /** * {@inheritdoc} */ public function stopQuery() { if (null !== $this->stopwatch) { $this->stopwatch->stop('doctrine'); } } /** * Logs a message. * * @param string $message A message to log * @param array $params The context */ protected function log($message, array $params) { $this->logger->debug($message, $params); } } Validator/Constraints/UniqueEntity.php000064400000002332152406467650014173 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** * Constraint for the Unique Entity validator * * @Annotation * @author Benjamin Eberlei */ class UniqueEntity extends Constraint { public $message = 'This value is already used.'; public $service = 'doctrine.orm.validator.unique'; public $em = null; public $repositoryMethod = 'findBy'; public $fields = array(); public $errorPath = null; public $ignoreNull = true; public function getRequiredOptions() { return array('fields'); } /** * The validator must be defined as a service with this name. * * @return string */ public function validatedBy() { return $this->service; } /** * {@inheritDoc} */ public function getTargets() { return self::CLASS_CONSTRAINT; } public function getDefaultOption() { return 'fields'; } } Validator/Constraints/UniqueEntityValidator.php000064400000012022152406467650016036 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Validator\Constraints; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\ConstraintValidator; /** * Unique Entity Validator checks if one or a set of fields contain unique values. * * @author Benjamin Eberlei */ class UniqueEntityValidator extends ConstraintValidator { /** * @var ManagerRegistry */ private $registry; /** * @param ManagerRegistry $registry */ public function __construct(ManagerRegistry $registry) { $this->registry = $registry; } /** * @param object $entity * @param Constraint $constraint * * @throws UnexpectedTypeException * @throws ConstraintDefinitionException */ public function validate($entity, Constraint $constraint) { if (!is_array($constraint->fields) && !is_string($constraint->fields)) { throw new UnexpectedTypeException($constraint->fields, 'array'); } if (null !== $constraint->errorPath && !is_string($constraint->errorPath)) { throw new UnexpectedTypeException($constraint->errorPath, 'string or null'); } $fields = (array) $constraint->fields; if (0 === count($fields)) { throw new ConstraintDefinitionException('At least one field has to be specified.'); } if ($constraint->em) { $em = $this->registry->getManager($constraint->em); if (!$em) { throw new ConstraintDefinitionException(sprintf('Object manager "%s" does not exist.', $constraint->em)); } } else { $em = $this->registry->getManagerForClass(get_class($entity)); if (!$em) { throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_class($entity))); } } $class = $em->getClassMetadata(get_class($entity)); /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ $criteria = array(); foreach ($fields as $fieldName) { if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) { throw new ConstraintDefinitionException(sprintf("The field '%s' is not mapped by Doctrine, so it cannot be validated for uniqueness.", $fieldName)); } $criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity); if ($constraint->ignoreNull && null === $criteria[$fieldName]) { return; } if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { /* Ensure the Proxy is initialized before using reflection to * read its identifiers. This is necessary because the wrapped * getter methods in the Proxy are being bypassed. */ $em->initializeObject($criteria[$fieldName]); $relatedClass = $em->getClassMetadata($class->getAssociationTargetClass($fieldName)); $relatedId = $relatedClass->getIdentifierValues($criteria[$fieldName]); if (count($relatedId) > 1) { throw new ConstraintDefinitionException( "Associated entities are not allowed to have more than one identifier field to be " . "part of a unique constraint in: ".$class->getName()."#".$fieldName ); } $criteria[$fieldName] = array_pop($relatedId); } } $repository = $em->getRepository(get_class($entity)); $result = $repository->{$constraint->repositoryMethod}($criteria); /* If the result is a MongoCursor, it must be advanced to the first * element. Rewinding should have no ill effect if $result is another * iterator implementation. */ if ($result instanceof \Iterator) { $result->rewind(); } elseif (is_array($result)) { reset($result); } /* If no entity matched the query criteria or a single entity matched, * which is the same as the entity being validated, the criteria is * unique. */ if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { return; } $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; $this->context->addViolationAt($errorPath, $constraint->message, array(), $criteria[$fields[0]]); } } Validator/DoctrineInitializer.php000064400000001644152406467650013201 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Validator; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Validator\ObjectInitializerInterface; /** * Automatically loads proxy object before validation. * * @author Fabien Potencier */ class DoctrineInitializer implements ObjectInitializerInterface { protected $registry; public function __construct(ManagerRegistry $registry) { $this->registry = $registry; } public function initialize($object) { $manager = $this->registry->getManagerForClass(get_class($object)); if (null !== $manager) { $manager->initializeObject($object); } } } Security/User/EntityUserProvider.php000064400000007203152406467650013651 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Security\User; use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserInterface; /** * Wrapper around a Doctrine ObjectManager. * * Provides easy to use provisioning for Doctrine entity users. * * @author Fabien Potencier * @author Johannes M. Schmitt */ class EntityUserProvider implements UserProviderInterface { private $class; private $repository; private $property; private $metadata; public function __construct(ManagerRegistry $registry, $class, $property = null, $managerName = null) { $em = $registry->getManager($managerName); $this->class = $class; $this->metadata = $em->getClassMetadata($class); if (false !== strpos($this->class, ':')) { $this->class = $this->metadata->getName(); } $this->repository = $em->getRepository($class); $this->property = $property; } /** * {@inheritdoc} */ public function loadUserByUsername($username) { if (null !== $this->property) { $user = $this->repository->findOneBy(array($this->property => $username)); } else { if (!$this->repository instanceof UserProviderInterface) { throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository))); } $user = $this->repository->loadUserByUsername($username); } if (null === $user) { throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); } return $user; } /** * {@inheritDoc} */ public function refreshUser(UserInterface $user) { if (!$user instanceof $this->class) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); } if ($this->repository instanceof UserProviderInterface) { $refreshedUser = $this->repository->refreshUser($user); } else { // The user must be reloaded via the primary key as all other data // might have changed without proper persistence in the database. // That's the case when the user has been changed by a form with // validation errors. if (!$id = $this->metadata->getIdentifierValues($user)) { throw new \InvalidArgumentException("You cannot refresh a user ". "from the EntityUserProvider that does not contain an identifier. ". "The user object has to be serialized with its own identifier " . "mapped by Doctrine." ); } $refreshedUser = $this->repository->find($id); if (null === $refreshedUser) { throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($id))); } } return $refreshedUser; } /** * {@inheritDoc} */ public function supportsClass($class) { return $class === $this->class || is_subclass_of($class, $this->class); } } Security/RememberMe/DoctrineTokenProvider.php000064400000011367152406467650015416 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Security\RememberMe; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Types\Type as DoctrineType; use PDO, DateTime; /** * This class provides storage for the tokens that is set in "remember me" * cookies. This way no password secrets will be stored in the cookies on * the client machine, and thus the security is improved. * * This depends only on doctrine in order to get a database connection * and to do the conversion of the datetime column. * * In order to use this class, you need the following table in your database: * CREATE TABLE `rememberme_token` ( * `series` char(88) UNIQUE PRIMARY KEY NOT NULL, * `value` char(88) NOT NULL, * `lastUsed` datetime NOT NULL, * `class` varchar(100) NOT NULL, * `username` varchar(200) NOT NULL * ); */ class DoctrineTokenProvider implements TokenProviderInterface { /** * Doctrine DBAL database connection * F.ex. service id: doctrine.dbal.default_connection * * @var \Doctrine\DBAL\Connection */ private $conn; /** * new DoctrineTokenProvider for the RememberMe authentication service * * @param \Doctrine\DBAL\Connection $conn */ public function __construct(Connection $conn) { $this->conn = $conn; } /** * {@inheritdoc} */ public function loadTokenBySeries($series) { $sql = 'SELECT class, username, value, lastUsed' . ' FROM rememberme_token WHERE series=:series'; $paramValues = array('series' => $series); $paramTypes = array('series' => PDO::PARAM_STR); $stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row) { return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new DateTime($row['lastUsed']) ); } throw new TokenNotFoundException('No token found.'); } /** * {@inheritdoc} */ public function deleteTokenBySeries($series) { $sql = 'DELETE FROM rememberme_token WHERE series=:series'; $paramValues = array('series' => $series); $paramTypes = array('series' => PDO::PARAM_STR); $this->conn->executeUpdate($sql, $paramValues, $paramTypes); } /** * {@inheritdoc} */ public function updateToken($series, $tokenValue, DateTime $lastUsed) { $sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed' . ' WHERE series=:series'; $paramValues = array('value' => $tokenValue, 'lastUsed' => $lastUsed, 'series' => $series); $paramTypes = array('value' => PDO::PARAM_STR, 'lastUsed' => DoctrineType::DATETIME, 'series' => PDO::PARAM_STR); $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes); if ($updated < 1) { throw new TokenNotFoundException('No token found.'); } } /** * {@inheritdoc} */ public function createNewToken(PersistentTokenInterface $token) { $sql = 'INSERT INTO rememberme_token' . ' (class, username, series, value, lastUsed)' . ' VALUES (:class, :username, :series, :value, :lastUsed)'; $paramValues = array('class' => $token->getClass(), 'username' => $token->getUsername(), 'series' => $token->getSeries(), 'value' => $token->getTokenValue(), 'lastUsed' => $token->getLastUsed()); $paramTypes = array('class' => PDO::PARAM_STR, 'username' => PDO::PARAM_STR, 'series' => PDO::PARAM_STR, 'value' => PDO::PARAM_STR, 'lastUsed' => DoctrineType::DATETIME); $this->conn->executeUpdate($sql, $paramValues, $paramTypes); } } autoloader.php000064400000000517152406467650007436 0ustar00