FormRegistry.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. * Extensions.
  24. *
  25. * @var FormExtensionInterface[]
  26. */
  27. private $extensions = [];
  28. /**
  29. * @var ResolvedFormTypeInterface[]
  30. */
  31. private $types = [];
  32. /**
  33. * @var FormTypeGuesserInterface|false|null
  34. */
  35. private $guesser = false;
  36. /**
  37. * @var ResolvedFormTypeFactoryInterface
  38. */
  39. private $resolvedTypeFactory;
  40. private $checkedTypes = [];
  41. /**
  42. * @param FormExtensionInterface[] $extensions An array of FormExtensionInterface
  43. *
  44. * @throws UnexpectedTypeException if any extension does not implement FormExtensionInterface
  45. */
  46. public function __construct(array $extensions, ResolvedFormTypeFactoryInterface $resolvedTypeFactory)
  47. {
  48. foreach ($extensions as $extension) {
  49. if (!$extension instanceof FormExtensionInterface) {
  50. throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormExtensionInterface');
  51. }
  52. }
  53. $this->extensions = $extensions;
  54. $this->resolvedTypeFactory = $resolvedTypeFactory;
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function getType(string $name)
  60. {
  61. if (!isset($this->types[$name])) {
  62. $type = null;
  63. foreach ($this->extensions as $extension) {
  64. if ($extension->hasType($name)) {
  65. $type = $extension->getType($name);
  66. break;
  67. }
  68. }
  69. if (!$type) {
  70. // Support fully-qualified class names
  71. if (!class_exists($name)) {
  72. throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not exist.', $name));
  73. }
  74. if (!is_subclass_of($name, 'Symfony\Component\Form\FormTypeInterface')) {
  75. throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not implement "Symfony\Component\Form\FormTypeInterface".', $name));
  76. }
  77. $type = new $name();
  78. }
  79. $this->types[$name] = $this->resolveType($type);
  80. }
  81. return $this->types[$name];
  82. }
  83. /**
  84. * Wraps a type into a ResolvedFormTypeInterface implementation and connects it with its parent type.
  85. */
  86. private function resolveType(FormTypeInterface $type): ResolvedFormTypeInterface
  87. {
  88. $typeExtensions = [];
  89. $parentType = $type->getParent();
  90. $fqcn = \get_class($type);
  91. if (isset($this->checkedTypes[$fqcn])) {
  92. $types = implode(' > ', array_merge(array_keys($this->checkedTypes), [$fqcn]));
  93. throw new LogicException(sprintf('Circular reference detected for form type "%s" (%s).', $fqcn, $types));
  94. }
  95. $this->checkedTypes[$fqcn] = true;
  96. try {
  97. foreach ($this->extensions as $extension) {
  98. $typeExtensions = array_merge(
  99. $typeExtensions,
  100. $extension->getTypeExtensions($fqcn)
  101. );
  102. }
  103. return $this->resolvedTypeFactory->createResolvedType(
  104. $type,
  105. $typeExtensions,
  106. $parentType ? $this->getType($parentType) : null
  107. );
  108. } finally {
  109. unset($this->checkedTypes[$fqcn]);
  110. }
  111. }
  112. /**
  113. * {@inheritdoc}
  114. */
  115. public function hasType(string $name)
  116. {
  117. if (isset($this->types[$name])) {
  118. return true;
  119. }
  120. try {
  121. $this->getType($name);
  122. } catch (ExceptionInterface $e) {
  123. return false;
  124. }
  125. return true;
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. public function getTypeGuesser()
  131. {
  132. if (false === $this->guesser) {
  133. $guessers = [];
  134. foreach ($this->extensions as $extension) {
  135. $guesser = $extension->getTypeGuesser();
  136. if ($guesser) {
  137. $guessers[] = $guesser;
  138. }
  139. }
  140. $this->guesser = !empty($guessers) ? new FormTypeGuesserChain($guessers) : null;
  141. }
  142. return $this->guesser;
  143. }
  144. /**
  145. * {@inheritdoc}
  146. */
  147. public function getExtensions()
  148. {
  149. return $this->extensions;
  150. }
  151. }