Skip to content

Commit

Permalink
[TASK] Merge the package DependencyResolver into the PackageManager
Browse files Browse the repository at this point in the history
The DependencyResolver and the PackageManager have a cyclic dependency to
each other which is currently resolved using GeneralUtility::makeInstance.
As the DependencyResolver is actually only used for the
PackageManager – and relies on it – it can be merged, saving a lot of
hassle.

The DependencyResolver class is not marked @internal and is therefore
deprecated.

Releases: master
Resolves: #84109
Change-Id: I71adccec3f13eb6de859f065937fbcde369758fe
Reviewed-on: https://review.typo3.org/55977
Reviewed-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
  • Loading branch information
bnf authored and lolli42 committed Mar 7, 2018
1 parent 9d34db4 commit 3daaf6c
Show file tree
Hide file tree
Showing 13 changed files with 650 additions and 41 deletions.
6 changes: 2 additions & 4 deletions typo3/sysext/core/Classes/Core/Bootstrap.php
Expand Up @@ -292,15 +292,13 @@ public function loadConfigurationAndInitialize($allowCaching = true, $packageMan
*/
public function initializePackageManagement($packageManagerClassName)
{
$dependencyOrderingService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\DependencyOrderingService::class);
/** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
$packageManager = new $packageManagerClassName();
$packageManager = new $packageManagerClassName($dependencyOrderingService);
GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
$this->setEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
ExtensionManagementUtility::setPackageManager($packageManager);
$packageManager->injectCoreCache(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core'));
$dependencyResolver = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\DependencyResolver::class);
$dependencyResolver->injectDependencyOrderingService(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\DependencyOrderingService::class));
$packageManager->injectDependencyResolver($dependencyResolver);
$packageManager->initialize();
return $this;
}
Expand Down
3 changes: 2 additions & 1 deletion typo3/sysext/core/Classes/Package/DependencyResolver.php
Expand Up @@ -21,6 +21,7 @@
* This class takes care about dependencies between packages.
* It provides functionality to resolve dependencies and to determine
* the crucial loading order of the packages.
* @deprecated since TYPO3 v9.2, will be removed in TYPO3 v10
*/
class DependencyResolver
{
Expand All @@ -44,6 +45,7 @@ public function injectDependencyOrderingService(DependencyOrderingService $depen
*/
public function sortPackageStatesConfigurationByDependency(array $packageStatesConfiguration)
{
trigger_error(self::class . ' has been deprecated with v9.2 and will be removed in TYPO3 v10.', E_USER_DEPRECATED);
return $this->dependencyOrderingService->calculateOrder($this->buildDependencyGraph($packageStatesConfiguration));
}

Expand Down Expand Up @@ -149,7 +151,6 @@ protected function buildDependencyGraph(array $packageStateConfiguration)
/**
* @param array $packageStateConfiguration
* @return array
* @throws \TYPO3\CMS\Core\Exception
*/
protected function findFrameworkPackages(array $packageStateConfiguration)
{
Expand Down
145 changes: 139 additions & 6 deletions typo3/sysext/core/Classes/Package/PackageManager.php
Expand Up @@ -20,6 +20,7 @@
use TYPO3\CMS\Core\Compatibility\LoadedExtensionArrayElement;
use TYPO3\CMS\Core\Core\ClassLoadingInformation;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Service\DependencyOrderingService;
use TYPO3\CMS\Core\Service\OpcodeCacheService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand All @@ -32,9 +33,9 @@
class PackageManager implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var \TYPO3\CMS\Core\Package\DependencyResolver
* @var DependencyOrderingService
*/
protected $dependencyResolver;
protected $dependencyOrderingService;

/**
* @var FrontendInterface
Expand Down Expand Up @@ -102,11 +103,16 @@ class PackageManager implements \TYPO3\CMS\Core\SingletonInterface
protected $packageStatesConfiguration = [];

/**
* Constructor
* @param DependencyOrderingService $dependencyOrderingService
*/
public function __construct()
public function __construct(DependencyOrderingService $dependencyOrderingService = null)
{
$this->packageStatesPathAndFilename = PATH_typo3conf . 'PackageStates.php';
if ($dependencyOrderingService === null) {
trigger_error(self::class . ' without constructor based dependency injection has been deprecated in v9.2 and will not work in TYPO3 v10.', E_USER_DEPRECATED);
$dependencyOrderingService = GeneralUtility::makeInstance(DependencyOrderingService::class);
}
$this->dependencyOrderingService = $dependencyOrderingService;
}

/**
Expand All @@ -119,10 +125,11 @@ public function injectCoreCache(FrontendInterface $coreCache)

/**
* @param DependencyResolver $dependencyResolver
* @deprecated
*/
public function injectDependencyResolver(DependencyResolver $dependencyResolver)
{
$this->dependencyResolver = $dependencyResolver;
trigger_error(self::class . '::injectDependencyResolver() has been deprecated with v9.2 and will be removed in TYPO3 v10.', E_USER_DEPRECATED);
}

/**
Expand Down Expand Up @@ -686,7 +693,7 @@ protected function sortActivePackagesByDependencies()

// sort the packages by key at first, so we get a stable sorting of "equivalent" packages afterwards
ksort($packagesWithDependencies);
$sortedPackageKeys = $this->dependencyResolver->sortPackageStatesConfigurationByDependency($packagesWithDependencies);
$sortedPackageKeys = $this->sortPackageStatesConfigurationByDependency($packagesWithDependencies);

// Reorder the packages according to the loading order
$this->packageStatesConfiguration['packages'] = [];
Expand Down Expand Up @@ -1063,4 +1070,130 @@ protected function hasSubDirectories(string $path): bool
{
return !empty(glob(rtrim($path, '/\\') . '/*', GLOB_ONLYDIR));
}

/**
* @param array $packageStatesConfiguration
* @return array Returns the packageStatesConfiguration sorted by dependencies
* @throws \UnexpectedValueException
*/
protected function sortPackageStatesConfigurationByDependency(array $packageStatesConfiguration)
{
return $this->dependencyOrderingService->calculateOrder($this->buildDependencyGraph($packageStatesConfiguration));
}

/**
* Convert the package configuration into a dependency definition
*
* This converts "dependencies" and "suggestions" to "after" syntax for the usage in DependencyOrderingService
*
* @param array $packageStatesConfiguration
* @param array $packageKeys
* @return array
* @throws \UnexpectedValueException
*/
protected function convertConfigurationForGraph(array $packageStatesConfiguration, array $packageKeys)
{
$dependencies = [];
foreach ($packageKeys as $packageKey) {
if (!isset($packageStatesConfiguration[$packageKey]['dependencies']) && !isset($packageStatesConfiguration[$packageKey]['suggestions'])) {
continue;
}
$dependencies[$packageKey] = [
'after' => []
];
if (isset($packageStatesConfiguration[$packageKey]['dependencies'])) {
foreach ($packageStatesConfiguration[$packageKey]['dependencies'] as $dependentPackageKey) {
if (!in_array($dependentPackageKey, $packageKeys, true)) {
throw new \UnexpectedValueException(
'The package "' . $packageKey . '" depends on "'
. $dependentPackageKey . '" which is not present in the system.',
1519931815
);
}
$dependencies[$packageKey]['after'][] = $dependentPackageKey;
}
}
if (isset($packageStatesConfiguration[$packageKey]['suggestions'])) {
foreach ($packageStatesConfiguration[$packageKey]['suggestions'] as $suggestedPackageKey) {
// skip suggestions on not existing packages
if (in_array($suggestedPackageKey, $packageKeys, true)) {
// Suggestions actually have never been meant to influence loading order.
// We misuse this currently, as there is no other way to influence the loading order
// for not-required packages (soft-dependency).
// When considering suggestions for the loading order, we might create a cyclic dependency
// if the suggested package already has a real dependency on this package, so the suggestion
// has do be dropped in this case and must *not* be taken into account for loading order evaluation.
$dependencies[$packageKey]['after-resilient'][] = $suggestedPackageKey;
}
}
}
}
return $dependencies;
}

/**
* Adds all root packages of current dependency graph as dependency to all extensions
*
* This ensures that the framework extensions (aka sysext) are
* always loaded first, before any other external extension.
*
* @param array $packageStateConfiguration
* @param array $rootPackageKeys
* @return array
*/
protected function addDependencyToFrameworkToAllExtensions(array $packageStateConfiguration, array $rootPackageKeys)
{
$frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
$extensionPackageKeys = array_diff(array_keys($packageStateConfiguration), $frameworkPackageKeys);
foreach ($extensionPackageKeys as $packageKey) {
// Remove framework packages from list
$packageKeysWithoutFramework = array_diff(
$packageStateConfiguration[$packageKey]['dependencies'],
$frameworkPackageKeys
);
// The order of the array_merge is crucial here,
// we want the framework first
$packageStateConfiguration[$packageKey]['dependencies'] = array_merge(
$rootPackageKeys,
$packageKeysWithoutFramework
);
}
return $packageStateConfiguration;
}

/**
* Builds the dependency graph for all packages
*
* This method also introduces dependencies among the dependencies
* to ensure the loading order is exactly as specified in the list.
*
* @param array $packageStateConfiguration
* @return array
*/
protected function buildDependencyGraph(array $packageStateConfiguration)
{
$frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
$frameworkPackagesDependencyGraph = $this->dependencyOrderingService->buildDependencyGraph($this->convertConfigurationForGraph($packageStateConfiguration, $frameworkPackageKeys));
$packageStateConfiguration = $this->addDependencyToFrameworkToAllExtensions($packageStateConfiguration, $this->dependencyOrderingService->findRootIds($frameworkPackagesDependencyGraph));

$packageKeys = array_keys($packageStateConfiguration);
return $this->dependencyOrderingService->buildDependencyGraph($this->convertConfigurationForGraph($packageStateConfiguration, $packageKeys));
}

/**
* @param array $packageStateConfiguration
* @return array
*/
protected function findFrameworkPackages(array $packageStateConfiguration)
{
$frameworkPackageKeys = [];
foreach ($packageStateConfiguration as $packageKey => $packageConfiguration) {
$package = $this->getPackage($packageKey);
if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') {
$frameworkPackageKeys[] = $packageKey;
}
}

return $frameworkPackageKeys;
}
}
@@ -0,0 +1,43 @@
.. include:: ../../Includes.txt

==================================================
Deprecation: #84109 - Deprecate DependencyResolver
==================================================

See :issue:`84109`

Description
===========

The class :php:`\TYPO3\CMS\Core\Package\DependencyResolver` has been marked as
deprecated as the code as been merged into :php:`\TYPO3\CMS\Core\Package\PackageManager`.
Additionally the :php:`\TYPO3\CMS\Core\Package\PackageManager` method
:php:`injectDependencyResolver` has been deprecated and the
:php:`\TYPO3\CMS\Core\Package\PackageManager` triggers a deprecation log entry
when the :php:`\TYPO3\CMS\Core\Service\DependencyOrderingService` is not injected
through the constructor.

Impact
======

Installations that use :php:`\TYPO3\CMS\Core\Package\DependencyResolver` or create
an own :php:`\TYPO3\CMS\Core\Package\PackageManager` instance will trigger a
deprecation log entry.


Affected Installations
======================

All installations that use custom extensions that use the
:php:`\TYPO3\CMS\Core\Package\DependencyResolver` class or create
an own :php:`\TYPO3\CMS\Core\Package\PackageManager` instance.


Migration
=========

Use :php:`\TYPO3\CMS\Core\Service\DependencyOrderingService` to manually sort packages.
Pass :php:`\TYPO3\CMS\Core\Service\DependencyOrderingService` to the
:php:`\TYPO3\CMS\Core\Package\PackageManager` constructor if a new instance is created.

.. index:: PHP-API, FullyScanned

0 comments on commit 3daaf6c

Please sign in to comment.