8ec727c1
曹明
初始化代码提交
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
<?php
namespace Codeception\Util;
use Codeception\Exception\ConfigurationException;
class PathResolver
{
/**
* Returns path to a given directory relative to $projDir.
* @param string $path
* @param string $projDir
* @param string $dirSep
* @return string
*/
public static function getRelativeDir($path, $projDir, $dirSep = DIRECTORY_SEPARATOR)
{
// ensure $projDir ends with a trailing $dirSep
$projDir = preg_replace('/'.preg_quote($dirSep, '/').'*$/', $dirSep, $projDir);
// if $path is a within $projDir
if (self::fsCaseStrCmp(substr($path, 0, strlen($projDir)), $projDir, $dirSep) == 0) {
// simply chop it off the front
return substr($path, strlen($projDir));
}
// Identify any absoluteness prefix (like '/' in Unix or "C:\\" in Windows)
$pathAbsPrefix = self::getPathAbsolutenessPrefix($path, $dirSep);
$projDirAbsPrefix = self::getPathAbsolutenessPrefix($projDir, $dirSep);
$sameAbsoluteness = (self::fsCaseStrCmp($pathAbsPrefix['wholePrefix'], $projDirAbsPrefix['wholePrefix'], $dirSep) == 0);
if (!$sameAbsoluteness) {
// if the $projDir and $path aren't relative to the same
// thing, we can't make a relative path.
// if we're relative to the same device ...
if (strlen($pathAbsPrefix['devicePrefix']) &&
(self::fsCaseStrCmp($pathAbsPrefix['devicePrefix'], $projDirAbsPrefix['devicePrefix'], $dirSep) == 0)
) {
// ... shave that off
return substr($path, strlen($pathAbsPrefix['devicePrefix']));
}
// Return the input unaltered
return $path;
}
// peel off optional absoluteness prefixes and convert
// $path and $projDir to an subdirectory path array
$relPathParts = array_filter(explode($dirSep, substr($path, strlen($pathAbsPrefix['wholePrefix']))), 'strlen');
$relProjDirParts = array_filter(explode($dirSep, substr($projDir, strlen($projDirAbsPrefix['wholePrefix']))), 'strlen');
// While there are any, peel off any common parent directories
// from the beginning of the $projDir and $path
while ((count($relPathParts) > 0) && (count($relProjDirParts) > 0) &&
(self::fsCaseStrCmp($relPathParts[0], $relProjDirParts[0], $dirSep) == 0)
) {
array_shift($relPathParts);
array_shift($relProjDirParts);
}
if (count($relProjDirParts) > 0) {
// prefix $relPath with '..' for all remaining unmatched $projDir
// subdirectories
$relPathParts = array_merge(array_fill(0, count($relProjDirParts), '..'), $relPathParts);
}
// only append a trailing seperator if one is already present
$trailingSep = preg_match('/'.preg_quote($dirSep, '/').'$/', $path) ? $dirSep : '';
// convert array of dir paths back into a string path
return implode($dirSep, $relPathParts).$trailingSep;
}
/**
* FileSystem Case String Compare
* compare two strings with the filesystem's case-sensitiveness
*
* @param string $str1
* @param string $str2
* @param string $dirSep
* @return int -1 / 0 / 1 for < / = / > respectively
*/
private static function fsCaseStrCmp($str1, $str2, $dirSep = DIRECTORY_SEPARATOR)
{
$cmpFn = self::isWindows($dirSep) ? 'strcasecmp' : 'strcmp';
return $cmpFn($str1, $str2);
}
/**
* What part of this path (leftmost 0-3 characters) what
* it is absolute relative to:
*
* On Unix:
* This is simply '/' for an absolute path or
* '' for a relative path
*
* On Windows this is more complicated:
* If the first two characters are a letter followed
* by a ':', this indicates that the path is
* on a specific device.
* With or without a device specified, a path MAY
* start with a '\\' to indicate an absolute path
* on the device or '' to indicate a path relative
* to the device's CWD
*
* @param string $path
* @param string $dirSep
* @return string
*/
private static function getPathAbsolutenessPrefix($path, $dirSep = DIRECTORY_SEPARATOR)
{
$devLetterPrefixPattern = '';
if (self::isWindows($dirSep)) {
$devLetterPrefixPattern = '([A-Za-z]:|)';
}
$matches = [];
if (!preg_match('/^'.$devLetterPrefixPattern.preg_quote($dirSep, '/').'?/', $path, $matches)) {
// This should match, even if it matches 0 characters
throw new ConfigurationException("INTERNAL ERROR: This must be a regex problem.");
}
return [
'wholePrefix' => $matches[0], // The optional device letter followed by the optional $dirSep
'devicePrefix' => self::isWindows($dirSep) ? $matches[1] : ''];
}
/**
* Are we in a Windows style filesystem?
*
* @param string $dirSep
* @return bool
*/
private static function isWindows($dirSep = DIRECTORY_SEPARATOR)
{
return ($dirSep == '\\');
}
}
|