TYPO3 CMS  TYPO3_7-6
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  */
16 
22 {
26  protected $fakedExtensions = [];
27 
35  protected function createFakeExtension($extkeyOnly = false)
36  {
37  $extKey = strtolower($this->getUniqueId('testing'));
38  $absExtPath = PATH_site . 'typo3temp/ext-' . $extKey . '/';
39  $relPath = 'typo3temp/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/ext-' . $extKey;
49  return $extKey;
50  }
51 
57  {
58  $extKey = $this->createFakeExtension();
59  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir'], [], '', false);
60  $fileHandlerMock->expects($this->once())
61  ->method('removeDirectory')
62  ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
63  $fileHandlerMock->expects($this->any())
64  ->method('getExtensionDir')
65  ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
66  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
67  }
68 
73  {
74  return [
75  ['../../'],
76  ['/foo/bar'],
77  ['foo//bar'],
78  ['foo/bar' . chr(0)],
79  ];
80  }
81 
88  public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath)
89  {
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 
122  {
123  $extKey = $this->createFakeExtension();
124  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir']);
125  $fileHandlerMock->expects($this->once())
126  ->method('addDirectory')
127  ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
128  $fileHandlerMock->expects($this->any())
129  ->method('getExtensionDir')
130  ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
131  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
132  }
133 
140  {
141  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
142  $languageServiceMock = $this->getMock(\TYPO3\CMS\Lang\LanguageService::class);
143  $fileHandlerMock->_set('languageService', $languageServiceMock);
144  $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
145  }
146 
151  public function addDirectoryAddsDirectory()
152  {
153  $extDirPath = PATH_site . '/typo3temp/' . $this->getUniqueId('test-extensions-');
154  $this->testFilesToDelete[] = $extDirPath;
155  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
156  $fileHandlerMock->_call('addDirectory', $extDirPath);
157  $this->assertTrue(is_dir($extDirPath));
158  }
159 
165  {
166  $extDirPath = PATH_site . '/typo3temp/' . $this->getUniqueId('test-extensions-');
167  @mkdir($extDirPath);
168  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
169  $fileHandlerMock->_call('removeDirectory', $extDirPath);
170  $this->assertFalse(is_dir($extDirPath));
171  }
172 
178  {
179  $absoluteSymlinkPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_symlink_');
180  $absoluteFilePath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_file_');
181  touch($absoluteFilePath);
182  $this->testFilesToDelete[] = $absoluteFilePath;
183  symlink($absoluteFilePath, $absoluteSymlinkPath);
184  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
185  $fileHandler->removeDirectory($absoluteSymlinkPath);
186  $this->assertFalse(is_link($absoluteSymlinkPath));
187  }
188 
194  {
195  $absoluteSymlinkPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_symlink_');
196  $absoluteDirectoryPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_dir_') . '/';
197  $relativeFilePath = $this->getUniqueId('test_file_');
198 
199  mkdir($absoluteDirectoryPath);
200  touch($absoluteDirectoryPath . $relativeFilePath);
201 
202  $this->testFilesToDelete[] = $absoluteDirectoryPath . $relativeFilePath;
203  $this->testFilesToDelete[] = $absoluteDirectoryPath;
204 
205  symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
206 
207  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
208  $fileHandler->removeDirectory($absoluteSymlinkPath);
209  $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
210  }
211 
217  {
218  $extensionData = [
219  'extKey' => 'test'
220  ];
221  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
222  'makeAndClearExtensionDir',
223  'writeEmConfToFile',
224  'extractFilesArrayFromExtensionData',
225  'extractDirectoriesFromExtensionData',
226  'createDirectoriesForExtensionFiles',
227  'writeExtensionFiles',
228  'reloadPackageInformation',
229  ]);
230  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue([]));
231  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue([]));
232  $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
233  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
234  }
235 
241  {
242  $extensionData = [
243  'extKey' => 'test'
244  ];
245  $files = [
246  'ChangeLog' => [
247  'name' => 'ChangeLog',
248  'size' => 4559,
249  'mtime' => 1219448527,
250  'is_executable' => false,
251  'content' => 'some content to write'
252  ],
253  'doc/' => [
254  'name' => 'doc/',
255  'size' => 0,
256  'mtime' => 1219448527,
257  'is_executable' => false,
258  'content' => ''
259  ],
260  'doc/ChangeLog' => [
261  'name' => 'ChangeLog',
262  'size' => 4559,
263  'mtime' => 1219448527,
264  'is_executable' => false,
265  'content' => 'some content to write'
266  ],
267  ];
268  $cleanedFiles = [
269  'ChangeLog' => [
270  'name' => 'ChangeLog',
271  'size' => 4559,
272  'mtime' => 1219448527,
273  'is_executable' => false,
274  'content' => 'some content to write'
275  ],
276  'doc/ChangeLog' => [
277  'name' => 'ChangeLog',
278  'size' => 4559,
279  'mtime' => 1219448527,
280  'is_executable' => false,
281  'content' => 'some content to write'
282  ],
283  ];
284  $directories = [
285  'doc/',
286  'mod/doc/'
287  ];
288 
289  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
290  'makeAndClearExtensionDir',
291  'writeEmConfToFile',
292  'extractFilesArrayFromExtensionData',
293  'extractDirectoriesFromExtensionData',
294  'createDirectoriesForExtensionFiles',
295  'writeExtensionFiles',
296  'reloadPackageInformation',
297  ]);
298  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue($files));
299  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue($directories));
300  $fileHandlerMock->expects($this->once())->method('createDirectoriesForExtensionFiles')->with($directories);
301  $fileHandlerMock->expects($this->once())->method('writeExtensionFiles')->with($cleanedFiles);
302  $fileHandlerMock->expects($this->once())->method('reloadPackageInformation')->with('test');
303  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
304  }
305 
311  {
312  $extensionData = [
313  'key' => 'test',
314  'FILES' => [
315  'filename1' => 'dummycontent',
316  'filename2' => 'dummycontent2'
317  ]
318  ];
319  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
320  $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
321  $this->assertArrayHasKey('filename1', $extractedFiles);
322  $this->assertArrayHasKey('filename2', $extractedFiles);
323  }
324 
330  {
331  $files = [
332  'ChangeLog' => [
333  'name' => 'ChangeLog',
334  'size' => 4559,
335  'mtime' => 1219448527,
336  'is_executable' => false,
337  'content' => 'some content to write'
338  ],
339  'README' => [
340  'name' => 'README',
341  'size' => 4566,
342  'mtime' => 1219448533,
343  'is_executable' => false,
344  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
345  ]
346  ];
347  $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
348  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
349  $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
350  $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
351  }
352 
358  {
359  $files = [
360  'ChangeLog' => [
361  'name' => 'ChangeLog',
362  'size' => 4559,
363  'mtime' => 1219448527,
364  'is_executable' => false,
365  'content' => 'some content to write'
366  ],
367  'doc/' => [
368  'name' => 'doc/',
369  'size' => 0,
370  'mtime' => 1219448527,
371  'is_executable' => false,
372  'content' => ''
373  ],
374  'doc/ChangeLog' => [
375  'name' => 'ChangeLog',
376  'size' => 4559,
377  'mtime' => 1219448527,
378  'is_executable' => false,
379  'content' => 'some content to write'
380  ],
381  'doc/README' => [
382  'name' => 'README',
383  'size' => 4566,
384  'mtime' => 1219448533,
385  'is_executable' => false,
386  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
387  ],
388  'mod/doc/README' => [
389  'name' => 'README',
390  'size' => 4566,
391  'mtime' => 1219448533,
392  'is_executable' => false,
393  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
394  ]
395  ];
396  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
397  $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
398  $expected = [
399  'doc/',
400  'mod/doc/'
401  ];
402  $this->assertSame($expected, array_values($extractedDirectories));
403  }
404 
410  {
411  $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
412  $directories = [
413  'doc/',
414  'mod/doc/'
415  ];
416  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
417  $this->assertFalse(is_dir($rootPath . 'doc/'));
418  $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
419  $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
420  $this->assertTrue(is_dir($rootPath . 'doc/'));
421  $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
422  }
423 
428  public function writeEmConfWritesEmConfFile()
429  {
430  $extKey = $this->createFakeExtension();
431  $extensionData = [
432  'extKey' => $extKey,
433  'EM_CONF' => [
434  'title' => 'Plugin cache engine',
435  'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
436  'category' => 'Frontend',
437  ]
438  ];
439  $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
440  $emConfUtilityMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility::class, ['constructEmConf']);
441  $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], true)));
442  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
443  $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
444  $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
445  $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
446  }
447 
451  protected function getPreparedFileHandlingMockForDirectoryCreationTests()
452  {
454  $fileHandlerMock = $this->getMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['createNestedDirectory', 'getAbsolutePath', 'directoryExists']);
455  $fileHandlerMock->expects($this->any())
456  ->method('getAbsolutePath')
457  ->will($this->returnArgument(0));
458  return $fileHandlerMock;
459  }
460 
465  {
466  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
467  $fileHandlerMock->expects($this->never())
468  ->method('createNestedDirectory');
469  $fileHandlerMock->ensureConfiguredDirectoriesExist([
470  'key' => 'foo_bar',
471  'uploadfolder' => 0,
472  ]
473  );
474  }
475 
480  {
481  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
482  $fileHandlerMock->expects($this->never())
483  ->method('createNestedDirectory');
484  $fileHandlerMock->ensureConfiguredDirectoriesExist([
485  'key' => 'foo_bar',
486  'createDirs' => '',
487  ]
488  );
489  }
490 
495  {
496  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
497  $fileHandlerMock->expects($this->once())
498  ->method('createNestedDirectory')
499  ->with('uploads/tx_foobar/');
500  $fileHandlerMock->ensureConfiguredDirectoriesExist([
501  'key' => 'foo_bar',
502  'uploadfolder' => 1,
503  ]
504  );
505  }
506 
511  {
512  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
513  $fileHandlerMock->expects($this->exactly(2))
514  ->method('createNestedDirectory')
515  ->will($this->returnCallback(function ($path) {
516  if (!in_array($path, ['foo/bar', 'baz/foo'])) {
517  throw new \Exception('Path "' . $path . '" is not expected to be created');
518  }
519  })
520  );
521  $fileHandlerMock->ensureConfiguredDirectoriesExist([
522  'key' => 'foo_bar',
523  'createDirs' => 'foo/bar, baz/foo',
524  ]
525  );
526  }
527 
532  {
533  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
534  $fileHandlerMock->expects($this->exactly(3))
535  ->method('directoryExists')
536  ->will($this->returnValue(true));
537  $fileHandlerMock->expects($this->never())
538  ->method('createNestedDirectory');
539  $fileHandlerMock->ensureConfiguredDirectoriesExist([
540  'key' => 'foo_bar',
541  'uploadfolder' => 1,
542  'createDirs' => 'foo/bar, baz/foo',
543  ]
544  );
545  }
546 
553  {
554  // 42 second of first day in 1970 - used to have achieve stable file names
555  $GLOBALS['EXEC_TIME'] = 42;
556 
557  // Create extension for testing:
558  $extKey = $this->createFakeExtension();
559  $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
560 
561  // Build mocked fileHandlingUtility:
562  $fileHandlerMock = $this->getAccessibleMock(
563  \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class,
564  ['getAbsoluteExtensionPath', 'getExtensionVersion']
565  );
566  $fileHandlerMock->expects($this->any())
567  ->method('getAbsoluteExtensionPath')
568  ->will($this->returnValue($extensionRoot));
569  $fileHandlerMock->expects($this->any())
570  ->method('getExtensionVersion')
571  ->will($this->returnValue('0.0.0'));
572 
573  // Add files and directories to extension:
574  touch($extensionRoot . 'emptyFile.txt');
575  file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
576  touch($extensionRoot . '.hiddenFile');
577  mkdir($extensionRoot . 'emptyDir');
578  mkdir($extensionRoot . 'notEmptyDir');
579  touch($extensionRoot . 'notEmptyDir/file.txt');
580 
581  // Create zip-file from extension
582  $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
583 
584  $expectedFilename = PATH_site . 'typo3temp/ExtensionManager/' . $extKey . '_0.0.0_' . date('YmdHi', 42) . '.zip';
585  $this->testFilesToDelete[] = $filename;
586  $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
587 
588  // File was created
589  $this->assertTrue(file_exists($filename), 'Zip file not created');
590 
591  // Read archive and check its contents
592  $archive = new \ZipArchive();
593  $this->assertTrue($archive->open($filename), 'Unable to open archive');
594  $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
595  $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
596  $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
597  $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
598  $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
599  $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
600 
601  // Check that the archive has no additional content
602  $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
603  }
604 }
getAccessibleMock( $originalClassName, $methods=[], array $arguments=[], $mockClassName='', $callOriginalConstructor=true, $callOriginalClone=true, $callAutoload=true)
getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']