TYPO3 CMS  TYPO3_6-2
PackageManager.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\Flow\Package;
3 
4 /* *
5  * This script belongs to the TYPO3 Flow framework. *
6  * *
7  * It is free software; you can redistribute it and/or modify it under *
8  * the terms of the GNU Lesser General Public License, either version 3 *
9  * of the License, or (at your option) any later version. *
10  * *
11  * The TYPO3 project - inspiring people to share! *
12  * */
13 
18 use TYPO3\Flow\Annotations as Flow;
19 
27 
31  protected $classLoader;
32 
36  protected $bootstrap;
37 
41  protected $packageFactory;
42 
47  protected $packages = array();
48 
53  protected $packageKeys = array();
54 
59  protected $composerNameToPackageKeyMap = array();
60 
65  protected $activePackages = array();
66 
71  protected $packagesBasePath = FLOW_PATH_PACKAGES;
72 
77 
82  protected $packageStatesConfiguration = array();
83 
87  protected $settings;
88 
92  protected $systemLogger;
93 
98  public function injectClassLoader(\TYPO3\Flow\Core\ClassLoader $classLoader) {
99  $this->classLoader = $classLoader;
100  }
101 
106  public function injectSettings(array $settings) {
107  $this->settings = $settings;
108  }
109 
114  public function injectSystemLogger(\TYPO3\Flow\Log\SystemLoggerInterface $systemLogger) {
115  if ($this->systemLogger instanceof \TYPO3\Flow\Log\EarlyLogger) {
116  $this->systemLogger->replayLogsOn($systemLogger);
117  unset($this->systemLogger);
118  }
119  $this->systemLogger = $systemLogger;
120  }
121 
128  public function initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap) {
129  $this->systemLogger = new \TYPO3\Flow\Log\EarlyLogger();
130 
131  $this->bootstrap = $bootstrap;
132  $this->packageStatesPathAndFilename = $this->packageStatesPathAndFilename ?: FLOW_PATH_CONFIGURATION . 'PackageStates.php';
133  $this->packageFactory = new PackageFactory($this);
134 
135  $this->loadPackageStates();
136 
137  foreach ($this->packages as $packageKey => $package) {
138  if ($package->isProtected() || (isset($this->packageStatesConfiguration['packages'][$packageKey]['state']) && $this->packageStatesConfiguration['packages'][$packageKey]['state'] === 'active')) {
139  $this->activePackages[$packageKey] = $package;
140  }
141  }
142 
143  $this->classLoader->setPackages($this->activePackages);
144 
145  foreach ($this->activePackages as $package) {
146  $package->boot($bootstrap);
147  }
148 
149  }
150 
159  public function isPackageAvailable($packageKey) {
160  return (isset($this->packages[$packageKey]));
161  }
162 
170  public function isPackageActive($packageKey) {
171  return (isset($this->activePackages[$packageKey]));
172  }
173 
179  public function getPackagesBasePath() {
181  }
182 
192  public function getPackage($packageKey) {
193  if (!$this->isPackageAvailable($packageKey)) {
194  throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available. Please check if the package exists and that the package key is correct (package keys are case sensitive).', 1166546734);
195  }
196  return $this->packages[$packageKey];
197  }
198 
211  public function getPackageOfObject($object) {
212  $sortedAvailablePackages = $this->getAvailablePackages();
213  usort($sortedAvailablePackages, function (PackageInterface $packageOne, PackageInterface $packageTwo) {
214  return strlen($packageTwo->getPackagePath()) - strlen($packageOne->getPackagePath());
215  });
216 
217  $className = $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->getClassNameByObject($object);
218  $reflectedClass = new \ReflectionClass($className);
219  $fileName = Files::getUnixStylePath($reflectedClass->getFileName());
220 
221  foreach ($sortedAvailablePackages as $package) {
222  $packagePath = Files::getUnixStylePath($package->getPackagePath());
223  if (strpos($fileName, $packagePath) === 0) {
224  return $package;
225  }
226  }
227  return NULL;
228  }
229 
237  public function getAvailablePackages() {
238  return $this->packages;
239  }
240 
249  public function getActivePackages() {
250  return $this->activePackages;
251  }
252 
260  public function getFrozenPackages() {
261  $frozenPackages = array();
262  if ($this->bootstrap->getContext()->isDevelopment()) {
263  foreach ($this->packages as $packageKey => $package) {
264  if (isset($this->packageStatesConfiguration['packages'][$packageKey]['frozen']) &&
265  $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] === TRUE) {
266  $frozenPackages[$packageKey] = $package;
267  }
268  }
269  }
270  return $frozenPackages;
271  }
272 
285  public function getFilteredPackages($packageState = 'available', $packagePath = NULL, $packageType = NULL) {
286  $packages = array();
287  switch (strtolower($packageState)) {
288  case 'available':
289  $packages = $this->getAvailablePackages();
290  break;
291  case 'active':
292  $packages = $this->getActivePackages();
293  break;
294  case 'frozen':
295  $packages = $this->getFrozenPackages();
296  break;
297  default:
298  throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException('The package state "' . $packageState . '" is invalid', 1372458274);
299  }
300 
301  if($packagePath !== NULL) {
302  $packages = $this->filterPackagesByPath($packages, $packagePath);
303  }
304  if($packageType !== NULL) {
305  $packages = $this->filterPackagesByType($packages, $packageType);
306  }
307 
308  return $packages;
309  }
310 
319  protected function filterPackagesByPath(&$packages, $filterPath) {
320  $filteredPackages = array();
322  foreach ($packages as $package) {
323  $packagePath = substr($package->getPackagePath(), strlen(FLOW_PATH_PACKAGES));
324  $packageGroup = substr($packagePath, 0, strpos($packagePath, '/'));
325  if ($packageGroup === $filterPath) {
326  $filteredPackages[$package->getPackageKey()] = $package;
327  }
328  }
329  return $filteredPackages;
330  }
331 
340  protected function filterPackagesByType(&$packages, $packageType) {
341  $filteredPackages = array();
343  foreach ($packages as $package) {
344  if ($package->getComposerManifest('type') === $packageType) {
345  $filteredPackages[$package->getPackageKey()] = $package;
346  }
347  }
348  return $filteredPackages;
349  }
350 
359  public function getCaseSensitivePackageKey($unknownCasedPackageKey) {
360  $lowerCasedPackageKey = strtolower($unknownCasedPackageKey);
361  return (isset($this->packageKeys[$lowerCasedPackageKey])) ? $this->packageKeys[$lowerCasedPackageKey] : FALSE;
362  }
363 
371  public function getPackageKeyFromComposerName($composerName) {
372  if (count($this->composerNameToPackageKeyMap) === 0) {
373  foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $packageStateConfiguration) {
374  $this->composerNameToPackageKeyMap[strtolower($packageStateConfiguration['composerName'])] = $packageKey;
375  }
376  }
377  $lowercasedComposerName = strtolower($composerName);
378  if (!isset($this->composerNameToPackageKeyMap[$lowercasedComposerName])) {
379  throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException('Could not find package with composer name "' . $composerName . '" in PackageStates configuration.', 1352320649);
380  }
381  return $this->composerNameToPackageKeyMap[$lowercasedComposerName];
382  }
383 
391  public function isPackageKeyValid($packageKey) {
392  return preg_match(PackageInterface::PATTERN_MATCH_PACKAGEKEY, $packageKey) === 1;
393  }
394 
408  public function createPackage($packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData = NULL, $packagesPath = NULL, $packageType = 'typo3-flow-package') {
409  if (!$this->isPackageKeyValid($packageKey)) {
410  throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('The package key "' . $packageKey . '" is invalid', 1220722210);
411  }
412  if ($this->isPackageAvailable($packageKey)) {
413  throw new \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException('The package key "' . $packageKey . '" already exists', 1220722873);
414  }
415 
416  if ($packagesPath === NULL) {
417  if (is_array($this->settings['package']['packagesPathByType']) && isset($this->settings['package']['packagesPathByType'][$packageType])) {
418  $packagesPath = $this->settings['package']['packagesPathByType'][$packageType];
419  } else {
420  $packagesPath = 'Application';
421  }
422  $packagesPath = Files::getUnixStylePath(Files::concatenatePaths(array($this->packagesBasePath, $packagesPath)));
423  }
424 
425  if ($packageMetaData === NULL) {
426  $packageMetaData = new MetaData($packageKey);
427  }
428  if ($packageMetaData->getPackageType() === NULL) {
429  $packageMetaData->setPackageType($packageType);
430  }
431 
432  $packagePath = Files::concatenatePaths(array($packagesPath, $packageKey)) . '/';
433  Files::createDirectoryRecursively($packagePath);
434 
435  foreach (
436  array(
444  ) as $path) {
445  Files::createDirectoryRecursively(Files::concatenatePaths(array($packagePath, $path)));
446  }
447 
448  $this->writeComposerManifest($packagePath, $packageKey, $packageMetaData);
449 
450  $packagePath = str_replace($this->packagesBasePath, '', $packagePath);
451  $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageKey, PackageInterface::DIRECTORY_CLASSES);
452 
453  $this->packages[$packageKey] = $package;
454  foreach ($this->packages as $upperCamelCasedPackageKey => $_) {
455  $this->packageKeys[strtolower($upperCamelCasedPackageKey)] = $upperCamelCasedPackageKey;
456  }
457 
458  $this->activatePackage($packageKey);
459 
460  return $package;
461  }
462 
471  protected function writeComposerManifest($manifestPath, $packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData = NULL) {
472  $manifest = array();
473 
474  $nameParts = explode('.', $packageKey);
475  $vendor = array_shift($nameParts);
476  $manifest['name'] = strtolower($vendor . '/' . implode('-', $nameParts));
477  if ($packageMetaData !== NULL) {
478  $manifest['type'] = $packageMetaData->getPackageType();
479  $manifest['description'] = $packageMetaData->getDescription();
480  $manifest['version'] = $packageMetaData->getVersion();
481  } else {
482  $manifest['type'] = 'typo3-flow-package';
483  $manifest['description'] = '';
484  }
485  $manifest['require'] = array('typo3/flow' => '*');
486  $manifest['autoload'] = array('psr-0' => array(str_replace('.', '\\', $packageKey) => 'Classes'));
487 
488  if (defined('JSON_PRETTY_PRINT')) {
489  file_put_contents(Files::concatenatePaths(array($manifestPath, 'composer.json')), json_encode($manifest, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
490  } else {
491  file_put_contents(Files::concatenatePaths(array($manifestPath, 'composer.json')), json_encode($manifest));
492  }
493  }
494 
503  public function deactivatePackage($packageKey) {
504  if (!$this->isPackageActive($packageKey)) {
505  return;
506  }
507 
508  $package = $this->getPackage($packageKey);
509  if ($package->isProtected()) {
510  throw new \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException('The package "' . $packageKey . '" is protected and cannot be deactivated.', 1308662891);
511  }
512 
513  unset($this->activePackages[$packageKey]);
514  $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
515  $this->sortAndSavePackageStates();
516  }
517 
525  public function activatePackage($packageKey) {
526  if ($this->isPackageActive($packageKey)) {
527  return;
528  }
529 
530  $package = $this->getPackage($packageKey);
531  $this->activePackages[$packageKey] = $package;
532  $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'active';
533  if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['packagePath'])) {
534  $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $package->getPackagePath());
535  }
536  if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['classesPath'])) {
537  $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
538  }
539  $this->sortAndSavePackageStates();
540  }
541 
550  public function freezePackage($packageKey) {
551  if (!$this->bootstrap->getContext()->isDevelopment()) {
552  throw new \LogicException('Package freezing is only supported in Development context.', 1338810870);
553  }
554 
555  if (!$this->isPackageActive($packageKey)) {
556  throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available or active.', 1331715956);
557  }
558  if ($this->isPackageFrozen($packageKey)) {
559  return;
560  }
561 
562  $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->freezePackageReflection($packageKey);
563 
564  $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] = TRUE;
565  $this->sortAndSavePackageStates();
566  }
567 
574  public function isPackageFrozen($packageKey) {
575  return (
576  $this->bootstrap->getContext()->isDevelopment()
577  && isset($this->packageStatesConfiguration['packages'][$packageKey]['frozen'])
578  && $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] === TRUE
579  );
580  }
581 
588  public function unfreezePackage($packageKey) {
589  if (!$this->isPackageFrozen($packageKey)) {
590  return;
591  }
592 
593  $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->unfreezePackageReflection($packageKey);
594 
595  unset($this->packageStatesConfiguration['packages'][$packageKey]['frozen']);
596  $this->sortAndSavePackageStates();
597  }
598 
605  public function refreezePackage($packageKey) {
606  if (!$this->isPackageFrozen($packageKey)) {
607  return;
608  }
609 
610  $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->unfreezePackageReflection($packageKey);
611  }
612 
621  public function registerPackage(PackageInterface $package, $sortAndSave = TRUE) {
622  $packageKey = $package->getPackageKey();
623  if ($this->isPackageAvailable($packageKey)) {
624  throw new Exception\InvalidPackageStateException('Package "' . $packageKey . '" is already registered.', 1338996122);
625  }
626 
627  $this->packages[$packageKey] = $package;
628  $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $package->getPackagePath());
629  $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = str_replace($package->getPackagePath(), '', $package->getClassesPath());
630 
631  if ($sortAndSave === TRUE) {
632  $this->sortAndSavePackageStates();
633  }
634 
635  return $package;
636  }
637 
645  public function unregisterPackage(PackageInterface $package) {
646  $packageKey = $package->getPackageKey();
647  if (!$this->isPackageAvailable($packageKey)) {
648  throw new Exception\InvalidPackageStateException('Package "' . $packageKey . '" is not registered.', 1338996142);
649  }
650  $this->unregisterPackageByPackageKey($packageKey);
651  }
652 
659  protected function unregisterPackageByPackageKey($packageKey) {
660  unset($this->packages[$packageKey]);
661  unset($this->packageKeys[strtolower($packageKey)]);
662  unset($this->packageStatesConfiguration['packages'][$packageKey]);
663  $this->sortAndSavePackageStates();
664  }
665 
676  public function deletePackage($packageKey) {
677  if (!$this->isPackageAvailable($packageKey)) {
678  throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available and cannot be removed.', 1166543253);
679  }
680 
681  $package = $this->getPackage($packageKey);
682  if ($package->isProtected()) {
683  throw new \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException('The package "' . $packageKey . '" is protected and cannot be removed.', 1220722120);
684  }
685 
686  if ($this->isPackageActive($packageKey)) {
687  $this->deactivatePackage($packageKey);
688  }
689 
690  $packagePath = $package->getPackagePath();
691  try {
692  Files::removeDirectoryRecursively($packagePath);
693  } catch (\TYPO3\Flow\Utility\Exception $exception) {
694  throw new \TYPO3\Flow\Package\Exception('Please check file permissions. The directory "' . $packagePath . '" for package "' . $packageKey . '" could not be removed.', 1301491089, $exception);
695  }
696 
697  $this->unregisterPackage($package);
698  }
699 
706  protected function loadPackageStates() {
707  $this->packageStatesConfiguration = file_exists($this->packageStatesPathAndFilename) ? include($this->packageStatesPathAndFilename) : array();
708  if (!isset($this->packageStatesConfiguration['version']) || $this->packageStatesConfiguration['version'] < 4) {
709  $this->packageStatesConfiguration = array();
710  }
711  if ($this->packageStatesConfiguration === array() || !$this->bootstrap->getContext()->isProduction()) {
712  $this->scanAvailablePackages();
713  } else {
715  }
716  }
717 
725  protected function scanAvailablePackages() {
726  $previousPackageStatesConfiguration = $this->packageStatesConfiguration;
727 
728  if (isset($this->packageStatesConfiguration['packages'])) {
729  foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $configuration) {
730  if (!file_exists($this->packagesBasePath . $configuration['packagePath'])) {
731  unset($this->packageStatesConfiguration['packages'][$packageKey]);
732  }
733  }
734  } else {
735  $this->packageStatesConfiguration['packages'] = array();
736  }
737 
738  $packagePaths = array();
739  foreach (new \DirectoryIterator($this->packagesBasePath) as $parentFileInfo) {
740  $parentFilename = $parentFileInfo->getFilename();
741  if ($parentFilename[0] !== '.' && $parentFileInfo->isDir()) {
742  $packagePaths = array_merge($packagePaths, $this->scanPackagesInPath($parentFileInfo->getPathName()));
743  }
744  }
745 
749  foreach ($packagePaths as $packagePath => $composerManifestPath) {
750  try {
751  $composerManifest = self::getComposerManifest($composerManifestPath);
752  $packageKey = PackageFactory::getPackageKeyFromManifest($composerManifest, $packagePath, $this->packagesBasePath);
753  $this->composerNameToPackageKeyMap[strtolower($composerManifest->name)] = $packageKey;
754  $this->packageStatesConfiguration['packages'][$packageKey]['manifestPath'] = substr($composerManifestPath, strlen($packagePath)) ?: '';
755  $this->packageStatesConfiguration['packages'][$packageKey]['composerName'] = $composerManifest->name;
756  } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) {
757  $relativePackagePath = substr($packagePath, strlen($this->packagesBasePath));
758  $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
759  }
760  if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['state'])) {
764  if (is_array($this->settings['package']['inactiveByDefault']) && in_array($packageKey, $this->settings['package']['inactiveByDefault'], TRUE)) {
765  $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
766  } else {
767  $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'active';
768  }
769  }
770 
771  $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $packagePath);
772 
773  // Change this to read the target from Composer or any other source
774  $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
775  }
776 
778  if ($this->packageStatesConfiguration != $previousPackageStatesConfiguration) {
779  $this->sortAndsavePackageStates();
780  }
781  }
782 
789  protected function findComposerManifestPaths($packagePath) {
790  $manifestPaths = array();
791  if (file_exists($packagePath . '/composer.json')) {
792  $manifestPaths[] = $packagePath . '/';
793  } else {
794  $jsonPathsAndFilenames = Files::readDirectoryRecursively($packagePath, '.json');
795  asort($jsonPathsAndFilenames);
796  while (list($unusedKey, $jsonPathAndFilename) = each($jsonPathsAndFilenames)) {
797  if (basename($jsonPathAndFilename) === 'composer.json') {
798  $manifestPath = dirname($jsonPathAndFilename) . '/';
799  $manifestPaths[] = $manifestPath;
800  $isNotSubPathOfManifestPath = function ($otherPath) use ($manifestPath) {
801  return strpos($otherPath, $manifestPath) !== 0;
802  };
803  $jsonPathsAndFilenames = array_filter($jsonPathsAndFilenames, $isNotSubPathOfManifestPath);
804  }
805  }
806  }
807 
808  return $manifestPaths;
809  }
810 
820  protected function scanPackagesInPath($startPath, array &$collectedPackagePaths = array()) {
821  foreach (new \DirectoryIterator($startPath) as $fileInfo) {
822  if (!$fileInfo->isDir()) {
823  continue;
824  }
825  $filename = $fileInfo->getFilename();
826  if ($filename[0] !== '.') {
827  $currentPath = Files::getUnixStylePath($fileInfo->getPathName());
828  $composerManifestPaths = $this->findComposerManifestPaths($currentPath);
829  foreach ($composerManifestPaths as $composerManifestPath) {
830  $targetDirectory = rtrim(self::getComposerManifest($composerManifestPath, 'target-dir'), '/');
831  $packagePath = $targetDirectory ? substr(rtrim($composerManifestPath, '/'), 0, -strlen((string)$targetDirectory)) : $composerManifestPath;
832  $collectedPackagePaths[$packagePath] = $composerManifestPath;
833  }
834  }
835  }
836  return $collectedPackagePaths;
837  }
838 
849  static public function getComposerManifest($manifestPath, $key = NULL, $composerManifest = NULL) {
850  if ($composerManifest === NULL) {
851  if (!file_exists($manifestPath . 'composer.json')) {
852  throw new \TYPO3\Flow\Package\Exception\MissingPackageManifestException('No composer manifest file found at "' . $manifestPath . '/composer.json".', 1349868540);
853  }
854  $json = file_get_contents($manifestPath . 'composer.json');
855  $composerManifest = json_decode($json);
856  }
857 
858  if ($key !== NULL) {
859  if (isset($composerManifest->{$key})) {
860  $value = $composerManifest->{$key};
861  } else {
862  $value = NULL;
863  }
864  } else {
865  $value = $composerManifest;
866  }
867  return $value;
868  }
869 
876  protected function registerPackagesFromConfiguration() {
877  foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $stateConfiguration) {
878 
879  $packagePath = isset($stateConfiguration['packagePath']) ? $stateConfiguration['packagePath'] : NULL;
880  $classesPath = isset($stateConfiguration['classesPath']) ? $stateConfiguration['classesPath'] : NULL;
881  $manifestPath = isset($stateConfiguration['manifestPath']) ? $stateConfiguration['manifestPath'] : NULL;
882 
883  try {
884  $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath);
885  } catch (\TYPO3\Flow\Package\Exception\InvalidPackagePathException $exception) {
886  $this->unregisterPackageByPackageKey($packageKey);
887  $this->systemLogger->log('Package ' . $packageKey . ' could not be loaded, it has been unregistered. Error description: "' . $exception->getMessage() . '" (' . $exception->getCode() . ')', LOG_WARNING);
888  continue;
889  }
890 
891  $this->registerPackage($package, FALSE);
892 
893  if (!$this->packages[$packageKey] instanceof PackageInterface) {
894  throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package class in package "%s" does not implement PackageInterface.', $packageKey), 1300782487);
895  }
896 
897  $this->packageKeys[strtolower($packageKey)] = $packageKey;
898  if ($stateConfiguration['state'] === 'active') {
899  $this->activePackages[$packageKey] = $this->packages[$packageKey];
900  }
901  }
902  }
903 
910  protected function sortAndSavePackageStates() {
912 
913  $this->packageStatesConfiguration['version'] = 4;
914 
915  $fileDescription = "# PackageStates.php\n\n";
916  $fileDescription .= "# This file is maintained by Flow's package management. Although you can edit it\n";
917  $fileDescription .= "# manually, you should rather use the command line commands for maintaining packages.\n";
918  $fileDescription .= "# You'll find detailed information about the typo3.flow:package:* commands in their\n";
919  $fileDescription .= "# respective help screens.\n\n";
920  $fileDescription .= "# This file will be regenerated automatically if it doesn't exist. Deleting this file\n";
921  $fileDescription .= "# should, however, never become necessary if you use the package commands.\n";
922 
923  // we do not need the dependencies on disk...
924  foreach ($this->packageStatesConfiguration['packages'] as &$packageConfiguration) {
925  if (isset($packageConfiguration['dependencies'])) {
926  unset($packageConfiguration['dependencies']);
927  }
928  }
929  if (!@is_writable($this->packageStatesPathAndFilename)) {
930  // If file does not exists try to create it
931  $fileHandle = @fopen($this->packageStatesPathAndFilename, 'x');
932  if (!$fileHandle) {
933  throw new \TYPO3\Flow\Package\Exception\PackageStatesFileNotWritableException(
934  sprintf('We could not update the list of installed packages because the file %s is not writable. Please, check the file system permissions for this file and make sure that the web server can update it.', $this->packageStatesPathAndFilename),
935  1382449759
936  );
937  }
938  fclose($fileHandle);
939  }
940  $packageStatesCode = "<?php\n$fileDescription\nreturn " . var_export($this->packageStatesConfiguration, TRUE) . "\n ?>";
941  @file_put_contents($this->packageStatesPathAndFilename, $packageStatesCode);
942  }
943 
951  protected function resolvePackageDependencies() {
952  foreach ($this->packages as $packageKey => $package) {
953  $this->packageStatesConfiguration['packages'][$packageKey]['dependencies'] = $this->getDependencyArrayForPackage($packageKey);
954  }
955  }
956 
968  protected function getDependencyArrayForPackage($packageKey, array &$dependentPackageKeys = array(), array $trace = array()) {
969  if (!isset($this->packages[$packageKey])) {
970  return NULL;
971  }
972  if (in_array($packageKey, $trace) !== FALSE) {
973  return $dependentPackageKeys;
974  }
975  $trace[] = $packageKey;
976  $dependentPackageConstraints = $this->packages[$packageKey]->getPackageMetaData()->getConstraintsByType(MetaDataInterface::CONSTRAINT_TYPE_DEPENDS);
977  foreach ($dependentPackageConstraints as $constraint) {
978  if ($constraint instanceof \TYPO3\Flow\Package\MetaData\PackageConstraint) {
979  $dependentPackageKey = $constraint->getValue();
980  if (in_array($dependentPackageKey, $dependentPackageKeys) === FALSE && in_array($dependentPackageKey, $trace) === FALSE) {
981  $dependentPackageKeys[] = $dependentPackageKey;
982  }
983  $this->getDependencyArrayForPackage($dependentPackageKey, $dependentPackageKeys, $trace);
984  }
985  }
986  return array_reverse($dependentPackageKeys);
987  }
988 
996  protected function sortAvailablePackagesByDependencies() {
998 
999  $packageStatesConfiguration = $this->packageStatesConfiguration['packages'];
1000 
1001  $comparator = function ($firstPackageKey, $secondPackageKey) use ($packageStatesConfiguration) {
1002  if (isset($packageStatesConfiguration[$firstPackageKey]['dependencies'])
1003  && (in_array($secondPackageKey, $packageStatesConfiguration[$firstPackageKey]['dependencies'])
1004  && !in_array($firstPackageKey, $packageStatesConfiguration[$secondPackageKey]['dependencies']))) {
1005  return 1;
1006  } elseif (isset($packageStatesConfiguration[$secondPackageKey]['dependencies'])
1007  && (in_array($firstPackageKey, $packageStatesConfiguration[$secondPackageKey]['dependencies'])
1008  && !in_array($secondPackageKey, $packageStatesConfiguration[$firstPackageKey]['dependencies']))) {
1009  return -1;
1010  }
1011  return strcmp($firstPackageKey, $secondPackageKey);
1012  };
1013 
1014  uasort($this->packages,
1015  function (\TYPO3\Flow\Package\PackageInterface $firstPackage, \TYPO3\Flow\Package\PackageInterface $secondPackage) use ($comparator) {
1016  return $comparator($firstPackage->getPackageKey(), $secondPackage->getPackageKey());
1017  }
1018  );
1019 
1020  uksort($this->packageStatesConfiguration['packages'],
1021  function ($firstPackageKey, $secondPackageKey) use ($comparator) {
1022  return $comparator($firstPackageKey, $secondPackageKey);
1023  }
1024  );
1025  }
1026 }
1027 
1028 ?>
unregisterPackage(PackageInterface $package)
injectClassLoader(\TYPO3\Flow\Core\ClassLoader $classLoader)
static readDirectoryRecursively($path, $suffix=NULL, $returnRealPath=FALSE, $returnDotFiles=FALSE, &$filenames=array())
Definition: Files.php:71
writeComposerManifest($manifestPath, $packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData=NULL)
scanPackagesInPath($startPath, array &$collectedPackagePaths=array())
initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap)
getFilteredPackages($packageState='available', $packagePath=NULL, $packageType=NULL)
static concatenatePaths(array $paths)
Definition: Files.php:55
injectSystemLogger(\TYPO3\Flow\Log\SystemLoggerInterface $systemLogger)
getDependencyArrayForPackage($packageKey, array &$dependentPackageKeys=array(), array $trace=array())
createPackage($packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData=NULL, $packagesPath=NULL, $packageType='typo3-flow-package')
getCaseSensitivePackageKey($unknownCasedPackageKey)
static createDirectoryRecursively($path)
Definition: Files.php:160
registerPackage(PackageInterface $package, $sortAndSave=TRUE)
static getPackageKeyFromManifest($manifest, $packagePath, $packagesBasePath)
static getComposerManifest($manifestPath, $key=NULL, $composerManifest=NULL)
static getUnixStylePath($path)
Definition: Files.php:27
static removeDirectoryRecursively($path)
Definition: Files.php:134