ILIAS  release_8 Revision v8.24
AbstractFileSystemStorageHandler.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
36
42{
43 protected const DATA = 'data';
44 protected \ILIAS\ResourceStorage\StorageHandler\PathGenerator\PathGenerator $path_generator;
45 protected \ILIAS\ResourceStorage\Identification\IdentificationGenerator $id;
46 protected bool $links_possible = false;
47 protected Filesystem $fs;
49
50 public function __construct(
53 bool $determine_linking_possible = false
54 ) {
55 $this->fs = $fs;
56 $this->location = $location;
57 $this->id = new UniqueIDIdentificationGenerator();
58 if ($determine_linking_possible) {
59 $this->links_possible = $this->determineLinksPossible();
60 }
61 }
62
63 private function determineLinksPossible(): bool
64 {
65 $random_filename = "test_" . random_int(10000, 99999);
66
67 $original_filename = $this->getStorageLocationBasePath() . "/" . $random_filename . '_orig';
68 $linked_filename = $this->getStorageLocationBasePath() . "/" . $random_filename . '_link';
69 $cleaner = function () use ($original_filename, $linked_filename): void {
70 try {
71 $this->fs->delete($original_filename);
72 } catch (\Throwable $exception) {
73 }
74 try {
75 $this->fs->delete($linked_filename);
76 } catch (\Throwable $exception) {
77 }
78 };
79
80 try {
81 // remove existing files
82 $cleaner();
83
84 // create file
85 $this->fs->write($original_filename, 'data');
86 $stream = $this->fs->readStream($original_filename);
87
88 // determine absolute pathes
89 $original_absolute_path = $stream->getMetadata('uri');
90 $linked_absolute_path = dirname($original_absolute_path) . "/" . $random_filename . '_link';
91
92 // try linking and unlinking
94 $linking = @link($original_absolute_path, $linked_absolute_path);
96 $unlinking = @unlink($original_absolute_path);
97 $stream->close();
98 if ($linking && $unlinking && $this->fs->has($linked_filename)) {
99 $cleaner();
100
101 return true;
102 }
103 $cleaner();
104 } catch (\Throwable $exception) {
105 return false;
106 }
107 return false;
108 }
109
114 {
115 return $this->id;
116 }
117
118 public function has(ResourceIdentification $identification): bool
119 {
120 return $this->fs->has($this->getFullContainerPath($identification));
121 }
122
126 public function getStream(Revision $revision): FileStream
127 {
128 return $this->fs->readStream($this->getRevisionPath($revision) . '/' . self::DATA);
129 }
130
131 public function storeUpload(UploadedFileRevision $revision): bool
132 {
133 global $DIC;
134
135 $DIC->upload()->moveOneFileTo(
136 $revision->getUpload(),
137 $this->getRevisionPath($revision),
138 $this->location,
139 self::DATA
140 );
141
142 return true;
143 }
144
148 public function storeStream(FileStreamRevision $revision): bool
149 {
150 try {
151 if ($revision->keepOriginal()) {
152 $stream = $revision->getStream();
153 $this->fs->writeStream($this->getRevisionPath($revision) . '/' . self::DATA, $stream);
154 $stream->close();
155 } else {
156 $original_path = $revision->getStream()->getMetadata('uri');
157 if ($this->links_possible) {
158 $this->fs->createDir($this->getRevisionPath($revision));
159 link($original_path, $this->getAbsoluteRevisionPath($revision) . '/' . self::DATA);
160 unlink($original_path);
161 } else {
162 $source_fs = LegacyPathHelper::deriveLocationFrom($original_path);
163 if ($source_fs !== Location::STORAGE) {
164 $stream = $revision->getStream();
165 $this->fs->writeStream($this->getRevisionPath($revision) . '/' . self::DATA, $stream);
166 $stream->close();
167 unlink($original_path);
168 } else {
169 $this->fs->rename(
171 $this->getRevisionPath($revision) . '/' . self::DATA
172 );
173 }
174 }
175 $revision->getStream()->close();
176 }
177 } catch (\Throwable $exception) {
178 return false;
179 }
180
181 return true;
182 }
183
184 public function cloneRevision(CloneRevision $revision): bool
185 {
186 $stream = $this->getStream($revision->getRevisionToClone());
187 try {
188 $this->fs->writeStream($this->getRevisionPath($revision) . '/' . self::DATA, $stream);
189 $stream->close();
190 } catch (\Throwable $exception) {
191 return false;
192 }
193
194 return true;
195 }
196
200 public function deleteRevision(Revision $revision): void
201 {
202 try {
203 $this->fs->deleteDir($this->getRevisionPath($revision));
204 } catch (\Throwable $exception) {
205 }
206 }
207
211 public function deleteResource(StorableResource $resource): void
212 {
213 try {
214 $this->fs->deleteDir($this->getFullContainerPath($resource->getIdentification()));
215 } catch (\Throwable $exception) {
216 }
217 try {
218 $this->cleanUpContainer($resource);
219 } catch (\Throwable $exception) {
220 }
221 }
222
223 public function cleanUpContainer(StorableResource $resource): void
224 {
225 $storage_path = $this->getStorageLocationBasePath();
226 $container_path = $this->getContainerPathWithoutBase($resource->getIdentification());
227 $first_level = strtok($container_path, "/");
228 if (!empty($first_level)) {
229 $full_first_level = $storage_path . '/' . $first_level;
230 $number_of_files = $this->fs->finder()->files()->in([$full_first_level])->count();
231 if ($number_of_files === 0) {
232 $this->fs->deleteDir($full_first_level);
233 }
234 }
235 }
236
237 public function getBasePath(ResourceIdentification $identification): string
238 {
239 return $this->getFullContainerPath($identification);
240 }
241
242 public function getRevisionPath(Revision $revision): string
243 {
244 return $this->getFullContainerPath($revision->getIdentification()) . '/' . $revision->getVersionNumber();
245 }
246
247 public function getFullContainerPath(ResourceIdentification $identification): string
248 {
249 return $this->getStorageLocationBasePath() . '/' . $this->getContainerPathWithoutBase($identification);
250 }
251
252 public function getContainerPathWithoutBase(ResourceIdentification $identification): string
253 {
254 return $this->path_generator->getPathFor($identification);
255 }
256
257 private function getAbsoluteRevisionPath(Revision $revision): string
258 {
259 return rtrim(CLIENT_DATA_DIR, "/") . "/" . ltrim($this->getRevisionPath($revision), "/");
260 }
261
262 public function movementImplementation(): string
263 {
264 return $this->links_possible ? 'link' : 'rename';
265 }
266}
static createRelativePath(string $absolute_path)
Creates a relative path from an absolute path which starts with a valid storage location.
static deriveLocationFrom(string $absolute_path)
getContainerPathWithoutBase(ResourceIdentification $identification)
This is only the path of a ResourceIdentification inside the StorageLocation base path.
getRevisionPath(Revision $revision)
This is the full path to a revision of a Resource, incl.
getFullContainerPath(ResourceIdentification $identification)
This is the full path to the container of a ResourceIdentification (incl.
__construct(Filesystem $fs, int $location=Location::STORAGE, bool $determine_linking_possible=false)
cleanUpContainer(StorableResource $resource)
This checks if there are empty directories in the filesystem which can be deleted.
const CLIENT_DATA_DIR
Definition: constants.php:46
global $DIC
Definition: feed.php:28
Interface Location.
Definition: Location.php:30
const STORAGE
The filesystem outside of the ilias web root.
Definition: Location.php:40
Interface Filesystem.
Definition: Filesystem.php:40
getStorageLocationBasePath()
This is the place in the filesystem where the containers (nested) get created.
Class FlySystemFileAccessTest \Provider\FlySystem @runTestsInSeparateProcesses @preserveGlobalState d...