Replace use of CronjobUtil by dragonmantank/cron-expression
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 4 Aug 2022 12:08:18 +0000 (14:08 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 4 Aug 2022 12:08:18 +0000 (14:08 +0200)
wcfsetup/install/files/lib/acp/form/CronjobAddForm.class.php
wcfsetup/install/files/lib/data/cronjob/Cronjob.class.php
wcfsetup/install/files/lib/system/package/plugin/CronjobPackageInstallationPlugin.class.php

index 57158829052976e984b57aa4e1174e41c8910daf..309d1db16b7a55f20a0b2223cab2c58246e2ff4f 100755 (executable)
@@ -2,16 +2,16 @@
 
 namespace wcf\acp\form;
 
+use Cron\CronExpression;
+use Cron\FieldFactory;
 use wcf\data\cronjob\Cronjob;
 use wcf\data\cronjob\CronjobAction;
 use wcf\data\cronjob\CronjobEditor;
 use wcf\form\AbstractForm;
-use wcf\system\exception\SystemException;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\I18nHandler;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
-use wcf\util\CronjobUtil;
 use wcf\util\StringUtil;
 
 /**
@@ -149,22 +149,21 @@ class CronjobAddForm extends AbstractForm
             }
         }
 
-        try {
-            CronjobUtil::validate(
-                $this->startMinute,
-                $this->startHour,
-                $this->startDom,
-                $this->startMonth,
-                $this->startDow
-            );
-        } catch (SystemException $e) {
-            // extract field name
-            $fieldName = '';
-            if (\preg_match("/cronjob attribute '(.*)'/", $e->getMessage(), $match)) {
-                $fieldName = $match[1];
-            }
-
-            throw new UserInputException($fieldName, 'invalid');
+        $fieldFactory = new FieldFactory();
+        if (!$fieldFactory->getField(CronExpression::MINUTE)->validate($this->startMinute)) {
+            throw new UserInputException('startMinute', 'invalid');
+        }
+        if (!$fieldFactory->getField(CronExpression::HOUR)->validate($this->startHour)) {
+            throw new UserInputException('startHour', 'invalid');
+        }
+        if (!$fieldFactory->getField(CronExpression::DAY)->validate($this->startDom)) {
+            throw new UserInputException('startDom', 'invalid');
+        }
+        if (!$fieldFactory->getField(CronExpression::MONTH)->validate($this->startMonth)) {
+            throw new UserInputException('startMonth', 'invalid');
+        }
+        if (!$fieldFactory->getField(CronExpression::WEEKDAY)->validate($this->startDow)) {
+            throw new UserInputException('startDow', 'invalid');
         }
     }
 
index 75cc6fb31f6fea8707d97700557e2779bc54bc2d..8cf510251c2be4f37faf6a6c1ab41a90f66648e0 100644 (file)
@@ -2,10 +2,10 @@
 
 namespace wcf\data\cronjob;
 
+use Cron\CronExpression;
 use wcf\data\DatabaseObject;
 use wcf\data\TDatabaseObjectOptions;
 use wcf\system\WCF;
-use wcf\util\CronjobUtil;
 
 /**
  * Represents a cronjob.
@@ -70,9 +70,8 @@ class Cronjob extends DatabaseObject
      * Returns timestamp of next execution.
      *
      * @param int $timeBase
-     * @return  int
      */
-    public function getNextExec($timeBase = null)
+    public function getNextExec($timeBase = null): int
     {
         if ($timeBase === null) {
             if ($this->lastExec) {
@@ -86,14 +85,20 @@ class Cronjob extends DatabaseObject
             }
         }
 
-        return CronjobUtil::calculateNextExec(
+        $dateTime = (new \DateTimeImmutable("@{$timeBase}"))
+            // The TZ parameter in the constructor is ignored for timestamps.
+            ->setTimezone(new \DateTimeZone(TIMEZONE));
+
+        $expression = new CronExpression(\sprintf(
+            '%s %s %s %s %s',
             $this->startMinute,
             $this->startHour,
             $this->startDom,
             $this->startMonth,
             $this->startDow,
-            $timeBase
-        );
+        ));
+
+        return $expression->getNextRunDate($dateTime)->getTimestamp();
     }
 
     /**
index 0c992381f161085353f88ede68428f368954c71a..3cc0768aae613aff54874d259eed08f29806eacd 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace wcf\system\package\plugin;
 
+use Cron\CronExpression;
+use Cron\FieldFactory;
 use wcf\data\cronjob\Cronjob;
 use wcf\data\cronjob\CronjobEditor;
 use wcf\data\cronjob\CronjobList;
@@ -10,7 +12,6 @@ use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\devtools\pip\IDevtoolsPipEntryList;
 use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
 use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
-use wcf\system\exception\SystemException;
 use wcf\system\form\builder\container\IFormContainer;
 use wcf\system\form\builder\field\BooleanFormField;
 use wcf\system\form\builder\field\ClassNameFormField;
@@ -21,7 +22,6 @@ use wcf\system\form\builder\field\validation\FormFieldValidator;
 use wcf\system\form\builder\IFormDocument;
 use wcf\system\language\LanguageFactory;
 use wcf\system\WCF;
-use wcf\util\CronjobUtil;
 use wcf\util\StringUtil;
 
 /**
@@ -134,13 +134,14 @@ class CronjobPackageInstallationPlugin extends AbstractXMLPackageInstallationPlu
      */
     protected function validateImport(array $data)
     {
-        CronjobUtil::validate(
-            $data['startMinute'],
+        // The constructor will throw if the expression is not valid.
+        new CronExpression(\sprintf(
+            '%s %s %s %s %s',
             $data['startHour'],
             $data['startDom'],
             $data['startMonth'],
             $data['startDow']
-        );
+        ));
     }
 
     /**
@@ -261,6 +262,7 @@ class CronjobPackageInstallationPlugin extends AbstractXMLPackageInstallationPlu
                 ->value(true),
         ]);
 
+        $fieldFactory = new FieldFactory();
         foreach (['startMinute', 'startHour', 'startDom', 'startMonth', 'startDow'] as $timeProperty) {
             $dataContainer->insertBefore(
                 TextFormField::create($timeProperty)
@@ -270,10 +272,16 @@ class CronjobPackageInstallationPlugin extends AbstractXMLPackageInstallationPlu
                     ->required()
                     ->addValidator(new FormFieldValidator(
                         'format',
-                        static function (TextFormField $formField) use ($timeProperty) {
-                            try {
-                                CronjobUtil::validateAttribute($timeProperty, $formField->getSaveValue());
-                            } catch (SystemException $e) {
+                        static function (TextFormField $formField) use ($timeProperty, $fieldFactory) {
+                            $position = match ($timeProperty) {
+                                'startMinute' => CronExpression::MINUTE,
+                                'startHour' => CronExpression::HOUR,
+                                'startDom' => CronExpression::MONTH,
+                                'startMonth' => CronExpression::MONTH,
+                                'startDow' => CronExpression::WEEKDAY,
+                            };
+
+                            if (!$fieldFactory->getField($position)->validate($formField->getSaveValue())) {
                                 $formField->addValidationError(
                                     new FormFieldValidationError(
                                         'format',