Skip to content

Commit f0aa291

Browse files
DanielSiepmannsbuerk
authored andcommitted
[FEATURE] Add support for composer.json only extensions
The internal array always uses the extension key. Not having an ext_emconf.php with dependencies to extension keys, but only a composer.json with dependencies to package names will always result in: UnexpectedValueException: The package "extension_key" depends on "composer/package-key" which is not present in the system. This is a common situation in modern TYPO3 projects where multiple extensions are given which declare their internal dependencies to have proper TYPO3 loading order. A fallback from the dependency (which might be the composer name) to the actual extension key is added. Resolves: #541
1 parent 302dd83 commit f0aa291

File tree

6 files changed

+165
-5
lines changed

6 files changed

+165
-5
lines changed

Classes/Core/PackageCollection.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ protected function convertConfigurationForGraph(array $allPackageConstraints, ar
157157
];
158158
if (isset($allPackageConstraints[$packageKey]['dependencies'])) {
159159
foreach ($allPackageConstraints[$packageKey]['dependencies'] as $dependentPackageKey) {
160-
if (!in_array($dependentPackageKey, $packageKeys, true)) {
160+
$extensionKey = $this->composerPackageManager->getPackageInfo($dependentPackageKey)?->getExtensionKey() ?? '';
161+
if (!in_array($dependentPackageKey, $packageKeys, true) && !in_array($extensionKey, $packageKeys, true)) {
161162
if ($this->isComposerDependency($dependentPackageKey)) {
162163
// The given package has a dependency to a Composer package that has no relation to TYPO3
163164
// We can ignore those, when calculating the extension order
@@ -169,7 +170,7 @@ protected function convertConfigurationForGraph(array $allPackageConstraints, ar
169170
1519931815
170171
);
171172
}
172-
$dependencies[$packageKey]['after'][] = $dependentPackageKey;
173+
$dependencies[$packageKey]['after'][] = $extensionKey ?: $dependentPackageKey;
173174
}
174175
}
175176
if (isset($allPackageConstraints[$packageKey]['suggestions'])) {
@@ -250,15 +251,18 @@ protected function getDependencyArrayForPackage(PackageInterface $package, array
250251
foreach ($dependentPackageConstraints as $constraint) {
251252
if ($constraint instanceof PackageConstraint) {
252253
$dependentPackageKey = $constraint->getValue();
254+
$extensionKey = $this->composerPackageManager->getPackageInfo($dependentPackageKey)?->getExtensionKey() ?? '';
253255
if (!in_array($dependentPackageKey, $dependentPackageKeys, true) && !in_array($dependentPackageKey, $trace, true)) {
254-
$dependentPackageKeys[] = $dependentPackageKey;
256+
$dependentPackageKeys[] = $extensionKey ?: $dependentPackageKey;
255257
}
256-
if (!isset($this->packages[$dependentPackageKey])) {
258+
259+
if (!isset($this->packages[$dependentPackageKey]) && !isset($this->packages[$extensionKey])) {
257260
if ($this->isComposerDependency($dependentPackageKey)) {
258261
// The given package has a dependency to a Composer package that has no relation to TYPO3
259262
// We can ignore those, when calculating the extension order
260263
continue;
261264
}
265+
262266
throw new Exception(
263267
sprintf(
264268
'Package "%s" depends on package "%s" which does not exist.',
@@ -268,7 +272,7 @@ protected function getDependencyArrayForPackage(PackageInterface $package, array
268272
1695119749
269273
);
270274
}
271-
$this->getDependencyArrayForPackage($this->packages[$dependentPackageKey], $dependentPackageKeys, $trace);
275+
$this->getDependencyArrayForPackage($this->packages[$dependentPackageKey] ?? $this->packages[$extensionKey], $dependentPackageKeys, $trace);
272276
}
273277
}
274278
return array_reverse($dependentPackageKeys);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
return [
4+
'packages' => [
5+
'core' => [
6+
'packagePath' => '.Build/vendor/typo3/cms-core/',
7+
],
8+
'extbase' => [
9+
'packagePath' => '.Build/vendor/typo3/cms-extbase/',
10+
],
11+
'fluid' => [
12+
'packagePath' => '.Build/vendor/typo3/cms-fluid/',
13+
],
14+
'backend' => [
15+
'packagePath' => '.Build/vendor/typo3/cms-backend/',
16+
],
17+
'frontend' => [
18+
'packagePath' => '.Build/vendor/typo3/cms-frontend/',
19+
],
20+
'package0' => [
21+
'packagePath' => 'Tests/Unit/Core/Fixtures/Packages/package0/',
22+
],
23+
'package2' => [
24+
'packagePath' => 'Tests/Unit/Core/Fixtures/Packages/package2/',
25+
],
26+
'package1' => [
27+
'packagePath' => 'Tests/Unit/Core/Fixtures/Packages/package1/',
28+
],
29+
],
30+
'version' => 5,
31+
];
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "typo3/testing-framework-package-0",
3+
"description": "Package 0",
4+
"type": "typo3-cms-extension",
5+
"license": [
6+
"GPL-2.0-or-later"
7+
],
8+
"require": {
9+
"php": "*",
10+
"typo3/cms-core": "*"
11+
},
12+
"extra": {
13+
"typo3/cms": {
14+
"extension-key": "package0"
15+
}
16+
}
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "typo3/testing-framework-package-1",
3+
"description": "Package 1, with replace entry",
4+
"type": "typo3-cms-extension",
5+
"license": [
6+
"GPL-2.0-or-later"
7+
],
8+
"require": {
9+
"php": "*",
10+
"typo3/cms-core": "*"
11+
},
12+
"replace": {
13+
"typo3-ter/package1": "self.version"
14+
},
15+
"extra": {
16+
"typo3/cms": {
17+
"extension-key": "package1"
18+
}
19+
}
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "typo3/testing-framework-package-2",
3+
"description": "Package 2 depending on package 1",
4+
"type": "typo3-cms-extension",
5+
"license": [
6+
"GPL-2.0-or-later"
7+
],
8+
"require": {
9+
"php": "*",
10+
"typo3/testing-framework-package-1": "*",
11+
"typo3/testing-framework-package-0": "*",
12+
"typo3/cms-core": "*"
13+
},
14+
"extra": {
15+
"typo3/cms": {
16+
"extension-key": "package2"
17+
}
18+
}
19+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* Copyright (C) 2024 Daniel Siepmann <[email protected]>
7+
*
8+
* This program is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU General Public License
10+
* as published by the Free Software Foundation; either version 2
11+
* of the License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21+
* 02110-1301, USA.
22+
*/
23+
24+
namespace Typo3\TestingFramework\Tests\Unit\Core;
25+
26+
use PHPUnit\Framework\TestCase;
27+
use TYPO3\CMS\Core\Package\PackageManager;
28+
use TYPO3\CMS\Core\Service\DependencyOrderingService;
29+
use TYPO3\TestingFramework\Composer\ComposerPackageManager;
30+
use TYPO3\TestingFramework\Core\PackageCollection;
31+
32+
final class PackageCollectionTest extends TestCase
33+
{
34+
/**
35+
* @test
36+
*/
37+
public function sortsComposerPackages(): void
38+
{
39+
$packageStates = require __DIR__ . '/Fixtures/Packages/PackageStates.php';
40+
$packageStates = $packageStates['packages'];
41+
$basePath = realpath(__DIR__ . '/../../../');
42+
43+
$composerPackageManager = new ComposerPackageManager();
44+
// That way it knows about the extensions, this is done by TestBase upfront.
45+
$composerPackageManager->getPackageInfoWithFallback(__DIR__ . '/Fixtures/Packages/package0');
46+
$composerPackageManager->getPackageInfoWithFallback(__DIR__ . '/Fixtures/Packages/package1');
47+
$composerPackageManager->getPackageInfoWithFallback(__DIR__ . '/Fixtures/Packages/package2');
48+
49+
$subject = PackageCollection::fromPackageStates(
50+
$composerPackageManager,
51+
new PackageManager(
52+
new DependencyOrderingService(),
53+
__DIR__ . '/Fixtures/Packages/PackageStates.php',
54+
$basePath
55+
),
56+
$basePath,
57+
$packageStates
58+
);
59+
60+
$result = $subject->sortPackageStates(
61+
$packageStates,
62+
new DependencyOrderingService()
63+
);
64+
65+
self::assertSame(7, array_search('package2', array_keys($result)), 'Package 2 is not stored at loading order 7.');
66+
self::assertSame(6, array_search('package1', array_keys($result)), 'Package 1 is not stored at loading order 6.');
67+
self::assertSame(5, array_search('package0', array_keys($result)), 'Package 0 is not stored at loading order 5.');
68+
}
69+
}

0 commit comments

Comments
 (0)