123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\View\Layout\Generator;
- use Magento\Framework\App\State;
- use Magento\Framework\Exception\LocalizedException;
- use Magento\Framework\ObjectManager\Config\Reader\Dom;
- use Magento\Framework\View\Element\Template;
- use Magento\Framework\View\Layout;
- /**
- * Class Block
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
- class Block implements Layout\GeneratorInterface
- {
- /**
- * Type of generator
- */
- const TYPE = 'block';
- /**
- * @var \Magento\Framework\View\Element\BlockFactory
- */
- protected $blockFactory;
- /**
- * @var \Magento\Framework\Data\Argument\InterpreterInterface
- */
- protected $argumentInterpreter;
- /**
- * @var \Magento\Framework\Event\ManagerInterface
- */
- protected $eventManager;
- /**
- * @var \Psr\Log\LoggerInterface
- */
- protected $logger;
- /**
- * @var \Magento\Framework\App\Config\ScopeConfigInterface
- */
- protected $scopeConfig;
- /**
- * @var \Magento\Framework\App\ScopeResolverInterface
- */
- protected $scopeResolver;
- /**
- * @var State
- */
- protected $appState;
- /**
- * @var \Magento\Framework\View\Element\ExceptionHandlerBlock
- */
- protected $exceptionHandlerBlockFactory;
- /**
- * Default block class name. Will be used if no class name is specified in block configuration.
- *
- * @var string
- */
- private $defaultClass;
- /**
- * @param \Magento\Framework\View\Element\BlockFactory $blockFactory
- * @param \Magento\Framework\Data\Argument\InterpreterInterface $argumentInterpreter
- * @param \Magento\Framework\Event\ManagerInterface $eventManager
- * @param \Psr\Log\LoggerInterface $logger
- * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
- * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver
- * @param \Magento\Framework\View\Element\ExceptionHandlerBlockFactory $exceptionHandlerBlockFactory
- * @param State $appState
- * @param string $defaultClass
- */
- public function __construct(
- \Magento\Framework\View\Element\BlockFactory $blockFactory,
- \Magento\Framework\Data\Argument\InterpreterInterface $argumentInterpreter,
- \Magento\Framework\Event\ManagerInterface $eventManager,
- \Psr\Log\LoggerInterface $logger,
- \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
- \Magento\Framework\App\ScopeResolverInterface $scopeResolver,
- \Magento\Framework\View\Element\ExceptionHandlerBlockFactory $exceptionHandlerBlockFactory,
- State $appState,
- $defaultClass = Template::class
- ) {
- $this->blockFactory = $blockFactory;
- $this->argumentInterpreter = $argumentInterpreter;
- $this->eventManager = $eventManager;
- $this->logger = $logger;
- $this->scopeConfig = $scopeConfig;
- $this->scopeResolver = $scopeResolver;
- $this->exceptionHandlerBlockFactory = $exceptionHandlerBlockFactory;
- $this->appState = $appState;
- $this->defaultClass = $defaultClass;
- }
- /**
- * @inheritdoc
- */
- public function getType()
- {
- return self::TYPE;
- }
- /**
- * Creates block object based on data and add it to the layout
- *
- * @param Layout\Reader\Context $readerContext
- * @param Context $generatorContext
- * @return $this
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- */
- public function process(Layout\Reader\Context $readerContext, Layout\Generator\Context $generatorContext)
- {
- $scheduledStructure = $readerContext->getScheduledStructure();
- $layout = $generatorContext->getLayout();
- $structure = $generatorContext->getStructure();
- /** @var $blocks \Magento\Framework\View\Element\AbstractBlock[] */
- $blocks = [];
- $blockActions = [];
- // Instantiate blocks and collect all actions data
- foreach ($scheduledStructure->getElements() as $elementName => $element) {
- list($type, $data) = $element;
- if ($type === self::TYPE) {
- try {
- $block = $this->generateBlock($scheduledStructure, $structure, $elementName);
- $blocks[$elementName] = $block;
- $layout->setBlock($elementName, $block);
- if (!empty($data['actions'])) {
- $blockActions[$elementName] = $data['actions'];
- }
- } catch (\Exception $e) {
- $this->handleRenderException($e);
- unset($blocks[$elementName]);
- }
- }
- }
- // Set layout instance to all generated block (trigger _prepareLayout method)
- foreach ($blocks as $elementName => $block) {
- try {
- $block->setLayout($layout);
- $this->eventManager->dispatch('core_layout_block_create_after', ['block' => $block]);
- } catch (\Exception $e) {
- $this->handleRenderException($e);
- $layout->setBlock(
- $elementName,
- $this->exceptionHandlerBlockFactory->create(['blockName' => $elementName])
- );
- unset($blockActions[$elementName]);
- }
- $scheduledStructure->unsetElement($elementName);
- }
- // Run all actions after layout initialization
- foreach ($blockActions as $elementName => $actions) {
- try {
- foreach ($actions as $action) {
- list($methodName, $actionArguments, $configPath, $scopeType) = $action;
- if (empty($configPath)
- || $this->scopeConfig->isSetFlag($configPath, $scopeType, $this->scopeResolver->getScope())
- ) {
- $this->generateAction($blocks[$elementName], $methodName, $actionArguments);
- }
- }
- } catch (\Exception $e) {
- $this->handleRenderException($e);
- $layout->setBlock(
- $elementName,
- $this->exceptionHandlerBlockFactory->create(['blockName' => $elementName])
- );
- }
- }
- return $this;
- }
- /**
- * Handle exceptions during rendering process
- *
- * @param \Exception $cause
- * @throws \Exception
- * @return void
- */
- protected function handleRenderException(\Exception $cause)
- {
- if ($this->appState->getMode() === State::MODE_DEVELOPER) {
- throw $cause;
- }
- $message = ($cause instanceof LocalizedException) ? $cause->getLogMessage() : $cause->getMessage();
- $this->logger->critical($message);
- }
- /**
- * Create block and set related data
- *
- * @param \Magento\Framework\View\Layout\ScheduledStructure $scheduledStructure
- * @param \Magento\Framework\View\Layout\Data\Structure $structure
- * @param string $elementName
- * @return \Magento\Framework\View\Element\AbstractBlock
- */
- protected function generateBlock(
- Layout\ScheduledStructure $scheduledStructure,
- Layout\Data\Structure $structure,
- $elementName
- ) {
- list(, $data) = $scheduledStructure->getElement($elementName);
- $attributes = $data['attributes'];
- if (!empty($attributes['group'])) {
- $structure->addToParentGroup($elementName, $attributes['group']);
- }
- if (!empty($attributes['display'])) {
- $structure->setAttribute($elementName, 'display', $attributes['display']);
- }
- // create block
- $className = isset($attributes['class']) && !empty($attributes['class']) ?
- $attributes['class'] : $this->defaultClass;
- $block = $this->createBlock($className, $elementName, [
- 'data' => $this->evaluateArguments($data['arguments'])
- ]);
- if (!empty($attributes['template'])) {
- $block->setTemplate($attributes['template']);
- }
- if (!empty($attributes['ttl'])) {
- $ttl = (int)$attributes['ttl'];
- $block->setTtl($ttl);
- }
- return $block;
- }
- /**
- * Create block instance
- *
- * @param string|\Magento\Framework\View\Element\AbstractBlock $block
- * @param string $name
- * @param array $arguments
- * @return \Magento\Framework\View\Element\AbstractBlock
- */
- public function createBlock($block, $name, array $arguments = [])
- {
- $block = $this->getBlockInstance($block, $arguments);
- $block->setType(get_class($block));
- $block->setNameInLayout($name);
- $block->addData(isset($arguments['data']) ? $arguments['data'] : []);
- return $block;
- }
- /**
- * Create block object instance based on block type
- *
- * @param string|\Magento\Framework\View\Element\AbstractBlock $block
- * @param array $arguments
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return \Magento\Framework\View\Element\AbstractBlock
- */
- protected function getBlockInstance($block, array $arguments = [])
- {
- $e = null;
- if ($block && is_string($block)) {
- try {
- $block = $this->blockFactory->createBlock($block, $arguments);
- } catch (\ReflectionException $e) {
- $this->logger->critical($e->getMessage());
- }
- }
- if (!$block instanceof \Magento\Framework\View\Element\AbstractBlock) {
- throw new LocalizedException(
- new \Magento\Framework\Phrase(
- 'Invalid block type: %1',
- [is_object($block) ? get_class($block) : (string) $block]
- ),
- $e
- );
- }
- return $block;
- }
- /**
- * Run action defined in layout update
- *
- * @param \Magento\Framework\View\Element\AbstractBlock $block
- * @param string $methodName
- * @param array $actionArguments
- * @return void
- */
- protected function generateAction($block, $methodName, $actionArguments)
- {
- $profilerKey = 'BLOCK_ACTION:' . $block->getNameInLayout() . '>' . $methodName;
- \Magento\Framework\Profiler::start($profilerKey);
- $args = $this->evaluateArguments($actionArguments);
- call_user_func_array([$block, $methodName], $args);
- \Magento\Framework\Profiler::stop($profilerKey);
- }
- /**
- * Compute and return argument values
- *
- * @param array $arguments
- * @return array
- */
- protected function evaluateArguments(array $arguments)
- {
- $result = [];
- foreach ($arguments as $argumentName => $argumentData) {
- if (!isset($argumentData[Dom::TYPE_ATTRIBUTE])) {
- $result[$argumentName] = $argumentData;
- continue;
- }
- $result[$argumentName] = $this->argumentInterpreter->evaluate($argumentData);
- }
- return $result;
- }
- }
|