Completed
Push — master ( eb1d12...837f9d )
by Marcel
02:14
created

Path::extractScheme()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 14
rs 9.2
cc 4
eloc 10
nc 4
nop 1
1
<?php
2
namespace nochso\Omni;
3
4
/**
5
 * Path helps keep the directory separator/implode/trim/replace madness away.
6
 */
7
final class Path
8
{
9
    /**
10
     * Combine any amount of strings into a path.
11
     *
12
     * @param string|array ...$paths One or as many parameters as you need. Both strings and arrays of strings can be mixed.
13
     *
14
     * @return string The combined paths. Note that the directory separators will be changed to reflect the local system.
15
     */
16
    public static function combine(...$paths)
17
    {
18
        // Flatten into simple array
19
        $paths = Arrays::flatten(...$paths);
20
        // Keep non-empty elements
21
        $paths = array_filter($paths, 'strlen');
22
23
        // Split into scheme:// and rest
24
        $scheme = self::extractScheme($paths);
25
        // Implode, localize and simplify everything after scheme://
26
        $path = implode(DIRECTORY_SEPARATOR, $paths);
27
        $path = self::localize($path);
28
        $quotedSeparator = preg_quote(DIRECTORY_SEPARATOR);
29
        $pattern = '#' . $quotedSeparator . '+#';
30
        $path = preg_replace($pattern, $quotedSeparator, $path);
31
        return $scheme . $path;
32
    }
33
34
    /**
35
     * Localize directory separators for any file path according to current environment.
36
     *
37
     * @param string $path
38
     * @param string $directorySeparator
39
     *
40
     * @return string
41
     */
42
    public static function localize($path, $directorySeparator = DIRECTORY_SEPARATOR)
43
    {
44
        $paths = [$path];
45
        // Do not localize scheme:// paths
46
        $scheme = self::extractScheme($paths);
47
        if ($scheme !== '') {
48
            return $path;
49
        }
50
        $path = str_replace(['\\', '/'], $directorySeparator, $path);
51
        return $path;
52
    }
53
54
    /**
55
     * Contains returns true if a base path contains a needle.
56
     *
57
     * Note that `realpath` is used on both base and needle: they need to exist or false is returned.
58
     *
59
     * Use this for avoiding directory traversal outside of a base path.
60
     *
61
     * @param string $base   Path to base directory.
62
     * @param string $needle Needle that must exist within the base directory.
63
     *
64
     * @return bool True if both exist and needle does not escape the base folder.
65
     */
66
    public static function contains($base, $needle)
67
    {
68
        $realBase = realpath($base);
69
        $needle = realpath($needle);
70
        if ($realBase === false || $needle === false) {
71
            return false;
72
        }
73
        return Strings::startsWith($needle, $base);
74
    }
75
76
    /**
77
     * isAbsolute checks for an absolute UNIX, Windows or scheme:// path.
78
     *
79
     * Note that paths containing parent dots (`..`) can still be considered absolute.
80
     *
81
     * @param string $path
82
     *
83
     * @return bool True if the path is absolute i.e. it should be safe to append a relative path to it.
84
     */
85
    public static function isAbsolute($path)
86
    {
87
        $pattern = '@^
88
        (                         # Either..
89
            [/\\\\]               # absolute start
90
        |   [a-z]:[/\\\\]         # or Windows drive path
91
        |   [a-z][a-z0-9\.+-]+:// # or URI scheme:// - see http://tools.ietf.org/html/rfc3986#section-3.1
92
        )@ix';
93
        return preg_match($pattern, $path) === 1;
94
    }
95
96
    private static function extractScheme(&$parts) {
97
        if (!count($parts)) {
98
            return '';
99
        }
100
        $first = reset($parts);
101
        if (preg_match('/^([a-z]+:\\/\\/)(.*)$/', $first, $matches)) {
102
            array_shift($parts);
103
            if ($matches[2] !== '') {
104
                array_unshift($parts, $matches[2]);
105
            }
106
            return $matches[1];
107
        }
108
        return '';
109
    }
110
}
111