Completed
Push — master ( 8eb698...a673ca )
by Alexander
02:26
created

RevisionLog::_queryRevisionData()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 5

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 35
ccs 26
cts 26
cp 1
rs 8.439
cc 5
eloc 20
nc 12
nop 2
crap 5
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 revisions.
255
	 *
256
	 * @param string $plugin_name Plugin name.
257
	 * @param array  $revisions   Revisions.
258
	 *
259
	 * @return array
260
	 * @throws \InvalidArgumentException When unknown plugin is given.
261
	 */
262 3
	public function getRevisionsData($plugin_name, array $revisions)
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]->getRevisionsData($revisions);
269
	}
270
271
	/**
272
	 * Determines if plugin is registered.
273
	 *
274
	 * @param string $plugin_name Plugin name.
275
	 *
276
	 * @return boolean
277
	 */
278 17
	public function pluginRegistered($plugin_name)
279
	{
280 17
		return array_key_exists($plugin_name, $this->_plugins);
281
	}
282
283
	/**
284
	 * Returns bugs, from revisions.
285
	 *
286
	 * @param array $revisions Revisions.
287
	 *
288
	 * @return array
289
	 */
290 1
	public function getBugsFromRevisions(array $revisions)
291
	{
292 1
		$bugs = array();
293 1
		$revisions_bugs = $this->getRevisionsData('bugs', $revisions);
294
295 1
		foreach ( $revisions as $revision ) {
296 1
			$revision_bugs = $revisions_bugs[$revision];
297
298 1
			foreach ( $revision_bugs as $bug_id ) {
299 1
				$bugs[$bug_id] = true;
300 1
			}
301 1
		}
302
303 1
		return array_keys($bugs);
304
	}
305
306
}
307