diff --git a/docs/Application-flow.md b/docs/Application-flow.md index ad19b0e..5854088 100644 --- a/docs/Application-flow.md +++ b/docs/Application-flow.md @@ -86,17 +86,3 @@ The steps listed above for the two stages represent the "happy paths". If any ex 2. The **`Package::ACTION_FAILED_BOOT`** action hook is fired, passing the raised `Throwable` as an argument. 3. If the `Package`'s `Properties` instance is in "debug mode" (`Properties::isDebug()` returns `true`), the exception bubbles up, and the flow stops here. 4. `Package::boot()` returns false. - - - -## About modules passed to `Package::boot()` - -Passing modules to add to `Package::boot()` has been deprecated since Modularity `v1.7.0`. - -For backward compatibility, when that happens, a deprecation notice is triggered (similarly to WordPress' `_deprecated_argument`) but modules are still added. - -It must be noted, that when first calling `Package::build()` and after that `Package::boot()` passing modules as argument, we will add those modules _after_ the status is already at `Package::STATUS_INITIALIZED` (because of the `Package::build()` call) and, as mentioned above, that should not be possible. - -The `Package` class still deals with this scenario aiming for 100% backward compatibility, but there's an edge case. If anything that listens to the `Package::ACTION_INITIALIZED` hook accesses the container (which is an accepted and documented possibility) the compiled container will be created, which means we can't add modules to it anymore. In this specific case, calling something like `$package->build()->boot($someModule)` will end-up in an exception. - -While this is a breakage of the backward compatibility promise, it is also true that `Package::build()` was introduced in `v1.7.0` when passing modules to `Package::boot()` was deprecated. Developers who have introduced `Package::build()` should also have removed any module passed to `Package::boot()`. diff --git a/docs/Package.md b/docs/Package.md index 15e6e4b..4e9281c 100644 --- a/docs/Package.md +++ b/docs/Package.md @@ -335,4 +335,4 @@ Retrieve the current status of the application. The following statuses are avail | `Package::STATUS_BOOTING` | Before `Package::boot()` started processing executable modules' "run procedures". | | `Package::STATUS_BOOTED` | After `Package::boot()` ended processing executable modules' "run procedures". | | `Package::STATUS_DONE` | The application has successfully completed both processes. | -| `Package::STATUS_FAILED` | The application did not build/boot properly. | \ No newline at end of file +| `Package::STATUS_FAILED` | The application did not build/boot properly. | diff --git a/src/Package.php b/src/Package.php index 737a999..f994161 100644 --- a/src/Package.php +++ b/src/Package.php @@ -373,10 +373,9 @@ public function build(): Package } /** - * @param Module ...$defaultModules Deprecated, use `addModule()` to add default modules. * @return bool */ - public function boot(Module ...$defaultModules): bool + public function boot(): bool { try { // When package is done, nothing should happen to it calling boot again, but we call @@ -385,9 +384,9 @@ public function boot(Module ...$defaultModules): bool return false; } - // Call build() if not called yet, and ensure any new module passed here is added - // as well, throwing if the container was already built. - $this->doBuild(...$defaultModules); + if (!$this->built && $this->statusIs(self::STATUS_IDLE)) { + $this->build(); + } // Make sure we call boot() on a non-failed instance, and also make a sanity check // on the status flow, e.g. prevent calling boot() from an action hook. @@ -420,88 +419,6 @@ public function boot(Module ...$defaultModules): bool return true; } - /** - * @param Module ...$defaultModules - * @return void - */ - private function doBuild(Module ...$defaultModules): void - { - if ($defaultModules) { - $this->deprecatedArgument( - sprintf( - 'Passing default modules to %1$s::boot() is deprecated since version 1.7.0.' - . ' Please add modules via %1$s::addModule().', - __CLASS__ - ), - __METHOD__, - '1.7.0' - ); - } - - // We expect `boot()` to be called either: - // 1. Directly after `addModule()`/`connect()`, without any `build()` call in between, so - // status is IDLE and `$this->built` is `false`. - // 2. After `build()` is called, so status is INITIALIZED and `$this->built` is `true`. - // Any other usage is not allowed (e.g. calling `boot()` from an hook callback) and in that - // case we return here, giving back control to `boot()` which will throw. - $validFlows = (!$this->built && $this->statusIs(self::STATUS_IDLE)) - || ($this->built && $this->statusIs(self::STATUS_INITIALIZED)); - - if (!$validFlows) { - // If none of the two supported flows happened, we just return handling control back - // to `boot()`, that will throw. - return; - } - - if (!$this->built) { - // First valid flow: `boot()` was called directly after `addModule()`/`connect()` - // without any call to `build()`. We can call `build()` and return, handing control - // back to `boot()`. Before returning, if we had default modules passed to `boot()` we - // already have fired a deprecation, so here we just add them dealing with back-compat. - foreach ($defaultModules as $defaultModule) { - $this->addModule($defaultModule); - } - $this->build(); - - return; - } - - // Second valid flow: we have called `boot()` after `build()`. If we did it correctly, - // without default modules passed to `boot()`, we can just return handing control back - // to `boot()`. - if (!$defaultModules) { - return; - } - - // If here, we have done something like: `$package->build()->boot($module1, $module2)`. - // Passing modules to `boot()` was deprecated when `build()` was introduced, so whoever - // added `build()` should have removed modules passed to `boot()`. - // But we want to keep 100% backward compatibility so we still support this behavior - // until the next major is released. To do that, we simulate IDLE status to prevent - // `addModule()` from throwing when adding default modules. - // But we can do that only if we don't have a compiled container yet. - // If anything hooking ACTION_INIT called `container()` we have a compiled container - // already, and we can't add modules, so we not going to simulate INIT status, which mean - // the `$this->addModule()` call below will throw. - $backup = $this->status; - try { - if (!$this->hasContainer()) { - $this->status = self::STATUS_IDLE; - } - foreach ($defaultModules as $defaultModule) { - // If a module was already added via `addModule()` we can skip it, reducing the - // chances of throwing an exception if not needed. - if (!$this->moduleIs($defaultModule->id(), self::MODULE_ADDED)) { - $this->addModule($defaultModule); - } - } - } finally { - if (!$this->hasFailed()) { - $this->status = $backup; - } - } - } - /** * @param Module $module * @param string $status diff --git a/tests/unit/PackageTest.php b/tests/unit/PackageTest.php index 55785fa..e84e12e 100644 --- a/tests/unit/PackageTest.php +++ b/tests/unit/PackageTest.php @@ -401,54 +401,30 @@ public function testBootWithExecutableModuleFailed(): void /** * @test - * @runInSeparateProcess */ - public function testBootPassingModulesEmitDeprecation(): void - { - $module1 = $this->stubModule('module_1', ServiceModule::class); - $module1->allows('services')->andReturn($this->stubServices('service_1')); - - $package = Package::new($this->stubProperties('test', true)); - - $this->convertDeprecationsToExceptions(); - try { - $count = 0; - $package->boot($module1); - } catch (\Throwable $throwable) { - $count++; - $this->assertThrowableMessageMatches($throwable, 'boot().+?deprecated.+?1\.7'); - } finally { - static::assertSame(1, $count); - } - } - - /** - * @test - * @runInSeparateProcess - */ - public function testBootPassingModulesAddModules(): void + public function testAddModuleFailsAfterBuild(): void { $module1 = $this->stubModule('module_1', ServiceModule::class); - $module1->allows('services')->andReturn($this->stubServices('service_1')); + $module1->expects('services')->andReturn($this->stubServices('service_1')); - $package = Package::new($this->stubProperties('test', true)); + $module2 = $this->stubModule('module_2', ServiceModule::class); + $module2->expects('services')->andReturn($this->stubServices('service_2')); - $this->ignoreDeprecations(); - $package->boot($module1); + $module3 = $this->stubModule('module_3', ServiceModule::class); + $module3->allows('services')->andReturn($this->stubServices('service_3')); - static::assertSame('service_1', $package->container()->get('service_1')['id']); - } + $package = Package::new($this->stubProperties('test', true)) + ->addModule($module1) + ->addModule($module2) + ->build(); - /** - * @test - */ - public function testAddModuleFailsAfterBuild(): void - { - $package = Package::new($this->stubProperties('test', true))->build(); + $container = $package->container(); - $this->expectExceptionMessageMatches("/add module/i"); + static::assertSame('service_1', $container->get('service_1')['id']); + static::assertSame('service_2', $container->get('service_2')['id']); - $package->addModule($this->stubModule()); + $this->expectExceptionMessageMatches("/can't add module module_3/i"); + $package->addModule($module3); } /** @@ -520,64 +496,6 @@ public function run(ContainerInterface $container): bool static::assertSame('Works!', $actual); } - /** - * @test - */ - public function testBuildPassingModulesToBoot(): void - { - $module1 = $this->stubModule('module_1', ServiceModule::class); - $module1->expects('services')->andReturn($this->stubServices('service_1')); - - $module2 = $this->stubModule('module_2', ServiceModule::class); - $module2->expects('services')->andReturn($this->stubServices('service_2')); - - $module3 = $this->stubModule('module_3', ServiceModule::class); - $module3->expects('services')->andReturn($this->stubServices('service_3')); - - $package = Package::new($this->stubProperties('test', true)) - ->addModule($module1) - ->addModule($module2) - ->build(); - - $this->ignoreDeprecations(); - $package->boot($module2, $module3); - - $container = $package->container(); - - static::assertSame('service_1', $container->get('service_1')['id']); - static::assertSame('service_2', $container->get('service_2')['id']); - static::assertSame('service_3', $container->get('service_3')['id']); - } - - /** - * @test - */ - public function testBootFailsIfPassingNotAddedModulesAfterContainer(): void - { - $module1 = $this->stubModule('module_1', ServiceModule::class); - $module1->expects('services')->andReturn($this->stubServices('service_1')); - - $module2 = $this->stubModule('module_2', ServiceModule::class); - $module2->expects('services')->andReturn($this->stubServices('service_2')); - - $module3 = $this->stubModule('module_3', ServiceModule::class); - $module3->allows('services')->andReturn($this->stubServices('service_3')); - - $package = Package::new($this->stubProperties('test', true)) - ->addModule($module1) - ->addModule($module2) - ->build(); - - $container = $package->container(); - - static::assertSame('service_1', $container->get('service_1')['id']); - static::assertSame('service_2', $container->get('service_2')['id']); - - $this->expectExceptionMessageMatches("/can't add module module_3/i"); - $this->ignoreDeprecations(); - $package->boot($module2, $module3); - } - /** * @test */ @@ -773,7 +691,11 @@ public function testItFailsWhenCallingBootFromInitHookDebugOff(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT)) ->once() - ->whenHappen([$package, 'boot']); + ->whenHappen( + static function () use ($package): void { + $package->boot(); + } + ); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->never(); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INITIALIZED))->never(); @@ -793,7 +715,11 @@ public function testItFailsWhenCallingBootFromInitHookDebugOn(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT)) ->once() - ->whenHappen([$package, 'boot']); + ->whenHappen( + static function () use ($package): void { + $package->boot(); + } + ); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->never(); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INITIALIZED))->never(); @@ -814,7 +740,11 @@ public function testItFailsWhenCallingBootFromInitializedHook(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_INITIALIZED)) ->once() - ->whenHappen([$package, 'boot']); + ->whenHappen( + static function () use ($package): void { + $package->boot(); + } + ); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT))->once(); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->once(); @@ -835,7 +765,11 @@ public function testItFailsWhenCallingBootFromReadyHook(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_BOOTED)) ->once() - ->whenHappen([$package, 'boot']); + ->whenHappen( + static function () use ($package): void { + $package->boot(); + } + ); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT))->once(); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->once(); @@ -856,7 +790,11 @@ public function testItFailsWhenCallingBuildFromInitHook(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT)) ->once() - ->whenHappen([$package, 'build']); + ->whenHappen( + static function () use ($package): void { + $package->build(); + } + ); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->never(); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INITIALIZED))->never(); @@ -877,7 +815,11 @@ public function testItFailsWhenCallingBuildFromInitializedHook(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_INITIALIZED)) ->once() - ->whenHappen([$package, 'build']); + ->whenHappen( + static function () use ($package): void { + $package->build(); + } + ); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT))->once(); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->once(); @@ -898,7 +840,11 @@ public function testItFailsWhenCallingBuildFromReadyHook(): void Monkey\Actions\expectDone($package->hookName(Package::ACTION_BOOTED)) ->once() - ->whenHappen([$package, 'build']); + ->whenHappen( + static function () use ($package): void { + $package->build(); + } + ); Monkey\Actions\expectDone($package->hookName(Package::ACTION_INIT))->once(); Monkey\Actions\expectDone(Package::ACTION_MODULARITY_INIT)->once();