class RulesData

A class holding static methods related to data.

Hierarchy

Expanded class hierarchy of RulesData

3 string references to 'RulesData'
RulesData::addMetadataAssertions in includes/rules.state.inc
Adds asserted metadata to the variable info.
RulesData::applyMetadataAssertions in includes/rules.state.inc
Property info alter callback for the entity metadata wrapper.
RulesState::defaultVariables in includes/rules.state.inc
Defines always-available variables.

File

includes/rules.state.inc, line 391

View source
class RulesData {
    
    /**
     * Returns whether the type match. They match if type1 is compatible to type2.
     *
     * @param $var_info
     *   The name of the type to check for whether it is compatible to type2.
     * @param $param_info
     *   The type expression to check for.
     * @param bool $ancestors
     *   (optional) Whether sub-type relationships for checking type compatibility
     *   should be taken into account. Defaults to TRUE.
     *
     * @return bool
     *   Whether the types match.
     */
    public static function typesMatch($var_info, $param_info, $ancestors = TRUE) {
        $var_type = $var_info['type'];
        $param_type = $param_info['type'];
        if ($param_type == '*' || $param_type == 'unknown') {
            return TRUE;
        }
        if ($var_type == $param_type) {
            // Make sure the bundle matches, if specified by the parameter.
            return !isset($param_info['bundles']) || isset($var_info['bundle']) && in_array($var_info['bundle'], $param_info['bundles']);
        }
        // Parameters may specify multiple types using an array.
        $valid_types = is_array($param_type) ? $param_type : array(
            $param_type,
        );
        if (in_array($var_type, $valid_types)) {
            return TRUE;
        }
        // Check for sub-type relationships.
        if ($ancestors && !isset($param_info['bundles'])) {
            $cache =& rules_get_cache();
            self::typeCalcAncestors($cache, $var_type);
            // If one of the types is an ancestor return TRUE.
            return (bool) array_intersect_key($cache['data_info'][$var_type]['ancestors'], array_flip($valid_types));
        }
        return FALSE;
    }
    protected static function typeCalcAncestors(&$cache, $type) {
        if (!isset($cache['data_info'][$type]['ancestors'])) {
            $cache['data_info'][$type]['ancestors'] = array();
            if (isset($cache['data_info'][$type]['parent']) && ($parent = $cache['data_info'][$type]['parent'])) {
                $cache['data_info'][$type]['ancestors'][$parent] = TRUE;
                self::typeCalcAncestors($cache, $parent);
                // Add all parent ancestors to our own ancestors.
                $cache['data_info'][$type]['ancestors'] += $cache['data_info'][$parent]['ancestors'];
            }
            // For special lists like list<node> add in "list" as valid parent.
            if (entity_property_list_extract_type($type)) {
                $cache['data_info'][$type]['ancestors']['list'] = TRUE;
            }
        }
    }
    
    /**
     * Returns data for the given info and the to-be-configured parameter.
     *
     * Returns matching data variables or properties for the given info and the
     * to-be-configured parameter.
     *
     * @param $source
     *   Either an array of info about available variables or a entity metadata
     *   wrapper.
     * @param $param_info
     *   The information array about the to be configured parameter.
     * @param string $prefix
     *   An optional prefix for the data selectors.
     * @param int $recursions
     *   The number of recursions used to go down the tree. Defaults to 2.
     * @param bool $suggestions
     *   Whether possibilities to recurse are suggested as soon as the deepest
     *   level of recursions is reached. Defaults to TRUE.
     *
     * @return array
     *   An array of info about matching variables or properties that match, keyed
     *   with the data selector.
     */
    public static function matchingDataSelector($source, $param_info, $prefix = '', $recursions = 2, $suggestions = TRUE) {
        // If an array of info is given, get entity metadata wrappers first.
        $data = NULL;
        if (is_array($source)) {
            foreach ($source as $name => $info) {
                $source[$name] = rules_wrap_data($data, $info, TRUE);
            }
        }
        $matches = array();
        foreach ($source as $name => $wrapper) {
            $info = $wrapper->info();
            $name = str_replace('_', '-', $name);
            if (self::typesMatch($info, $param_info)) {
                $matches[$prefix . $name] = $info;
                if (!is_array($source) && $source instanceof EntityListWrapper) {
                    // Add some more possible list items.
                    for ($i = 1; $i < 4; $i++) {
                        $matches[$prefix . $i] = $info;
                    }
                }
            }
            // Recurse later on to get an improved ordering of the results.
            if ($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper) {
                $recurse[$prefix . $name] = $wrapper;
                if ($recursions > 0) {
                    $matches += self::matchingDataSelector($wrapper, $param_info, $prefix . $name . ':', $recursions - 1, $suggestions);
                }
                elseif ($suggestions) {
                    // We may not recurse any more,
                    // but indicate the possibility to recurse.
                    $matches[$prefix . $name . ':'] = $wrapper->info();
                    if (!is_array($source) && $source instanceof EntityListWrapper) {
                        // Add some more possible list items.
                        for ($i = 1; $i < 4; $i++) {
                            $matches[$prefix . $i . ':'] = $wrapper->info();
                        }
                    }
                }
            }
        }
        return $matches;
    }
    
    /**
     * Adds asserted metadata to the variable info.
     *
     * In case there are already assertions for a variable, the assertions are
     * merged such that both apply.
     *
     * @see RulesData::applyMetadataAssertions()
     */
    public static function addMetadataAssertions($var_info, $assertions) {
        foreach ($assertions as $selector => $assertion) {
            // Convert the selector back to underscores, such it matches the varname.
            $selector = str_replace('-', '_', $selector);
            $parts = explode(':', $selector);
            if (isset($var_info[$parts[0]])) {
                // Apply the selector to determine the right target array. We build an
                // array like
                // $var_info['rules assertion']['property1']['property2']['#info'] = ..
                $target =& $var_info[$parts[0]]['rules assertion'];
                foreach (array_slice($parts, 1) as $part) {
                    $target =& $target[$part];
                }
                // In case the assertion is directly for a variable, we have to modify
                // the variable info directly. In case the asserted property is nested
                // the info-has to be altered by RulesData::applyMetadataAssertions()
                // before the child-wrapper is created.
                if (count($parts) == 1) {
                    // Support asserting a type in case of generic entity references only.
                    $var_type =& $var_info[$parts[0]]['type'];
                    if (isset($assertion['type']) && ($var_type == 'entity' || $var_type == 'list<entity>')) {
                        $var_type = $assertion['type'];
                        unset($assertion['type']);
                    }
                    // Add any single bundle directly to the variable info, so the
                    // variable fits as argument for parameters requiring the bundle.
                    if (isset($assertion['bundle']) && count($bundles = (array) $assertion['bundle']) == 1) {
                        $var_info[$parts[0]]['bundle'] = reset($bundles);
                    }
                }
                // Add the assertions, but merge them with any previously added
                // assertions if necessary.
                $target['#info'] = isset($target['#info']) ? rules_update_array($target['#info'], $assertion) : $assertion;
                // Add in a callback that the entity metadata wrapper pick up for
                // altering the property info, such that we can add in the assertions.
                $var_info[$parts[0]] += array(
                    'property info alter' => array(
                        'RulesData',
                        'applyMetadataAssertions',
                    ),
                );
                // In case there is a VARNAME_unchanged variable as it is used in update
                // hooks, assume the assertions are valid for the unchanged variable
                // too.
                if (isset($var_info[$parts[0] . '_unchanged'])) {
                    $name = $parts[0] . '_unchanged';
                    $var_info[$name]['rules assertion'] = $var_info[$parts[0]]['rules assertion'];
                    $var_info[$name]['property info alter'] = array(
                        'RulesData',
                        'applyMetadataAssertions',
                    );
                    if (isset($var_info[$parts[0]]['bundle']) && !isset($var_info[$name]['bundle'])) {
                        $var_info[$name]['bundle'] = $var_info[$parts[0]]['bundle'];
                    }
                }
            }
        }
        return $var_info;
    }
    
    /**
     * Property info alter callback for the entity metadata wrapper.
     *
     * Used for applying the rules metadata assertions.
     *
     * @see RulesData::addMetadataAssertions()
     */
    public static function applyMetadataAssertions(EntityMetadataWrapper $wrapper, $property_info) {
        $info = $wrapper->info();
        if (!empty($info['rules assertion'])) {
            $assertion = $info['rules assertion'];
            // In case there are list-wrappers pass through the assertions of the item
            // but make sure we only apply the assertions for the list items for
            // which the conditions are executed.
            if (isset($info['parent']) && $info['parent'] instanceof EntityListWrapper) {
                $assertion = isset($assertion[$info['name']]) ? $assertion[$info['name']] : array();
            }
            // Support specifying multiple bundles, whereas the added properties are
            // the intersection of the bundle properties.
            if (isset($assertion['#info']['bundle'])) {
                $bundles = (array) $assertion['#info']['bundle'];
                foreach ($bundles as $bundle) {
                    $properties[] = isset($property_info['bundles'][$bundle]['properties']) ? $property_info['bundles'][$bundle]['properties'] : array();
                }
                // Add the intersection.
                $property_info['properties'] += count($properties) > 1 ? call_user_func_array('array_intersect_key', $properties) : reset($properties);
            }
            // Support adding directly asserted property info.
            if (isset($assertion['#info']['property info'])) {
                $property_info['properties'] += $assertion['#info']['property info'];
            }
            // Pass through any rules assertion of properties to their info, so any
            // derived wrappers apply it.
            foreach (element_children($assertion) as $key) {
                $property_info['properties'][$key]['rules assertion'] = $assertion[$key];
                $property_info['properties'][$key]['property info alter'] = array(
                    'RulesData',
                    'applyMetadataAssertions',
                );
                // Apply any 'type' and 'bundle' assertion directly to the property
                // info.
                if (isset($assertion[$key]['#info']['type'])) {
                    $type = $assertion[$key]['#info']['type'];
                    // Support asserting a type in case of generic entity references only.
                    if ($property_info['properties'][$key]['type'] == 'entity' && entity_get_info($type)) {
                        $property_info['properties'][$key]['type'] = $type;
                    }
                }
                if (isset($assertion[$key]['#info']['bundle'])) {
                    $bundle = (array) $assertion[$key]['#info']['bundle'];
                    // Add any single bundle directly to the variable info, so the
                    // property fits as argument for parameters requiring the bundle.
                    if (count($bundle) == 1) {
                        $property_info['properties'][$key]['bundle'] = reset($bundle);
                    }
                }
            }
        }
        return $property_info;
    }
    
    /**
     * Property info alter callback for the entity metadata wrapper.
     *
     * Used to inject metadata for the 'site' variable. In contrast to doing this
     * via hook_rules_data_info() this callback makes use of the already existing
     * property info cache for site information of entity metadata.
     *
     * @see RulesPlugin::availableVariables()
     */
    public static function addSiteMetadata(EntityMetadataWrapper $wrapper, $property_info) {
        $site_info = entity_get_property_info('site');
        $property_info['properties'] += $site_info['properties'];
        // Also invoke the usual callback for altering metadata, in case actions
        // have specified further metadata.
        return RulesData::applyMetadataAssertions($wrapper, $property_info);
    }

}

Members

Title Sort descending Modifiers Object type Summary
RulesData::addMetadataAssertions public static function Adds asserted metadata to the variable info.
RulesData::addSiteMetadata public static function Property info alter callback for the entity metadata wrapper.
RulesData::applyMetadataAssertions public static function Property info alter callback for the entity metadata wrapper.
RulesData::matchingDataSelector public static function Returns data for the given info and the to-be-configured parameter.
RulesData::typeCalcAncestors protected static function
RulesData::typesMatch public static function Returns whether the type match. They match if type1 is compatible to type2.