RuleExpression.php

Namespace

Drupal\rules\Plugin\RulesExpression

File

src/Plugin/RulesExpression/RuleExpression.php

View source
<?php

namespace Drupal\rules\Plugin\RulesExpression;

use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\rules\Context\ContextConfig;
use Drupal\rules\Context\ExecutionMetadataStateInterface;
use Drupal\rules\Context\ExecutionStateInterface;
use Drupal\rules\Engine\ActionExpressionContainerInterface;
use Drupal\rules\Engine\ActionExpressionInterface;
use Drupal\rules\Engine\ConditionExpressionContainerInterface;
use Drupal\rules\Engine\ConditionExpressionInterface;
use Drupal\rules\Engine\ExpressionBase;
use Drupal\rules\Engine\ExpressionInterface;
use Drupal\rules\Engine\ExpressionManagerInterface;
use Drupal\rules\Engine\RuleExpressionInterface;
use Drupal\rules\Exception\InvalidExpressionException;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a rule, executing actions when conditions are met.
 *
 * Actions added to a rule can also be rules themselves, so it is possible to
 * nest several rules into one rule. This is the functionality of so called
 * "rule sets" in Drupal 7.
 *
 * @RulesExpression(
 *   id = "rules_rule",
 *   label = @Translation("Rule"),
 *   form_class = "\Drupal\rules\Form\Expression\RuleExpressionForm"
 * )
 */
class RuleExpression extends ExpressionBase implements RuleExpressionInterface, ContainerFactoryPluginInterface {
    
    /**
     * The rules expression plugin manager.
     *
     * @var \Drupal\rules\Engine\ExpressionManagerInterface
     */
    protected $expressionManager;
    
    /**
     * List of conditions that must be met before actions are executed.
     *
     * @var \Drupal\rules\Engine\ConditionExpressionContainerInterface
     */
    protected $conditions;
    
    /**
     * List of actions that get executed if the conditions are met.
     *
     * @var \Drupal\rules\Engine\ActionExpressionContainerInterface
     */
    protected $actions;
    
    /**
     * The rules debug logger channel.
     *
     * @var \Drupal\Core\Logger\LoggerChannelInterface
     */
    protected $rulesDebugLogger;
    
    /**
     * Constructs a new class instance.
     *
     * @param array $configuration
     *   A configuration array containing information about the plugin instance.
     * @param string $plugin_id
     *   The plugin_id for the plugin instance.
     * @param array $plugin_definition
     *   The plugin implementation definition.
     * @param \Drupal\rules\Engine\ExpressionManagerInterface $expression_manager
     *   The rules expression plugin manager.
     * @param \Drupal\Core\Logger\LoggerChannelInterface $logger
     *   The Rules debug logger channel.
     */
    public function __construct(array $configuration, $plugin_id, array $plugin_definition, ExpressionManagerInterface $expression_manager, LoggerChannelInterface $logger) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);
        $configuration += [
            'conditions' => [],
            'actions' => [],
        ];
        // Per default the outer condition container of a rule is initialized as
        // conjunction (AND), meaning that all conditions in it must evaluate to
        // TRUE to fire the actions.
        $this->conditions = $expression_manager->createInstance('rules_and', $configuration['conditions']);
        $this->conditions
            ->setRoot($this->getRoot());
        $this->actions = $expression_manager->createInstance('rules_action_set', $configuration['actions']);
        $this->actions
            ->setRoot($this->getRoot());
        $this->expressionManager = $expression_manager;
        $this->rulesDebugLogger = $logger;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
        return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.rules_expression'), $container->get('logger.channel.rules_debug'));
    }
    
    /**
     * {@inheritdoc}
     */
    public function executeWithState(ExecutionStateInterface $state) {
        // Evaluate the rule's conditions.
        $this->rulesDebugLogger
            ->info('Evaluating conditions of rule %label.', [
            '%label' => $this->getLabel(),
            'element' => $this,
        ]);
        if (!$this->conditions
            ->isEmpty() && !$this->conditions
            ->executeWithState($state)) {
            // Do not run the actions if the conditions are not met.
            return;
        }
        $this->rulesDebugLogger
            ->info('Rule %label fires.', [
            '%label' => $this->getLabel(),
            'element' => $this,
            'scope' => TRUE,
        ]);
        $this->actions
            ->executeWithState($state);
        $this->rulesDebugLogger
            ->info('Rule %label has fired.', [
            '%label' => $this->getLabel(),
            'element' => $this,
            'scope' => FALSE,
        ]);
    }
    
    /**
     * {@inheritdoc}
     */
    public function addCondition($condition_id, ContextConfig $config = NULL) {
        return $this->conditions
            ->addCondition($condition_id, $config);
    }
    
    /**
     * {@inheritdoc}
     */
    public function getConditions() {
        return $this->conditions;
    }
    
    /**
     * {@inheritdoc}
     */
    public function setConditions(ConditionExpressionContainerInterface $conditions) {
        $this->conditions = $conditions;
        return $this;
    }
    
    /**
     * {@inheritdoc}
     */
    public function addAction($action_id, ContextConfig $config = NULL) {
        return $this->actions
            ->addAction($action_id, $config);
    }
    
    /**
     * {@inheritdoc}
     */
    public function getActions() {
        return $this->actions;
    }
    
    /**
     * {@inheritdoc}
     */
    public function setActions(ActionExpressionContainerInterface $actions) {
        $this->actions = $actions;
        return $this;
    }
    
    /**
     * {@inheritdoc}
     */
    public function addExpressionObject(ExpressionInterface $expression) {
        if ($expression instanceof ConditionExpressionInterface) {
            $this->conditions
                ->addExpressionObject($expression);
        }
        elseif ($expression instanceof ActionExpressionInterface) {
            $this->actions
                ->addExpressionObject($expression);
        }
        else {
            throw new InvalidExpressionException();
        }
        return $this;
    }
    
    /**
     * {@inheritdoc}
     */
    public function addExpression($plugin_id, ContextConfig $config = NULL) {
        return $this->addExpressionObject($this->expressionManager
            ->createInstance($plugin_id, $config ? $config->toArray() : []));
    }
    
    /**
     * {@inheritdoc}
     */
    public function getConfiguration() {
        $configuration = parent::getConfiguration();
        // We need to update the configuration in case actions/conditions have been
        // added or changed.
        $configuration['conditions'] = $this->conditions
            ->getConfiguration();
        $configuration['actions'] = $this->actions
            ->getConfiguration();
        return $configuration;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getIterator() : \Traversable {
        // Just pass up the actions for iterating over.
        return $this->actions
            ->getIterator();
    }
    
    /**
     * {@inheritdoc}
     */
    public function getExpression($uuid) {
        $condition = $this->conditions
            ->getExpression($uuid);
        if ($condition) {
            return $condition;
        }
        return $this->actions
            ->getExpression($uuid);
    }
    
    /**
     * {@inheritdoc}
     */
    public function deleteExpression($uuid) {
        $deleted = $this->conditions
            ->deleteExpression($uuid);
        if (!$deleted) {
            $deleted = $this->actions
                ->deleteExpression($uuid);
        }
        return $deleted;
    }
    
    /**
     * {@inheritdoc}
     */
    public function checkIntegrity(ExecutionMetadataStateInterface $metadata_state, $apply_assertions = TRUE) {
        $violation_list = $this->conditions
            ->checkIntegrity($metadata_state, $apply_assertions);
        $violation_list->addAll($this->actions
            ->checkIntegrity($metadata_state, $apply_assertions));
        return $violation_list;
    }
    
    /**
     * {@inheritdoc}
     */
    public function prepareExecutionMetadataState(ExecutionMetadataStateInterface $metadata_state, ExpressionInterface $until = NULL, $apply_assertions = TRUE) {
        // @todo If the rule is nested, we may not pass assertions to following
        // expressions as we do not know whether the rule fires at all. Should we
        // clone the metadata state to ensure modifications stay local?
        $found = $this->conditions
            ->prepareExecutionMetadataState($metadata_state, $until, $apply_assertions);
        if ($found) {
            return TRUE;
        }
        return $this->actions
            ->prepareExecutionMetadataState($metadata_state, $until, $apply_assertions);
    }
    
    /**
     * PHP magic __clone function.
     */
    public function __clone() {
        $this->actions = clone $this->actions;
        $this->actions
            ->setRoot($this->getRoot());
        $this->conditions = clone $this->conditions;
        $this->conditions
            ->setRoot($this->getRoot());
    }

}

Classes

Title Deprecated Summary
RuleExpression Provides a rule, executing actions when conditions are met.