Index: branches/5.2.x/composer.lock =================================================================== diff -u -N -r16761 -r16769 --- branches/5.2.x/composer.lock (.../composer.lock) (revision 16761) +++ branches/5.2.x/composer.lock (.../composer.lock) (revision 16769) @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "982948269c77150f85ea5efd93aef365", + "content-hash": "e44d657cbe6f1c15496b087609a60d98", "packages": [ { "name": "ircmaxell/password-compat", @@ -53,6 +53,56 @@ "time": "2014-11-20T16:49:30+00:00" }, { + "name": "mtdowling/cron-expression", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/in-portal/cron-expression", + "reference": "c4dda94f8f13f5447aa82e9b9528de1115448c74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/in-portal/cron-expression/zipball/c4dda94f8f13f5447aa82e9b9528de1115448c74", + "reference": "c4dda94f8f13f5447aa82e9b9528de1115448c74", + "shasum": "" + }, + "require": { + "php": ">=5.4.7" + }, + "require-dev": { + "yoast/phpunit-polyfills": "^1.0" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/Cron/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "abandoned": "dragonmantank/cron-expression", + "time": "2022-11-08T11:57:39+00:00" + }, + { "name": "paragonie/random_compat", "version": "v2.0.21", "source": { @@ -382,28 +432,29 @@ }, { "name": "aik099/phpunit-mink", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/minkphp/phpunit-mink.git", - "reference": "68b94432ac12ad4f714ef540037396aeb369e230" + "reference": "2f80d83eb1ed7da1c5c96b1eb7bab6687193493e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/phpunit-mink/zipball/68b94432ac12ad4f714ef540037396aeb369e230", - "reference": "68b94432ac12ad4f714ef540037396aeb369e230", + "url": "https://api.github.com/repos/minkphp/phpunit-mink/zipball/2f80d83eb1ed7da1c5c96b1eb7bab6687193493e", + "reference": "2f80d83eb1ed7da1c5c96b1eb7bab6687193493e", "shasum": "" }, "require": { "behat/mink": "~1.6@dev", "behat/mink-selenium2-driver": "~1.2", - "php": ">=5.3.2", - "phpunit/phpunit": "~4|~5", + "php": ">=5.4.7", + "phpunit/phpunit": ">=4.8.35 <5|>=5.4.3", "symfony/event-dispatcher": "~2.4|~3.0" }, "require-dev": { "aik099/coding-standard": "dev-master", - "mockery/mockery": "~0.9" + "mockery/mockery": "~0.9|^1.5", + "yoast/phpunit-polyfills": "^1.0" }, "type": "library", "extra": { @@ -440,9 +491,9 @@ ], "support": { "issues": "https://github.com/minkphp/phpunit-mink/issues", - "source": "https://github.com/minkphp/phpunit-mink/tree/v2.2.0" + "source": "https://github.com/minkphp/phpunit-mink/tree/v2.3.0" }, - "time": "2016-06-26T09:07:47+00:00" + "time": "2022-11-24T15:03:10+00:00" }, { "name": "behat/mink", @@ -2193,16 +2244,17 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "mtdowling/cron-expression": 20, "aik099/coding-standard": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.4.0" + "php": ">=5.4.7" }, "platform-dev": [], "platform-overrides": { - "php": "5.4.0" + "php": "5.4.7" }, "plugin-api-version": "2.2.0" } Index: branches/5.2.x/composer.json =================================================================== diff -u -N -r16761 -r16769 --- branches/5.2.x/composer.json (.../composer.json) (revision 16761) +++ branches/5.2.x/composer.json (.../composer.json) (revision 16769) @@ -1,10 +1,11 @@ { "name": "intechnic/in-portal", "require": { - "php": ">=5.4.0", + "php": ">=5.4.7", "paragonie/random_compat": "^2.0", "symfony/polyfill-php55": "^1.19", - "symfony/polyfill-php56": "^1.19" + "symfony/polyfill-php56": "^1.19", + "mtdowling/cron-expression": "dev-master" }, "require-dev": { "behat/mink": "^1.7", @@ -14,9 +15,16 @@ "phpspec/prophecy": "^1.10", "aik099/coding-standard": "dev-in-portal" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/in-portal/cron-expression", + "no-api": true + } + ], "config": { "platform": { - "php": "5.4.0" + "php": "5.4.7" } } } Index: branches/5.2.x/core/units/helpers/cron_helper.php =================================================================== diff -u -N -r16513 -r16769 --- branches/5.2.x/core/units/helpers/cron_helper.php (.../cron_helper.php) (revision 16513) +++ branches/5.2.x/core/units/helpers/cron_helper.php (.../cron_helper.php) (revision 16769) @@ -1,6 +1,6 @@ fieldFactory = new FieldFactory(); + } + + /** * Defines possible cron fields and their matching priority * * @var Array @@ -193,6 +214,26 @@ } /** + * Get an instance of a field object for a cron expression field type. + * + * @param integer $field_type Field type. + * + * @return FieldInterface + */ + protected function getField($field_type) + { + $field_mapping = array( + self::MINUTE => 0, + self::HOUR => 1, + self::DAY => 2, + self::MONTH => 3, + self::WEEKDAY => 4, + ); + + return $this->fieldFactory->getField($field_mapping[$field_type]); + } + + /** * Creates virtual fields for given unit * * @param string $prefix @@ -252,13 +293,12 @@ { $validated = true; $combined_value = Array (); - $cron_field = new kCronField(); foreach ($this->fieldTypes as $field_type) { $field_name = $this->_getFieldNameByType($field_type, $field_prefix); $value = preg_replace('/\s+/s', '', mb_strtoupper($object->GetDBField($field_name))); - if ( $cron_field->validate($field_type, $value) ) { + if ( $this->getField($field_type)->validate($value) ) { $object->SetDBField($field_name, $value); } else { @@ -276,36 +316,6 @@ } /** - * Replaces aliases in the field - * - * @param int $field_type - * @param string $value - * @return string - * @access public - */ - public static function replaceAliases($field_type, $value) - { - $replacements = Array (); - $value = mb_strtolower($value); - - if ( $field_type == self::MONTH ) { - $replacements = Array ( - 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, - 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12, - ); - } - elseif ( $field_type == self::WEEKDAY ) { - $replacements = Array ('sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3, 'thu' => 4, 'fri' => 5, 'sat' => 6); - } - - if ( $replacements ) { - $value = str_replace(array_keys($replacements), array_values($replacements), $value); - } - - return $value; - } - - /** * Returns next (after given one or now) timestamp matching given cron expression * * @param string $expression @@ -322,359 +332,15 @@ $date = TIMENOW; } - $next_run = strtotime('-' . (int)adodb_date('s', $date) . ' seconds', $date); - $expression_parts = explode(' ', $expression); + $cron = CronExpression::factory($expression); - $cron_field = new kCronField(); + $date_formatted = date('Y-m-d H:i:s', $date); - // set a hard limit to bail on an impossible date - for ($i = 0; $i < 1000; $i++) { - foreach ($this->fieldTypes as $field_type) { - $matched = false; - $part = $expression_parts[$field_type - 1]; - - // check if this is singular or a list - if ( strpos($part, ',') === false ) { - $matched = $cron_field->match($field_type, $next_run, $part); - } - else { - $rules = explode(',', $part); - - foreach ($rules as $rule) { - if ( $cron_field->match($field_type, $next_run, $rule) ) { - $matched = true; - break; - } - } - } - - // if the field is not matched, then start over - if ( !$matched ) { - $next_run = $cron_field->increment($field_type, $next_run, $inverse); - continue 2; - } - } - - // Skip this match if needed - if ( (!$allow_current_date && $next_run == $date) ) { - $next_run = $cron_field->increment(self::MINUTE, $next_run, $inverse); - continue; - } - - return $next_run; + if ( $inverse ) { + return $cron->getPreviousRunDate($date_formatted, 0, $allow_current_date)->format('U'); } - throw new RuntimeException('Impossible CRON expression'); + return $cron->getNextRunDate($date_formatted, 0, $allow_current_date)->format('U'); } -} - -class kCronField extends kBase { - - /** - * Validates field value - * - * @param int $field_type - * @param string $value - * @param bool $asterisk_allowed - * @return bool - * @access public - */ - public function validate($field_type, $value, $asterisk_allowed = true) - { - $rules = explode(',', kCronHelper::replaceAliases($field_type, $value)); - - foreach ($rules as $rule) { - if ( $this->_isIncrementRule($rule) ) { - if ( !$this->_validateIncrementRule($field_type, $rule) ) { - return false; - } - } - elseif ( $this->_isRangeRule($rule) ) { - if ( !$this->_validateRangeRule($field_type, $rule) ) { - return false; - } - } - elseif ( !$this->_validateNumberRule($field_type, $rule, $asterisk_allowed) ) { - return false; - } - } - - return true; - } - - /** - * Determines if expression is range - * - * @param string $rule - * @return bool - * @access protected - */ - protected function _isRangeRule($rule) - { - return strpos($rule, '-') !== false; - } - - /** - * Validates range rule - * - * @param int $field_type - * @param string $rule - * @return bool - * @access protected - */ - protected function _validateRangeRule($field_type, $rule) - { - $parts = explode('-', $rule); - - if ( count($parts) != 2 ) { - return false; - } - - $min_value = $parts[0]; - $max_value = $parts[1]; - - if ( !$this->_validateNumberRule($field_type, $min_value) || !$this->_validateNumberRule($field_type, $max_value) || $min_value >= $max_value ) { - return false; - } - - return true; - } - - /** - * Determines if expression is increment - * - * @param string $rule - * @return bool - * @access protected - */ - protected function _isIncrementRule($rule) - { - return strpos($rule, '/') !== false; - } - - /** - * Validates increment rule - * - * @param int $field_type - * @param string $rule - * @return bool - * @access protected - */ - protected function _validateIncrementRule($field_type, $rule) - { - $parts = explode('/', $rule); - - if ( count($parts) != 2 ) { - return false; - } - - $interval = $parts[0]; - $increment = $parts[1]; - - if ( $this->_isRangeRule($interval) ) { - if ( !$this->_validateRangeRule($field_type, $interval) ) { - return false; - } - } - elseif ( !$this->_validateNumberRule($field_type, $interval, true) ) { - return false; - } - - if ( !$this->_validateNumberRule($field_type, $increment) ) { - return false; - } - - return true; - } - - /** - * Validates, that number within range OR an asterisk is given - * - * @param int $field_type - * @param string $rule - * @param bool $asterisk_allowed - * @return bool - * @access protected - */ - protected function _validateNumberRule($field_type, $rule, $asterisk_allowed = false) - { - if ( "$rule" === '*' ) { - return $asterisk_allowed; - } - - $int_rule = (int)$rule; - - if ( !is_numeric($rule) || "$int_rule" !== "$rule" ) { - // not integer - return false; - } - - $range_mapping = Array ( - kCronHelper::MINUTE => Array ('from' => 0, 'to' => 59), - kCronHelper::HOUR => Array ('from' => 0, 'to' => 23), - kCronHelper::DAY => Array ('from' => 1, 'to' => 31), - kCronHelper::MONTH => Array ('from' => 1, 'to' => 12), - kCronHelper::WEEKDAY => Array ('from' => 0, 'to' => 7), - ); - - return $int_rule >= $range_mapping[$field_type]['from'] && $int_rule <= $range_mapping[$field_type]['to']; - } - - /** - * Tries to match given date to given expression - * - * @param int $field_type - * @param int $date - * @param string $rule - * @return bool - * @access public - */ - public function match($field_type, $date, $rule) - { - $date_part = $this->_getDatePart($field_type, $date, $rule); - - if ( $this->_isIncrementRule($rule) ) { - return $this->_isInIncrement($date_part, $rule); - } - elseif ( $this->_isRangeRule($rule) ) { - return $this->_isInRange($date_part, $rule); - } - - return $rule == '*' || $date_part == $rule; - } - - /** - * Returns only part, needed based on field type of date in timestamp - * - * @param int $field_type - * @param int $date - * @param string $rule - * @return int - * @access protected - */ - protected function _getDatePart($field_type, $date, $rule) - { - $mapping = Array ( - kCronHelper::MINUTE => 'i', - kCronHelper::HOUR => 'G', - kCronHelper::DAY => 'j', - kCronHelper::MONTH => 'n', - kCronHelper::WEEKDAY => 'N', - ); - - if ( $field_type == kCronHelper::WEEKDAY ) { - // Test to see which Sunday to use -- 0 == 7 == Sunday - $mapping[$field_type] = in_array(7, str_split($rule)) ? 'N' : 'w'; - } - - return (int)adodb_date($mapping[$field_type], $date); - } - - /** - * Test if a value is within a range - * - * @param string $date_value Set date value - * @param string $rule Value to test - * @return bool - * @access protected - */ - protected function _isInRange($date_value, $rule) - { - $parts = array_map('trim', explode('-', $rule, 2)); - - return $date_value >= $parts[0] && $date_value <= $parts[1]; - } - - /** - * Test if a value is within an increments of ranges (offset[-to]/step size) - * - * @param string $date_value Set date value - * @param string $rule Value to test - * @return bool - * @access protected - */ - protected function _isInIncrement($date_value, $rule) - { - $parts = array_map('trim', explode('/', $rule, 2)); - $stepSize = isset($parts[1]) ? $parts[1] : 0; - - if ( $parts[0] == '*' || $parts[0] == 0 ) { - return (int)$date_value % $stepSize == 0; - } - - $range = explode('-', $parts[0], 2); - $offset = $range[0]; - $to = isset($range[1]) ? $range[1] : $date_value; - - // Ensure that the date value is within the range - if ( $date_value < $offset || $date_value > $to ) { - return false; - } - - for ($i = $offset; $i <= $to; $i += $stepSize) { - if ( $i == $date_value ) { - return true; - } - } - - return false; - } - - /** - * Increments/decrements given date for 1 unit based on field type - * - * @param int $field_type - * @param int $date - * @param bool $inverse - * @return int - * @access public - */ - public function increment($field_type, $date, $inverse = false) - { - $mapping = Array ( - kCronHelper::MINUTE => '1 minute', - kCronHelper::HOUR => '1 hour', - kCronHelper::DAY => '1 day', - kCronHelper::MONTH => '1 month', - kCronHelper::WEEKDAY => '1 day', - ); - - return $this->_resetTime($field_type, strtotime(($inverse ? '-' : '+') . $mapping[$field_type], $date), $inverse); - } - - /** - * Resets time based on field type - * - * @param int $field_type - * @param int $date - * @param bool $inverse - * @return int - * @access public - */ - protected function _resetTime($field_type, $date, $inverse = false) - { - if ( $field_type == kCronHelper::MONTH || $field_type == kCronHelper::WEEKDAY || $field_type == kCronHelper::DAY ) { - if ( $inverse ) { - $date = strtotime(adodb_date('Y-m-d 23:59:59', $date)); - // set time 23:59:00 - } - else { - // set time 00:00:00 - $date = strtotime(adodb_date('Y-m-d 00:00:00', $date)); - } - } - elseif ( $field_type == kCronHelper::HOUR ) { - if ( $inverse ) { - // set time :59:00 - $date = strtotime(adodb_date('Y-m-d H:59:59', $date)); - } - else { - // set time :00:00 - $date = strtotime(adodb_date('Y-m-d H:00:00', $date)); - } - } - - return $date; - } -} \ No newline at end of file +}