/** * Constructor * * @param \TYPO3\Flow\Package\PackageManager $packageManager the package manager which knows this package * @param string $packageKey Key of this package * @param string $packagePath Absolute path to the location of the package's composer manifest * @param string $classesPath Path the classes of the package are in, relative to $packagePath. Optional, read from Composer manifest if not set. * @param string $manifestPath Path the composer manifest of the package, relative to $packagePath. Optional, defaults to ''. * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException if an invalid package key was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackagePathException if an invalid package path was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackageManifestException if no composer manifest file could be found */ public function __construct(\TYPO3\Flow\Package\PackageManager $packageManager, $packageKey, $packagePath, $classesPath = NULL, $manifestPath = '') { if (!$packageManager->isPackageKeyValid($packageKey)) { throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959511); } if (!(@is_dir($packagePath) || \TYPO3\Flow\Utility\Files::is_link($packagePath) && is_dir(\TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath)))) { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631890); } if (substr($packagePath, -1, 1) !== '/') { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633722); } if ($classesPath[1] === '/') { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package classes path provided for package "%s" has a leading forward slash.', $packageKey), 1334841321); } if (!@file_exists($packagePath . $manifestPath . 'ext_emconf.php')) { throw new \TYPO3\Flow\Package\Exception\InvalidPackageManifestException(sprintf('No ext_emconf file found for package "%s". Please create one at "%sext_emconf.php".', $packageKey, $manifestPath), 1360403545); } $this->packageManager = $packageManager; $this->manifestPath = $manifestPath; $this->packageKey = $packageKey; $this->packagePath = \TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath); $this->classesPath = \TYPO3\Flow\Utility\Files::getNormalizedPath(\TYPO3\Flow\Utility\Files::concatenatePaths(array($this->packagePath, self::DIRECTORY_CLASSES))); try { $this->getComposerManifest(); } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) { $this->getExtensionEmconf($packageKey, $this->packagePath); } $this->loadFlagsFromComposerManifest(); if ($this->objectManagementEnabled === NULL) { $this->objectManagementEnabled = FALSE; } }
/** * Returns a package instance. * * @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 string $classesPath path to the classes directory, relative to the package path * @param string $manifestPath path to the package's Composer manifest, relative to package path, defaults to same path * @return \TYPO3\Flow\Package\PackageInterface * @throws Exception\CorruptPackageException */ public function create($packagesBasePath, $packagePath, $packageKey, $classesPath = null, $manifestPath = null) { $absolutePackagePath = Files::concatenatePaths(array($packagesBasePath, $packagePath)) . '/'; $absoluteManifestPath = $manifestPath === null ? $absolutePackagePath : Files::concatenatePaths(array($absolutePackagePath, $manifestPath)) . '/'; $autoLoadDirectives = array(); try { $autoLoadDirectives = (array) PackageManager::getComposerManifest($absoluteManifestPath, 'autoload'); } catch (MissingPackageManifestException $exception) { } if (isset($autoLoadDirectives[Package::AUTOLOADER_TYPE_PSR4])) { $packageClassPathAndFilename = Files::concatenatePaths(array($absolutePackagePath, 'Classes', 'Package.php')); } else { $packageClassPathAndFilename = Files::concatenatePaths(array($absolutePackagePath, 'Classes', str_replace('.', '/', $packageKey), 'Package.php')); } $package = null; if (file_exists($packageClassPathAndFilename)) { require_once $packageClassPathAndFilename; $packageClassContents = file_get_contents($packageClassPathAndFilename); $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); } $package = new $packageClassName($this->packageManager, $packageKey, $absolutePackagePath, $classesPath, $manifestPath); if (!$package instanceof PackageInterface) { throw new Exception\CorruptPackageException(sprintf('The package class of package "%s" does not implement \\TYPO3\\Flow\\Package\\PackageInterface. Check the file "%s".', $packageKey, $packageClassPathAndFilename), 1427193370); } return $package; } return new Package($this->packageManager, $packageKey, $absolutePackagePath, $classesPath, $manifestPath); }
/** * @test */ public function unfreezePackageEmitsPackageStatesUpdatedSignal() { $this->mockApplicationContext->expects($this->atLeastOnce())->method('isDevelopment')->will($this->returnValue(true)); $this->packageManager->createPackage('Some.Package', null, null, null, ['name' => 'some/package', 'type' => 'typo3-flow-package']); $this->packageManager->freezePackage('Some.Package'); $this->mockDispatcher->expects($this->once())->method('dispatch')->with(PackageManager::class, 'packageStatesUpdated'); $this->packageManager->unfreezePackage('Some.Package'); }
/** * @test */ public function unfreezePackageEmitsPackageStatesUpdatedSignal() { $this->mockApplicationContext->expects($this->atLeastOnce())->method('isDevelopment')->will($this->returnValue(TRUE)); $this->packageManager->createPackage('Some.Package'); $this->packageManager->freezePackage('Some.Package'); $this->mockDispatcher->expects($this->once())->method('dispatch')->with('TYPO3\\Flow\\Package\\PackageManager', 'packageStatesUpdated'); $this->packageManager->unfreezePackage('Some.Package'); }
/** * Make sure required paths and files are available outside of Package * Run on every Composer install or update - must be configured in root manifest * * @param CommandEvent $event * @return void */ public static function postUpdateAndInstall(CommandEvent $event) { if (!defined('FLOW_PATH_ROOT')) { define('FLOW_PATH_ROOT', getcwd() . '/'); } if (!defined('FLOW_PATH_PACKAGES')) { define('FLOW_PATH_PACKAGES', getcwd() . '/Packages/'); } if (!defined('FLOW_PATH_CONFIGURATION')) { define('FLOW_PATH_CONFIGURATION', getcwd() . '/Configuration/'); } Files::createDirectoryRecursively('Configuration'); Files::createDirectoryRecursively('Data'); Files::copyDirectoryRecursively('Packages/Framework/TYPO3.Flow/Resources/Private/Installer/Distribution/Essentials', './', false, true); Files::copyDirectoryRecursively('Packages/Framework/TYPO3.Flow/Resources/Private/Installer/Distribution/Defaults', './', true, true); $packageManager = new PackageManager(); $packageManager->rescanPackages(); chmod('flow', 0755); }
/** * @test */ public function deletePackageRemovesPackageFromAvailableAndActivePackagesAndDeletesThePackageDirectory() { $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage'); $packagePath = $package->getPackagePath(); $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA)); $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage')); $this->assertTrue($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage')); $this->packageManager->deletePackage('Acme.YetAnotherTestPackage'); $this->assertFalse(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA)); $this->assertFalse($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage')); $this->assertFalse($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage')); }
/** * Returns the package meta data object of this package. * * @return \TYPO3\Flow\Package\MetaData */ public function getPackageMetaData() { if ($this->packageMetaData === NULL) { $this->packageMetaData = new MetaData($this->getPackageKey()); $this->packageMetaData->setDescription($this->getComposerManifest('description')); $this->packageMetaData->setVersion($this->getComposerManifest('version')); $requirements = $this->getComposerManifest('require'); if ($requirements !== NULL) { foreach ($requirements as $requirement => $version) { if ($this->packageRequirementIsComposerPackage($requirement) === FALSE) { // Skip non-package requirements continue; } $packageKey = $this->packageManager->getPackageKeyFromComposerName($requirement); $constraint = new MetaData\PackageConstraint(MetaDataInterface::CONSTRAINT_TYPE_DEPENDS, $packageKey); $this->packageMetaData->addConstraint($constraint); } } } return $this->packageMetaData; }
/** * Returns contents of Composer manifest - or part there of. * * @param string $key Optional. Only return the part of the manifest indexed by 'key' * @return mixed|NULL * @see json_decode for return values */ public function getComposerManifest($key = null) { if (!isset($this->composerManifest)) { $this->composerManifest = PackageManager::getComposerManifest($this->getManifestPath()); } return PackageManager::getComposerManifest($this->getManifestPath(), $key, $this->composerManifest); }
/** * Returns the package meta data object of this package. * * @return \TYPO3\Flow\Package\MetaData */ public function getPackageMetaData() { if ($this->packageMetaData === null) { $this->packageMetaData = new MetaData($this->getPackageKey()); $this->packageMetaData->setDescription($this->getComposerManifest('description')); $this->packageMetaData->setVersion($this->getComposerManifest('version')); $this->packageMetaData->setPackageType($this->getComposerManifest('type')); $requirements = $this->getComposerManifest('require'); if ($requirements !== null) { foreach ($requirements as $requirement => $version) { if ($this->packageRequirementIsComposerPackage($requirement) === false) { // Skip non-package requirements continue; } try { $packageKey = $this->packageManager->getPackageKeyFromComposerName($requirement); } catch (Exception\InvalidPackageStateException $exception) { continue; } $constraint = new MetaData\PackageConstraint(MetaDataInterface::CONSTRAINT_TYPE_DEPENDS, $packageKey); $this->packageMetaData->addConstraint($constraint); } } } return $this->packageMetaData; }
/** * @test */ public function getPackageByClassNameReturnsNullForClassesThatAreNotPartOfAPackage() { $globalClassName = 'stdClass'; $this->assertNull($this->packageManager->getPackageByClassName($globalClassName)); }
/** * Get the installed package version (from composer) and as fallback the version given by composer manifest. * * @return string * @api * TODO: Should be added to the interface in the next major Flow version (4.0) */ public function getInstalledVersion() { return PackageManager::getPackageVersion($this->composerName) ?: $this->getComposerManifest('version'); }
/** * Check the conformance of the given package key * * @param string $packageKey The package key to validate * @return boolean If the package key is valid, returns TRUE otherwise FALSE * @api */ public function isPackageKeyValid($packageKey) { return parent::isPackageKeyValid($packageKey) || preg_match(\TYPO3\CMS\Core\Package\Package::PATTERN_MATCH_EXTENSIONKEY, $packageKey) === 1; }
/** * @test * @expectedException \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException */ public function registeringTheSamePackageKeyWithDifferentCaseShouldThrowException() { $this->packageManager->createPackage('doctrine.instantiator'); $this->packageManager->createPackage('doctrine.Instantiator'); }
/** * Get the installed package version (from composer) * * @return string * @api * TODO: Should be added to the interface in the next major Flow version (4.0) */ public function getInstalledVersion() { return PackageManager::getPackageVersion($this->composerName); }