Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitAuto: [FEATURE] Add a Dependency Inversion Container Class to Pancake #251

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Pancake Framework

## Dependency Inversion Container

The Pancake framework now includes a Dependency Inversion Container (`DIContainer`) that implements the PSR-11 `ContainerInterface`. For detailed usage instructions, refer to the [DIContainer Usage Guide](docs/DIContainerUsage.md).

# pancake

🧰 🛠️ Pancake project - a toolkit for PHP projects.
Expand Down
56 changes: 56 additions & 0 deletions docs/DIContainerUsage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Dependency Inversion Container Usage

The `DIContainer` class in the Pancake framework provides a robust way to manage dependencies in your application. It adheres to the PSR-11 `ContainerInterface` standard, ensuring compatibility with other libraries and frameworks.

## Registering Services

You can register services with the container using the `register`, `registerSingleton`, and `registerTransient` methods.

```php
$container = new DIContainer();

// Register a singleton service
$container->registerSingleton('service', function() {
return new Service();
});

// Register a transient service
$container->registerTransient('transientService', function() {
return new TransientService();
});

// General registration
$container->register('generalService', function() {
return new GeneralService();
}, true); // true for singleton
```

## Resolving Services

To resolve a service, use the `get` method. The container will automatically handle dependencies.

```php
$service = $container->get('service');
$transientService = $container->get('transientService');
```

## Best Practices

- **Use Singleton for Shared Instances**: Use `registerSingleton` for services that should maintain state or are expensive to create.
- **Use Transient for Stateless Services**: Use `registerTransient` for services that do not maintain state and can be recreated easily.
- **Leverage Automatic Dependency Resolution**: Define dependencies in service constructors and let the container resolve them automatically.

## Example

```php
$container->registerSingleton('config', function() {
return new Config(['setting' => 'value']);
});

$container->registerSingleton('service', function($c) {
$config = $c->get('config');
return new Service($config);
});

$service = $container->get('service');
```
6 changes: 6 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Pancake Framework Documentation

## Features

- **Dependency Inversion Container**: The framework includes a DI container that simplifies dependency management and adheres to the PSR-11 standard. See [DIContainer Usage](DIContainerUsage.md) for more details.

58 changes: 58 additions & 0 deletions src/Pancake/DIContainer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Pancake;

use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Container\ContainerExceptionInterface;

class DIContainer implements ContainerInterface
{
private $services = [];

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing member variable doc comment Warning

Missing member variable doc comment

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line(s) before first member var; 0 found Warning

Expected 1 blank line(s) before first member var; 0 found
private $sharedInstances = [];

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing member variable doc comment Warning

Missing member variable doc comment

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line(s) before member var; 0 found Warning

Expected 1 blank line(s) before member var; 0 found

public function register(string $name, callable $resolver, bool $shared = false): void

Check warning

Code scanning / Phpcs (reported by Codacy)

Incorrect spacing between default value and equals sign for argument "$shared"; expected 0 but found 1 Warning

Incorrect spacing between default value and equals sign for argument "$shared"; expected 0 but found 1

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function register() Warning

Missing doc comment for function register()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines before function; 1 found Warning

Expected 2 blank lines before function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Incorrect spacing between argument "$shared" and equals sign; expected 0 but found 1 Warning

Incorrect spacing between argument "$shared" and equals sign; expected 0 but found 1
{
$this->services[$name] = ['resolver' => $resolver, 'shared' => $shared];

Check warning

Code scanning / Phpcs (reported by Codacy)

Array with multiple values cannot be declared on a single line Warning

Array with multiple values cannot be declared on a single line
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end register() Warning

Expected //end register()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning

Expected 2 blank lines after function; 1 found

public function registerSingleton(string $name, callable $resolver): void

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function registerSingleton() Warning

Missing doc comment for function registerSingleton()
{
$this->register($name, $resolver, true);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning

Expected 2 blank lines after function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end registerSingleton() Warning

Expected //end registerSingleton()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

public function registerTransient(string $name, callable $resolver): void

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function registerTransient() Warning

Missing doc comment for function registerTransient()
{
$this->register($name, $resolver, false);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning

Expected 2 blank lines after function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end registerTransient() Warning

Expected //end registerTransient()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

public function get(string $name)

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function get() Warning

Missing doc comment for function get()
{
if (!isset($this->services[$name])) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Operator ! prohibited; use === FALSE instead Warning

Operator ! prohibited; use === FALSE instead
throw new class () extends \Exception implements NotFoundExceptionInterface {};

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing brace must be on a line by itself Warning

Closing brace must be on a line by itself
}

if ($this->services[$name]['shared']) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Implicit true comparisons prohibited; use === TRUE instead Warning

Implicit true comparisons prohibited; use === TRUE instead
if (!isset($this->sharedInstances[$name])) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Operator ! prohibited; use === FALSE instead Warning

Operator ! prohibited; use === FALSE instead
$this->sharedInstances[$name] = $this->resolve($name);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

No blank line found after control structure Warning

No blank line found after control structure
return $this->sharedInstances[$name];
}

return $this->resolve($name);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning

Expected 2 blank lines after function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end get() Warning

Expected //end get()

private function resolve(string $name)

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function resolve() Warning

Missing doc comment for function resolve()
{
try {
return call_user_func($this->services[$name]['resolver'], $this);

Check warning

Code scanning / Phpcs (reported by Codacy)

The use of function call_user_func() is discouraged Warning

The use of function call_user_func() is discouraged
} catch (\Exception $e) {
throw new class () extends \Exception implements ContainerExceptionInterface {};

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing brace must be on a line by itself Warning

Closing brace must be on a line by itself
}
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end resolve() Warning

Expected //end resolve()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning

Expected 2 blank lines after function; 1 found

public function has(string $name): bool

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function has() Warning

Missing doc comment for function has()
{
return isset($this->services[$name]);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 0 found Warning

Expected 2 blank lines after function; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end has() Warning

Expected //end has()
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end class Warning

Expected //end class
64 changes: 64 additions & 0 deletions tests/DIContainerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

use PHPUnit\Framework\TestCase;
use Pancake\DIContainer;

class DIContainerTest extends TestCase
{
public function testRegisterAndResolveSingleton()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines before function; 0 found Warning test

Expected 2 blank lines before function; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testRegisterAndResolveSingleton() Warning test

Missing doc comment for function testRegisterAndResolveSingleton()
{
$container = new DIContainer();
$container->registerSingleton('service', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
return new stdClass();
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$service1 = $container->get('service');
$service2 = $container->get('service');

$this->assertSame($service1, $service2);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testRegisterAndResolveSingleton() Warning test

Expected //end testRegisterAndResolveSingleton()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning test

Expected 2 blank lines after function; 1 found

public function testRegisterAndResolveTransient()

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testRegisterAndResolveTransient() Warning test

Missing doc comment for function testRegisterAndResolveTransient()
{
$container = new DIContainer();
$container->registerTransient('service', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
return new stdClass();
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$service1 = $container->get('service');
$service2 = $container->get('service');

$this->assertNotSame($service1, $service2);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testRegisterAndResolveTransient() Warning test

Expected //end testRegisterAndResolveTransient()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning test

Expected 2 blank lines after function; 1 found

public function testServiceNotFound()

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testServiceNotFound() Warning test

Missing doc comment for function testServiceNotFound()
{
$this->expectException(Psr\Container\NotFoundExceptionInterface::class);

$container = new DIContainer();
$container->get('non_existent_service');
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning test

Expected 2 blank lines after function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testServiceNotFound() Warning test

Expected //end testServiceNotFound()

public function testHasMethod()

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testHasMethod() Warning test

Missing doc comment for function testHasMethod()
{
$container = new DIContainer();
$container->registerSingleton('service', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
return new stdClass();
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$this->assertTrue($container->has('service'));
$this->assertFalse($container->has('non_existent_service'));
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testHasMethod() Warning test

Expected //end testHasMethod()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning test

Expected 2 blank lines after function; 1 found

public function testExceptionOnResolutionFailure()

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testExceptionOnResolutionFailure() Warning test

Missing doc comment for function testExceptionOnResolutionFailure()
{
$this->expectException(Psr\Container\ContainerExceptionInterface::class);

$container = new DIContainer();
$container->register('service', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
throw new Exception('Resolution failed');
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$container->get('service');
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 0 found Warning test

Expected 2 blank lines after function; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testExceptionOnResolutionFailure() Warning test

Expected //end testExceptionOnResolutionFailure()
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end class Warning test

Expected //end class
55 changes: 55 additions & 0 deletions tests/Integration/DIContainerIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

use PHPUnit\Framework\TestCase;
use Pancake\DIContainer;

class DIContainerIntegrationTest extends TestCase
{
public function testIntegrationWithPancakeFramework()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines before function; 0 found Warning test

Expected 2 blank lines before function; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testIntegrationWithPancakeFramework() Warning test

Missing doc comment for function testIntegrationWithPancakeFramework()
{
$container = new DIContainer();

// Simulate a service with dependencies

Check warning

Code scanning / Phpcs (reported by Codacy)

Inline comments must end in full-stops, exclamation marks, or question marks Warning test

Inline comments must end in full-stops, exclamation marks, or question marks
$container->registerSingleton('config', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
return ['setting' => 'value'];
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$container->registerSingleton('service', function ($c) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
$config = $c->get('config');
$service = new stdClass();
$service->config = $config;
return $service;
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$service = $container->get('service');

$this->assertEquals('value', $service->config['setting']);
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 1 found Warning test

Expected 2 blank lines after function; 1 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testIntegrationWithPancakeFramework() Warning test

Expected //end testIntegrationWithPancakeFramework()

public function testComplexDependencyChain()

Check warning

Code scanning / Phpcs (reported by Codacy)

Missing doc comment for function testComplexDependencyChain() Warning test

Missing doc comment for function testComplexDependencyChain()
{
$container = new DIContainer();

$container->registerSingleton('dependency1', function () {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
return new stdClass();
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$container->registerSingleton('dependency2', function ($c) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
$dep1 = $c->get('dependency1');
$dep2 = new stdClass();
$dep2->dep1 = $dep1;
return $dep2;
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$container->registerSingleton('mainService', function ($c) {

Check warning

Code scanning / Phpcs (reported by Codacy)

Opening parenthesis of a multi-line function call must be the last content on the line Warning test

Opening parenthesis of a multi-line function call must be the last content on the line
$dep2 = $c->get('dependency2');
$mainService = new stdClass();
$mainService->dep2 = $dep2;
return $mainService;
});

Check warning

Code scanning / Phpcs (reported by Codacy)

Closing parenthesis of a multi-line function call must be on a line by itself Warning test

Closing parenthesis of a multi-line function call must be on a line by itself

$mainService = $container->get('mainService');

$this->assertSame($mainService->dep2->dep1, $container->get('dependency1'));
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 2 blank lines after function; 0 found Warning test

Expected 2 blank lines after function; 0 found

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end testComplexDependencyChain() Warning test

Expected //end testComplexDependencyChain()

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected 1 blank line before closing function brace; 0 found Warning test

Expected 1 blank line before closing function brace; 0 found
}

Check warning

Code scanning / Phpcs (reported by Codacy)

Expected //end class Warning test

Expected //end class
Loading