Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 25
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 / 24
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 / 10
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 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Utils;
10
11use MediaWiki\Shell\Shell;
12
13/**
14 * Utility class to find executables in likely places
15 *
16 * @since 1.31
17 */
18class ExecutableFinder {
19
20    /**
21     * Get an array of likely places we can find executables. Check a bunch
22     * of known Unix-like defaults, as well as the PATH environment variable
23     * (which should maybe make it work for Windows?)
24     *
25     * @return array
26     */
27    protected static function getPossibleBinPaths() {
28        return array_unique( array_merge(
29            [ '/usr/bin', '/bin', '/usr/local/bin', '/opt/csw/bin',
30                '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ],
31            explode( PATH_SEPARATOR, getenv( 'PATH' ) )
32        ) );
33    }
34
35    /**
36     * Search a path for any of the given executable names. Returns the
37     * executable name if found. Also checks the version string returned
38     * by each executable.
39     *
40     * Used only by environment checks.
41     *
42     * @param string $path Path to search
43     * @param string $name Executable name to look for
44     * @param array|bool $versionInfo False or array with two members:
45     *   0 => Parameter to pass to binary for version check (e.g. --version)
46     *   1 => String to compare the output with
47     *
48     * If $versionInfo is not false, only executables with a version
49     * matching $versionInfo[1] will be returned.
50     * @return bool|string
51     */
52    protected static function findExecutable( $path, $name, $versionInfo = false ) {
53        $command = $path . DIRECTORY_SEPARATOR . $name;
54
55        // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
56        $file_exists = @is_executable( $command );
57
58        if ( $file_exists ) {
59            if ( !$versionInfo ) {
60                return $command;
61            }
62
63            $output = Shell::command( $command, $versionInfo[0] )
64                ->includeStderr()->execute()->getStdout();
65            if ( str_contains( $output, $versionInfo[1] ) ) {
66                return $command;
67            }
68        }
69
70        return false;
71    }
72
73    /**
74     * Same as locateExecutable(), but checks in getPossibleBinPaths() by default
75     * @see locateExecutable()
76     * @param string|string[] $names Array of possible names.
77     * @param array|bool $versionInfo Default: false or array with two members:
78     *   0 => Parameter to run for version check, e.g. '--version'
79     *   1 => String to compare the output with
80     *
81     * If $versionInfo is not false, only executables with a version
82     * matching $versionInfo[1] will be returned.
83     * @return bool|string
84     */
85    public static function findInDefaultPaths( $names, $versionInfo = false ) {
86        if ( Shell::isDisabled() ) {
87            // If we can't shell out, there's no point looking for executables
88            return false;
89        }
90
91        $paths = self::getPossibleBinPaths();
92        foreach ( (array)$names as $name ) {
93            foreach ( $paths as $path ) {
94                $exe = self::findExecutable( $path, $name, $versionInfo );
95                if ( $exe !== false ) {
96                    return $exe;
97                }
98            }
99        }
100
101        return false;
102    }
103
104}
105
106/** @deprecated class alias since 1.46 */
107class_alias( ExecutableFinder::class, 'ExecutableFinder' );