Failed Conditions
Push — master ( 830d2d...99bc8c )
by Alexander
02:52
created

RevisionLog::refresh()   C

Complexity

Conditions 7
Paths 13

Size

Total Lines 41
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7.0222

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 41
ccs 24
cts 26
cp 0.9231
rs 6.7272
cc 7
eloc 21
nc 13
nop 0
crap 7.0222
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 18
	public function __construct(
67
		$repository_url,
68
		Connector $repository_connector,
69
		CacheManager $cache_manager,
70
		ConsoleIO $io = null
71
	) {
72 18
		$this->_repositoryUrl = $repository_url;
73 18
		$this->_repositoryConnector = $repository_connector;
74 18
		$this->_cacheManager = $cache_manager;
75 18
		$this->_io = $io;
76 18
	}
77
78
	/**
79
	 * Queries missing revisions.
80
	 *
81
	 * @return void
82
	 * @throws \LogicException When no plugins are registered.
83
	 */
84 10
	public function refresh()
85
	{
86 10
		if ( !$this->_plugins ) {
87 1
			throw new \LogicException('Please register at least one revision log plugin.');
88
		}
89
90 9
		$project_url = $this->_repositoryConnector->getProjectUrl($this->_repositoryUrl);
91 9
		$url_regexp = '/^((.+):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+[^#?\s]+)(.*)?(#[\w\-]+)?$/';
92
93 9
		if ( preg_match($url_regexp, $project_url, $regs) ) {
94
			$cache_key = $regs[3] . '/log:' . $project_url;
95
		}
96
		else {
97 9
			$cache_key = 'misc/log:' . $project_url;
98
		}
99
100
		// Initialize plugins with data from cache.
101 9
		$cache = $this->_cacheManager->getCache($cache_key, $this->_getCacheInvalidator());
102
103 9
		if ( is_array($cache) ) {
104 6
			foreach ( $this->_plugins as $plugin_name => $plugin ) {
105 6
				$plugin->setCollectedData($cache[$plugin_name]);
106 6
			}
107 6
		}
108
109 9
		$from_revision = $this->_getLastRevision();
110 9
		$to_revision = $this->_repositoryConnector->getLastRevision($project_url);
111
112 9
		if ( $to_revision > $from_revision ) {
113 2
			$this->_queryRevisionData($from_revision, $to_revision);
114
115
			// Collect and cache plugin data.
116 2
			$cache = array();
117
118 2
			foreach ( $this->_plugins as $plugin_name => $plugin ) {
119 2
				$cache[$plugin_name] = $plugin->getCollectedData();
120 2
			}
121
122 2
			$this->_cacheManager->setCache($cache_key, $cache, $this->_getCacheInvalidator());
123 2
		}
124 9
	}
125
126
	/**
127
	 * Returns format version.
128
	 *
129
	 * @return mixed
130
	 */
131 9
	private function _getCacheInvalidator()
132
	{
133 9
		$invalidators = array('main' => 'main:' . self::CACHE_FORMAT_VERSION);
134
135 9
		foreach ( $this->_plugins as $plugin_name => $plugin ) {
136 9
			$invalidator_key = 'plugin(' . $plugin_name . ')';
137 9
			$invalidators[$invalidator_key] = $invalidator_key . ':' . $plugin->getCacheInvalidator();
138 9
		}
139
140 9
		ksort($invalidators);
141
142 9
		return implode(';', $invalidators);
143
	}
144
145
	/**
146
	 * Returns last known revision.
147
	 *
148
	 * @return integer
149
	 */
150 9
	private function _getLastRevision()
151
	{
152
		/** @var IRevisionLogPlugin $plugin */
153 9
		$plugin = reset($this->_plugins);
154 9
		$last_revision = $plugin->getLastRevision();
155
156 9
		if ( $last_revision === null ) {
157 4
			return $this->_repositoryConnector->getFirstRevision(
158 4
				$this->_repositoryConnector->getProjectUrl($this->_repositoryUrl)
159 4
			);
160
		}
161
162 5
		return $last_revision;
163
	}
164
165
	/**
166
	 * Queries missing revision data.
167
	 *
168
	 * @param integer $from_revision From revision.
169
	 * @param integer $to_revision   To revision.
170
	 *
171
	 * @return void
172
	 */
173 2
	private function _queryRevisionData($from_revision, $to_revision)
174
	{
175 2
		$range_start = $from_revision;
176 2
		$project_url = $this->_repositoryConnector->getProjectUrl($this->_repositoryUrl);
177
178
		// The "io" isn't set during autocomplete.
179 2
		if ( isset($this->_io) ) {
180 1
			$progress_bar = $this->_io->createProgressBar(ceil(($to_revision - $from_revision) / 200));
181 1
			$progress_bar->setFormat(
182
				' * Reading missing revisions: %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%'
183 1
			);
184 1
			$progress_bar->start();
185 1
		}
186
187 2
		while ( $range_start <= $to_revision ) {
188 2
			$range_end = min($range_start + 199, $to_revision);
189
190 2
			$command = $this->_repositoryConnector->getCommand(
191 2
				'log',
192 2
				'-r ' . $range_start . ':' . $range_end . ' --xml --verbose --use-merge-history {' . $project_url . '}'
193 2
			);
194 2
			$command->setCacheDuration('10 years');
195
196 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...
197 2
			$range_start = $range_end + 1;
198
199 2
			if ( isset($progress_bar) ) {
200 1
				$progress_bar->advance();
201 1
			}
202 2
		}
203
204 2
		if ( isset($progress_bar) ) {
205 1
			$progress_bar->finish();
206 1
			$this->_io->writeln('');
207 1
		}
208 2
	}
209
210
	/**
211
	 * Parses output of "svn log" command.
212
	 *
213
	 * @param \SimpleXMLElement $log Log.
214
	 *
215
	 * @return void
216
	 */
217 2
	private function _parseLog(\SimpleXMLElement $log)
218
	{
219 2
		foreach ( $this->_plugins as $plugin ) {
220 2
			$plugin->parse($log);
221 2
		}
222 2
	}
223
224
	/**
225
	 * Registers a plugin.
226
	 *
227
	 * @param IRevisionLogPlugin $plugin Plugin.
228
	 *
229
	 * @return void
230
	 * @throws \LogicException When plugin is registered several times.
231
	 */
232 15
	public function registerPlugin(IRevisionLogPlugin $plugin)
233
	{
234 15
		$plugin_name = $plugin->getName();
235
236 15
		if ( $this->pluginRegistered($plugin_name) ) {
237 1
			throw new \LogicException('The "' . $plugin_name . '" revision log plugin is already registered.');
238
		}
239
240 15
		$this->_plugins[$plugin_name] = $plugin;
241 15
	}
242
243
	/**
244
	 * Finds information using plugin.
245
	 *
246
	 * @param string       $plugin_name Plugin name.
247
	 * @param array|string $criteria    Search criteria.
248
	 *
249
	 * @return array
250
	 * @throws \InvalidArgumentException When unknown plugin is given.
251
	 */
252 3
	public function find($plugin_name, $criteria)
253
	{
254 3
		if ( !$this->pluginRegistered($plugin_name) ) {
255 1
			throw new \InvalidArgumentException('The "' . $plugin_name . '" revision log plugin is unknown.');
256
		}
257
258 2
		return $this->_plugins[$plugin_name]->find((array)$criteria);
259
	}
260
261
	/**
262
	 * Returns information about revisions.
263
	 *
264
	 * @param string $plugin_name Plugin name.
265
	 * @param array  $revisions   Revisions.
266
	 *
267
	 * @return array
268
	 * @throws \InvalidArgumentException When unknown plugin is given.
269
	 */
270 3
	public function getRevisionsData($plugin_name, array $revisions)
271
	{
272 3
		if ( !$this->pluginRegistered($plugin_name) ) {
273 1
			throw new \InvalidArgumentException('The "' . $plugin_name . '" revision log plugin is unknown.');
274
		}
275
276 2
		return $this->_plugins[$plugin_name]->getRevisionsData($revisions);
277
	}
278
279
	/**
280
	 * Determines if plugin is registered.
281
	 *
282
	 * @param string $plugin_name Plugin name.
283
	 *
284
	 * @return boolean
285
	 */
286 17
	public function pluginRegistered($plugin_name)
287
	{
288 17
		return array_key_exists($plugin_name, $this->_plugins);
289
	}
290
291
	/**
292
	 * Returns bugs, from revisions.
293
	 *
294
	 * @param array $revisions Revisions.
295
	 *
296
	 * @return array
297
	 */
298 1
	public function getBugsFromRevisions(array $revisions)
299
	{
300 1
		$bugs = array();
301 1
		$revisions_bugs = $this->getRevisionsData('bugs', $revisions);
302
303 1
		foreach ( $revisions as $revision ) {
304 1
			$revision_bugs = $revisions_bugs[$revision];
305
306 1
			foreach ( $revision_bugs as $bug_id ) {
307 1
				$bugs[$bug_id] = true;
308 1
			}
309 1
		}
310
311 1
		return array_keys($bugs);
312
	}
313
314
}
315