TracingStepExecutedListener   F
last analyzed

Complexity

Total Complexity 73

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Test Coverage

Coverage 82.99%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 137
dl 0
loc 244
ccs 122
cts 147
cp 0.8299
rs 2.56
c 2
b 0
f 0
wmc 73

11 Methods

Rating   Name   Duplication   Size   Complexity  
A enable() 0 3 1
A __construct() 0 3 1
A setMinVerbosity() 0 3 1
B onBeforeStepExecution() 0 28 8
A echoMessage() 0 8 4
A onMigrationSuspended() 0 15 3
A setOutput() 0 3 1
D getObjectIdentifierAsString() 0 51 19
F onStepExecuted() 0 77 31
A onMigrationAborted() 0 15 3
A disable() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like TracingStepExecutedListener often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TracingStepExecutedListener, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\EventListener;
4
5
use Kaliop\eZMigrationBundle\API\Event\BeforeStepExecutionEvent;
6
use Kaliop\eZMigrationBundle\API\Event\StepExecutedEvent;
7
use Kaliop\eZMigrationBundle\API\Event\MigrationAbortedEvent;
8
use Kaliop\eZMigrationBundle\API\Event\MigrationSuspendedEvent;
9
use Kaliop\eZMigrationBundle\API\Collection\AbstractCollection;
10
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
/**
14
 * A listener designed to give feedback on the execution of migration steps
15
 *
16
 * @todo add proper support for plural forms, as well as for proper sentences when dealing with empty collections
17
 */
18
class TracingStepExecutedListener
19
{
20
    /** @var  OutputInterface $output */
21
    protected $output;
22
    protected $minVerbosityLevel = OutputInterface::VERBOSITY_VERBOSE;
23
    protected $entity = 'migration';
24
    protected $enabled = true;
25
    protected $stepStartTime;
26
    protected $stepStartMemory;
27
28 102
    public function __construct($enabled = true)
29
    {
30 102
        $this->enabled = $enabled;
31 102
    }
32
33 98
    public function setOutput(OutputInterface $output)
34
    {
35 98
        $this->output = $output;
36 98
    }
37
38
    /**
39
     * NB: only works when using an OutputInterface to echo output
40
     * @param int $level
41
     */
42
    public function setMinVerbosity($level)
43
    {
44
        $this->minVerbosityLevel = $level;
45
    }
46
47
    public function enable()
48
    {
49
        $this->enabled = true;
50
    }
51
52
    public function disable()
53
    {
54
        $this->enabled = false;
55
    }
56
57 89
    public function onBeforeStepExecution(BeforeStepExecutionEvent $event) {
58 89
        if (!$this->enabled) {
59
            return;
60
        }
61
62
        // optimization - only valid because we only echo in this method at std levels and higher
63 89
        if ($this->output && $this->output->getVerbosity() < $this->minVerbosityLevel) {
64 78
            return;
65
        }
66
67 11
        if ($this->output && $this->output->isVeryVerbose()) {
0 ignored issues
show
Bug introduced by
The method isVeryVerbose() does not exist on Symfony\Component\Console\Output\OutputInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

67
        if ($this->output && $this->output->/** @scrutinizer ignore-call */ isVeryVerbose()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
68 6
            $this->stepStartTime = microtime(true);
69 6
            $this->stepStartMemory = memory_get_usage(true);
70
        }
71
72 11
        $type = $event->getStep()->type;
73 11
        $dsl = $event->getStep()->dsl;
74 11
        $context = $event->getStep()->context;
75 11
        $stepNr = '';
76 11
        if (isset($context['step'])) {
77 11
            $stepNr = "{$context['step']}: ";
78
        }
79 11
        if (isset($dsl['mode'])) {
80 7
            $type .= ' / ' . $dsl['mode'];
81
        }
82 11
        $out = $this->entity . " step $stepNr'$type' will be executed...";
83
84 11
        $this->echoMessage($out, OutputInterface::VERBOSITY_VERY_VERBOSE);
85 11
    }
86
87 63
    public function onStepExecuted(StepExecutedEvent $event)
88
    {
89 63
        if (!$this->enabled) {
90
            return;
91
        }
92
93
        // optimization - only valid because we only echo in this method at std levels and higher
94 63
        if ($this->output && $this->output->getVerbosity() < $this->minVerbosityLevel) {
95 56
            return;
96
        }
97
98 7
        if ($this->output && $this->output->isVeryVerbose()) {
99 5
            $stepTime = microtime(true) - $this->stepStartTime;
100 5
            $stepMemory = memory_get_usage(true) - $this->stepStartMemory;
101
        }
102
103 7
        $obj = $event->getResult();
104 7
        $type = $event->getStep()->type;
105 7
        $dsl = $event->getStep()->dsl;
106 7
        $context = $event->getStep()->context;
107 7
        $stepNr = '';
108 7
        if (isset($context['step'])) {
109 7
            $stepNr = "{$context['step']}: ";
110
        }
111
112 7
        switch ($type) {
113 7
            case 'trash':
114
                $type = 'trashed_item';
115
                // fall through voluntarily
116 7
            case 'content':
117 7
            case 'content_type':
118 7
            case 'content_type_group':
119 7
            case 'language':
120 7
            case 'location':
121 7
            case 'object_state':
122 7
            case 'object_state_group':
123 7
            case 'role':
124 7
            case 'section':
125 7
            case 'tag':
126 7
            case 'user':
127 7
            case 'user_group':
128 2
                $action = isset($dsl['mode']) ? ($dsl['mode'] == 'load' ? 'loaded' : ($dsl['mode'] . 'd')) : 'acted upon';
129 2
                $verb = 'has';
130 2
                $prefix = '';
131 2
                $label = '';
132 2
                if ($obj instanceof AbstractCollection || is_array($obj)) {
133 2
                    $count = count($obj);
134 2
                    if ($count == 0) {
135
                        $prefix = 'no ';
136
                    } else {
137 2
                        $label = ' ' . $this->getObjectIdentifierAsString($obj);
138 2
                        if (count($obj) > 1) {
139
                            $verb = 'have';
140
                        }
141
                    }
142
                }
143 2
                $out = $this->entity . " step {$stepNr}{$prefix}{$type}{$label} {$verb} been {$action}";
144 2
                break;
145 7
            case 'sql':
146
                $out = $this->entity . " step {$stepNr}sql has been executed";
147
                break;
148 7
            case 'php':
149
                $out = $this->entity . " step {$stepNr}class '{$dsl['class']}' has been executed";
150
                break;
151
            default:
152
                // custom migration step types...
153 7
                if (isset($dsl['mode'])) {
154 5
                    $type .= ' / ' . $dsl['mode'];
155
                }
156 7
                $out = $this->entity . " step $stepNr'$type' has been executed";
157
        }
158
159 7
        if ($this->output && $this->output->isVeryVerbose()) {
160 5
           $out .= sprintf(". <info>Time taken: %.3f secs, memory delta: %d bytes</info>", $stepTime, $stepMemory);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $stepTime does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $stepMemory does not seem to be defined for all execution paths leading up to this point.
Loading history...
161
        }
162
163 7
        $this->echoMessage($out);
164 7
    }
165
166 4
    public function onMigrationAborted(MigrationAbortedEvent $event)
167
    {
168 4
        if (!$this->enabled) {
169
            return;
170
        }
171
172 4
        $type = $event->getStep()->type;
173 4
        $dsl = $event->getStep()->dsl;
174 4
        if (isset($dsl['mode'])) {
175 2
            $type .= '/' . $dsl['mode'];
176
        }
177
178 4
        $out = $this->entity . " aborted with status " . $event->getException()->getCode() . " during execution of step '$type'. Message: " . $event->getException()->getMessage();
179
180 4
        $this->echoMessage($out);
181 4
    }
182
183 1
    public function onMigrationSuspended(MigrationSuspendedEvent $event)
184
    {
185 1
        if (!$this->enabled) {
186
            return;
187
        }
188
189 1
        $type = $event->getStep()->type;
190 1
        $dsl = $event->getStep()->dsl;
191 1
        if (isset($dsl['mode'])) {
192 1
            $type .= '/' . $dsl['mode'];
193
        }
194
195 1
        $out = $this->entity . " suspended during execution of step '$type'. Message: " . $event->getException()->getMessage();
196
197 1
        $this->echoMessage($out);
198 1
    }
199
200 13
    protected function echoMessage($out, $verbosity = null)
201
    {
202 13
        if ($this->output) {
203 9
            if ($this->output->getVerbosity() >= ($verbosity ? $verbosity : $this->minVerbosityLevel)) {
204 9
                $this->output->writeln($out);
205
            }
206
        } else {
207 4
            echo $out . "\n";
208
        }
209 13
    }
210
211 2
    protected function getObjectIdentifierAsString($objOrCollection)
212
    {
213 2
        if ($objOrCollection instanceof AbstractCollection || is_array($objOrCollection)) {
214 2
            $out = array();
215 2
            if ($objOrCollection instanceof AbstractCollection) {
216 2
                $objOrCollection = $objOrCollection->getArrayCopy();
217
            }
218
            // totally arbitrary limit
219 2
            foreach (array_slice($objOrCollection, 0, 25) as $obj) {
220 2
                $out[] = $this->getObjectIdentifierAsString($obj);
221
            }
222 2
            if (count($objOrCollection) > 25) {
223
                $out[24] = 'etc...';
224
            }
225 2
            return implode(", ", $out);
226
        }
227
228 2
        switch (gettype($objOrCollection)) {
229
230 2
            case 'object':
231 2
                if ($objOrCollection instanceof \eZ\Publish\API\Repository\Values\Content\Content ||
232 2
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\User\UserGroup
233
                ) {
234 2
                    return "'" . $objOrCollection->contentInfo->name . "'";
235
                }
236 2
                if ($objOrCollection instanceof \eZ\Publish\API\Repository\Values\ContentType\ContentType ||
237 1
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup ||
238 1
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\ObjectState\ObjectState ||
239 1
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup ||
240 1
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\User\Role ||
241 2
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\Content\Section
242
                ) {
243 2
                    return "'" . $objOrCollection->identifier . "'";
244
                }
245 1
                if ($objOrCollection instanceof \eZ\Publish\API\Repository\Values\Content\Location ||
246 1
                    $objOrCollection instanceof \eZ\Publish\API\Repository\Values\Content\TrashItem) {
247
                    return "'" . $objOrCollection->pathString . "'";
248
                }
249 1
                if ($objOrCollection instanceof \eZ\Publish\API\Repository\Values\Content\Language) {
250
                    return "'" . $objOrCollection->languageCode . "'";
251
                }
252 1
                if ($objOrCollection instanceof \eZ\Publish\API\Repository\Values\User\User) {
253
                    return "'" . $objOrCollection->login . "'";
254
                }
255
                // unknown objects - we can't know what the desired identifier is...
256 1
                return '';
257
258
            default:
259
                // scalars: the identifier is the value...
260
                /// @todo make readable NULL, true/false
261
                return "'" . $objOrCollection . "'";
262
        }
263
    }
264
}
265