Path::contains()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

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