Structure.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\View\Layout\Data;
  7. use Magento\Framework\Data\Structure as DataStructure;
  8. use Magento\Framework\App\State;
  9. /**
  10. * An associative data structure, that features "nested set" parent-child relations
  11. *
  12. * @api
  13. * @since 100.0.2
  14. */
  15. class Structure extends DataStructure
  16. {
  17. /**
  18. * Name increment counter
  19. *
  20. * @var array
  21. */
  22. protected $_nameIncrement = [];
  23. /**
  24. * @var \Psr\Log\LoggerInterface
  25. */
  26. protected $logger;
  27. /**
  28. * @var State
  29. */
  30. protected $state;
  31. /**
  32. * Constructor
  33. *
  34. * @param \Psr\Log\LoggerInterface $logger
  35. * @param State $state
  36. * @param array $elements
  37. */
  38. public function __construct(
  39. \Psr\Log\LoggerInterface $logger,
  40. State $state,
  41. array $elements = null
  42. ) {
  43. $this->logger = $logger;
  44. $this->state = $state;
  45. parent::__construct($elements);
  46. }
  47. /**
  48. * Register an element in structure
  49. *
  50. * Will assign an "anonymous" name to the element, if provided with an empty name
  51. *
  52. * @param string $name
  53. * @param string $type
  54. * @param string $class
  55. * @return string
  56. */
  57. public function createStructuralElement($name, $type, $class)
  58. {
  59. if (empty($name)) {
  60. $name = $this->_generateAnonymousName($class);
  61. }
  62. $this->createElement($name, ['type' => $type]);
  63. return $name;
  64. }
  65. /**
  66. * Generate anonymous element name for structure
  67. *
  68. * @param string $class
  69. * @return string
  70. */
  71. protected function _generateAnonymousName($class)
  72. {
  73. $position = strpos($class, '\\Block\\');
  74. $key = $position !== false ? substr($class, $position + 7) : $class;
  75. $key = strtolower(trim($key, '_'));
  76. if (!isset($this->_nameIncrement[$key])) {
  77. $this->_nameIncrement[$key] = 0;
  78. }
  79. do {
  80. $name = $key . '_' . $this->_nameIncrement[$key]++;
  81. } while ($this->hasElement($name));
  82. return $name;
  83. }
  84. /**
  85. * Reorder a child of a specified element
  86. *
  87. * If $offsetOrSibling is null, it will put the element to the end
  88. * If $offsetOrSibling is numeric (integer) value, it will put the element after/before specified position
  89. * Otherwise -- after/before specified sibling
  90. *
  91. * @param string $parentName
  92. * @param string $childName
  93. * @param string|int|null $offsetOrSibling
  94. * @param bool $after
  95. * @return void
  96. */
  97. public function reorderChildElement($parentName, $childName, $offsetOrSibling, $after = true)
  98. {
  99. if (is_numeric($offsetOrSibling)) {
  100. $offset = (int)abs($offsetOrSibling) * ($after ? 1 : -1);
  101. $this->reorderChild($parentName, $childName, $offset);
  102. } elseif (null === $offsetOrSibling) {
  103. $this->reorderChild($parentName, $childName, null);
  104. } else {
  105. $children = array_keys($this->getChildren($parentName));
  106. if ($this->getChildId($parentName, $offsetOrSibling) !== false) {
  107. $offsetOrSibling = $this->getChildId($parentName, $offsetOrSibling);
  108. }
  109. $sibling = $this->_filterSearchMinus($offsetOrSibling, $children, $after);
  110. if ($childName !== $sibling) {
  111. $siblingParentName = $this->getParentId($sibling);
  112. if ($parentName !== $siblingParentName) {
  113. if ($this->state->getMode() === State::MODE_DEVELOPER) {
  114. $this->logger->info(
  115. "Broken reference: the '{$childName}' tries to reorder itself towards '{$sibling}', but " .
  116. "their parents are different: '{$parentName}' and '{$siblingParentName}' respectively."
  117. );
  118. }
  119. return;
  120. }
  121. $this->reorderToSibling($parentName, $childName, $sibling, $after ? 1 : -1);
  122. }
  123. }
  124. }
  125. /**
  126. * Search for an array element using needle, but needle may be '-', which means "first" or "last" element
  127. *
  128. * Returns first or last element in the haystack, or the $needle argument
  129. *
  130. * @param string $needle
  131. * @param array $haystack
  132. * @param bool $isLast
  133. * @return string
  134. */
  135. protected function _filterSearchMinus($needle, array $haystack, $isLast)
  136. {
  137. if ('-' === $needle) {
  138. if ($isLast) {
  139. return array_pop($haystack);
  140. }
  141. return array_shift($haystack);
  142. }
  143. return $needle;
  144. }
  145. }