Meant to be used only inside the Flow package management code.
 /**
  */
 public function setUp()
 {
     ComposerUtility::flushCaches();
     vfsStream::setup('Packages');
     $this->mockPackageManager = $this->getMockBuilder(\Neos\Flow\Package\PackageManager::class)->disableOriginalConstructor()->getMock();
     ObjectAccess::setProperty($this->mockPackageManager, 'composerManifestData', array(), true);
 }
 /**
  * Detects if the package contains a package file and returns the path and classname.
  *
  * @param string $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.
  * @throws Exception\CorruptPackageException
  * @throws Exception\InvalidPackagePathException
  */
 public function detectFlowPackageFilePath($packageKey, $absolutePackagePath)
 {
     if (!is_dir($absolutePackagePath)) {
         throw new Exception\InvalidPackagePathException(sprintf('The given package path "%s" is not a readable directory.', $absolutePackagePath), 1445904440);
     }
     $composerManifest = ComposerUtility::getComposerManifest($absolutePackagePath);
     if (!ComposerUtility::isFlowPackageType(isset($composerManifest['type']) ? $composerManifest['type'] : '')) {
         return [];
     }
     $possiblePackageClassPaths = [Files::concatenatePaths(['Classes', 'Package.php']), Files::concatenatePaths(['Classes', str_replace('.', '/', $packageKey), 'Package.php'])];
     $foundPackageClassPaths = array_filter($possiblePackageClassPaths, function ($packageClassPathAndFilename) use($absolutePackagePath) {
         $absolutePackageClassPath = Files::concatenatePaths([$absolutePackagePath, $packageClassPathAndFilename]);
         return is_file($absolutePackageClassPath);
     });
     if ($foundPackageClassPaths === []) {
         return [];
     }
     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);
     }
     $packageClassPathAndFilename = reset($foundPackageClassPaths);
     $absolutePackageClassPath = Files::concatenatePaths([$absolutePackagePath, $packageClassPathAndFilename]);
     $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);
     }
     return ['className' => $packageClassName, 'pathAndFilename' => $packageClassPathAndFilename];
 }
 /**
  * Create a new package
  *
  * This command creates a new package which contains only the mandatory
  * directories and files.
  *
  * @Flow\FlushesCaches
  * @param string $packageKey The package key of the package to create
  * @param string $packageType The package type of the package to create
  * @return string
  * @see neos.kickstarter:kickstart:package
  */
 public function createCommand($packageKey, $packageType = PackageInterface::DEFAULT_COMPOSER_TYPE)
 {
     if (!$this->packageManager->isPackageKeyValid($packageKey)) {
         $this->outputLine('The package key "%s" is not valid.', [$packageKey]);
         $this->quit(1);
     }
     if ($this->packageManager->isPackageAvailable($packageKey)) {
         $this->outputLine('The package "%s" already exists.', [$packageKey]);
         $this->quit(1);
     }
     if (!ComposerUtility::isFlowPackageType($packageType)) {
         $this->outputLine('The package must be a Flow package, but "%s" is not a valid Flow package type.', [$packageType]);
         $this->quit(1);
     }
     $package = $this->packageManager->createPackage($packageKey, ['type' => $packageType], null);
     $this->outputLine('Created new package "' . $packageKey . '" at "' . $package->getPackagePath() . '".');
 }
 /**
  * Returns contents of Composer manifest - or part there of.
  *
  * @param string $key Optional. Only return the part of the manifest indexed by 'key'
  * @return array|mixed
  * @api
  */
 public function getComposerManifest($key = null)
 {
     return ComposerUtility::getComposerManifest($this->packagePath, $key);
 }
 /**
  * @test
  */
 public function packageStatesConfigurationContainsRelativePaths()
 {
     $packageKeys = ['RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), true)), 'Neos.Flow' . md5(uniqid(mt_rand(), true)), 'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), true))];
     foreach ($packageKeys as $packageKey) {
         $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
         mkdir($packagePath, 0770, true);
         mkdir($packagePath . 'Classes');
         ComposerUtility::writeComposerManifest($packagePath, $packageKey, ['type' => 'flow-test', 'autoload' => []]);
     }
     $packageManager = $this->getAccessibleMock(PackageManager::class, ['updateShortcuts', 'emitPackageStatesUpdated'], [], '', false);
     $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
     $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
     $packageFactory = new PackageFactory($packageManager);
     $this->inject($packageManager, 'packageFactory', $packageFactory);
     $packageManager->_set('packages', []);
     $actualPackageStatesConfiguration = $packageManager->rescanPackages();
     $expectedPackageStatesConfiguration = [];
     foreach ($packageKeys as $packageKey) {
         $composerName = ComposerUtility::getComposerPackageNameFromPackageKey($packageKey);
         $expectedPackageStatesConfiguration[$composerName] = ['state' => 'active', 'packagePath' => 'Application/' . $packageKey . '/', 'composerName' => $composerName, 'packageClassInformation' => [], 'packageKey' => $packageKey, 'autoloadConfiguration' => []];
     }
     $this->assertEquals($expectedPackageStatesConfiguration, $actualPackageStatesConfiguration['packages']);
 }
 /**
  * Traverses through all class files of the active packages and registers collects the class names as
  * "all available class names". If the respective Flow settings say so, also function test classes
  * are registered.
  *
  * For performance reasons this function ignores classes whose name ends with "Exception".
  *
  * @param array $packages A list of packages to consider
  * @return array A list of class names which were discovered in the given packages
  *
  * @throws InvalidConfigurationTypeException
  */
 protected function registerClassFiles(array $packages)
 {
     $includeClassesConfiguration = [];
     if (isset($this->allSettings['Neos']['Flow']['object']['includeClasses'])) {
         if (!is_array($this->allSettings['Neos']['Flow']['object']['includeClasses'])) {
             throw new InvalidConfigurationTypeException('The setting "Neos.Flow.object.includeClasses" is invalid, it must be an array if set. Check the syntax in the YAML file.', 1422357285);
         }
         $includeClassesConfiguration = $this->allSettings['Neos']['Flow']['object']['includeClasses'];
     }
     $availableClassNames = ['' => ['DateTime']];
     /** @var \Neos\Flow\Package\Package $package */
     foreach ($packages as $packageKey => $package) {
         if ($package->isObjectManagementEnabled() && (ComposerUtility::isFlowPackageType($package->getComposerManifest('type')) || isset($includeClassesConfiguration[$packageKey]))) {
             foreach ($package->getClassFiles() as $fullClassName => $path) {
                 if (substr($fullClassName, -9, 9) !== 'Exception') {
                     $availableClassNames[$packageKey][] = $fullClassName;
                 }
             }
             if (isset($this->allSettings['Neos']['Flow']['object']['registerFunctionalTestClasses']) && $this->allSettings['Neos']['Flow']['object']['registerFunctionalTestClasses'] === true) {
                 foreach ($package->getFunctionalTestsClassFiles() as $fullClassName => $path) {
                     if (substr($fullClassName, -9, 9) !== 'Exception') {
                         $availableClassNames[$packageKey][] = $fullClassName;
                     }
                 }
             }
             if (isset($availableClassNames[$packageKey]) && is_array($availableClassNames[$packageKey])) {
                 $availableClassNames[$packageKey] = array_unique($availableClassNames[$packageKey]);
             }
         }
     }
     return $this->filterClassNamesFromConfiguration($availableClassNames, $includeClassesConfiguration);
 }
 /**
  * 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($composerName, $packageType = null, $packagePath = null, $autoloadNamespace = null)
 {
     $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;
 }