Failed Conditions
Push — master ( 30a496...e276e6 )
by Alexander
02:31
created

RevisionLog::_parseLog()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

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