You are here

class RulesData in Rules 7.2

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 379
Contains the state and data related stuff.

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

Namesort descending Modifiers Type Description Overrides
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.