Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExecutableFinder
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 3
110
0.00% covered (danger)
0.00%
0 / 1
 getPossibleBinPaths
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 findExecutable
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 findInDefaultPaths
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * Copyright (C) 2017 Kunal Mehta <legoktm@debian.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21use MediaWiki\Shell\Shell;
22use Wikimedia\AtEase\AtEase;
23
24/**
25 * Utility class to find executables in likely places
26 *
27 * @since 1.31
28 */
29class ExecutableFinder {
30
31    /**
32     * Get an array of likely places we can find executables. Check a bunch
33     * of known Unix-like defaults, as well as the PATH environment variable
34     * (which should maybe make it work for Windows?)
35     *
36     * @return array
37     */
38    protected static function getPossibleBinPaths() {
39        return array_unique( array_merge(
40            [ '/usr/bin', '/bin', '/usr/local/bin', '/opt/csw/bin',
41                '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ],
42            explode( PATH_SEPARATOR, getenv( 'PATH' ) )
43        ) );
44    }
45
46    /**
47     * Search a path for any of the given executable names. Returns the
48     * executable name if found. Also checks the version string returned
49     * by each executable.
50     *
51     * Used only by environment checks.
52     *
53     * @param string $path Path to search
54     * @param string $name Executable name to look for
55     * @param array|bool $versionInfo False or array with two members:
56     *   0 => Parameter to pass to binary for version check (e.g. --version)
57     *   1 => String to compare the output with
58     *
59     * If $versionInfo is not false, only executables with a version
60     * matching $versionInfo[1] will be returned.
61     * @return bool|string
62     */
63    protected static function findExecutable( $path, $name, $versionInfo = false ) {
64        $command = $path . DIRECTORY_SEPARATOR . $name;
65
66        AtEase::suppressWarnings();
67        $file_exists = is_executable( $command );
68        AtEase::restoreWarnings();
69
70        if ( $file_exists ) {
71            if ( !$versionInfo ) {
72                return $command;
73            }
74
75            $output = Shell::command( $command, $versionInfo[0] )
76                ->includeStderr()->execute()->getStdout();
77            if ( str_contains( $output, $versionInfo[1] ) ) {
78                return $command;
79            }
80        }
81
82        return false;
83    }
84
85    /**
86     * Same as locateExecutable(), but checks in getPossibleBinPaths() by default
87     * @see locateExecutable()
88     * @param string|string[] $names Array of possible names.
89     * @param array|bool $versionInfo Default: false or array with two members:
90     *   0 => Parameter to run for version check, e.g. '--version'
91     *   1 => String to compare the output with
92     *
93     * If $versionInfo is not false, only executables with a version
94     * matching $versionInfo[1] will be returned.
95     * @return bool|string
96     */
97    public static function findInDefaultPaths( $names, $versionInfo = false ) {
98        if ( Shell::isDisabled() ) {
99            // If we can't shell out, there's no point looking for executables
100            return false;
101        }
102
103        $paths = self::getPossibleBinPaths();
104        foreach ( (array)$names as $name ) {
105            foreach ( $paths as $path ) {
106                $exe = self::findExecutable( $path, $name, $versionInfo );
107                if ( $exe !== false ) {
108                    return $exe;
109                }
110            }
111        }
112
113        return false;
114    }
115
116}