class RulesAbstractPlugin
Defines a common base class for so-called "Abstract Plugins" like actions.
Modules have to provide the concrete plugin implementation.
Hierarchy
- class \RulesExtendable extends \FacesExtendable
- class \RulesPlugin extends \RulesExtendable
- class \RulesAbstractPlugin extends \RulesPlugin
- class \RulesPlugin extends \RulesExtendable
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 1511
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']) && isset($this->elementName) && 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) {
// Ensure we don't trigger a file scan if $module is FALSE. This can happen
// if _rules_discover_module() fails.
if ($module) {
$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
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | 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. |