Completed
Pull Request — develop (#109)
by
unknown
15:23
created

Caller   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
c 1
b 0
f 0
lcom 1
cbo 4
dl 0
loc 166
ccs 39
cts 39
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A getBinaryPath() 0 4 1
B execute() 0 34 6
A getOutput() 0 4 1
A getOutputLines() 0 15 4
A getRawOutput() 0 4 1
A setExecuteCallback() 0 4 1
1
<?php
2
/**
3
 * GitElephant - An abstraction layer for git written in PHP
4
 * Copyright (C) 2013  Matteo Giachino
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see [http://www.gnu.org/licenses/].
18
 */
19
20
namespace GitElephant\Command\Caller;
21
22
use GitElephant\Exception\InvalidRepositoryPathException;
23
use \GitElephant\GitBinary;
24
use \GitElephant\Utilities;
25
use \Symfony\Component\Process\Process;
26
27
/**
28
 * Caller
29
 *
30
 * @author Matteo Giachino <[email protected]>
31
 */
32
class Caller implements CallerInterface
33
{
34
    /**
35
     * GitBinary instance
36
     *
37
     * @var \GitElephant\GitBinary
38
     */
39
    private $binary;
40
41
    /**
42
     * the repository path
43
     *
44
     * @var string
45
     */
46
    private $repositoryPath;
47
48
    /**
49
     * the output lines of the command
50
     *
51
     * @var array
52
     */
53
    private $outputLines = array();
54
55
    /**
56
     * raw output
57
     *
58
     * @var string
59
     */
60
    private $rawOutput;
61
62
	/**
63
	 * Callback for real-time output yielding
64
	 *
65
	 * @var callable
66
	 */
67 103
    private $callback;
68
69 103
    /**
70
     * Class constructor
71 103
     *
72 1
     * @param \GitElephant\GitBinary $binary         the binary
73
     * @param string                 $repositoryPath the physical base path for the repository
74
     */
75 103
    public function __construct(GitBinary $binary, $repositoryPath)
76 103
    {
77
        $this->binary         = $binary;
78
79
        if (!is_dir($repositoryPath)) {
80
            throw new InvalidRepositoryPathException($repositoryPath);
81
        }
82
83 1
        $this->repositoryPath = $repositoryPath;
84
    }
85 1
86
    /**
87
     * Get the binary path
88
     *
89
     * @return mixed
90
     */
91
    public function getBinaryPath()
92
    {
93
        return $this->binary->getPath();
94
    }
95
96
    /**
97
     * Executes a command
98
     *
99
     * @param string $cmd               the command to execute
100
     * @param bool   $git               if the command is git or a generic command
101
     * @param null   $cwd               the directory where the command must be executed
102
     * @param array  $acceptedExitCodes exit codes accepted to consider the command execution successful
103 97
     *
104
     * @throws \RuntimeException
105 97
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
106 97
     * @throws \Symfony\Component\Process\Exception\ProcessTimedOutException
107 97
     * @throws \Symfony\Component\Process\Exception\RuntimeException
108
     * @throws \Symfony\Component\Process\Exception\LogicException
109 97
     * @return Caller
110 97
     */
111 97
    public function execute($cmd, $git = true, $cwd = null, $acceptedExitCodes = array(0))
112 97
    {
113 1
        if ($git) {
114 1
            $cmd = $this->binary->getPath() . ' ' . $cmd;
115 1
        }
116 1
117 1
        $process = new Process($cmd, is_null($cwd) ? $this->repositoryPath : $cwd);
118
        $process->setTimeout(15000);
119 96
120
        if(is_callable($this->callback)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
121 96
        	$callback = $this->callback;
122 96
123
	        $process->run(function ($type, $buffer) use($callback) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after USE keyword; found 0
Loading history...
124 96
		        $callback($type, $buffer);
125
	        });
126
        }else{
0 ignored issues
show
Coding Style introduced by
Expected 1 space after ELSE keyword; 0 found
Loading history...
127
	        $process->run();
128
        }
129
130
	    if(!in_array($process->getExitCode(), $acceptedExitCodes)){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
131
		    $text = 'Exit code: ' . $process->getExitCode();
132 2
		    $text .= ' while executing: "' . $cmd;
133
		    $text .= '" with reason: ' . $process->getErrorOutput();
134 2
		    $text .= "\n" . $process->getOutput();
135
		    throw new \RuntimeException($text);
136
	    }
137
	    $this->rawOutput = $process->getOutput();
138
139
	    $separator = Utilities::isWindows() ? "\n" : PHP_EOL;
140
	    $values = array_map('rtrim', explode($separator, $process->getOutput()));
141
	    $this->outputLines = $values;
142
143
        return $this;
144 89
    }
145
146 89
    /**
147 80
     * returns the raw output of the last executed command
148 80
     *
149 80
     * @return string
150 80
     */
151 80
    public function getOutput()
152 80
    {
153
        return implode(" ", $this->outputLines);
154 80
    }
155
156
    /**
157 43
     * returns the output of the last executed command as an array of lines
158
     *
159
     * @param bool $stripBlankLines remove the blank lines
160
     *
161
     * @return array
162
     */
163
    public function getOutputLines($stripBlankLines = false)
164
    {
165 1
        if ($stripBlankLines) {
166
            $output = array();
167 1
            foreach ($this->outputLines as $line) {
168
                if ('' !== $line) {
169
                    $output[] = $line;
170
                }
171
            }
172
173
            return $output;
174
        }
175
176
        return $this->outputLines;
177
    }
178
179
    /**
180
     * Get RawOutput
181
     *
182
     * @return string
183
     */
184
    public function getRawOutput()
185
    {
186
        return $this->rawOutput;
187
    }
188
189
	/**
190
	 * @param callable $callback
191
	 * @return $this
192
	 */
193
    public function setExecuteCallback(callable $callback){
194
    	$this->callback = $callback;
195
    	return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (GitElephant\Command\Caller\Caller) is incompatible with the return type declared by the interface GitElephant\Command\Call...ace::setExecuteCallback of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
196
    }
197
}
198