1: <?php
2:
3: namespace Charcoal\App\Script;
4:
5: use \InvalidArgumentException;
6:
7: 8: 9: 10: 11: 12: 13: 14:
15: trait PathScriptTrait
16: {
17: 18: 19: 20: 21:
22: protected static $globCache = [];
23:
24: 25: 26: 27: 28:
29: abstract public function basePath();
30:
31: 32: 33: 34: 35: 36: 37:
38: public function processMultiplePaths($paths)
39: {
40: $paths = $this->parseAsArray($paths);
41: $paths = array_map([ $this, 'filterPath' ], $paths);
42: $paths = array_filter($paths, [ $this, 'pathExists' ]);
43:
44: if ($paths === false) {
45: throw new InvalidArgumentException('Received invalid paths.');
46: }
47:
48: if (empty($paths)) {
49: throw new InvalidArgumentException('Received empty paths.');
50: }
51:
52: return $paths;
53: }
54:
55: 56: 57: 58: 59: 60: 61:
62: public function pathExists($path)
63: {
64: if (!is_string($path)) {
65: throw new InvalidArgumentException('The path must be a string.');
66: }
67:
68: return $this->globRecursive($this->basePath().'/'.$path, GLOB_BRACE);
69: }
70:
71: 72: 73: 74: 75: 76: 77: 78: 79: 80:
81: public function filterPath($path, $trim = null)
82: {
83: if (!is_string($path)) {
84: throw new InvalidArgumentException('The path must be a string.');
85: }
86:
87: if ($trim === null && defined(get_called_class().'::DIRECTORY_SEPARATORS')) {
88: $trim = static::DIRECTORY_SEPARATORS;
89: }
90:
91: if ($trim) {
92: if (!is_string($trim)) {
93: throw new InvalidArgumentException(
94: 'The characters to strip must be a string or use static::DIRECTORY_SEPARATORS.'
95: );
96: }
97:
98: $path = trim($path, $trim);
99: }
100:
101: return trim($path);
102: }
103:
104: 105: 106: 107: 108: 109: 110: 111:
112: public function filterWritablePath($path, $name = null)
113: {
114: if ($name === null && defined(get_called_class().'::DEFAULT_BASENAME')) {
115: $name = static::DEFAULT_BASENAME;
116: }
117:
118: if (!is_string($name)) {
119: throw new InvalidArgumentException(
120: 'The target file name must be a string or use static::DEFAULT_BASENAME.'
121: );
122: }
123:
124: $path = $this->filterPath($path);
125: $test = $this->basePath().'/'.$path;
126:
127: if (is_dir($test)) {
128: if (is_writable($test)) {
129: $path .= '/'.$name;
130: } else {
131: throw new InvalidArgumentException('The target path is not writeable.');
132: }
133: } elseif (is_file($test)) {
134: if (!is_writable($test)) {
135: throw new InvalidArgumentException('The target file is not writeable.');
136: }
137: } else {
138: $info = pathinfo($path);
139: $path = $this->filterWritablePath($info['dirname'], $info['basename']);
140: }
141:
142: return $path;
143: }
144:
145: 146: 147: 148: 149: 150: 151: 152:
153: public function globRecursive($pattern, $flags = 0)
154: {
155: $maxDepth = $this->maxDepth();
156: $depthKey = strval($maxDepth);
157:
158: if (isset(static::$globCache[$pattern][$depthKey])) {
159: return static::$globCache[$pattern][$depthKey];
160: }
161:
162: $depth = 1;
163: $files = glob($pattern, $flags);
164: foreach (glob(dirname($pattern).'/*', (GLOB_ONLYDIR|GLOB_NOSORT)) as $dir) {
165: $files = array_merge($files, $this->globRecursive($dir.'/'.basename($pattern), $flags));
166: $depth++;
167: if ($maxDepth > 0 && $depth >= $maxDepth) {
168: break;
169: }
170: }
171:
172: static::$globCache[$pattern][$depthKey] = array_filter($files, 'is_file');
173:
174: return $files;
175: }
176: }
177: