WindowsSystem::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * this file is part of magerun
4
 *
5
 * @author Tom Klingenberg <https://github.com/ktomk>
6
 */
7
8
namespace N98\Util;
9
10
/**
11
 * Class WindowsSystem
12
 *
13
 * Utility class with global static functions.
14
 *
15
 * @package N98\Util
16
 */
17
final class WindowsSystem
18
{
19
    const PATH_SEPARATOR = ';';
20
21
    const FORBIDDEN_CHARS = '<>:"/\|?*';
22
23
    /**
24
     * @var WindowsSystem
25
     */
26
    private static $instance;
27
28
    /**
29
     * @var array
30
     */
31
    private $exts;
32
33
    /**
34
     * an instance is bootstrapped in to prevent initialization overhead
35
     *
36
     * @return WindowsSystem
37
     */
38
    private static function getInstance()
39
    {
40
        self::$instance || self::$instance = new WindowsSystem();
41
42
        return self::$instance;
43
    }
44
45
    private function __construct()
46
    {
47
    }
48
49
    /**
50
     * @return array keys are uppercase extensions incl. dot
51
     */
52
    private function getExecuteableExtesions()
53
    {
54
        // PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.PSC1
55
        $this->exts || $this->exts = array_flip(
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->exts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
56
            array_map('strtoupper', explode(self::PATH_SEPARATOR, getenv('PATHEXT')))
57
        );
58
59
        return $this->exts;
60
    }
61
62
    /**
63
     * a name is executable based on it's extension
64
     *
65
     * @param string $name
66
     *
67
     * @return bool
68
     */
69
    public static function isExecutableName($name)
70
    {
71
        // invalid name is never executable
72
        if (false !== strpbrk($name, self::FORBIDDEN_CHARS)) {
73
            return false;
74
        }
75
76
        $compare = '.' . strtoupper(pathinfo($name, PATHINFO_EXTENSION));
77
78
        if ($compare === '.') {
79
            return false;
80
        }
81
82
        $exts = self::getInstance()->getExecuteableExtesions();
83
84
        return isset($exts[$compare]);
85
    }
86
87
    /**
88
     * a program (by it's basename) is available on system for execution
89
     *
90
     * @param string $program
91
     * @return bool
92
     */
93
    public static function isProgramInstalled($program)
94
    {
95
        // programs with an invalid name do not exist
96
        if (false !== strpbrk($program, self::FORBIDDEN_CHARS)) {
97
            return false;
98
        }
99
100
        $isExecutable = self::isExecutableName($program);
101
102
        $paths = explode(self::PATH_SEPARATOR, getenv('PATH'));
103
        array_unshift($paths, getcwd());
104
        $exts = self::getInstance()->getExecuteableExtesions();
105
106
        foreach ($paths as $path) {
107
            if (!is_dir($path)) {
108
                continue;
109
            }
110
            $file = $path . '/' . $program;
111
112
            if ($isExecutable && is_readable($file)) {
113
                return true;
114
            }
115
116
            foreach ($exts as $ext => $index) {
117
                $fileEx = $file . $ext;
118
                if (is_readable($fileEx)) {
119
                    return true;
120
                }
121
            }
122
        }
123
124
        return false;
125
    }
126
}
127