Skip to content

Commit

Permalink
Refactor singleton and prototype handling in DI compiler
Browse files Browse the repository at this point in the history
Updated the dependency injection compiler to streamline the handling of singleton and prototype instances. Introduced a consistent approach for processing $dependencyIndex in both singleton and prototype implementations, ensuring better maintainability and test coverage. Adjusted related tests and modules to align with these changes.
  • Loading branch information
koriym committed Jan 12, 2025
1 parent b974dad commit 5d3352e
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src-function/prototype.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
*
* @return mixed
*/
function prototype(string $scriptDir, string $filePath, ?array $ip = null) {
function prototype(string $scriptDir, string $dependencyIndex, string $filePath, ?array $ip = null) {
$file = $scriptDir . DIRECTORY_SEPARATOR . $filePath;
if (! file_exists($file)) {
throw new ScriptFileNotFound($filePath);
}

// $scriptDir, $Singletons and $ip can be used in the included file
// $scriptDir, $Singletons, $dependencyIndex and $ip can be used in the included file
return require $file;
};
3 changes: 1 addition & 2 deletions src-function/singleton.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ function singleton(string $scriptDir, array &$singletons, string $dependencyInde
throw new ScriptFileNotFound($scriptFile);
}

// $scriptDir, $Singletons and $ip can be used in the included file
// $scriptDir, $Singletons, $dependencyIndex and $ip can be used in the included file
$instance = require $scriptFile;
$singletons[$dependencyIndex] = $instance;

return $instance;
};
7 changes: 0 additions & 7 deletions src/AirInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,6 @@ public function getInstance($interface, $name = Name::ANY)
/** @var mixed $instance */
$instance = require $scriptFile;

// Save singleton
/** @psalm-suppress UndefinedVariable */
if (isset($isSingleton) && $isSingleton) {
/** @var object $instance */
$this->singletons[$dependencyIndex] = $instance;
}

/** @pslam-var T $instance */
return $instance;
}
Expand Down
20 changes: 19 additions & 1 deletion src/CompileVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ public function visitProvider(
$script = $dependency->accept($this);
assert(is_string($script));

return str_replace('return $instance', 'return $instance->get()', $script);
$providerScript = $this->getProviderScript($isSingleton);

return str_replace(InstanceScript::COMMENT, $providerScript, $script);
}

/** @inheritDoc */
Expand Down Expand Up @@ -129,4 +131,20 @@ public function visitArgument(
): void {
$this->script->addArg($index, $isDefaultAvailable, $defaultValue, $parameter);
}

private function getProviderScript(bool $isSingleton): string
{
if ($isSingleton) {
return <<<'EOT'
$instance = $instance->get();
// singleton
$singletons[$dependencyIndex] = $instance;
EOT;
}

return <<<'EOT'
$instance = $instance->get();
// prototype
EOT;
}
}
11 changes: 7 additions & 4 deletions src/InstanceScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ final class InstanceScript
{
public const RAY_DI_INJECTOR_INTERFACE = 'Ray\Di\InjectorInterface-';
public const RAY_DI_INJECTION_POINT_INTERFACE = 'Ray\Di\InjectionPointInterface-';
public const COMMENT = '// prototype';

/** @var array<mixed> */
private $args = [];
Expand Down Expand Up @@ -103,7 +104,7 @@ private function addDependencyArg(bool $isSingleton, string $index, ReflectionPa
// Add prototype or singleton
$this->args[] = $isSingleton ?
sprintf("\\Ray\\Compiler\\singleton(\$scriptDir, \$singletons, '%s', '%s', %s)", $index, $filePath, $ip) :
sprintf("\\Ray\\Compiler\\prototype(\$scriptDir, '%s', %s)", $filePath, $ip);
sprintf("\\Ray\\Compiler\\prototype(\$scriptDir, '%s', '%s', %s)", $index, $filePath, $ip);
}

/** @param mixed $default */
Expand Down Expand Up @@ -168,14 +169,16 @@ public function getScript(?string $postConstruct, bool $isSingleton): string
$this->laterLines[] = sprintf('$instance->setContext(%s);', var_export($this->context, true));
}

$isSingleton = $this->isSingleton ?? $isSingleton;
$this->laterLines[] = sprintf('$isSingleton = %s;', $isSingleton ? 'true' : 'false');
$this->laterLines[] = self::COMMENT;
if ($isSingleton) {
$this->laterLines[] = sprintf('$singletons[$dependencyIndex] = $instance;');
}

$this->laterLines[] = 'return $instance;';

$script = implode(PHP_EOL, $this->formerLines) . PHP_EOL . implode(PHP_EOL, $this->laterLines);
$this->formerLines = [];
$this->laterLines = [];
$this->isSingleton = null;

return $script;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Fake/FakeToBindSingletonModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ class FakeToBindSingletonModule extends AbstractModule
protected function configure()
{
$this->bind(FakeRobotInterface::class)->to(FakeRobot::class)->in(Scope::SINGLETON);
$this->bind(FakeDependSingleton::class);
$this->bind(FakeDependSingleton::class)->in(Scope::SINGLETON);
}
}
22 changes: 12 additions & 10 deletions tests/VisitorCompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ public function testDependencyCompile(): Container
assert($dependency instanceof AcceptInterface);
$code = $dependency->accept(new CompileVisitor($container));
$expected = <<<'EOT'
$instance = new \Ray\Compiler\FakeCar(\Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeEngineInterface-.php', ['Ray\Compiler\FakeCar', '__construct', 'engine']));
$instance->setTires(\Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeTyreInterface-.php', ['Ray\Compiler\FakeCar', 'setTires', 'frontTyre']), \Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeTyreInterface-.php', ['Ray\Compiler\FakeCar', 'setTires', 'rearTyre']), NULL);
$instance->setHardtop(\Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeHardtopInterface-.php', ['Ray\Compiler\FakeCar', 'setHardtop', 'hardtop']));
$instance = new \Ray\Compiler\FakeCar(\Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeEngineInterface-', '/Ray_Compiler_FakeEngineInterface-.php', ['Ray\Compiler\FakeCar', '__construct', 'engine']));
$instance->setTires(\Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeTyreInterface-', '/Ray_Compiler_FakeTyreInterface-.php', ['Ray\Compiler\FakeCar', 'setTires', 'frontTyre']), \Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeTyreInterface-', '/Ray_Compiler_FakeTyreInterface-.php', ['Ray\Compiler\FakeCar', 'setTires', 'rearTyre']), NULL);
$instance->setHardtop(\Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeHardtopInterface-', '/Ray_Compiler_FakeHardtopInterface-.php', ['Ray\Compiler\FakeCar', 'setHardtop', 'hardtop']));
$instance->setMirrors(\Ray\Compiler\singleton($scriptDir, $singletons, 'Ray\Compiler\FakeMirrorInterface-right', '/Ray_Compiler_FakeMirrorInterface-right.php', ['Ray\Compiler\FakeCar', 'setMirrors', 'rightMirror']), \Ray\Compiler\singleton($scriptDir, $singletons, 'Ray\Compiler\FakeMirrorInterface-left', '/Ray_Compiler_FakeMirrorInterface-left.php', ['Ray\Compiler\FakeCar', 'setMirrors', 'leftMirror']));
$instance->setSpareMirror(\Ray\Compiler\singleton($scriptDir, $singletons, 'Ray\Compiler\FakeMirrorInterface-right', '/Ray_Compiler_FakeMirrorInterface-right.php', ['Ray\Compiler\FakeCar', 'setSpareMirror', 'rightMirror']));
$instance->setHandle(\Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeHandleInterface-.php', ['Ray\Compiler\FakeCar', 'setHandle', 'handle']));
$instance->setOil(\Ray\Compiler\prototype($scriptDir, '/Ray_Compiler_FakeOilInterface-.php', ['Ray\Compiler\FakeCar', 'setOil', 'oil']));
$instance->setHandle(\Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeHandleInterface-', '/Ray_Compiler_FakeHandleInterface-.php', ['Ray\Compiler\FakeCar', 'setHandle', 'handle']));
$instance->setOil(\Ray\Compiler\prototype($scriptDir, 'Ray\Compiler\FakeOilInterface-', '/Ray_Compiler_FakeOilInterface-.php', ['Ray\Compiler\FakeCar', 'setOil', 'oil']));
$instance->postConstruct();
$isSingleton = false;
// prototype
return $instance;
EOT;
$this->assertSame(
Expand All @@ -98,8 +98,9 @@ public function testDependencyProviderCompile(Container $container): void
$code = $dependency->accept(new CompileVisitor($container));
$expected = <<<'EOT'
$instance = new \Ray\Compiler\FakeHandleProvider('momo');
$isSingleton = false;
return $instance->get();
$instance = $instance->get();
// prototype
return $instance;
EOT;
$this->assertSame(
$this->normalizeLineEndings($expected),
Expand Down Expand Up @@ -143,8 +144,9 @@ public function testContextualProviderCompile(): void
$expected = <<<'EOT'
$instance = new \Ray\Compiler\FakeContextualProvider();
$instance->setContext('context');
$isSingleton = false;
return $instance->get();
$instance = $instance->get();
// prototype
return $instance;
EOT;
$this->assertSame(
$this->normalizeLineEndings($expected),
Expand Down

0 comments on commit 5d3352e

Please sign in to comment.