View difference between Paste ID: yLubJ79r and 8NNXiV6J
SHOW: | | - or go back to the newest paste.
1
<?php
2
3
/**
4
 * The abstract class which every cron job class needs to be extending for it 
5
 * to function properly. Nothing here needs to be changed, but if you feel it's
6
 * required to do so, please contact the author first.
7
 *
8
 * @author Stefan van Beusekom 
9
 * @version 1.1
10
 * @abstract
11
 */
12
abstract class Callcenter_Model_Cron_Abstract {
13
14
    protected $_errorList = array();
15
    protected $_handleExecList = false;
16
    protected $_lastExecuted = false;
17
    protected $_startScript = false;
18
    protected $_endScript = false;
19
    protected $_executionSuccessful = false;
20
21
    /**
22
     * Set the time limit for script execution
23
     * 
24
     * @author Stefan van Beusekom 
25
     * @since 1.1
26
     */
27
    protected function setTimelimit() {
28
        $childClass = get_called_class();
29
        set_time_limit(($childClass::CRON_TIMEOUT_MINUTES * 60) + $childClass::CRON_TIMEOUT_SECONDS);
30
        return $this;
31
    }
32
33
    /**
34
     * Set the date and time string of when this cronjob was started
35
     * 
36
     * @author Stefan van Beusekom 
37
     * @since 1.1
38
     */
39
    protected function setStartScript() {
40
        $dateTime = new DateTime();
41
        $this->_startScript = $dateTime->format("Y-m-d H:i:s");
42
        return $this;
43
    }
44
45
    /**
46
     * Set the date and time string of when this cronjob ended
47
     * 
48
     * @author Stefan van Beusekom 
49
     * @since 1.1
50
     */
51
    protected function setEndScript() {
52
        $dateTime = new DateTime();
53
        $this->_endScript = $dateTime->format("Y-m-d H:i:s");
54
        return $this;
55
    }
56
57
    /**
58
     * Get the path to the cronjob folder
59
     * 
60
     * @author Stefan van Beusekom 
61
     * @since 1.0
62
     * @return string
63
     */
64
    protected function getCronPath($file = null) {
65
        return APPLICATION_PATH . "/../cron/" . $file;
66
    }
67
68
    /**
69
     * Add an error for the current cronjob
70
     * 
71
     * @author Stefan van Beusekom 
72
     * @since 1.0
73
     * @param string $error Error message
74
     */
75
    protected function addError($error) {
76
        array_push($this->_errorList, $error);
77
    }
78
79
    /**
80
     * Return a list of errors for the current cronjob
81
     * 
82
     * @author Stefan van Beusekom 
83
     * @since 1.0
84
     * @return array
85
     */
86
    protected function getErrors() {
87
        return $this->_errorList;
88
    }
89
90
    /**
91
     * Set the current timestamp of when this cronjob was last executed
92
     * 
93
     * @author Stefan van Beusekom 
94
     * @since 1.0
95
     */
96
    protected function setLastExecuted() {
97
        $this->_handleExecList = fopen($this->getCronPath(get_called_class() . ".cron"), "w+");
98
        $cronArray = fread($this->_handleExecList, filesize($this->getCronPath(get_called_class() . ".cron")));
99
        if (($cronArray) == '')
100
            $cronArray = array();
101
        else
102
            $cronArray = unserialize($cronArray);
103
        $this->_lastExecuted = time();
104
        $cronArray[get_called_class()] = $this->_lastExecuted;
105
        fwrite($this->_handleExecList, serialize($cronArray));
106
        fclose($this->_handleExecList);
107
        return $this;
108
    }
109
110
    /**
111
     * Get the timestamp of when this cronjob was last executed
112
     * 
113
     * @author Stefan van Beusekom 
114
     * @since 1.0
115
     * @return int|boolean
116
     */
117
    protected function getLastExecuted() {
118
        if (!$this->_lastExecuted) {
119
            if (file_exists($this->getCronPath(get_called_class() . ".cron"))) {
120
                $this->_handleExecList = fopen($this->getCronPath(get_called_class() . ".cron"), "r");
121
                $cronArray = fread($this->_handleExecList, filesize($this->getCronPath(get_called_class() . ".cron")));
122
                if (!$cronArray)
123
                    $cronArray = array();
124
                else
125
                    $cronArray = unserialize($cronArray);
126
                return (key_exists(get_called_class(), $cronArray)) ? $cronArray[get_called_class()] : 0;
127
            } else {
128
                return 0;
129
            }
130
        } else {
131
            return $this->_lastExecuted;
132
        }
133
    }
134
135
    /**
136
     * Return a true or false if the current cronjob is allowed to run based on
137
     * the timestamps.
138
     * 
139
     * @author Stefan van Beusekom 
140
     * @since 1.0
141
     * @return boolean
142
     */
143
    protected function allowExecute() {
144
        $childClass = get_called_class();
145
        $dateTimePre = new DateTime();
146
        $dateTimePre->setTimestamp($this->getLastExecuted());
147
        $dateTimePre->setTime($dateTimePre->format("H"), $dateTimePre->format("i"), 0);
148
149
        if ($childClass::CRON_INTERVAL_HOURS != 0)
150
            $dateTimePre->setTime($dateTimePre->format("H"), 0, 0);
151
        if ($childClass::CRON_INTERVAL_DAYS != 0)
152
            $dateTimePre->setTime(0, 0, 0);
153
154
        $currentSeconds = (time() - $dateTimePre->getTimestamp());
155
        $intervalSeconds = ($childClass::CRON_INTERVAL_DAYS * 24 * 60 * 60) + ($childClass::CRON_INTERVAL_HOURS * 60 * 60) + ($childClass::CRON_INTERVAL_MINUTES * 60);
156
157
        $curDateObj = new DateTime();
158
        $allDateObj = new DateTime($childClass::CRON_START_DATETIME);
159
160
        if ($curDateObj->getTimestamp() < $allDateObj->getTimestamp())
161
            return false;
162
163
        if ($currentSeconds >= $intervalSeconds)
164
            return true;
165
        else
166
            return false;
167
    }
168
169
    /**
170
     * Append the log with data pertaining to the execution, or failure thereof
171
     * 
172
     * @author Stefan van Beusekom 
173
     * @since 1.0
174
     */
175
    protected function addLog() {
176
        $fileHandle = fopen($this->getCronPath(get_called_class() . ".txt"), "a");
177
        $dateObjectStart = new DateTime($this->_startScript);
178
        $dateObjectEnd = new DateTime($this->_endScript);
179
        $datePrefix = "[" . $dateObjectStart->format("d-m-Y H:i:s") . "][" . $dateObjectEnd->format("d-m-Y H:i:s") . "] ";
180
        $logEntry = "";
181
        if ($this->isSuccessful()) {
182
            $logEntry .= $datePrefix . "Cronjob was completed successfully." . "\r\n";
183
        } else {
184
            $errorPrefix = str_pad("", strlen($datePrefix), " ", STR_PAD_LEFT);
185-
            $logEntry .= $datePrefix . "Cronjob could not be completed. The following errors occured:" . "\r\n";
185+
            $logEntry .= $datePrefix . "Cronjob could not be completed. The following errors occurred:" . "\r\n";
186
            foreach ($this->getErrors() as $error) {
187
                $logEntry .= $errorPrefix . $error . "\r\n";
188
            }
189
        }
190
        fwrite($fileHandle, $logEntry);
191
        fclose($fileHandle);
192
    }
193
194
    /**
195
     * Return a true or false if the current cronjob has been successful
196
     * 
197
     * @author Stefan van Beusekom 
198
     * @since 1.0
199
     * @return boolean
200
     */
201
    protected function isSuccessful() {
202
        return ((count($this->getErrors()) == 0) ? true : false);
203
    }
204
205
    /**
206
     * The on exit callback which will produce an error message despite an 
207
     * uncatchable error being thrown.
208
     * 
209
     * @author Stefan van Beusekom 
210
     * @since 1.1
211
     * @todo Although this method logs the error, nothing can be done about its 
212
     *       ability to continue running other cronjobs. Search for possible 
213
     *       solution to this problem.
214
     */
215
    public function onExecute() {
216
        if (!$this->_executionSuccessful) {
217
            $errorMessage = "CRITICAL ERROR - Entire cron process was abruptly halted due to an uncatchable error. Please disable this class until the problem has been resolved. - No stack trace is available for debugging, but common errors are: faulty code, maximum execution time exceeded, or a server crash.";
218
            $this->addError($errorMessage);
219
            $this->addLog();
220
        }
221
    }
222
223
    /**
224
     * Execute the cronjob
225
     * 
226
     * @author Stefan van Beusekom
227
     * @since 1.0
228
     * @return boolean
229
     */
230
    public function execute() {
231
        register_shutdown_function(array($this, "onExecute"));
232
        $this->setTimelimit();
233
        if ($this->allowExecute()) {
234
            $this->setStartScript();
235
            try {
236
                $this->task();
237
            } catch (Exception $e) {
238
                $this->addError("The current task generated an exception, message: " . $e->getMessage());
239
            }
240
            if ($this->isSuccessful()) {
241
                $this->addLog();
242
                $this->setEndScript();
243
                $this->setLastExecuted();
244
            } else {
245
                $this->addLog();
246
            }
247
            $this->_executionSuccessful = true;
248
            return $this->isSuccessful();
249
        }
250
        $this->_executionSuccessful = true;
251
        return false;
252
    }
253
254
    /**
255
     * Task to be executed
256
     * 
257
     * @author Stefan van Beusekom
258
     * @since 1.0
259
     */
260
    protected abstract function task();
261
}
262
263
?>