Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/Filesystem/Directory/Fly.php (1 issue)

1
<?php
2
namespace Elgg\Filesystem\Directory;
3
4
use Elgg\Filesystem\Directory;
5
use Elgg\Filesystem\File;
6
use Elgg\Structs\Collection;
7
use League\Flysystem\Adapter\Local as LocalAdapter;
8
use League\Flysystem\Filesystem;
9
use League\Flysystem\Memory\MemoryAdapter;
10
11
/**
12
 * A wrapper around Flysystem that implements Elgg's filesystem API.
13
 *
14
 * @since 1.10.0
15
 *
16
 * @access private
17
 */
18
final class Fly implements Directory {
19
20
	/** @var Filesystem */
21
	private $fs;
22
23
	/** @var string */
24
	private $local_path;
25
26
	/** @var string Path relative to the filesystem's root */
27
	private $chroot;
28
29
	/**
30
	 * Use one of the static factory functions to create an instance.
31
	 *
32
	 * @param Filesystem $filesystem The underlying filesystem implementation. It must have the 'ListFiles' plugin.
33
	 * @param string     $local_path Only applicable for local filesystem.
34
	 * @param string     $chroot     Path relative to the underlying filesystem root.
35
	 */
36 7
	public function __construct(Filesystem $filesystem, $local_path = '', $chroot = '') {
37 7
		$this->fs = $filesystem;
38 7
		$this->local_path = rtrim(strtr($local_path, '\\', '/'), "/\\");
39 7
		$this->chroot = $this->normalize($chroot);
40 6
	}
41
42
	/**
43
	 * {@inheritDoc}
44
	 */
45 3
	public function chroot($path) {
46 3
		return new self($this->fs, $this->local_path, $path);
47
	}
48
49
	/**
50
	 * Whether this filesystem has an existing directory at the given path.
51
	 *
52
	 * @param string $path The path to the directory, relative to this filesystem.
53
	 *
54
	 * @return boolean
55
	 */
56 4
	private function isDirectory($path) {
57 4
		$path = $this->getInternalPath($path);
58 3
		return $this->fs->has($path) && $this->fs->get($path)->isDir();
59
	}
60
61
	/**
62
	 * {@inheritDoc}
63
	 */
64 2
	public function isFile($path) {
65 2
		$path = $this->getInternalPath($path);
66 2
		return $this->fs->has($path) && $this->fs->get($path)->isFile();
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72 8
	public function getContents($path) {
73 8
		return (string) $this->fs->read($this->getInternalPath($path));
74
	}
75
76
	/**
77
	 * {@inheritDoc}
78
	 */
79 4
	public function getFile($path) {
80 4
		if ($this->isDirectory($path)) {
81
			throw new \RuntimeException("There is already a directory at that location: $path");
82
		}
83
84 3
		return new File($this, $path);
85
	}
86
87
	/**
88
	 * {@inheritDoc}
89
	 */
90 3
	public function getFiles($path = '', $recursive = true) {
91 3
		return $this->getEntries($path, $recursive, ['file']);
92
	}
93
94
	/**
95
	 * {@inheritDoc}
96
	 */
97 3
	public function getDirectories($path = '', $recursive = true) {
98 3
		return $this->getEntries($path, $recursive, ['dir']);
99
	}
100
101
	/**
102
	 * List the files and directories in the given directory path.
103
	 *
104
	 * @param string   $path      The subdirectory path within this directory
105
	 * @param bool     $recursive Find files and directories recursively
106
	 * @param string[] $types     Entry types to return ('file' and/or 'dir')
107
	 *
108
	 * @return Collection<File|Directory>
109
	 *
110
	 * @throws \InvalidArgumentException
111
	 */
112 4
	protected function getEntries($path = '', $recursive = true, $types = ['file', 'dir']) {
113 4
		$contents = $this->fs->listContents($this->getInternalPath($path), $recursive);
114 4
		if (!$contents) {
0 ignored issues
show
Bug Best Practice introduced by Steve Clay
The expression $contents of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
115
			$contents = [];
116
		}
117
118 4
		$contents = array_filter($contents, function ($metadata) use ($types) {
119 4
			return in_array($metadata['type'], $types);
120 4
		});
121
122 4
		return Collection\InMemory::fromArray(array_map(function ($metadata) {
123 4
			if ($metadata['type'] === 'file') {
124 3
				return new File($this, $metadata['path']);
125
			}
126
127 3
			return new self($this->fs, $this->local_path, $metadata['path']);
128 4
		}, $contents));
129
	}
130
131
	/**
132
	 * {@inheritDoc}
133
	 */
134 15
	public function getPath($path = '') {
135 15
		$path = $this->normalize($this->getInternalPath($path));
136 15
		return "{$this->local_path}/$path";
137
	}
138
139
	/**
140
	 * Get a path suitable for passing to the underlying filesystem.
141
	 *
142
	 * @param string $path The path relative to this directory.
143
	 *
144
	 * @return string
145
	 *
146
	 * @throws \InvalidArgumentException
147
	 */
148 23
	private function getInternalPath($path) {
149 23
		$path = strtr($path, '\\', '//');
150 23
		return $this->normalize("{$this->chroot}/$path");
151
	}
152
153
	/**
154
	 * {@inheritDoc}
155
	 */
156
	public function includeFile($path) {
157
		return include $this->getPath($path);
158
	}
159
160
	/**
161
	 * {@inheritDoc}
162
	 */
163 6
	public function putContents($path, $content) {
164 6
		$this->fs->put($this->getInternalPath($path), $content);
165 6
	}
166
167
	/**
168
	 * Shorthand for generating a new local filesystem.
169
	 *
170
	 * @param string $path absolute path to directory on local filesystem.
171
	 *
172
	 * @return Directory
173
	 */
174
	public static function createLocal($path) {
175
		$fs = new Filesystem(new LocalAdapter($path));
176
		return new self($fs, $path);
177
	}
178
179
	/**
180
	 * Shorthand for generating a new in-memory-only filesystem.
181
	 *
182
	 * @return Directory
183
	 */
184 1
	public static function createInMemory() {
185 1
		$fs = new Filesystem(new MemoryAdapter());
186 1
		return new self($fs);
187
	}
188
189
	/**
190
	 * Get a standardized form of the given path to work with internally.
191
	 *
192
	 * @param string $path A relative path within this filesystem
193
	 *
194
	 * @return string
195
	 *
196
	 * @throws \InvalidArgumentException
197
	 */
198 23
	private function normalize($path) {
199
200 23
		$test_path = "/$path/";
201 23
		if (strpos($test_path, '/./') !== false || strpos($test_path, '/../') !== false) {
202 1
			throw new \InvalidArgumentException('Paths cannot contain "." or ".."');
203
		}
204
205 22
		return trim(strtr($path, '\\', '/'), "/");
206
	}
207
}
208