rules.processor.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /**
  3. * @file Contains classes for data processing.
  4. *
  5. * Data processors can be used to process element arguments on evaluation time,
  6. * e.g. to apply input evaluators or to apply simple calculations to number
  7. * arguments.
  8. */
  9. /**
  10. * Common base class for Rules data processors.
  11. */
  12. abstract class RulesDataProcessor {
  13. /**
  14. * The processors' setting value.
  15. */
  16. protected $setting = NULL;
  17. /**
  18. * Allows chaining processors. If set, the next processor to invoke.
  19. */
  20. protected $processor = NULL;
  21. /**
  22. * Constructor.
  23. */
  24. protected function __construct($setting, $param_info, $var_info = array(), $processor = NULL) {
  25. $this->setting = $setting;
  26. $this->processor = $processor;
  27. }
  28. /**
  29. * Return $this or skip this processor by returning the next processor.
  30. */
  31. protected function getPreparedValue() {
  32. return isset($this->setting) && array_filter($this->setting) ? $this : $this->processor;
  33. }
  34. /**
  35. * Returns whether the current user has permission to edit this chain of data
  36. * processors.
  37. */
  38. public function editAccess() {
  39. return $this->access() && (!isset($this->processor) || $this->processor->editAccess());
  40. }
  41. /**
  42. * Prepares the processor for parameters.
  43. *
  44. * It turns the settings into a suiting processor object, which gets invoked
  45. * on evaluation time.
  46. *
  47. * @param $setting
  48. * The processor settings which are to be prepared.
  49. * @param $param_info
  50. * The info about the parameter to prepare the processor for.
  51. * @param $var_info
  52. * An array of info about the available variables.
  53. */
  54. public static function prepareSetting(&$setting, $param_info, $var_info = array()) {
  55. $processor = NULL;
  56. foreach (self::processors($param_info, FALSE) as $name => $info) {
  57. if (!empty($setting[$name])) {
  58. $object = new $info['class']($setting[$name], $param_info, $var_info, $processor);
  59. $processor = $object->getPreparedValue();
  60. }
  61. }
  62. $setting = $processor;
  63. }
  64. /**
  65. * Attaches the form of applicable data processors.
  66. */
  67. public static function attachForm(&$form, $settings, $param_info, $var_info, $access_check = TRUE) {
  68. // If $settings is already prepared get the settings from the processors.
  69. if ($settings instanceof RulesDataProcessor) {
  70. $settings = $settings->getChainSettings();
  71. }
  72. foreach (self::processors($param_info, $access_check) as $name => $info) {
  73. $settings += array($name => array());
  74. $form[$name] = call_user_func(array($info['class'], 'form'), $settings[$name], $var_info);
  75. $form[$name]['#weight'] = $info['weight'];
  76. }
  77. }
  78. /**
  79. * Returns defined data processors applicable for the given parameter.
  80. * Optionally also access to the processors is checked.
  81. *
  82. * @param $param_info
  83. * If given, only processors valid for this parameter are returned.
  84. */
  85. public static function processors($param_info = NULL, $access_check = TRUE, $hook = 'data_processor_info') {
  86. static $items = array();
  87. if (!isset($items[$hook]['all'])) {
  88. $items[$hook]['all'] = rules_fetch_data($hook);
  89. uasort($items[$hook]['all'], array(__CLASS__, '_item_sort'));
  90. }
  91. // Data processing isn't supported for multiple types.
  92. if (isset($param_info) && is_array($param_info['type'])) {
  93. return array();
  94. }
  95. // Filter the items by type.
  96. if (isset($param_info['type']) && !isset($items[$hook][$param_info['type']])) {
  97. $items[$hook][$param_info['type']] = array();
  98. foreach ($items[$hook]['all'] as $name => $info) {
  99. // Check whether the parameter type matches the supported types.
  100. $info += array('type' => 'text');
  101. if (RulesData::typesMatch($param_info, $info, FALSE)) {
  102. $items[$hook][$param_info['type']][$name] = $info;
  103. }
  104. }
  105. }
  106. // Apply the access check.
  107. $return = isset($param_info['type']) ? $items[$hook][$param_info['type']] : $items[$hook]['all'];
  108. if ($access_check) {
  109. foreach ($return as $base => $info) {
  110. if (!call_user_func(array($info['class'], 'access'))) {
  111. unset($return[$base]);
  112. }
  113. }
  114. }
  115. return $return;
  116. }
  117. public static function _item_sort($a, $b) {
  118. return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : 0);
  119. }
  120. /**
  121. * Gets the settings array for this and all contained chained processors.
  122. */
  123. public function getChainSettings() {
  124. foreach ($this->unchain() as $name => $processor) {
  125. $settings[$name] = $processor->getSetting();
  126. }
  127. return isset($settings) ? $settings : array();
  128. }
  129. /**
  130. * Returns an array of modules which we depend on.
  131. */
  132. public function dependencies() {
  133. $used_processor_info = array_intersect_key($this->processors(), $this->unchain());
  134. $modules = array();
  135. foreach ($used_processor_info as $name => $info) {
  136. $modules[] = $info['module'];
  137. }
  138. return array_filter($modules);
  139. }
  140. /**
  141. * @return
  142. * An array of processors keyed by processor name.
  143. */
  144. protected function unchain() {
  145. $processor = $this;
  146. while ($processor instanceof RulesDataProcessor) {
  147. $processors[get_class($processor)] = $processor;
  148. $processor = $processor->processor;
  149. }
  150. // Note: Don't use the static context to call processors() here as we need a
  151. // late binding to invoke the input evaluators version, if needed.
  152. $return = array();
  153. foreach ($this->processors() as $name => $info) {
  154. if (isset($processors[$info['class']])) {
  155. $return[$name] = $processors[$info['class']];
  156. }
  157. }
  158. return $return;
  159. }
  160. /**
  161. * Gets the settings of this processor.
  162. */
  163. public function getSetting() {
  164. return $this->setting;
  165. }
  166. /**
  167. * Processes the value. If $this->processor is set, invoke this processor
  168. * first so chaining multiple processors is working.
  169. *
  170. * @param $value
  171. * The value to process.
  172. * @param $info
  173. * Info about the parameter for which we process the value.
  174. * @param $state RulesState
  175. * The rules evaluation state.
  176. * @param $element RulesPlugin
  177. * The element for which we process the value.
  178. * @return
  179. * The processed value.
  180. */
  181. abstract public function process($value, $info, RulesState $state, RulesPlugin $element);
  182. /**
  183. * Return whether the current user has permission to use the processor.
  184. */
  185. public static function access() {
  186. return TRUE;
  187. }
  188. /**
  189. * Defines the processor form element.
  190. *
  191. * @param $settings
  192. * The settings of the processor.
  193. * @param $var_info
  194. * An array of info about the available variables.
  195. *
  196. * @return
  197. * A form element structure.
  198. */
  199. protected static function form($settings, $var_info) {
  200. return array();
  201. }
  202. }
  203. /**
  204. * A base processor for use as input evaluators. Input evaluators are not listed
  205. * in hook_rules_data_processor_info(). Instead they use
  206. * hook_rules_evaluator_info() and get attached to input forms.
  207. */
  208. abstract class RulesDataInputEvaluator extends RulesDataProcessor {
  209. /**
  210. * Overridden to invoke prepare().
  211. */
  212. protected function __construct($setting, $param_info, $var_info = array(), $processor = NULL) {
  213. $this->setting = TRUE;
  214. $this->processor = $processor;
  215. $this->prepare($setting, $var_info, $param_info);
  216. }
  217. /**
  218. * Overridden to generate evaluator $options and invoke evaluate().
  219. */
  220. public function process($value, $info, RulesState $state, RulesPlugin $element, $options = NULL) {
  221. $options = isset($options) ? $options : $this->getEvaluatorOptions($info, $state, $element);
  222. $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element, $options) : $value;
  223. return $this->evaluate($value, $options, $state);
  224. }
  225. /**
  226. * Generates the evaluator $options.
  227. */
  228. protected function getEvaluatorOptions($info, $state, $element) {
  229. $cache = rules_get_cache();
  230. $languages = language_list();
  231. $info += array(
  232. 'cleaning callback' => isset($cache['data info'][$info['type']]['cleaning callback']) ? $cache['data info'][$info['type']]['cleaning callback'] : FALSE,
  233. 'sanitize' => FALSE,
  234. );
  235. $options = array_filter(array(
  236. 'language' => $info['#langcode'] != LANGUAGE_NONE && isset($languages[$info['#langcode']]) ? $languages[$info['#langcode']] : NULL,
  237. 'callback' => $info['cleaning callback'],
  238. 'sanitize' => $info['sanitize'],
  239. ));
  240. return $options;
  241. }
  242. /**
  243. * Overriden to prepare input evaluator processors. The setting is expected
  244. * to be the input value to be evaluated later on and is replaced by the
  245. * suiting processor.
  246. */
  247. public static function prepareSetting(&$setting, $param_info, $var_info = array()) {
  248. $processor = NULL;
  249. foreach (self::evaluators($param_info, FALSE) as $name => $info) {
  250. $object = new $info['class']($setting, $param_info, $var_info, $processor);
  251. $processor = $object->getPreparedValue();
  252. }
  253. $setting = $processor;
  254. }
  255. protected function getPreparedValue() {
  256. return isset($this->setting) ? $this : $this->processor;
  257. }
  258. /**
  259. * Overriden to just attach the help() of evaluators.
  260. */
  261. public static function attachForm(&$form, $settings, $param_info, $var_info, $access_check = TRUE) {
  262. foreach (self::evaluators($param_info, $access_check) as $name => $info) {
  263. $form['help'][$name] = call_user_func(array($info['class'], 'help'), $var_info, $param_info);
  264. $form['help'][$name]['#weight'] = $info['weight'];
  265. }
  266. }
  267. /**
  268. * Returns all input evaluators that can be applied to the parameters needed
  269. * type.
  270. */
  271. public static function evaluators($param_info = NULL, $access_check = TRUE) {
  272. return parent::processors($param_info, $access_check, 'evaluator_info');
  273. }
  274. /**
  275. * Overridden to default to our hook, thus being equivalent to
  276. * self::evaluators().
  277. */
  278. public static function processors($param_info = NULL, $access_check = TRUE, $hook = 'evaluator_info') {
  279. return parent::processors($param_info, $access_check, $hook);
  280. }
  281. /**
  282. * Prepares the evalution, e.g. to determine whether the input evaluator has
  283. * been used. If this evaluator should be skipped just unset $this->setting.
  284. *
  285. * @param $text
  286. * The text to evaluate later on.
  287. * @param $variables
  288. * An array of info about available variables.
  289. * @param $param_info
  290. * (optional) An array of information about the handled parameter value.
  291. * For backward compatibility, this parameter is not required.
  292. */
  293. abstract public function prepare($text, $variables);
  294. /**
  295. * Apply the input evaluator.
  296. *
  297. * @param $text
  298. * The text to evaluate.
  299. * @param $options
  300. * A keyed array of settings and flags to control the processing.
  301. * Supported options are:
  302. * - language: A language object to be used when processing.
  303. * - callback: A callback function that will be used to post-process
  304. * replacements that might be incorporated, so they can be cleaned in a
  305. * certain way.
  306. * - sanitize: A boolean flag indicating whether incorporated replacements
  307. * should be sanitized.
  308. * @param RulesState
  309. * The rules evaluation state.
  310. *
  311. * @return
  312. * The evaluated text.
  313. */
  314. abstract public function evaluate($text, $options, RulesState $state);
  315. /**
  316. * Provide some usage help for the evaluator.
  317. *
  318. * @param $variables
  319. * An array of info about available variables.
  320. * @param $param_info
  321. * (optional) An array of information about the handled parameter value.
  322. * For backward compatibility, this parameter is not required.
  323. *
  324. * @return
  325. * A renderable array.
  326. */
  327. public static function help($variables) {
  328. return array();
  329. }
  330. }