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