Lines 72.34% 34 / 47
Methods 20.00% 1 / 5
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 locateInCommonSystemPaths 77.77% 14 / 18 0.00% 0 / 1 4.18
 locateBinariesUsingWhereIs 90.00% 9 / 10 0.00% 0 / 1 5.03
 locateBinariesUsingWhich 100.00% 4 / 4 100.00% 1 / 1 2
 locateBinariesUsingWhere 0.00% 0 / 4 0.00% 0 / 1 6
 locateInstalledBinaries 63.63% 7 / 11 0.00% 0 / 1 6.20
14class LocateBinaries
15{
16    /**
17     * Locate binaries by looking in common system paths.
18     *
19     * We try a small set of common system paths, such as "/usr/bin".
20     * On Windows, we only try C:\Windows\System32
21     * Note that you do not have to add ".exe" file extension on Windows, it is taken care of
22     *
23     * @param  string $binary  the binary to look for (ie "cwebp")
24     *
25     * @return array binaries found in common system locations
26     */
27    public static function locateInCommonSystemPaths($binary)
28    {
29        $binaries = [];
30
31        $commonSystemPaths = [];
32
33        // TODO: Maybe check $PATH instead of using hardcoded paths?
34
35        if (stripos(PHP_OS, 'WIN') === 0) {
36            $commonSystemPaths = [
37                'C:\Windows\System32',
38            ];
39            $binary .= '.exe';
40        } else {
41            $commonSystemPaths = [
42                '/usr/bin',
43                '/usr/local/bin',
44                '/usr/gnu/bin',
45                '/usr/syno/bin',
46                '/bin',
47            ];
48        }
49
50        foreach ($commonSystemPaths as $dir) {
51            // PS: FileExists might throw if exec() or similar is unavailable. We let it.
52            // - this class assumes exec is available
53            if (FileExists::fileExistsTryHarder($dir . DIRECTORY_SEPARATOR . $binary)) {
54                $binaries[] = $dir . DIRECTORY_SEPARATOR . $binary;
55            }
56        }
57        return $binaries;
58    }
59
60    /**
61     * Locate installed binaries using ie "whereis -b cwebp" (for Linux, Mac, etc)
62     *
63     * @return array  Array of paths locateed (possibly empty)
64     */
65    private static function locateBinariesUsingWhereIs($binary)
66    {
67        $isMac = (PHP_OS == 'Darwin');
68        $command = 'whereis ' . ($isMac ? '' : '-b ') . $binary . ' 2>&1';
69
70        ExecWithFallback::exec($command, $output, $returnCode);
71        //echo 'command:' . $command;
72        //echo 'output:' . print_r($output, true);
73
74        if (($returnCode == 0) && (isset($output[0]))) {
75            // On linux, result looks like this:
76            // "cwebp: /usr/bin/cwebp /usr/local/bin/cwebp"
77            // or, for empty: "cwebp:"
78
79            if ($output[0] == ($binary . ':')) {
80                return [];
81            }
82
83            // On mac, it is not prepended with name of binary.
84            // I don't know if mac returns one result per line or is space seperated
85            // As I don't know if some systems might return several lines,
86            // I assume that some do and convert to space-separation:
87            $result = implode(' ', $output);
88
89            // Next, lets remove the prepended binary (if exists)
90            $result = preg_replace('#\b' . $binary . ':\s?#', '', $result);
91
92            // And back to array
93            return explode(' ', $result);
94        }
95        return [];
96    }
97
98    /**
99     * locate installed binaries using "which -a cwebp"
100     *
101     * @param  string $binary  the binary to look for (ie "cwebp")
102     *
103     * @return array  Array of paths locateed (possibly empty)
104     */
105    private static function locateBinariesUsingWhich($binary)
106    {
107        // As suggested by @cantoute here:
108        // https://wordpress.org/support/topic/sh-1-usr-local-bin-cwebp-not-found/
109        ExecWithFallback::exec('which -a ' . $binary . ' 2>&1', $output, $returnCode);
110        if ($returnCode == 0) {
111            return $output;
112        }
113        return [];
114    }
115
116    /**
117     * Locate binaries using where.exe (for Windows)
118     *
119     * @param  string $binary  the binary to look for (ie "cwebp")
120     *
121     * @return array binaries found
122     */
123    private static function locateBinariesUsingWhere($binary)
124    {
125        ExecWithFallback::exec('where.exe ' . $binary . ' 2>&1', $output, $returnCode);
126        if ($returnCode == 0) {
127            return $output;
128        }
129        return [];
130    }
131
132    /**
133     * Locate installed binaries
134     *
135     * For linuk, we use "which -a" or, if that fails "whereis -b"
136     * For Windows, we use "where.exe"
137     * These commands only searces within $PATH. So it only finds installed binaries (which is good,
138     * as it would be unsafe to deal with binaries found scattered around)
139     *
140     * @param  string $binary  the binary to look for (ie "cwebp")
141     *
142     * @return array binaries found
143     */
144    public static function locateInstalledBinaries($binary)
145    {
146        if (stripos(PHP_OS, 'WIN') === 0) {
147            $paths = self::locateBinariesUsingWhere($binary);
148            if (count($paths) > 0) {
149                return $paths;
150            }
151        } else {
152            $paths = self::locateBinariesUsingWhich($binary);
153            if (count($paths) > 0) {
154                return $paths;
155            }
156
157            $paths = self::locateBinariesUsingWhereIs($binary);
158            if (count($paths) > 0) {
159                return $paths;
160            }
161        }
162        return [];
163    }
164}