Chris@0: getConfigDirs()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get potential home config directory paths. Chris@0: * Chris@0: * Returns `~/.psysh`, `%APPDATA%/PsySH` (when on Windows), and the Chris@0: * XDG Base Directory home config directory: Chris@0: * Chris@0: * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html Chris@0: * Chris@0: * @return string[] Chris@0: */ Chris@0: public static function getHomeConfigDirs() Chris@0: { Chris@0: $xdg = new Xdg(); Chris@0: Chris@0: return self::getDirNames([$xdg->getHomeConfigDir()]); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the current home config directory. Chris@0: * Chris@0: * Returns the highest precedence home config directory which actually Chris@0: * exists. If none of them exists, returns the highest precedence home Chris@0: * config directory (`%APPDATA%/PsySH` on Windows, `~/.config/psysh` Chris@0: * everywhere else). Chris@0: * Chris@0: * @see self::getHomeConfigDirs Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public static function getCurrentConfigDir() Chris@0: { Chris@0: $configDirs = self::getHomeConfigDirs(); Chris@0: foreach ($configDirs as $configDir) { Chris@4: if (@\is_dir($configDir)) { Chris@0: return $configDir; Chris@0: } Chris@0: } Chris@0: Chris@0: return $configDirs[0]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Find real config files in config directories. Chris@0: * Chris@0: * @param string[] $names Config file names Chris@0: * @param string $configDir Optionally use a specific config directory Chris@0: * Chris@0: * @return string[] Chris@0: */ Chris@0: public static function getConfigFiles(array $names, $configDir = null) Chris@0: { Chris@0: $dirs = ($configDir === null) ? self::getConfigDirs() : [$configDir]; Chris@0: Chris@0: return self::getRealFiles($dirs, $names); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get potential data directory paths. Chris@0: * Chris@0: * If a `dataDir` option was explicitly set, returns an array containing Chris@0: * just that directory. Chris@0: * Chris@0: * Otherwise, it returns `~/.psysh` and all XDG Base Directory data directories: Chris@0: * Chris@0: * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html Chris@0: * Chris@0: * @return string[] Chris@0: */ Chris@0: public static function getDataDirs() Chris@0: { Chris@0: $xdg = new Xdg(); Chris@0: Chris@0: return self::getDirNames($xdg->getDataDirs()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Find real data files in config directories. Chris@0: * Chris@0: * @param string[] $names Config file names Chris@0: * @param string $dataDir Optionally use a specific config directory Chris@0: * Chris@0: * @return string[] Chris@0: */ Chris@0: public static function getDataFiles(array $names, $dataDir = null) Chris@0: { Chris@0: $dirs = ($dataDir === null) ? self::getDataDirs() : [$dataDir]; Chris@0: Chris@0: return self::getRealFiles($dirs, $names); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get a runtime directory. Chris@0: * Chris@0: * Defaults to `/psysh` inside the system's temp dir. Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: public static function getRuntimeDir() Chris@0: { Chris@0: $xdg = new Xdg(); Chris@0: Chris@4: \set_error_handler(['Psy\Exception\ErrorException', 'throwException']); Chris@0: Chris@0: try { Chris@0: // XDG doesn't really work on Windows, sometimes complains about Chris@0: // permissions, sometimes tries to remove non-empty directories. Chris@0: // It's a bit flaky. So we'll give this a shot first... Chris@0: $runtimeDir = $xdg->getRuntimeDir(false); Chris@0: } catch (\Exception $e) { Chris@0: // Well. That didn't work. Fall back to a boring old folder in the Chris@0: // system temp dir. Chris@4: $runtimeDir = \sys_get_temp_dir(); Chris@0: } Chris@0: Chris@4: \restore_error_handler(); Chris@0: Chris@4: return \strtr($runtimeDir, '\\', '/') . '/psysh'; Chris@0: } Chris@0: Chris@0: private static function getDirNames(array $baseDirs) Chris@0: { Chris@4: $dirs = \array_map(function ($dir) { Chris@4: return \strtr($dir, '\\', '/') . '/psysh'; Chris@0: }, $baseDirs); Chris@0: Chris@0: // Add ~/.psysh Chris@4: if ($home = \getenv('HOME')) { Chris@4: $dirs[] = \strtr($home, '\\', '/') . '/.psysh'; Chris@0: } Chris@0: Chris@0: // Add some Windows specific ones :) Chris@4: if (\defined('PHP_WINDOWS_VERSION_MAJOR')) { Chris@4: if ($appData = \getenv('APPDATA')) { Chris@0: // AppData gets preference Chris@4: \array_unshift($dirs, \strtr($appData, '\\', '/') . '/PsySH'); Chris@0: } Chris@0: Chris@4: $dir = \strtr(\getenv('HOMEDRIVE') . '/' . \getenv('HOMEPATH'), '\\', '/') . '/.psysh'; Chris@4: if (!\in_array($dir, $dirs)) { Chris@0: $dirs[] = $dir; Chris@0: } Chris@0: } Chris@0: Chris@0: return $dirs; Chris@0: } Chris@0: Chris@0: private static function getRealFiles(array $dirNames, array $fileNames) Chris@0: { Chris@0: $files = []; Chris@0: foreach ($dirNames as $dir) { Chris@0: foreach ($fileNames as $name) { Chris@0: $file = $dir . '/' . $name; Chris@4: if (@\is_file($file)) { Chris@0: $files[] = $file; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $files; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Ensure that $file exists and is writable, make the parent directory if necessary. Chris@0: * Chris@0: * Generates E_USER_NOTICE error if either $file or its directory is not writable. Chris@0: * Chris@0: * @param string $file Chris@0: * Chris@0: * @return string|false Full path to $file, or false if file is not writable Chris@0: */ Chris@0: public static function touchFileWithMkdir($file) Chris@0: { Chris@4: if (\file_exists($file)) { Chris@4: if (\is_writable($file)) { Chris@0: return $file; Chris@0: } Chris@0: Chris@4: \trigger_error(\sprintf('Writing to %s is not allowed.', $file), E_USER_NOTICE); Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@4: $dir = \dirname($file); Chris@0: Chris@4: if (!\is_dir($dir)) { Chris@0: // Just try making it and see if it works Chris@4: @\mkdir($dir, 0700, true); Chris@0: } Chris@0: Chris@4: if (!\is_dir($dir) || !\is_writable($dir)) { Chris@4: \trigger_error(\sprintf('Writing to %s is not allowed.', $dir), E_USER_NOTICE); Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@4: \touch($file); Chris@0: Chris@0: return $file; Chris@0: } Chris@0: }