File indexing completed on 2024-12-22 05:36:18
0001 <?php 0002 /** 0003 * @author Krzysztof Suszyński <k.suszynski@mediovski.pl> 0004 * @version 0.2 0005 * @package php.manager.crontab 0006 * 0007 * Copyright (c) 2012 Krzysztof Suszyński <k.suszynski@mediovski.pl> 0008 * 0009 * Permission is hereby granted, free of charge, to any person obtaining a copy 0010 * of this software and associated documentation files (the "Software"), to deal 0011 * in the Software without restriction, including without limitation the rights 0012 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0013 * copies of the Software, and to permit persons to whom the Software is 0014 * furnished to do so, subject to the following conditions: 0015 * 0016 * The above copyright notice and this permission notice shall be included in 0017 * all copies or substantial portions of the Software. 0018 * 0019 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0020 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0021 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0022 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0023 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 0024 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 0025 * THE SOFTWARE. 0026 * 0027 */ 0028 0029 //namespace Crontab\Manager; 0030 0031 /** 0032 * Crontab Entry object 0033 * 0034 * @author Krzysztof Suszyński <k.suszynski@mediovski.pl> 0035 */ 0036 class Crontab_Manager_CronEntry 0037 { 0038 /** 0039 * @var array|null 0040 */ 0041 public $comments = null; 0042 /** 0043 * @var string|null 0044 */ 0045 public $lineComment = null; 0046 /** 0047 * Minute (0 - 59) 0048 * 0049 * @var string 0050 */ 0051 private $minute = 0; 0052 /** 0053 * Hour (0 - 23) 0054 * 0055 * @var string 0056 */ 0057 private $hour = 10; 0058 /** 0059 * Day of Month (1 - 31) 0060 * 0061 * @var string 0062 */ 0063 private $dayOfMonth = '*'; 0064 /** 0065 * Month (1 - 12) OR jan,feb,mar,apr... 0066 * 0067 * @var string 0068 */ 0069 private $month = '*'; 0070 /** 0071 * Day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat 0072 * 0073 * @var string 0074 */ 0075 private $dayOfWeek = '*'; 0076 /** 0077 * Job to be done 0078 * 0079 * @var string 0080 */ 0081 private $job = null; 0082 /** 0083 * Group of job 0084 * 0085 * @var string|null 0086 */ 0087 private $group = null; 0088 /** 0089 * Cron manager 0090 * 0091 * @var Crontab_Manager_CrontabManager|null 0092 */ 0093 private $_manager; 0094 /** 0095 * @var string 0096 */ 0097 private $_root = ''; 0098 0099 /** 0100 * Constructor 0101 * 0102 * @param Crontab_Manager_CrontabManager $manager 0103 * @param string|null $group 0104 */ 0105 public function __construct( 0106 $jobSpec = null, 0107 Crontab_Manager_CrontabManager $manager = null, 0108 $group = null 0109 ) { 0110 if ($jobSpec) { 0111 $this->_parse($jobSpec); 0112 } 0113 $this->_manager = $manager; 0114 if ($group) { 0115 $this->group = $group; 0116 } 0117 } 0118 0119 /** 0120 * Parse crontab line into CronEntry object 0121 * 0122 * @param string $jobSpec 0123 * @return CronEntry 0124 * @throw \InvalidArgumentException if $jobSpec isn't crontab entry 0125 */ 0126 private function _parse($jobSpec) 0127 { 0128 $regex = '/^\s*(([^\s\#]+)\s+([^\s\#]+)\s+([^\s\#]+)\s+([^\s\#]+)\s+' . 0129 '([^\s\#]+))\s+([^\#]+)(?:#(.*))?$/'; 0130 if (!preg_match($regex, $jobSpec, $match)) { 0131 throw new \InvalidArgumentException('$jobSpec must be crontab compatibile entry'); 0132 } 0133 list(, , 0134 $minute, 0135 $hour, 0136 $dayOfMonth, 0137 $month, 0138 $dayOfWeek, 0139 $command) = $match; 0140 if (isset($match[8])) { 0141 $lineComment = $match[8]; 0142 $this->lineComment = trim($lineComment); 0143 } 0144 $this 0145 ->onMinute($minute) 0146 ->onHour($hour) 0147 ->onDayOfMonth($dayOfMonth) 0148 ->onMonth($month) 0149 ->onDayOfWeek($dayOfWeek); 0150 $this->doJob($command); 0151 0152 return $this; 0153 } 0154 0155 /** 0156 * Set minute or minutes 0157 * 0158 * @param string $minute required 0159 * 0160 * @return CronEntry 0161 */ 0162 public function onMinute($minute) 0163 { 0164 $this->minute = $minute; 0165 return $this; 0166 } 0167 0168 /** 0169 * Add job to the jobs array. 0170 * 0171 * Add job to the jobs array. Each time segment should be set before calling 0172 * this method. The job should include the absolute path to the commands 0173 * being used. 0174 * 0175 * @param string $job required 0176 * @param string|null $group optional 0177 * 0178 * @param bool $autoAdd 0179 * 0180 * @return Crontab_Manager_CrontabManager 0181 */ 0182 public function doJob($job, $group = null, $autoAdd = false) 0183 { 0184 $this->job = $job; 0185 $this->job = preg_replace('/\\\n/m', '', $this->job); 0186 if ($group) { 0187 $this->group = $group; 0188 } 0189 if ($autoAdd && $this->_manager) { 0190 $this->_manager->add($this); 0191 } 0192 0193 return $this->_manager; 0194 } 0195 0196 /** 0197 * Set root directory for relative commands 0198 * 0199 * @param string $root 0200 */ 0201 public function setRootForCommands($root) 0202 { 0203 $this->_root = $root; 0204 } 0205 0206 /** 0207 * Set hour or hours 0208 * 0209 * @param string $hour required 0210 * 0211 * @return CronEntry 0212 */ 0213 public function onHour($hour) 0214 { 0215 $this->hour = $hour; 0216 return $this; 0217 } 0218 0219 /** 0220 * Set day of month or days of month 0221 * 0222 * @param string $dayOfMonth required 0223 * 0224 * @return CronEntry 0225 */ 0226 public function onDayOfMonth($dayOfMonth) 0227 { 0228 $this->dayOfMonth = $dayOfMonth; 0229 return $this; 0230 } 0231 0232 /** 0233 * Set month or months 0234 * 0235 * @param string $month required 0236 * 0237 * @return CronEntry 0238 */ 0239 public function onMonth($month) 0240 { 0241 $this->month = $month; 0242 return $this; 0243 } 0244 0245 /** 0246 * Set day of week or days of week 0247 * 0248 * @param string $minute required 0249 * 0250 * @return CronEntry 0251 */ 0252 public function onDayOfWeek($day) 0253 { 0254 $this->dayOfWeek = $day; 0255 return $this; 0256 } 0257 0258 /** 0259 * Set entire time code with one public function. 0260 * 0261 * Set entire time code with one public function. This has to be a 0262 * complete entry. See http://en.wikipedia.org/wiki/Cron#crontab_syntax 0263 * 0264 * @param string $timeCode required 0265 * 0266 * @return CronEntry 0267 */ 0268 public function on($timeCode) 0269 { 0270 list( 0271 $this->minute, 0272 $this->hour, 0273 $this->dayOfMonth, 0274 $this->month, 0275 $this->dayOfWeek 0276 ) = preg_split('/\s+/', trim($timeCode)); 0277 0278 return $this; 0279 } 0280 0281 /** 0282 * Adds comments to this job 0283 * 0284 * @param string[] $comments 0285 * 0286 * @return CronEntry 0287 */ 0288 public function addComments(array $comments) 0289 { 0290 $this->comments = $comments; 0291 return $this; 0292 } 0293 0294 /** 0295 * Render to string method 0296 * 0297 * @return string 0298 */ 0299 public function __toString() 0300 { 0301 return $this->render(true); 0302 } 0303 0304 /** 0305 * Render to string method 0306 * 0307 * @return string 0308 */ 0309 public function render($commentEntry = true) 0310 { 0311 if (empty($this->job)) { 0312 return ''; 0313 } 0314 $entry = array( 0315 $this->minute, 0316 $this->hour, 0317 $this->dayOfMonth, 0318 $this->month, 0319 $this->dayOfWeek, 0320 $this->_getFullCommand() 0321 ); 0322 $entry = join("\t", $entry); 0323 if ($commentEntry) { 0324 $hash = base_convert( 0325 $this->_signedInt(crc32($entry . $this->group)), 0326 10, 36 0327 ); 0328 $comments = is_array($this->comments) ? $this->comments : array(); 0329 $comments = $this->_fixComments($comments); 0330 $comments = join("\n", $comments); 0331 if (!empty($comments)) { 0332 $comments .= "\n"; 0333 } 0334 $entry = $comments . $entry . " # "; 0335 if ($this->lineComment) { 0336 $entry .= $this->lineComment . ' '; 0337 } 0338 $entry .= $hash; 0339 } 0340 return $entry; 0341 } 0342 0343 /** 0344 * Retrives full command path if can and should 0345 * 0346 * @return string 0347 */ 0348 private function _getFullCommand() 0349 { 0350 $parts = preg_split('/\s+/', $this->job); 0351 reset($parts); 0352 $first = current($parts); 0353 unset($parts[key($parts)]); 0354 ob_start(); 0355 passthru("which $first", $ret); 0356 $fullcommand = trim(ob_get_clean()); 0357 if ($ret == 0 && substr($fullcommand, 0, 1) == '/') { 0358 return trim($fullcommand . ' ' . join(' ', $parts)); 0359 } else { 0360 $root = $this->_root; 0361 if ($this->_root) { 0362 $root = rtrim($this->_root, DIRECTORY_SEPARATOR); 0363 $root .= DIRECTORY_SEPARATOR; 0364 } 0365 return trim($root . $this->job); 0366 } 0367 } 0368 0369 /** 0370 * Gets signed int from unsigned 64bit int 0371 * 0372 * @param integer $in 0373 * @return integer 0374 */ 0375 private static function _signedInt($in) 0376 { 0377 $int_max = 2147483647; // pow(2, 31) - 1 0378 if ($in > $int_max) { 0379 $out = $in - $int_max * 2 - 2; 0380 } else { 0381 $out = $in; 0382 } 0383 return $out; 0384 } 0385 0386 /** 0387 * Fix comments by adding # sign 0388 * 0389 * @param array $comments 0390 * @return array 0391 */ 0392 private function _fixComments(array $comments) 0393 { 0394 $fixed = array(); 0395 foreach ($comments as $comment) { 0396 if (!preg_match('/^\s*#/', $comment)) { 0397 $comment = '# ' . $comment; 0398 } 0399 $fixed[] = $comment; 0400 } 0401 return $fixed; 0402 } 0403 }