Chris@13
|
1 <?php
|
Chris@13
|
2
|
Chris@13
|
3 /*
|
Chris@13
|
4 * This file is part of Psy Shell.
|
Chris@13
|
5 *
|
Chris@13
|
6 * (c) 2012-2018 Justin Hileman
|
Chris@13
|
7 *
|
Chris@13
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@13
|
9 * file that was distributed with this source code.
|
Chris@13
|
10 */
|
Chris@13
|
11
|
Chris@13
|
12 namespace Psy;
|
Chris@13
|
13
|
Chris@13
|
14 use XdgBaseDir\Xdg;
|
Chris@13
|
15
|
Chris@13
|
16 /**
|
Chris@13
|
17 * A Psy Shell configuration path helper.
|
Chris@13
|
18 */
|
Chris@13
|
19 class ConfigPaths
|
Chris@13
|
20 {
|
Chris@13
|
21 /**
|
Chris@13
|
22 * Get potential config directory paths.
|
Chris@13
|
23 *
|
Chris@13
|
24 * Returns `~/.psysh`, `%APPDATA%/PsySH` (when on Windows), and all
|
Chris@13
|
25 * XDG Base Directory config directories:
|
Chris@13
|
26 *
|
Chris@13
|
27 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
Chris@13
|
28 *
|
Chris@13
|
29 * @return string[]
|
Chris@13
|
30 */
|
Chris@13
|
31 public static function getConfigDirs()
|
Chris@13
|
32 {
|
Chris@13
|
33 $xdg = new Xdg();
|
Chris@13
|
34
|
Chris@13
|
35 return self::getDirNames($xdg->getConfigDirs());
|
Chris@13
|
36 }
|
Chris@13
|
37
|
Chris@13
|
38 /**
|
Chris@13
|
39 * Get potential home config directory paths.
|
Chris@13
|
40 *
|
Chris@13
|
41 * Returns `~/.psysh`, `%APPDATA%/PsySH` (when on Windows), and the
|
Chris@13
|
42 * XDG Base Directory home config directory:
|
Chris@13
|
43 *
|
Chris@13
|
44 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
Chris@13
|
45 *
|
Chris@13
|
46 * @return string[]
|
Chris@13
|
47 */
|
Chris@13
|
48 public static function getHomeConfigDirs()
|
Chris@13
|
49 {
|
Chris@13
|
50 $xdg = new Xdg();
|
Chris@13
|
51
|
Chris@13
|
52 return self::getDirNames([$xdg->getHomeConfigDir()]);
|
Chris@13
|
53 }
|
Chris@13
|
54
|
Chris@13
|
55 /**
|
Chris@13
|
56 * Get the current home config directory.
|
Chris@13
|
57 *
|
Chris@13
|
58 * Returns the highest precedence home config directory which actually
|
Chris@13
|
59 * exists. If none of them exists, returns the highest precedence home
|
Chris@13
|
60 * config directory (`%APPDATA%/PsySH` on Windows, `~/.config/psysh`
|
Chris@13
|
61 * everywhere else).
|
Chris@13
|
62 *
|
Chris@13
|
63 * @see self::getHomeConfigDirs
|
Chris@13
|
64 *
|
Chris@13
|
65 * @return string
|
Chris@13
|
66 */
|
Chris@13
|
67 public static function getCurrentConfigDir()
|
Chris@13
|
68 {
|
Chris@13
|
69 $configDirs = self::getHomeConfigDirs();
|
Chris@13
|
70 foreach ($configDirs as $configDir) {
|
Chris@17
|
71 if (@\is_dir($configDir)) {
|
Chris@13
|
72 return $configDir;
|
Chris@13
|
73 }
|
Chris@13
|
74 }
|
Chris@13
|
75
|
Chris@13
|
76 return $configDirs[0];
|
Chris@13
|
77 }
|
Chris@13
|
78
|
Chris@13
|
79 /**
|
Chris@13
|
80 * Find real config files in config directories.
|
Chris@13
|
81 *
|
Chris@13
|
82 * @param string[] $names Config file names
|
Chris@13
|
83 * @param string $configDir Optionally use a specific config directory
|
Chris@13
|
84 *
|
Chris@13
|
85 * @return string[]
|
Chris@13
|
86 */
|
Chris@13
|
87 public static function getConfigFiles(array $names, $configDir = null)
|
Chris@13
|
88 {
|
Chris@13
|
89 $dirs = ($configDir === null) ? self::getConfigDirs() : [$configDir];
|
Chris@13
|
90
|
Chris@13
|
91 return self::getRealFiles($dirs, $names);
|
Chris@13
|
92 }
|
Chris@13
|
93
|
Chris@13
|
94 /**
|
Chris@13
|
95 * Get potential data directory paths.
|
Chris@13
|
96 *
|
Chris@13
|
97 * If a `dataDir` option was explicitly set, returns an array containing
|
Chris@13
|
98 * just that directory.
|
Chris@13
|
99 *
|
Chris@13
|
100 * Otherwise, it returns `~/.psysh` and all XDG Base Directory data directories:
|
Chris@13
|
101 *
|
Chris@13
|
102 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
Chris@13
|
103 *
|
Chris@13
|
104 * @return string[]
|
Chris@13
|
105 */
|
Chris@13
|
106 public static function getDataDirs()
|
Chris@13
|
107 {
|
Chris@13
|
108 $xdg = new Xdg();
|
Chris@13
|
109
|
Chris@13
|
110 return self::getDirNames($xdg->getDataDirs());
|
Chris@13
|
111 }
|
Chris@13
|
112
|
Chris@13
|
113 /**
|
Chris@13
|
114 * Find real data files in config directories.
|
Chris@13
|
115 *
|
Chris@13
|
116 * @param string[] $names Config file names
|
Chris@13
|
117 * @param string $dataDir Optionally use a specific config directory
|
Chris@13
|
118 *
|
Chris@13
|
119 * @return string[]
|
Chris@13
|
120 */
|
Chris@13
|
121 public static function getDataFiles(array $names, $dataDir = null)
|
Chris@13
|
122 {
|
Chris@13
|
123 $dirs = ($dataDir === null) ? self::getDataDirs() : [$dataDir];
|
Chris@13
|
124
|
Chris@13
|
125 return self::getRealFiles($dirs, $names);
|
Chris@13
|
126 }
|
Chris@13
|
127
|
Chris@13
|
128 /**
|
Chris@13
|
129 * Get a runtime directory.
|
Chris@13
|
130 *
|
Chris@13
|
131 * Defaults to `/psysh` inside the system's temp dir.
|
Chris@13
|
132 *
|
Chris@13
|
133 * @return string
|
Chris@13
|
134 */
|
Chris@13
|
135 public static function getRuntimeDir()
|
Chris@13
|
136 {
|
Chris@13
|
137 $xdg = new Xdg();
|
Chris@13
|
138
|
Chris@17
|
139 \set_error_handler(['Psy\Exception\ErrorException', 'throwException']);
|
Chris@13
|
140
|
Chris@13
|
141 try {
|
Chris@13
|
142 // XDG doesn't really work on Windows, sometimes complains about
|
Chris@13
|
143 // permissions, sometimes tries to remove non-empty directories.
|
Chris@13
|
144 // It's a bit flaky. So we'll give this a shot first...
|
Chris@13
|
145 $runtimeDir = $xdg->getRuntimeDir(false);
|
Chris@13
|
146 } catch (\Exception $e) {
|
Chris@13
|
147 // Well. That didn't work. Fall back to a boring old folder in the
|
Chris@13
|
148 // system temp dir.
|
Chris@17
|
149 $runtimeDir = \sys_get_temp_dir();
|
Chris@13
|
150 }
|
Chris@13
|
151
|
Chris@17
|
152 \restore_error_handler();
|
Chris@13
|
153
|
Chris@17
|
154 return \strtr($runtimeDir, '\\', '/') . '/psysh';
|
Chris@13
|
155 }
|
Chris@13
|
156
|
Chris@13
|
157 private static function getDirNames(array $baseDirs)
|
Chris@13
|
158 {
|
Chris@17
|
159 $dirs = \array_map(function ($dir) {
|
Chris@17
|
160 return \strtr($dir, '\\', '/') . '/psysh';
|
Chris@13
|
161 }, $baseDirs);
|
Chris@13
|
162
|
Chris@13
|
163 // Add ~/.psysh
|
Chris@17
|
164 if ($home = \getenv('HOME')) {
|
Chris@17
|
165 $dirs[] = \strtr($home, '\\', '/') . '/.psysh';
|
Chris@13
|
166 }
|
Chris@13
|
167
|
Chris@13
|
168 // Add some Windows specific ones :)
|
Chris@17
|
169 if (\defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
Chris@17
|
170 if ($appData = \getenv('APPDATA')) {
|
Chris@13
|
171 // AppData gets preference
|
Chris@17
|
172 \array_unshift($dirs, \strtr($appData, '\\', '/') . '/PsySH');
|
Chris@13
|
173 }
|
Chris@13
|
174
|
Chris@17
|
175 $dir = \strtr(\getenv('HOMEDRIVE') . '/' . \getenv('HOMEPATH'), '\\', '/') . '/.psysh';
|
Chris@17
|
176 if (!\in_array($dir, $dirs)) {
|
Chris@13
|
177 $dirs[] = $dir;
|
Chris@13
|
178 }
|
Chris@13
|
179 }
|
Chris@13
|
180
|
Chris@13
|
181 return $dirs;
|
Chris@13
|
182 }
|
Chris@13
|
183
|
Chris@13
|
184 private static function getRealFiles(array $dirNames, array $fileNames)
|
Chris@13
|
185 {
|
Chris@13
|
186 $files = [];
|
Chris@13
|
187 foreach ($dirNames as $dir) {
|
Chris@13
|
188 foreach ($fileNames as $name) {
|
Chris@13
|
189 $file = $dir . '/' . $name;
|
Chris@17
|
190 if (@\is_file($file)) {
|
Chris@13
|
191 $files[] = $file;
|
Chris@13
|
192 }
|
Chris@13
|
193 }
|
Chris@13
|
194 }
|
Chris@13
|
195
|
Chris@13
|
196 return $files;
|
Chris@13
|
197 }
|
Chris@13
|
198
|
Chris@13
|
199 /**
|
Chris@13
|
200 * Ensure that $file exists and is writable, make the parent directory if necessary.
|
Chris@13
|
201 *
|
Chris@13
|
202 * Generates E_USER_NOTICE error if either $file or its directory is not writable.
|
Chris@13
|
203 *
|
Chris@13
|
204 * @param string $file
|
Chris@13
|
205 *
|
Chris@13
|
206 * @return string|false Full path to $file, or false if file is not writable
|
Chris@13
|
207 */
|
Chris@13
|
208 public static function touchFileWithMkdir($file)
|
Chris@13
|
209 {
|
Chris@17
|
210 if (\file_exists($file)) {
|
Chris@17
|
211 if (\is_writable($file)) {
|
Chris@13
|
212 return $file;
|
Chris@13
|
213 }
|
Chris@13
|
214
|
Chris@17
|
215 \trigger_error(\sprintf('Writing to %s is not allowed.', $file), E_USER_NOTICE);
|
Chris@13
|
216
|
Chris@13
|
217 return false;
|
Chris@13
|
218 }
|
Chris@13
|
219
|
Chris@17
|
220 $dir = \dirname($file);
|
Chris@13
|
221
|
Chris@17
|
222 if (!\is_dir($dir)) {
|
Chris@13
|
223 // Just try making it and see if it works
|
Chris@17
|
224 @\mkdir($dir, 0700, true);
|
Chris@13
|
225 }
|
Chris@13
|
226
|
Chris@17
|
227 if (!\is_dir($dir) || !\is_writable($dir)) {
|
Chris@17
|
228 \trigger_error(\sprintf('Writing to %s is not allowed.', $dir), E_USER_NOTICE);
|
Chris@13
|
229
|
Chris@13
|
230 return false;
|
Chris@13
|
231 }
|
Chris@13
|
232
|
Chris@17
|
233 \touch($file);
|
Chris@13
|
234
|
Chris@13
|
235 return $file;
|
Chris@13
|
236 }
|
Chris@13
|
237 }
|