Binaries::whichFrom()   D
last analyzed

Complexity

Conditions 9
Paths 30

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 21
cts 21
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 17
nc 30
nop 2
crap 9
1
<?php
2
3
/*
4
 * This file is part of Rocketeer
5
 *
6
 * (c) Maxime Fabre <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 */
12
13
namespace Rocketeer\Services\Connections\Shell\Modules;
14
15
/**
16
 * Handles finding and calling binaries.
17
 *
18
 * @method \Rocketeer\Binaries\PackageManagers\Bower bower()
19
 * @method \Rocketeer\Binaries\PackageManagers\Bundler bundler()
20
 * @method \Rocketeer\Binaries\PackageManagers\Composer composer()
21
 * @method \Rocketeer\Binaries\PackageManagers\Npm npm()
22
 * @method \Rocketeer\Binaries\Php php()
23
 * @method \Rocketeer\Binaries\Phpunit phpunit()
24
 * @method \Rocketeer\Binaries\Vcs\Git git()
25
 * @method \Rocketeer\Binaries\Vcs\Hg hg()
26
 * @method \Rocketeer\Binaries\Vcs\Svn svn()
27
 */
28
class Binaries extends AbstractBashModule
29
{
30
    /**
31
     * @var bool
32
     */
33
    protected $default = true;
34
35
    /**
36
     * Get a binary.
37
     *
38
     * @param string $name
39
     * @param array  $arguments
40
     *
41
     * @return \Rocketeer\Binaries\PackageManagers\AbstractPackageManager|string
42
     */
43 64
    public function __call($name, $arguments)
44
    {
45 29
        $binary = $this->binary($name);
46 64
        if ($arguments) {
47 1
            return $binary->run(...$arguments);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method run does not exist on object<Rocketeer\Binarie...AbstractPackageManager>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
48
        }
49
50 28
        return $binary;
51
    }
52
53
    ////////////////////////////////////////////////////////////////////
54
    /////////////////////////////// BINARIES ///////////////////////////
55
    ////////////////////////////////////////////////////////////////////
56
57
    /**
58
     * Get an AnonymousBinary instance.
59
     *
60
     * @param string $binary
61
     *
62
     * @return \Rocketeer\Binaries\PackageManagers\AbstractPackageManager
63
     */
64 60
    public function binary($binary)
65
    {
66 60
        return $this->modulable->builder->buildBinary($binary);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method buildBinary does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
67
    }
68
69
    ////////////////////////////////////////////////////////////////////
70
    /////////////////////////////// HELPERS ////////////////////////////
71
    ////////////////////////////////////////////////////////////////////
72
73
    /**
74
     * Get the path to a binary.
75
     *
76
     * @param string      $binary   The name of the binary
77
     * @param string|null $fallback A fallback location
78
     * @param bool        $prompt
79
     *
80
     * @return string
81
     */
82 62
    public function which($binary, $fallback = null, $prompt = true)
83
    {
84
        $locations = [
85 62
            $this->paths->getPath($binary),
86 62
            $this->localStorage->get($this->getBinaryStoragePath($binary)),
87 62
            $binary,
88 62
        ];
89
90
        // Add fallback if provided
91 62
        if ($fallback) {
92 47
            $locations[] = $fallback;
93 47
        }
94
95
        // Add command prompt if possible
96 62
        if ($this->hasCommand() && $prompt) {
97 15
            $prompt = 'Binary "'.$binary.'" could not be found, please enter the path to it';
98 15
            $locations[] = [$this->command, 'ask', $prompt];
99 15
        }
100
101 62
        return $this->whichFrom($binary, $locations);
102
    }
103
104
    /**
105
     * Scan an array of locations for a binary.
106
     *
107
     * @param string $binary
108
     * @param array  $locations
109
     *
110
     * @return string
111
     */
112 62
    protected function whichFrom($binary, array $locations)
113
    {
114 62
        $location = false;
115
116
        // Look in all the locations
117 62
        $tryout = 0;
118 62
        while (!$location && array_key_exists($tryout, $locations)) {
119
            // Execute method if required
120 62
            $location = $locations[$tryout];
121 62
            if (is_array($location)) {
122 1
                list($object, $method, $argument) = $location;
123 1
                $location = $object->$method($argument);
124 1
            }
125
126
            // Verify existence of returned path
127 62
            if ($location && $location !== $this->paths->getPath($binary)) {
128 59
                $location = $this->rawWhich($location);
129 59
            }
130
131 62
            ++$tryout;
132 62
        }
133
134
        // Store found location or remove it if invalid
135 62
        if (!$this->modulable->connections->is('local')) {
136 55
            if ($location) {
137 52
                $this->localStorage->set($this->getBinaryStoragePath($binary), $location);
138 52
            } else {
139 11
                $this->localStorage->forget($this->getBinaryStoragePath($binary));
140
            }
141 55
        }
142
143 62
        return $location ?: $binary;
144
    }
145
146
    /**
147
     * Do a straight call to which.
148
     *
149
     * @param string $location
150
     *
151
     * @return string|false
152
     */
153 60
    public function rawWhich($location)
154
    {
155 60
        $location = $this->modulable->runSilently('which '.$location);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method runSilently does not exist on object<Rocketeer\Services\Connections\Shell\Bash>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
156 60
        if (mb_strpos($location, 'not found') !== false || mb_strpos($location, 'in (') !== false) {
157 1
            return false;
158
        }
159
160 59
        return $location;
161
    }
162
163
    /**
164
     * Get the path in which to store/retrieve a binary's path.
165
     *
166
     * @param string $binary
167
     *
168
     * @return string
169
     */
170 62
    protected function getBinaryStoragePath($binary)
171
    {
172 62
        return 'paths.'.$this->connections->getCurrentConnectionKey()->name.'.'.$binary;
173
    }
174
175
    /**
176
     * @return string[]
177
     */
178 442
    public function getProvided()
179
    {
180
        return [
181 442
            'binary',
182 442
            'rawWhich',
183 442
            'which',
184 442
        ];
185
    }
186
}
187