Table of Contents
What is Pimple and why is it used in PHP?
How does Pimple work?
How to install Pimple?
How to define a service in Pimple?
How to access services in Pimple?
How to share services in Pimple?
Can I extend the service in Pimple?
How to protect parameters in Pimple?
How to use Pimple in a project?
What are the benefits of using Pimple?
Home Backend Development PHP Tutorial PHP Master | Dependency Injection with Pimple

PHP Master | Dependency Injection with Pimple

Feb 24, 2025 am 08:57 AM

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();
    }
}
Copy after login
Copy after login
Copy after login
Copy after login

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();
    }
}
Copy after login
Copy after login
Copy after login
Copy after login
<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
Copy after login
Copy after login
Copy after login
<?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);
    }
}
Copy after login
<?php
class OAuth
{
    public function requestTwitterFeed($token) {
        // Retrieve and return twitter feed using the token         
    }
}
Copy after login

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
    }
}
Copy after login

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();
    }
}
Copy after login

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();
Copy after login

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;
    }
}
Copy after login

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();
    }
}
Copy after login
Copy after login
Copy after login
Copy after login

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();
    }
}
Copy after login
Copy after login
Copy after login

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();
    }
}
Copy after login
Copy after login
Copy after login
Copy after login

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();
    }
}
Copy after login
Copy after login
Copy after login

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 of this Website
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

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

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1653
14
PHP Tutorial
1251
29
C# Tutorial
1224
24
Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

What are Enumerations (Enums) in PHP 8.1? What are Enumerations (Enums) in PHP 8.1? Apr 03, 2025 am 12:05 AM

The enumeration function in PHP8.1 enhances the clarity and type safety of the code by defining named constants. 1) Enumerations can be integers, strings or objects, improving code readability and type safety. 2) Enumeration is based on class and supports object-oriented features such as traversal and reflection. 3) Enumeration can be used for comparison and assignment to ensure type safety. 4) Enumeration supports adding methods to implement complex logic. 5) Strict type checking and error handling can avoid common errors. 6) Enumeration reduces magic value and improves maintainability, but pay attention to performance optimization.

Describe the SOLID principles and how they apply to PHP development. Describe the SOLID principles and how they apply to PHP development. Apr 03, 2025 am 12:04 AM

The application of SOLID principle in PHP development includes: 1. Single responsibility principle (SRP): Each class is responsible for only one function. 2. Open and close principle (OCP): Changes are achieved through extension rather than modification. 3. Lisch's Substitution Principle (LSP): Subclasses can replace base classes without affecting program accuracy. 4. Interface isolation principle (ISP): Use fine-grained interfaces to avoid dependencies and unused methods. 5. Dependency inversion principle (DIP): High and low-level modules rely on abstraction and are implemented through dependency injection.

How does session hijacking work and how can you mitigate it in PHP? How does session hijacking work and how can you mitigate it in PHP? Apr 06, 2025 am 12:02 AM

Session hijacking can be achieved through the following steps: 1. Obtain the session ID, 2. Use the session ID, 3. Keep the session active. The methods to prevent session hijacking in PHP include: 1. Use the session_regenerate_id() function to regenerate the session ID, 2. Store session data through the database, 3. Ensure that all session data is transmitted through HTTPS.

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What is REST API design principles? What is REST API design principles? Apr 04, 2025 am 12:01 AM

RESTAPI design principles include resource definition, URI design, HTTP method usage, status code usage, version control, and HATEOAS. 1. Resources should be represented by nouns and maintained at a hierarchy. 2. HTTP methods should conform to their semantics, such as GET is used to obtain resources. 3. The status code should be used correctly, such as 404 means that the resource does not exist. 4. Version control can be implemented through URI or header. 5. HATEOAS boots client operations through links in response.

How do you handle exceptions effectively in PHP (try, catch, finally, throw)? How do you handle exceptions effectively in PHP (try, catch, finally, throw)? Apr 05, 2025 am 12:03 AM

In PHP, exception handling is achieved through the try, catch, finally, and throw keywords. 1) The try block surrounds the code that may throw exceptions; 2) The catch block handles exceptions; 3) Finally block ensures that the code is always executed; 4) throw is used to manually throw exceptions. These mechanisms help improve the robustness and maintainability of your code.

What are anonymous classes in PHP and when might you use them? What are anonymous classes in PHP and when might you use them? Apr 04, 2025 am 12:02 AM

The main function of anonymous classes in PHP is to create one-time objects. 1. Anonymous classes allow classes without names to be directly defined in the code, which is suitable for temporary requirements. 2. They can inherit classes or implement interfaces to increase flexibility. 3. Pay attention to performance and code readability when using it, and avoid repeatedly defining the same anonymous classes.

See all articles