1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the SVN-Buddy library. |
4
|
|
|
* For the full copyright and license information, please view |
5
|
|
|
* the LICENSE file that was distributed with this source code. |
6
|
|
|
* |
7
|
|
|
* @copyright Alexander Obuhovich <[email protected]> |
8
|
|
|
* @link https://github.com/console-helpers/svn-buddy |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog; |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
use ConsoleHelpers\ConsoleKit\ConsoleIO; |
15
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector; |
16
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\DatabaseCollectorPlugin\IDatabaseCollectorPlugin; |
17
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; |
18
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IPlugin; |
19
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\RepositoryCollectorPlugin\IRepositoryCollectorPlugin; |
20
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionUrlBuilder; |
21
|
|
|
|
22
|
|
|
class RevisionLog |
23
|
|
|
{ |
24
|
|
|
|
25
|
|
|
const FLAG_VERBOSE = 1; |
26
|
|
|
|
27
|
|
|
const FLAG_MERGE_HISTORY = 2; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Repository path. |
31
|
|
|
* |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
private $_repositoryRootUrl; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Project path. |
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
private $_projectPath; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Ref name. |
45
|
|
|
* |
46
|
|
|
* @var string |
47
|
|
|
*/ |
48
|
|
|
private $_refName; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Repository connector. |
52
|
|
|
* |
53
|
|
|
* @var Connector |
54
|
|
|
*/ |
55
|
|
|
private $_repositoryConnector; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Console IO. |
59
|
|
|
* |
60
|
|
|
* @var ConsoleIO |
61
|
|
|
*/ |
62
|
|
|
private $_io; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Installed plugins. |
66
|
|
|
* |
67
|
|
|
* @var IPlugin[] |
68
|
|
|
*/ |
69
|
|
|
private $_plugins = array(); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Revision URL builder. |
73
|
|
|
* |
74
|
|
|
* @var RevisionUrlBuilder |
75
|
|
|
*/ |
76
|
|
|
private $_revisionUrlBuilder; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Force refresh flag filename. |
80
|
|
|
* |
81
|
|
|
* @var string |
82
|
|
|
*/ |
83
|
|
|
private $_forceRefreshFlagFilename; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Create revision log. |
87
|
|
|
* |
88
|
|
|
* @param string $repository_url Repository url. |
89
|
|
|
* @param RevisionUrlBuilder $revision_url_builder Revision URL builder. |
90
|
|
|
* @param Connector $repository_connector Repository connector. |
91
|
|
|
* @param string $working_directory Working directory. |
92
|
|
|
* @param ConsoleIO $io Console IO. |
93
|
|
|
*/ |
94
|
19 |
|
public function __construct( |
95
|
|
|
$repository_url, |
96
|
|
|
RevisionUrlBuilder $revision_url_builder, |
97
|
|
|
Connector $repository_connector, |
98
|
|
|
$working_directory, |
99
|
|
|
ConsoleIO $io = null |
100
|
|
|
) { |
101
|
19 |
|
$this->_io = $io; |
102
|
19 |
|
$this->_repositoryConnector = $repository_connector; |
103
|
|
|
|
104
|
19 |
|
$this->_repositoryRootUrl = $repository_connector->getRootUrl($repository_url); |
105
|
|
|
|
106
|
19 |
|
$relative_path = $repository_connector->getRelativePath($repository_url); |
107
|
19 |
|
$this->_projectPath = $repository_connector->getProjectUrl($relative_path) . '/'; |
108
|
19 |
|
$this->_refName = $repository_connector->getRefByPath($relative_path); |
|
|
|
|
109
|
19 |
|
$this->_revisionUrlBuilder = $revision_url_builder; |
110
|
19 |
|
$this->_forceRefreshFlagFilename = $working_directory . '/' . md5($this->_repositoryRootUrl) . '.force-refresh'; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Returns revision URL builder. |
115
|
|
|
* |
116
|
|
|
* @return RevisionUrlBuilder |
117
|
|
|
*/ |
118
|
1 |
|
public function getRevisionURLBuilder() |
119
|
|
|
{ |
120
|
1 |
|
return $this->_revisionUrlBuilder; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Queries missing revisions. |
125
|
|
|
* |
126
|
|
|
* @param boolean $is_migration Is migration. |
127
|
|
|
* |
128
|
|
|
* @return void |
129
|
|
|
* @throws \LogicException When no plugins are registered. |
130
|
|
|
*/ |
131
|
6 |
|
public function refresh($is_migration) |
132
|
|
|
{ |
133
|
6 |
|
if ( !$this->_plugins ) { |
|
|
|
|
134
|
1 |
|
throw new \LogicException('Please register at least one revision log plugin.'); |
135
|
|
|
} |
136
|
|
|
|
137
|
5 |
|
$this->_databaseReady(); |
138
|
|
|
|
139
|
5 |
|
if ( $is_migration ) { |
140
|
|
|
// Import missing data for imported commits only. |
141
|
|
|
$from_revision = 0; |
142
|
|
|
$to_revision = $this->_getAggregateRevision('max'); |
143
|
|
|
} |
144
|
|
|
else { |
145
|
|
|
// Import all data for new commits only. |
146
|
5 |
|
$from_revision = $this->_getAggregateRevision('min'); |
147
|
|
|
|
148
|
5 |
|
if ( $this->getForceRefreshFlag() ) { |
149
|
|
|
$this->_repositoryConnector->withCacheOverwrite(true); |
150
|
|
|
$this->setForceRefreshFlag(false); |
151
|
|
|
} |
152
|
|
|
|
153
|
5 |
|
$to_revision = $this->_repositoryConnector->getLastRevision($this->_repositoryRootUrl); |
154
|
|
|
} |
155
|
|
|
|
156
|
5 |
|
if ( $to_revision > $from_revision ) { |
157
|
3 |
|
$this->_queryRevisionData($from_revision, $to_revision); |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Sets force refresh flag. |
163
|
|
|
* |
164
|
|
|
* @param boolean $flag Flag. |
165
|
|
|
* |
166
|
|
|
* @return void |
167
|
|
|
*/ |
168
|
|
|
public function setForceRefreshFlag($flag) |
169
|
|
|
{ |
170
|
|
|
if ( $flag ) { |
171
|
|
|
touch($this->_forceRefreshFlagFilename); |
172
|
|
|
} |
173
|
|
|
else { |
174
|
|
|
unlink($this->_forceRefreshFlagFilename); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Gets force refresh flag. |
180
|
|
|
* |
181
|
|
|
* @return boolean |
182
|
|
|
*/ |
183
|
5 |
|
protected function getForceRefreshFlag() |
184
|
|
|
{ |
185
|
5 |
|
return file_exists($this->_forceRefreshFlagFilename); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Reparses a revision. |
190
|
|
|
* |
191
|
|
|
* @param integer $revision Revision. |
192
|
|
|
* |
193
|
|
|
* @return void |
194
|
|
|
* @throws \LogicException When no plugins are registered. |
195
|
|
|
*/ |
196
|
|
|
public function reparse($revision) |
197
|
|
|
{ |
198
|
|
|
if ( !$this->_plugins ) { |
199
|
|
|
throw new \LogicException('Please register at least one revision log plugin.'); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$this->_databaseReady(); |
203
|
|
|
$this->_queryRevisionData($revision, $revision, true); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Reports to each plugin, that database is ready for usage. |
208
|
|
|
* |
209
|
|
|
* @return void |
210
|
|
|
*/ |
211
|
5 |
|
private function _databaseReady() |
212
|
|
|
{ |
213
|
5 |
|
foreach ( $this->_plugins as $plugin ) { |
214
|
5 |
|
$plugin->whenDatabaseReady(); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Returns aggregated revision from all plugins. |
220
|
|
|
* |
221
|
|
|
* @param string $function Aggregate function. |
222
|
|
|
* |
223
|
|
|
* @return integer |
224
|
|
|
*/ |
225
|
5 |
|
private function _getAggregateRevision($function) |
226
|
|
|
{ |
227
|
5 |
|
$last_revisions = array(); |
228
|
|
|
|
229
|
5 |
|
foreach ( $this->_plugins as $plugin ) { |
230
|
5 |
|
$last_revisions[] = $plugin->getLastRevision(); |
231
|
|
|
} |
232
|
|
|
|
233
|
5 |
|
if ( count($last_revisions) > 1 ) { |
234
|
5 |
|
return call_user_func_array($function, $last_revisions); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
return current($last_revisions); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Queries missing revision data. |
242
|
|
|
* |
243
|
|
|
* @param integer $from_revision From revision. |
244
|
|
|
* @param integer $to_revision To revision. |
245
|
|
|
* @param boolean $overwrite Overwrite. |
246
|
|
|
* |
247
|
|
|
* @return void |
248
|
|
|
*/ |
249
|
3 |
|
private function _queryRevisionData($from_revision, $to_revision, $overwrite = false) |
250
|
|
|
{ |
251
|
3 |
|
$this->_useRepositoryCollectorPlugins($from_revision, $to_revision, $overwrite); |
252
|
3 |
|
$this->_useDatabaseCollectorPlugins($from_revision, $to_revision, $overwrite); |
253
|
|
|
|
254
|
3 |
|
if ( isset($this->_io) && $this->_io->isVerbose() ) { |
255
|
1 |
|
$this->_displayPluginActivityStatistics(); |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Use repository collector plugins. |
261
|
|
|
* |
262
|
|
|
* @param integer $from_revision From revision. |
263
|
|
|
* @param integer $to_revision To revision. |
264
|
|
|
* @param boolean $overwrite Overwrite. |
265
|
|
|
* |
266
|
|
|
* @return void |
267
|
|
|
*/ |
268
|
3 |
|
private function _useRepositoryCollectorPlugins($from_revision, $to_revision, $overwrite = false) |
269
|
|
|
{ |
270
|
|
|
// The "io" isn't set during autocomplete. |
271
|
3 |
|
if ( isset($this->_io) ) { |
272
|
|
|
// Create progress bar for repository plugins, where data amount is known upfront. |
273
|
2 |
|
$progress_bar = $this->_io->createProgressBar(ceil(($to_revision - $from_revision) / 200) + 1); |
|
|
|
|
274
|
2 |
|
$progress_bar->setMessage( |
275
|
2 |
|
$overwrite ? '* Reparsing revisions:' : ' * Reading missing revisions:' |
276
|
2 |
|
); |
277
|
2 |
|
$progress_bar->setFormat( |
278
|
2 |
|
'%message% %current%/%max% [%bar%] <info>%percent:3s%%</info> %elapsed:6s%/%estimated:-6s% <info>%memory:-10s%</info>' |
279
|
2 |
|
); |
280
|
2 |
|
$progress_bar->start(); |
281
|
|
|
} |
282
|
|
|
|
283
|
3 |
|
$plugins = $this->getRepositoryCollectorPlugins($overwrite); |
284
|
|
|
|
285
|
3 |
|
if ( $overwrite ) { |
286
|
|
|
$this->setPluginsOverwriteMode($plugins, true); |
287
|
|
|
} |
288
|
|
|
|
289
|
3 |
|
$range_start = $from_revision; |
290
|
3 |
|
$cache_duration = $overwrite ? null : '10 years'; |
291
|
3 |
|
$log_command_arguments = $this->_getLogCommandArguments($plugins); |
292
|
|
|
|
293
|
3 |
|
while ( $range_start <= $to_revision ) { |
294
|
3 |
|
$range_end = min($range_start + 199, $to_revision); |
295
|
|
|
|
296
|
3 |
|
$command_arguments = str_replace( |
297
|
3 |
|
array('{revision_range}', '{repository_url}'), |
298
|
3 |
|
array($range_start . ':' . $range_end, $this->_repositoryRootUrl), |
299
|
3 |
|
$log_command_arguments |
300
|
3 |
|
); |
301
|
3 |
|
$command = $this->_repositoryConnector->getCommand('log', $command_arguments); |
302
|
3 |
|
$command->setCacheDuration($cache_duration); |
303
|
3 |
|
$svn_log = $command->run(); |
304
|
|
|
|
305
|
3 |
|
$this->_parseLog($svn_log, $plugins); |
|
|
|
|
306
|
|
|
|
307
|
3 |
|
$range_start = $range_end + 1; |
308
|
|
|
|
309
|
3 |
|
if ( isset($progress_bar) ) { |
310
|
2 |
|
$progress_bar->advance(); |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
// Remove progress bar of repository plugins. |
315
|
3 |
|
if ( isset($progress_bar) ) { |
316
|
2 |
|
$progress_bar->clear(); |
317
|
2 |
|
unset($progress_bar); |
318
|
|
|
} |
319
|
|
|
|
320
|
3 |
|
if ( $overwrite ) { |
321
|
|
|
$this->setPluginsOverwriteMode($plugins, false); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Use database collector plugins. |
327
|
|
|
* |
328
|
|
|
* @param integer $from_revision From revision. |
329
|
|
|
* @param integer $to_revision To revision. |
330
|
|
|
* @param boolean $overwrite Overwrite. |
331
|
|
|
* |
332
|
|
|
* @return void |
333
|
|
|
*/ |
334
|
3 |
|
private function _useDatabaseCollectorPlugins($from_revision, $to_revision, $overwrite = false) |
335
|
|
|
{ |
336
|
3 |
|
$plugins = $this->getDatabaseCollectorPlugins($overwrite); |
337
|
|
|
|
338
|
3 |
|
if ( $overwrite ) { |
339
|
|
|
$this->setPluginsOverwriteMode($plugins, true); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
// The "io" isn't set during autocomplete. |
343
|
3 |
|
if ( isset($this->_io) ) { |
344
|
|
|
// Create progress bar for database plugins, where data amount isn't known upfront. |
345
|
2 |
|
$progress_bar = $this->_io->createProgressBar(); |
346
|
2 |
|
$progress_bar->setMessage( |
347
|
2 |
|
$overwrite ? '* Reparsing revisions:' : ' * Reading missing revisions:' |
348
|
2 |
|
); |
349
|
2 |
|
$progress_bar->setFormat('%message% %current% [%bar%] %elapsed:6s% <info>%memory:-10s%</info>'); |
350
|
2 |
|
$progress_bar->start(); |
351
|
|
|
|
352
|
2 |
|
foreach ( $plugins as $plugin ) { |
353
|
2 |
|
$plugin->process($from_revision, $to_revision, $progress_bar); |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
else { |
357
|
1 |
|
foreach ( $plugins as $plugin ) { |
358
|
1 |
|
$plugin->process($from_revision, $to_revision); |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
|
362
|
3 |
|
if ( $overwrite ) { |
363
|
|
|
$this->setPluginsOverwriteMode($plugins, false); |
364
|
|
|
} |
365
|
|
|
|
366
|
3 |
|
if ( isset($progress_bar) ) { |
367
|
2 |
|
$progress_bar->finish(); |
368
|
2 |
|
$this->_io->writeln(''); |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Returns arguments for "log" command. |
374
|
|
|
* |
375
|
|
|
* @param IRepositoryCollectorPlugin[] $plugins Plugins. |
376
|
|
|
* |
377
|
|
|
* @return array |
378
|
|
|
*/ |
379
|
3 |
|
private function _getLogCommandArguments(array $plugins) |
380
|
|
|
{ |
381
|
3 |
|
$query_flags = $this->_getRevisionQueryFlags($plugins); |
382
|
|
|
|
383
|
3 |
|
$ret = array('-r', '{revision_range}', '--xml'); |
384
|
|
|
|
385
|
3 |
|
if ( in_array(self::FLAG_VERBOSE, $query_flags) ) { |
386
|
3 |
|
$ret[] = '--verbose'; |
387
|
|
|
} |
388
|
|
|
|
389
|
3 |
|
if ( in_array(self::FLAG_MERGE_HISTORY, $query_flags) ) { |
390
|
3 |
|
$ret[] = '--use-merge-history'; |
391
|
|
|
} |
392
|
|
|
|
393
|
3 |
|
$ret[] = '{repository_url}'; |
394
|
|
|
|
395
|
3 |
|
return $ret; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* Returns revision query flags. |
400
|
|
|
* |
401
|
|
|
* @param IRepositoryCollectorPlugin[] $plugins Plugins. |
402
|
|
|
* |
403
|
|
|
* @return array |
404
|
|
|
*/ |
405
|
3 |
|
private function _getRevisionQueryFlags(array $plugins) |
406
|
|
|
{ |
407
|
3 |
|
$ret = array(); |
408
|
|
|
|
409
|
3 |
|
foreach ( $plugins as $plugin ) { |
410
|
3 |
|
$ret = array_merge($ret, $plugin->getRevisionQueryFlags()); |
411
|
|
|
} |
412
|
|
|
|
413
|
3 |
|
return array_unique($ret); |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Parses output of "svn log" command. |
418
|
|
|
* |
419
|
|
|
* @param \SimpleXMLElement $log Log. |
420
|
|
|
* @param IRepositoryCollectorPlugin[] $plugins Plugins. |
421
|
|
|
* |
422
|
|
|
* @return void |
423
|
|
|
*/ |
424
|
3 |
|
private function _parseLog(\SimpleXMLElement $log, array $plugins) |
425
|
|
|
{ |
426
|
3 |
|
foreach ( $plugins as $plugin ) { |
427
|
3 |
|
$plugin->parse($log); |
428
|
|
|
} |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* Displays plugin activity statistics. |
433
|
|
|
* |
434
|
|
|
* @return void |
435
|
|
|
*/ |
436
|
1 |
|
private function _displayPluginActivityStatistics() |
437
|
|
|
{ |
438
|
1 |
|
$statistics = array(); |
439
|
|
|
|
440
|
|
|
// Combine statistics from all plugins. |
441
|
1 |
|
foreach ( $this->_plugins as $plugin ) { |
442
|
1 |
|
$statistics = array_merge($statistics, array_filter($plugin->getStatistics())); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
// Show statistics. |
446
|
1 |
|
$this->_io->writeln('<debug>Combined Plugin Statistics:</debug>'); |
447
|
|
|
|
448
|
1 |
|
foreach ( $statistics as $statistic_type => $occurrences ) { |
449
|
1 |
|
$this->_io->writeln('<debug> * ' . $statistic_type . ': ' . $occurrences . '</debug>'); |
450
|
|
|
} |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Registers a plugin. |
455
|
|
|
* |
456
|
|
|
* @param IPlugin $plugin Plugin. |
457
|
|
|
* |
458
|
|
|
* @return void |
459
|
|
|
* @throws \LogicException When plugin is registered several times. |
460
|
|
|
*/ |
461
|
12 |
|
public function registerPlugin(IPlugin $plugin) |
462
|
|
|
{ |
463
|
12 |
|
$plugin_name = $plugin->getName(); |
464
|
|
|
|
465
|
12 |
|
if ( $this->pluginRegistered($plugin_name) ) { |
466
|
1 |
|
throw new \LogicException('The "' . $plugin_name . '" revision log plugin is already registered.'); |
467
|
|
|
} |
468
|
|
|
|
469
|
12 |
|
$plugin->setRevisionLog($this); |
470
|
12 |
|
$this->_plugins[$plugin_name] = $plugin; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Finds information using plugin. |
475
|
|
|
* |
476
|
|
|
* @param string $plugin_name Plugin name. |
477
|
|
|
* @param array|string $criteria Search criteria. |
478
|
|
|
* |
479
|
|
|
* @return array |
480
|
|
|
*/ |
481
|
3 |
|
public function find($plugin_name, $criteria) |
482
|
|
|
{ |
483
|
3 |
|
return $this->getPlugin($plugin_name)->find((array)$criteria, $this->_projectPath); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* Returns information about revisions. |
488
|
|
|
* |
489
|
|
|
* @param string $plugin_name Plugin name. |
490
|
|
|
* @param array $revisions Revisions. |
491
|
|
|
* |
492
|
|
|
* @return array |
493
|
|
|
*/ |
494
|
3 |
|
public function getRevisionsData($plugin_name, array $revisions) |
495
|
|
|
{ |
496
|
3 |
|
return $this->getPlugin($plugin_name)->getRevisionsData($revisions); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
/** |
500
|
|
|
* Determines if plugin is registered. |
501
|
|
|
* |
502
|
|
|
* @param string $plugin_name Plugin name. |
503
|
|
|
* |
504
|
|
|
* @return boolean |
505
|
|
|
*/ |
506
|
15 |
|
public function pluginRegistered($plugin_name) |
507
|
|
|
{ |
508
|
15 |
|
return array_key_exists($plugin_name, $this->_plugins); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Returns plugin instance. |
513
|
|
|
* |
514
|
|
|
* @param string $plugin_name Plugin name. |
515
|
|
|
* |
516
|
|
|
* @return IPlugin |
517
|
|
|
* @throws \InvalidArgumentException When unknown plugin is given. |
518
|
|
|
*/ |
519
|
8 |
|
public function getPlugin($plugin_name) |
520
|
|
|
{ |
521
|
8 |
|
if ( !$this->pluginRegistered($plugin_name) ) { |
522
|
3 |
|
throw new \InvalidArgumentException('The "' . $plugin_name . '" revision log plugin is unknown.'); |
523
|
|
|
} |
524
|
|
|
|
525
|
5 |
|
return $this->_plugins[$plugin_name]; |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Returns bugs, from revisions. |
530
|
|
|
* |
531
|
|
|
* @param array $revisions Revisions. |
532
|
|
|
* |
533
|
|
|
* @return array |
534
|
|
|
*/ |
535
|
1 |
|
public function getBugsFromRevisions(array $revisions) |
536
|
|
|
{ |
537
|
1 |
|
$bugs = array(); |
538
|
1 |
|
$revisions_bugs = $this->getRevisionsData('bugs', $revisions); |
539
|
|
|
|
540
|
1 |
|
foreach ( $revisions as $revision ) { |
541
|
1 |
|
$revision_bugs = $revisions_bugs[$revision]; |
542
|
|
|
|
543
|
1 |
|
foreach ( $revision_bugs as $bug_id ) { |
544
|
1 |
|
$bugs[$bug_id] = true; |
545
|
|
|
} |
546
|
|
|
} |
547
|
|
|
|
548
|
1 |
|
return array_keys($bugs); |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
/** |
552
|
|
|
* Returns repository collector plugins. |
553
|
|
|
* |
554
|
|
|
* @param boolean $overwrite_mode Overwrite mode. |
555
|
|
|
* |
556
|
|
|
* @return IRepositoryCollectorPlugin[] |
557
|
|
|
*/ |
558
|
3 |
|
protected function getRepositoryCollectorPlugins($overwrite_mode) |
559
|
|
|
{ |
560
|
3 |
|
$plugins = $this->getPluginsByInterface(IRepositoryCollectorPlugin::class); |
561
|
|
|
|
562
|
3 |
|
if ( !$overwrite_mode ) { |
563
|
3 |
|
return $plugins; |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
return $this->getPluginsByInterface(IOverwriteAwarePlugin::class, $plugins); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* Returns database collector plugins. |
571
|
|
|
* |
572
|
|
|
* @param boolean $overwrite_mode Overwrite mode. |
573
|
|
|
* |
574
|
|
|
* @return IDatabaseCollectorPlugin[] |
575
|
|
|
*/ |
576
|
3 |
|
protected function getDatabaseCollectorPlugins($overwrite_mode) |
577
|
|
|
{ |
578
|
3 |
|
$plugins = $this->getPluginsByInterface(IDatabaseCollectorPlugin::class); |
579
|
|
|
|
580
|
3 |
|
if ( !$overwrite_mode ) { |
581
|
3 |
|
return $plugins; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
return $this->getPluginsByInterface(IOverwriteAwarePlugin::class, $plugins); |
585
|
|
|
} |
586
|
|
|
|
587
|
|
|
/** |
588
|
|
|
* Returns plugin list filtered by interface. |
589
|
|
|
* |
590
|
|
|
* @param string $interface Interface name. |
591
|
|
|
* @param IPlugin[] $plugins Plugins. |
592
|
|
|
* |
593
|
|
|
* @return IPlugin[] |
594
|
|
|
*/ |
595
|
3 |
|
protected function getPluginsByInterface($interface, array $plugins = array()) |
596
|
|
|
{ |
597
|
3 |
|
if ( !$plugins ) { |
598
|
3 |
|
$plugins = $this->_plugins; |
599
|
|
|
} |
600
|
|
|
|
601
|
3 |
|
$ret = array(); |
602
|
|
|
|
603
|
3 |
|
foreach ( $plugins as $plugin ) { |
604
|
3 |
|
if ( $plugin instanceof $interface ) { |
605
|
3 |
|
$ret[] = $plugin; |
606
|
|
|
} |
607
|
|
|
} |
608
|
|
|
|
609
|
3 |
|
return $ret; |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Sets overwrite mode. |
614
|
|
|
* |
615
|
|
|
* @param IOverwriteAwarePlugin[] $plugins Plugins. |
616
|
|
|
* @param boolean $overwrite_mode Overwrite mode. |
617
|
|
|
* |
618
|
|
|
* @return void |
619
|
|
|
*/ |
620
|
|
|
protected function setPluginsOverwriteMode(array $plugins, $overwrite_mode) |
621
|
|
|
{ |
622
|
|
|
foreach ( $plugins as $plugin ) { |
623
|
|
|
$plugin->setOverwriteMode($overwrite_mode); |
624
|
|
|
} |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
/** |
628
|
|
|
* Returns project path. |
629
|
|
|
* |
630
|
|
|
* @return string |
631
|
|
|
*/ |
632
|
1 |
|
public function getProjectPath() |
633
|
|
|
{ |
634
|
1 |
|
return $this->_projectPath; |
635
|
|
|
} |
636
|
|
|
|
637
|
|
|
/** |
638
|
|
|
* Returns ref name. |
639
|
|
|
* |
640
|
|
|
* @return string |
641
|
|
|
*/ |
642
|
1 |
|
public function getRefName() |
643
|
|
|
{ |
644
|
1 |
|
return $this->_refName; |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
} |
648
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.