TYPO3 CMS  TYPO3_8-7
FileHandlingUtilityTest.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
17 
21 class FileHandlingUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
22 {
26  protected $fakedExtensions = [];
27 
35  protected function createFakeExtension($extkeyOnly = false)
36  {
37  $extKey = strtolower($this->getUniqueId('testing'));
38  $absExtPath = PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/';
39  $relPath = 'typo3temp/var/tests/ext-' . $extKey . '/';
40  $this->fakedExtensions[$extKey] = [
41  'siteRelPath' => $relPath,
42  'siteAbsPath' => $absExtPath
43  ];
44  if ($extkeyOnly === true) {
45  return $extKey;
46  }
48  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/ext-' . $extKey;
49  return $extKey;
50  }
51 
56  {
57  $extKey = $this->createFakeExtension();
58  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir'], [], '', false);
59  $fileHandlerMock->expects($this->once())
60  ->method('removeDirectory')
61  ->with(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
62  $fileHandlerMock->expects($this->any())
63  ->method('getExtensionDir')
64  ->willReturn(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
65  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
66  }
67 
72  {
73  return [
74  ['../../'],
75  ['/foo/bar'],
76  ['foo//bar'],
77  ['foo/bar' . chr(0)],
78  ];
79  }
80 
86  public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath)
87  {
88  $this->expectException(ExtensionManagerException::class);
89  $this->expectExceptionCode(1350742864);
90  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy'], []);
91  $fileHandlerMock->_call('getAbsolutePath', $invalidRelativePath);
92  }
93 
98  {
99  return [
100  ['foo/../bar', PATH_site . 'bar'],
101  ['bas', PATH_site . 'bas'],
102  ];
103  }
104 
111  public function getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath)
112  {
113  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
114  $this->assertSame($expectedAbsolutePath, $fileHandlerMock->_call('getAbsolutePath', $validRelativePath));
115  }
116 
121  {
122  $extKey = $this->createFakeExtension();
123  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir']);
124  $fileHandlerMock->expects($this->once())
125  ->method('addDirectory')
126  ->with(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
127  $fileHandlerMock->expects($this->any())
128  ->method('getExtensionDir')
129  ->willReturn(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
130  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
131  }
132 
137  {
138  $this->expectException(ExtensionManagerException::class);
139  $this->expectExceptionCode(1337280417);
140  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
141  $languageServiceMock = $this->getMockBuilder(\TYPO3\CMS\Lang\LanguageService::class)->getMock();
142  $fileHandlerMock->_set('languageService', $languageServiceMock);
143  $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
144  }
145 
149  public function addDirectoryAddsDirectory()
150  {
151  $extDirPath = PATH_site . '/typo3temp/var/tests/' . $this->getUniqueId('test-extensions-');
152  $this->testFilesToDelete[] = $extDirPath;
153  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
154  $fileHandlerMock->_call('addDirectory', $extDirPath);
155  $this->assertTrue(is_dir($extDirPath));
156  }
157 
162  {
163  $extDirPath = PATH_site . '/typo3temp/var/tests/' . $this->getUniqueId('test-extensions-');
164  @mkdir($extDirPath);
165  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
166  $fileHandlerMock->_call('removeDirectory', $extDirPath);
167  $this->assertFalse(is_dir($extDirPath));
168  }
169 
174  {
175  $absoluteSymlinkPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_symlink_');
176  $absoluteFilePath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_file_');
177  touch($absoluteFilePath);
178  $this->testFilesToDelete[] = $absoluteFilePath;
179  symlink($absoluteFilePath, $absoluteSymlinkPath);
180  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
181  $fileHandler->removeDirectory($absoluteSymlinkPath);
182  $this->assertFalse(is_link($absoluteSymlinkPath));
183  }
184 
189  {
190  $absoluteSymlinkPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_symlink_');
191  $absoluteDirectoryPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_dir_') . '/';
192  $relativeFilePath = $this->getUniqueId('test_file_');
193 
194  mkdir($absoluteDirectoryPath);
195  touch($absoluteDirectoryPath . $relativeFilePath);
196 
197  $this->testFilesToDelete[] = $absoluteDirectoryPath . $relativeFilePath;
198  $this->testFilesToDelete[] = $absoluteDirectoryPath;
199 
200  symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
201 
202  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
203  $fileHandler->removeDirectory($absoluteSymlinkPath);
204  $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
205  }
206 
211  {
212  $extensionData = [
213  'extKey' => 'test'
214  ];
215  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
216  'makeAndClearExtensionDir',
217  'writeEmConfToFile',
218  'extractFilesArrayFromExtensionData',
219  'extractDirectoriesFromExtensionData',
220  'createDirectoriesForExtensionFiles',
221  'writeExtensionFiles',
222  'reloadPackageInformation',
223  ]);
224  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue([]));
225  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue([]));
226  $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
227  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
228  }
229 
234  {
235  $extensionData = [
236  'extKey' => 'test'
237  ];
238  $files = [
239  'ChangeLog' => [
240  'name' => 'ChangeLog',
241  'size' => 4559,
242  'mtime' => 1219448527,
243  'is_executable' => false,
244  'content' => 'some content to write'
245  ],
246  'doc/' => [
247  'name' => 'doc/',
248  'size' => 0,
249  'mtime' => 1219448527,
250  'is_executable' => false,
251  'content' => ''
252  ],
253  'doc/ChangeLog' => [
254  'name' => 'ChangeLog',
255  'size' => 4559,
256  'mtime' => 1219448527,
257  'is_executable' => false,
258  'content' => 'some content to write'
259  ],
260  ];
261  $cleanedFiles = [
262  'ChangeLog' => [
263  'name' => 'ChangeLog',
264  'size' => 4559,
265  'mtime' => 1219448527,
266  'is_executable' => false,
267  'content' => 'some content to write'
268  ],
269  'doc/ChangeLog' => [
270  'name' => 'ChangeLog',
271  'size' => 4559,
272  'mtime' => 1219448527,
273  'is_executable' => false,
274  'content' => 'some content to write'
275  ],
276  ];
277  $directories = [
278  'doc/',
279  'mod/doc/'
280  ];
281 
282  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
283  'makeAndClearExtensionDir',
284  'writeEmConfToFile',
285  'extractFilesArrayFromExtensionData',
286  'extractDirectoriesFromExtensionData',
287  'createDirectoriesForExtensionFiles',
288  'writeExtensionFiles',
289  'reloadPackageInformation',
290  ]);
291  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue($files));
292  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue($directories));
293  $fileHandlerMock->expects($this->once())->method('createDirectoriesForExtensionFiles')->with($directories);
294  $fileHandlerMock->expects($this->once())->method('writeExtensionFiles')->with($cleanedFiles);
295  $fileHandlerMock->expects($this->once())->method('reloadPackageInformation')->with('test');
296  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
297  }
298 
303  {
304  $extensionData = [
305  'key' => 'test',
306  'FILES' => [
307  'filename1' => 'dummycontent',
308  'filename2' => 'dummycontent2'
309  ]
310  ];
311  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
312  $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
313  $this->assertArrayHasKey('filename1', $extractedFiles);
314  $this->assertArrayHasKey('filename2', $extractedFiles);
315  }
316 
321  {
322  $files = [
323  'ChangeLog' => [
324  'name' => 'ChangeLog',
325  'size' => 4559,
326  'mtime' => 1219448527,
327  'is_executable' => false,
328  'content' => 'some content to write'
329  ],
330  'README' => [
331  'name' => 'README',
332  'size' => 4566,
333  'mtime' => 1219448533,
334  'is_executable' => false,
335  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
336  ]
337  ];
338  $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
339  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
340  $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
341  $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
342  }
343 
348  {
349  $files = [
350  'ChangeLog' => [
351  'name' => 'ChangeLog',
352  'size' => 4559,
353  'mtime' => 1219448527,
354  'is_executable' => false,
355  'content' => 'some content to write'
356  ],
357  'doc/' => [
358  'name' => 'doc/',
359  'size' => 0,
360  'mtime' => 1219448527,
361  'is_executable' => false,
362  'content' => ''
363  ],
364  'doc/ChangeLog' => [
365  'name' => 'ChangeLog',
366  'size' => 4559,
367  'mtime' => 1219448527,
368  'is_executable' => false,
369  'content' => 'some content to write'
370  ],
371  'doc/README' => [
372  'name' => 'README',
373  'size' => 4566,
374  'mtime' => 1219448533,
375  'is_executable' => false,
376  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
377  ],
378  'mod/doc/README' => [
379  'name' => 'README',
380  'size' => 4566,
381  'mtime' => 1219448533,
382  'is_executable' => false,
383  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
384  ]
385  ];
386  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
387  $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
388  $expected = [
389  'doc/',
390  'mod/doc/'
391  ];
392  $this->assertSame($expected, array_values($extractedDirectories));
393  }
394 
399  {
400  $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
401  $directories = [
402  'doc/',
403  'mod/doc/'
404  ];
405  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
406  $this->assertFalse(is_dir($rootPath . 'doc/'));
407  $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
408  $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
409  $this->assertTrue(is_dir($rootPath . 'doc/'));
410  $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
411  }
412 
416  public function writeEmConfWritesEmConfFile()
417  {
418  $extKey = $this->createFakeExtension();
419  $extensionData = [
420  'extKey' => $extKey,
421  'EM_CONF' => [
422  'title' => 'Plugin cache engine',
423  'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
424  'category' => 'Frontend',
425  ]
426  ];
427  $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
428  $emConfUtilityMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility::class, ['constructEmConf']);
429  $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], true)));
430  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
431  $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
432  $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
433  $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
434  }
435 
439  protected function getPreparedFileHandlingMockForDirectoryCreationTests()
440  {
442  $fileHandlerMock = $this->getMockBuilder(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class)
443  ->setMethods(['createNestedDirectory', 'getAbsolutePath', 'directoryExists'])
444  ->getMock();
445  $fileHandlerMock->expects($this->any())
446  ->method('getAbsolutePath')
447  ->will($this->returnArgument(0));
448  return $fileHandlerMock;
449  }
450 
455  {
456  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
457  $fileHandlerMock->expects($this->never())
458  ->method('createNestedDirectory');
459  $fileHandlerMock->ensureConfiguredDirectoriesExist(
460  [
461  'key' => 'foo_bar',
462  'uploadfolder' => 0,
463  ]
464  );
465  }
466 
471  {
472  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
473  $fileHandlerMock->expects($this->never())
474  ->method('createNestedDirectory');
475  $fileHandlerMock->ensureConfiguredDirectoriesExist(
476  [
477  'key' => 'foo_bar',
478  'createDirs' => '',
479  ]
480  );
481  }
482 
487  {
488  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
489  $fileHandlerMock->expects($this->once())
490  ->method('createNestedDirectory')
491  ->with('uploads/tx_foobar/');
492  $fileHandlerMock->ensureConfiguredDirectoriesExist(
493  [
494  'key' => 'foo_bar',
495  'uploadfolder' => 1,
496  ]
497  );
498  }
499 
504  {
505  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
506  $fileHandlerMock->expects($this->exactly(2))
507  ->method('createNestedDirectory')
508  ->will(
509  $this->returnCallback(function ($path) {
510  if (!in_array($path, ['foo/bar', 'baz/foo'])) {
511  throw new \Exception('Path "' . $path . '" is not expected to be created', 1476108500);
512  }
513  })
514  );
515  $fileHandlerMock->ensureConfiguredDirectoriesExist(
516  [
517  'key' => 'foo_bar',
518  'createDirs' => 'foo/bar, baz/foo',
519  ]
520  );
521  }
522 
527  {
528  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
529  $fileHandlerMock->expects($this->exactly(3))
530  ->method('directoryExists')
531  ->will($this->returnValue(true));
532  $fileHandlerMock->expects($this->never())
533  ->method('createNestedDirectory');
534  $fileHandlerMock->ensureConfiguredDirectoriesExist(
535  [
536  'key' => 'foo_bar',
537  'uploadfolder' => 1,
538  'createDirs' => 'foo/bar, baz/foo',
539  ]
540  );
541  }
542 
549  {
550  // 42 second of first day in 1970 - used to have achieve stable file names
551  $GLOBALS['EXEC_TIME'] = 42;
552 
553  // Create extension for testing:
554  $extKey = $this->createFakeExtension();
555  $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
556 
557  // Build mocked fileHandlingUtility:
558  $fileHandlerMock = $this->getAccessibleMock(
559  \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class,
560  ['getAbsoluteExtensionPath', 'getExtensionVersion']
561  );
562  $fileHandlerMock->expects($this->any())
563  ->method('getAbsoluteExtensionPath')
564  ->will($this->returnValue($extensionRoot));
565  $fileHandlerMock->expects($this->any())
566  ->method('getExtensionVersion')
567  ->will($this->returnValue('0.0.0'));
568 
569  // Add files and directories to extension:
570  touch($extensionRoot . 'emptyFile.txt');
571  file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
572  touch($extensionRoot . '.hiddenFile');
573  mkdir($extensionRoot . 'emptyDir');
574  mkdir($extensionRoot . 'notEmptyDir');
575  touch($extensionRoot . 'notEmptyDir/file.txt');
576 
577  // Create zip-file from extension
578  $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
579 
580  $expectedFilename = PATH_site . 'typo3temp/var/ExtensionManager/' . $extKey . '_0.0.0_' . date('YmdHi', 42) . '.zip';
581  $this->testFilesToDelete[] = $filename;
582  $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
583 
584  // File was created
585  $this->assertTrue(file_exists($filename), 'Zip file not created');
586 
587  // Read archive and check its contents
588  $archive = new \ZipArchive();
589  $this->assertTrue($archive->open($filename), 'Unable to open archive');
590  $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
591  $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
592  $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
593  $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
594  $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
595  $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
596 
597  // Check that the archive has no additional content
598  $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
599  }
600 }
getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']