<?php
/**
* Contao Bootstrap
*
* @package contao-bootstrap
* @subpackage Core
* @author David Molineus <david.molineus@netzmacht.de>
* @copyright 2017 netzmacht David Molineus. All rights reserved.
* @license LGPL-3.0 https://github.com/contao-bootstrap/core
* @filesource
*/
namespace ContaoBootstrap\Core;
use Contao\LayoutModel;
use ContaoBootstrap\Core\Config\ArrayConfig;
use ContaoBootstrap\Core\Environment\Context;
use ContaoBootstrap\Core\Exception\LeavingContextFailed;
use ContaoBootstrap\Core\Message\Event\ContextEntered;
use ContaoBootstrap\Core\Message\Command\BuildContextConfig;
use Symfony\Component\EventDispatcher\EventDispatcherInterface as MessageBus;
/**
* Class Environment contain all things being provided in the bootstrap environment.
*
* @package ContaoBootstrap\Core
*/
final class Environment
{
/**
* Bootstrap enabled state.
*
* @var bool
*/
protected $enabled = false;
/**
* Bootstrap config.
*
* @var Config
*/
protected $config;
/**
* Layout model of current page.
*
* @var \LayoutModel
*/
private $layout;
/**
* Current context.
*
* @var Context
*/
private $context;
/**
* List of contexts.
*
* @var Context[]
*/
private $contextStack;
/**
* MessageBus.
*
* @var MessageBus
*/
private $messageBus;
/**
* Construct.
*
* @param MessageBus $messageBus Message bus.
*/
public function __construct(MessageBus $messageBus)
{
$this->messageBus = $messageBus;
$this->config = new ArrayConfig();
}
/**
* Get bootstrap config.
*
* @return Config
*/
public function getConfig(): Config
{
return $this->config;
}
/**
* Enter a context.
*
* @param Context $context Context.
*
* @return void
*/
public function enterContext(Context $context): void
{
// Already in the context.
if ($this->context && $this->context->match($context)) {
return;
}
$this->switchContext($context, true);
}
/**
* Leave current context and enter the context which was used before.
*
* @param Context|null $currentContext Optional expected current context. Won't do anything if context not match.
*
* @return void
* @throws LeavingContextFailed When context stack is empty.
*/
public function leaveContext(?Context $currentContext = null): void
{
if (!$this->context) {
throw LeavingContextFailed::noContext();
}
// Not in expected context. Just quit.
if ($currentContext && !$currentContext->match($this->context)) {
return;
}
// Get last know context which was used before current context.
$context = array_pop($this->contextStack);
if ($context === null) {
throw LeavingContextFailed::inContext($this->context);
}
$this->switchContext($context);
}
/**
* Switch to another context.
*
* @param Context $context New context.
* @param bool $keepCurrentInStack If true current context is added to the stack.
*
* @return void
*/
private function switchContext(Context $context, bool $keepCurrentInStack = false): void
{
$command = new BuildContextConfig($this, $context, $this->config);
$this->messageBus->dispatch($command::NAME, $command);
if ($command->getConfig()) {
$this->config = $command->getConfig();
}
if ($keepCurrentInStack && $this->context) {
$this->contextStack[] = $this->context;
}
$this->context = $context;
$event = new ContextEntered($this, $context);
$this->messageBus->dispatch($event::NAME, $event);
}
/**
* Consider if bootstrap theme is enabled.
*
* @return bool
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Enable current bootstrap theme.
*
* @param bool $enabled Enabled state.
*
* @return $this
*/
public function setEnabled(bool $enabled): self
{
$this->enabled = $enabled;
return $this;
}
/**
* Set the layout.
*
* @param LayoutModel $layout Page layout.
*
* @return $this
*/
public function setLayout(LayoutModel $layout): self
{
$this->layout = $layout;
return $this;
}
/**
* Get the page layout.
*
* @return LayoutModel|null
*/
public function getLayout()
{
return $this->layout;
}
}