‪TYPO3CMS  ‪main
PackageManagerTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use PHPUnit\Framework\MockObject\MockObject;
31 use TYPO3\CMS\Core\Package\PackageManager;
34 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
35 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
36 
37 final class PackageManagerTest extends UnitTestCase
38 {
39  protected PackageManager&MockObject&AccessibleObjectInterface $packageManager;
40 
41  protected string $testRoot;
42 
46  protected function setUp(): void
47  {
48  parent::setUp();
49 
50  $this->testRoot = ‪Environment::getVarPath() . '/tests/PackageManager/';
51  $this->testFilesToDelete[] = $this->testRoot;
52 
53  $mockCache = $this->createMock(PhpFrontend::class);
54  $mockCacheBackend = $this->createMock(SimpleFileBackend::class);
55  $mockCache->method('has')->willReturn(false);
56  $mockCache->method('set')->willReturn(true);
57  $mockCache->method('getBackend')->willReturn($mockCacheBackend);
58  $mockCacheBackend->method('getCacheDirectory')->willReturn($this->testRoot . 'Cache');
59  $this->packageManager = $this->getAccessibleMock(
60  PackageManager::class,
61  ['sortAndSavePackageStates', 'sortActivePackagesByDependencies', 'registerTransientClassLoadingInformationForPackage'],
62  [new DependencyOrderingService()]
63  );
64 
65  if (!is_dir($this->testRoot . 'Packages/Application')) {
66  mkdir($this->testRoot . 'Packages/Application', 0700, true);
67  }
68  if (!is_dir($this->testRoot . 'Configuration')) {
69  mkdir($this->testRoot . 'Configuration');
70  }
71  file_put_contents($this->testRoot . 'Configuration/PackageStates.php', "<?php return array ('packages' => array(), 'version' => 5); ");
72 
73  $composerNameToPackageKeyMap = [
74  'typo3/flow' => 'TYPO3.Flow',
75  ];
76 
77  $this->packageManager->setPackageCache(new PackageStatesPackageCache($this->testRoot . 'Configuration/PackageStates.php', $mockCache));
78  $this->packageManager->_set('composerNameToPackageKeyMap', $composerNameToPackageKeyMap);
79  $this->packageManager->_set('packagesBasePath', $this->testRoot . 'Packages/');
80  $this->packageManager->_set('packageStatesPathAndFilename', $this->testRoot . 'Configuration/PackageStates.php');
81  }
82 
83  protected function createPackage(string $packageKey): Package
84  {
85  $packagePath = $this->testRoot . 'Packages/Application/' . $packageKey . '/';
86  if (!is_dir($packagePath)) {
87  mkdir($packagePath, 0770, true);
88  }
89  file_put_contents($packagePath . 'ext_emconf.php', '<?php' . LF . '$EM_CONF[$_EXTKEY] = [];');
90  file_put_contents($packagePath . 'composer.json', '{}');
91  $package = new Package($this->packageManager, $packageKey, $packagePath);
92  $this->packageManager->registerPackage($package);
93  $this->packageManager->activatePackage($packageKey);
94 
95  return $package;
96  }
97 
98  #[Test]
99  public function getPackageReturnsTheSpecifiedPackage(): void
100  {
101  $this->createPackage('TYPO3.MyPackage');
102  $package = $this->packageManager->getPackage('TYPO3.MyPackage');
103 
104  self::assertInstanceOf(Package::class, $package, 'The result of getPackage() was no valid package object.');
105  }
106 
107  #[Test]
108  public function getPackageThrowsExceptionOnUnknownPackage(): void
109  {
110  $this->expectException(UnknownPackageException::class);
111  $this->expectExceptionCode(1166546734);
112 
113  $this->packageManager->getPackage('PrettyUnlikelyThatThisPackageExists');
114  }
115 
116  #[Test]
117  public function scanAvailablePackagesTraversesThePackagesDirectoryAndRegistersPackagesItFinds(): void
118  {
119  $expectedPackageKeys = [
120  ‪StringUtility::getUniqueId('TYPO3.CMS'),
121  ‪StringUtility::getUniqueId('TYPO3.CMS.Test'),
122  ‪StringUtility::getUniqueId('TYPO3.YetAnotherTestPackage'),
123  ‪StringUtility::getUniqueId('Lolli.Pop.NothingElse'),
124  ];
125 
126  foreach ($expectedPackageKeys as $packageKey) {
127  $packagePath = $this->testRoot . 'Packages/Application/' . $packageKey . '/';
128 
129  mkdir($packagePath, 0770, true);
130  file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-test"}');
131  }
132 
133  $packageManager = $this->getAccessibleMock(PackageManager::class, ['sortAndSavePackageStates'], [new DependencyOrderingService()]);
134  $packageManager->_set('packagesBasePath', $this->testRoot . 'Packages/');
135  $packageManager->_set('packageStatesPathAndFilename', $this->testRoot . 'Configuration/PackageStates.php');
136 
137  $packageManager->_set('packages', []);
138  $packageManager->_call('scanAvailablePackages');
139 
140  $packageStates = require $this->testRoot . 'Configuration/PackageStates.php';
141  $actualPackageKeys = array_keys($packageStates['packages']);
142  self::assertEquals(sort($expectedPackageKeys), sort($actualPackageKeys));
143  }
144 
145  #[Test]
146  public function scanAvailablePackagesKeepsExistingPackageConfiguration(): void
147  {
148  $expectedPackageKeys = [
149  ‪StringUtility::getUniqueId('TYPO3.CMS'),
150  ‪StringUtility::getUniqueId('TYPO3.CMS.Test'),
151  ‪StringUtility::getUniqueId('TYPO3.YetAnotherTestPackage'),
152  ‪StringUtility::getUniqueId('Lolli.Pop.NothingElse'),
153  ];
154 
155  $packagePaths = [];
156  foreach ($expectedPackageKeys as $packageKey) {
157  $packagePath = $this->testRoot . 'Packages/Application/' . $packageKey . '/';
158  $packagePaths[] = $packagePath;
159 
160  mkdir($packagePath, 0770, true);
161  file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-cms-test"}');
162  file_put_contents($packagePath . 'ext_emconf.php', '<?php' . LF . '$EM_CONF[$_EXTKEY] = [];');
163  }
164 
165  $packageManager = $this->getAccessibleMock(PackageManager::class, null, [new DependencyOrderingService()]);
166  $packageManager->_set('packagesBasePaths', $packagePaths);
167  $packageManager->_set('packagesBasePath', $this->testRoot . 'Packages/');
168  $packageManager->_set('packageStatesPathAndFilename', $this->testRoot . 'Configuration/PackageStates.php');
169  $mockCache = $this->getMockBuilder(PhpFrontend::class)->disableOriginalConstructor()->getMock();
170  $packageManager->_set('packageCache', new PackageStatesPackageCache($this->testRoot . 'Configuration/PackageStates.php', $mockCache));
171 
172  $packageKey = $expectedPackageKeys[0];
173  $packageManager->_set('packageStatesConfiguration', [
174  'packages' => [
175  $packageKey => [
176  'packagePath' => 'Application/' . $packageKey . '/',
177  ],
178  ],
179  'version' => 5,
180  ]);
181  $packageManager->_call('scanAvailablePackages');
182  $packageManager->_call('sortAndSavePackageStates');
183 
184  $packageStates = require $this->testRoot . 'Configuration/PackageStates.php';
185  self::assertEquals('Application/' . $packageKey . '/', $packageStates['packages'][$packageKey]['packagePath']);
186  }
187 
188  #[Test]
189  public function extractPackageKeyFromPackagePathFindsPackageKey(): void
190  {
191  $this->createPackage('typo3/my-package');
192  $resolvedPackage = $this->packageManager->extractPackageKeyFromPackagePath('EXT:typo3/my-package/path/to/file');
193  self::assertSame('typo3/my-package', $resolvedPackage);
194  }
195 
196  #[Test]
197  public function extractPackageKeyFromPackagePathThrowsExceptionOnNonPackagePaths(): void
198  {
199  $this->expectException(UnknownPackageException::class);
200  $this->expectExceptionCode(1631630764);
201 
202  $this->packageManager->extractPackageKeyFromPackagePath($this->testRoot . 'Packages/Application/InvalidPackage/');
203  }
204 
205  #[Test]
206  public function extractPackageKeyFromPackagePathThrowsExceptionOnInvalidPackagePaths(): void
207  {
208  $this->expectException(UnknownPackagePathException::class);
209  $this->expectExceptionCode(1631630087);
210 
211  $this->packageManager->extractPackageKeyFromPackagePath('EXT:typo3/my-package/path/to/file');
212  }
213 
214  #[Test]
215  public function packageStatesConfigurationContainsRelativePaths(): void
216  {
217  $packageKeys = [
218  ‪StringUtility::getUniqueId('Lolli.Pop.NothingElse'),
219  ‪StringUtility::getUniqueId('TYPO3.Package'),
220  ‪StringUtility::getUniqueId('TYPO3.YetAnotherTestPackage'),
221  ];
222 
223  $packagePaths = [];
224  foreach ($packageKeys as $packageKey) {
225  $packagePath = $this->testRoot . 'Packages/Application/' . $packageKey . '/';
226 
227  mkdir($packagePath, 0770, true);
228  file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "typo3-cms-test"}');
229  file_put_contents($packagePath . 'ext_emconf.php', '<?php' . LF . '$EM_CONF[$_EXTKEY] = [];');
230  $packagePaths[] = $packagePath;
231  }
232 
233  $packageManager = $this->getAccessibleMock(PackageManager::class, ['sortAndSavePackageStates', 'registerTransientClassLoadingInformationForPackage'], [new DependencyOrderingService()]);
234  $packageManager->_set('packagesBasePaths', $packagePaths);
235  $packageManager->_set('packagesBasePath', $this->testRoot . 'Packages/');
236  $packageManager->_set('packageStatesPathAndFilename', $this->testRoot . 'Configuration/PackageStates.php');
237 
238  $packageManager->_set('packages', []);
239  $packageManager->_call('scanAvailablePackages');
240 
241  $expectedPackageStatesConfiguration = [];
242  foreach ($packageKeys as $packageKey) {
243  $expectedPackageStatesConfiguration[$packageKey] = [
244  'packagePath' => 'Application/' . $packageKey . '/',
245  ];
246  $packageManager->activatePackage($packageKey);
247  }
248 
249  $actualPackageStatesConfiguration = $packageManager->_get('packageStatesConfiguration');
250  self::assertEquals($expectedPackageStatesConfiguration, $actualPackageStatesConfiguration['packages']);
251  }
252 
253  public static function createPackageCreatesPackageFolderAndReturnsPackageDataProvider(): array
254  {
255  return [
256  ['TYPO3.YetAnotherTestPackage', 'Packages/Application/TYPO3.YetAnotherTestPackage/'],
257  ['Lolli.Pop.NothingElse', 'Packages/Application/Lolli.Pop.NothingElse/'],
258  ];
259  }
260 
261  #[DataProvider('createPackageCreatesPackageFolderAndReturnsPackageDataProvider')]
262  #[Test]
263  public function createPackageCreatesPackageFolderAndReturnsPackage($packageKey, $expectedPackagePath): void
264  {
265  $actualPackage = $this->createPackage($packageKey);
266  $actualPackagePath = $actualPackage->getPackagePath();
267 
268  self::assertEquals($this->testRoot . $expectedPackagePath, $actualPackagePath);
269  self::assertDirectoryExists($actualPackagePath, 'Package path should exist after createPackage()');
270  self::assertEquals($packageKey, $actualPackage->getPackageKey());
271  self::assertTrue($this->packageManager->isPackageAvailable($packageKey));
272  }
273 
274  #[Test]
275  public function activatePackageAndDeactivatePackageActivateAndDeactivateTheGivenPackage(): void
276  {
277  $packageKey = 'Acme.YetAnotherTestPackage';
278  $this->createPackage($packageKey);
279  $this->packageManager->method('sortActivePackagesByDependencies')->willReturn([]);
280  $this->packageManager->deactivatePackage($packageKey);
281  self::assertFalse($this->packageManager->isPackageActive($packageKey));
282  $this->packageManager->activatePackage($packageKey);
283  self::assertTrue($this->packageManager->isPackageActive($packageKey));
284  }
285 
286  #[Test]
287  public function deactivatePackageThrowsAnExceptionIfPackageIsProtected(): void
288  {
289  $this->expectException(ProtectedPackageKeyException::class);
290  $this->expectExceptionCode(1308662891);
291 
292  $package = $this->createPackage('Acme.YetAnotherTestPackage');
293  $package->setProtected(true);
294  $this->packageManager->method('sortActivePackagesByDependencies')->willReturn([]);
295  $this->packageManager->deactivatePackage('Acme.YetAnotherTestPackage');
296  }
297 
298  #[Test]
299  public function deletePackageThrowsErrorIfPackageIsNotAvailable(): void
300  {
301  $this->expectException(UnknownPackageException::class);
302  $this->expectExceptionCode(1166543253);
303 
304  $this->packageManager->method('sortActivePackagesByDependencies')->willReturn([]);
305  $this->packageManager->deletePackage('PrettyUnlikelyThatThisPackageExists');
306  }
307 
308  #[Test]
309  public function deletePackageThrowsAnExceptionIfPackageIsProtected(): void
310  {
311  $this->expectException(ProtectedPackageKeyException::class);
312  $this->expectExceptionCode(1220722120);
313 
314  $package = $this->createPackage('Acme.YetAnotherTestPackage');
315  $package->setProtected(true);
316  $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
317  }
318 
319  #[Test]
320  public function deletePackageRemovesPackageFromAvailableAndActivePackagesAndDeletesThePackageDirectory(): void
321  {
322  $this->createPackage('Acme.YetAnotherTestPackage');
323 
324  $this->packageManager->method('sortActivePackagesByDependencies')->willReturn([]);
325 
326  self::assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
327  self::assertTrue($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
328 
329  $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
330  // unregister from test file removal, else error will be thrown
331  unset($this->testFilesToDelete[ array_search($this->testRoot . 'Packages/Application/' . 'Acme.YetAnotherTestPackage', $this->testFilesToDelete)]);
332 
333  self::assertFalse($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage/'));
334  self::assertFalse($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
335  }
336 
337  public static function buildDependencyGraphBuildsCorrectGraphDataProvider(): array
338  {
339  return [
340  'TYPO3 CMS Extensions' => [
341  [
342  'core' => [
343  'dependencies' => [],
344  ],
345  'setup' => [
346  'dependencies' => ['core'],
347  ],
348  'openid' => [
349  'dependencies' => ['core', 'setup'],
350  ],
351  'news' => [
352  'dependencies' => ['extbase'],
353  ],
354  'extbase' => [
355  'dependencies' => ['core'],
356  ],
357  'pt_extbase' => [
358  'dependencies' => ['extbase'],
359  ],
360  'foo' => [
361  'dependencies' => [],
362  ],
363  ],
364  [
365  'core',
366  'setup',
367  'openid',
368  'extbase',
369  ],
370  [
371  'core' => [
372  'core' => false,
373  'setup' => false,
374  'openid' => false,
375  'news' => false,
376  'extbase' => false,
377  'pt_extbase' => false,
378  'foo' => false,
379  ],
380  'setup' => [
381  'core' => true,
382  'setup' => false,
383  'openid' => false,
384  'news' => false,
385  'extbase' => false,
386  'pt_extbase' => false,
387  'foo' => false,
388  ],
389  'openid' => [
390  'core' => true,
391  'setup' => true,
392  'openid' => false,
393  'news' => false,
394  'extbase' => false,
395  'pt_extbase' => false,
396  'foo' => false,
397  ],
398  'news' => [
399  'core' => false,
400  'setup' => false,
401  'openid' => true,
402  'news' => false,
403  'extbase' => true,
404  'pt_extbase' => false,
405  'foo' => false,
406  ],
407  'extbase' => [
408  'core' => true,
409  'setup' => false,
410  'openid' => false,
411  'news' => false,
412  'extbase' => false,
413  'pt_extbase' => false,
414  'foo' => false,
415  ],
416  'pt_extbase' => [
417  'core' => false,
418  'setup' => false,
419  'openid' => true,
420  'news' => false,
421  'extbase' => true,
422  'pt_extbase' => false,
423  'foo' => false,
424  ],
425  'foo' => [
426  'core' => false,
427  'setup' => false,
428  'openid' => true,
429  'news' => false,
430  'extbase' => true,
431  'pt_extbase' => false,
432  'foo' => false,
433  ],
434  ],
435  ],
436  'Dummy Packages' => [
437  [
438  'A' => [
439  'dependencies' => ['B', 'D', 'C'],
440  ],
441  'B' => [
442  'dependencies' => [],
443  ],
444  'C' => [
445  'dependencies' => ['E'],
446  ],
447  'D' => [
448  'dependencies' => ['E'],
449  ],
450  'E' => [
451  'dependencies' => [],
452  ],
453  'F' => [
454  'dependencies' => [],
455  ],
456  ],
457  [
458  'B',
459  'C',
460  'E',
461  ],
462  [
463  'A' => [
464  'A' => false,
465  'B' => true,
466  'C' => true,
467  'D' => true,
468  'E' => false,
469  'F' => false,
470  ],
471  'B' => [
472  'A' => false,
473  'B' => false,
474  'C' => false,
475  'D' => false,
476  'E' => false,
477  'F' => false,
478  ],
479  'C' => [
480  'A' => false,
481  'B' => false,
482  'C' => false,
483  'D' => false,
484  'E' => true,
485  'F' => false,
486  ],
487  'D' => [
488  'A' => false,
489  'B' => true,
490  'C' => true,
491  'D' => false,
492  'E' => false,
493  'F' => false,
494  ],
495  'E' => [
496  'A' => false,
497  'B' => false,
498  'C' => false,
499  'D' => false,
500  'E' => false,
501  'F' => false,
502  ],
503  'F' => [
504  'A' => false,
505  'B' => true,
506  'C' => true,
507  'D' => false,
508  'E' => false,
509  'F' => false,
510  ],
511  ],
512  ],
513  ];
514  }
515 
516  #[DataProvider('buildDependencyGraphBuildsCorrectGraphDataProvider')]
517  #[Test]
518  public function buildDependencyGraphBuildsCorrectGraph(array $unsortedPackageStatesConfiguration, array $frameworkPackageKeys, array $expectedGraph): void
519  {
520  $packageManager = $this->getAccessibleMock(PackageManager::class, ['findFrameworkPackages'], [new DependencyOrderingService()]);
521  $packageManager->method('findFrameworkPackages')->willReturn($frameworkPackageKeys);
522 
523  $dependencyGraph = $packageManager->_call('buildDependencyGraph', $unsortedPackageStatesConfiguration);
524 
525  self::assertEquals($expectedGraph, $dependencyGraph);
526  }
527 
528  public static function packageSortingDataProvider(): array
529  {
530  return [
531  'TYPO3 Flow Packages' => [
532  [
533  'TYPO3.Flow' => [
534  'dependencies' => ['Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM'],
535  ],
536  'Doctrine.ORM' => [
537  'dependencies' => ['Doctrine.Common', 'Doctrine.DBAL'],
538  ],
539  'Doctrine.Common' => [
540  'dependencies' => [],
541  ],
542  'Doctrine.DBAL' => [
543  'dependencies' => ['Doctrine.Common'],
544  ],
545  'Symfony.Component.Yaml' => [
546  'dependencies' => [],
547  ],
548  ],
549  [
550  'Doctrine.Common',
551  ],
552  [
553  'Doctrine.Common',
554  'Doctrine.DBAL',
555  'Doctrine.ORM',
556  'Symfony.Component.Yaml',
557  'TYPO3.Flow',
558  ],
559  ],
560  'TYPO3 CMS Extensions' => [
561  [
562  'core' => [
563  'dependencies' => [],
564  ],
565  'setup' => [
566  'dependencies' => ['core'],
567  ],
568  'openid' => [
569  'dependencies' => ['core', 'setup'],
570  ],
571  'news' => [
572  'dependencies' => ['extbase'],
573  ],
574  'extbase' => [
575  'dependencies' => ['core'],
576  ],
577  'pt_extbase' => [
578  'dependencies' => ['extbase'],
579  ],
580  'foo' => [
581  'dependencies' => [],
582  ],
583  ],
584  [
585  'core',
586  'setup',
587  'openid',
588  'extbase',
589  ],
590  [
591  'core',
592  'setup',
593  'openid',
594  'extbase',
595  'foo',
596  'news',
597  'pt_extbase',
598  ],
599  ],
600  'Dummy Packages' => [
601  [
602  'A' => [
603  'dependencies' => ['B', 'D', 'C'],
604  ],
605  'B' => [
606  'dependencies' => [],
607  ],
608  'C' => [
609  'dependencies' => ['E'],
610  ],
611  'D' => [
612  'dependencies' => ['E'],
613  ],
614  'E' => [
615  'dependencies' => [],
616  ],
617  'F' => [
618  'dependencies' => [],
619  ],
620  ],
621  [
622  'B',
623  'C',
624  'E',
625  ],
626  [
627  'E',
628  'C',
629  'B',
630  'D',
631  'A',
632  'F',
633  ],
634  ],
635  ];
636  }
637 
638  #[DataProvider('packageSortingDataProvider')]
639  #[Test]
640  public function sortPackageStatesConfigurationByDependencyMakesSureThatDependantPackagesAreStandingBeforeAPackageInTheInternalPackagesAndPackagesConfigurationArrays(
641  array $unsortedPackageStatesConfiguration,
642  array $frameworkPackageKeys,
643  array $expectedSortedPackageKeys
644  ): void {
645  $packageManager = $this->getAccessibleMock(PackageManager::class, ['findFrameworkPackages'], [new DependencyOrderingService()]);
646  $packageManager->method('findFrameworkPackages')->willReturn($frameworkPackageKeys);
647 
648  $sortedPackageKeys = $packageManager->_call('sortPackageStatesConfigurationByDependency', $unsortedPackageStatesConfiguration);
649 
650  self::assertEquals($expectedSortedPackageKeys, $sortedPackageKeys, 'The package states configurations have not been ordered according to their dependencies!');
651  }
652 
653  #[Test]
654  public function sortPackageStatesConfigurationByDependencyThrowsExceptionWhenCycleDetected(): void
655  {
656  $unsortedPackageStatesConfiguration = [
657  'A' => [
658  'dependencies' => ['B'],
659  ],
660  'B' => [
661  'dependencies' => ['A'],
662  ],
663  ];
664 
665  $this->expectException(\UnexpectedValueException::class);
666  $this->expectExceptionCode(1381960494);
667 
668  $packageManager = $this->getAccessibleMock(PackageManager::class, ['findFrameworkPackages'], [new DependencyOrderingService()]);
669  $packageManager->method('findFrameworkPackages')->willReturn([]);
670 
671  $packageManager->_call('sortPackageStatesConfigurationByDependency', $unsortedPackageStatesConfiguration);
672  }
673 }
‪TYPO3\CMS\Core\Package\Exception\UnknownPackageException
Definition: UnknownPackageException.php:23
‪TYPO3\CMS\Core\Package\Cache\PackageStatesPackageCache
Definition: PackageStatesPackageCache.php:32
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Core\Tests\Unit\Package
Definition: AbstractServiceProviderTest.php:18
‪TYPO3\CMS\Core\Package\Exception\UnknownPackagePathException
Definition: UnknownPackagePathException.php:23
‪TYPO3\CMS\Core\Package\Package
Definition: Package.php:30
‪TYPO3\CMS\Core\Service\DependencyOrderingService
Definition: DependencyOrderingService.php:32
‪TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend
Definition: SimpleFileBackend.php:33
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Package\Exception\ProtectedPackageKeyException
Definition: ProtectedPackageKeyException.php:23
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57