Tracker::getStartTime()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Tack Tracker - A library for tracking long-running task progress
5
 *
6
 * @license http://opensource.org/licenses/MIT
7
 * @link https://github.com/caseyamcl/tasktracker
8
 * @version 2.0
9
 * @package caseyamcl/tasktracker
10
 * @author Casey McLaughlin <[email protected]>
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 *
15
 * ------------------------------------------------------------------
16
 */
17
18
namespace TaskTracker;
19
20
use Symfony\Component\EventDispatcher\EventDispatcher;
21
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23
24
/**
25
 * Task Tracker
26
 *
27
 * Maintains the state of a long-running task and dispatches events
28
 * to subscribers for reporting on progress
29
 *
30
 * @author Casey McLaughlin <[email protected]>
31
 */
32
class Tracker
33
{
34
    const UNKNOWN = -1;
35
36
    const NOT_STARTED  = 0;
37
    const RUNNING      = 1;
38
    const FINISHED     = 2;
39
    const ABORTED      = 3;
40
41
    /**
42
     * @var EventDispatcherInterface
43
     */
44
    private $dispatcher;
45
46
    /**
47
     * @var int  The number of total item (-1 for infinite/unknown)
48
     */
49
    private $numTotalItems;
50
51
    /**
52
     * @var array  Array holding processed items by Tick type
53
     */
54
    private $numProcessedItems;
55
56
    /**
57
     * @var float  The start time (a float value)
58
     */
59
    private $startTime;
60
61
    /**
62
     * @var Tick  Holds the last tick
63
     */
64
    private $lastTick;
65
66
    /**
67
     * @var int  One of the constants defined above
68
     */
69
    private $status;
70
71
    /**
72
     * Build a task tracker using a list of subscribers
73
     *
74
     * This is an alternate constructor, to be used when constructing a
75
     * Tracker object using a collection of subscribers
76
     *
77
     * @param array|EventSubscriberInterface[] $subscribers
78
     * @param int                              $totalItems
79
     * @return static
80
     */
81
    public static function build(array $subscribers = [], $totalItems = self::UNKNOWN)
82
    {
83
        // New object
84
        $that = new static($totalItems);
85
86
        // Register subscribers
87
        foreach ($subscribers as $subscriber) {
88
            $that->addSubscriber($subscriber);
89
        }
90
91
        // Return it
92
        return $that;
93
    }
94
95
    /**
96
     * Constructor
97
     *
98
     * @param EventDispatcherInterface  $dispatcher
99
     * @param int                       $totalItems     Default is unknown (-1)
100
     */
101
    public function __construct($totalItems = self::UNKNOWN, EventDispatcherInterface $dispatcher = null)
102
    {
103
        $this->status            = self::NOT_STARTED;
104
        $this->dispatcher        = $dispatcher ?: new EventDispatcher();
105
        $this->numTotalItems     = $totalItems;
106
        $this->numProcessedItems = [];
107
    }
108
109
    /**
110
     * Iterate over items and track them
111
     *
112
     * @param \Traversable $items
113
     * @param callable     $itemCallback  Callback accepts arguments: (Tracker $tracker, $item)
114
     * @return Report      The final report
115
     */
116
    public function run(\Traversable $items, callable $itemCallback)
117
    {
118
        $this->start();
119
        foreach ($items as $item) {
120
            call_user_func($itemCallback, $this, $item);
121
        }
122
        return $this->finish();
123
    }
124
125
126
    /**
127
     * @return EventDispatcherInterface
128
     */
129
    public function getDispatcher()
130
    {
131
        return $this->dispatcher;
132
    }
133
134
    /**
135
     * Add a subscriber to this Tracker instance
136
     *
137
     * @param EventSubscriberInterface $subscriber
138
     */
139
    public function addSubscriber(EventSubscriberInterface $subscriber)
140
    {
141
        $this->dispatcher->addSubscriber($subscriber);
142
    }
143
144
    /**
145
     * Get number of total items to be processed (-1 for unknown)
146
     *
147
     * @return int
148
     */
149
    public function getNumTotalItems()
150
    {
151
        return $this->numTotalItems;
152
    }
153
154
    /**
155
     * Return the number of items processed, including failed/succeeded
156
     *
157
     * @param int $tickType  Tick::SUCCESS, Tick::FAIL, Tick::SKIP, or null for all
158
     * @return int
159
     */
160
    public function getNumProcessedItems($tickType = null)
161
    {
162
        if (null !== $tickType) {
163
            return (array_key_exists($tickType, $this->numProcessedItems))
164
                ? $this->numProcessedItems[$tickType]
165
                : 0;
166
        }
167
        else {
168
            return array_sum($this->numProcessedItems);
169
        }
170
    }
171
172
    /**
173
     * Get the start time in microseconds (returns NULL if not yet started)
174
     *
175
     * @return float|null
176
     */
177
    public function getStartTime()
178
    {
179
        return $this->startTime;
180
    }
181
182
    /**
183
     * Get the last report
184
     *
185
     * Returns null if not started
186
     *
187
     * @return Report|null
188
     */
189
    public function getLastTick()
190
    {
191
        return $this->lastTick;
192
    }
193
194
    /**
195
     * Get the status
196
     *
197
     * @return int
198
     */
199
    public function getStatus()
200
    {
201
        return $this->status;
202
    }
203
204
    /**
205
     * Is the tracker running?
206
     *
207
     * @return bool
208
     */
209
    public function isRunning()
210
    {
211
        return ($this->getStatus() == self::RUNNING);
212
    }
213
214
    /**
215
     * Start processing
216
     *
217
     * If this method is not called explicitely, it will automatically
218
     * be called upon first tick
219
     *
220
     * @param string $msg Optional message to include
221
     * @param array  $extraInfo
222
     */
223
    public function start($msg = null, array $extraInfo = [])
224
    {
225
        if ($this->status != self::NOT_STARTED) {
226
            throw new TrackerException("Cannot start tracker that was already started");
227
        }
228
229
        $this->status = self::RUNNING;
230
        $this->startTime = microtime(true);
231
232
        $tick = new Tick($this, Tick::SUCCESS, $msg, $extraInfo, 0);
233
        $this->dispatcher->dispatch(Events::TRACKER_START, $tick);
234
        $this->lastTick = $tick;
235
    }
236
237
    /**
238
     * Indicate progress to the tracker
239
     *
240
     * Builds a report and send it to the tick method in the output handlers
241
     *
242
     * @param int    $status SUCCESS (default), SKIP, or FAIL
243
     * @param string $msg    Message to include for this report
244
     * @param array  $extraInfo
245
     * @param int    $incrementBy
246
     * @return Report
247
     */
248
    public function tick($status = Tick::SUCCESS, $msg = null, array $extraInfo = [], $incrementBy = 1)
249
    {
250
        if ( ! $this->isRunning()) {
251
            $this->start();
252
        }
253
254
        $tick = new Tick($this, $status, $msg, $extraInfo, $incrementBy);
255
        // Increment the counter
256
        if (array_key_exists($tick->getStatus(), $this->numProcessedItems)) {
257
            $this->numProcessedItems[$tick->getStatus()] += $tick->getIncrementBy();
258
        }
259
        else {
260
            $this->numProcessedItems[$tick->getStatus()] = $tick->getIncrementBy();
261
        }
262
263
        $this->dispatcher->dispatch(Events::TRACKER_TICK, $tick);
264
265
        $this->lastTick = $tick;
266
        return $tick->getReport();
267
    }
268
269
    /**
270
     * Finish processing
271
     *
272
     * Builds a report and sends it to the finish method in the output handlers
273
     *
274
     * @param string $msg Optional message to include
275
     * @param array  $extraInfo
276
     * @return Report
277
     */
278
    public function finish($msg = null, array $extraInfo = [])
279
    {
280
        if ( ! $this->isRunning()) {
281
            throw new TrackerException("Cannot finish Tracker.  Not running.");
282
        }
283
284
        $tick = new Tick($this, Tick::SUCCESS, $msg, $extraInfo, 0);
285
        $this->status = self::FINISHED;
286
287
        $this->dispatcher->dispatch(Events::TRACKER_FINISH, $tick);
288
289
        $this->lastTick = $tick;
290
        return $tick->getReport();
291
    }
292
293
    /**
294
     * Abort processing
295
     *
296
     * Builds a reports and send it to the abort method in the output handlers
297
     *
298
     * @param string $msg Optional message to include
299
     * @param array  $extraInfo
300
     * @return Report
301
     */
302
    public function abort($msg = null, array $extraInfo = [])
303
    {
304
        if ( ! $this->isRunning()) {
305
            throw new TrackerException("Cannot abort Tracker.  Not running.");
306
        }
307
308
        $tick = new Tick($this, Tick::FAIL, $msg, $extraInfo, 0);
309
        $this->status = self::ABORTED;
310
311
        $this->dispatcher->dispatch(Events::TRACKER_ABORT, $tick);
312
        
313
        $this->lastTick = $tick;
314
        return $tick->getReport();
315
    }
316
}
317