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()) { |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|
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.