├── Bootstrap.php ├── .php_cs ├── LICENSE ├── composer.json ├── CallbackEvent.php ├── Schedule.php ├── ScheduleController.php ├── README.md └── Event.php /Bootstrap.php: -------------------------------------------------------------------------------- 1 | controllerMap['schedule'])) { 22 | $app->controllerMap['schedule'] = 'yii2mod\scheduling\ScheduleController'; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | exclude([ 5 | 'vendor', 6 | 'runtime', 7 | 'tests/_output', 8 | 'tests/_support', 9 | ]) 10 | ->in([__DIR__]); 11 | 12 | $config = PhpCsFixer\Config::create() 13 | ->setUsingCache(false) 14 | ->setRules([ 15 | '@Symfony' => true, 16 | 'phpdoc_align' => false, 17 | 'phpdoc_summary' => false, 18 | 'phpdoc_inline_tag' => false, 19 | 'pre_increment' => false, 20 | 'heredoc_to_nowdoc' => false, 21 | 'cast_spaces' => false, 22 | 'include' => false, 23 | 'phpdoc_no_package' => false, 24 | 'concat_space' => ['spacing' => 'one'], 25 | 'ordered_imports' => true, 26 | 'array_syntax' => ['syntax' => 'short'], 27 | 'yoda_style' => false, 28 | ]) 29 | ->setFinder($finder); 30 | 31 | return $config; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yii2 modules & extensions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yii2mod/yii2-scheduling", 3 | "description": "Scheduling extension for Yii2 framework", 4 | "type": "yii2-extension", 5 | "keywords": [ 6 | "yii", 7 | "scheduling", 8 | "cron" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Igor Chepurnoi", 13 | "email": "chepurnoi.igor@gmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.6", 18 | "yiisoft/yii2": "2.0.*", 19 | "symfony/process": "~3.2", 20 | "mtdowling/cron-expression": "~1.0", 21 | "league/climate": "^3.2", 22 | "guzzlehttp/guzzle": "^6.3", 23 | "nesbot/carbon": "~1.21" 24 | }, 25 | "require-dev": { 26 | "friendsofphp/php-cs-fixer": "~2.0", 27 | "phpunit/phpunit": "~6.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "yii2mod\\scheduling\\": "" 32 | } 33 | }, 34 | "extra": { 35 | "bootstrap": "yii2mod\\scheduling\\Bootstrap" 36 | }, 37 | "repositories": [ 38 | { 39 | "type": "composer", 40 | "url": "https://asset-packagist.org" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /CallbackEvent.php: -------------------------------------------------------------------------------- 1 | callback = $callback; 37 | $this->parameters = $parameters; 38 | 39 | if (!is_string($this->callback) && !is_callable($this->callback)) { 40 | throw new InvalidParamException( 41 | 'Invalid scheduled callback event. Must be string or callable.' 42 | ); 43 | } 44 | 45 | parent::__construct($config); 46 | } 47 | 48 | /** 49 | * Run the given event. 50 | * 51 | * @param Application $app 52 | * 53 | * @return mixed 54 | */ 55 | public function run(Application $app) 56 | { 57 | $response = call_user_func_array($this->callback, array_merge($this->parameters, [$app])); 58 | parent::callAfterCallbacks($app); 59 | 60 | return $response; 61 | } 62 | 63 | /** 64 | * Get the summary of the event for display. 65 | * 66 | * @return string 67 | */ 68 | public function getSummaryForDisplay() 69 | { 70 | if (is_string($this->_description)) { 71 | return $this->_description; 72 | } 73 | 74 | return is_string($this->callback) ? $this->callback : 'Closure'; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Schedule.php: -------------------------------------------------------------------------------- 1 | _events[] = $event = new CallbackEvent($callback, $parameters); 31 | 32 | return $event; 33 | } 34 | 35 | /** 36 | * Add a new cli command event to the schedule. 37 | * 38 | * @param string $command 39 | * 40 | * @return Event 41 | */ 42 | public function command($command) 43 | { 44 | return $this->exec(PHP_BINARY . ' yii ' . $command); 45 | } 46 | 47 | /** 48 | * Add a new command event to the schedule. 49 | * 50 | * @param string $command 51 | * 52 | * @return Event 53 | */ 54 | public function exec($command) 55 | { 56 | $this->_events[] = $event = new Event($command); 57 | 58 | return $event; 59 | } 60 | 61 | /** 62 | * Get events 63 | * 64 | * @return Event[] 65 | */ 66 | public function getEvents() 67 | { 68 | return $this->_events; 69 | } 70 | 71 | /** 72 | * Get all of the events on the schedule that are due. 73 | * 74 | * @param \yii\base\Application $app 75 | * 76 | * @return Event[] 77 | */ 78 | public function dueEvents(Application $app) 79 | { 80 | return array_filter($this->_events, function (Event $event) use ($app) { 81 | return $event->isDue($app); 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ScheduleController.php: -------------------------------------------------------------------------------- 1 | has($this->schedule)) { 48 | $this->schedule = Instance::ensure($this->schedule, Schedule::class); 49 | } else { 50 | $this->schedule = Yii::createObject(Schedule::class); 51 | } 52 | } 53 | 54 | /** 55 | * Run scheduled commands 56 | */ 57 | public function actionRun() 58 | { 59 | $this->importScheduleFile(); 60 | 61 | $events = $this->schedule->dueEvents(Yii::$app); 62 | 63 | foreach ($events as $event) { 64 | $this->stdout('Running scheduled command: ' . $event->getSummaryForDisplay() . "\n"); 65 | $event->run(Yii::$app); 66 | } 67 | 68 | if (count($events) === 0) { 69 | $this->stdout("No scheduled commands are ready to run.\n"); 70 | } 71 | } 72 | 73 | /** 74 | * Render list of registered tasks 75 | */ 76 | public function actionList() 77 | { 78 | $this->importScheduleFile(); 79 | 80 | $climate = new CLImate(); 81 | $data = []; 82 | $row = 0; 83 | 84 | foreach ($this->schedule->getEvents() as $event) { 85 | $data[] = [ 86 | '#' => ++$row, 87 | 'Task' => $event->getSummaryForDisplay(), 88 | 'Expression' => $event->getExpression(), 89 | 'Command to Run' => is_a($event, CallbackEvent::class) 90 | ? $event->getSummaryForDisplay() 91 | : $event->command, 92 | 'Next run at' => $this->getNextRunDate($event), 93 | ]; 94 | } 95 | 96 | $climate->table($data); 97 | } 98 | 99 | /** 100 | * Import schedule file 101 | * 102 | * @throws InvalidConfigException 103 | */ 104 | protected function importScheduleFile() 105 | { 106 | $scheduleFile = Yii::getAlias($this->scheduleFile); 107 | 108 | if (!file_exists($scheduleFile)) { 109 | throw new InvalidConfigException("Can not load schedule file {$this->scheduleFile}"); 110 | } 111 | 112 | $schedule = $this->schedule; 113 | call_user_func(function () use ($schedule, $scheduleFile) { 114 | include $scheduleFile; 115 | }); 116 | } 117 | 118 | /** 119 | * Get the next scheduled run date for this event 120 | * 121 | * @param Event $event 122 | * 123 | * @return string 124 | */ 125 | protected function getNextRunDate(Event $event) 126 | { 127 | $cron = CronExpression::factory($event->getExpression()); 128 | $date = Carbon::now(); 129 | 130 | if ($event->hasTimezone()) { 131 | $date->setTimezone($event->getTimezone()); 132 | } 133 | 134 | return $cron->getNextRunDate()->format('Y-m-d H:i:s'); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

Yii2 Schedule Extension

6 |
7 |

8 | 9 | This extension is the port of Laravel's Schedule component (http://laravel.com/docs/master/artisan#scheduling-artisan-commands) 10 | 11 | [![Latest Stable Version](https://poser.pugx.org/yii2mod/yii2-scheduling/v/stable)](https://packagist.org/packages/yii2mod/yii2-scheduling) [![Total Downloads](https://poser.pugx.org/yii2mod/yii2-scheduling/downloads)](https://packagist.org/packages/yii2mod/yii2-scheduling) 12 | [![License](https://poser.pugx.org/yii2mod/yii2-scheduling/license)](https://packagist.org/packages/yii2mod/yii2-scheduling) 13 | [![Build Status](https://travis-ci.org/yii2mod/yii2-scheduling.svg?branch=master)](https://travis-ci.org/yii2mod/yii2-scheduling) 14 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yii2mod/yii2-scheduling/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yii2mod/yii2-scheduling/?branch=master) 15 | 16 | Installation 17 | ------------ 18 | 19 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 20 | 21 | Either run 22 | 23 | ``` 24 | php composer.phar require yii2mod/yii2-scheduling "*" 25 | ``` 26 | 27 | or add 28 | 29 | ``` 30 | "yii2mod/yii2-scheduling": "*" 31 | ``` 32 | 33 | to the `require` section of your composer.json. 34 | 35 | Description 36 | ----------- 37 | 38 | This project is inspired by the Laravel's Schedule component and tries to bring it's simplicity to the Yii framework. 39 | Quote from Laravel's documentation: 40 | 41 | ``` 42 | In the past, developers have generated a Cron entry for each console command they wished to schedule. 43 | However, this is a headache. Your console schedule is no longer in source control, 44 | and you must SSH into your server to add the Cron entries. Let's make our lives easier. 45 | ``` 46 | 47 | After installation all you have to do is to put single line into crontab: 48 | 49 | ``` 50 | * * * * * php /path/to/yii yii schedule/run --scheduleFile=@path/to/schedule.php 1>> /dev/null 2>&1 51 | ``` 52 | 53 | You can put your schedule into the `schedule.php` file, or add it withing bootstrapping of your extension or 54 | application 55 | 56 | Schedule examples 57 | ----------------- 58 | 59 | This extension is support all features of Laravel's Schedule, except environments and maintance mode. 60 | 61 | **Scheduling Closures** 62 | 63 | ```php 64 | $schedule->call(function() 65 | { 66 | // Do some task... 67 | 68 | })->hourly(); 69 | ``` 70 | 71 | **Scheduling Terminal Commands** 72 | 73 | ```php 74 | $schedule->exec('composer self-update')->daily(); 75 | ``` 76 | 77 | **Running command of your application** 78 | 79 | ```php 80 | $schedule->command('migrate')->cron('* * * * *'); 81 | ``` 82 | 83 | **Frequent Jobs** 84 | 85 | ```php 86 | $schedule->command('foo')->everyFiveMinutes(); 87 | 88 | $schedule->command('foo')->everyTenMinutes(); 89 | 90 | $schedule->command('foo')->everyThirtyMinutes(); 91 | ``` 92 | 93 | **Daily Jobs** 94 | 95 | ```php 96 | $schedule->command('foo')->daily(); 97 | ``` 98 | 99 | **Daily Jobs At A Specific Time (24 Hour Time)** 100 | 101 | ```php 102 | $schedule->command('foo')->dailyAt('15:00'); 103 | ``` 104 | 105 | **Twice Daily Jobs** 106 | 107 | ```php 108 | $schedule->command('foo')->twiceDaily(); 109 | ``` 110 | 111 | **Job That Runs Every Weekday** 112 | 113 | ```php 114 | $schedule->command('foo')->weekdays(); 115 | ``` 116 | 117 | **Weekly Jobs** 118 | 119 | ```php 120 | $schedule->command('foo')->weekly(); 121 | 122 | // Schedule weekly job for specific day (0-6) and time... 123 | $schedule->command('foo')->weeklyOn(1, '8:00'); 124 | ``` 125 | 126 | **Monthly Jobs** 127 | 128 | ```php 129 | $schedule->command('foo')->monthly(); 130 | ``` 131 | 132 | **Job That Runs On Specific Days** 133 | 134 | ```php 135 | $schedule->command('foo')->mondays(); 136 | $schedule->command('foo')->tuesdays(); 137 | $schedule->command('foo')->wednesdays(); 138 | $schedule->command('foo')->thursdays(); 139 | $schedule->command('foo')->fridays(); 140 | $schedule->command('foo')->saturdays(); 141 | $schedule->command('foo')->sundays(); 142 | ``` 143 | 144 | **Only Allow Job To Run When Callback Is True** 145 | 146 | ```php 147 | $schedule->command('foo')->monthly()->when(function() 148 | { 149 | return true; 150 | }); 151 | ``` 152 | 153 | **Only Allow Job To Run When `skip` Callback is False** 154 | 155 | ```php 156 | $schedule->command('foo')->monthly()->skip(function() 157 | { 158 | return false; 159 | }); 160 | ``` 161 | 162 | **Set Custom Description For Scheduled Job** 163 | 164 | ```php 165 | $schedule->command('foo')->monthly()->description('command description'); 166 | ``` 167 | 168 | **E-mail The Output Of A Scheduled Job** 169 | 170 | ```php 171 | $schedule->command('foo')->sendOutputTo($filePath)->emailOutputTo('foo@example.com'); 172 | ``` 173 | 174 | **To see the list of registered tasks, you can use the `schedule/list` command as below:** 175 | 176 | ```bash 177 | php yii schedule/list 178 | 179 | +---+-------------------------------------+-------------+-----------------------------------------------------+ 180 | | # | Task | Expression | Command to Run | 181 | +---+-------------------------------------+-------------+-----------------------------------------------------+ 182 | | 1 | Delete the logs older than week | 0 0 * * 0 * | /usr/bin/php5 yii app/delete-logs-older-then-week | 183 | +---+-------------------------------------+-------------+-----------------------------------------------------+ 184 | ``` 185 | 186 | How to use this extension in your application? 187 | ---------------------------------------------- 188 | 189 | You should create the following file under `@console/config/schedule.php` (note: you can create file with any name 190 | and extension and anywere on your server, simpli ajust the name of the scheduleFile in the command below): 191 | 192 | ```php 193 | exec('ls')->everyFiveMinutes(); 202 | 203 | // This command will execute migration command of your application every hour 204 | $schedule->command('migrate')->hourly(); 205 | 206 | // This command will call callback function every day at 10:00 207 | $schedule->call(function(\yii\console\Application $app) { 208 | // Some code here... 209 | })->dailyAt('10:00'); 210 | 211 | ``` 212 | 213 | Next your should add the following command to your crontab: 214 | ``` 215 | * * * * * php /path/to/yii yii schedule/run --scheduleFile=@console/config/schedule.php 1>> /dev/null 2>&1 216 | ``` 217 | 218 | That's all! Now all your cronjobs will be runned as configured in your schedule.php file. 219 | 220 | How to use this extension in your own extension? 221 | ------------------------------------------------ 222 | 223 | First of all, you should include dependency to the `yii2mod\yii2-scheduling` into your composer.json: 224 | 225 | ``` 226 | 'require': { 227 | "yii2mod/yii2-scheduling": "*" 228 | } 229 | ``` 230 | 231 | Next you should create bootstrapping class for your extension, as described in the http://www.yiiframework.com/doc-2.0/guide-structure-extensions.html#bootstrapping-classes 232 | 233 | Place into your bootstrapping method the following code: 234 | 235 | ```php 236 | public function bootstrap(Application $app) 237 | { 238 | if ($app instanceof \yii\console\Application) { 239 | if ($app->has('schedule')) { 240 | /** @var omnilight\scheduling\Schedule $schedule */ 241 | $schedule = $app->get('schedule'); 242 | // Place all your shedule command below 243 | $schedule->command('my-extension-command')->dailyAt('12:00'); 244 | } 245 | } 246 | } 247 | ``` 248 | 249 | Add to the README of your extension info for the user to register `schedule` component for the application 250 | and add `schedule/run` command to the crontab as described upper. 251 | 252 | Using `schedule` component 253 | -------------------------- 254 | 255 | You do not have to use `schedule` component directly or define it in your application if you use schedule only in your application (and do not want to give ability for extensions to register they own cron jobs). But if you what to give extensions ability to register cronjobs, you should define `schedule` component in the application config: 256 | 257 | ```php 258 | 'schedule' => 'yii2mod\scheduling\Schedule', 259 | ``` 260 | 261 | Using addition functions 262 | ------------------------ 263 | 264 | If you want to use `thenPing` method of the Event, you should add the following string to the `composer.json` of your app: 265 | ``` 266 | "guzzlehttp/guzzle": "~5.3|~6.0" 267 | ``` 268 | 269 | Note about timezones 270 | -------------------- 271 | 272 | Please note, that this is PHP extension, so it use timezone defined in php config or in your Yii's configuration file, 273 | so set them correctly. 274 | -------------------------------------------------------------------------------- /Event.php: -------------------------------------------------------------------------------- 1 | command = $command; 102 | $this->_output = $this->getDefaultOutput(); 103 | 104 | parent::__construct($config); 105 | } 106 | 107 | /** 108 | * Run the given event. 109 | * 110 | * @param Application $app 111 | */ 112 | public function run(Application $app) 113 | { 114 | $this->trigger(self::EVENT_BEFORE_RUN); 115 | 116 | if (count($this->_afterCallbacks) > 0) { 117 | $this->runCommandInForeground($app); 118 | } else { 119 | $this->runCommandInBackground($app); 120 | } 121 | 122 | $this->trigger(self::EVENT_AFTER_RUN); 123 | } 124 | 125 | /** 126 | * Run the command in the foreground. 127 | * 128 | * @param Application $app 129 | */ 130 | protected function runCommandInForeground(Application $app) 131 | { 132 | (new Process( 133 | trim($this->buildCommand(), '& '), dirname($app->request->getScriptFile()), null, null, null 134 | ))->run(); 135 | $this->callAfterCallbacks($app); 136 | } 137 | 138 | /** 139 | * Build the command string. 140 | * 141 | * @return string 142 | */ 143 | public function buildCommand() 144 | { 145 | $command = $this->command . ' > ' . $this->_output . ' 2>&1 &'; 146 | 147 | return $this->_user ? 'sudo -u ' . $this->_user . ' ' . $command : $command; 148 | } 149 | 150 | /** 151 | * Call all of the "after" callbacks for the event. 152 | * 153 | * @param Application $app 154 | */ 155 | protected function callAfterCallbacks(Application $app) 156 | { 157 | foreach ($this->_afterCallbacks as $callback) { 158 | call_user_func($callback, $app); 159 | } 160 | } 161 | 162 | /** 163 | * Run the command in the background using exec. 164 | * 165 | * @param Application $app 166 | */ 167 | protected function runCommandInBackground(Application $app) 168 | { 169 | chdir(dirname($app->request->getScriptFile())); 170 | exec($this->buildCommand()); 171 | } 172 | 173 | /** 174 | * Determine if the given event should run based on the Cron expression. 175 | * 176 | * @param Application $app 177 | * 178 | * @return bool 179 | */ 180 | public function isDue(Application $app) 181 | { 182 | return $this->expressionPasses() && $this->filtersPass($app); 183 | } 184 | 185 | /** 186 | * Determine if the Cron expression passes. 187 | * 188 | * @return bool 189 | */ 190 | protected function expressionPasses() 191 | { 192 | $date = new \DateTime('now'); 193 | if ($this->_timezone) { 194 | $date->setTimezone($this->_timezone); 195 | } 196 | 197 | return CronExpression::factory($this->_expression)->isDue($date); 198 | } 199 | 200 | /** 201 | * Determine if the filters pass for the event. 202 | * 203 | * @param Application $app 204 | * 205 | * @return bool 206 | */ 207 | protected function filtersPass(Application $app) 208 | { 209 | if (is_callable($this->_filter) && call_user_func($this->_filter, $app) === false) { 210 | return false; 211 | } 212 | 213 | if (is_callable($this->_reject) && call_user_func($this->_reject, $app)) { 214 | return false; 215 | } 216 | 217 | return true; 218 | } 219 | 220 | /** 221 | * Schedule the event to run hourly. 222 | * 223 | * @return $this 224 | */ 225 | public function hourly() 226 | { 227 | return $this->cron('0 * * * * *'); 228 | } 229 | 230 | /** 231 | * The Cron expression representing the event's frequency. 232 | * 233 | * @param string $expression 234 | * 235 | * @return $this 236 | */ 237 | public function cron($expression) 238 | { 239 | $this->_expression = $expression; 240 | 241 | return $this; 242 | } 243 | 244 | /** 245 | * Schedule the event to run daily. 246 | * 247 | * @return $this 248 | */ 249 | public function daily() 250 | { 251 | return $this->cron('0 0 * * * *'); 252 | } 253 | 254 | /** 255 | * Schedule the command at a given time. 256 | * 257 | * @param string $time 258 | * 259 | * @return $this 260 | */ 261 | public function at($time) 262 | { 263 | return $this->dailyAt($time); 264 | } 265 | 266 | /** 267 | * Schedule the event to run daily at a given time (10:00, 19:30, etc). 268 | * 269 | * @param string $time 270 | * 271 | * @return $this 272 | */ 273 | public function dailyAt($time) 274 | { 275 | $segments = explode(':', $time); 276 | 277 | return $this->spliceIntoPosition(2, (int) $segments[0]) 278 | ->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0'); 279 | } 280 | 281 | /** 282 | * Splice the given value into the given position of the expression. 283 | * 284 | * @param int $position 285 | * @param string $value 286 | * 287 | * @return Event 288 | */ 289 | protected function spliceIntoPosition($position, $value) 290 | { 291 | $segments = explode(' ', $this->_expression); 292 | $segments[$position - 1] = $value; 293 | 294 | return $this->cron(implode(' ', $segments)); 295 | } 296 | 297 | /** 298 | * Schedule the event to run twice daily. 299 | * 300 | * @return $this 301 | */ 302 | public function twiceDaily() 303 | { 304 | return $this->cron('0 1,13 * * * *'); 305 | } 306 | 307 | /** 308 | * Schedule the event to run only on weekdays. 309 | * 310 | * @return $this 311 | */ 312 | public function weekdays() 313 | { 314 | return $this->spliceIntoPosition(5, '1-5'); 315 | } 316 | 317 | /** 318 | * Schedule the event to run only on Mondays. 319 | * 320 | * @return $this 321 | */ 322 | public function mondays() 323 | { 324 | return $this->days(1); 325 | } 326 | 327 | /** 328 | * Set the days of the week the command should run on. 329 | * 330 | * @param array|int $days 331 | * 332 | * @return $this 333 | */ 334 | public function days($days) 335 | { 336 | $days = is_array($days) ? $days : func_get_args(); 337 | 338 | return $this->spliceIntoPosition(5, implode(',', $days)); 339 | } 340 | 341 | /** 342 | * Schedule the event to run only on Tuesdays. 343 | * 344 | * @return $this 345 | */ 346 | public function tuesdays() 347 | { 348 | return $this->days(2); 349 | } 350 | 351 | /** 352 | * Schedule the event to run only on Wednesdays. 353 | * 354 | * @return $this 355 | */ 356 | public function wednesdays() 357 | { 358 | return $this->days(3); 359 | } 360 | 361 | /** 362 | * Schedule the event to run only on Thursdays. 363 | * 364 | * @return $this 365 | */ 366 | public function thursdays() 367 | { 368 | return $this->days(4); 369 | } 370 | 371 | /** 372 | * Schedule the event to run only on Fridays. 373 | * 374 | * @return $this 375 | */ 376 | public function fridays() 377 | { 378 | return $this->days(5); 379 | } 380 | 381 | /** 382 | * Schedule the event to run only on Saturdays. 383 | * 384 | * @return $this 385 | */ 386 | public function saturdays() 387 | { 388 | return $this->days(6); 389 | } 390 | 391 | /** 392 | * Schedule the event to run only on Sundays. 393 | * 394 | * @return $this 395 | */ 396 | public function sundays() 397 | { 398 | return $this->days(0); 399 | } 400 | 401 | /** 402 | * Schedule the event to run weekly. 403 | * 404 | * @return $this 405 | */ 406 | public function weekly() 407 | { 408 | return $this->cron('0 0 * * 0 *'); 409 | } 410 | 411 | /** 412 | * Schedule the event to run weekly on a given day and time. 413 | * 414 | * @param int $day 415 | * @param string $time 416 | * 417 | * @return $this 418 | */ 419 | public function weeklyOn($day, $time = '0:0') 420 | { 421 | $this->dailyAt($time); 422 | 423 | return $this->spliceIntoPosition(5, $day); 424 | } 425 | 426 | /** 427 | * Schedule the event to run monthly. 428 | * 429 | * @return $this 430 | */ 431 | public function monthly() 432 | { 433 | return $this->cron('0 0 1 * * *'); 434 | } 435 | 436 | /** 437 | * Schedule the event to run yearly. 438 | * 439 | * @return $this 440 | */ 441 | public function yearly() 442 | { 443 | return $this->cron('0 0 1 1 * *'); 444 | } 445 | 446 | /** 447 | * Schedule the event to run every minute. 448 | * 449 | * @return $this 450 | */ 451 | public function everyMinute() 452 | { 453 | return $this->cron('* * * * * *'); 454 | } 455 | 456 | /** 457 | * Schedule the event to run every N minutes. 458 | * 459 | * @param int|string $minutes 460 | * 461 | * @return $this 462 | */ 463 | public function everyNMinutes($minutes) 464 | { 465 | return $this->cron('*/' . $minutes . ' * * * * *'); 466 | } 467 | 468 | /** 469 | * Schedule the event to run every five minutes. 470 | * 471 | * @return $this 472 | */ 473 | public function everyFiveMinutes() 474 | { 475 | return $this->everyNMinutes(5); 476 | } 477 | 478 | /** 479 | * Schedule the event to run every ten minutes. 480 | * 481 | * @return $this 482 | */ 483 | public function everyTenMinutes() 484 | { 485 | return $this->everyNMinutes(10); 486 | } 487 | 488 | /** 489 | * Schedule the event to run every thirty minutes. 490 | * 491 | * @return $this 492 | */ 493 | public function everyThirtyMinutes() 494 | { 495 | return $this->cron('0,30 * * * * *'); 496 | } 497 | 498 | /** 499 | * Set the timezone the date should be evaluated on. 500 | * 501 | * @param \DateTimeZone|string $timezone 502 | * 503 | * @return $this 504 | */ 505 | public function timezone($timezone) 506 | { 507 | $this->_timezone = $timezone; 508 | 509 | return $this; 510 | } 511 | 512 | /** 513 | * @return bool 514 | */ 515 | public function hasTimezone() 516 | { 517 | return $this->_timezone !== null; 518 | } 519 | 520 | /** 521 | * @return \DateTimeZone|string 522 | */ 523 | public function getTimezone() 524 | { 525 | return $this->_timezone; 526 | } 527 | 528 | /** 529 | * Set which user the command should run as. 530 | * 531 | * @param string $user 532 | * 533 | * @return $this 534 | */ 535 | public function user($user) 536 | { 537 | $this->_user = $user; 538 | 539 | return $this; 540 | } 541 | 542 | /** 543 | * Register a callback to further filter the schedule. 544 | * 545 | * @param \Closure $callback 546 | * 547 | * @return $this 548 | */ 549 | public function when(\Closure $callback) 550 | { 551 | $this->_filter = $callback; 552 | 553 | return $this; 554 | } 555 | 556 | /** 557 | * Register a callback to further filter the schedule. 558 | * 559 | * @param \Closure $callback 560 | * 561 | * @return $this 562 | */ 563 | public function skip(\Closure $callback) 564 | { 565 | $this->_reject = $callback; 566 | 567 | return $this; 568 | } 569 | 570 | /** 571 | * Send the output of the command to a given location. 572 | * 573 | * @param string $location 574 | * 575 | * @return $this 576 | */ 577 | public function sendOutputTo($location) 578 | { 579 | $this->_output = $location; 580 | 581 | return $this; 582 | } 583 | 584 | /** 585 | * E-mail the results of the scheduled operation. 586 | * 587 | * @param array $addresses 588 | * 589 | * @return $this 590 | * 591 | * @throws \LogicException 592 | */ 593 | public function emailOutputTo($addresses) 594 | { 595 | if (is_null($this->_output) || $this->_output == $this->getDefaultOutput()) { 596 | throw new InvalidCallException('Must direct output to a file in order to e-mail results.'); 597 | } 598 | $addresses = is_array($addresses) ? $addresses : func_get_args(); 599 | 600 | return $this->then(function (Application $app) use ($addresses) { 601 | $this->emailOutput($app->mailer, $addresses); 602 | }); 603 | } 604 | 605 | /** 606 | * Register a callback to be called after the operation. 607 | * 608 | * @param \Closure $callback 609 | * 610 | * @return $this 611 | */ 612 | public function then(\Closure $callback) 613 | { 614 | $this->_afterCallbacks[] = $callback; 615 | 616 | return $this; 617 | } 618 | 619 | /** 620 | * E-mail the output of the event to the recipients. 621 | * 622 | * @param MailerInterface $mailer 623 | * @param array $addresses 624 | */ 625 | protected function emailOutput(MailerInterface $mailer, $addresses) 626 | { 627 | $mailer->compose() 628 | ->setTextBody(file_get_contents($this->_output)) 629 | ->setSubject($this->getEmailSubject()) 630 | ->setTo($addresses) 631 | ->send(); 632 | } 633 | 634 | /** 635 | * Get the e-mail subject line for output results. 636 | * 637 | * @return string 638 | */ 639 | protected function getEmailSubject() 640 | { 641 | if ($this->_description) { 642 | return 'Scheduled Job Output (' . $this->_description . ')'; 643 | } 644 | 645 | return 'Scheduled Job Output'; 646 | } 647 | 648 | /** 649 | * Register a callback to the ping a given URL after the job runs. 650 | * 651 | * @param string $url 652 | * 653 | * @return $this 654 | */ 655 | public function thenPing($url) 656 | { 657 | return $this->then(function () use ($url) { 658 | (new HttpClient())->get($url); 659 | }); 660 | } 661 | 662 | /** 663 | * Set the human-friendly description of the event. 664 | * 665 | * @param string $description 666 | * 667 | * @return $this 668 | */ 669 | public function description($description) 670 | { 671 | $this->_description = $description; 672 | 673 | return $this; 674 | } 675 | 676 | /** 677 | * Get the Cron description for the event. 678 | * 679 | * @return string 680 | */ 681 | public function getDescription() 682 | { 683 | return $this->_description; 684 | } 685 | 686 | /** 687 | * Get the summary of the event for display. 688 | * 689 | * @return string 690 | */ 691 | public function getSummaryForDisplay() 692 | { 693 | if (is_string($this->_description)) { 694 | return $this->_description; 695 | } 696 | 697 | return $this->buildCommand(); 698 | } 699 | 700 | /** 701 | * Get the Cron expression for the event. 702 | * 703 | * @return string 704 | */ 705 | public function getExpression() 706 | { 707 | return $this->_expression; 708 | } 709 | 710 | /** 711 | * Get default output 712 | * 713 | * @return string 714 | */ 715 | public function getDefaultOutput() 716 | { 717 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 718 | return 'NUL'; 719 | } else { 720 | return '/dev/null'; 721 | } 722 | } 723 | } 724 | --------------------------------------------------------------------------------