search
HomeBackend DevelopmentPHP TutorialPHP Master | Dependency Injection with Pimple

PHP Master | Dependency Injection with Pimple

Core points

  • Dependency injection is a key concept in application development, and by injecting dependencies into modules rather than hard-code them, you can write more efficient and more maintainable code.
  • Pimple is a simple dependency injection container that uses PHP's closure to define dependencies in a manageable way, helping to keep your code maintainable.
  • The two main techniques for injecting dependencies are constructor-based dependency injection and setter-based injection, each with its own advantages and disadvantages.
  • Pimple supports the DRY principle of software development by acting as a container that defines dependencies, avoiding duplication, making it easier to manage and centralize services in applications.
  • Pimple also provides advanced features such as the ability to return the same instance using shared objects, and the ability to dynamically modify existing closures without affecting the original implementation.

In application development, we try to create standalone modules to reuse code in future projects. However, creating completely independent modules that provide useful functionality is difficult; unless their dependencies are properly managed, they can lead to maintenance nightmare. This is where dependency injection comes in handy, as it allows us to inject the dependencies needed for the code to function properly without hard-code them into the module. Pimple is a simple dependency injection container that utilizes PHP's closure to define dependencies in a manageable way. In this article, we will explore the problem of hard-coded dependencies, how dependency injection solves these problems, and how to use Pimple to make code leveraging dependency injection easier to maintain.

Specific dependencies issue

When writing applications, we use many PHP classes. A class may need to call methods of one or more other classes to provide the expected functionality, so we say that the first class depends on other classes. For example:

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}

Class A depends on class B. If class B is not available, the above code will not work. Furthermore, every time we hardcode the creation of an object in the class, we will have specific dependencies on the class. Specific dependencies are a barrier to writing testable code. A better approach is to provide an object of class B to class A. These objects can be provided by A's constructor or setter method. Before we further discuss, let's look at a more realistic scenario.

Sharing content on social network sites is very common these days, and most websites directly display their social profile feeds on their websites. Suppose we have a class called SocialFeeds that generates feeds from social sites such as Twitter, Facebook, Google, etc. Create separate classes to handle each of these services. Here we will look at the class TwitterService that interacts with Twitter. The SocialFeeds class uses TwitterService to request Twitter feeds. TwitterService interacts with the database to retrieve specific user tokens that access the API. The token is passed to the OAuth class, which retrieves the feed using the provided token and returns it to the SocialFeeds class.

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}
<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
<?php
class TwitterService
{
    public function getTweets() {
        $db = new DB();
        $query = "Query to get user token from database";
        $token = $db->getQueryResults($query);

        $oauth = new OAuth();
        return $oauth->requestTwitterFeed($token);
    }
}
<?php
class OAuth
{
    public function requestTwitterFeed($token) {
        // Retrieve and return twitter feed using the token         
    }
}

It is obvious that SocialFeeds relies on TwitterService. But TwitterService depends on DB and OAuth, so SocialFeeds depend on DB and OAuth indirectly. So what is the problem? SocialFeeds rely on the concrete implementation of three classes, so it is impossible to test SocialFeeds separately without the real implementation of other classes. Or, suppose we want to use a different database or a different OAuth provider. In this case, we have to replace the existing class with the new class throughout the code.

Fix specific dependencies

The solution to these dependencies is simple, i.e. dynamically provide objects when necessary without using concrete implementations. There are two types of techniques that can inject dependencies: constructor-based dependency injection and setter-based injection.

Constructor-based injection

Using constructor-based dependency injection, the dependency object is created externally and passed as a parameter to the constructor of the class. We can assign these objects to class variables and use them anywhere within the class. The constructor-based injection of the SocialFeeds class is as follows:

<?php
class DB
{
    public function getQueryResults($query) {
        // Get results from database and return token
    }
}

An instance of TwitterService is passed as an object to the constructor. SocialFeeds still relies on TwitterService, but now we are free to provide different versions of Twitter service providers, and even mock objects for testing purposes. Regarding TwitterService, DB and OAuth classes are also defined in a similar way.

<?php
class SocialFeeds
{
    public $twService;

    public function __construct($twService) {
        $this->twService = $twService;
    }

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }
}

Seter-based injection

Using setter-based injection, objects are provided by setter methods instead of constructors. The following is the setter-based dependency injection implementation of the SocialFeeds class:

<?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService($db, $oauth);
$socialFeeds = new SocialFeeds($twService);
$socialFeeds->getSocialFeeds();

The initialization code now includes DB and OAuth looks like this:

<?php
class SocialFeeds
{
    public $twService;

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }

    public function setTwitterService($twService) {
        $this->twService = $twService;
    }
}

Constructor and Setter Injection

Select constructor-based injection or setter-based injection is up to you. Constructor-based injection is suitable when all dependencies are required to instantiate a class. Setter-based injection is suitable when dependencies are not required every time.

Pros

  • Constructor – Just look at the constructor of the class to identify all dependencies of the class
  • Setor - Adding new dependencies is as simple as adding a new setter method and does not break existing code

Disadvantages

  • Constructor – Adding new dependencies will add parameters to the constructor; existing code in the entire application needs to be updated to provide new dependencies
  • Setor - We have to search for necessary dependencies manually because they are not specified anywhere

After understanding dependency injection and various injection techniques, it's time to see Pimple and how it fits into it.

Pimple's role in DI

When we can already inject dependencies using the aforementioned techniques, you may be wondering why you need Pimple. To answer this question, we need to look at the DRY principle.

Don't repeat yourself (DRY) is a principle in software development that aims to reduce the duplication of various information, which is especially useful in multi-layer architectures. The statement of the DRY principle is that "every fragment of knowledge must have a single, clear, authoritative representation in a system" - Wikipedia

Consider the constructor-based injection example. Every time we want an object of the SocialFeed class, we have to repeat the entire setup process of instantiating and passing its dependencies. According to DRY, such code should be avoided to prevent maintenance problems. Pimple acts as a container that defines such dependencies to avoid duplication. Let's look at a simple example to see how Pimple works.

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}

Create an instance of Pimple as a container for storing dependencies. It implements the SPL ArrayAccess interface, so using it is very similar to using arrays. First, we define a key that holds the name of any class we want. We then define a closure to return an instance of the specified class that acts as a service. Note that an instance of the container will be passed to $c, so we can refer to other defined keys as needed; each defined parameter or object can be used in the closure via the $c variable. Now, whenever we want an instance of a class, we can reference the key to retrieve the object. Let's convert the SocialFeeds example to Pimple. The examples on the official Pimple website show constructor-based injection, so here we will illustrate setter-based injection. Remember that in order to use Pimple, we don't need to modify any setter methods or code defined earlier - we just encapsulate the logic.

<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}

DB and OAuth classes are both independent modules, so we return their new instances directly within the closure. We then use setter-based injection to add dependencies to the TwitterService class. We have added the DB and OAuth classes to the container, so we can access them directly inside the function using $c['db'] and $c['oauth']. Now, the dependencies are encapsulated within the container as a service. Whenever we want to use a different DB class or a different OAuth service, we just replace the class in the container statement and everything will run perfectly. With Pimple, you just need to add new dependencies in one place.

Advanced Pimple Usage

In the above scenario, Pimple returns a new instance of each class from the closure every time it is requested. In some cases, we need to use the same object without initializing a new instance every time, such as connecting to a database is a perfect example. Pimple provides the ability to return the same instance using a shared object. In doing so, we need to specify the closure through the share() method, as shown below:

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}

Also, so far we have defined all dependencies in a single location in the Pimple container. However, consider a case where we need a service that has its dependencies but is configured slightly differently from the original service. For example, suppose we need to access the ORM to implement some functionality of the TwitterService class. We cannot change the existing closure because it forces all existing functions to use ORM. Pimple provides the extend() method to dynamically modify existing closures without affecting the original implementation. Consider the following code:

<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}

Now, we are able to use different extension versions of tweet_service in special cases. The first parameter is the name of the service, and the second parameter is a function that can access object instances and containers. In fact, extend() is a powerful way to dynamically add dependencies to suit different situations, but make sure to limit the extended version of the service to a minimum, as it increases the amount of duplicate code.

Summary

Managing dependencies is one of the most important and difficult tasks in web application development. We can use dependency injection of constructors and setter methods to effectively manage them. However, dependency injection itself also has some troubles, and Pimple solves these problems by providing a lightweight container to create and store object dependencies in the DRY way. Feel free to share your experience managing dependencies in your project and your thoughts on Pimple as a dependency injection container in the comments below.

FAQs about Dependency Injection with Pimple (FAQ)

What is Pimple and why is it used in PHP?

Pimple is a simple PHP dependency injection container that allows you to manage and centralize services in your application. It is used in PHP, making the code more flexible, reusable and easier to test. By using Pimple, you can instantiate objects in one place and inject them into different parts of your application, reducing the need for global state and making your code easier to maintain and test.

How does Pimple work?

Pimple works by storing service definitions in containers. These definitions are callable (function or method) and they return instances of the service. When you access a service from a container, Pimple executes the service definition to create a service object. This allows you to manage services in a centralized manner and share services throughout the application.

How to install Pimple?

Pimple can be installed using Composer (PHP's dependency management tool). You can install Composer globally on your system and then introduce Pimple in your project by running the command composer require pimple/pimple.

How to define a service in Pimple?

In Pimple, you can define a service by assigning a callable object to a key in a container. The callable object should return an instance of the service. For example, you could define a service for the mail sender class like this:

$container['mailer'] = function ($c) { return new Mailer($c['smtp']); };

In this example, the mail sender service is defined as a new instance of the Mailer class, where the smtp service is injected as a dependency.

How to access services in Pimple?

You can access services in Pimple using array notation with service keys. For example, you can access the mail sender service like this: $mailer = $container['mailer'];. When you access a service, Pimple executes the service definition and returns the service object.

How to share services in Pimple?

By default, Pimple returns a new instance of the service every time it accesses the service. If you want to share the service and return the same instance each time, you can use the share() method. For example, you can share the mail sender service like this: $container['mailer'] = $container->share(function ($c) { return new Mailer($c['smtp']); });.

Can I extend the service in Pimple?

Yes, you can use the extend() method to extend services in Pimple. This allows you to modify the service after defining it. For example, you can extend the mail sender service like this to add additional configuration:

$container['mailer'] = $container->extend('mailer', function ($mailer, $c) { $mailer->setFrom($c['email.from']); return $mailer; });

In this example, the setFrom() method is called on the mail sender service using the email.from service as a parameter.

How to protect parameters in Pimple?

In Pimple, you can use the protect() method to protect parameters (which should not be considered as parameters of services). This allows you to store values ​​in a container without treating them as service definitions. For example, you can protect the configuration value like this: $container['config.value'] = $container->protect(function () { return 'value'; });.

How to use Pimple in a project?

You can use Pimple in a project by creating a new instance of the PimpleContainer class and defining a service there. You can then access the service from the container where you need it in the application. This allows you to manage services in a centralized manner and inject them into different parts of your application.

What are the benefits of using Pimple?

Pimple provides many benefits for PHP development. It makes your code more flexible because it allows you to manage services in a centralized way. It makes your code easier to reuse because it allows you to share services throughout your application. It makes your code easier to test because it allows you to inject mock services for testing. By using Pimple, you can improve the quality of your code and make it easier to maintain and test.

The above is the detailed content of PHP Master | Dependency Injection with Pimple. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
What is the difference between unset() and session_destroy()?What is the difference between unset() and session_destroy()?May 04, 2025 am 12:19 AM

Thedifferencebetweenunset()andsession_destroy()isthatunset()clearsspecificsessionvariableswhilekeepingthesessionactive,whereassession_destroy()terminatestheentiresession.1)Useunset()toremovespecificsessionvariableswithoutaffectingthesession'soveralls

What is sticky sessions (session affinity) in the context of load balancing?What is sticky sessions (session affinity) in the context of load balancing?May 04, 2025 am 12:16 AM

Stickysessionsensureuserrequestsareroutedtothesameserverforsessiondataconsistency.1)SessionIdentificationassignsuserstoserversusingcookiesorURLmodifications.2)ConsistentRoutingdirectssubsequentrequeststothesameserver.3)LoadBalancingdistributesnewuser

What are the different session save handlers available in PHP?What are the different session save handlers available in PHP?May 04, 2025 am 12:14 AM

PHPoffersvarioussessionsavehandlers:1)Files:Default,simplebutmaybottleneckonhigh-trafficsites.2)Memcached:High-performance,idealforspeed-criticalapplications.3)Redis:SimilartoMemcached,withaddedpersistence.4)Databases:Offerscontrol,usefulforintegrati

What is a session in PHP, and why are they used?What is a session in PHP, and why are they used?May 04, 2025 am 12:12 AM

Session in PHP is a mechanism for saving user data on the server side to maintain state between multiple requests. Specifically, 1) the session is started by the session_start() function, and data is stored and read through the $_SESSION super global array; 2) the session data is stored in the server's temporary files by default, but can be optimized through database or memory storage; 3) the session can be used to realize user login status tracking and shopping cart management functions; 4) Pay attention to the secure transmission and performance optimization of the session to ensure the security and efficiency of the application.

Explain the lifecycle of a PHP session.Explain the lifecycle of a PHP session.May 04, 2025 am 12:04 AM

PHPsessionsstartwithsession_start(),whichgeneratesauniqueIDandcreatesaserverfile;theypersistacrossrequestsandcanbemanuallyendedwithsession_destroy().1)Sessionsbeginwhensession_start()iscalled,creatingauniqueIDandserverfile.2)Theycontinueasdataisloade

What is the difference between absolute and idle session timeouts?What is the difference between absolute and idle session timeouts?May 03, 2025 am 12:21 AM

Absolute session timeout starts at the time of session creation, while an idle session timeout starts at the time of user's no operation. Absolute session timeout is suitable for scenarios where strict control of the session life cycle is required, such as financial applications; idle session timeout is suitable for applications that want users to keep their session active for a long time, such as social media.

What steps would you take if sessions aren't working on your server?What steps would you take if sessions aren't working on your server?May 03, 2025 am 12:19 AM

The server session failure can be solved through the following steps: 1. Check the server configuration to ensure that the session is set correctly. 2. Verify client cookies, confirm that the browser supports it and send it correctly. 3. Check session storage services, such as Redis, to ensure that they are running normally. 4. Review the application code to ensure the correct session logic. Through these steps, conversation problems can be effectively diagnosed and repaired and user experience can be improved.

What is the significance of the session_start() function?What is the significance of the session_start() function?May 03, 2025 am 12:18 AM

session_start()iscrucialinPHPformanagingusersessions.1)Itinitiatesanewsessionifnoneexists,2)resumesanexistingsession,and3)setsasessioncookieforcontinuityacrossrequests,enablingapplicationslikeuserauthenticationandpersonalizedcontent.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

DVWA

DVWA

Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use