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( |
|
|
|
|
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
|
|
|
|
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.