├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
├── Formatter
│ ├── Comment.php
│ ├── Cron.php
│ ├── Header.php
│ ├── InvalidEmail.php
│ ├── Job.php
│ ├── Output.php
│ └── Time.php
└── Updater
│ ├── CommandCronManipulator.php
│ ├── CronManipulator.php
│ ├── CronUpdater.php
│ ├── FileSystem.php
│ ├── ProcessRunner.php
│ ├── StandardFileSystem.php
│ ├── SymfonyFileSystem.php
│ └── SymfonyProcessRunner.php
└── tests
├── Formatter
└── CronTest.php
└── Updater
└── CronUpdaterTest.php
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | run:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | include:
11 | - php-versions: 8.0
12 | symfony-versions: 4.4.*
13 | - php-versions: 8.0
14 | symfony-versions: 5.4.*
15 | - php-versions: 8.0
16 | symfony-versions: 6.0.*
17 | - php-versions: 8.1
18 | symfony-versions: 4.4.*
19 | - php-versions: 8.1
20 | symfony-versions: 5.4.*
21 | - php-versions: 8.1
22 | symfony-versions: 6.2.*
23 | - php-versions: 8.2
24 | symfony-versions: 6.2.*
25 |
26 | name: PHP ${{ matrix.php-versions }} Test with Symfony ${{ matrix.symfony-versions }}
27 | steps:
28 | # —— Setup Github actions 🐙 —————————————————————————————————————————————
29 | - name: Checkout
30 | uses: actions/checkout@v3
31 |
32 | # https://github.com/shivammathur/setup-php (community)
33 | - name: Setup PHP, with composer and extensions
34 | uses: shivammathur/setup-php@v2
35 | with:
36 | php-version: ${{ matrix.php-versions }}
37 | extensions: mbstring, xml, ctype, iconv, intl
38 | coverage: xdebug #optional
39 |
40 | # https://github.com/marketplace/actions/setup-php-action#problem-matchers
41 | - name: Setup problem matchers for PHP
42 | run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
43 |
44 | # https://github.com/marketplace/actions/setup-php-action#problem-matchers
45 | - name: Setup problem matchers for PHPUnit
46 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
47 |
48 | # —— Composer 🧙️ —————————————————————————————————————————————————————————
49 | - name: Get composer cache directory
50 | id: composer-cache
51 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
52 |
53 | - name: Cache composer dependencies
54 | uses: actions/cache@v3
55 | with:
56 | path: ${{ steps.composer-cache.outputs.dir }}
57 | # Use composer.json for key, if composer.lock is not committed.
58 | # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
59 | key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ matrix.symfony-versions }}-${{ hashFiles('**/composer.json') }}
60 | restore-keys: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ matrix.symfony-versions }}-
61 |
62 | - name: Validate Composer.json
63 | run: composer validate
64 |
65 | - name: Fix symfony version for symfony/process
66 | run: composer require --no-update symfony/process:"${{ matrix.symfony-versions }}";
67 |
68 | - name: Install Composer dependencies
69 | run: composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader
70 |
71 | ## —— Test ✅ ———————————————————————————————————————————————————————————
72 | - name: Run Tests
73 | run: php bin/phpunit --coverage-text
74 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | composer.phar
3 | .phpunit.result.cache
4 | bin/
5 | vendor/
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-2020 MyBuilder Limited
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cronos
2 |
3 | Easily configure cron through PHP.
4 |
5 | If you use Symfony 4/5/6, you could use our [cool bundle](https://github.com/mybuilder/cronos-bundle) in order to configure your app jobs through fancy annotations!
6 |
7 | ## Setup and Configuration
8 |
9 | Require the library via composer:
10 |
11 | composer require mybuilder/cronos
12 |
13 | ## Usage
14 |
15 | ### Build Cron
16 |
17 | ```php
18 | header()
25 | ->setPath('path')
26 | ->setHome('home')
27 | ->setMailto('test@example.com')
28 | ->setShell('shell')
29 | ->setContentType('text')
30 | ->setContentTransferEncoding('utf8')
31 | ->end()
32 | ->comment('Comment')
33 | ->job('/bin/bash command --env=dev')
34 | ->setMinute(1)
35 | ->setHour(2)
36 | ->setDayOfMonth(3)
37 | ->setMonth(4)
38 | ->setDayOfWeek(5)
39 | ->setStandardOutFile('log')
40 | ->appendStandardErrorToFile('error')
41 | ->end();
42 |
43 | echo $cron->format();
44 | ```
45 |
46 | That will print
47 |
48 | MAILTO=test@example.com
49 | HOME=home
50 | SHELL=shell
51 | LOGNAME=logName
52 | CONTENT_TYPE=text
53 | CONTENT_TRANSFER_ENCODING=utf8
54 |
55 | #Comment
56 | 1 2 3 4 5 /bin/bash command --env=dev > log 2>> error
57 |
58 | ### Updating Cron
59 |
60 | ```php
61 | replaceWith($cron);
73 | ```
74 |
75 | ## Troubleshooting
76 |
77 | * The current user must have a existing crontab file to use the updater, use `crontab -e` to create one.
78 | * When a cron line is executed it is executed with the user that owns the crontab, but it will not execute any of the users default shell files so all paths etc need to be specified in the command called from the cron line.
79 | * Your crontab will not be executed if you do not have usable shell in `/etc/passwd`
80 | * If your jobs don't seem to be running, check the cron daemon is running, also check your username is in `/etc/cron.allow` and not in `/etc/cron.deny`.
81 | * Environmental substitutions do not work, you cannot use things like `$PATH`, `$HOME`, or `~/sbin`.
82 | * You cannot use `%` in the command, if you need to use it, escape the command in backticks.
83 |
84 | ---
85 |
86 | Created by [MyBuilder](http://www.mybuilder.com/) - Check out our [blog](http://tech.mybuilder.com/) for more insight into this and other open-source projects we release.
87 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mybuilder/cronos",
3 | "description": "Configure Cron task through PHP",
4 | "keywords": ["php", "cron"],
5 | "minimum-stability": "stable",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Gavin Love",
10 | "homepage": "https://www.mybuilder.com"
11 | },
12 | {
13 | "name": "Keyvan Akbary",
14 | "homepage": "https://www.mybuilder.com"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=8.0.2",
19 | "symfony/process": "^4.4|^5.4|^6.0"
20 | },
21 | "suggest": {
22 | "symfony/filesystem": "Allows using Symfony Filesystem"
23 | },
24 | "require-dev": {
25 | "symfony/filesystem": "^4.4|^5.4|^6.0",
26 | "phpunit/phpunit": "^9.5"
27 | },
28 | "autoload": {
29 | "psr-4": { "MyBuilder\\Cronos\\": "src" }
30 | },
31 | "autoload-dev": {
32 | "psr-4": { "MyBuilder\\Cronos\\Tests\\": "tests/" }
33 | },
34 | "config": {
35 | "bin-dir": "bin"
36 | },
37 | "extra": {
38 | "branch-alias": {
39 | "dev-master": "3.0-dev"
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./src
17 |
18 |
19 | ./tests
20 | ./vendor
21 |
22 |
23 |
24 |
25 | tests
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/Formatter/Comment.php:
--------------------------------------------------------------------------------
1 | removeLineBreaks($this->comment);
13 | }
14 |
15 | private function removeLineBreaks($text): string
16 | {
17 | return \str_replace(["\r", "\r\n", "\n", \PHP_EOL], '', $text);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Formatter/Cron.php:
--------------------------------------------------------------------------------
1 | header = new Header($this);
15 |
16 | return $this->header;
17 | }
18 |
19 | public function job(string $command): Job
20 | {
21 | $line = new Job($command, $this);
22 | $this->lines[] = $line;
23 |
24 | return $line;
25 | }
26 |
27 | public function comment(string $comment): Cron
28 | {
29 | $this->lines[] = new Comment($comment);
30 |
31 | return $this;
32 | }
33 |
34 | public function countLines(): int
35 | {
36 | return count($this->lines);
37 | }
38 |
39 | public function format(): string
40 | {
41 | $lines = '';
42 |
43 | foreach ($this->lines as $line) {
44 | $lines .= $line->format() . \PHP_EOL;
45 | }
46 |
47 | return (($this->hasHeader()) ? $this->header->format() . \PHP_EOL : '') . \trim($lines) . \PHP_EOL;
48 | }
49 |
50 | public function hasHeader(): bool
51 | {
52 | return $this->header !== null;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Formatter/Header.php:
--------------------------------------------------------------------------------
1 | path = $path;
24 |
25 | return $this;
26 | }
27 |
28 | public function setHome(string $home): self
29 | {
30 | $this->home = $home;
31 |
32 | return $this;
33 | }
34 |
35 | /** @throws InvalidEmail if given email is invalid */
36 | public function setMailto(string $email): self
37 | {
38 | $this->assertValidEmail($email);
39 | $this->mailTo = $email;
40 |
41 | return $this;
42 | }
43 |
44 | private function assertValidEmail($email): void
45 | {
46 | if (false === \filter_var($email, \FILTER_VALIDATE_EMAIL)) {
47 | throw new InvalidEmail($email);
48 | }
49 | }
50 |
51 | /**
52 | * Set the shell to be used when executing commands
53 | *
54 | * Default is /bin/sh but can also be changed to /bin/php
55 | */
56 | public function setShell(string $shell): self
57 | {
58 | $this->shell = $shell;
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Set the content-type to use for cron output emails.
65 | */
66 | public function setContentType(string $contentType): self
67 | {
68 | $this->contentType = $contentType;
69 |
70 | return $this;
71 | }
72 |
73 | /**
74 | * Set the charset to use for cron output emails.
75 | */
76 | public function setContentTransferEncoding(string $encoding): self
77 | {
78 | $this->encoding = $encoding;
79 |
80 | return $this;
81 | }
82 |
83 | public function setTimezone(string $timezone): self
84 | {
85 | $this->timezone = $timezone;
86 |
87 | return $this;
88 | }
89 |
90 | public function format(): string
91 | {
92 | $headers = '';
93 |
94 | if ($this->path) {
95 | $headers .= $this->createHeader('PATH', $this->path);
96 | }
97 | if ($this->mailTo) {
98 | $headers .= $this->createHeader('MAILTO', $this->mailTo);
99 | }
100 | if ($this->home) {
101 | $headers .= $this->createHeader('HOME', $this->home);
102 | }
103 | if ($this->shell) {
104 | $headers .= $this->createHeader('SHELL', $this->shell);
105 | }
106 | if ($this->contentType) {
107 | $headers .= $this->createHeader('CONTENT_TYPE', $this->contentType);
108 | }
109 | if ($this->encoding) {
110 | $headers .= $this->createHeader('CONTENT_TRANSFER_ENCODING', $this->encoding);
111 | }
112 | if ($this->timezone) {
113 | $headers .= $this->createHeader('CRON_TZ', $this->timezone);
114 | }
115 |
116 | return $headers;
117 | }
118 |
119 | private function createHeader($name, $value): string
120 | {
121 | return $name . '=' . $value . \PHP_EOL;
122 | }
123 |
124 | public function end(): Cron
125 | {
126 | return $this->cron;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/Formatter/InvalidEmail.php:
--------------------------------------------------------------------------------
1 | time = new Time;
13 | $this->output = new Output;
14 | }
15 |
16 | /** @see Time::setMinute */
17 | public function setMinute(string $value): self
18 | {
19 | $this->time->setMinute($value);
20 |
21 | return $this;
22 | }
23 |
24 | /** @see Time::setHour */
25 | public function setHour(string $value): self
26 | {
27 | $this->time->setHour($value);
28 |
29 | return $this;
30 | }
31 |
32 | /** @see Time::setDayOfMonth */
33 | public function setDayOfMonth(string $value): self
34 | {
35 | $this->time->setDayOfMonth($value);
36 |
37 | return $this;
38 | }
39 |
40 | /** @see Time::setMonth */
41 | public function setMonth(string $value): self
42 | {
43 | $this->time->setMonth($value);
44 |
45 | return $this;
46 | }
47 |
48 | /** @see Time::setDayOfWeek */
49 | public function setDayOfWeek(string$value): self
50 | {
51 | $this->time->setDayOfWeek($value);
52 |
53 | return $this;
54 | }
55 |
56 | /**
57 | * Suppress the output of this command when executed
58 | *
59 | * @see Output::suppressOutput
60 | */
61 | public function suppressOutput(): self
62 | {
63 | $this->output->suppressOutput();
64 |
65 | return $this;
66 | }
67 |
68 | /** @see Output::setStandardOutFile */
69 | public function setStandardOutFile(string $filePath): self
70 | {
71 | $this->output->setStandardOutFile($filePath);
72 |
73 | return $this;
74 | }
75 |
76 | /** @see Output::appendStandardOutToFile */
77 | public function appendStandardOutToFile(string $filePath): self
78 | {
79 | $this->output->appendStandardOutToFile($filePath);
80 |
81 | return $this;
82 | }
83 |
84 | /** @see Output::setStandardErrorFile */
85 | public function setStandardErrorFile(string $filePath): self
86 | {
87 | $this->output->setStandardErrorFile($filePath);
88 |
89 | return $this;
90 | }
91 |
92 | /** @see Output::appendStandardErrorToFile */
93 | public function appendStandardErrorToFile(string $filePath): self
94 | {
95 | $this->output->appendStandardErrorToFile($filePath);
96 |
97 | return $this;
98 | }
99 |
100 | public function end(): Cron
101 | {
102 | return $this->cron;
103 | }
104 |
105 | public function format(): string
106 | {
107 | return
108 | $this->time->format() .
109 | $this->command .
110 | $this->output->format() .
111 | \PHP_EOL;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Formatter/Output.php:
--------------------------------------------------------------------------------
1 | noOutput = true;
17 |
18 | return $this;
19 | }
20 |
21 | public function setStandardOutFile(string $filePath): self
22 | {
23 | $this->stdOutFile = $filePath;
24 | $this->stdOutAppend = false;
25 |
26 | return $this;
27 | }
28 |
29 | public function appendStandardOutToFile(string $filePath): self
30 | {
31 | $this->stdOutFile = $filePath;
32 | $this->stdOutAppend = true;
33 |
34 | return $this;
35 | }
36 |
37 | /**
38 | * @param bool $append Either append or rewrite log file
39 | */
40 | public function setStandardErrorFile(string $filePath, bool $append = false): self
41 | {
42 | $this->stdErrFile = $filePath;
43 | $this->stdErrAppend = $append;
44 |
45 | return $this;
46 | }
47 |
48 | public function appendStandardErrorToFile(string $filePath): self
49 | {
50 | $this->stdErrFile = $filePath;
51 | $this->stdErrAppend = true;
52 |
53 | return $this;
54 | }
55 |
56 | public function format(): string
57 | {
58 | if ($this->noOutput) {
59 | return $this->redirectStandardOutTo(self::NO_FILE) . $this->redirectStandardErrorTo(self::NO_FILE);
60 | }
61 |
62 | return $this->createOutput();
63 | }
64 |
65 | private function redirectStandardOutTo($filePath): string
66 | {
67 | return $this->redirectOutputTo('', $this->stdOutAppend, $filePath);
68 | }
69 |
70 | private function redirectOutputTo($out, $isAppend, $filePath): string
71 | {
72 | $operator = $isAppend ? '>>' : '>';
73 |
74 | return ' ' . $out . $operator . ' ' . $filePath;
75 | }
76 |
77 | private function redirectStandardErrorTo($filePath): string
78 | {
79 | return $this->redirectOutputTo('2', $this->stdErrAppend, $filePath);
80 | }
81 |
82 | private function createOutput(): string
83 | {
84 | $out = '';
85 |
86 | if ($this->stdOutFile) {
87 | $out .= $this->redirectStandardOutTo($this->stdOutFile);
88 | }
89 |
90 | if ($this->stdErrFile) {
91 | $out .= $this->redirectStandardErrorTo($this->stdErrFile);
92 | }
93 |
94 | return $out;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Formatter/Time.php:
--------------------------------------------------------------------------------
1 | minute = $this->parse($value);
41 |
42 | return $this;
43 | }
44 |
45 | /**
46 | * @param string $value 0-23 or a list or range
47 | */
48 | public function setHour(string $value): self
49 | {
50 | $this->hour = $this->parse($value);
51 |
52 | return $this;
53 | }
54 |
55 | /**
56 | * @param string $value 0-31 or a list or range
57 | */
58 | public function setDayOfMonth(string $value): self
59 | {
60 | $this->dayOfMonth = $this->parse($value);
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * @param string $value 0-12 or a list or range
67 | */
68 | public function setMonth(string $value): self
69 | {
70 | $this->month = $this->parse($value);
71 |
72 | return $this;
73 | }
74 |
75 | /**
76 | * @param string $value 0-7 (0 or 7 is Sun, or use names) or a list or range
77 | */
78 | public function setDayOfWeek(string $value): self
79 | {
80 | $this->dayOfWeek = $this->parse($value);
81 |
82 | return $this;
83 | }
84 |
85 | public function format(): string
86 | {
87 | return \sprintf(self::FORMAT, $this->minute, $this->hour, $this->dayOfMonth, $this->month, $this->dayOfWeek);
88 | }
89 |
90 | private function parse(string $value): string
91 | {
92 | if (\str_starts_with($value, '/')) {
93 | return self::WILDCARD_TIME . $value;
94 | }
95 |
96 | return $value;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Updater/CommandCronManipulator.php:
--------------------------------------------------------------------------------
1 | fileSystem->createTempFile('cron', $contents);
15 | $this->processRunner->run([$this->cronCommand, $filePath]);
16 | $this->fileSystem->removeFile($filePath);
17 | }
18 |
19 | public function getContent(): string
20 | {
21 | return $this->processRunner->run([$this->cronCommand, '-l']);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Updater/CronManipulator.php:
--------------------------------------------------------------------------------
1 | cronManipulator->replace($cron->format());
23 | }
24 |
25 | public function updateWith(Cron $cron, string $key): void
26 | {
27 | $this->cronManipulator->replace($this->updateContent($cron, $key));
28 | }
29 |
30 | private function updateContent(Cron $cron, string $key): string
31 | {
32 | $content = $this->cronManipulator->getContent();
33 |
34 | $count = 0;
35 | $pattern = '/\r?\n' . $this->beginKey($key) . '.*?' . self::KEY_END . '/s';
36 | $replacedContent = \preg_replace($pattern, $this->wrapInKey($cron, $key), $content, -1, $count);
37 |
38 | if ($count > 0) {
39 | return $replacedContent;
40 | }
41 |
42 | return $this->appendContent($cron, $key, $content);
43 | }
44 |
45 | private function wrapInKey(Cron $cron, string $key): string
46 | {
47 | return \PHP_EOL . $this->beginKey($key) . \PHP_EOL . \trim($cron->format()) . \PHP_EOL . self::KEY_END;
48 | }
49 |
50 | private function beginKey(string $key): string
51 | {
52 | return \str_replace('%key%', $key, self::KEY_BEGIN);
53 | }
54 |
55 | private function appendContent(Cron $cron, string $key, string $content): string
56 | {
57 | return $content . $this->wrapInKey($cron, $key) . \PHP_EOL;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Updater/FileSystem.php:
--------------------------------------------------------------------------------
1 | createTempName($prefix);
10 | \file_put_contents($filePath, $content);
11 |
12 | return $filePath;
13 | }
14 |
15 | private function createTempName(string $prefix): string
16 | {
17 | return \tempnam(\sys_get_temp_dir(), $prefix);
18 | }
19 |
20 | public function removeFile(string $filePath): void
21 | {
22 | \unlink($filePath);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Updater/SymfonyFileSystem.php:
--------------------------------------------------------------------------------
1 | filesystem = new FileSystemHelper();
14 | }
15 |
16 | /** @throws \Symfony\Component\Filesystem\Exception\IOException If the file cannot be written to. */
17 | public function createTempFile($prefix, $content): string
18 | {
19 | $filePath = $this->createTempName($prefix);
20 | $this->dumpToFile($filePath, $content);
21 |
22 | return $filePath;
23 | }
24 |
25 | private function dumpToFile(string $filePath, string $content): void
26 | {
27 | if (\method_exists($this->filesystem, 'dumpFile')) {
28 | $this->filesystem->dumpFile($filePath, $content);
29 | } else {
30 | \file_put_contents($filePath, $content);
31 | }
32 | }
33 |
34 | private function createTempName(string $prefix): string
35 | {
36 | return \tempnam(\sys_get_temp_dir(), $prefix);
37 | }
38 |
39 | /** @throws \Symfony\Component\Filesystem\Exception\IOException When removal fails */
40 | public function removeFile(string $filePath): void
41 | {
42 | $this->filesystem->remove($filePath);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Updater/SymfonyProcessRunner.php:
--------------------------------------------------------------------------------
1 | run();
13 |
14 | if (false === $process->isSuccessful()) {
15 | throw new \RuntimeException($process->getErrorOutput());
16 | }
17 |
18 | return $process->getOutput();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/Formatter/CronTest.php:
--------------------------------------------------------------------------------
1 | cron = new Cron;
15 | }
16 |
17 | /**
18 | * @test
19 | */
20 | public function shouldBuildConfiguration(): void
21 | {
22 | $this->cron
23 | ->header()
24 | ->setPath('path')
25 | ->setHome('home')
26 | ->setMailto('test@example.com')
27 | ->setShell('shell')
28 | ->setContentType('text')
29 | ->setContentTransferEncoding('utf8')
30 | ->setTimezone('Europe/Paris')
31 | ->end()
32 | ->comment('This is a command!')
33 | ->job('/bin/bash command --env=dev')
34 | ->setMinute(1)
35 | ->setHour(2)
36 | ->setDayOfMonth(3)
37 | ->setMonth(4)
38 | ->setDayOfWeek(5)
39 | ->setStandardOutFile('log')
40 | ->appendStandardErrorToFile('error')
41 | ->end()
42 | ->comment('This is another command!')
43 | ->job('/bin/php command2 --env=prod')
44 | ->setMinute('/5')
45 | ->setDayOfWeek('sun')
46 | ->suppressOutput()
47 | ->end();
48 |
49 | $expected = << log 2>> error
60 |
61 | #This is another command!
62 | */5 * * * sun /bin/php command2 --env=prod > /dev/null 2> /dev/null
63 |
64 | EXP;
65 |
66 | $this->assertEquals($expected, $this->cron->format());
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tests/Updater/CronUpdaterTest.php:
--------------------------------------------------------------------------------
1 | manipulatorStub = new CronManipulatorStub();
18 |
19 | $this->updater = new CronUpdater($this->manipulatorStub);
20 | }
21 |
22 | /**
23 | * @test
24 | */
25 | public function shouldReplaceContent(): void
26 | {
27 | $this->updater->replaceWith(new Cron());
28 |
29 | $this->assertEquals(\PHP_EOL, $this->manipulatorStub->contents);
30 | }
31 |
32 | /**
33 | * @test
34 | */
35 | public function shouldAppendKeyIfNotExist(): void
36 | {
37 | $this->manipulatorStub->contents = <<header()->setPath('path')->end();
47 | $cron->comment('new content');
48 | $this->updater->updateWith($cron, 'key2');
49 |
50 | $expectedCron = <<assertEquals($expectedCron, $this->manipulatorStub->contents);
65 | }
66 |
67 | /**
68 | * @test
69 | */
70 | public function shouldReplaceKeyIfExist(): void
71 | {
72 | $this->manipulatorStub->contents = <<comment('replace');
89 | $this->updater->updateWith($cron, 'key2');
90 |
91 | $expectedCron = <<assertEquals($expectedCron, $this->manipulatorStub->contents);
107 | }
108 | }
109 |
110 | class CronManipulatorStub implements CronManipulator
111 | {
112 | public string $contents;
113 |
114 | public function replace(string $contents): void
115 | {
116 | $this->contents = $contents;
117 | }
118 |
119 | public function getContent(): string
120 | {
121 | return $this->contents;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------