Completed
Push — master ( 9d2f47...58748d )
by Marcel
02:18
created

Path::create()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 13
rs 9.4285
cc 3
eloc 9
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
    private $scheme = '';
10
    private $path = '';
11
12
    /**
13
     * Combine any amount of strings into a path.
14
     *
15
     * @param string|array ...$paths One or as many parameters as you need. Both strings and arrays of strings can be mixed.
16
     *
17
     * @return string The combined paths. Note that the directory separators will be changed to reflect the local system.
18
     */
19
    public static function combine(...$paths)
20
    {
21
        // Flatten into simple array
22
        $paths = Arrays::flatten(...$paths);
23
        // Keep non-empty elements
24
        $paths = array_filter($paths, 'strlen');
25
26
        // Join parts
27
        $path = implode(DIRECTORY_SEPARATOR, $paths);
28
29
        // Localize everything after an optional scheme://
30
        $path = self::create($path);
31
        $path->path = self::localize($path->path);
32
        // Replace multiple with single separator
33
        $quotedSeparator = preg_quote(DIRECTORY_SEPARATOR);
34
        $pattern = '#' . $quotedSeparator . '+#';
35
        $path->path = preg_replace($pattern, $quotedSeparator, $path->path);
36
        return (string) $path;
37
    }
38
39
    /**
40
     * Localize directory separators for any file path according to current environment.
41
     *
42
     * @param string $path
43
     * @param string $directorySeparator
44
     *
45
     * @return string
46
     */
47
    public static function localize($path, $directorySeparator = DIRECTORY_SEPARATOR)
48
    {
49
        if (!$path instanceof self) {
50
            $path = self::create($path);
51
        }
52
        $path->path = str_replace(['\\', '/'], $directorySeparator, $path->path);
53
        return (string) $path;
54
    }
55
56
    /**
57
     * Contains returns true if a base path contains a needle.
58
     *
59
     * Note that `realpath` is used on both base and needle: they need to exist or false is returned.
60
     *
61
     * Use this for avoiding directory traversal outside of a base path.
62
     *
63
     * @param string $base   Path to base directory.
64
     * @param string $needle Needle that must exist within the base directory.
65
     *
66
     * @return bool True if both exist and needle does not escape the base folder.
67
     */
68
    public static function contains($base, $needle)
69
    {
70
        $realBase = realpath($base);
71
        $needle = realpath($needle);
72
        if ($realBase === false || $needle === false) {
73
            return false;
74
        }
75
        return Strings::startsWith($needle, $base);
76
    }
77
78
    /**
79
     * isAbsolute checks for an absolute UNIX or Windows path.
80
     *
81
     * @param string $path
82
     *
83
     * @return bool True if the path is absolute.
84
     */
85
    public static function isAbsolute($path)
86
    {
87
        return preg_match('/^([\\/\\\\]|[a-z]:[\\/\\\\])/', $path) === 1;
88
    }
89
90
    public function __toString()
91
    {
92
        return $this->scheme . $this->path;
93
    }
94
95
    /**
96
     * @param string $fullPath
97
     *
98
     * @return \nochso\Omni\Path
99
     */
100
    private static function create($fullPath)
101
    {
102
        $path = new self();
103
        $path->path = $fullPath;
104
        if (preg_match('/^([a-z]+:\\/\\/)?(.*)$/', $fullPath, $matches)) {
105
            $path->scheme = $matches[1];
106
            $path->path = $matches[2];
107
        }
108
        if ($path->scheme !== '') {
109
            $path->path = ltrim($path->path, '\\/');
110
        }
111
        return $path;
112
    }
113
}
114