View file vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php

File size: 7.83Kb
  1. <?php
  2.  
  3. namespace Cron;
  4.  
  5. /**
  6. * Abstract CRON expression field
  7. */
  8. abstract class AbstractField implements FieldInterface
  9. {
  10. /**
  11. * Full range of values that are allowed for this field type
  12. * @var array
  13. */
  14. protected $fullRange = [];
  15.  
  16. /**
  17. * Literal values we need to convert to integers
  18. * @var array
  19. */
  20. protected $literals = [];
  21.  
  22. /**
  23. * Start value of the full range
  24. * @var integer
  25. */
  26. protected $rangeStart;
  27.  
  28. /**
  29. * End value of the full range
  30. * @var integer
  31. */
  32. protected $rangeEnd;
  33.  
  34. /**
  35. * Constructor
  36. */
  37. public function __construct()
  38. {
  39. $this->fullRange = range($this->rangeStart, $this->rangeEnd);
  40. }
  41.  
  42. /**
  43. * Check to see if a field is satisfied by a value
  44. *
  45. * @param string $dateValue Date value to check
  46. * @param string $value Value to test
  47. *
  48. * @return bool
  49. */
  50. public function isSatisfied($dateValue, $value)
  51. {
  52. if ($this->isIncrementsOfRanges($value)) {
  53. return $this->isInIncrementsOfRanges($dateValue, $value);
  54. } elseif ($this->isRange($value)) {
  55. return $this->isInRange($dateValue, $value);
  56. }
  57.  
  58. return $value == '*' || $dateValue == $value;
  59. }
  60.  
  61. /**
  62. * Check if a value is a range
  63. *
  64. * @param string $value Value to test
  65. *
  66. * @return bool
  67. */
  68. public function isRange($value)
  69. {
  70. return strpos($value, '-') !== false;
  71. }
  72.  
  73. /**
  74. * Check if a value is an increments of ranges
  75. *
  76. * @param string $value Value to test
  77. *
  78. * @return bool
  79. */
  80. public function isIncrementsOfRanges($value)
  81. {
  82. return strpos($value, '/') !== false;
  83. }
  84.  
  85. /**
  86. * Test if a value is within a range
  87. *
  88. * @param string $dateValue Set date value
  89. * @param string $value Value to test
  90. *
  91. * @return bool
  92. */
  93. public function isInRange($dateValue, $value)
  94. {
  95. $parts = array_map(function($value) {
  96. $value = trim($value);
  97. $value = $this->convertLiterals($value);
  98. return $value;
  99. },
  100. explode('-', $value, 2)
  101. );
  102.  
  103.  
  104. return $dateValue >= $parts[0] && $dateValue <= $parts[1];
  105. }
  106.  
  107. /**
  108. * Test if a value is within an increments of ranges (offset[-to]/step size)
  109. *
  110. * @param string $dateValue Set date value
  111. * @param string $value Value to test
  112. *
  113. * @return bool
  114. */
  115. public function isInIncrementsOfRanges($dateValue, $value)
  116. {
  117. $chunks = array_map('trim', explode('/', $value, 2));
  118. $range = $chunks[0];
  119. $step = isset($chunks[1]) ? $chunks[1] : 0;
  120.  
  121. // No step or 0 steps aren't cool
  122. if (is_null($step) || '0' === $step || 0 === $step) {
  123. return false;
  124. }
  125.  
  126. // Expand the * to a full range
  127. if ('*' == $range) {
  128. $range = $this->rangeStart . '-' . $this->rangeEnd;
  129. }
  130.  
  131. // Generate the requested small range
  132. $rangeChunks = explode('-', $range, 2);
  133. $rangeStart = $rangeChunks[0];
  134. $rangeEnd = isset($rangeChunks[1]) ? $rangeChunks[1] : $rangeStart;
  135.  
  136. if ($rangeStart < $this->rangeStart || $rangeStart > $this->rangeEnd || $rangeStart > $rangeEnd) {
  137. throw new \OutOfRangeException('Invalid range start requested');
  138. }
  139.  
  140. if ($rangeEnd < $this->rangeStart || $rangeEnd > $this->rangeEnd || $rangeEnd < $rangeStart) {
  141. throw new \OutOfRangeException('Invalid range end requested');
  142. }
  143.  
  144. // Steps larger than the range need to wrap around and be handled slightly differently than smaller steps
  145. if ($step >= $this->rangeEnd) {
  146. $thisRange = [$this->fullRange[$step % count($this->fullRange)]];
  147. } else {
  148. $thisRange = range($rangeStart, $rangeEnd, $step);
  149. }
  150.  
  151. return in_array($dateValue, $thisRange);
  152. }
  153.  
  154. /**
  155. * Returns a range of values for the given cron expression
  156. *
  157. * @param string $expression The expression to evaluate
  158. * @param int $max Maximum offset for range
  159. *
  160. * @return array
  161. */
  162. public function getRangeForExpression($expression, $max)
  163. {
  164. $values = array();
  165. $expression = $this->convertLiterals($expression);
  166.  
  167. if (strpos($expression, ',') !== false) {
  168. $ranges = explode(',', $expression);
  169. $values = [];
  170. foreach ($ranges as $range) {
  171. $expanded = $this->getRangeForExpression($range, $this->rangeEnd);
  172. $values = array_merge($values, $expanded);
  173. }
  174. return $values;
  175. }
  176.  
  177. if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) {
  178. if (!$this->isIncrementsOfRanges($expression)) {
  179. list ($offset, $to) = explode('-', $expression);
  180. $offset = $this->convertLiterals($offset);
  181. $to = $this->convertLiterals($to);
  182. $stepSize = 1;
  183. }
  184. else {
  185. $range = array_map('trim', explode('/', $expression, 2));
  186. $stepSize = isset($range[1]) ? $range[1] : 0;
  187. $range = $range[0];
  188. $range = explode('-', $range, 2);
  189. $offset = $range[0];
  190. $to = isset($range[1]) ? $range[1] : $max;
  191. }
  192. $offset = $offset == '*' ? $this->rangeStart : $offset;
  193. if ($stepSize >= $this->rangeEnd) {
  194. $values = [$this->fullRange[$stepSize % count($this->fullRange)]];
  195. } else {
  196. for ($i = $offset; $i <= $to; $i += $stepSize) {
  197. $values[] = (int)$i;
  198. }
  199. }
  200. sort($values);
  201. }
  202. else {
  203. $values = array($expression);
  204. }
  205.  
  206. return $values;
  207. }
  208.  
  209. /**
  210. * Convert literal
  211. *
  212. * @param string $value
  213. * @return string
  214. */
  215. protected function convertLiterals($value)
  216. {
  217. if (count($this->literals)) {
  218. $key = array_search($value, $this->literals);
  219. if ($key !== false) {
  220. return (string) $key;
  221. }
  222. }
  223.  
  224. return $value;
  225. }
  226.  
  227. /**
  228. * Checks to see if a value is valid for the field
  229. *
  230. * @param string $value
  231. * @return bool
  232. */
  233. public function validate($value)
  234. {
  235. $value = $this->convertLiterals($value);
  236.  
  237. // All fields allow * as a valid value
  238. if ('*' === $value) {
  239. return true;
  240. }
  241.  
  242. if (strpos($value, '/') !== false) {
  243. list($range, $step) = explode('/', $value);
  244. return $this->validate($range) && filter_var($step, FILTER_VALIDATE_INT);
  245. }
  246.  
  247. // Validate each chunk of a list individually
  248. if (strpos($value, ',') !== false) {
  249. foreach (explode(',', $value) as $listItem) {
  250. if (!$this->validate($listItem)) {
  251. return false;
  252. }
  253. }
  254. return true;
  255. }
  256.  
  257. if (strpos($value, '-') !== false) {
  258. if (substr_count($value, '-') > 1) {
  259. return false;
  260. }
  261.  
  262. $chunks = explode('-', $value);
  263. $chunks[0] = $this->convertLiterals($chunks[0]);
  264. $chunks[1] = $this->convertLiterals($chunks[1]);
  265.  
  266. if ('*' == $chunks[0] || '*' == $chunks[1]) {
  267. return false;
  268. }
  269.  
  270. return $this->validate($chunks[0]) && $this->validate($chunks[1]);
  271. }
  272.  
  273. if (!is_numeric($value)) {
  274. return false;
  275. }
  276.  
  277. if (is_float($value) || strpos($value, '.') !== false) {
  278. return false;
  279. }
  280.  
  281. // We should have a numeric by now, so coerce this into an integer
  282. $value = (int) $value;
  283.  
  284. return in_array($value, $this->fullRange, true);
  285. }
  286. }