diff --git a/Neos.Flow/Classes/Composer/ComposerUtility.php b/Neos.Flow/Classes/Composer/ComposerUtility.php index 94cca5a9ab..fa18783685 100644 --- a/Neos.Flow/Classes/Composer/ComposerUtility.php +++ b/Neos.Flow/Classes/Composer/ComposerUtility.php @@ -12,13 +12,14 @@ */ use Neos\Flow\Package\FlowPackageInterface; +use Neos\Flow\Package\FlowPackageKey; use Neos\Utility\ObjectAccess; use Neos\Utility\Files; /** * Utility to access composer information like composer manifests (composer.json) and the lock file. * - * Meant to be used only inside the Flow package management code. + * @internal Only meant to be used only inside the Flow package management code. */ class ComposerUtility { @@ -125,28 +126,15 @@ public static function isFlowPackageType(string $packageType): bool return false; } - /** - * Determines the composer package name ("vendor/foo-bar") from the Flow package key ("Vendor.Foo.Bar") - * - * @param string $packageKey - * @return string - */ - public static function getComposerPackageNameFromPackageKey(string $packageKey): string - { - $nameParts = explode('.', $packageKey); - $vendor = array_shift($nameParts); - return strtolower($vendor . '/' . implode('-', $nameParts)); - } - /** * Write a composer manifest for the package. * * @param string $manifestPath - * @param string $packageKey + * @param FlowPackageKey $packageKey * @param array $composerManifestData * @return array the manifest data written */ - public static function writeComposerManifest(string $manifestPath, string $packageKey, array $composerManifestData = []): array + public static function writeComposerManifest(string $manifestPath, FlowPackageKey $packageKey, array $composerManifestData = []): array { $manifest = [ 'description' => '' @@ -156,7 +144,7 @@ public static function writeComposerManifest(string $manifestPath, string $packa $manifest = array_merge($manifest, $composerManifestData); } if (!isset($manifest['name']) || empty($manifest['name'])) { - $manifest['name'] = static::getComposerPackageNameFromPackageKey($packageKey); + $manifest['name'] = $packageKey->deriveComposerPackageName(); } if (!isset($manifest['require']) || empty($manifest['require'])) { @@ -164,11 +152,11 @@ public static function writeComposerManifest(string $manifestPath, string $packa } if (!isset($manifest['autoload'])) { - $namespace = str_replace('.', '\\', $packageKey) . '\\'; + $namespace = str_replace('.', '\\', $packageKey->value) . '\\'; $manifest['autoload'] = ['psr-4' => [$namespace => FlowPackageInterface::DIRECTORY_CLASSES]]; } - $manifest['extra']['neos']['package-key'] = $packageKey; + $manifest['extra']['neos']['package-key'] = $packageKey->value; if (defined('JSON_PRETTY_PRINT')) { file_put_contents(Files::concatenatePaths([$manifestPath, 'composer.json']), json_encode($manifest, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); diff --git a/Neos.Flow/Classes/Package/FlowPackageKey.php b/Neos.Flow/Classes/Package/FlowPackageKey.php new file mode 100644 index 0000000000..5e5a72ec6e --- /dev/null +++ b/Neos.Flow/Classes/Package/FlowPackageKey.php @@ -0,0 +1,130 @@ +value); + $vendor = array_shift($nameParts); + return strtolower($vendor . '/' . implode('-', $nameParts)); + } +} diff --git a/Neos.Flow/Classes/Package/PackageFactory.php b/Neos.Flow/Classes/Package/PackageFactory.php index 8a3725c352..e3856cf238 100644 --- a/Neos.Flow/Classes/Package/PackageFactory.php +++ b/Neos.Flow/Classes/Package/PackageFactory.php @@ -25,14 +25,14 @@ class PackageFactory * * @param string $packagesBasePath the base install path of packages, * @param string $packagePath path to package, relative to base path - * @param string $packageKey key / name of the package + * @param FlowPackageKey $packageKey key / name of the package * @param string $composerName * @param array $autoloadConfiguration Autoload configuration as defined in composer.json - * @param array $packageClassInformation - * @return PackageInterface|PackageKeyAwareInterface + * @param array{className: class-string, pathAndFilename: string}|null $packageClassInformation + * @return PackageInterface&PackageKeyAwareInterface * @throws Exception\CorruptPackageException */ - public function create($packagesBasePath, $packagePath, $packageKey, $composerName, array $autoloadConfiguration = [], array $packageClassInformation = null) + public function create(string $packagesBasePath, string $packagePath, FlowPackageKey $packageKey, string $composerName, array $autoloadConfiguration = [], array $packageClassInformation = null): PackageInterface { $absolutePackagePath = Files::concatenatePaths([$packagesBasePath, $packagePath]) . '/'; @@ -40,34 +40,34 @@ public function create($packagesBasePath, $packagePath, $packageKey, $composerNa $packageClassInformation = $this->detectFlowPackageFilePath($packageKey, $absolutePackagePath); } - $packageClassName = Package::class; - if (!empty($packageClassInformation)) { - $packageClassName = $packageClassInformation['className']; - $packageClassPath = !empty($packageClassInformation['pathAndFilename']) ? Files::concatenatePaths([$absolutePackagePath, $packageClassInformation['pathAndFilename']]) : null; - } + $packageClassName = $packageClassInformation['className']; - if (!empty($packageClassPath)) { + if (!empty($packageClassInformation['pathAndFilename'])) { + $packageClassPath = Files::concatenatePaths([$absolutePackagePath, $packageClassInformation['pathAndFilename']]); require_once($packageClassPath); } - $package = new $packageClassName($packageKey, $composerName, $absolutePackagePath, $autoloadConfiguration); + /** dynamic construction {@see GenericPackage::__construct} */ + $package = new $packageClassName($packageKey->value, $composerName, $absolutePackagePath, $autoloadConfiguration); if (!$package instanceof PackageInterface) { - throw new Exception\CorruptPackageException(sprintf('The package class of package "%s" does not implement \Neos\Flow\Package\PackageInterface. Check the file "%s".', $packageKey, $packageClassInformation['pathAndFilename']), 1427193370); + throw new Exception\CorruptPackageException(sprintf('The package class of package "%s" does not implement \Neos\Flow\Package\PackageInterface. Check the file "%s".', $packageKey->value, $packageClassInformation['pathAndFilename']), 1427193370); + } + if (!$package instanceof PackageKeyAwareInterface) { + throw new Exception\CorruptPackageException(sprintf('The package class of package "%s" does not implement \Neos\Flow\Package\PackageKeyAwareInterface. Check the file "%s".', $packageKey->value, $packageClassInformation['pathAndFilename']), 1711665156); } - return $package; } /** * Detects if the package contains a package file and returns the path and classname. * - * @param string $packageKey The package key + * @param FlowPackageKey $packageKey The package key * @param string $absolutePackagePath Absolute path to the package - * @return array The path to the package file and classname for this package or an empty array if none was found. + * @return array{className: class-string, pathAndFilename: string} The path to the package file and classname for this package or an empty array if none was found. * @throws Exception\CorruptPackageException * @throws Exception\InvalidPackagePathException */ - public function detectFlowPackageFilePath($packageKey, $absolutePackagePath) + public function detectFlowPackageFilePath(FlowPackageKey $packageKey, $absolutePackagePath): array { if (!is_dir($absolutePackagePath)) { throw new Exception\InvalidPackagePathException(sprintf('The given package path "%s" is not a readable directory.', $absolutePackagePath), 1445904440); @@ -80,7 +80,7 @@ public function detectFlowPackageFilePath($packageKey, $absolutePackagePath) $possiblePackageClassPaths = [ Files::concatenatePaths(['Classes', 'Package.php']), - Files::concatenatePaths(['Classes', str_replace('.', '/', $packageKey), 'Package.php']) + Files::concatenatePaths(['Classes', str_replace('.', '/', $packageKey->value), 'Package.php']) ]; $foundPackageClassPaths = array_filter($possiblePackageClassPaths, function ($packageClassPathAndFilename) use ($absolutePackagePath) { @@ -93,7 +93,7 @@ public function detectFlowPackageFilePath($packageKey, $absolutePackagePath) } if (count($foundPackageClassPaths) > 1) { - throw new Exception\CorruptPackageException(sprintf('The package "%s" contains multiple possible "Package.php" files. Please make sure that only one "Package.php" exists in the autoload root(s) of your Flow package.', $packageKey), 1457454840); + throw new Exception\CorruptPackageException(sprintf('The package "%s" contains multiple possible "Package.php" files. Please make sure that only one "Package.php" exists in the autoload root(s) of your Flow package.', $packageKey->value), 1457454840); } $packageClassPathAndFilename = reset($foundPackageClassPaths); @@ -102,7 +102,7 @@ public function detectFlowPackageFilePath($packageKey, $absolutePackagePath) $packageClassContents = file_get_contents($absolutePackageClassPath); $packageClassName = (new PhpAnalyzer($packageClassContents))->extractFullyQualifiedClassName(); if ($packageClassName === null) { - throw new Exception\CorruptPackageException(sprintf('The package "%s" does not contain a valid package class. Check if the file "%s" really contains a class.', $packageKey, $packageClassPathAndFilename), 1327587091); + throw new Exception\CorruptPackageException(sprintf('The package "%s" does not contain a valid package class. Check if the file "%s" really contains a class.', $packageKey->value, $packageClassPathAndFilename), 1327587091); } return ['className' => $packageClassName, 'pathAndFilename' => $packageClassPathAndFilename]; diff --git a/Neos.Flow/Classes/Package/PackageInterface.php b/Neos.Flow/Classes/Package/PackageInterface.php index 5c7e115480..b8ce3e2efd 100644 --- a/Neos.Flow/Classes/Package/PackageInterface.php +++ b/Neos.Flow/Classes/Package/PackageInterface.php @@ -18,7 +18,8 @@ */ interface PackageInterface { - const PATTERN_MATCH_PACKAGEKEY = '/^[a-z0-9]+\.(?:[a-z0-9][\.a-z0-9]*)+$/i'; + /** @deprecated with Flow 9, please use {@see PackageManager::isPackageKeyValid()} instead. */ + const PATTERN_MATCH_PACKAGEKEY = FlowPackageKey::PATTERN; const DEFAULT_COMPOSER_TYPE = 'neos-package'; /** diff --git a/Neos.Flow/Classes/Package/PackageManager.php b/Neos.Flow/Classes/Package/PackageManager.php index f724e10179..41d6f2ce7d 100644 --- a/Neos.Flow/Classes/Package/PackageManager.php +++ b/Neos.Flow/Classes/Package/PackageManager.php @@ -68,7 +68,7 @@ class PackageManager /** * A translation table between lower cased and upper camel cased package keys * - * @var array + * @var array */ protected $packageKeys = []; @@ -316,11 +316,9 @@ protected function filterPackagesByType($packages, $packageType): array */ public function createPackage($packageKey, array $manifest = [], $packagesPath = null): PackageInterface { - if (!$this->isPackageKeyValid($packageKey)) { - throw new Exception\InvalidPackageKeyException('The package key "' . $packageKey . '" is invalid', 1220722210); - } - if ($this->isPackageAvailable($packageKey)) { - throw new Exception\PackageKeyAlreadyExistsException('The package key "' . $packageKey . '" already exists', 1220722873); + $packageKey = FlowPackageKey::fromString($packageKey); + if ($this->isPackageAvailable($packageKey->value)) { + throw new Exception\PackageKeyAlreadyExistsException('The package key "' . $packageKey->value . '" already exists', 1220722873); } if (!isset($manifest['type'])) { $manifest['type'] = PackageInterface::DEFAULT_COMPOSER_TYPE; @@ -352,7 +350,7 @@ public function createPackage($packageKey, array $manifest = [], $packagesPath = $packagesPath = Files::getUnixStylePath(Files::concatenatePaths([$this->packagesBasePath, $packagesPath])); } - $packagePath = Files::concatenatePaths([$packagesPath, $packageKey]) . '/'; + $packagePath = Files::concatenatePaths([$packagesPath, $packageKey->value]) . '/'; Files::createDirectoryRecursively($packagePath); foreach ( @@ -388,9 +386,9 @@ public function createPackage($packageKey, array $manifest = [], $packagesPath = $refreshedPackageStatesConfiguration = $this->rescanPackages(); $this->packageStatesConfiguration = $refreshedPackageStatesConfiguration; $this->registerPackageFromStateConfiguration($manifest['name'], $this->packageStatesConfiguration['packages'][$manifest['name']]); - $package = $this->packages[$packageKey]; + $package = $this->packages[$packageKey->value]; if ($package instanceof FlowPackageInterface) { - $this->flowPackages[$packageKey] = $package; + $this->flowPackages[$packageKey->value] = $package; } return $package; @@ -592,8 +590,8 @@ protected function scanAvailablePackages(): array continue; } - $packageKey = $this->getPackageKeyFromManifest($composerManifest, $packagePath); - $this->composerNameToPackageKeyMap[strtolower($composerManifest['name'])] = $packageKey; + $packageKey = FlowPackageKey::deriveFromManifestOrPath($composerManifest, $packagePath); + $this->composerNameToPackageKeyMap[strtolower($composerManifest['name'])] = $packageKey->value; $packageConfiguration = $this->preparePackageStateConfiguration($packageKey, $packagePath, $composerManifest); if (isset($newPackageStatesConfiguration['packages'][$composerManifest['name']])) { @@ -640,19 +638,15 @@ protected function findComposerPackagesInPath(string $startingDirectory): \Gener } /** - * @param string $packageKey - * @param string $packagePath - * @param array $composerManifest - * @return array * @throws Exception\CorruptPackageException * @throws Exception\InvalidPackagePathException */ - protected function preparePackageStateConfiguration($packageKey, $packagePath, $composerManifest): array + protected function preparePackageStateConfiguration(FlowPackageKey $packageKey, string $packagePath, array $composerManifest): array { $autoload = $composerManifest['autoload'] ?? []; return [ - 'packageKey' => $packageKey, + 'packageKey' => $packageKey->value, 'packagePath' => str_replace($this->packagesBasePath, '', $packagePath), 'composerName' => $composerManifest['name'], 'autoloadConfiguration' => $autoload, @@ -687,7 +681,7 @@ protected function registerPackageFromStateConfiguration($composerName, $package { $packagePath = $packageStateConfiguration['packagePath'] ?? null; $packageClassInformation = $packageStateConfiguration['packageClassInformation'] ?? null; - $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageStateConfiguration['packageKey'], $composerName, $packageStateConfiguration['autoloadConfiguration'], $packageClassInformation); + $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, FlowPackageKey::fromString($packageStateConfiguration['packageKey']), $composerName, $packageStateConfiguration['autoloadConfiguration'], $packageClassInformation); $this->packageKeys[strtolower($package->getPackageKey())] = $package->getPackageKey(); $this->packages[$package->getPackageKey()] = $package; } @@ -829,81 +823,7 @@ public function getPackageKeyFromComposerName($composerName): string */ public function isPackageKeyValid($packageKey): bool { - return preg_match(PackageInterface::PATTERN_MATCH_PACKAGEKEY, $packageKey) === 1; - } - - /** - * Resolves package key from Composer manifest - * - * If it is a Flow package the name of the containing directory will be used. - * - * Else if the composer name of the package matches the first part of the lowercased namespace of the package, the mixed - * case version of the composer name / namespace will be used, with backslashes replaced by dots. - * - * Else the composer name will be used with the slash replaced by a dot - * - * @param array $manifest - * @param string $packagePath - * @return string - */ - protected function getPackageKeyFromManifest(array $manifest, $packagePath): string - { - if (isset($manifest['extra']['neos']['package-key']) && $this->isPackageKeyValid($manifest['extra']['neos']['package-key'])) { - return $manifest['extra']['neos']['package-key']; - } - - $composerName = $manifest['name']; - $autoloadNamespace = null; - $type = null; - if (isset($manifest['autoload']['psr-0']) && is_array($manifest['autoload']['psr-0'])) { - $namespaces = array_keys($manifest['autoload']['psr-0']); - $autoloadNamespace = reset($namespaces); - } - - if (isset($manifest['type'])) { - $type = $manifest['type']; - } - - return $this->derivePackageKey($composerName, $type, $packagePath, $autoloadNamespace); - } - - /** - * Derive a flow package key from the given information. - * The order of importance is: - * - * - package install path - * - first found autoload namespace - * - composer name - * - * @param string $composerName - * @param string $packageType - * @param string $packagePath - * @param string $autoloadNamespace - * @return string - */ - protected function derivePackageKey(string $composerName, string $packageType = null, string $packagePath = '', string $autoloadNamespace = null): string - { - $packageKey = ''; - - if ($packageType !== null && ComposerUtility::isFlowPackageType($packageType)) { - $lastSegmentOfPackagePath = substr(trim($packagePath, '/'), strrpos(trim($packagePath, '/'), '/') + 1); - if (strpos($lastSegmentOfPackagePath, '.') !== false) { - $packageKey = $lastSegmentOfPackagePath; - } - } - - if ($autoloadNamespace !== null && ($packageKey === null || $this->isPackageKeyValid($packageKey) === false)) { - $packageKey = str_replace('\\', '.', $autoloadNamespace); - } - - if ($packageKey === null || $this->isPackageKeyValid($packageKey) === false) { - $packageKey = str_replace('/', '.', $composerName); - } - - $packageKey = trim($packageKey, '.'); - $packageKey = preg_replace('/[^A-Za-z0-9.]/', '', $packageKey); - - return $packageKey; + return FlowPackageKey::isPackageKeyValid($packageKey); } /** diff --git a/Neos.Flow/Tests/Unit/Package/FlowPackageKeyTest.php b/Neos.Flow/Tests/Unit/Package/FlowPackageKeyTest.php new file mode 100644 index 0000000000..3d9262132e --- /dev/null +++ b/Neos.Flow/Tests/Unit/Package/FlowPackageKeyTest.php @@ -0,0 +1,175 @@ +value); + } + + /** + * @dataProvider invalidPackageKeys + * @test + */ + public function invalidPackageKeysAreRejected(string $packageKey) + { + $this->expectException(InvalidPackageKeyException::class); + FlowPackageKey::fromString($packageKey); + } + + public static function deriveFromManifestOrPathExamples(): iterable + { + yield 'for libraries the package key inferred from composer name' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "my/package" + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'my.package' + ]; + + yield 'for libraries the package key inferred from composer name (hyphens are removed)' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "vendor/foo-bar" + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'vendor.foobar' + ]; + + yield 'for type neos packages the case sensitive name from the folder is used' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "my/flow-package", + "type": "neos-plugin" + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '/app/Packages/Framework/My.Flow.Package', + 'expected' => 'My.Flow.Package' + ]; + + yield 'empty defined Flow package key' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "my/package", + "extra": { + "neos": { + "package-key": "" + } + } + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'my.package' + ]; + + + yield 'invalid defined Flow package key' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "my/package", + "extra": { + "neos": { + "package-key": "Floh:" + } + } + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'my.package' + ]; + + yield 'explicitly (differently) defined Flow package key' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "my/package", + "extra": { + "neos": { + "package-key": "MyCustom.PackageKey" + } + } + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'MyCustom.PackageKey' + ]; + + yield 'legacy psr-0 autoload path will be used as fallback' => [ + 'manifest' => json_decode(<<<'JSON' + { + "name": "acme/mypackage", + "autoload": { + "psr-0": { + "Acme\\MyPackage": "Classes/" + } + } + } + JSON, true, 512, JSON_THROW_ON_ERROR), + 'packagePath' => '', + 'expected' => 'Acme.MyPackage' + ]; + } + + /** + * @dataProvider deriveFromManifestOrPathExamples + * @test + */ + public function deriveFromManifestOrPath(array $manifest, string $packagePath, string $expected) + { + $actual = FlowPackageKey::deriveFromManifestOrPath($manifest, $packagePath); + self::assertSame($expected, $actual->value); + } + + /** + * @test + */ + public function deriveComposerPackageName() + { + self::assertSame( + 'neos/flow', + FlowPackageKey::fromString('Neos.Flow')->deriveComposerPackageName() + ); + + self::assertSame( + 'vendor/foo-bar', + FlowPackageKey::fromString('Vendor.Foo.Bar')->deriveComposerPackageName() + ); + } +} diff --git a/Neos.Flow/Tests/Unit/Package/PackageFactoryTest.php b/Neos.Flow/Tests/Unit/Package/PackageFactoryTest.php index 01c3eaa55f..8109b55985 100644 --- a/Neos.Flow/Tests/Unit/Package/PackageFactoryTest.php +++ b/Neos.Flow/Tests/Unit/Package/PackageFactoryTest.php @@ -13,6 +13,7 @@ use Neos\Flow\Package\Exception\CorruptPackageException; use Neos\Flow\Package\Exception\InvalidPackagePathException; +use Neos\Flow\Package\FlowPackageKey; use org\bovigo\vfs\vfsStream; use Neos\Flow\Composer\ComposerUtility; use Neos\Flow\Package\Package; @@ -54,7 +55,7 @@ protected function setUp(): void public function createThrowsExceptionWhenSpecifyingANonExistingPackagePath() { $this->expectException(InvalidPackagePathException::class); - $this->packageFactory->create('vfs://Packages/', 'Some/Non/Existing/Path/Some.Package/', 'Some.Package', 'some/package'); + $this->packageFactory->create('vfs://Packages/', 'Some/Non/Existing/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package'); } /** @@ -69,7 +70,7 @@ public function createThrowsExceptionIfCustomPackageFileCantBeAnalyzed() file_put_contents($packagePath . 'composer.json', '{"name": "some/package", "type": "neos-test", "autoload": { "psr-0": { "Foo": "bar" }}}'); file_put_contents($packageFilePath, 'packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', 'Some.Package', 'some/package'); + $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package'); } /** @@ -86,7 +87,7 @@ public function createThrowsExceptionIfCustomPackageDoesNotImplementPackageInter require($packageFilePath); - $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', 'Some.Package', 'some/package'); + $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package'); } /** @@ -102,7 +103,7 @@ public function createReturnsInstanceOfCustomPackageIfItExists() require($packageFilePath); - $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', 'Some.Package', 'some/package'); + $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package'); self::assertSame('Neos\Flow\Fixtures\CustomPackage2', get_class($package)); } @@ -121,7 +122,7 @@ public function createTakesAutoloaderTypeIntoAccountWhenLoadingCustomPackage() require($packageFilePath); - $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', 'Some.Package', 'some/package', $composerManifest['autoload']); + $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package', $composerManifest['autoload']); self::assertSame('Neos\Flow\Fixtures\CustomPackage3', get_class($package)); } @@ -134,7 +135,7 @@ public function createReturnsAnInstanceOfTheDefaultPackageIfNoCustomPackageExist mkdir($packagePath, 0777, true); file_put_contents($packagePath . 'composer.json', '{"name": "some/package", "type": "neos-test"}'); - $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', 'Some.Package', 'some/package'); + $package = $this->packageFactory->create('vfs://Packages/', 'Some/Path/Some.Package/', FlowPackageKey::fromString('Some.Package'), 'some/package'); self::assertSame(Package::class, get_class($package)); } } diff --git a/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php b/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php index ab32cb06e3..60caf74770 100644 --- a/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php +++ b/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php @@ -19,6 +19,7 @@ use Neos\Flow\Package\Exception\PackageKeyAlreadyExistsException; use Neos\Flow\Package\Exception\UnknownPackageException; use Neos\Flow\Package\FlowPackageInterface; +use Neos\Flow\Package\FlowPackageKey; use Neos\Flow\Package\PackageFactory; use Neos\Flow\Package\PackageInterface; use org\bovigo\vfs\vfsStream; @@ -234,14 +235,15 @@ public function scanAvailablePackagesTraversesThePackagesDirectoryAndRespectsPac */ public function packageStatesConfigurationContainsRelativePaths() { + /** @var list $packageKeys */ $packageKeys = [ - 'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), true)), - 'Neos.Flow' . md5(uniqid(mt_rand(), true)), - 'Neos.YetAnotherTestPackage' . md5(uniqid(mt_rand(), true)), + FlowPackageKey::fromString('RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), true))), + FlowPackageKey::fromString('Neos.Flow' . md5(uniqid(mt_rand(), true))), + FlowPackageKey::fromString('Neos.YetAnotherTestPackage' . md5(uniqid(mt_rand(), true))), ]; foreach ($packageKeys as $packageKey) { - $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/'; + $packagePath = 'vfs://Test/Packages/Application/' . $packageKey->value . '/'; mkdir($packagePath, 0770, true); mkdir($packagePath . 'Classes'); @@ -252,7 +254,7 @@ public function packageStatesConfigurationContainsRelativePaths() $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/'); $packageManager->_set('packageInformationCacheFilePath', 'vfs://Test/Configuration/PackageStates.php'); - $packageFactory = new PackageFactory($packageManager); + $packageFactory = new PackageFactory(); $this->inject($packageManager, 'packageFactory', $packageFactory); $packageManager->_set('packages', []); @@ -260,12 +262,12 @@ public function packageStatesConfigurationContainsRelativePaths() $expectedPackageStatesConfiguration = []; foreach ($packageKeys as $packageKey) { - $composerName = ComposerUtility::getComposerPackageNameFromPackageKey($packageKey); + $composerName = $packageKey->deriveComposerPackageName(); $expectedPackageStatesConfiguration[$composerName] = [ - 'packagePath' => 'Application/' . $packageKey . '/', + 'packagePath' => 'Application/' . $packageKey->value . '/', 'composerName' => $composerName, 'packageClassInformation' => ['className' => 'Neos\Flow\Package\GenericPackage', 'pathAndFilename' => ''], - 'packageKey' => $packageKey, + 'packageKey' => $packageKey->value, 'autoloadConfiguration' => [] ]; } diff --git a/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php b/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php index 735d43351f..286870a6ac 100644 --- a/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php +++ b/Neos.FluidAdaptor/Classes/Core/Parser/Interceptor/ResourceInterceptor.php @@ -11,7 +11,7 @@ * source code. */ -use Neos\Flow\Package\Package; +use Neos\Flow\Package\FlowPackageKey; use Neos\FluidAdaptor\Core\Parser\SyntaxTree\ResourceUriNode; use TYPO3Fluid\Fluid\Core\Parser\InterceptorInterface; use TYPO3Fluid\Fluid\Core\Parser\ParsingState; @@ -53,7 +53,7 @@ class ResourceInterceptor implements InterceptorInterface * Is the text at hand a resource URI and what are path/package? * * @var string - * @see \Neos\Flow\Package\Package::PATTERN_MATCH_PACKAGEKEY + * @see \Neos\Flow\Package\FlowPackageKey::PATTERN */ const PATTERN_MATCH_RESOURCE_URI = '!(?:../)*(?:(?P[A-Za-z0-9]+\.(?:[A-Za-z0-9][\.a-z0-9]*)+)/Resources/)?Public/(?P[^"]+)!'; @@ -74,7 +74,7 @@ class ResourceInterceptor implements InterceptorInterface */ public function setDefaultPackageKey($defaultPackageKey) { - if (!preg_match(Package::PATTERN_MATCH_PACKAGEKEY, $defaultPackageKey)) { + if (!FlowPackageKey::isPackageKeyValid($defaultPackageKey)) { throw new \InvalidArgumentException('The given argument was not a valid package key.', 1277287099); } $this->defaultPackageKey = $defaultPackageKey; @@ -109,7 +109,7 @@ public function process(NodeInterface $node, $interceptorPosition, ParsingState if ($this->defaultPackageKey !== null) { $arguments['package'] = new TextNode($this->defaultPackageKey); } - if (isset($matches['Package']) && preg_match(Package::PATTERN_MATCH_PACKAGEKEY, $matches['Package'])) { + if (isset($matches['Package']) && FlowPackageKey::isPackageKeyValid($matches['Package'])) { $arguments['package'] = new TextNode($matches['Package']); }