[BUGFIX] Missing initialization of typo3/phar-stream-wrapper
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / IO / PharStreamWrapperInterceptorTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Functional\IO;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\PharStreamWrapper\Exception;
19 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
20
21 class PharStreamWrapperInterceptorTest extends FunctionalTestCase
22 {
23 /**
24 * @var array
25 */
26 protected $testExtensionsToLoad = [
27 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources'
28 ];
29
30 /**
31 * @var array
32 */
33 protected $pathsToLinkInTestInstance = [
34 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources/bundle.phar' => 'fileadmin/bundle.phar'
35 ];
36
37 protected function setUp()
38 {
39 parent::setUp();
40 if (!in_array('phar', stream_get_wrappers())) {
41 $this->markTestSkipped('Phar stream wrapper is not registered');
42 }
43 // PharStreamWrapper is not initialized here since it relies on being
44 // properly defined in \TYPO3\CMS\Core\Core\Bootstrap - thus, it tests
45 // are expected to fail in case PharStreamWrapper is not initialized
46 }
47
48 public function directoryActionAllowsInvocationDataProvider()
49 {
50 $allowedPath = 'typo3conf/ext/test_resources/bundle.phar';
51
52 return [
53 'root directory' => [
54 $allowedPath,
55 ['Classes', 'Resources']
56 ],
57 'Classes/Domain/Model directory' => [
58 $allowedPath . '/Classes/Domain/Model',
59 ['DemoModel.php']
60 ],
61 'Resources directory' => [
62 $allowedPath . '/Resources',
63 ['content.txt']
64 ],
65 ];
66 }
67
68 /**
69 * @param string $path
70 *
71 * @test
72 * @dataProvider directoryActionAllowsInvocationDataProvider
73 */
74 public function directoryOpenAllowsInvocation(string $path)
75 {
76 $path = $this->instancePath . '/' . $path;
77 $handle = opendir('phar://' . $path);
78 self::assertInternalType('resource', $handle);
79 }
80
81 /**
82 * @param string $path
83 * @param $expectation
84 *
85 * @test
86 * @dataProvider directoryActionAllowsInvocationDataProvider
87 */
88 public function directoryReadAllowsInvocation(string $path, array $expectation)
89 {
90 $path = $this->instancePath . '/' . $path;
91
92 $items = [];
93 $handle = opendir('phar://' . $path);
94 while (false !== $item = readdir($handle)) {
95 $items[] = $item;
96 }
97
98 self::assertSame($expectation, $items);
99 }
100
101 /**
102 * @param string $path
103 * @param $expectation
104 *
105 * @test
106 * @dataProvider directoryActionAllowsInvocationDataProvider
107 */
108 public function directoryCloseAllowsInvocation(string $path, array $expectation)
109 {
110 $path = $this->instancePath . '/' . $path;
111
112 $handle = opendir('phar://' . $path);
113 closedir($handle);
114
115 self::assertFalse(is_resource($handle));
116 }
117
118 public function directoryActionDeniesInvocationDataProvider()
119 {
120 $deniedPath = 'fileadmin/bundle.phar';
121
122 return [
123 'root directory' => [
124 $deniedPath,
125 ['Classes', 'Resources']
126 ],
127 'Classes/Domain/Model directory' => [
128 $deniedPath . '/Classes/Domain/Model',
129 ['DemoModel.php']
130 ],
131 'Resources directory' => [
132 $deniedPath . '/Resources',
133 ['content.txt']
134 ],
135 ];
136 }
137
138 /**
139 * @param string $path
140 *
141 * @test
142 * @dataProvider directoryActionDeniesInvocationDataProvider
143 */
144 public function directoryActionDeniesInvocation(string $path)
145 {
146 self::expectException(Exception::class);
147 self::expectExceptionCode(1530103998);
148
149 $path = $this->instancePath . '/' . $path;
150 opendir('phar://' . $path);
151 }
152
153 /**
154 * @return array
155 */
156 public function urlStatAllowsInvocationDataProvider(): array
157 {
158 $allowedPath = 'typo3conf/ext/test_resources/bundle.phar';
159
160 return [
161 'filesize base file' => [
162 'filesize',
163 $allowedPath,
164 0, // Phar base file always has zero size when accessed through phar://
165 ],
166 'filesize Resources/content.txt' => [
167 'filesize',
168 $allowedPath . '/Resources/content.txt',
169 21,
170 ],
171 'is_file base file' => [
172 'is_file',
173 $allowedPath,
174 false, // Phar base file is not a file when accessed through phar://
175 ],
176 'is_file Resources/content.txt' => [
177 'is_file',
178 $allowedPath . '/Resources/content.txt',
179 true,
180 ],
181 'is_dir base file' => [
182 'is_dir',
183 $allowedPath,
184 true, // Phar base file is a directory when accessed through phar://
185 ],
186 'is_dir Resources/content.txt' => [
187 'is_dir',
188 $allowedPath . '/Resources/content.txt',
189 false,
190 ],
191 'file_exists base file' => [
192 'file_exists',
193 $allowedPath,
194 true
195 ],
196 'file_exists Resources/content.txt' => [
197 'file_exists',
198 $allowedPath . '/Resources/content.txt',
199 true
200 ],
201 ];
202 }
203
204 /**
205 * @param string $functionName
206 * @param string $path
207 * @param mixed $expectation
208 *
209 * @test
210 * @dataProvider urlStatAllowsInvocationDataProvider
211 */
212 public function urlStatAllowsInvocation(string $functionName, string $path, $expectation)
213 {
214 $path = $this->instancePath . '/' . $path;
215
216 self::assertSame(
217 $expectation,
218 call_user_func($functionName, 'phar://' . $path)
219 );
220 }
221
222 /**
223 * @return array
224 */
225 public function urlStatDeniesInvocationDataProvider(): array
226 {
227 $deniedPath = 'fileadmin/bundle.phar';
228
229 return [
230 'filesize base file' => [
231 'filesize',
232 $deniedPath,
233 0, // Phar base file always has zero size when accessed through phar://
234 ],
235 'filesize Resources/content.txt' => [
236 'filesize',
237 $deniedPath . '/Resources/content.txt',
238 21,
239 ],
240 'is_file base file' => [
241 'is_file',
242 $deniedPath,
243 false, // Phar base file is not a file when accessed through phar://
244 ],
245 'is_file Resources/content.txt' => [
246 'is_file',
247 $deniedPath . '/Resources/content.txt',
248 true,
249 ],
250 'is_dir base file' => [
251 'is_dir',
252 $deniedPath,
253 true, // Phar base file is a directory when accessed through phar://
254 ],
255 'is_dir Resources/content.txt' => [
256 'is_dir',
257 $deniedPath . '/Resources/content.txt',
258 false,
259 ],
260 'file_exists base file' => [
261 'file_exists',
262 $deniedPath,
263 true
264 ],
265 'file_exists Resources/content.txt' => [
266 'file_exists',
267 $deniedPath . '/Resources/content.txt',
268 true
269 ],
270 ];
271 }
272
273 /**
274 * @param string $functionName
275 * @param string $path
276 * @param mixed $expectation
277 *
278 * @test
279 * @dataProvider urlStatDeniesInvocationDataProvider
280 */
281 public function urlStatDeniesInvocation(string $functionName, string $path)
282 {
283 self::expectException(Exception::class);
284 self::expectExceptionCode(1530103998);
285
286 $path = $this->instancePath . '/' . $path;
287 call_user_func($functionName, 'phar://' . $path);
288 }
289
290 /**
291 * @test
292 */
293 public function streamOpenAllowsInvocationForFileOpen()
294 {
295 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
296 $handle = fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
297 self::assertInternalType('resource', $handle);
298 }
299
300 /**
301 * @test
302 */
303 public function streamOpenAllowsInvocationForFileRead()
304 {
305 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
306 $handle = fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
307 $content = fread($handle, 1024);
308 self::assertSame('TYPO3 demo text file.', $content);
309 }
310
311 /**
312 * @test
313 */
314 public function streamOpenAllowsInvocationForFileEnd()
315 {
316 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
317 $handle = fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
318 fread($handle, 1024);
319 self::assertTrue(feof($handle));
320 }
321
322 /**
323 * @test
324 */
325 public function streamOpenAllowsInvocationForFileClose()
326 {
327 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
328 $handle = fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
329 fclose($handle);
330 self::assertFalse(is_resource($handle));
331 }
332
333 /**
334 * @test
335 */
336 public function streamOpenAllowsInvocationForFileGetContents()
337 {
338 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
339 $content = file_get_contents('phar://' . $allowedPath . '/Resources/content.txt');
340 self::assertSame('TYPO3 demo text file.', $content);
341 }
342
343 /**
344 * @test
345 */
346 public function streamOpenAllowsInvocationForInclude()
347 {
348 $allowedPath = $this->instancePath . '/typo3conf/ext/test_resources/bundle.phar';
349 include('phar://' . $allowedPath . '/Classes/Domain/Model/DemoModel.php');
350
351 self::assertTrue(
352 class_exists(
353 \TYPO3Demo\Demo\Domain\Model\DemoModel::class,
354 false
355 )
356 );
357 }
358
359 /**
360 * @test
361 */
362 public function streamOpenDeniesInvocationForFileOpen()
363 {
364 self::expectException(Exception::class);
365 self::expectExceptionCode(1530103998);
366
367 $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
368 fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
369 }
370
371 /**
372 * @test
373 */
374 public function streamOpenDeniesInvocationForFileGetContents()
375 {
376 self::expectException(Exception::class);
377 self::expectExceptionCode(1530103998);
378
379 $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
380 file_get_contents('phar://' . $allowedPath . '/Resources/content.txt');
381 }
382
383 /**
384 * @test
385 */
386 public function streamOpenDeniesInvocationForInclude()
387 {
388 self::expectException(Exception::class);
389 self::expectExceptionCode(1530103998);
390
391 $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
392 include('phar://' . $allowedPath . '/Classes/Domain/Model/DemoModel.php');
393 }
394 }