This commit is contained in:
Marek
2026-03-24 00:04:55 +01:00
commit c5229e48ed
4225 changed files with 511461 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers additional validators.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class DoctrineValidationPass implements CompilerPassInterface
{
public function __construct(
private readonly string $managerType,
) {
}
public function process(ContainerBuilder $container): void
{
$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).
*/
private function updateValidatorMappingFiles(ContainerBuilder $container, string $mapping, string $extension): void
{
if (!$container->hasParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files')) {
return;
}
$files = $container->getParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files');
$validationPath = '/config/validation.'.$this->managerType.'.'.$extension;
foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
if ($container->fileExists($file = $bundle['path'].'/Resources'.$validationPath) || $container->fileExists($file = $bundle['path'].$validationPath)) {
$files[] = $file;
}
}
$container->setParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files', $files);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Bridge\Doctrine\Types\DatePointType;
use Symfony\Bridge\Doctrine\Types\DayPointType;
use Symfony\Bridge\Doctrine\Types\TimePointType;
use Symfony\Component\Clock\DatePoint;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class RegisterDatePointTypePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!class_exists(DatePoint::class)) {
return;
}
if (!$container->hasParameter('doctrine.dbal.connection_factory.types')) {
return;
}
$types = $container->getParameter('doctrine.dbal.connection_factory.types');
$types['date_point'] ??= ['class' => DatePointType::class];
$types['day_point'] ??= ['class' => DayPointType::class];
$types['time_point'] ??= ['class' => TimePointType::class];
$container->setParameter('doctrine.dbal.connection_factory.types', $types);
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Bridge\Doctrine\ContainerAwareEventManager;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Registers event listeners to the available doctrine connections.
*
* @author Jeremy Mikola <jmikola@gmail.com>
* @author Alexander <iam.asm89@gmail.com>
* @author David Maicher <mail@dmaicher.de>
*/
class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
{
private array $connections;
/**
* @var array<string, Definition>
*/
private array $eventManagers = [];
/**
* @param string $managerTemplate sprintf() template for generating the event
* manager's service ID for a connection name
* @param string $tagPrefix Tag prefix for listeners
*/
public function __construct(
private readonly string $connectionsParameter,
private readonly string $managerTemplate,
private readonly string $tagPrefix,
) {
}
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter($this->connectionsParameter)) {
return;
}
$this->connections = $container->getParameter($this->connectionsParameter);
$listenerRefs = $this->addTaggedServices($container);
// replace service container argument of event managers with smaller service locator
// so services can even remain private
foreach ($listenerRefs as $connection => $refs) {
$this->getEventManagerDef($container, $connection)
->replaceArgument(0, ServiceLocatorTagPass::register($container, $refs));
}
}
private function addTaggedServices(ContainerBuilder $container): array
{
$listenerRefs = [];
$managerDefs = [];
foreach ($this->findAndSortTags($container) as [$id, $tag]) {
$connections = isset($tag['connection'])
? [$container->getParameterBag()->resolveValue($tag['connection'])]
: array_keys($this->connections);
if (!isset($tag['event'])) {
throw new InvalidArgumentException(\sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
}
foreach ($connections as $con) {
if (!isset($this->connections[$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 (!isset($managerDefs[$con])) {
$managerDef = $parentDef = $this->getEventManagerDef($container, $con);
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
$parentDef = $container->findDefinition($parentDef->getParent());
}
$managerClass = $container->getParameterBag()->resolveValue($parentDef->getClass());
$managerDefs[$con] = [$managerDef, $managerClass];
} else {
[$managerDef, $managerClass] = $managerDefs[$con];
}
if (ContainerAwareEventManager::class === $managerClass) {
$refs = $managerDef->getArguments()[1] ?? [];
$listenerRefs[$con][$id] = new Reference($id);
$refs[] = [[$tag['event']], $id];
$managerDef->setArgument(1, $refs);
} else {
$managerDef->addMethodCall('addEventListener', [[$tag['event']], new Reference($id)]);
}
}
}
return $listenerRefs;
}
private function getEventManagerDef(ContainerBuilder $container, string $name): Definition
{
if (!isset($this->eventManagers[$name])) {
$this->eventManagers[$name] = $container->getDefinition(\sprintf($this->managerTemplate, $name));
}
return $this->eventManagers[$name];
}
/**
* Finds and orders all service tags with the given name by their priority.
*
* The order of additions must be respected for services having the same priority,
* and knowing that the \SplPriorityQueue class does not respect the FIFO method,
* we should not use this class.
*
* @see https://bugs.php.net/53710
* @see https://bugs.php.net/60926
*/
private function findAndSortTags(ContainerBuilder $container): array
{
$sortedTags = [];
foreach ($container->findTaggedServiceIds($this->tagPrefix.'.event_listener', true) as $serviceId => $tags) {
foreach ($tags as $attributes) {
$priority = $attributes['priority'] ?? 0;
$sortedTags[$priority][] = [$serviceId, $attributes];
}
}
krsort($sortedTags);
return array_merge(...$sortedTags);
}
}

View File

@@ -0,0 +1,169 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* 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, see the RegisterXyMappingsPass classes
* in the DoctrineBundle resp.
* DoctrineMongodbBundle, DoctrineCouchdbBundle and DoctrinePhpcrBundle.
*
* @author David Buchmann <david@liip.ch>
*/
abstract class RegisterMappingsPass implements CompilerPassInterface
{
/**
* The $managerParameters is an ordered list of container parameters that could provide the
* name of the manager to register these namespaces and alias on. The first non-empty name
* is used, the others skipped.
*
* The $aliasMap parameter can be used to define bundle namespace shortcuts like the
* DoctrineBundle provides automatically for objects in the default Entity/Document folder.
*
* @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 for the metadata chain driver service ids (e.g. "doctrine.orm.%s_metadata_driver")
* @param string|false $enabledParameter Service container parameter that must be
* present to enable the mapping (regardless of the
* parameter value). Pass false to not do any check.
* @param string $configurationPattern Pattern for the Configuration service name,
* for example 'doctrine.orm.%s_configuration'.
* @param string $registerAliasMethodName Method name to call on the configuration service. This
* depends on the Doctrine implementation.
* For example addEntityNamespace.
* @param string[] $aliasMap Map of alias to namespace
*/
public function __construct(
protected Definition|Reference $driver,
protected array $namespaces,
protected array $managerParameters,
protected string $driverPattern,
protected string|false $enabledParameter = false,
private readonly string $configurationPattern = '',
private readonly string $registerAliasMethodName = '',
private readonly array $aliasMap = [],
) {
if ($aliasMap && (!$configurationPattern || !$registerAliasMethodName)) {
throw new \InvalidArgumentException('configurationPattern and registerAliasMethodName are required to register namespace alias.');
}
}
/**
* Register mappings and alias with the metadata drivers.
*/
public function process(ContainerBuilder $container): void
{
if (!$this->enabled($container)) {
return;
}
$mappingDriverDef = $this->getDriver($container);
$chainDriverDefService = $this->getChainDriverServiceName($container);
// Definition for a Doctrine\Persistence\Mapping\Driver\MappingDriverChain
$chainDriverDef = $container->getDefinition($chainDriverDefService);
foreach ($this->namespaces as $namespace) {
$chainDriverDef->addMethodCall('addDriver', [$mappingDriverDef, $namespace]);
}
if (!\count($this->aliasMap)) {
return;
}
$configurationServiceName = $this->getConfigurationServiceName($container);
// Definition of the Doctrine\...\Configuration class specific to the Doctrine flavour.
$configurationServiceDefinition = $container->getDefinition($configurationServiceName);
foreach ($this->aliasMap as $alias => $namespace) {
$configurationServiceDefinition->addMethodCall($this->registerAliasMethodName, [$alias, $namespace]);
}
}
/**
* Get the service name of the metadata chain driver that the mappings
* should be registered with.
*
* @throws InvalidArgumentException if non of the managerParameters has a
* non-empty value
*/
protected function getChainDriverServiceName(ContainerBuilder $container): string
{
return \sprintf($this->driverPattern, $this->getManagerName($container));
}
/**
* Create the service definition for the metadata driver.
*
* @param ContainerBuilder $container Passed on in case an extending class
* needs access to the container
*/
protected function getDriver(ContainerBuilder $container): Definition|Reference
{
return $this->driver;
}
/**
* Get the service name from the pattern and the configured manager name.
*
* @throws InvalidArgumentException if none of the managerParameters has a
* non-empty value
*/
private function getConfigurationServiceName(ContainerBuilder $container): string
{
return \sprintf($this->configurationPattern, $this->getManagerName($container));
}
/**
* Determine the manager name.
*
* The default implementation loops over the managerParameters and returns
* the first non-empty parameter.
*
* @throws InvalidArgumentException if none of the managerParameters is found in the container
*/
private function getManagerName(ContainerBuilder $container): string
{
foreach ($this->managerParameters as $param) {
if ($container->hasParameter($param)) {
$name = $container->getParameter($param);
if ($name) {
return $name;
}
}
}
throw new InvalidArgumentException(\sprintf('Could not find the manager name parameter in the container. Tried the following parameter names: "%s".', implode('", "', $this->managerParameters)));
}
/**
* 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.
*/
protected function enabled(ContainerBuilder $container): bool
{
return !$this->enabledParameter || $container->hasParameter($this->enabledParameter);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Bridge\Doctrine\Types\UlidType;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Uid\AbstractUid;
final class RegisterUidTypePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!class_exists(AbstractUid::class)) {
return;
}
if (!$container->hasParameter('doctrine.dbal.connection_factory.types')) {
return;
}
$typeDefinition = $container->getParameter('doctrine.dbal.connection_factory.types');
if (!isset($typeDefinition['uuid'])) {
$typeDefinition['uuid'] = ['class' => UuidType::class];
}
if (!isset($typeDefinition['ulid'])) {
$typeDefinition['ulid'] = ['class' => UlidType::class];
}
$container->setParameter('doctrine.dbal.connection_factory.types', $typeDefinition);
}
}