class RulesAbstractPlugin

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 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.