GithubHelp home page GithubHelp logo

hartl3y94 / php-di Goto Github PK

View Code? Open in Web Editor NEW

This project forked from biurad/php-di

0.0 0.0 0.0 1.07 MB

Rade DI is a library that provides a simple and smart dependency injection for PHP

License: BSD 3-Clause "New" or "Revised" License

PHP 100.00%

php-di's Introduction

The PHP Rade DI

PHP Version Latest Version Workflow Status Code Maintainability Coverage Status Psalm Type Coverage Quality Score


divineniiquaye/rade-di is a HIGH performance smart tool for performing simple to complex dependency injection in your application for PHP 7.4+ created by Divine Niiquaye referenced to Nette DI and Pimple. This library provides an advance way of resolving services for best performance to your application.

This project adheres to a code of conduct. By participating in this project and its community, you are expected to uphold this code.

πŸ“¦ Installation & Basic Usage

This project requires PHP 7.4 or higher. The recommended way to install, is via Composer. Simply run:

$ composer require divineniiquaye/rade-di

Creating a container is a matter of creating a Container instance:

use Rade\DI\Container;

$container = new Container();

For registering services into container, a service must be a real valid PHP object type. Container implements both PSR-11 ContainerInterface and ArrayAccess, so here's an example to demonstrate:

Using Container without ArrayAccess

// Should service be autowired or not ...
$autowire = true;

// define some services
$container->set('session_storage', new SessionStorage('SESSION_ID'), $autowire);

$container->set(
    'session', // The unique service id identifier
    static fn(): Session => new Session($container['session_storage']),
    $autowire
);
// or
$container->set('session', $container->resolveClass(Session::class), $autowire);
// or further
$container->set('session', $container->lazy(Session::class), $autowire);

Using Container with ArrayAccess

// define some services
$container['session_storage'] = new SessionStorage('SESSION_ID');

$container['session'] = fn(): Session => new Session($container['session_storage']);
// or
$container['session'] = $container->lazy(Session::class);
// or
$container['session'] = $container->resolveClass(Session::class);
// or further
$container['session'] = new Session($container['session_storage']);

Using the defined services is also very easy:

// get the session object
$session = $container['session'];
// or
$session = $container->get('session');
// or using ArrayAccess
$session = $container['session'];
// or use it's service class name, parent classes or interfaces
$session = $container->get(Session::class);


// the above call is roughly equivalent to the following code:
$storage = new SessionStorage('SESSION_ID');
$session = new Session($storage);

Container supports reuseable service instance. This is means, a registered service which is resolved, is frozen and object's id does not change throughout your application using Rade DI.

Rade DI also supports autowiring except a return type of a callable is not define or better still if you do not want autowiring at all, use the container's set method. By default, registering services with ArrayAccess implementation are all autowired.

To prevent registered services from being shared, use the container's factory method.

$container['session'] = $container->definition(new Session($container['session_storage']), Definition::FACTORY);

With the example above, each call to $container['session'] returns a new instance of the session.

In some cases you may want to modify a service definition after it has been defined. You can use the extend() method to define additional code to be run on your service just after it is created:

$container['session_storage'] = function (Container $container) {
    return new $container['session_storage_class']($container['cookie_name']);
};

// By default container is passed unto second parameter, but can be omitted.
$container->extend('session_storage', function ($storage) {
    $storage->...();

    return $storage;
});

The first argument is the name of the service to extend, the second a function that gets access to the object instance and the container.

Also Rade has aliasing and tagging support for services. If you want to add a different name to a registered service, use alias method.

$container['film'] = new Movie('S1', 'EP202');
$container->alias('movie', 'film');

// Can be access by $container['film'] or $container['movie']

For tagging, perhaps you are building a report aggregator that receives an array of many different Report interface implementations.

$container['speed.report'] = new SpeedReport(...);
$container['memory.report'] = new MemoryReport(...);

$container->tag([SpeedReport::class, MemoryReport::class], ['reports']);

Once the services have been tagged, you may easily resolve them all via the tagged method:

$tags = $container->tagged('reports');
$reports = [];

foreach ($tags as [$report, $attr]) {
    $reports[] = $report;
}

$container->tag([SpeedReport::class, MemoryReport::class], ['reports'])

$manager = new ReportAggregator($reports);

// For the $attr var, this is useful if you need tag to have extra values
// for tagging, eg:
$container->tag([BackupProcessor::class, MonitorProcessor::class], ['process' => true]);
$container->tag(CacheProcessor::class, ['process' => false]);

foreach ($container->tagged('process') as [$process, $enabled]) {
    if ($enabled) {
        $manager->addProcessor($process);
    }
}

Container support services injection using a PHP 8 #[Inject] attribute for class object properties and methods. In order to use property injection, you have to attribute your public properties with #[Inject] and provide their type declaration, while with methods injection, you also have to attribute your injectable methods with #[Inject]. Container takes care of autowiring.

This injection directive is enabled by default on all services using Definition class or calling a service with either resolveClass, call or resolver's class resolve method.

Eg: Dependency Service1 will be passed by calling the injectService1 method, dependency Service2 will be assigned to the $service2 property:

use Rade\DI\Attribute\Inject;

class FooClass
{
    #[Inject]
	public Service2 $service2;

    private Service1 $service1;

    #[Inject]
	public function injectService1(Service1 $service)
	{
		$this->service1 = $service1;
	}

    public function getService1(): Service1
    {
        return $this->service1;
    }
}

NB: Property injection uses the declared typed name, while methods uses same, this types declared must be found in container else exception is thrown. I prefer this type of injection in my controllers, but nowhere else.

Rade Di has service provider support, which allows the container to be extensible and reuseable. With Rade DI, your project do not need so to depend on PSR-11 container so much. Using service providers in your project, saves you alot.

use Rade\DI\Container;

class FooProvider implements Rade\DI\ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function register(AbstractContainer $container, array $configs = []): void
    {
        // register some services and parameters
        // on $container
    }
}

Then, register the provider on a Container:

$container->register(new FooProvider());

Service providers support Symfony's config component for writing configuration for service definitions found in a provider. Implement the service provider class to Symfony\Component\Config\Definition\ConfigurationInterface.

Writing configurations for a service provider by default, the service provider's class name, becomes the key pointing to the require config data. Want to use a custom key name, set add a static getId method returning your custom key name.

Using Symfony's config component + Rade\DI\ContainerBuilder class is highly recommended.

$ composer require symfony/config

Also the Rade\DI\ServiceLocator class is intended of setting predefined services while instantiating them only when actually needed.

For service locators, Rade uses symfony's service contracts.

It also allows you to make your services available under different naming. For instance, you may want to use an object that expects an instance of EventDispatcherInterface to be available under the name event_dispatcher while your event dispatcher has been registered under the name dispatcher:

use Monolog\Logger;
use Rade\DI\ServiceLocator;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

class MyService implements ServiceSubscriberInterface
{
    /**
     * "logger" must be an instance of Psr\Log\LoggerInterface
     * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    private ?ContainerInterface $container;

    public function __construct(ServiceProviderInterface $provider = null)
    {
        $this->container = $provider;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedServices(): array
    {
        return ['logger', 'event_dispatcher' => 'dispatcher'];
    }
}

$container['logger'] = new Monolog\Logger();
$container['dispatcher'] = new EventDispatcher();

$container['service'] = $container->lazy(MyService::class);

πŸ““ Documentation

For in-depth documentation before using this library. Full documentation on advanced usage, configuration, and customization can be found at docs.divinenii.com.

⏫ Upgrading

Information on how to upgrade to newer versions of this library can be found in the UPGRADE.

🏷️ Changelog

SemVer is followed closely. Minor and patch releases should not introduce breaking changes to the codebase; See CHANGELOG for more information on what has changed recently.

Any classes or methods marked @internal are not intended for use outside of this library and are subject to breaking changes at any time, so please avoid using them.

πŸ› οΈ Maintenance & Support

(This policy may change in the future and exceptions may be made on a case-by-case basis.)

  • A new patch version released (e.g. 1.0.10, 1.1.6) comes out roughly every month. It only contains bug fixes, so you can safely upgrade your applications.
  • A new minor version released (e.g. 1.1, 1.2) comes out every six months: one in June and one in December. It contains bug fixes and new features, but it doesn’t include any breaking change, so you can safely upgrade your applications;
  • A new major version released (e.g. 1.0, 2.0, 3.0) comes out every two years. It can contain breaking changes, so you may need to do some changes in your applications before upgrading.

When a major version is released, the number of minor versions is limited to five per branch (X.0, X.1, X.2, X.3 and X.4). The last minor version of a branch (e.g. 1.4, 2.4) is considered a long-term support (LTS) version with lasts for more that 2 years and the other ones cam last up to 8 months:

Get a professional support from Biurad Lap after the active maintenance of a released version has ended.

πŸ§ͺ Testing

$ ./vendor/bin/phpunit

This will tests divineniiquaye/rade-di will run against PHP 7.4 version or higher.

πŸ›οΈ Governance

This project is primarily maintained by Divine Niiquaye Ibok. Contributions are welcome πŸ‘·β€β™€οΈ! To contribute, please familiarize yourself with our CONTRIBUTING guidelines.

To report a security vulnerability, please use the Biurad Security. We will coordinate the fix and eventually commit the solution in this project.

πŸ™Œ Sponsors

Are you interested in sponsoring development of this project? Reach out and support us on Patreon or see https://biurad.com/sponsor for a list of ways to contribute.

πŸ‘₯ Credits & Acknowledgements

πŸ“„ License

The divineniiquaye/rade-di library is copyright Β© Divine Niiquaye Ibok and licensed for use under the Software License.

php-di's People

Contributors

dependabot[bot] avatar divineniiquaye avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.