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 }