Coder Social home page Coder Social logo

phpgt / servicecontainer Goto Github PK

View Code? Open in Web Editor NEW
0.0 3.0 1.0 99 KB

Centralised container of a project's core objects.

Home Page: https://www.php.gt/servicecontainer

License: MIT License

PHP 100.00%
dependency-injection dependency-injection-container service-container maintainability testability autowiring

servicecontainer's Introduction

Centralised container of a project's core objects.

When PHP applications have a lot of classes, it's important to use dependency injection techniques to keep your code maintainable and testable; rather than constructing objects within the functions they are used, pass the objects into the function via parameters.

Within this repository, a Container is an object that can be assigned pre-constructed instances of objects, and an Injector is an object that can automatically invoke other objects' methods with the matching instances from the Container.

There are no rules to how this repository should be used. You may want to have a single Container accessible to a single layer of your code, or you may want to have multiple Containers within your application to provide a different pool of object instances to different areas of your code.

This repository is used within WebEngine to automatically invoke Page Logic functions, allowing the developer to have a single point within the application that is responsible for the object access of all first and third party classes.


Build status Code quality Code coverage Current version PHP.G/ServiceContainer documentation

servicecontainer's People

Contributors

dependabot[bot] avatar g105b avatar

Watchers

 avatar  avatar  avatar

Forkers

isabella232

servicecontainer's Issues

Allow getting nullable type

It would be super useful to be able to request a nullable type from within a service-container function. For example, in a WebEngine project, to request a ?User $user as a parameter would be so useful, because then the developer could know whether the app is authenticated by whether $user is null or not.

Methods without a return type should be ignored

It's fine to have a method without a return type, sometimes. In this case, it shouldn't be used. This is especially important when creating your own ServiceContainer because you may want a constructor, which can't have a return type.

Exposing lazy-loaded interfaces

An often overlooked benefit of using service containers is the fact that, as the framework knows when and where a service is being requested, it doesn't actually need to construct the object until the first time it's requested.

A good example of where this will benefit is the Database. Using a lazy-loaded service container has two implementation benefits:

  1. On pages that don't query the database, the database object is never constructed, so no connections are made to the database until they're used.
  2. The framework can suggest default behaviour for the construction of objects, but if the developer wants to use a different implementation of the same interface, they are free to override it.

The other benefit is how the container is used: the developer can register their own classes in the container, again only being constructed at use-time, allowing much richer abstraction patterns like the Repository Pattern hiding any database implementations.

Lazy Load - no argument necessary if return type specified

It would be a nice improvement to not have to duplicate the class name in the LazyLoad attribute and return type.

For example:

class ServiceLoader {
	#[LazyLoad(ContentRepository::class)]
	public function getContentRepository():ContentRepository {
		// ...
	}
}

Could become simply:

class ServiceLoader {
	#[LazyLoad]
	public function getContentRepository():ContentRepository {
		// ...
	}
}

Generic Container::get()

Getting a service from the container with ->get($className) will always return null or an instance of the class name.

PHPStan and PhpStorm can both understand generics, even though there isn't any capability in the language itself for them.

This would be a lovely feature to remove the need to put /** @var whatever **/ everywhere.

Should the ServiceContainer allow the app to overload default extension classes?

Specific example: My app deals with editing HTTP Requests, so I have a class called EditableRequest that extends Gt\Http\Request. Because of this, the EditableRequest class is instantiated and passed to the DefaultServiceLoader as the actual Request, rather than the actual request that's coming in.

This is obviously a flaw in the system, because it should be fine for the App's service loader to supply classes that extend already-existing classes.

The question is, what should the default behaviour be? If there's already a class defined in the default service loader, should we ignore loading any extras that simply extend the base classes? I think that would be safest.

Provide specific objects on individual invocation

We already have this:
https://github.com/PhpGt/ServiceContainer/blob/master/src/Injector.php#L26C24-L26C24

This $extraArgs parameter is currently not used anywhere, as I've never found a use for matching a parameter by string.

I think we should drop this functionality completely, and replace the function of $extraArgs.

At the point of invocation, I would like to be able to pass a specific object, so we can have DomTemplate Components with their own PHP, solving the issue described in PhpGt/DomTemplate#331

When a DomTemplate Component is detected, the invoke function of the Injector should be called with a few extra indices of $extraArgs: The Gt\Dom\Element of the component, and a new ComponentBinder (the same as a DocumentBinder, but with a pre-constrained context element of the current component).

This would be a fabulous addition to WebEngine development!

Chained loaders

It's very common for one service to require another, and the way I have been coding this for the last few projects is styled like this:

#[LazyLoad]
function loadServiceA():ServiceA {
  /** @var ServiceB **/
  $serviceB = $this->container->get(ServiceB::class);
  return new ServiceA($serviceB);
}

But this isn't very DRY. There's a better way: as we're already in the ServiceContainer code at this point, it should be possible to list each loader's dependencies as parameters, so the above code would update to look like this:

#[LazyLoad]
function loadServiceA(ServiceB $serviceB):ServiceA {
  return new ServiceA($serviceB);
}

Because of the hard-dependency on another class, we should be able to type-safely load it in the parameters of the function.

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.