Failed Conditions
Push — master ( d32373...a3f91c )
by Alexander
01:43
created

Command   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 258
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 5
dl 0
loc 258
ccs 68
cts 68
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A setCacheInvalidator() 0 6 1
A setCacheDuration() 0 6 1
A setCacheOverwrite() 0 6 1
B run() 0 32 8
A _getCacheKey() 0 12 4
A _doRun() 0 30 4
A runLive() 0 4 1
A _createLiveOutputCallback() 0 26 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\Connector;
12
13
14
use ConsoleHelpers\ConsoleKit\ConsoleIO;
15
use ConsoleHelpers\SVNBuddy\Cache\CacheManager;
16
use ConsoleHelpers\SVNBuddy\Exception\RepositoryCommandException;
17
use ConsoleHelpers\SVNBuddy\Process\IProcessFactory;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Process\Exception\ProcessFailedException;
20
use Symfony\Component\Process\Process;
21
22
class Command
23
{
24
25
	/**
26
	 * Process factory.
27
	 *
28
	 * @var IProcessFactory
29
	 */
30
	private $_processFactory;
31
32
	/**
33
	 * Command line.
34
	 *
35
	 * @var string
36
	 */
37
	private $_commandLine;
38
39
	/**
40
	 * Console IO.
41
	 *
42
	 * @var ConsoleIO
43
	 */
44
	private $_io;
45
46
	/**
47
	 * Cache manager.
48
	 *
49
	 * @var CacheManager
50
	 */
51
	private $_cacheManager;
52
53
	/**
54
	 * Cache duration.
55
	 *
56
	 * @var mixed
57
	 */
58
	private $_cacheDuration;
59
60
	/**
61
	 * Text that when different from cached will invalidate the cache.
62
	 *
63
	 * @var string
64
	 */
65
	private $_cacheInvalidator;
66
67
	/**
68
	 * Overwrites cached value regardless of it's expiration/invalidation settings.
69
	 *
70
	 * @var boolean
71
	 */
72
	private $_cacheOverwrite = false;
73
74
	/**
75
	 * Creates a command instance.
76
	 *
77
	 * @param string          $command_line    Command line.
78
	 * @param ConsoleIO       $io              Console IO.
79
	 * @param CacheManager    $cache_manager   Cache manager.
80
	 * @param IProcessFactory $process_factory Process factory.
81
	 */
82 65
	public function __construct(
83
		$command_line,
84
		ConsoleIO $io,
85
		CacheManager $cache_manager,
86
		IProcessFactory $process_factory
87
	) {
88 65
		$this->_commandLine = $command_line;
89 65
		$this->_io = $io;
90 65
		$this->_cacheManager = $cache_manager;
91 65
		$this->_processFactory = $process_factory;
92 65
	}
93
94
	/**
95
	 * Set cache invalidator.
96
	 *
97
	 * @param string $invalidator Invalidator.
98
	 *
99
	 * @return self
100
	 */
101 16
	public function setCacheInvalidator($invalidator)
102
	{
103 16
		$this->_cacheInvalidator = $invalidator;
104
105 16
		return $this;
106
	}
107
108
	/**
109
	 * Set cache duration.
110
	 *
111
	 * @param mixed $duration Duration (seconds if numeric OR whatever "strtotime" accepts).
112
	 *
113
	 * @return self
114
	 */
115 39
	public function setCacheDuration($duration)
116
	{
117 39
		$this->_cacheDuration = $duration;
118
119 39
		return $this;
120
	}
121
122
	/**
123
	 * Set cache overwrite.
124
	 *
125
	 * @param boolean $cache_overwrite Cache replace.
126
	 *
127
	 * @return self
128
	 */
129 4
	public function setCacheOverwrite($cache_overwrite)
130
	{
131 4
		$this->_cacheOverwrite = $cache_overwrite;
132
133 4
		return $this;
134
	}
135
136
	/**
137
	 * Runs the command.
138
	 *
139
	 * @param callable|null $callback Callback.
140
	 *
141
	 * @return string|\SimpleXMLElement
142
	 */
143 65
	public function run($callback = null)
144
	{
145 65
		$output = null;
146 65
		$cache_key = $this->_getCacheKey();
147
148 65
		if ( $cache_key ) {
149 47
			if ( $this->_cacheOverwrite ) {
150 2
				$this->_cacheManager->deleteCache($cache_key, $this->_cacheDuration);
151
			}
152
			else {
153 45
				$output = $this->_cacheManager->getCache($cache_key, $this->_cacheInvalidator, $this->_cacheDuration);
154
			}
155
156 47
			if ( isset($output) && is_callable($callback) ) {
157 6
				call_user_func($callback, Process::OUT, $output);
158
			}
159
		}
160
161 65
		if ( !isset($output) ) {
162 34
			$output = $this->_doRun($callback);
163
164 33
			if ( $cache_key ) {
165 16
				$this->_cacheManager->setCache($cache_key, $output, $this->_cacheInvalidator, $this->_cacheDuration);
166
			}
167
		}
168
169 64
		if ( strpos($this->_commandLine, '--xml') !== false ) {
170 14
			return simplexml_load_string($output);
171
		}
172
173 50
		return $output;
174
	}
175
176
	/**
177
	 * Returns cache key for a command.
178
	 *
179
	 * @return string
180
	 */
181 65
	private function _getCacheKey()
182
	{
183 65
		if ( $this->_cacheInvalidator || $this->_cacheDuration ) {
184 47
			if ( preg_match(Connector::URL_REGEXP, $this->_commandLine, $regs) ) {
185 17
				return $regs[2] . $regs[3] . $regs[4] . '/command:' . $this->_commandLine;
186
			}
187
188 30
			return 'misc/command:' . $this->_commandLine;
189
		}
190
191 18
		return '';
192
	}
193
194
	/**
195
	 * Runs the command.
196
	 *
197
	 * @param callable|null $callback Callback.
198
	 *
199
	 * @return string
200
	 * @throws RepositoryCommandException When command execution failed.
201
	 */
202 34
	private function _doRun($callback = null)
203
	{
204 34
		$process = $this->_processFactory->createProcess($this->_commandLine, 1200);
205
206
		try {
207 34
			$start = microtime(true);
208 34
			$process->mustRun($callback);
209
210 33
			if ( $this->_io->isVerbose() ) {
211 1
				$runtime = sprintf('%01.2f', microtime(true) - $start);
212 1
				$this->_io->writeln(
213 1
					array('', '<debug>[svn, ' . round($runtime, 2) . 's]: ' . $this->_commandLine . '</debug>')
214
				);
215
			}
216
217 33
			$output = (string)$process->getOutput();
218
219 33
			if ( $this->_io->isDebug() ) {
220 1
				$this->_io->writeln($output, OutputInterface::OUTPUT_RAW);
221
			}
222
223 33
			return $output;
224
		}
225 1
		catch ( ProcessFailedException $e ) {
226 1
			throw new RepositoryCommandException(
227 1
				$this->_commandLine,
228 1
				$process->getErrorOutput()
229
			);
230
		}
231
	}
232
233
	/**
234
	 * Runs an svn command and displays output in real time.
235
	 *
236
	 * @param array $replacements Replacements for the output.
237
	 *
238
	 * @return string
239
	 */
240 2
	public function runLive(array $replacements = array())
241
	{
242 2
		return $this->run($this->_createLiveOutputCallback($replacements));
243
	}
244
245
	/**
246
	 * Creates "live output" callback.
247
	 *
248
	 * @param array $replacements Replacements for the output.
249
	 *
250
	 * @return callable
251
	 */
252 2
	private function _createLiveOutputCallback(array $replacements = array())
253
	{
254 2
		$io = $this->_io;
255
256 2
		$replace_froms = array_keys($replacements);
257 2
		$replace_tos = array_values($replacements);
258
259
		return function ($type, $buffer) use ($io, $replace_froms, $replace_tos) {
260 2
			foreach ( $replace_froms as $index => $replace_from ) {
261 2
				$replace_to = $replace_tos[$index];
262
263 2
				if ( substr($replace_from, 0, 1) === '/' && substr($replace_from, -1, 1) === '/' ) {
264 2
					$buffer = preg_replace($replace_from, $replace_to, $buffer);
265
				}
266
				else {
267 2
					$buffer = str_replace($replace_from, $replace_to, $buffer);
268
				}
269
			}
270
271 2
			if ( $type === Process::ERR ) {
272 1
				$buffer = '<error>ERR:</error> ' . $buffer;
273
			}
274
275 2
			$io->write($buffer);
276 2
		};
277
	}
278
279
}
280