Completed
Push — master ( dcfce3...ad1d1c )
by Alexander
02:47
created

RevisionLog   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 99.05%

Importance

Changes 11
Bugs 0 Features 1
Metric Value
wmc 29
c 11
b 0
f 1
lcom 1
cbo 6
dl 0
loc 296
ccs 104
cts 105
cp 0.9905
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
C refresh() 0 40 7
A _getCacheInvalidator() 0 13 2
A _getLastRevision() 0 14 2
B _queryRevisionData() 0 36 5
A _parseLog() 0 6 2
A registerPlugin() 0 10 2
A find() 0 8 2
A getRevisionsData() 0 8 2
A pluginRegistered() 0 4 1
A getBugsFromRevisions() 0 15 3
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\Cache\CacheManager;
16
use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector;
17
18
class RevisionLog
19
{
20
21
	const CACHE_FORMAT_VERSION = 1;
22
23
	/**
24
	 * Repository path.
25
	 *
26
	 * @var string
27
	 */
28
	private $_repositoryUrl;
29
30
	/**
31
	 * Repository connector.
32
	 *
33
	 * @var Connector
34
	 */
35
	private $_repositoryConnector;
36
37
	/**
38
	 * Cache manager.
39
	 *
40
	 * @var CacheManager
41
	 */
42
	private $_cacheManager;
43
44
	/**
45
	 * Console IO.
46
	 *
47
	 * @var ConsoleIO
48
	 */
49
	private $_io;
50
51
	/**
52
	 * Installed plugins.
53
	 *
54
	 * @var IRevisionLogPlugin[]
55
	 */
56
	private $_plugins = array();
57
58
	/**
59
	 * Create revision log.
60
	 *
61
	 * @param string       $repository_url       Repository url.
62
	 * @param Connector    $repository_connector Repository connector.
63
	 * @param CacheManager $cache_manager        Cache.
64
	 * @param ConsoleIO    $io                   Console IO.
65
	 */
66 34
	public function __construct(
67
		$repository_url,
68
		Connector $repository_connector,
69
		CacheManager $cache_manager,
70
		ConsoleIO $io = null
71
	) {
72 34
		$this->_repositoryUrl = $repository_url;
73 34
		$this->_repositoryConnector = $repository_connector;
74 34
		$this->_cacheManager = $cache_manager;
75 34
		$this->_io = $io;
76 34
	}
77
78
	/**
79
	 * Queries missing revisions.
80
	 *
81
	 * @return void
82
	 * @throws \LogicException When no plugins are registered.
83
	 */
84 26
	public function refresh()
85
	{
86 26
		if ( !$this->_plugins ) {
87 1
			throw new \LogicException('Please register at least one revision log plugin.');
88
		}
89
90 25
		$project_url = $this->_repositoryConnector->getProjectUrl($this->_repositoryUrl);
91
92 25
		if ( preg_match(Connector::URL_REGEXP, $project_url, $regs) ) {
93 25
			$cache_key = $regs[2] . $regs[3] . $regs[4] . '/log:' . $project_url;
94 25
		}
95
		else {
96
			$cache_key = 'misc/log:' . $project_url;
97
		}
98
99
		// Initialize plugins with data from cache.
100 25
		$cache = $this->_cacheManager->getCache($cache_key, $this->_getCacheInvalidator());
101
102 25
		if ( is_array($cache) ) {
103 6
			foreach ( $this->_plugins as $plugin_name => $plugin ) {
104 6
				$plugin->setCollectedData($cache[$plugin_name]);
105 6
			}
106 6
		}
107
108 25
		$from_revision = $this->_getLastRevision();
109 25
		$to_revision = $this->_repositoryConnector->getLastRevision($project_url);
110
111 25
		if ( $to_revision > $from_revision ) {
112 2
			$this->_queryRevisionData($from_revision, $to_revision);
113
114
			// Collect and cache plugin data.
115 2
			$cache = array();
116
117 2
			foreach ( $this->_plugins as $plugin_name => $plugin ) {
118 2
				$cache[$plugin_name] = $plugin->getCollectedData();
119 2
			}
120
121 2
			$this->_cacheManager->setCache($cache_key, $cache, $this->_getCacheInvalidator());
122 2
		}
123 25
	}
124
125
	/**
126
	 * Returns format version.
127
	 *
128
	 * @return mixed
129
	 */
130 25
	private function _getCacheInvalidator()
131
	{
132 25
		$invalidators = array('main' => 'main:' . self::CACHE_FORMAT_VERSION);
133
134 25
		foreach ( $this->_plugins as $plugin_name => $plugin ) {
135 25
			$invalidator_key = 'plugin(' . $plugin_name . ')';
136 25
			$invalidators[$invalidator_key] = $invalidator_key . ':' . $plugin->getCacheInvalidator();
137 25
		}
138
139 25
		ksort($invalidators);
140
141 25
		return implode(';', $invalidators);
142
	}
143
144
	/**
145
	 * Returns last known revision.
146
	 *
147
	 * @return integer
148
	 */
149 25
	private function _getLastRevision()
150
	{
151
		/** @var IRevisionLogPlugin $plugin */
152 25
		$plugin = reset($this->_plugins);
153 25
		$last_revision = $plugin->getLastRevision();
154
155 25
		if ( $last_revision === null ) {
156 20
			return $this->_repositoryConnector->getFirstRevision(
157 20
				$this->_repositoryConnector->getProjectUrl($this->_repositoryUrl)
158 20
			);
159
		}
160
161 5
		return $last_revision;
162
	}
163
164
	/**
165
	 * Queries missing revision data.
166
	 *
167
	 * @param integer $from_revision From revision.
168
	 * @param integer $to_revision   To revision.
169
	 *
170
	 * @return void
171
	 */
172 2
	private function _queryRevisionData($from_revision, $to_revision)
173
	{
174 2
		$range_start = $from_revision;
175 2
		$project_url = $this->_repositoryConnector->getProjectUrl($this->_repositoryUrl);
176
177
		// The "io" isn't set during autocomplete.
178 2
		if ( isset($this->_io) ) {
179 1
			$progress_bar = $this->_io->createProgressBar(ceil(($to_revision - $from_revision) / 200));
180 1
			$progress_bar->setFormat(
181
				' * Reading missing revisions: %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%'
182 1
			);
183 1
			$progress_bar->start();
184 1
		}
185
186 2
		while ( $range_start <= $to_revision ) {
187 2
			$range_end = min($range_start + 199, $to_revision);
188
189 2
			$command = $this->_repositoryConnector->getCommand(
190 2
				'log',
191 2
				'-r ' . $range_start . ':' . $range_end . ' --xml --verbose --use-merge-history {' . $project_url . '}'
192 2
			);
193 2
			$command->setCacheDuration('10 years');
194
195 2
			$this->_parseLog($command->run());
0 ignored issues
show
Bug introduced by
It seems like $command->run() targeting ConsoleHelpers\SVNBuddy\...onnector\Command::run() can also be of type string; however, ConsoleHelpers\SVNBuddy\...evisionLog::_parseLog() does only seem to accept object<SimpleXMLElement>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
196 2
			$range_start = $range_end + 1;
197
198 2
			if ( isset($progress_bar) ) {
199 1
				$progress_bar->advance();
200 1
			}
201 2
		}
202
203 2
		if ( isset($progress_bar) ) {
204 1
			$progress_bar->finish();
205 1
			$this->_io->writeln('');
206 1
		}
207 2
	}
208
209
	/**
210
	 * Parses output of "svn log" command.
211
	 *
212
	 * @param \SimpleXMLElement $log Log.
213
	 *
214
	 * @return void
215
	 */
216 2
	private function _parseLog(\SimpleXMLElement $log)
217
	{
218 2
		foreach ( $this->_plugins as $plugin ) {
219 2
			$plugin->parse($log);
220 2
		}
221 2
	}
222
223
	/**
224
	 * Registers a plugin.
225
	 *
226
	 * @param IRevisionLogPlugin $plugin Plugin.
227
	 *
228
	 * @return void
229
	 * @throws \LogicException When plugin is registered several times.
230
	 */
231 31
	public function registerPlugin(IRevisionLogPlugin $plugin)
232
	{
233 31
		$plugin_name = $plugin->getName();
234
235 31
		if ( $this->pluginRegistered($plugin_name) ) {
236 1
			throw new \LogicException('The "' . $plugin_name . '" revision log plugin is already registered.');
237
		}
238
239 31
		$this->_plugins[$plugin_name] = $plugin;
240 31
	}
241
242
	/**
243
	 * Finds information using plugin.
244
	 *
245
	 * @param string       $plugin_name Plugin name.
246
	 * @param array|string $criteria    Search criteria.
247
	 *
248
	 * @return array
249
	 * @throws \InvalidArgumentException When unknown plugin is given.
250
	 */
251 3
	public function find($plugin_name, $criteria)
252
	{
253 3
		if ( !$this->pluginRegistered($plugin_name) ) {
254 1
			throw new \InvalidArgumentException('The "' . $plugin_name . '" revision log plugin is unknown.');
255
		}
256
257 2
		return $this->_plugins[$plugin_name]->find((array)$criteria);
258
	}
259
260
	/**
261
	 * Returns information about revisions.
262
	 *
263
	 * @param string $plugin_name Plugin name.
264
	 * @param array  $revisions   Revisions.
265
	 *
266
	 * @return array
267
	 * @throws \InvalidArgumentException When unknown plugin is given.
268
	 */
269 3
	public function getRevisionsData($plugin_name, array $revisions)
270
	{
271 3
		if ( !$this->pluginRegistered($plugin_name) ) {
272 1
			throw new \InvalidArgumentException('The "' . $plugin_name . '" revision log plugin is unknown.');
273
		}
274
275 2
		return $this->_plugins[$plugin_name]->getRevisionsData($revisions);
276
	}
277
278
	/**
279
	 * Determines if plugin is registered.
280
	 *
281
	 * @param string $plugin_name Plugin name.
282
	 *
283
	 * @return boolean
284
	 */
285 33
	public function pluginRegistered($plugin_name)
286
	{
287 33
		return array_key_exists($plugin_name, $this->_plugins);
288
	}
289
290
	/**
291
	 * Returns bugs, from revisions.
292
	 *
293
	 * @param array $revisions Revisions.
294
	 *
295
	 * @return array
296
	 */
297 1
	public function getBugsFromRevisions(array $revisions)
298
	{
299 1
		$bugs = array();
300 1
		$revisions_bugs = $this->getRevisionsData('bugs', $revisions);
301
302 1
		foreach ( $revisions as $revision ) {
303 1
			$revision_bugs = $revisions_bugs[$revision];
304
305 1
			foreach ( $revision_bugs as $bug_id ) {
306 1
				$bugs[$bug_id] = true;
307 1
			}
308 1
		}
309
310 1
		return array_keys($bugs);
311
	}
312
313
}
314