You are here

abstract class RulesPlugin in Rules 7.2

Base class for rules plugins.

We cannot inherit from EntityDB at the same time, so we implement our own entity related methods. Any CRUD related actions performed on contained plugins are applied and the root element representing the configuration is saved.

Hierarchy

Expanded class hierarchy of RulesPlugin

1 string reference to 'RulesPlugin'
RulesContainerPlugin::sortChildren in includes/rules.core.inc
Sorts all child elements by their weight.

File

includes/rules.core.inc, line 438
Rules base classes and interfaces needed for any rule evaluation.

View source
abstract class RulesPlugin extends RulesExtendable {

  /**
   * If this is a configuration saved to the db, the id of it.
   */
  public $id = NULL;
  public $weight = 0;
  public $name = NULL;

  /**
   * An array of settings for this element.
   *
   * @var array
   */
  public $settings = array();

  /**
   * Info about this element. Usage depends on the plugin.
   *
   * @var array
   */
  protected $info = array();

  /**
   * The parent element, if any.
   *
   * @var RulesContainerPlugin
   */
  protected $parent = NULL;
  protected $cache = NULL;

  /**
   * @var array
   */
  protected $hook = 'plugin_info';

  /**
   * Identifies an element inside a configuration.
   */
  protected $elementId = NULL;

  /**
   * Static cache for availableVariables().
   */
  protected $availableVariables;

  /**
   * Sets a new parent element.
   */
  public function setParent(RulesContainerPlugin $parent) {
    if ($this->parent == $parent) {
      return;
    }
    if (isset($this->parent) && ($key = array_search($this, $this->parent->children)) !== FALSE) {

      // Remove element from any previous parent.
      unset($this->parent->children[$key]);
      $this->parent
        ->resetInternalCache();
    }

    // Make sure the interface matches the type of the container.
    if ($parent instanceof RulesActionContainer && $this instanceof RulesActionInterface || $parent instanceof RulesConditionContainer && $this instanceof RulesConditionInterface) {
      $this->parent = $parent;
      $parent->children[] = $this;
      $this->parent
        ->resetInternalCache();
    }
    else {
      throw new RulesEvaluationException('The given container is incompatible with this element.', array(), $this, RulesLog::ERROR);
    }
  }

  /**
   * Gets the root element of the configuration.
   */
  public function root() {
    $element = $this;
    while (!$element
      ->isRoot()) {
      $element = $element->parent;
    }
    return $element;
  }

  /**
   * Returns whether the element is the root of the configuration.
   */
  public function isRoot() {
    return empty($this->parent) || isset($this->name);
  }

  /**
   * Returns the element's parent.
   */
  public function parentElement() {
    return $this->parent;
  }

  /**
   * Returns the element id, which identifies the element inside the config.
   */
  public function elementId() {
    if (!isset($this->elementId)) {
      $this
        ->elementMap()
        ->index();
    }
    return $this->elementId;
  }

  /**
   * Gets the element map helper object, which helps mapping elements to ids.
   *
   * @return RulesElementMap
   */
  public function elementMap() {
    $config = $this
      ->root();
    if (empty($config->map)) {
      $config->map = new RulesElementMap($config);
    }
    return $config->map;
  }

  /**
   * Iterate over all elements nested below the current element.
   *
   * This helper can be used to recursively iterate over all elements of a
   * configuration. To iterate over the children only, just regularly iterate
   * over the object.
   *
   * @param int $mode
   *   (optional) The iteration mode used. See
   *   RecursiveIteratorIterator::construct(). Defaults to SELF_FIRST.
   *
   * @return RecursiveIteratorIterator
   */
  public function elements($mode = RecursiveIteratorIterator::SELF_FIRST) {
    return new RecursiveIteratorIterator($this, $mode);
  }

  /**
   * Do a deep clone.
   */
  public function __clone() {

    // Make sure the element map is cleared.
    // @see self::elementMap()
    unset($this->map);
  }

  /**
   * Returns the depth of this element in the configuration.
   */
  public function depth() {
    $element = $this;
    $i = 0;
    while (!empty($element->parent)) {
      $element = $element->parent;
      $i++;
    }
    return $i;
  }

  /**
   * Execute the configuration.
   *
   * @param ...
   *   Arguments to pass to the configuration.
   */
  public function execute() {
    return $this
      ->executeByArgs(func_get_args());
  }

  /**
   * Execute the configuration by passing arguments in a single array.
   */
  public abstract function executeByArgs($args = array());

  /**
   * Evaluate the element on a given rules evaluation state.
   */
  public abstract function evaluate(RulesState $state);
  protected static function compare(RulesPlugin $a, RulesPlugin $b) {
    if ($a->weight == $b->weight) {
      return 0;
    }
    return $a->weight < $b->weight ? -1 : 1;
  }

  /**
   * Returns info about parameters needed by the plugin.
   *
   * Note that not necessarily all parameters are needed when executing the
   * plugin, as values for the parameter might have been already configured via
   * the element settings.
   *
   * @see self::parameterInfo()
   */
  public function pluginParameterInfo() {
    return isset($this->info['parameter']) ? $this->info['parameter'] : array();
  }

  /**
   * Returns info about parameters needed for executing the configured plugin.
   *
   * @param bool $optional
   *   Whether optional parameters should be included.
   *
   * @see self::pluginParameterInfo()
   */
  public function parameterInfo($optional = FALSE) {

    // We have to filter out parameters that are already configured.
    foreach ($this
      ->pluginParameterInfo() as $name => $info) {
      if (!isset($this->settings[$name . ':select']) && !isset($this->settings[$name]) && ($optional || empty($info['optional']) && $info['type'] != 'hidden')) {
        $vars[$name] = $info;
      }
    }
    return isset($vars) ? $vars : array();
  }

  /**
   * Returns info about variables 'provided' by the plugin.
   *
   * Note that this method returns info about the provided variables as defined
   * by the plugin. Thus this resembles the original info, which may be
   * adapted via configuration.
   *
   * @see self::providesVariables()
   */
  public function pluginProvidesVariables() {
    return isset($this->info['provides']) ? $this->info['provides'] : array();
  }

  /**
   * Returns info about all variables provided for later evaluated elements.
   *
   * @see self::pluginProvidesVariables()
   */
  public function providesVariables() {
    foreach ($this
      ->pluginProvidesVariables() as $name => $info) {
      $info['source name'] = $name;
      $info['label'] = isset($this->settings[$name . ':label']) ? $this->settings[$name . ':label'] : $info['label'];
      if (isset($this->settings[$name . ':var'])) {
        $name = $this->settings[$name . ':var'];
      }
      $provides[$name] = $info;
    }
    return isset($provides) ? $provides : array();
  }

  /**
   * Returns the info of the plugin.
   */
  public function info() {
    return $this->info;
  }

  /**
   * When converted to a string, just use the export format.
   */
  public function __toString() {
    return $this
      ->isRoot() ? $this
      ->export() : entity_var_json_export($this
      ->export());
  }

  /**
   * Gets variables to return once the configuration has been executed.
   */
  protected function returnVariables(RulesState $state, $result = NULL) {
    $var_info = $this
      ->providesVariables();
    foreach ($var_info as $name => $info) {
      try {
        $vars[$name] = $this
          ->getArgument($name, $info, $state);
      } catch (RulesEvaluationException $e) {

        // Ignore not existing variables.
        $vars[$name] = NULL;
      }
      $var_info[$name] += array(
        'allow null' => TRUE,
      );
    }
    return isset($vars) ? array_values(rules_unwrap_data($vars, $var_info)) : array();
  }

  /**
   * Sets up the execution state for the given arguments.
   */
  public function setUpState(array $args) {
    $state = new RulesState();
    $vars = $this
      ->setUpVariables();

    // Fix numerically indexed args to start with 0.
    if (!isset($args[rules_array_key($vars)])) {
      $args = array_values($args);
    }
    $offset = 0;
    foreach (array_keys($vars) as $i => $name) {
      $info = $vars[$name];
      if (!empty($info['handler']) || isset($info['parameter']) && $info['parameter'] === FALSE) {
        $state
          ->addVariable($name, NULL, $info);

        // Count the variables that are not passed as parameters.
        $offset++;
      }
      elseif (isset($args[$i - $offset])) {
        $state
          ->addVariable($name, $args[$i - $offset], $info);
      }
      elseif (isset($args[$name])) {
        $state
          ->addVariable($name, $args[$name], $info);
      }
      elseif (empty($info['optional']) && $info['type'] != 'hidden') {
        throw new RulesEvaluationException('Argument %name is missing.', array(
          '%name' => $name,
        ), $this, RulesLog::ERROR);
      }
    }
    return $state;
  }

  /**
   * Returns info about all variables that have to be setup in the state.
   */
  protected function setUpVariables() {
    return $this
      ->parameterInfo(TRUE);
  }

  /**
   * Returns info about variables available to be used as arguments for this element.
   *
   * As this is called very often, e.g. during integrity checks, we statically
   * cache the results.
   *
   * @see RulesPlugin::resetInternalCache()
   */
  public function availableVariables() {
    if (!isset($this->availableVariables)) {
      $this->availableVariables = !$this
        ->isRoot() ? $this->parent
        ->stateVariables($this) : RulesState::defaultVariables();
    }
    return $this->availableVariables;
  }

  /**
   * Returns asserted additions to the available variable info.
   *
   * Any returned info is merged into the variable info, in case the execution
   * flow passes the element.
   * E.g. this is used to assert the content type of a node if the condition
   * is met, such that the per-node type properties are available.
   */
  protected function variableInfoAssertions() {
    return array();
  }

  /**
   * Gets the name of this plugin instance.
   *
   * The returned name should identify the code which drives this plugin.
   */
  public function getPluginName() {
    return $this->itemName;
  }

  /**
   * Calculates an array of required modules.
   *
   * You can use $this->dependencies to access dependencies for saved
   * configurations.
   */
  public function dependencies() {
    $this
      ->processSettings();
    $modules = isset($this->itemInfo['module']) && $this->itemInfo['module'] != 'rules' ? array(
      $this->itemInfo['module'] => 1,
    ) : array();
    foreach ($this
      ->pluginParameterInfo() as $name => $info) {
      if (isset($this->settings[$name . ':process']) && $this->settings[$name . ':process'] instanceof RulesDataProcessor) {
        $modules += array_flip($this->settings[$name . ':process']
          ->dependencies());
      }
    }
    return array_keys($modules);
  }

  /**
   * Whether the currently logged in user has access to all configured elements.
   *
   * Note that this only checks whether the current user has permission to all
   * configured elements, but not whether a user has access to configure Rule
   * configurations in general. Use rules_config_access() for that.
   *
   * Use this to determine access permissions for configuring or triggering the
   * execution of certain configurations independent of the Rules UI.
   *
   * @see rules_config_access()
   */
  public function access() {
    $this
      ->processSettings();
    foreach ($this
      ->pluginParameterInfo() as $name => $info) {
      if (isset($this->settings[$name . ':select']) && ($wrapper = $this
        ->applyDataSelector($this->settings[$name . ':select']))) {
        if ($wrapper
          ->access('view') === FALSE) {
          return FALSE;
        }
      }

      // Incorporate access checks for data processors and input evaluators.
      if (isset($this->settings[$name . ':process']) && $this->settings[$name . ':process'] instanceof RulesDataProcessor && !$this->settings[$name . ':process']
        ->editAccess()) {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * Processes the settings e.g. to prepare input evaluators.
   *
   * Usually settings get processed automatically, however if $this->settings
   * has been altered manually after element construction, it needs to be
   * invoked explicitly with $force set to TRUE.
   */
  public function processSettings($force = FALSE) {

    // Process if not done yet.
    if ($force || !empty($this->settings['#_needs_processing'])) {
      $var_info = $this
        ->availableVariables();
      foreach ($this
        ->pluginParameterInfo() as $name => $info) {

        // Prepare input evaluators.
        if (isset($this->settings[$name])) {
          $this->settings[$name . ':process'] = $this->settings[$name];
          RulesDataInputEvaluator::prepareSetting($this->settings[$name . ':process'], $info, $var_info);
        }
        elseif (isset($this->settings[$name . ':select']) && !empty($this->settings[$name . ':process'])) {
          RulesDataProcessor::prepareSetting($this->settings[$name . ':process'], $info, $var_info);
        }

        // Clean up.
        if (empty($this->settings[$name . ':process'])) {
          unset($this->settings[$name . ':process']);
        }
      }
      unset($this->settings['#_needs_processing']);
    }
  }

  /**
   * Makes sure the plugin is configured right.
   *
   * "Configured right" means all needed variables are available in the
   * element's scope and dependent modules are enabled.
   *
   * @return $this
   *
   * @throws RulesIntegrityException
   *   In case of a failed integrity check, a RulesIntegrityException exception
   *   is thrown.
   */
  public function integrityCheck() {

    // First process the settings if not done yet.
    $this
      ->processSettings();

    // Check dependencies using the pre-calculated dependencies stored in
    // $this->dependencies. Fail back to calculation them on the fly, e.g.
    // during creation.
    $dependencies = empty($this->dependencies) ? $this
      ->dependencies() : $this->dependencies;
    foreach ($dependencies as $module) {
      if (!module_exists($module)) {
        throw new RulesDependencyException(t('Missing required module %name.', array(
          '%name' => $module,
        )));
      }
    }

    // Check the parameter settings.
    $this
      ->checkParameterSettings();

    // Check variable names for provided variables to be valid.
    foreach ($this
      ->pluginProvidesVariables() as $name => $info) {
      if (isset($this->settings[$name . ':var'])) {
        $this
          ->checkVarName($this->settings[$name . ':var']);
      }
    }
    return $this;
  }
  protected function checkVarName($name) {
    if (!preg_match('/^[0-9a-zA-Z_]*$/', $name)) {
      throw new RulesIntegrityException(t('%plugin: The variable name %name contains not allowed characters.', array(
        '%plugin' => $this
          ->getPluginName(),
        '%name' => $name,
      )), $this);
    }
  }

  /**
   * Checks whether parameters are correctly configured.
   */
  protected function checkParameterSettings() {
    foreach ($this
      ->pluginParameterInfo() as $name => $info) {
      if (isset($info['restriction']) && $info['restriction'] == 'selector' && isset($this->settings[$name])) {
        throw new RulesIntegrityException(t("The parameter %name may only be configured using a selector.", array(
          '%name' => $name,
        )), array(
          $this,
          'parameter',
          $name,
        ));
      }
      elseif (isset($info['restriction']) && $info['restriction'] == 'input' && isset($this->settings[$name . ':select'])) {
        throw new RulesIntegrityException(t("The parameter %name may not be configured using a selector.", array(
          '%name' => $name,
        )), array(
          $this,
          'parameter',
          $name,
        ));
      }
      elseif (!empty($this->settings[$name . ':select']) && !$this
        ->applyDataSelector($this->settings[$name . ':select'])) {
        throw new RulesIntegrityException(t("Data selector %selector for parameter %name is invalid.", array(
          '%selector' => $this->settings[$name . ':select'],
          '%name' => $name,
        )), array(
          $this,
          'parameter',
          $name,
        ));
      }
      elseif ($arg_info = $this
        ->getArgumentInfo($name)) {

        // If we have enough metadata, check whether the types match.
        if (!RulesData::typesMatch($arg_info, $info)) {
          throw new RulesIntegrityException(t("The data type of the configured argument does not match the parameter's %name requirement.", array(
            '%name' => $name,
          )), array(
            $this,
            'parameter',
            $name,
          ));
        }
      }
      elseif (!$this
        ->isRoot() && !isset($this->settings[$name]) && empty($info['optional']) && $info['type'] != 'hidden') {
        throw new RulesIntegrityException(t('Missing configuration for parameter %name.', array(
          '%name' => $name,
        )), array(
          $this,
          'parameter',
          $name,
        ));
      }

      // @todo Make sure used values are allowed.
      // (key/value pairs + allowed values).
    }
  }

  /**
   * Returns the argument for the parameter $name described with $info.
   *
   * Returns the argument as configured in the element settings for the
   * parameter $name described with $info.
   *
   * @param string $name
   *   The name of the parameter for which to get the argument.
   * @param $info
   *   Info about the parameter.
   * @param RulesState $state
   *   The current evaluation state.
   * @param string $langcode
   *   (optional) The language code used to get the argument value if the
   *   argument value should be translated. By default (NULL) the current
   *   interface language will be used.
   *
   * @return
   *   The argument, possibly wrapped.
   *
   * @throws RulesEvaluationException
   *   In case the argument cannot be retrieved an exception is thrown.
   */
  protected function getArgument($name, $info, RulesState $state, $langcode = NULL) {

    // Only apply the langcode if the parameter has been marked translatable.
    if (empty($info['translatable'])) {
      $langcode = LANGUAGE_NONE;
    }
    elseif (!isset($langcode)) {
      $langcode = $GLOBALS['language']->language;
    }
    if (!empty($this->settings[$name . ':select'])) {
      $arg = $state
        ->applyDataSelector($this->settings[$name . ':select'], $langcode);
    }
    elseif (isset($this->settings[$name])) {
      $arg = rules_wrap_data($this->settings[$name], $info);

      // We don't sanitize directly specified values.
      $skip_sanitize = TRUE;
    }
    elseif ($state
      ->varinfo($name)) {
      $arg = $state
        ->get($name);
    }
    elseif (empty($info['optional']) && $info['type'] != 'hidden') {
      throw new RulesEvaluationException('Required parameter %name is missing.', array(
        '%name' => $name,
      ), $this, RulesLog::ERROR);
    }
    else {
      $arg = isset($info['default value']) ? $info['default value'] : NULL;
      $skip_sanitize = TRUE;
      $info['allow null'] = TRUE;
    }

    // Make sure the given value is set if required (default).
    if (!isset($arg) && empty($info['allow null'])) {
      throw new RulesEvaluationException('The provided argument for parameter %name is empty.', array(
        '%name' => $name,
      ), $this);
    }

    // Support passing already sanitized values.
    if ($info['type'] == 'text' && !isset($skip_sanitize) && !empty($info['sanitize']) && !$arg instanceof EntityMetadataWrapper) {
      $arg = check_plain((string) $arg);
    }

    // Apply any configured data processors.
    if (!empty($this->settings[$name . ':process'])) {

      // For processing, make sure the data is unwrapped now.
      $return = rules_unwrap_data(array(
        $arg,
      ), array(
        $info,
      ));

      // @todo For Drupal 8: Refactor to add the name and language code as
      // separate parameter to process().
      $info['#name'] = $name;
      $info['#langcode'] = $langcode;
      return isset($return[0]) ? $this->settings[$name . ':process']
        ->process($return[0], $info, $state, $this) : NULL;
    }
    return $arg;
  }

  /**
   * Gets the right arguments for executing the element.
   *
   * @throws RulesEvaluationException
   *   If case an argument cannot be retrieved an exception is thrown.
   */
  protected function getExecutionArguments(RulesState $state) {
    $parameters = $this
      ->pluginParameterInfo();

    // If there is language parameter, get its value first so it can be used
    // for getting other translatable values.
    $langcode = NULL;
    if (isset($parameters['language'])) {
      $lang_arg = $this
        ->getArgument('language', $parameters['language'], $state);
      $langcode = $lang_arg instanceof EntityMetadataWrapper ? $lang_arg
        ->value() : $lang_arg;
    }

    // Now get all arguments.
    foreach ($parameters as $name => $info) {
      $args[$name] = $name == 'language' ? $lang_arg : $this
        ->getArgument($name, $info, $state, $langcode);
    }

    // Append the settings and the execution state. Faces will append $this.
    $args['settings'] = $this->settings;
    $args['state'] = $state;

    // Make the wrapped variables for the arguments available in the state.
    $state->currentArguments = $args;
    return rules_unwrap_data($args, $parameters);
  }

  /**
   * Applies the given data selector.
   *
   * Applies the given data selector by using the info about available
   * variables. Thus it doesn't require an actual evaluation state.
   *
   * @param string $selector
   *   The selector string, e.g. "node:author:mail".
   *
   * @return EntityMetadataWrapper
   *   An empty wrapper for the given selector or FALSE if the selector couldn't
   *   be applied.
   */
  public function applyDataSelector($selector) {
    $parts = explode(':', str_replace('-', '_', $selector), 2);
    if (($vars = $this
      ->availableVariables()) && isset($vars[$parts[0]]['type'])) {
      $wrapper = rules_wrap_data(NULL, $vars[$parts[0]], TRUE);
      if (count($parts) > 1 && $wrapper instanceof EntityMetadataWrapper) {
        try {
          foreach (explode(':', $parts[1]) as $name) {
            if ($wrapper instanceof EntityListWrapper || $wrapper instanceof EntityStructureWrapper) {
              $wrapper = $wrapper
                ->get($name);
            }
            else {
              return FALSE;
            }
          }
        } catch (EntityMetadataWrapperException $e) {
          return FALSE;
        }
      }
    }
    return isset($wrapper) ? $wrapper : FALSE;
  }

  /**
   * Returns info about the configured argument.
   *
   * @return
   *   The determined info. If it's not known NULL is returned.
   */
  public function getArgumentInfo($name) {
    $vars = $this
      ->availableVariables();
    if (!empty($this->settings[$name . ':select']) && !empty($vars[$this->settings[$name . ':select']])) {
      return $vars[$this->settings[$name . ':select']];
    }
    elseif (!empty($this->settings[$name . ':select'])) {
      if ($wrapper = $this
        ->applyDataSelector($this->settings[$name . ':select'])) {
        return $wrapper
          ->info();
      }
      return;
    }
    elseif (isset($this->settings[$name . ':type'])) {
      return array(
        'type' => $this->settings[$name . ':type'],
      );
    }
    elseif (!isset($this->settings[$name]) && isset($vars[$name])) {
      return $vars[$name];
    }
  }

  /**
   * Saves the configuration to the database.
   *
   * The configuration is saved regardless whether this method is invoked on
   * the rules configuration or a contained rule element.
   */
  public function save($name = NULL, $module = 'rules') {
    if (isset($this->parent)) {
      $this->parent
        ->sortChildren();
      return $this->parent
        ->save($name, $module);
    }
    else {

      // Update the dirty flag before saving.
      // However, this operation depends on a fully built Rules-cache, so skip
      // it when entities in code are imported to the database.
      // @see _rules_rebuild_cache()
      if (empty($this->is_rebuild)) {
        rules_config_update_dirty_flag($this, FALSE);

        // In case the config is not dirty, pre-calculate the dependencies for
        // later checking. Note that this also triggers processing settings if
        // necessary.
        // @see rules_modules_enabled()
        if (empty($this->dirty)) {
          $this->dependencies = $this
            ->dependencies();
        }
      }
      $this->plugin = $this->itemName;
      $this->name = isset($name) ? $name : $this->name;

      // Module stores the module via which the rule is configured and is used
      // for generating machine names with the right prefix. However, for
      // default configurations 'module' points to the module providing the
      // default configuration, so the module via which the rules is configured
      // is stored in the "owner" property.
      // @todo For Drupal 8 use "owner" for generating machine names also and
      // module only for the modules providing default configurations.
      $this->module = !isset($this->module) || $module != 'rules' ? $module : $this->module;
      if (!isset($this->owner)) {
        $this->owner = 'rules';
      }
      $this
        ->ensureNameExists();
      $this->data = $this;
      $return = entity_get_controller('rules_config')
        ->save($this);
      unset($this->data);

      // Care about clearing necessary caches.
      if (!empty($this->is_rebuild)) {
        rules_clear_cache();
      }
      else {
        $plugin_info = $this
          ->pluginInfo();
        if (!empty($plugin_info['component'])) {

          // When component variables changes rebuild the complete cache so the
          // changes to the provided action/condition take affect.
          if (empty($this->original) || $this
            ->componentVariables() != $this->original
            ->componentVariables()) {
            rules_clear_cache();
          }

          // Clear components cached for evaluation.
          cache_clear_all('comp_', 'cache_rules', TRUE);
        }
        elseif ($this->plugin == 'reaction rule') {

          // Clear event sets cached for evaluation.
          cache_clear_all('event_', 'cache_rules', TRUE);

          // Clear event whitelist for rebuild.
          cache_clear_all('rules_event_whitelist', 'cache_rules', TRUE);
        }
        drupal_static_reset('rules_get_cache');
        drupal_static_reset('rules_config_update_dirty_flag');
      }
      return $return;
    }
  }

  /**
   * Ensure the configuration has a name. If not, generate one.
   */
  protected function ensureNameExists() {
    if (!isset($this->module)) {
      $this->module = 'rules';
    }
    if (!isset($this->name)) {

      // Find a unique name for this configuration.
      $this->name = $this->module . '_';
      for ($i = 0; $i < 8; $i++) {

        // Alphanumeric name generation.
        $rnd = mt_rand(97, 122);
        $this->name .= chr($rnd);
      }
    }
  }
  public function __sleep() {

    // Keep the id always as we need it for the recursion prevention.
    $array = drupal_map_assoc(array(
      'parent',
      'id',
      'elementId',
      'weight',
      'settings',
    ));

    // Keep properties related to configurations if they are there.
    $info = entity_get_info('rules_config');
    $fields = array_merge($info['schema_fields_sql']['base table'], array(
      'recursion',
      'tags',
    ));
    foreach ($fields as $key) {
      if (isset($this->{$key})) {
        $array[$key] = $key;
      }
    }
    return $array;
  }

  /**
   * Optimizes a rule configuration in order to speed up evaluation.
   *
   * Additional optimization methods may be inserted by an extender
   * implementing the RulesOptimizationInterface. By default, there is no
   * optimization extender.
   *
   * An optimization method may rearrange the internal structure of a
   * configuration in order to speed up the evaluation. As the configuration may
   * change optimized configurations should not be saved permanently, except
   * when saving it temporary, for later execution only.
   *
   * @see RulesOptimizationInterface
   */
  public function optimize() {

    // Make sure settings are processed before configs are cached.
    $this
      ->processSettings();
    if ($this
      ->facesAs('RulesOptimizationInterface')) {
      $this
        ->__call('optimize');
    }
  }

  /**
   * Deletes configuration from database.
   *
   * If invoked on a rules configuration it is deleted from database. If
   * invoked on a contained rule element, it's removed from the configuration.
   */
  public function delete() {
    if (isset($this->parent)) {
      foreach ($this->parent->children as $key => $child) {
        if ($child === $this) {
          unset($this->parent->children[$key]);
          break;
        }
      }
    }
    elseif (isset($this->id)) {
      entity_get_controller('rules_config')
        ->delete(array(
        $this->name,
      ));
      rules_clear_cache();
    }
  }
  public function internalIdentifier() {
    return isset($this->id) ? $this->id : NULL;
  }

  /**
   * Returns the config name.
   */
  public function identifier() {
    return isset($this->name) ? $this->name : NULL;
  }
  public function entityInfo() {
    return entity_get_info('rules_config');
  }
  public function entityType() {
    return 'rules_config';
  }

  /**
   * Checks if the configuration has a certain exportable status.
   *
   * @param $status
   *   A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE,
   *   ENTITY_OVERRIDDEN or ENTITY_FIXED.
   *
   * @return bool
   *   TRUE if the configuration has the status, else FALSE.
   *
   * @see entity_has_status()
   */
  public function hasStatus($status) {
    return $this
      ->isRoot() && isset($this->status) && ($this->status & $status) == $status;
  }

  /**
   * Removes circular object references so PHP garbage collector can work.
   */
  public function destroy() {
    parent::destroy();
    $this->parent = NULL;
  }

  /**
   * Seamlessly invokes the method implemented via faces.
   *
   * Frees the caller from having to think about references.
   */
  public function form(&$form, &$form_state, array $options = array()) {
    $this
      ->__call('form', array(
      &$form,
      &$form_state,
      $options,
    ));
  }
  public function form_validate($form, &$form_state) {
    $this
      ->__call('form_validate', array(
      $form,
      &$form_state,
    ));
  }
  public function form_submit($form, &$form_state) {
    $this
      ->__call('form_submit', array(
      $form,
      &$form_state,
    ));
  }

  /**
   * Returns the label of the element.
   */
  public function label() {
    if (!empty($this->label) && $this->label != t('unlabeled')) {
      return $this->label;
    }
    $info = $this
      ->info();
    return isset($info['label']) ? $info['label'] : (!empty($this->name) ? $this->name : t('unlabeled'));
  }

  /**
   * Returns the name of the element's plugin.
   */
  public function plugin() {
    return $this->itemName;
  }

  /**
   * Returns info about the element's plugin.
   */
  public function pluginInfo() {
    $this
      ->forceSetUp();
    return $this->itemInfo;
  }

  /**
   * Applies the given export.
   */
  public function import(array $export) {
    $this
      ->importSettings($export[strtoupper($this
      ->plugin())]);
  }
  protected function importSettings($export) {

    // Import parameter settings.
    $export += array(
      'USING' => array(),
      'PROVIDE' => array(),
    );
    foreach ($export['USING'] as $name => $param_export) {
      $this
        ->importParameterSetting($name, $param_export);
    }
    foreach ($export['PROVIDE'] as $name => $var_export) {

      // The key of $var_export is the variable name, the value the label.
      $this->settings[$name . ':var'] = rules_array_key($var_export);
      $this->settings[$name . ':label'] = reset($var_export);
    }
  }
  protected function importParameterSetting($name, $export) {
    if (is_array($export) && isset($export['select'])) {
      $this->settings[$name . ':select'] = $export['select'];
      if (count($export) > 1) {

        // Add in processor settings.
        unset($export['select']);
        $this->settings[$name . ':process'] = $export;
      }
    }
    elseif (is_array($export) && count($export) == 1 && isset($export[0])) {
      $this->settings[$name . ':select'] = $export[0];
    }
    elseif (is_array($export) && isset($export['value'])) {
      $this->settings[$name] = $export['value'];
    }
    else {
      $this->settings[$name] = $export;
    }
  }

  /**
   * Exports a rule configuration.
   *
   * @param string $prefix
   *   An optional prefix for each line.
   * @param bool $php
   *   (optional) Set to TRUE to format the export using PHP arrays. By default
   *   JSON is used.
   *
   * @return
   *   The exported configuration.
   *
   * @see rules_import()
   */
  public function export($prefix = '', $php = FALSE) {
    $export = $this
      ->exportToArray();
    return $this
      ->isRoot() ? $this
      ->returnExport($export, $prefix, $php) : $export;
  }
  protected function exportToArray() {
    $export[strtoupper($this
      ->plugin())] = $this
      ->exportSettings();
    return $export;
  }
  protected function exportSettings() {
    $export = array();
    if (!$this
      ->isRoot()) {
      foreach ($this
        ->pluginParameterInfo() as $name => $info) {
        if (($return = $this
          ->exportParameterSetting($name, $info)) !== NULL) {
          $export['USING'][$name] = $return;
        }
      }
      foreach ($this
        ->providesVariables() as $name => $info) {
        if (!empty($info['source name'])) {
          $export['PROVIDE'][$info['source name']][$name] = $info['label'];
        }
      }
    }
    return $export;
  }
  protected function exportParameterSetting($name, $info) {
    if (isset($this->settings[$name]) && (empty($info['optional']) || !isset($info['default value']) || $this->settings[$name] != $info['default value'])) {

      // In case of an array-value wrap the value into another array, such that
      // the value cannot be confused with an exported data selector.
      return is_array($this->settings[$name]) ? array(
        'value' => $this->settings[$name],
      ) : $this->settings[$name];
    }
    elseif (isset($this->settings[$name . ':select'])) {
      if (isset($this->settings[$name . ':process']) && ($processor = $this->settings[$name . ':process'])) {
        $export['select'] = $this->settings[$name . ':select'];
        $export += $processor instanceof RulesDataProcessor ? $processor
          ->getChainSettings() : $processor;
        return $export;
      }

      // If there is no processor use a simple array to abbreviate this usual
      // case. In JSON this turns to a nice [selector] string.
      return array(
        $this->settings[$name . ':select'],
      );
    }
  }

  /**
   * Finalizes the configuration export.
   *
   * Adds general attributes regarding the configuration and returns it in the
   * right format for export.
   *
   * @param $export
   * @param string $prefix
   *   An optional prefix for each line.
   * @param bool $php
   *   (optional) Set to TRUE to format the export using PHP arrays. By default
   *   JSON is used.
   */
  protected function returnExport($export, $prefix = '', $php = FALSE) {
    $this
      ->ensureNameExists();
    if (!empty($this->label) && $this->label != t('unlabeled')) {
      $export_cfg[$this->name]['LABEL'] = $this->label;
    }
    $export_cfg[$this->name]['PLUGIN'] = $this
      ->plugin();
    if (!empty($this->weight)) {
      $export_cfg[$this->name]['WEIGHT'] = $this->weight;
    }
    if (isset($this->active) && !$this->active) {
      $export_cfg[$this->name]['ACTIVE'] = FALSE;
    }
    if (!empty($this->owner)) {
      $export_cfg[$this->name]['OWNER'] = $this->owner;
    }
    if (!empty($this->tags)) {
      $export_cfg[$this->name]['TAGS'] = $this->tags;
    }
    if ($modules = $this
      ->dependencies()) {
      $export_cfg[$this->name]['REQUIRES'] = $modules;
    }
    if (!empty($this->access_exposed)) {
      $export_cfg[$this->name]['ACCESS_EXPOSED'] = $this->access_exposed;
    }
    $export_cfg[$this->name] += $export;
    return $php ? entity_var_export($export_cfg, $prefix) : entity_var_json_export($export_cfg, $prefix);
  }

  /**
   * Resets any internal static caches.
   *
   * This function does not reset regular caches as retrieved via
   * rules_get_cache(). Usually, it's invoked automatically when a Rules
   * configuration is modified.
   *
   * Static caches are reset for the element and any elements down the tree. To
   * clear static caches of the whole configuration, invoke the function at the
   * root.
   *
   * @see RulesPlugin::availableVariables()
   */
  public function resetInternalCache() {
    $this->availableVariables = NULL;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
RulesExtendable::$itemInfo protected property
RulesExtendable::$itemName protected property The name of the item this class represents in the info hook. 9
RulesExtendable::facesAs public function
RulesExtendable::forceSetUp public function Forces the object to be setUp, this executes setUp() if not done yet. 1
RulesExtendable::itemFacesAs public static function Returns whether the a RuleExtendable supports the given interface.
RulesExtendable::rebuildCache public function Allows items to add something to the rules cache. 1
RulesExtendable::setUp protected function 1
RulesExtendable::__call public function Magic method: Invoke the dynamically implemented methods.
RulesExtendable::__construct public function 2
RulesPlugin::$availableVariables protected property Static cache for availableVariables(). 1
RulesPlugin::$cache protected property Overrides RulesExtendable::$cache
RulesPlugin::$elementId protected property Identifies an element inside a configuration.
RulesPlugin::$hook protected property Overrides RulesExtendable::$hook
RulesPlugin::$id public property If this is a configuration saved to the db, the id of it.
RulesPlugin::$info protected property Info about this element. Usage depends on the plugin. 2
RulesPlugin::$name public property
RulesPlugin::$parent protected property The parent element, if any.
RulesPlugin::$settings public property An array of settings for this element.
RulesPlugin::$weight public property
RulesPlugin::access public function Whether the currently logged in user has access to all configured elements. 2
RulesPlugin::applyDataSelector public function Applies the given data selector.
RulesPlugin::availableVariables public function Returns info about variables available to be used as arguments for this element. 1
RulesPlugin::checkParameterSettings protected function Checks whether parameters are correctly configured.
RulesPlugin::checkVarName protected function
RulesPlugin::compare protected static function
RulesPlugin::delete public function Deletes configuration from database. 1
RulesPlugin::dependencies public function Calculates an array of required modules. 2
RulesPlugin::depth public function Returns the depth of this element in the configuration.
RulesPlugin::destroy public function Removes circular object references so PHP garbage collector can work. 1
RulesPlugin::elementId public function Returns the element id, which identifies the element inside the config.
RulesPlugin::elementMap public function Gets the element map helper object, which helps mapping elements to ids.
RulesPlugin::elements public function Iterate over all elements nested below the current element.
RulesPlugin::ensureNameExists protected function Ensure the configuration has a name. If not, generate one.
RulesPlugin::entityInfo public function
RulesPlugin::entityType public function
RulesPlugin::evaluate abstract public function Evaluate the element on a given rules evaluation state. 5
RulesPlugin::execute public function Execute the configuration.
RulesPlugin::executeByArgs abstract public function Execute the configuration by passing arguments in a single array. 2
RulesPlugin::export public function Exports a rule configuration.
RulesPlugin::exportParameterSetting protected function
RulesPlugin::exportSettings protected function 1
RulesPlugin::exportToArray protected function 2
RulesPlugin::form public function Seamlessly invokes the method implemented via faces.
RulesPlugin::form_submit public function
RulesPlugin::form_validate public function
RulesPlugin::getArgument protected function Returns the argument for the parameter $name described with $info.
RulesPlugin::getArgumentInfo public function Returns info about the configured argument.
RulesPlugin::getExecutionArguments protected function Gets the right arguments for executing the element.
RulesPlugin::getPluginName public function Gets the name of this plugin instance. 1
RulesPlugin::hasStatus public function Checks if the configuration has a certain exportable status.
RulesPlugin::identifier public function Returns the config name.
RulesPlugin::import public function Applies the given export. 2
RulesPlugin::importParameterSetting protected function
RulesPlugin::importSettings protected function 1
RulesPlugin::info public function Returns the info of the plugin. 2
RulesPlugin::integrityCheck public function Makes sure the plugin is configured right. 2
RulesPlugin::internalIdentifier public function
RulesPlugin::isRoot public function Returns whether the element is the root of the configuration.
RulesPlugin::label public function Returns the label of the element. 4
RulesPlugin::optimize public function Optimizes a rule configuration in order to speed up evaluation. 1
RulesPlugin::parameterInfo public function Returns info about parameters needed for executing the configured plugin. 1
RulesPlugin::parentElement public function Returns the element's parent.
RulesPlugin::plugin public function Returns the name of the element's plugin.
RulesPlugin::pluginInfo public function Returns info about the element's plugin.
RulesPlugin::pluginParameterInfo public function Returns info about parameters needed by the plugin. 2
RulesPlugin::pluginProvidesVariables public function Returns info about variables 'provided' by the plugin. 2
RulesPlugin::processSettings public function Processes the settings e.g. to prepare input evaluators. 1
RulesPlugin::providesVariables public function Returns info about all variables provided for later evaluated elements. 2
RulesPlugin::resetInternalCache public function Resets any internal static caches. 1
RulesPlugin::returnExport protected function Finalizes the configuration export.
RulesPlugin::returnVariables protected function Gets variables to return once the configuration has been executed. 2
RulesPlugin::root public function Gets the root element of the configuration.
RulesPlugin::save public function Saves the configuration to the database. 1
RulesPlugin::setParent public function Sets a new parent element.
RulesPlugin::setUpState public function Sets up the execution state for the given arguments.
RulesPlugin::setUpVariables protected function Returns info about all variables that have to be setup in the state. 1
RulesPlugin::variableInfoAssertions protected function Returns asserted additions to the available variable info. 2
RulesPlugin::__clone public function Do a deep clone. 1
RulesPlugin::__sleep public function 2
RulesPlugin::__toString public function When converted to a string, just use the export format.