vendor/symfony/form/ChoiceList/Factory/CachingFactoryDecorator.php line 178

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\ChoiceList\Factory;
  11. use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
  12. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  13. use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
  14. use Symfony\Contracts\Service\ResetInterface;
  15. /**
  16. * Caches the choice lists created by the decorated factory.
  17. *
  18. * To cache a list based on its options, arguments must be decorated
  19. * by a {@see Cache\AbstractStaticOption} implementation.
  20. *
  21. * @author Bernhard Schussek <bschussek@gmail.com>
  22. * @author Jules Pietri <jules@heahprod.com>
  23. */
  24. class CachingFactoryDecorator implements ChoiceListFactoryInterface, ResetInterface
  25. {
  26. private $decoratedFactory;
  27. /**
  28. * @var ChoiceListInterface[]
  29. */
  30. private $lists = [];
  31. /**
  32. * @var ChoiceListView[]
  33. */
  34. private $views = [];
  35. /**
  36. * Generates a SHA-256 hash for the given value.
  37. *
  38. * Optionally, a namespace string can be passed. Calling this method will
  39. * the same values, but different namespaces, will return different hashes.
  40. *
  41. * @param mixed $value The value to hash
  42. *
  43. * @return string The SHA-256 hash
  44. *
  45. * @internal
  46. */
  47. public static function generateHash($value, string $namespace = ''): string
  48. {
  49. if (\is_object($value)) {
  50. $value = spl_object_hash($value);
  51. } elseif (\is_array($value)) {
  52. array_walk_recursive($value, function (&$v) {
  53. if (\is_object($v)) {
  54. $v = spl_object_hash($v);
  55. }
  56. });
  57. }
  58. return hash('sha256', $namespace.':'.serialize($value));
  59. }
  60. public function __construct(ChoiceListFactoryInterface $decoratedFactory)
  61. {
  62. $this->decoratedFactory = $decoratedFactory;
  63. }
  64. /**
  65. * Returns the decorated factory.
  66. *
  67. * @return ChoiceListFactoryInterface
  68. */
  69. public function getDecoratedFactory()
  70. {
  71. return $this->decoratedFactory;
  72. }
  73. /**
  74. * {@inheritdoc}
  75. *
  76. * @param mixed $value
  77. * @param mixed $filter
  78. */
  79. public function createListFromChoices(iterable $choices, $value = null/* , $filter = null */)
  80. {
  81. $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
  82. if ($choices instanceof \Traversable) {
  83. $choices = iterator_to_array($choices);
  84. }
  85. $cache = true;
  86. // Only cache per value and filter when needed. The value is not validated on purpose.
  87. // The decorated factory may decide which values to accept and which not.
  88. if ($value instanceof Cache\ChoiceValue) {
  89. $value = $value->getOption();
  90. } elseif ($value) {
  91. $cache = false;
  92. }
  93. if ($filter instanceof Cache\ChoiceFilter) {
  94. $filter = $filter->getOption();
  95. } elseif ($filter) {
  96. $cache = false;
  97. }
  98. if (!$cache) {
  99. return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
  100. }
  101. $hash = self::generateHash([$choices, $value, $filter], 'fromChoices');
  102. if (!isset($this->lists[$hash])) {
  103. $this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
  104. }
  105. return $this->lists[$hash];
  106. }
  107. /**
  108. * {@inheritdoc}
  109. *
  110. * @param mixed $value
  111. * @param mixed $filter
  112. */
  113. public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/* , $filter = null */)
  114. {
  115. $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
  116. $cache = true;
  117. if ($loader instanceof Cache\ChoiceLoader) {
  118. $loader = $loader->getOption();
  119. } else {
  120. $cache = false;
  121. }
  122. if ($value instanceof Cache\ChoiceValue) {
  123. $value = $value->getOption();
  124. } elseif ($value) {
  125. $cache = false;
  126. }
  127. if ($filter instanceof Cache\ChoiceFilter) {
  128. $filter = $filter->getOption();
  129. } elseif ($filter) {
  130. $cache = false;
  131. }
  132. if (!$cache) {
  133. return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
  134. }
  135. $hash = self::generateHash([$loader, $value, $filter], 'fromLoader');
  136. if (!isset($this->lists[$hash])) {
  137. $this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
  138. }
  139. return $this->lists[$hash];
  140. }
  141. /**
  142. * {@inheritdoc}
  143. *
  144. * @param mixed $preferredChoices
  145. * @param mixed $label
  146. * @param mixed $index
  147. * @param mixed $groupBy
  148. * @param mixed $attr
  149. * @param mixed $labelTranslationParameters
  150. */
  151. public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null/* , $labelTranslationParameters = [] */)
  152. {
  153. $labelTranslationParameters = \func_num_args() > 6 ? func_get_arg(6) : [];
  154. $cache = true;
  155. if ($preferredChoices instanceof Cache\PreferredChoice) {
  156. $preferredChoices = $preferredChoices->getOption();
  157. } elseif ($preferredChoices) {
  158. $cache = false;
  159. }
  160. if ($label instanceof Cache\ChoiceLabel) {
  161. $label = $label->getOption();
  162. } elseif (null !== $label) {
  163. $cache = false;
  164. }
  165. if ($index instanceof Cache\ChoiceFieldName) {
  166. $index = $index->getOption();
  167. } elseif ($index) {
  168. $cache = false;
  169. }
  170. if ($groupBy instanceof Cache\GroupBy) {
  171. $groupBy = $groupBy->getOption();
  172. } elseif ($groupBy) {
  173. $cache = false;
  174. }
  175. if ($attr instanceof Cache\ChoiceAttr) {
  176. $attr = $attr->getOption();
  177. } elseif ($attr) {
  178. $cache = false;
  179. }
  180. if ($labelTranslationParameters instanceof Cache\ChoiceTranslationParameters) {
  181. $labelTranslationParameters = $labelTranslationParameters->getOption();
  182. } elseif ([] !== $labelTranslationParameters) {
  183. $cache = false;
  184. }
  185. if (!$cache) {
  186. return $this->decoratedFactory->createView(
  187. $list,
  188. $preferredChoices,
  189. $label,
  190. $index,
  191. $groupBy,
  192. $attr,
  193. $labelTranslationParameters
  194. );
  195. }
  196. $hash = self::generateHash([$list, $preferredChoices, $label, $index, $groupBy, $attr, $labelTranslationParameters]);
  197. if (!isset($this->views[$hash])) {
  198. $this->views[$hash] = $this->decoratedFactory->createView(
  199. $list,
  200. $preferredChoices,
  201. $label,
  202. $index,
  203. $groupBy,
  204. $attr,
  205. $labelTranslationParameters
  206. );
  207. }
  208. return $this->views[$hash];
  209. }
  210. public function reset()
  211. {
  212. $this->lists = [];
  213. $this->views = [];
  214. Cache\AbstractStaticOption::reset();
  215. }
  216. }