vendor/symfony/form/FormRegistry.php line 122

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form;
  11. use Symfony\Component\Form\Exception\ExceptionInterface;
  12. use Symfony\Component\Form\Exception\InvalidArgumentException;
  13. use Symfony\Component\Form\Exception\LogicException;
  14. use Symfony\Component\Form\Exception\UnexpectedTypeException;
  15. /**
  16. * The central registry of the Form component.
  17. *
  18. * @author Bernhard Schussek <bschussek@gmail.com>
  19. */
  20. class FormRegistry implements FormRegistryInterface
  21. {
  22. /**
  23. * @var FormExtensionInterface[]
  24. */
  25. private $extensions = [];
  26. /**
  27. * @var ResolvedFormTypeInterface[]
  28. */
  29. private $types = [];
  30. /**
  31. * @var FormTypeGuesserInterface|false|null
  32. */
  33. private $guesser = false;
  34. /**
  35. * @var ResolvedFormTypeFactoryInterface
  36. */
  37. private $resolvedTypeFactory;
  38. private $checkedTypes = [];
  39. /**
  40. * @param FormExtensionInterface[] $extensions
  41. *
  42. * @throws UnexpectedTypeException if any extension does not implement FormExtensionInterface
  43. */
  44. public function __construct(array $extensions, ResolvedFormTypeFactoryInterface $resolvedTypeFactory)
  45. {
  46. foreach ($extensions as $extension) {
  47. if (!$extension instanceof FormExtensionInterface) {
  48. throw new UnexpectedTypeException($extension, FormExtensionInterface::class);
  49. }
  50. }
  51. $this->extensions = $extensions;
  52. $this->resolvedTypeFactory = $resolvedTypeFactory;
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function getType(string $name)
  58. {
  59. if (!isset($this->types[$name])) {
  60. $type = null;
  61. foreach ($this->extensions as $extension) {
  62. if ($extension->hasType($name)) {
  63. $type = $extension->getType($name);
  64. break;
  65. }
  66. }
  67. if (!$type) {
  68. // Support fully-qualified class names
  69. if (!class_exists($name)) {
  70. throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not exist.', $name));
  71. }
  72. if (!is_subclass_of($name, FormTypeInterface::class)) {
  73. throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not implement "Symfony\Component\Form\FormTypeInterface".', $name));
  74. }
  75. $type = new $name();
  76. }
  77. $this->types[$name] = $this->resolveType($type);
  78. }
  79. return $this->types[$name];
  80. }
  81. /**
  82. * Wraps a type into a ResolvedFormTypeInterface implementation and connects it with its parent type.
  83. */
  84. private function resolveType(FormTypeInterface $type): ResolvedFormTypeInterface
  85. {
  86. $parentType = $type->getParent();
  87. $fqcn = \get_class($type);
  88. if (isset($this->checkedTypes[$fqcn])) {
  89. $types = implode(' > ', array_merge(array_keys($this->checkedTypes), [$fqcn]));
  90. throw new LogicException(sprintf('Circular reference detected for form type "%s" (%s).', $fqcn, $types));
  91. }
  92. $this->checkedTypes[$fqcn] = true;
  93. $typeExtensions = [];
  94. try {
  95. foreach ($this->extensions as $extension) {
  96. $typeExtensions[] = $extension->getTypeExtensions($fqcn);
  97. }
  98. return $this->resolvedTypeFactory->createResolvedType(
  99. $type,
  100. array_merge([], ...$typeExtensions),
  101. $parentType ? $this->getType($parentType) : null
  102. );
  103. } finally {
  104. unset($this->checkedTypes[$fqcn]);
  105. }
  106. }
  107. /**
  108. * {@inheritdoc}
  109. */
  110. public function hasType(string $name)
  111. {
  112. if (isset($this->types[$name])) {
  113. return true;
  114. }
  115. try {
  116. $this->getType($name);
  117. } catch (ExceptionInterface $e) {
  118. return false;
  119. }
  120. return true;
  121. }
  122. /**
  123. * {@inheritdoc}
  124. */
  125. public function getTypeGuesser()
  126. {
  127. if (false === $this->guesser) {
  128. $guessers = [];
  129. foreach ($this->extensions as $extension) {
  130. $guesser = $extension->getTypeGuesser();
  131. if ($guesser) {
  132. $guessers[] = $guesser;
  133. }
  134. }
  135. $this->guesser = !empty($guessers) ? new FormTypeGuesserChain($guessers) : null;
  136. }
  137. return $this->guesser;
  138. }
  139. /**
  140. * {@inheritdoc}
  141. */
  142. public function getExtensions()
  143. {
  144. return $this->extensions;
  145. }
  146. }