Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a cron service and command with cron expressions (see #1098)
Description ----------- This is a revamp of my original PR #708. The basic features and service tag options are the same - with the addition of being able to use full CRON expressions, instead of just `minutely`, `hourly`, etc.! ### Command Cron jobs can now be executed via the following command: ``` vendor/bin/contao-console contao:cron ``` This can be used in a minutely crontab for example. When using the command, only cron jobs with no scope definition (`null`) or the `cli` scope will be executed, jobs with the scope `web` will be skipped. ### Service Tag The new service tag `contao.cronjob` has the following options: | Option | Description | | --- | --- | | `interval` | Can be `minutely`, `hourly`, `daily`, `weekly`, `monthly`, `yearly` or a full CRON expression, like `*/5 * * * *`. | | `method` | Will default to `__invoke` or `onMinutely` etc. when a named interval is used. Otherwise a method name has to be defined. | _Example:_ ```yml services: App\Cron\ExampleCron: tags: - name: contao.cronjob interval: * 0 * * * method: onDaily ``` ### Annotation Tagging services is also possible via Annotations: ```php // src/Cron/ExampleCronJob.php namespace App\Cron; use Contao\CoreBundle\ServiceAnnotation\CronJob; use Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface; /** * @cronjob("* 0 * * *") */ class ExampleCronJob implements ServiceAnnotationInterface { public function __invoke(): void { // … } } ``` When the annotation is used on the class, either the `__invoke` method will be used - or an auto generated method name (e.g. `onMinutely`), if present. Basically the same concept as in #1063. _Note:_ If you need an interval like `*/5 * * *` you need to escape either the `*` or `/` with `\`, since `*/` would close the PHP comment. This is what line 56 in the `Cron` annotation class is for. @aschempp may be this is something, that the ServiceAnnotationBundle should handle by itself? ### Front End Cron & Route The `/_contao/cron` route still exists but now uses the new `Cron` service instead of the `Contao\FrontendCron` controller class. Same with the `Contao\CoreBundle\EventListener\CommandSchedulerListener` (poor man's cron). Cron jobs tagged with the scope `cli` will __not__ be executed for either. ### Additional Dependency I am using [dragonmantank/cron-expression](https://github.com/dragonmantank/cron-expression) to parse CRON expressions and check if a cron job is, _or was_, due. I did not use [cron/cron](https://github.com/cron/cron) because it lacks the latter functionality. The Cron within Contao might or will be executed in irregular intervals, thus it needs to be checked whether a cron job with a defined interval would currently be _overdue_ relative to its last execution. _Example:_ say you have cron job with an interval of `*/5 * * * *` and you are still using the front end command scheduler. This cron job would be due every 5 minutes of every hour - e.g. `00:00`, `00:05`, `00:10` etc. However, there might not be a request which would execute the Cron service every minute. If the Cron service is executed at `00:03:58` for example and the cron job has not been executed before, the cron job will be executed right away. Then if the next run of the Cron service is at `00:07:10` the cron job will be executed again, since its next run would have been at `00:05:00`, relative to its last execution time. If another request is made at `00:09:46`, the cron job will not be executed, since its next scheduled execution time would be at `00:10:00` - etc. As for the package itself: it looks well maintained, has lots of installs and dependents - and apparently Laravel uses it too. ### New Entity The PR introduces a new `Cron` entity, which simply saves the "name" of a cron job (consisting of the class name and method) and its last execution. ![tl_cron](https://user-images.githubusercontent.com/4970961/70894253-f4eae880-1fec-11ea-8d59-23b2068127ee.png) ### Backwards Compatibility I have completely deleted the `tl_cron` DCA and am now using the `tl_cron` table for the new format (via an entity). This is probably not ideal for BC? ;) If we want to keep the old `tl_cron` table as is, we could use a new `tl_cron_runs` table instead. In that case: should we still update the values in the `tl_cron` table? May be simply through the legacy cron. ### Scope In some cases a cron job might want to know in which "scope" it is executed in - i.e. as part of a front end request as part of the cron command on the command line interface. The `Cron` service will pass a scope parameter to the cron job's method. ```php namespace App\Cron; use Contao\CoreBundle\Cron\Cron; class HourlyCron { public function __invoke(string $scope): void { // Do not execute this cron job in the web scope if (Cron::SCOPE_WEB === $scope) { return; } // … } } ``` Commits ------- 028ca37 add cron command and scheduler using CRON expressions 6a43e20 remove superfluous use statement e4dd67e implement changes reported by code analyzer 51ec0c1 remove priority and scope b9f6766 add simple test for CronCommand 547df99 rename CronJobs 2004f2a implement various improvements 1305bde fix merge error 340d701 remove definition tests e61623a fix unit tests 9900a32 update database dump for functional tests b4be3a6 code style fix 5894ddb use Cron::class for reference check 75faa5d change database table for canRunDbQuery d86c6ee try to fix code analysis errors 97f5487 fix return type declaration in test 1b23547 add CronJob class and scope interface a7c28dc rename service annotation class to CronJob 2eb3915 Fix the coding style aff1123 make CronJob method optional 5b2a38e remove final from CronJob class f9f8122 rename interface to ScopeAwareCronJob c39a210 use DateTimeImmutable 4f532c3 trigger TL_CRON deprecation warning also for core cron jobs 3453541 ammend interface renaming ea85e8f update unit tests dd8e05b remove connection checkt 9731e1a use constant for empty response 1b97ad3 add TODO comment 1751e23 add test for invalid scope argument e74a701 CS fix 72ed188 change constants values cc5a87f Fix the coding style 0cc1a4e make scope mandatory and remove scope interface bcddfae remove superfluous service tag b1918a4 change addCronJob parameter to CronJob object c421efa remove persistAndFlush method from CronJobRepository e288a7e fix rebase errors 80a2dab remove service argument for FrontendController
- Loading branch information