You are here

abstract class RulesAbstractPlugin in Rules 7.2

Defines a common base class for so-called "Abstract Plugins" like actions.

Modules have to provide the concrete plugin implementation.

Hierarchy

Expanded class hierarchy of RulesAbstractPlugin

1 string reference to 'RulesAbstractPlugin'
rules_ui_add_element in ui/ui.forms.inc
Add a new element a rules configuration.

File

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

View source
abstract class RulesAbstractPlugin extends RulesPlugin {
  protected $elementName;
  protected $info = array(
    'parameter' => array(),
    'provides' => array(),
  );
  protected $infoLoaded = FALSE;

  /**
   * @param string $name
   *   The plugin implementation's name.
   * @param $settings
   *   (optional) Further information provided about the plugin.
   * @throws RulesException
   *   If validation of the passed settings fails RulesExceptions are thrown.
   */
  public function __construct($name = NULL, $settings = array()) {
    $this->elementName = $name;
    $this->settings = (array) $settings + array(
      '#_needs_processing' => TRUE,
    );
    $this
      ->setUp();
  }
  protected function setUp() {
    parent::setUp();
    if (isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
      $this->info = $this->cache[$this->itemName . '_info'][$this->elementName];

      // Remember that the info has been correctly setup.
      // @see self::forceSetup()
      $this->infoLoaded = TRUE;

      // Register the defined class, if any.
      if (isset($this->info['class'])) {
        $this->faces['RulesPluginImplInterface'] = 'RulesPluginImplInterface';
        $face_methods = get_class_methods('RulesPluginImplInterface');
        $class_info = array(
          1 => $this->info['class'],
        );
        foreach ($face_methods as $method) {
          $this->facesMethods[$method] = $class_info;
        }
      }

      // Add in per-plugin implementation callbacks if any.
      if (!empty($this->info['faces_cache'])) {
        foreach ($this->info['faces_cache'] as $face => $data) {
          list($methods, $file_names) = $data;
          foreach ($methods as $method => $callback) {
            $this->facesMethods[$method] = $callback;
          }
          foreach ((array) $file_names as $method => $name) {
            $this->facesIncludes[$method] = array(
              'module' => $this->info['module'],
              'name' => $name,
            );
          }
        }

        // Invoke the info_alter callback, but only if it has been implemented.
        if ($this->facesMethods['info_alter'] != $this->itemInfo['faces_cache'][0]['info_alter']) {
          $this
            ->__call('info_alter', array(
            &$this->info,
          ));
        }
      }
    }
    elseif (!empty($this->itemInfo['faces_cache']) && function_exists($this->elementName)) {

      // We don't have any info, so just add the name as execution callback.
      $this
        ->override(array(
        'execute' => $this->elementName,
      ));
    }
  }
  public function forceSetUp() {
    if (!isset($this->cache) || !empty($this->itemInfo['faces_cache']) && !$this->faces) {
      $this
        ->setUp();
    }
    elseif (!$this->infoLoaded && isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
      $this
        ->setUp();
    }
  }

  /**
   * Returns the label of the element.
   */
  public function label() {
    $info = $this
      ->info();
    return isset($info['label']) ? $info['label'] : t('@plugin "@name"', array(
      '@name' => $this->elementName,
      '@plugin' => $this
        ->plugin(),
    ));
  }
  public function access() {
    $info = $this
      ->info();
    $this
      ->loadBasicInclude();
    if (!empty($info['access callback']) && !call_user_func($info['access callback'], $this->itemName, $this
      ->getElementName())) {
      return FALSE;
    }
    return parent::access() && $this
      ->__call('access');
  }
  public function integrityCheck() {

    // Do the usual integrity check first so the implementation's validation
    // handler can rely on that already.
    parent::integrityCheck();

    // Make sure the element is known.
    $this
      ->forceSetUp();
    if (!isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
      throw new RulesIntegrityException(t('Unknown @plugin %name.', array(
        '@plugin' => $this
          ->plugin(),
        '%name' => $this->elementName,
      )));
    }
    $this
      ->validate();
    return $this;
  }
  public function processSettings($force = FALSE) {

    // Process if not done yet.
    if ($force || !empty($this->settings['#_needs_processing'])) {
      $this
        ->resetInternalCache();

      // In case the element implements the info alteration callback, (re-)run
      // the alteration so that any settings depending info alterations are
      // applied.
      if ($this->facesMethods && $this->facesMethods['info_alter'] != $this->itemInfo['faces_cache'][0]['info_alter']) {
        $this
          ->__call('info_alter', array(
          &$this->info,
        ));
      }

      // First let the plugin implementation do processing, so data types of the
      // parameters are fixed when we process the settings.
      $this
        ->process();
      parent::processSettings($force);
    }
  }
  public function pluginParameterInfo() {

    // Ensure the info alter callback has been executed.
    $this
      ->forceSetup();
    return parent::pluginParameterInfo();
  }
  public function pluginProvidesVariables() {

    // Ensure the info alter callback has been executed.
    $this
      ->forceSetup();
    return parent::pluginProvidesVariables();
  }
  public function info() {

    // Ensure the info alter callback has been executed.
    $this
      ->forceSetup();
    return $this->info;
  }
  protected function variableInfoAssertions() {

    // Get the implementation's assertions and map them to the variable names.
    if ($assertions = $this
      ->__call('assertions')) {
      foreach ($assertions as $param_name => $data) {
        $name = isset($this->settings[$param_name . ':select']) ? $this->settings[$param_name . ':select'] : $param_name;
        $return[$name] = $data;
      }
      return $return;
    }
  }
  public function import(array $export) {

    // The key is the element name and the value the actual export.
    $this->elementName = rules_array_key($export);
    $export = reset($export);

    // After setting the element name, setup the element again so the right
    // element info is loaded.
    $this
      ->setUp();
    if (!isset($export['USING']) && !isset($export['PROVIDES']) && !empty($export)) {

      // The export has been abbreviated to skip "USING".
      $export = array(
        'USING' => $export,
      );
    }
    $this
      ->importSettings($export);
  }
  protected function exportToArray() {
    $export = $this
      ->exportSettings();
    if (!$this
      ->providesVariables()) {

      // Abbreviate the export making "USING" implicit.
      $export = isset($export['USING']) ? $export['USING'] : array();
    }
    return array(
      $this->elementName => $export,
    );
  }
  public function dependencies() {
    $modules = array_flip(parent::dependencies());
    $modules += array_flip((array) $this
      ->__call('dependencies'));
    return array_keys($modules + (!empty($this->info['module']) ? array(
      $this->info['module'] => 1,
    ) : array()));
  }
  public function executeByArgs($args = array()) {
    $replacements = array(
      '%label' => $this
        ->label(),
      '@plugin' => $this->itemName,
    );
    rules_log('Executing @plugin %label.', $replacements, RulesLog::INFO, $this, TRUE);
    $this
      ->processSettings();

    // If there is no element info, just pass through the passed arguments.
    // That way we support executing actions without any info at all.
    if ($this
      ->info()) {
      $state = $this
        ->setUpState($args);
      module_invoke_all('rules_config_execute', $this);
      $result = $this
        ->evaluate($state);
      $return = $this
        ->returnVariables($state, $result);
    }
    else {
      rules_log('Unable to execute @plugin %label.', $replacements, RulesLog::ERROR, $this);
    }
    $state
      ->cleanUp();
    rules_log('Finished executing of @plugin %label.', $replacements, RulesLog::INFO, $this, FALSE);
    return $return;
  }

  /**
   * Execute the configured execution callback and log that.
   */
  protected abstract function executeCallback(array $args, RulesState $state = NULL);
  public function evaluate(RulesState $state) {
    $this
      ->processSettings();
    try {

      // Get vars as needed for execute and call it.
      return $this
        ->executeCallback($this
        ->getExecutionArguments($state), $state);
    } catch (RulesEvaluationException $e) {
      rules_log($e->msg, $e->args, $e->severity);
      rules_log('Unable to evaluate %name.', array(
        '%name' => $this
          ->getPluginName(),
      ), RulesLog::WARN, $this);
    } catch (EntityMetadataWrapperException $e) {
      rules_log('Unable to get a data value. Error: !error', array(
        '!error' => $e
          ->getMessage(),
      ), RulesLog::WARN);
      rules_log('Unable to evaluate %name.', array(
        '%name' => $this
          ->getPluginName(),
      ), RulesLog::WARN, $this);
    }
  }
  public function __sleep() {
    return parent::__sleep() + array(
      'elementName' => 'elementName',
    );
  }
  public function getPluginName() {
    return $this->itemName . " " . $this->elementName;
  }

  /**
   * Gets the name of the configured action or condition.
   */
  public function getElementName() {
    return $this->elementName;
  }

  /**
   * Add in the data provided by the info hooks to the cache.
   */
  public function rebuildCache(&$itemInfo, &$cache) {
    parent::rebuildCache($itemInfo, $cache);

    // Include all declared files so we can find all implementations.
    self::includeFiles();

    // Get the plugin's own info data.
    $cache[$this->itemName . '_info'] = rules_fetch_data($this->itemName . '_info');
    foreach ($cache[$this->itemName . '_info'] as $name => &$info) {
      $info += array(
        'parameter' => isset($info['arguments']) ? $info['arguments'] : array(),
        'provides' => isset($info['new variables']) ? $info['new variables'] : array(),
        'base' => $name,
        'callbacks' => array(),
      );
      unset($info['arguments'], $info['new variables']);
      if (function_exists($info['base'])) {
        $info['callbacks'] += array(
          'execute' => $info['base'],
        );
      }

      // We do not need to build a faces cache for RulesPluginHandlerInterface,
      // which gets added in automatically as its a parent of
      // RulesPluginImplInterface.
      unset($this->faces['RulesPluginHandlerInterface']);

      // Build up the per-plugin implementation faces cache.
      foreach ($this->faces as $interface) {
        $methods = $file_names = array();
        $includes = self::getIncludeFiles($info['module']);
        foreach (get_class_methods($interface) as $method) {
          if (isset($info['callbacks'][$method]) && ($function = $info['callbacks'][$method])) {
            $methods[$method][0] = $function;
            $file_names[$method] = $this
              ->getFileName($function, $includes);
          }
          elseif (isset($info['class']) && is_subclass_of($info['class'], $interface)) {
            $methods[$method][1] = $info['class'];
          }
          elseif (function_exists($function = $info['base'] . '_' . $method)) {
            $methods[$method][0] = $function;
            $file_names[$method] = $this
              ->getFileName($function, $includes);
          }
        }

        // Cache only the plugin implementation specific callbacks.
        $info['faces_cache'][$interface] = array(
          $methods,
          array_filter($file_names),
        );
      }

      // Filter out interfaces with no overridden methods.
      $info['faces_cache'] = rules_filter_array($info['faces_cache'], 0, TRUE);

      // We don't need that any more.
      unset($info['callbacks'], $info['base']);
    }
  }

  /**
   * Loads this module's .rules.inc file.
   *
   * Makes sure the providing modules' .rules.inc file is included, as diverse
   * callbacks may reside in that file.
   */
  protected function loadBasicInclude() {
    static $included = array();
    if (isset($this->info['module']) && !isset($included[$this->info['module']])) {
      $module = $this->info['module'];
      module_load_include('inc', $module, $module . '.rules');
      $included[$module] = TRUE;
    }
  }

  /**
   * Makes sure all supported destinations are included.
   */
  public static function includeFiles() {
    static $included;
    if (!isset($included)) {
      foreach (module_implements('rules_file_info') as $module) {

        // rules.inc are already included thanks to the rules_hook_info() group.
        foreach (self::getIncludeFiles($module, FALSE) as $name) {
          module_load_include('inc', $module, $name);
        }
      }
      $dirs = array();
      foreach (module_implements('rules_directory') as $module) {

        // Include all files once, so the discovery can find them.
        $result = module_invoke($module, 'rules_directory');
        if (!is_array($result)) {
          $result = array(
            $module => $result,
          );
        }
        $dirs += $result;
      }
      foreach ($dirs as $module => $directory) {
        $module_path = drupal_get_path('module', $module);
        foreach (array(
          'inc',
          'php',
        ) as $extension) {
          foreach (glob("{$module_path}/{$directory}/*.{$extension}") as $filename) {
            include_once $filename;
          }
        }
      }
      $included = TRUE;
    }
  }

  /**
   * Returns all include files for a module.
   *
   * @param string $module
   *   The module name.
   * @param bool $all
   *   If FALSE, the $module.rules.inc file isn't added.
   *
   * @return string[]
   *   An array containing the names of all the include files for a module.
   */
  protected static function getIncludeFiles($module, $all = TRUE) {
    $files = (array) module_invoke($module, 'rules_file_info');

    // Automatically add "$module.rules_forms.inc" and "$module.rules.inc".
    $files[] = $module . '.rules_forms';
    if ($all) {
      $files[] = $module . '.rules';
    }
    return $files;
  }
  protected function getFileName($function, $includes) {
    static $filenames;
    if (!isset($filenames) || !array_key_exists($function, $filenames)) {
      $filenames[$function] = NULL;
      $reflector = new ReflectionFunction($function);

      // On windows the path contains backslashes instead of slashes, fix that.
      $file = str_replace('\\', '/', $reflector
        ->getFileName());
      foreach ($includes as $include) {
        $pos = strpos($file, $include . '.inc');

        // Test whether the file ends with the given filename.inc.
        if ($pos !== FALSE && strlen($file) - $pos == strlen($include) + 4) {
          $filenames[$function] = $include;
          return $include;
        }
      }
    }
    return $filenames[$function];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
RulesAbstractPlugin::$elementName protected property
RulesAbstractPlugin::$info protected property Info about this element. Usage depends on the plugin. Overrides RulesPlugin::$info
RulesAbstractPlugin::$infoLoaded protected property
RulesAbstractPlugin::access public function Whether the currently logged in user has access to all configured elements. Overrides RulesPlugin::access
RulesAbstractPlugin::dependencies public function Calculates an array of required modules. Overrides RulesPlugin::dependencies
RulesAbstractPlugin::evaluate public function Evaluate the element on a given rules evaluation state. Overrides RulesPlugin::evaluate
RulesAbstractPlugin::executeByArgs public function Execute the configuration by passing arguments in a single array. Overrides RulesPlugin::executeByArgs
RulesAbstractPlugin::executeCallback abstract protected function Execute the configured execution callback and log that. 2
RulesAbstractPlugin::exportToArray protected function Overrides RulesPlugin::exportToArray 1
RulesAbstractPlugin::forceSetUp public function Forces the object to be setUp, this executes setUp() if not done yet. Overrides RulesExtendable::forceSetUp
RulesAbstractPlugin::getElementName public function Gets the name of the configured action or condition.
RulesAbstractPlugin::getFileName protected function
RulesAbstractPlugin::getIncludeFiles protected static function Returns all include files for a module.
RulesAbstractPlugin::getPluginName public function Gets the name of this plugin instance. Overrides RulesPlugin::getPluginName
RulesAbstractPlugin::import public function Applies the given export. Overrides RulesPlugin::import 1
RulesAbstractPlugin::includeFiles public static function Makes sure all supported destinations are included.
RulesAbstractPlugin::info public function Returns the info of the plugin. Overrides RulesPlugin::info
RulesAbstractPlugin::integrityCheck public function Makes sure the plugin is configured right. Overrides RulesPlugin::integrityCheck
RulesAbstractPlugin::label public function Returns the label of the element. Overrides RulesPlugin::label 1
RulesAbstractPlugin::loadBasicInclude protected function Loads this module's .rules.inc file.
RulesAbstractPlugin::pluginParameterInfo public function Returns info about parameters needed by the plugin. Overrides RulesPlugin::pluginParameterInfo
RulesAbstractPlugin::pluginProvidesVariables public function Returns info about variables 'provided' by the plugin. Overrides RulesPlugin::pluginProvidesVariables
RulesAbstractPlugin::processSettings public function Processes the settings e.g. to prepare input evaluators. Overrides RulesPlugin::processSettings
RulesAbstractPlugin::rebuildCache public function Add in the data provided by the info hooks to the cache. Overrides RulesExtendable::rebuildCache
RulesAbstractPlugin::setUp protected function Overrides RulesExtendable::setUp
RulesAbstractPlugin::variableInfoAssertions protected function Returns asserted additions to the available variable info. Overrides RulesPlugin::variableInfoAssertions
RulesAbstractPlugin::__construct public function Overrides RulesExtendable::__construct
RulesAbstractPlugin::__sleep public function Overrides RulesPlugin::__sleep 1
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::itemFacesAs public static function Returns whether the a RuleExtendable supports the given interface.
RulesExtendable::__call public function Magic method: Invoke the dynamically implemented methods.
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::$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::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::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::execute public function Execute the configuration.
RulesPlugin::export public function Exports a rule configuration.
RulesPlugin::exportParameterSetting protected function
RulesPlugin::exportSettings protected function 1
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::hasStatus public function Checks if the configuration has a certain exportable status.
RulesPlugin::identifier public function Returns the config name.
RulesPlugin::importParameterSetting protected function
RulesPlugin::importSettings protected function 1
RulesPlugin::internalIdentifier public function
RulesPlugin::isRoot public function Returns whether the element is the root of the configuration.
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::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::__clone public function Do a deep clone. 1
RulesPlugin::__toString public function When converted to a string, just use the export format.