Skip to content

Commit 85b9b19

Browse files
committed
Force the replacement of the service
1 parent 1d60ab3 commit 85b9b19

5 files changed

Lines changed: 73 additions & 44 deletions

File tree

classes/UpgradeContainer.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
use PrestaShop\Module\AutoUpgrade\Twig\TransFilterExtension;
5858
use PrestaShop\Module\AutoUpgrade\Twig\TransFilterExtension3;
5959
use PrestaShop\Module\AutoUpgrade\UpgradeTools\CacheCleaner;
60+
use PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader\CoreServiceStub\CoreServiceStubRegistrar;
6061
use PrestaShop\Module\AutoUpgrade\UpgradeTools\FileFilter;
6162
use PrestaShop\Module\AutoUpgrade\UpgradeTools\FilesystemAdapter;
6263
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleAdapter;
@@ -212,6 +213,9 @@ class UpgradeContainer
212213
/** @var ConfigurationValidator */
213214
private $configurationValidator;
214215

216+
/** @var CoreServiceStubRegistrar */
217+
private $coreServiceStubRegistrar;
218+
215219
/** @var LocalChannelConfigurationValidator */
216220
private $localChannelConfigurationValidator;
217221

@@ -787,7 +791,7 @@ public function getPrestaShopConfiguration(): PrestashopConfiguration
787791
public function getSymfonyAdapter(): SymfonyAdapter
788792
{
789793
if (null === $this->symfonyAdapter) {
790-
$this->symfonyAdapter = new SymfonyAdapter();
794+
$this->symfonyAdapter = new SymfonyAdapter($this->getCoreServiceStubRegistrar());
791795
}
792796

793797
return $this->symfonyAdapter;
@@ -990,6 +994,18 @@ public function getConfigurationValidator(): ConfigurationValidator
990994
return $this->configurationValidator;
991995
}
992996

997+
public function getCoreServiceStubRegistrar(): CoreServiceStubRegistrar
998+
{
999+
if (null === $this->coreServiceStubRegistrar) {
1000+
$this->coreServiceStubRegistrar = new CoreServiceStubRegistrar(
1001+
$this->getLogger(),
1002+
$this->getTranslator()
1003+
);
1004+
}
1005+
1006+
return $this->coreServiceStubRegistrar;
1007+
}
1008+
9931009
/**
9941010
* @return LocalChannelConfigurationValidator
9951011
*/

classes/UpgradeTools/CoreUpgrader/CoreServiceStub/CoreServiceStubRegistrar.php

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
use PrestaShop\Module\AutoUpgrade\Log\LoggerInterface;
2525
use PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader\CoreServiceStub\Stubs\FaultTolerantExtraPropertyDefinitionRepository;
2626
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
27+
use ReflectionObject;
28+
use RuntimeException;
29+
use Symfony\Component\DependencyInjection\Container;
2730
use Symfony\Component\DependencyInjection\ContainerInterface;
2831
use Throwable;
2932

@@ -35,16 +38,6 @@
3538
* an early upgrade step can therefore fail on a schema that is not complete yet. For each
3639
* such case we register a stub that tolerates the incomplete schema until the migration
3740
* catches up.
38-
*
39-
* To add a new stub:
40-
* - create the stub class in the Stubs/ subfolder (a decorator implementing the core
41-
* contract is the usual shape), exposing a static register(ContainerInterface) method;
42-
* - add an entry below mapping the core symbol that must exist for the stub to be relevant
43-
* to the callable installing it.
44-
*
45-
* The map is keyed by a core class/interface name kept as a literal string: the existence
46-
* check runs before the stub class is referenced, so stub classes that implement a core
47-
* contract are never autoloaded against a core version that does not ship it.
4841
*/
4942
class CoreServiceStubRegistrar
5043
{
@@ -66,28 +59,62 @@ public function __construct(LoggerInterface $logger, Translator $translator)
6659

6760
public function register(ContainerInterface $container): void
6861
{
69-
foreach ($this->getStubs() as $requiredCoreSymbol => $register) {
62+
foreach ($this->getStubs() as $requiredCoreSymbol => $stubClass) {
7063
if (!interface_exists($requiredCoreSymbol) && !class_exists($requiredCoreSymbol)) {
7164
continue;
7265
}
7366

67+
if (!$container->has($requiredCoreSymbol)) {
68+
continue;
69+
}
70+
7471
try {
75-
$register($container);
72+
$decorated = $container->get($requiredCoreSymbol);
73+
$stub = new $stubClass($decorated);
74+
75+
try {
76+
$container->set($requiredCoreSymbol, $stub);
77+
} catch (Throwable $e) {
78+
// Fetching $decorated just above already resolved and cached the service,
79+
// so the container now considers it initialized and refuses a plain set() on
80+
// it ("already initialized"). Force the swap directly on the container's
81+
// internal service cache instead, so later callers in this same request
82+
// (ObjectModel in particular) get our stub rather than the raw service.
83+
$this->forceReplace($container, $requiredCoreSymbol, $stub);
84+
}
7685
} catch (Throwable $e) {
7786
$this->logger->warning($this->translator->trans('Unable to register the core service stub for %s during the update: %s', [$requiredCoreSymbol, $e->getMessage()]));
7887
}
7988
}
8089
}
8190

8291
/**
83-
* @return array<string, callable(ContainerInterface):void> core symbol => stub installer
92+
* @return array<string, class-string> core symbol => decorating stub class
8493
*/
8594
private function getStubs(): array
8695
{
8796
return [
8897
// PrestaShop 9.2+: the extra_property_definition table is created late in the
8998
// migration, after PHP scripts that already make ObjectModel query it.
90-
'PrestaShop\PrestaShop\Core\ExtraProperty\Definition\ExtraPropertyDefinitionRepositoryInterface' => [FaultTolerantExtraPropertyDefinitionRepository::class, 'register'],
99+
'PrestaShop\PrestaShop\Core\ExtraProperty\Definition\ExtraPropertyDefinitionRepositoryInterface' => FaultTolerantExtraPropertyDefinitionRepository::class,
91100
];
92101
}
102+
103+
private function forceReplace(ContainerInterface $container, string $id, object $service): void
104+
{
105+
$reflectionClass = new ReflectionObject($container);
106+
while ($reflectionClass && !$reflectionClass->hasProperty('services')) {
107+
$reflectionClass = $reflectionClass->getParentClass();
108+
}
109+
110+
if (!$reflectionClass) {
111+
throw new RuntimeException(sprintf('Cannot override the "%s" service: cound\'t reach the container "%s".', $id, get_class($container)));
112+
}
113+
114+
$servicesProperty = $reflectionClass->getProperty('services');
115+
$servicesProperty->setAccessible(true);
116+
$services = $servicesProperty->getValue($container);
117+
$services[$id] = $service;
118+
$servicesProperty->setValue($container, $services);
119+
}
93120
}

classes/UpgradeTools/CoreUpgrader/CoreServiceStub/Stubs/FaultTolerantExtraPropertyDefinitionRepository.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@
4747
*/
4848
class FaultTolerantExtraPropertyDefinitionRepository implements ExtraPropertyDefinitionRepositoryInterface
4949
{
50-
/**
51-
* Service id used by ObjectModel to resolve the repository from the container.
52-
*/
53-
const SERVICE_ID = ExtraPropertyDefinitionRepositoryInterface::class;
54-
5550
/**
5651
* @var ExtraPropertyDefinitionRepositoryInterface
5752
*/
@@ -62,26 +57,6 @@ public function __construct(ExtraPropertyDefinitionRepositoryInterface $decorate
6257
$this->decorated = $decorated;
6358
}
6459

65-
/**
66-
* Wraps the core repository service registered in the given container so it no longer
67-
* fails when the extra_property_definition table does not exist yet.
68-
*/
69-
public static function register(ContainerInterface $container): void
70-
{
71-
if (!$container->has(self::SERVICE_ID)) {
72-
return;
73-
}
74-
75-
$current = $container->get(self::SERVICE_ID);
76-
77-
// Avoid wrapping our own decorator again on subsequent update batches.
78-
if ($current instanceof self) {
79-
return;
80-
}
81-
82-
$container->set(self::SERVICE_ID, new self($current));
83-
}
84-
8560
public function getAllDefinitions(): ExtraPropertyDefinitionCollection
8661
{
8762
try {

classes/UpgradeTools/CoreUpgrader/CoreUpgrader80.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ protected function initConstants(): void
3737
$this->forceRemovingFiles();
3838
parent::initConstants();
3939
// Container may be needed to run upgrade scripts
40-
$kernel = $this->container->getSymfonyAdapter()->initKernel();
41-
// The destination core files run against a database that is not fully migrated yet:
42-
// replace the core services that would fail on the incomplete schema with stubs.
43-
(new CoreServiceStubRegistrar($this->logger, $this->container->getTranslator()))->register($kernel->getContainer());
40+
$this->container->getSymfonyAdapter()->initKernel();
4441
}
4542

4643
/**

classes/UpgradeTools/SymfonyAdapter.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,23 @@
2121

2222
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
2323

24+
use PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader\CoreServiceStub\CoreServiceStubRegistrar;
2425
use ReflectionClass;
2526

2627
/**
2728
* TODO: Create a class for 1.7 env and another one for 1.6 ?
2829
*/
2930
class SymfonyAdapter
3031
{
32+
/** @var CoreServiceStubRegistrar */
33+
private $coreServiceStubRegistrar;
34+
35+
public function __construct(
36+
CoreServiceStubRegistrar $coreServiceStubRegistrar
37+
) {
38+
$this->coreServiceStubRegistrar = $coreServiceStubRegistrar;
39+
}
40+
3141
public function isKernelReachable(): bool
3242
{
3343
return defined('_PS_ROOT_DIR_') && class_exists('AppKernel', true);
@@ -61,6 +71,10 @@ public function initKernel()
6171
$kernel->boot();
6272
// Starting from PrestaShop 9, some parts of the new context are defined by event listeners on kernel.request.
6373
// We may have to trigger it with dummy data in the future.
74+
75+
// The destination core files run against a database that may be not fully migrated yet:
76+
// replace the core services that would fail on the incomplete schema with stubs.
77+
$this->coreServiceStubRegistrar->register($kernel->getContainer());
6478
}
6579

6680
return $kernel;

0 commit comments

Comments
 (0)