Completed
Push — master ( cfa95b...fcac32 )
by Fabrice
01:49
created

FlowMap::duration()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 3
nop 1
1
<?php
2
3
/*
4
 * This file is part of NodalFlow.
5
 *     (c) Fabrice de Stefanis / https://github.com/fab2s/NodalFlow
6
 * This source file is licensed under the MIT license which you will
7
 * find in the LICENSE file or at https://opensource.org/licenses/MIT
8
 */
9
10
namespace fab2s\NodalFlow\Flows;
11
12
use fab2s\NodalFlow\NodalFlowException;
13
use fab2s\NodalFlow\Nodes\AggregateNodeInterface;
14
use fab2s\NodalFlow\Nodes\BranchNodeInterface;
15
use fab2s\NodalFlow\Nodes\NodeInterface;
16
17
/**
18
 * class FlowMap
19
 */
20
class FlowMap implements FlowMapInterface
21
{
22
    /**
23
     * Flow map
24
     *
25
     * @var array
26
     */
27
    protected $nodeMap = [];
28
29
    /**
30
     * @var array
31
     */
32
    protected $reverseMap = [];
33
34
    /**
35
     * The default Node Map values
36
     *
37
     * @var array
38
     */
39
    protected $nodeMapDefault = [
40
        'class'           => null,
41
        'flowId'          => null,
42
        'hash'            => null,
43
        'index'           => null,
44
        'isATraversable'  => null,
45
        'isAReturningVal' => null,
46
        'isAFlow'         => null,
47
        'num_exec'        => 0,
48
        'num_iterate'     => 0,
49
        'num_break'       => 0,
50
        'num_continue'    => 0,
51
    ];
52
53
    /**
54
     * The default Node stats values
55
     *
56
     * @var array
57
     */
58
    protected $incrementStatsDefault = [
59
        'num_exec'     => 0,
60
        'num_iterate'  => 0,
61
        'num_break'    => 0,
62
        'num_continue' => 0,
63
    ];
64
65
    /**
66
     * The Flow stats default values
67
     *
68
     * @var array
69
     */
70
    protected $flowStatsDefault = [
71
        'class'    => null,
72
        'id'       => null,
73
        'start'    => null,
74
        'end'      => null,
75
        'elapsed'  => null,
76
        'duration' => null,
77
        'mib'      => null,
78
    ];
79
80
    /**
81
     * @var array
82
     */
83
    protected $defaultFlowStats;
84
85
    /**
86
     * @var array
87
     */
88
    protected $flowStats;
89
90
    /**
91
     * @var bool
92
     */
93
    protected $resetOnRestart = false;
94
95
    /**
96
     * @var FlowInterface
97
     */
98
    protected $flow;
99
100
    /**
101
     * @var bool
102
     */
103
    protected $hasRun = false;
104
105
    /**
106
     * Instantiate a Flow Status
107
     *
108
     * @param FlowInterface $flow
109
     *
110
     * @internal param string $status The flow status
111
     */
112
    public function __construct(FlowInterface $flow)
113
    {
114
        $this->flow             = $flow;
115
        $this->defaultFlowStats = array_replace($this->flowStatsDefault, $this->incrementStatsDefault, [
116
            'class' => get_class($this->flow),
117
            'id'    => $this->flow->getId(),
118
        ]);
119
        $this->flowStats        = $this->defaultFlowStats;
120
    }
121
122
    /**
123
     * @param array $flowIncrements
124
     *
125
     * @throws NodalFlowException
126
     *
127
     * @return $this
128
     */
129
    public function setFlowIncrement(array $flowIncrements)
130
    {
131
        if ($this->hasRun) {
132
            throw new NodalFlowException('Cannot set custom increment after Flow started');
133
        }
134
135
        $this->defaultFlowStats = array_replace($flowIncrements, $this->defaultFlowStats);
136
        $this->flowStats        = $this->defaultFlowStats;
137
138
        return $this;
139
    }
140
141
    /**
142
     * @param NodeInterface $node
143
     * @param int           $index
144
     *
145
     * @throws NodalFlowException
146
     */
147
    public function register(NodeInterface $node, $index)
148
    {
149
        $this->enforceUniqueness($node);
150
        $nodeHash                      = $node->getNodeHash();
151
        $this->nodeMap[$nodeHash]      = array_replace($this->nodeMapDefault, [
152
            'class'           => get_class($node),
153
            'flowId'          => $node->getCarrier()->getId(),
154
            'hash'            => $nodeHash,
155
            'index'           => $index,
156
            'isATraversable'  => $node->isTraversable(),
157
            'isAReturningVal' => $node->isReturningVal(),
158
            'isAFlow'         => $node->isFlow(),
159
        ]);
160
161
        if (isset($this->reverseMap[$index])) {
162
            // replacing a note, maintain nodeMap accordingly
163
            unset($this->nodeMap[$this->reverseMap[$index]]);
164
        }
165
166
        $this->reverseMap[$index] = $nodeHash;
167
    }
168
169
    /**
170
     * Triggered right before the flow starts
171
     *
172
     * @return $this
173
     */
174
    public function flowStart()
175
    {
176
        $this->flowStats['start'] = microtime(true);
177
        $this->hasRun             = true;
178
179
        return $this;
180
    }
181
182
    /**
183
     * Triggered right after the flow stops
184
     *
185
     * @return $this
186
     */
187
    public function flowEnd()
188
    {
189
        $this->flowStats['end']     = microtime(true);
190
        $this->flowStats['mib']     = memory_get_peak_usage(true) / 1048576;
191
        $this->flowStats['elapsed'] = $this->flowStats['end'] - $this->flowStats['start'];
192
193
        $this->flowStats = array_replace($this->flowStats, $this->duration($this->flowStats['elapsed']));
194
        //dd($this->duration($this->flowStats['elapsed']),$this->flowStats);
0 ignored issues
show
Unused Code Comprehensibility introduced by
79% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
195
196
        return $this;
197
    }
198
199
    /**
200
     * Get/Generate Node Map
201
     *
202
     * @return array
203
     */
204
    public function getNodeMap()
205
    {
206
        foreach ($this->flow->getNodes() as $node) {
207
            if ($node instanceof BranchNodeInterface) {
208
                $this->nodeMap[$node->getNodeHash()]['nodes'] = $node->getPayload()->getNodeMap();
209
                continue;
210
            }
211
212
            if ($node instanceof AggregateNodeInterface) {
213
                $flowId = $node->getCarrier()->getId();
214
                foreach ($node->getNodeCollection() as $aggregatedNode) {
215
                    $this->nodeMap[$node->getNodeHash()]['nodes'][$aggregatedNode->getNodeHash()] = array_replace($this->nodeMapDefault, [
216
                        'class'  => get_class($aggregatedNode),
217
                        'flowId' => $flowId,
218
                        'hash'   => $aggregatedNode->getNodeHash(),
219
                    ]);
220
                }
221
                continue;
222
            }
223
        }
224
225
        return $this->nodeMap;
226
    }
227
228
    /**
229
     * Get the latest Node stats
230
     *
231
     * @return array
232
     */
233
    public function getStats()
234
    {
235
        $result = array_intersect_key($this->flowStats, $this->defaultFlowStats);
236
        foreach ($this->flow->getNodes() as $node) {
237
            if ($node instanceof BranchNodeInterface) {
238
                $result['branches'][$node->getPayload()->getId()] = $node->getPayload()->getStats();
239
            }
240
        }
241
242
        return $result;
243
    }
244
245
    /**
246
     * @param string $nodeHash
247
     * @param array  $incrementAliases
248
     *
249
     * @throws NodalFlowException
250
     *
251
     * @return $this
252
     */
253
    public function setIncrementAlias($nodeHash, array $incrementAliases = [])
254
    {
255
        foreach ($incrementAliases as $aliasKey => $incKey) {
256
            if (!isset($this->incrementStatsDefault[$incKey])) {
257
                throw new NodalFlowException('Tried to set an increment alias to an un-registered increment', 1, null, [
258
                    'aliasKey'     => $aliasKey,
259
                    'incrementKey' => $incKey,
260
                ]);
261
            }
262
263
            $this->nodeMap[$nodeHash][$aliasKey] = &$this->nodeMap[$nodeHash][$incKey];
264
        }
265
266
        return $this;
267
    }
268
269
    /**
270
     * @param string $nodeHash
271
     * @param string $key
272
     *
273
     * @return $this
274
     */
275
    public function increment($nodeHash, $key)
276
    {
277
        ++$this->nodeMap[$nodeHash][$key];
278
279
        return $this;
280
    }
281
282
    /**
283
     * @param string $key
284
     *
285
     * @return $this
286
     */
287
    public function incrementFlow($key)
288
    {
289
        ++$this->flowStats[$key];
290
291
        return $this;
292
    }
293
294
    /**
295
     * Resets Nodes stats, can be used prior to Flow's re-exec
296
     *
297
     * @return $this
298
     */
299
    public function resetNodeStats()
300
    {
301
        foreach ($this->nodeMap as &$nodeStat) {
302
            $nodeStat = array_replace($nodeStat, $this->incrementStatsDefault);
303
        }
304
305
        return $this;
306
    }
307
308
    /**
309
     * Computes a human readable duration string from floating seconds
310
     *
311
     * @param float $seconds
312
     *
313
     * @return array<string,integer|string>
314
     */
315
    public function duration($seconds)
316
    {
317
        $result = [
318
            'hour'     => (int) floor($seconds / 3600),
319
            'min'      => (int) floor(($seconds / 60) % 60),
320
            'sec'      => $seconds % 60,
321
            'ms'       => (int) round(\fmod($seconds, 1) * 1000),
322
        ];
323
324
        $duration = '';
325
        foreach ($result as $unit => $value) {
326
            if (!empty($value) || $unit === 'ms') {
327
                $duration .= $value . "$unit ";
328
            }
329
        }
330
331
        $result['duration'] = trim($duration);
332
333
        return $result;
334
    }
335
336
    /**
337
     * @param NodeInterface $node
338
     *
339
     * @throws NodalFlowException
340
     *
341
     * @return $this
342
     */
343
    protected function enforceUniqueness(NodeInterface $node)
344
    {
345
        if (isset($this->nodeMap[$node->getNodeHash()])) {
346
            throw new NodalFlowException('Cannot reuse Node instances within a Flow', 1, null, [
347
                'duplicate_node' => get_class($node),
348
                'hash'           => $node->getNodeHash(),
349
            ]);
350
        }
351
352
        return $this;
353
    }
354
}
355