rules.plugins.inc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. <?php
  2. /**
  3. * @file Contains plugin info and implementations not needed for rule evaluation.
  4. */
  5. /**
  6. * Implements a rules action.
  7. */
  8. class RulesAction extends RulesAbstractPlugin implements RulesActionInterface {
  9. protected $itemName = 'action';
  10. /**
  11. * Execute the callback and update/save data as specified by the action.
  12. */
  13. protected function executeCallback(array $args, RulesState $state = NULL) {
  14. rules_log('Evaluating the action %name.', array('%name' => $this->elementName), RulesLog::INFO, $this);
  15. $return = $this->__call('execute', empty($this->info['named parameter']) ? $args : array($args));
  16. // Get the (partially) wrapped arguments.
  17. $args = $state->currentArguments;
  18. if (is_array($return)) {
  19. foreach ($return as $name => $data) {
  20. // Add provided variables.
  21. if (isset($this->info['provides'][$name])) {
  22. $var_name = isset($this->settings[$name . ':var']) ? $this->settings[$name . ':var'] : $name;
  23. if (!$state->varInfo($var_name)) {
  24. $state->addVariable($var_name, $data, $this->info['provides'][$name]);
  25. rules_log('Added the provided variable %name of type %type', array('%name' => $var_name, '%type' => $this->info['provides'][$name]['type']), RulesLog::INFO, $this);
  26. if (!empty($this->info['provides'][$name]['save']) && $state->variables[$var_name] instanceof EntityMetadataWrapper) {
  27. $state->saveChanges($var_name, $state->variables[$var_name]);
  28. }
  29. }
  30. }
  31. // Support updating variables by returning the values.
  32. elseif (!isset($this->info['provides'][$name])) {
  33. // Update the data value using the wrapper.
  34. if (isset($args[$name]) && $args[$name] instanceof EntityMetadataWrapper) {
  35. try {
  36. $args[$name]->set($data);
  37. }
  38. catch (EntityMetadataWrapperException $e) {
  39. throw new RulesEvaluationException('Unable to update the argument for parameter %name: %error', array('%name' => $name, '%error' => $e->getMessage()), $this);
  40. }
  41. }
  42. elseif (array_key_exists($name, $args)) {
  43. // Map back to the source variable name and update it.
  44. $var_name = !empty($this->settings[$name . ':select']) ? str_replace('-', '_', $this->settings[$name . ':select']) : $name;
  45. $state->variables[$var_name] = $data;
  46. }
  47. }
  48. }
  49. }
  50. // Save parameters as defined in the parameter info.
  51. if ($return !== FALSE) {
  52. foreach ($this->info['parameter'] as $name => $info) {
  53. if (!empty($info['save']) && $args[$name] instanceof EntityMetadataWrapper) {
  54. if (isset($this->settings[$name . ':select'])) {
  55. $state->saveChanges($this->settings[$name . ':select'], $args[$name]);
  56. }
  57. else {
  58. // Wrapper has been configured via direct input, so just save.
  59. rules_log('Saved argument of type %type for parameter %name.', array('%name' => $name, '%type' => $args[$name]->type()));
  60. $args[$name]->save();
  61. }
  62. }
  63. }
  64. }
  65. }
  66. }
  67. /**
  68. * Implements a rules condition.
  69. */
  70. class RulesCondition extends RulesAbstractPlugin implements RulesConditionInterface {
  71. protected $itemName = 'condition';
  72. protected $negate = FALSE;
  73. public function providesVariables() {
  74. return array();
  75. }
  76. public function negate($negate = TRUE) {
  77. $this->negate = (bool) $negate;
  78. return $this;
  79. }
  80. public function isNegated() {
  81. return $this->negate;
  82. }
  83. protected function executeCallback(array $args, RulesState $state = NULL) {
  84. $return = (bool) $this->__call('execute', empty($this->info['named parameter']) ? $args : array($args));
  85. rules_log('The condition %name evaluated to %bool', array('%name' => $this->elementName, '%bool' => $return ? 'TRUE' : 'FALSE'), RulesLog::INFO, $this);
  86. return $this->negate ? !$return : $return;
  87. }
  88. public function __sleep() {
  89. return parent::__sleep() + array('negate' => 'negate');
  90. }
  91. /**
  92. * Just return the boolean result.
  93. */
  94. protected function returnVariables(RulesState $state, $result = NULL) {
  95. return $result;
  96. }
  97. protected function exportToArray() {
  98. $not = $this->negate ? 'NOT ' : '';
  99. $export = $this->exportSettings();
  100. // Abbreviate the export making "USING" implicit.
  101. return array($not . $this->elementName => isset($export['USING']) ? $export['USING'] : array());
  102. }
  103. public function import(array $export) {
  104. $this->elementName = rules_array_key($export);
  105. if (strpos($this->elementName, 'NOT ') === 0) {
  106. $this->elementName = substr($this->elementName, 4);
  107. $this->negate = TRUE;
  108. }
  109. // After setting the element name, setup the element again so the right
  110. // element info is loaded.
  111. $this->setUp();
  112. // Re-add 'USING' which has been removed for abbreviation.
  113. $this->importSettings(array('USING' => reset($export)));
  114. }
  115. public function label() {
  116. $label = parent::label();
  117. return $this->negate ? t('NOT @condition', array('@condition' => $label)) : $label;
  118. }
  119. }
  120. /**
  121. * An actual rule.
  122. * Note: A rule also implements the RulesActionInterface (inherited).
  123. */
  124. class Rule extends RulesActionContainer {
  125. protected $conditions = NULL;
  126. protected $itemName = 'rule';
  127. public $label = 'unlabeled';
  128. public function __construct($variables = array(), $providesVars = array()) {
  129. parent::__construct($variables, $providesVars);
  130. // Initialize the conditions container.
  131. if (!isset($this->conditions)) {
  132. $this->conditions = rules_and();
  133. // Don't use setParent() to avoid having it added to the children.
  134. $this->conditions->parent = $this;
  135. }
  136. }
  137. /**
  138. * Get an iterator over all contained conditions. Note that this iterator also
  139. * implements the ArrayAcces interface.
  140. *
  141. * @return RulesRecursiveElementIterator
  142. */
  143. public function conditions() {
  144. return $this->conditions->getIterator();
  145. }
  146. /**
  147. * Returns the "And" condition container, which contains all conditions of
  148. * this rule.
  149. *
  150. * @return RulesAnd
  151. */
  152. public function conditionContainer() {
  153. return $this->conditions;
  154. }
  155. public function __sleep() {
  156. return parent::__sleep() + drupal_map_assoc(array('conditions', 'label'));
  157. }
  158. /**
  159. * Get an iterator over all contained actions. Note that this iterator also
  160. * implements the ArrayAcces interface.
  161. *
  162. * @return RulesRecursiveElementIterator
  163. */
  164. public function actions() {
  165. return parent::getIterator();
  166. }
  167. /**
  168. * Add a condition. Pass either an instance of the RulesConditionInterface
  169. * or the arguments as needed by rules_condition().
  170. *
  171. * @return Rule
  172. * Returns $this to support chained usage.
  173. */
  174. public function condition($name, $settings = array()) {
  175. $this->conditions->condition($name, $settings);
  176. return $this;
  177. }
  178. public function sortChildren($deep = FALSE) {
  179. $this->conditions->sortChildren($deep);
  180. parent::sortChildren($deep);
  181. }
  182. public function evaluate(RulesState $state) {
  183. rules_log('Evaluating conditions of rule %label.', array('%label' => $this->label), RulesLog::INFO, $this);
  184. if ($this->conditions->evaluate($state)) {
  185. rules_log('Rule %label fires.', array('%label' => $this->label), RulesLog::INFO, $this, TRUE);
  186. parent::evaluate($state);
  187. rules_log('Rule %label has fired.', array('%label' => $this->label), RulesLog::INFO, $this, FALSE);
  188. }
  189. }
  190. /**
  191. * Fires the rule, i.e. evaluates the rule without checking its conditions.
  192. *
  193. * @see RulesPlugin::evaluate()
  194. */
  195. public function fire(RulesState $state) {
  196. rules_log('Firing rule %label.', array('%label' => $this->label), RulesLog::INFO, $this);
  197. parent::evaluate($state);
  198. }
  199. public function integrityCheck() {
  200. parent::integrityCheck();
  201. $this->conditions->integrityCheck();
  202. return $this;
  203. }
  204. public function access() {
  205. return (!isset($this->conditions) || $this->conditions->access()) && parent::access();
  206. }
  207. public function dependencies() {
  208. return array_keys(array_flip($this->conditions->dependencies()) + array_flip(parent::dependencies()));
  209. }
  210. public function destroy() {
  211. $this->conditions->destroy();
  212. parent::destroy();
  213. }
  214. /**
  215. * @return RulesRecursiveElementIterator
  216. */
  217. public function getIterator() {
  218. $array = array_merge(array($this->conditions), $this->children);
  219. return new RulesRecursiveElementIterator($array);
  220. }
  221. protected function stateVariables($element = NULL) {
  222. // Don't add in provided action variables for the conditions.
  223. if (isset($element) && $element === $this->conditions) {
  224. return $this->availableVariables();
  225. }
  226. $vars = parent::stateVariables($element);
  227. // Take variable info assertions of conditions into account.
  228. if ($assertions = $this->conditions->variableInfoAssertions()) {
  229. $vars = RulesData::addMetadataAssertions($vars, $assertions);
  230. }
  231. return $vars;
  232. }
  233. protected function exportFlat() {
  234. return $this->isRoot();
  235. }
  236. protected function exportToArray() {
  237. $export = parent::exportToArray();
  238. if (!$this->isRoot()) {
  239. $export[strtoupper($this->plugin())]['LABEL'] = $this->label;
  240. }
  241. return $export;
  242. }
  243. protected function exportChildren($key = NULL) {
  244. $export = array();
  245. if ($this->conditions->children) {
  246. $export = $this->conditions->exportChildren('IF');
  247. }
  248. return $export + parent::exportChildren('DO');
  249. }
  250. public function import(array $export) {
  251. if (!$this->isRoot() && isset($export[strtoupper($this->plugin())]['LABEL'])) {
  252. $this->label = $export[strtoupper($this->plugin())]['LABEL'];
  253. }
  254. parent::import($export);
  255. }
  256. protected function importChildren($export, $key = NULL) {
  257. if (!empty($export['IF'])) {
  258. $this->conditions->importChildren($export, 'IF');
  259. }
  260. parent::importChildren($export, 'DO');
  261. }
  262. public function __clone() {
  263. parent::__clone();
  264. $this->conditions = clone $this->conditions;
  265. $this->conditions->parent = $this;
  266. }
  267. /**
  268. * Rules may not provided any variable info assertions, as Rules are only
  269. * conditionally executed.
  270. */
  271. protected function variableInfoAssertions() {
  272. return array();
  273. }
  274. /**
  275. * Overridden to ensure the whole Rule is deleted at once.
  276. */
  277. public function delete($keep_children = FALSE) {
  278. parent::delete($keep_children);
  279. }
  280. /**
  281. * Overriden to expose the variables of all actions for embedded rules.
  282. */
  283. public function providesVariables() {
  284. $provides = parent::providesVariables();
  285. if (!$this->isRoot()) {
  286. foreach ($this->actions() as $action) {
  287. $provides += $action->providesVariables();
  288. }
  289. }
  290. return $provides;
  291. }
  292. public function resetInternalCache() {
  293. parent::resetInternalCache();
  294. $this->conditions->resetInternalCache();
  295. }
  296. }
  297. /**
  298. * Represents rules getting triggered by events.
  299. */
  300. class RulesReactionRule extends Rule implements RulesTriggerableInterface {
  301. protected $itemName = 'reaction rule';
  302. protected $events = array();
  303. /**
  304. * Returns the array of events associated with that Rule.
  305. */
  306. public function &events() {
  307. return $this->events;
  308. }
  309. /**
  310. * Removes an event from the rule configuration.
  311. *
  312. * @param $event
  313. * The name of the event to remove.
  314. * @return RulesReactionRule
  315. */
  316. public function removeEvent($event) {
  317. if (($id = array_search($event, $this->events)) !== FALSE) {
  318. unset($this->events[$id]);
  319. }
  320. return $this;
  321. }
  322. /**
  323. * @return RulesReactionRule
  324. */
  325. public function event($event) {
  326. $this->events[] = $event;
  327. return $this;
  328. }
  329. /**
  330. * Reaction rules can't add variables to the parent scope, so clone $state.
  331. */
  332. public function evaluate(RulesState $state) {
  333. // Implement recursion prevention for reaction rules.
  334. if ($state->isBlocked($this)) {
  335. return rules_log('Not evaluating @plugin %label to prevent recursion.', array('%label' => $this->label(), '@plugin' => $this->plugin()), RulesLog::INFO, $this);
  336. }
  337. $state->block($this);
  338. $copy = clone $state;
  339. parent::evaluate($copy);
  340. $state->unblock($this);
  341. }
  342. public function access() {
  343. $event_info = rules_fetch_data('event_info');
  344. foreach ($this->events as $event) {
  345. if (!empty($event_info[$event]['access callback']) && !call_user_func($event_info[$event]['access callback'], 'event', $event)) {
  346. return FALSE;
  347. }
  348. }
  349. return parent::access();
  350. }
  351. public function dependencies() {
  352. $modules = array_flip(parent::dependencies());
  353. $event_info = rules_fetch_data('event_info');
  354. foreach ($this->events as $event) {
  355. if (isset($event_info[$event]['module'])) {
  356. $modules[$event_info[$event]['module']] = TRUE;
  357. }
  358. }
  359. return array_keys($modules);
  360. }
  361. public function providesVariables() {
  362. return array();
  363. }
  364. public function parameterInfo($optional = FALSE) {
  365. // If executed directly, all variables as defined by the event need to
  366. // be passed.
  367. return rules_filter_array($this->availableVariables(), 'handler', FALSE);
  368. }
  369. public function availableVariables() {
  370. if (!isset($this->availableVariables)) {
  371. if (isset($this->parent)) {
  372. // Return the event variables provided by the event set, once cached.
  373. $this->availableVariables = $this->parent->stateVariables();
  374. }
  375. else {
  376. // The intersection of the variables provided by the events are
  377. // available.
  378. $event_info = rules_fetch_data('event_info');
  379. $events = array_intersect($this->events, array_keys($event_info));
  380. foreach ($events as $event) {
  381. $event_info[$event] += array('variables' => array());
  382. if (isset($this->availableVariables)) {
  383. $this->availableVariables = array_intersect_key($this->availableVariables, $event_info[$event]['variables']);
  384. }
  385. else {
  386. $this->availableVariables = $event_info[$event]['variables'];
  387. }
  388. }
  389. $this->availableVariables = isset($this->availableVariables) ? RulesState::defaultVariables() + $this->availableVariables : RulesState::defaultVariables();
  390. }
  391. }
  392. return $this->availableVariables;
  393. }
  394. public function __sleep() {
  395. return parent::__sleep() + drupal_map_assoc(array('events'));
  396. }
  397. protected function exportChildren($key = 'ON') {
  398. $export[$key] = array_values($this->events);
  399. return $export + parent::exportChildren();
  400. }
  401. protected function importChildren($export, $key = 'ON') {
  402. $this->events = $export[$key];
  403. parent::importChildren($export);
  404. }
  405. }
  406. /**
  407. * A logical AND.
  408. */
  409. class RulesAnd extends RulesConditionContainer {
  410. protected $itemName = 'and';
  411. public function evaluate(RulesState $state) {
  412. foreach ($this->children as $condition) {
  413. if (!$condition->evaluate($state)) {
  414. rules_log('AND evaluated to FALSE.');
  415. return $this->negate;
  416. }
  417. }
  418. rules_log('AND evaluated to TRUE.');
  419. return !$this->negate;
  420. }
  421. public function label() {
  422. return !empty($this->label) ? $this->label : ($this->negate ? t('NOT AND') : t('AND'));
  423. }
  424. }
  425. /**
  426. * A logical OR.
  427. */
  428. class RulesOr extends RulesConditionContainer {
  429. protected $itemName = 'or';
  430. public function evaluate(RulesState $state) {
  431. foreach ($this->children as $condition) {
  432. if ($condition->evaluate($state)) {
  433. rules_log('OR evaluated to TRUE.');
  434. return !$this->negate;
  435. }
  436. }
  437. rules_log('OR evaluated to FALSE.');
  438. return $this->negate;
  439. }
  440. public function label() {
  441. return !empty($this->label) ? $this->label : ($this->negate ? t('NOT OR') : t('OR'));
  442. }
  443. /**
  444. * Overridden to exclude all variable assertions as in an OR we cannot assert
  445. * the children are successfully evaluated.
  446. */
  447. protected function stateVariables($element = NULL) {
  448. $vars = $this->availableVariables();
  449. if (isset($element)) {
  450. // Add in variables provided by siblings executed before the element.
  451. foreach ($this->children as $child) {
  452. if ($child === $element) {
  453. break;
  454. }
  455. $vars += $child->providesVariables();
  456. }
  457. }
  458. return $vars;
  459. }
  460. }
  461. /**
  462. * A loop element.
  463. */
  464. class RulesLoop extends RulesActionContainer {
  465. protected $itemName = 'loop';
  466. protected $listItemInfo;
  467. public function __construct($settings = array(), $variables = NULL) {
  468. $this->setUp();
  469. $this->settings = (array) $settings + array(
  470. 'item:var' => 'list_item',
  471. 'item:label' => t('Current list item'),
  472. );
  473. if (!empty($variables)) {
  474. $this->info['variables'] = $variables;
  475. }
  476. }
  477. public function pluginParameterInfo() {
  478. $info['list'] = array(
  479. 'type' => 'list',
  480. 'restriction' => 'selector',
  481. 'label' => t('List'),
  482. 'description' => t('The list to loop over. The loop will step through each item in the list, allowing further actions on them. See <a href="@url"> the online handbook</a> for more information on how to use loops.',
  483. array('@url' => rules_external_help('loops'))),
  484. );
  485. return $info;
  486. }
  487. public function integrityCheck() {
  488. parent::integrityCheck();
  489. $this->checkVarName($this->settings['item:var']);
  490. }
  491. public function listItemInfo() {
  492. if (!isset($this->listItemInfo)) {
  493. if ($info = $this->getArgumentInfo('list')) {
  494. // Pass through the variable info keys like property info.
  495. $this->listItemInfo = array_intersect_key($info, array_flip(array('type', 'property info', 'bundle')));
  496. $this->listItemInfo['type'] = isset($info['type']) ? entity_property_list_extract_type($info['type']) : 'unknown';
  497. }
  498. else {
  499. $this->listItemInfo = array('type' => 'unknown');
  500. }
  501. $this->listItemInfo['label'] = $this->settings['item:label'];
  502. }
  503. return $this->listItemInfo;
  504. }
  505. public function evaluate(RulesState $state) {
  506. try {
  507. $param_info = $this->pluginParameterInfo();
  508. $list = $this->getArgument('list', $param_info['list'], $state);
  509. $item_var_info = $this->listItemInfo();
  510. $item_var_name = $this->settings['item:var'];
  511. if (isset($this->settings['list:select'])) {
  512. rules_log('Looping over the list items of %selector', array('%selector' => $this->settings['list:select']), RulesLog::INFO, $this);
  513. }
  514. // Loop over the list and evaluate the children for each list item.
  515. foreach ($list as $key => $item) {
  516. // Use a separate state so variables are available in the loop only.
  517. $state2 = clone $state;
  518. $state2->addVariable($item_var_name, $list[$key], $item_var_info);
  519. parent::evaluate($state2);
  520. // Update variables from parent scope.
  521. foreach ($state->variables as $var_key => &$var_value) {
  522. if (array_key_exists($var_key, $state2->variables)) {
  523. $var_value = $state2->variables[$var_key];
  524. }
  525. }
  526. }
  527. }
  528. catch (RulesEvaluationException $e) {
  529. rules_log($e->msg, $e->args, $e->severity);
  530. rules_log('Unable to evaluate %name.', array('%name' => $this->getPluginName()), RulesLog::WARN, $this);
  531. }
  532. }
  533. protected function stateVariables($element = NULL) {
  534. return array($this->settings['item:var'] => $this->listItemInfo()) + parent::stateVariables($element);
  535. }
  536. public function label() {
  537. return !empty($this->label) ? $this->label : t('Loop');
  538. }
  539. protected function exportChildren($key = 'DO') {
  540. return parent::exportChildren($key);
  541. }
  542. protected function importChildren($export, $key = 'DO') {
  543. parent::importChildren($export, $key);
  544. }
  545. protected function exportSettings() {
  546. $export = parent::exportSettings();
  547. $export['ITEM'][$this->settings['item:var']] = $this->settings['item:label'];
  548. return $export;
  549. }
  550. protected function importSettings($export) {
  551. parent::importSettings($export);
  552. if (isset($export['ITEM'])) {
  553. $this->settings['item:var'] = rules_array_key($export['ITEM']);
  554. $this->settings['item:label'] = reset($export['ITEM']);
  555. }
  556. }
  557. }
  558. /**
  559. * An action set component.
  560. */
  561. class RulesActionSet extends RulesActionContainer {
  562. protected $itemName = 'action set';
  563. }
  564. /**
  565. * A set of rules to execute upon defined variables.
  566. */
  567. class RulesRuleSet extends RulesActionContainer {
  568. protected $itemName = 'rule set';
  569. /**
  570. * @return RulesRuleSet
  571. */
  572. public function rule($rule) {
  573. return $this->action($rule);
  574. }
  575. protected function exportChildren($key = 'RULES') {
  576. return parent::exportChildren($key);
  577. }
  578. protected function importChildren($export, $key = 'RULES') {
  579. parent::importChildren($export, $key);
  580. }
  581. }
  582. /**
  583. * This class is used for caching the rules to be evaluated per event.
  584. */
  585. class RulesEventSet extends RulesRuleSet {
  586. protected $itemName = 'event set';
  587. // Event sets may recurse as we block recursions on rule-level.
  588. public $recursion = TRUE;
  589. public function __construct($info = array()) {
  590. $this->setup();
  591. $this->info = $info;
  592. }
  593. public function executeByArgs($args = array()) {
  594. rules_log('Reacting on event %label.', array('%label' => $this->info['label']), RulesLog::INFO, NULL, TRUE);
  595. $state = $this->setUpState($args);
  596. module_invoke_all('rules_config_execute', $this);
  597. $this->evaluate($state);
  598. $state->cleanUp($this);
  599. rules_log('Finished reacting on event %label.', array('%label' => $this->info['label']), RulesLog::INFO, NULL, FALSE);
  600. }
  601. /**
  602. * Cache event-sets per event to allow efficient usage via rules_invoke_event().
  603. *
  604. * @see rules_get_cache()
  605. * @see rules_invoke_event()
  606. */
  607. public static function rebuildEventCache() {
  608. // Set up the per-event cache.
  609. $events = rules_fetch_data('event_info');
  610. $sets = array();
  611. // Add all rules associated with this event to an EventSet for caching.
  612. $rules = rules_config_load_multiple(FALSE, array('plugin' => 'reaction rule', 'active' => TRUE));
  613. foreach ($rules as $name => $rule) {
  614. foreach ($rule->events() as $event) {
  615. // Skip not defined events.
  616. if (empty($events[$event])) {
  617. continue;
  618. }
  619. // Create an event set if not yet done.
  620. if (!isset($sets[$event])) {
  621. $event_info = $events[$event] + array(
  622. 'variables' => isset($events[$event]['arguments']) ? $events[$event]['arguments'] : array(),
  623. );
  624. $sets[$event] = new RulesEventSet($event_info);
  625. $sets[$event]->name = $event;
  626. }
  627. // If a rule is marked as dirty, check if this still applies.
  628. if ($rule->dirty) {
  629. rules_config_update_dirty_flag($rule);
  630. }
  631. if (!$rule->dirty) {
  632. // Clone the rule to avoid modules getting the changed version from
  633. // the static cache.
  634. $sets[$event]->rule(clone $rule);
  635. }
  636. }
  637. }
  638. // Create cache items for all created sets.
  639. foreach ($sets as $event => $set) {
  640. $set->sortChildren();
  641. $set->optimize();
  642. // Allow modules to alter the cached event set.
  643. drupal_alter('rules_event_set', $event, $set);
  644. rules_set_cache('event_' . $event, $set);
  645. }
  646. // Cache a list of empty sets so we can use it to speed up later calls.
  647. // See rules_get_event_set().
  648. $empty_events = array_keys(array_diff_key($events, $sets));
  649. variable_set('rules_empty_sets', array_flip($empty_events));
  650. }
  651. protected function stateVariables($element = NULL) {
  652. return $this->availableVariables();
  653. }
  654. /**
  655. * Do not save since this class is for caching purposes only.
  656. *
  657. * @see RulesPlugin::save()
  658. */
  659. public function save($name = NULL, $module = 'rules') {
  660. return FALSE;
  661. }
  662. }