1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: namespace Jyxo\Svn;
15:
16: /**
17: * SVN client for PHP.
18: *
19: * Does not use the php_svn extension, but executes SVN binaries directly.
20: *
21: * @category Jyxo
22: * @package Jyxo\Svn
23: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
24: * @license https://github.com/jyxo/php/blob/master/license.txt
25: * @author Matěj Humpál
26: * @author Ondřej Nešpor
27: */
28: class Client
29: {
30:
31: /**
32: * SVN username.
33: *
34: * @var string
35: */
36: protected $user = '';
37:
38: /**
39: * SVN user password.
40: *
41: * @var string
42: */
43: protected $password = '';
44:
45: /**
46: * Additional SVN parameters.
47: *
48: * @var array
49: */
50: protected $additional = array();
51:
52: /**
53: * Path to the SVN binary.
54: *
55: * @var string
56: */
57: protected $svnBinary = '/usr/bin/svn';
58:
59: /**
60: * Constructor.
61: *
62: * @param string $user SVN username
63: * @param string $password SVN user password
64: * @param array $additional Additional parameters
65: * @param string $svnBinary SVN binary path
66: */
67: public function __construct($user = '', $password = '', array $additional = array(), $svnBinary = '')
68: {
69: $this->user = (string) $user;
70: $this->password = (string) $password;
71: $this->additional = $additional;
72: $this->svnBinary = (string) $svnBinary;
73: }
74:
75: /**
76: * SVN checkout.
77: *
78: * @param string $url Repository URL
79: * @param string $path Local working copy path
80: * @param mixed $params Additional parameters
81: * @param string $user SVN username
82: * @param string $password SVN user password
83: * @return \Jyxo\Svn\Result
84: */
85: public function checkout($url, $path, $params = null, $user = '', $password = '')
86: {
87: return $this->callSvn('checkout', $user, $password, array_merge((array) $url, (array) $params, (array) $path));
88: }
89:
90: /**
91: * SVN checkout.
92: *
93: * @param string $url Repository URL
94: * @param string $path Local working copy path
95: * @param mixed $params Additional parameters
96: * @param string $user SVN username
97: * @param string $password SVN user password
98: * @return \Jyxo\Svn\Result
99: */
100: public function co($url, $path, $params = null, $user = '', $password = '')
101: {
102: return $this->checkout($url, $path, $params, $user, $password);
103: }
104:
105: /**
106: * SVN Update.
107: *
108: * @param string $path Local working copy path
109: * @param mixed $params Additional parameters
110: * @param string $user SVN username
111: * @param string $password SVN user password
112: * @return \Jyxo\Svn\Result
113: */
114: public function update($path, $params = null, $user = '', $password = '')
115: {
116: return $this->callSvn('update', $user, $password, array_merge((array) $params, (array) $path));
117: }
118:
119: /**
120: * SVN Update.
121: *
122: * @param string $path Local working copy path
123: * @param mixed $params Additional parameters
124: * @param string $user SVN username
125: * @param string $password SVN user password
126: * @return \Jyxo\Svn\Result
127: */
128: public function up($path, $params = null, $user = '', $password = '')
129: {
130: return $this->update($path, $params, $user, $password);
131: }
132:
133: /**
134: * SVN commit.
135: *
136: * @param string $path Local working copy path
137: * @param string $message Commit message
138: * @param mixed $params Additional parameters
139: * @param string $user SVN username
140: * @param string $password SVN user password
141: * @return \Jyxo\Svn\Result
142: */
143: public function commit($path, $message, $params = null, $user = '', $password = '')
144: {
145: return $this->callSvn('commit', $user, $password, array_merge((array) $params, array('-m' => $message), (array) $path));
146: }
147:
148: /**
149: * SVN commit.
150: *
151: * @param string $path Local working copy path
152: * @param string $message Commit message
153: * @param mixed $params Additional parameters
154: * @param string $user SVN username
155: * @param string $password SVN user password
156: * @return \Jyxo\Svn\Result
157: */
158: public function ci($path, $message, $params = null, $user = '', $password = '')
159: {
160: return $this->commit($path, $message, $params, $user, $password);
161: }
162:
163: /**
164: * Runs SVN add on the given path.
165: *
166: * @param array $path Path to be added to SVN
167: * @return \Jyxo\Svn\Result
168: */
169: public function add(array $path)
170: {
171: return $this->callSvn('add', false, false, $path);
172: }
173:
174: /**
175: * Runs SVN delete on the given path.
176: *
177: * @param array $path Path to be deleted from SVN
178: * @return \Jyxo\Svn\Result
179: */
180: public function delete(array $path)
181: {
182: return $this->callSvn('delete', false, false, $path);
183: }
184:
185: /**
186: * Retrieves SVN status information of the given path.
187: *
188: * @param array $path Checked path
189: * @return \Jyxo\Svn\Result
190: */
191: public function status(array $path)
192: {
193: return $this->callSvn('status', false, false, $path);
194: }
195:
196: /**
197: * Executes SVN binary with given parameters.
198: *
199: * @param string $action Action
200: * @param string $user Username
201: * @param string $password User password
202: * @param string $params Additional parameters
203: * @return \Jyxo\Svn\Result
204: * @throws \Jyxo\Svn\Exception On execute error
205: */
206: protected function callSvn($action, $user, $password, $params)
207: {
208: try {
209:
210: $command = escapeshellcmd($this->svnBinary) . ' ' . escapeshellarg($action);
211:
212: $command .= $this->getUserString($user);
213: $command .= $this->getPasswordString($password);
214:
215: switch ($action) {
216: case 'add':
217: case 'delete':
218: $command .= $this->getAdditionalParams($params, true);
219: break;
220: default:
221: $command .= $this->getAdditionalParams($params, false);
222: break;
223: }
224:
225: try {
226:
227: $shell = new \Jyxo\Shell\Client();
228: $shell->exec($command, $status);
229:
230: return new Result($action, $shell->getOut(), $status);
231:
232: } catch (\Jyxo\Shell\Exception $e) {
233: throw $e;
234: }
235:
236: } catch (\Exception $e) {
237: throw new Exception(sprintf('SVN %s failed: %s', $action, $e->getMessage()), 0, $e);
238: }
239: }
240:
241:
242: /**
243: * Sets SVN username.
244: *
245: * @param string $user Username
246: * @return \Jyxo\Svn\Client
247: */
248: public function setUser($user)
249: {
250: $this->user = (string) $user;
251:
252: return $this;
253: }
254:
255: /**
256: * Sets SVN user password.
257: *
258: * @param string $password Password
259: * @return \Jyxo\Svn\Client
260: */
261: public function setPassword($password)
262: {
263: $this->password = (string) $password;
264:
265: return $this;
266: }
267:
268: /**
269: * Sets additional parameters.
270: *
271: * @param array $params Array of parameters
272: * @return \Jyxo\Svn\Client
273: */
274: public function setAdditionalParams(array $params)
275: {
276: $this->additional = $params;
277:
278: return $this;
279: }
280:
281: /**
282: * Sets SVN binary path.
283: *
284: * @param string $path Path to the SVN binary
285: * @return \Jyxo\Svn\Client
286: */
287: public function setSvnBinary($path)
288: {
289: $this->svnBinary = (string) $path;
290:
291: return $this;
292: }
293:
294: /**
295: * Adds an additional parameter.
296: *
297: * @param string $param Parameter name
298: * @param string $value Parameter value
299: * @return \Jyxo\Svn\Client
300: */
301: public function addAdditionalParam($param, $value = '')
302: {
303: if (!empty($value)) {
304: $this->additional[$param] = escapeshellarg($value);
305: } else {
306: $this->additional[] = $param;
307: }
308:
309: return $this;
310: }
311:
312: /**
313: * Returns SVN username with the given value for use as SVN binary parameter.
314: *
315: * Username given in the argument has precedence over the value stored in object's attribute.
316: * Returns empty string if no username is set in any way.
317: *
318: * @param mixed $user Username
319: * @return string
320: */
321: protected function getUserString($user = '')
322: {
323: if (false === $user) {
324: return '';
325: } elseif (!empty($user)) {
326: return ' --username ' . escapeshellarg($user);
327: } elseif (!empty($this->user)) {
328: return ' --username ' . escapeshellarg($this->user);
329: } else {
330: return '';
331: }
332: }
333:
334: /**
335: * Returns SVN user password with the given value for use as SVN binary parameter.
336: *
337: * Password given in the argument has precedence over the value stored in object's attribute.
338: * Returns empty string if no password is set in any way.
339: *
340: * @param mixed $password Password
341: * @return string
342: */
343: protected function getPasswordString($password = '')
344: {
345: if (false === $password) {
346: return '';
347: } elseif (!empty($password)) {
348: return ' --password ' . escapeshellarg($password);
349: } elseif (!empty($this->password)) {
350: return ' --password ' . escapeshellarg($this->password);
351: } else {
352: return '';
353: }
354: }
355:
356: /**
357: * Returns additional parameters with the given value for use as SVN binary parameters.
358: *
359: * Parameters given in the argument have precedence over values stored in object's attribute.
360: * If parameters are given as arrays, they get merged.
361: *
362: * Returns empty string if no parameters are set in any way.
363: *
364: * @param mixed $params Parameters
365: * @param boolean $pathsOnly Use only path-parameters (not beginning with a dash "-")
366: * @return string
367: */
368: protected function getAdditionalParams($params = array(), $pathsOnly = false)
369: {
370: $ret = ' ';
371:
372: foreach ($this->additional as $param => $value) {
373:
374: // If the key exists in $params or it is numeric, skip it.
375: if (isset($params[$param]) && !is_numeric($param)) {
376: continue;
377: }
378:
379: // If we want only paths, skip parameters beginning with a dash "-".
380: if ($pathsOnly && ($param{0} == '-' || $value{0} == '-')) {
381: continue;
382: }
383:
384: // If the key is not numeric, add it as well.
385: if (!is_numeric($param)) {
386: $ret .= ' ' . $param;
387: }
388: // And finally add the parameter value.
389: $ret .= ' ' . $value;
390:
391: }
392:
393: foreach ((array) $params as $param => $value) {
394:
395: // If we want only paths.
396: if ($pathsOnly && ($param{0} == '-' || $value{0} == '-')) {
397: continue;
398: }
399:
400: if (!is_numeric($param)) {
401: $ret .= ' ' . $param;
402: }
403: $ret .= ' ' . escapeshellarg($value);
404:
405: }
406:
407: return $ret;
408: }
409: }
410: