/src/RuleBuilder/Variable.php

https://github.com/bobthecow/Ruler · PHP · 427 lines · 206 code · 53 blank · 168 comment · 3 complexity · 53f149fb57ceb2d18b332b189a9bcbd7 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Ruler package, an OpenSky project.
  4. *
  5. * (c) 2013 OpenSky Project Inc
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Ruler\RuleBuilder;
  11. use Ruler\Operator;
  12. use Ruler\Operator\VariableOperator;
  13. use Ruler\RuleBuilder;
  14. use Ruler\Variable as BaseVariable;
  15. use Ruler\VariableOperand;
  16. /**
  17. * A propositional Variable.
  18. *
  19. * Variables are placeholders in Propositions and Comparison Operators. During
  20. * evaluation, they are replaced with terminal Values, either from the Variable
  21. * default or from the current Context.
  22. *
  23. * The RuleBuilder Variable extends the base Variable class with a fluent
  24. * interface for creating VariableProperties, Operators and Rules without all
  25. * kinds of awkward object instantiation.
  26. *
  27. * @author Justin Hileman <justin@justinhileman.info>
  28. */
  29. class Variable extends BaseVariable implements \ArrayAccess
  30. {
  31. private $ruleBuilder;
  32. private array $properties = [];
  33. /**
  34. * RuleBuilder Variable constructor.
  35. *
  36. * @param RuleBuilder $ruleBuilder
  37. * @param string $name Variable name (default: null)
  38. * @param mixed $value Default Variable value (default: null)
  39. */
  40. public function __construct(RuleBuilder $ruleBuilder, string $name = null, $value = null)
  41. {
  42. $this->ruleBuilder = $ruleBuilder;
  43. parent::__construct($name, $value);
  44. }
  45. /**
  46. * Get the RuleBuilder instance set on this Variable.
  47. */
  48. public function getRuleBuilder(): RuleBuilder
  49. {
  50. return $this->ruleBuilder;
  51. }
  52. /**
  53. * Get a VariableProperty for accessing methods, indexes and properties of
  54. * the current variable.
  55. *
  56. * @param string $name Property name
  57. * @param mixed $value The default VariableProperty value
  58. */
  59. public function getProperty(string $name, $value = null): VariableProperty
  60. {
  61. if (!isset($this->properties[$name])) {
  62. $this->properties[$name] = new VariableProperty($this, $name, $value);
  63. }
  64. return $this->properties[$name];
  65. }
  66. /**
  67. * Fluent interface method for checking whether a VariableProperty has been defined.
  68. *
  69. * @param string $name Property name
  70. */
  71. public function offsetExists($name): bool
  72. {
  73. return isset($this->properties[$name]);
  74. }
  75. /**
  76. * Fluent interface method for creating or accessing VariableProperties.
  77. *
  78. * @see getProperty
  79. *
  80. * @param string $name Property name
  81. */
  82. public function offsetGet($name): VariableProperty
  83. {
  84. return $this->getProperty($name);
  85. }
  86. /**
  87. * Fluent interface method for setting default a VariableProperty value.
  88. *
  89. * @see setValue
  90. *
  91. * @param string $name Property name
  92. * @param mixed $value The default Variable value
  93. */
  94. public function offsetSet($name, $value): void
  95. {
  96. $this->getProperty($name)->setValue($value);
  97. }
  98. /**
  99. * Fluent interface method for removing a VariableProperty reference.
  100. *
  101. * @param string $name Property name
  102. */
  103. public function offsetUnset($name): void
  104. {
  105. unset($this->properties[$name]);
  106. }
  107. /**
  108. * Fluent interface helper to create a contains comparison operator.
  109. *
  110. * @param mixed $variable Right side of comparison operator
  111. */
  112. public function stringContains($variable): Operator\StringContains
  113. {
  114. return new Operator\StringContains($this, $this->asVariable($variable));
  115. }
  116. /**
  117. * Fluent interface helper to create a contains comparison operator.
  118. *
  119. * @param mixed $variable Right side of comparison operator
  120. */
  121. public function stringDoesNotContain($variable): Operator\StringDoesNotContain
  122. {
  123. return new Operator\StringDoesNotContain($this, $this->asVariable($variable));
  124. }
  125. /**
  126. * Fluent interface helper to create a insensitive contains comparison operator.
  127. *
  128. * @param mixed $variable Right side of comparison operator
  129. */
  130. public function stringContainsInsensitive($variable): Operator\StringContainsInsensitive
  131. {
  132. return new Operator\StringContainsInsensitive($this, $this->asVariable($variable));
  133. }
  134. /**
  135. * Fluent interface helper to create a GreaterThan comparison operator.
  136. *
  137. * @param mixed $variable Right side of comparison operator
  138. */
  139. public function greaterThan($variable): Operator\GreaterThan
  140. {
  141. return new Operator\GreaterThan($this, $this->asVariable($variable));
  142. }
  143. /**
  144. * Fluent interface helper to create a GreaterThanOrEqualTo comparison operator.
  145. *
  146. * @param mixed $variable Right side of comparison operator
  147. */
  148. public function greaterThanOrEqualTo($variable): Operator\GreaterThanOrEqualTo
  149. {
  150. return new Operator\GreaterThanOrEqualTo($this, $this->asVariable($variable));
  151. }
  152. /**
  153. * Fluent interface helper to create a LessThan comparison operator.
  154. *
  155. * @param mixed $variable Right side of comparison operator
  156. */
  157. public function lessThan($variable): Operator\LessThan
  158. {
  159. return new Operator\LessThan($this, $this->asVariable($variable));
  160. }
  161. /**
  162. * Fluent interface helper to create a LessThanOrEqualTo comparison operator.
  163. *
  164. * @param mixed $variable Right side of comparison operator
  165. */
  166. public function lessThanOrEqualTo($variable): Operator\LessThanOrEqualTo
  167. {
  168. return new Operator\LessThanOrEqualTo($this, $this->asVariable($variable));
  169. }
  170. /**
  171. * Fluent interface helper to create a EqualTo comparison operator.
  172. *
  173. * @param mixed $variable Right side of comparison operator
  174. */
  175. public function equalTo($variable): Operator\EqualTo
  176. {
  177. return new Operator\EqualTo($this, $this->asVariable($variable));
  178. }
  179. /**
  180. * Fluent interface helper to create a NotEqualTo comparison operator.
  181. *
  182. * @param mixed $variable Right side of comparison operator
  183. */
  184. public function notEqualTo($variable): Operator\NotEqualTo
  185. {
  186. return new Operator\NotEqualTo($this, $this->asVariable($variable));
  187. }
  188. /**
  189. * Fluent interface helper to create a SameAs comparison operator.
  190. *
  191. * @param mixed $variable Right side of comparison operator
  192. */
  193. public function sameAs($variable): Operator\SameAs
  194. {
  195. return new Operator\SameAs($this, $this->asVariable($variable));
  196. }
  197. /**
  198. * Fluent interface helper to create a NotSameAs comparison operator.
  199. *
  200. * @param mixed $variable Right side of comparison operator
  201. */
  202. public function notSameAs($variable): Operator\NotSameAs
  203. {
  204. return new Operator\NotSameAs($this, $this->asVariable($variable));
  205. }
  206. public function union(...$variables): self
  207. {
  208. return $this->applySetOperator('Union', $variables);
  209. }
  210. public function intersect(...$variables): self
  211. {
  212. return $this->applySetOperator('Intersect', $variables);
  213. }
  214. public function complement(...$variables): self
  215. {
  216. return $this->applySetOperator('Complement', $variables);
  217. }
  218. public function symmetricDifference(...$variables): self
  219. {
  220. return $this->applySetOperator('SymmetricDifference', $variables);
  221. }
  222. public function min(): self
  223. {
  224. return $this->wrap(new Operator\Min($this));
  225. }
  226. public function max(): self
  227. {
  228. return $this->wrap(new Operator\Max($this));
  229. }
  230. public function containsSubset($variable): Operator\ContainsSubset
  231. {
  232. return new Operator\ContainsSubset($this, $this->asVariable($variable));
  233. }
  234. public function doesNotContainSubset($variable): Operator\DoesNotContainSubset
  235. {
  236. return new Operator\DoesNotContainSubset($this, $this->asVariable($variable));
  237. }
  238. /**
  239. * Fluent interface helper to create a contains comparison operator.
  240. *
  241. * @param mixed $variable Right side of comparison operator
  242. */
  243. public function setContains($variable): Operator\SetContains
  244. {
  245. return new Operator\SetContains($this, $this->asVariable($variable));
  246. }
  247. /**
  248. * Fluent interface helper to create a contains comparison operator.
  249. *
  250. * @param mixed $variable Right side of comparison operator
  251. */
  252. public function setDoesNotContain($variable): Operator\SetDoesNotContain
  253. {
  254. return new Operator\SetDoesNotContain($this, $this->asVariable($variable));
  255. }
  256. public function add($variable): self
  257. {
  258. return $this->wrap(new Operator\Addition($this, $this->asVariable($variable)));
  259. }
  260. public function divide($variable): self
  261. {
  262. return $this->wrap(new Operator\Division($this, $this->asVariable($variable)));
  263. }
  264. public function modulo($variable): self
  265. {
  266. return $this->wrap(new Operator\Modulo($this, $this->asVariable($variable)));
  267. }
  268. public function multiply($variable): self
  269. {
  270. return $this->wrap(new Operator\Multiplication($this, $this->asVariable($variable)));
  271. }
  272. public function subtract($variable): self
  273. {
  274. return $this->wrap(new Operator\Subtraction($this, $this->asVariable($variable)));
  275. }
  276. public function negate(): self
  277. {
  278. return $this->wrap(new Operator\Negation($this));
  279. }
  280. public function ceil(): self
  281. {
  282. return $this->wrap(new Operator\Ceil($this));
  283. }
  284. public function floor(): self
  285. {
  286. return $this->wrap(new Operator\Floor($this));
  287. }
  288. public function exponentiate($variable): self
  289. {
  290. return $this->wrap(new Operator\Exponentiate($this, $this->asVariable($variable)));
  291. }
  292. /**
  293. * Private helper to retrieve a Variable instance for the given $variable.
  294. *
  295. * @param mixed $variable BaseVariable instance or value
  296. */
  297. private function asVariable($variable): BaseVariable
  298. {
  299. return ($variable instanceof BaseVariable) ? $variable : new BaseVariable(null, $variable);
  300. }
  301. /**
  302. * Private helper to apply a set operator.
  303. */
  304. private function applySetOperator(string $name, array $args): self
  305. {
  306. $reflection = new \ReflectionClass('\\Ruler\\Operator\\'.$name);
  307. \array_unshift($args, $this);
  308. return $this->wrap($reflection->newInstanceArgs($args));
  309. }
  310. /**
  311. * Private helper to wrap a VariableOperator in a Variable instance.
  312. */
  313. private function wrap(VariableOperator $op): self
  314. {
  315. return new self($this->ruleBuilder, null, $op);
  316. }
  317. /**
  318. * Fluent interface helper to create a endsWith comparison operator.
  319. *
  320. * @param mixed $variable Right side of comparison operator
  321. */
  322. public function endsWith($variable): Operator\EndsWith
  323. {
  324. return new Operator\EndsWith($this, $this->asVariable($variable));
  325. }
  326. /**
  327. * Fluent interface helper to create a endsWith insensitive comparison operator.
  328. *
  329. * @param mixed $variable Right side of comparison operator
  330. */
  331. public function endsWithInsensitive($variable): Operator\EndsWithInsensitive
  332. {
  333. return new Operator\EndsWithInsensitive($this, $this->asVariable($variable));
  334. }
  335. /**
  336. * Fluent interface helper to create a startsWith comparison operator.
  337. *
  338. * @param mixed $variable Right side of comparison operator
  339. */
  340. public function startsWith($variable): Operator\StartsWith
  341. {
  342. return new Operator\StartsWith($this, $this->asVariable($variable));
  343. }
  344. /**
  345. * Fluent interface helper to create a startsWith insensitive comparison operator.
  346. *
  347. * @param mixed $variable Right side of comparison operator
  348. */
  349. public function startsWithInsensitive($variable): Operator\StartsWithInsensitive
  350. {
  351. return new Operator\StartsWithInsensitive($this, $this->asVariable($variable));
  352. }
  353. /**
  354. * Magic method to apply operators registered with RuleBuilder.
  355. *
  356. * @see RuleBuilder::registerOperatorNamespace
  357. *
  358. * @throws \LogicException if operator is not registered
  359. *
  360. * @return Operator|self
  361. */
  362. public function __call(string $name, array $args)
  363. {
  364. $reflection = new \ReflectionClass($this->ruleBuilder->findOperator($name));
  365. $args = \array_map([$this, 'asVariable'], $args);
  366. \array_unshift($args, $this);
  367. $op = $reflection->newInstanceArgs($args);
  368. if ($op instanceof VariableOperand) {
  369. return $this->wrap($op);
  370. } else {
  371. return $op;
  372. }
  373. }
  374. }