ILIAS  release_7 Revision v7.30-3-g800a261c036
AbstractFileSystemStorageHandler.php
Go to the documentation of this file.
1<?php declare(strict_types=1);
2
4
19
25{
26 protected const DATA = 'data';
30 protected $path_generator;
34 protected $fs;
38 protected $id;
42 protected $location;
46 protected $links_possible = false;
47
48 public function __construct(
49 Filesystem $filesystem,
51 bool $determine_linking_possible = false
52 ) {
53 $this->fs = $filesystem;
54 $this->location = $location;
55 $this->id = new UniqueIDIdentificationGenerator();
56 if ($determine_linking_possible) {
57 $this->links_possible = $this->determineLinksPossible();
58 }
59 }
60
61 private function determineLinksPossible() : bool
62 {
63 $random_filename = "test_" . random_int(10000, 99999);
64
65 $original_filename = $this->getStorageLocationBasePath() . "/" . $random_filename . '_orig';
66 $linked_filename = $this->getStorageLocationBasePath() . "/" . $random_filename . '_link';
67 $cleaner = function () use ($original_filename, $linked_filename) {
68 try {
69 $this->fs->delete($original_filename);
70 } catch (\Throwable $t) {
71 }
72 try {
73 $this->fs->delete($linked_filename);
74 } catch (\Throwable $t) {
75 }
76 };
77
78 try {
79 // remove existing files
80 $cleaner();
81
82 // create file
83 $this->fs->write($original_filename, 'data');
84 $stream = $this->fs->readStream($original_filename);
85
86 // determine absolute pathes
87 $original_absolute_path = $stream->getMetadata('uri');
88 $linked_absolute_path = dirname($original_absolute_path) . "/" . $random_filename . '_link';
89
90 // try linking and unlinking
92 $linking = @link($original_absolute_path, $linked_absolute_path);
94 $unlinking = @unlink($original_absolute_path);
95 $stream->close();
96 if ($linking && $unlinking && $this->fs->has($linked_filename)) {
97 $cleaner();
98
99 return true;
100 }
101 $cleaner();
102 } catch (\Throwable $t) {
103 return false;
104 }
105 return false;
106 }
107
112 {
113 return $this->id;
114 }
115
116 public function has(ResourceIdentification $identification) : bool
117 {
118 return $this->fs->has($this->getFullContainerPath($identification));
119 }
120
124 public function getStream(Revision $revision) : FileStream
125 {
126 return $this->fs->readStream($this->getRevisionPath($revision) . '/' . self::DATA);
127 }
128
129 public function storeUpload(UploadedFileRevision $revision) : bool
130 {
131 global $DIC;
132
133 $DIC->upload()->moveOneFileTo($revision->getUpload(), $this->getRevisionPath($revision), $this->location,
134 self::DATA);
135
136 return true;
137 }
138
142 public function storeStream(FileStreamRevision $revision) : bool
143 {
144 try {
145 if ($revision->keepOriginal()) {
146 $stream = $revision->getStream();
147 $this->fs->writeStream($this->getRevisionPath($revision) . '/' . self::DATA, $stream);
148 $stream->close();
149 } else {
150 $target = $revision->getStream()->getMetadata('uri');
151 if ($this->links_possible) {
152 $this->fs->createDir($this->getRevisionPath($revision));
153 link($target, $this->getAbsoluteRevisionPath($revision) . '/' . self::DATA);
154 unlink($target);
155 } else {
156 $this->fs->rename(LegacyPathHelper::createRelativePath($target),
157 $this->getRevisionPath($revision) . '/' . self::DATA);
158 }
159 $revision->getStream()->close();
160 }
161 } catch (\Throwable $t) {
162 return false;
163 }
164
165 return true;
166 }
167
168 public function cloneRevision(CloneRevision $revision) : bool
169 {
170 $stream = $this->getStream($revision->getRevisionToClone());
171 try {
172 $this->fs->writeStream($this->getRevisionPath($revision) . '/' . self::DATA, $stream);
173 $stream->close();
174 } catch (\Throwable $t) {
175 return false;
176 }
177
178 return true;
179 }
180
184 public function deleteRevision(Revision $revision) : void
185 {
186 try {
187 $this->fs->deleteDir($this->getRevisionPath($revision));
188 } catch (\Throwable $t) {
189
190 }
191
192 }
193
197 public function deleteResource(StorableResource $resource) : void
198 {
199 try {
200 $this->fs->deleteDir($this->getFullContainerPath($resource->getIdentification()));
201 } catch (\Throwable $t) {
202 }
203 try {
204 $this->cleanUpContainer($resource);
205 } catch (\Throwable $t) {
206 }
207
208 }
209
210 public function cleanUpContainer(StorableResource $resource) : void
211 {
212 $storage_path = $this->getStorageLocationBasePath();
213 $container_path = $this->getContainerPathWithoutBase($resource->getIdentification());
214 $first_level = strtok($container_path, "/");
215 if (!empty($first_level)) {
216 $full_first_level = $storage_path . '/' . $first_level;
217 $number_of_files = $this->fs->finder()->files()->in([$full_first_level])->count();
218 if ($number_of_files === 0) {
219 $this->fs->deleteDir($full_first_level);
220 }
221 }
222 }
223
224 public function getBasePath(ResourceIdentification $identification) : string
225 {
226 return $this->getFullContainerPath($identification);
227 }
228
229 public function getRevisionPath(Revision $revision) : string
230 {
231 return $this->getFullContainerPath($revision->getIdentification()) . '/' . $revision->getVersionNumber();
232 }
233
234 public function getFullContainerPath(ResourceIdentification $identification) : string
235 {
236 return $this->getStorageLocationBasePath() . '/' . $this->getContainerPathWithoutBase($identification);
237 }
238
239 public function getContainerPathWithoutBase(ResourceIdentification $identification) : string
240 {
241 return $this->path_generator->getPathFor($identification);
242 }
243
244 private function getAbsoluteRevisionPath(Revision $revision) : string
245 {
246 $str = rtrim(CLIENT_DATA_DIR, "/") . "/" . ltrim($this->getRevisionPath($revision), "/");
247 return $str;
248 }
249
250 public function movementImplementation() : string
251 {
252 return $this->links_possible ? 'link' : 'rename';
253 }
254
255}
An exception for terminatinating execution or to throw for unit testing.
Class LegacyPathHelper The legacy path helper provides convenient functions for the integration of th...
static createRelativePath(string $absolute_path)
Creates a relative path from an absolute path which starts with a valid storage location.
__construct(Filesystem $filesystem, int $location=Location::STORAGE, bool $determine_linking_possible=false)
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.
cleanUpContainer(StorableResource $resource)
This checks if there are empty directories in the filesystem which can be deleted.
const CLIENT_DATA_DIR
Definition: constants.php:44
global $DIC
Definition: goto.php:24
Interface Location.
Definition: Location.php:17
const STORAGE
The filesystem outside of the ilias web root.
Definition: Location.php:28
Interface Filesystem The filesystem interface provides the public interface for the Filesystem servic...
Definition: Filesystem.php:22
Interface FileStream The base interface for all filesystem streams.
Definition: FileStream.php:18
getStorageLocationBasePath()
This is the place in the filesystem where the containers (nested) get created.
Class FlySystemFileAccessTest \Provider\FlySystem @runTestsInSeparateProcesses @preserveGlobalState d...