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

https://gitlab.com/madwanz64/laravel · PHP · 286 lines · 160 code · 39 blank · 87 comment · 45 complexity · 8481dab8ee17538b4eecf3afb37452fd MD5 · raw file

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