Ditch __get: Use Dependency Injection Instead

Cleaning Up Hidden Dependencies: Why Dependency Injection Beats __get in PHP

In many legacy or homegrown PHP codebases, you’ll often come across classes that rely on magic methods like __get to lazily load dependencies.

While this approach might seem convenient and even “smart” at first glance, it can create a host of hidden problems — from untestable code to tightly coupled logic that’s hard to refactor.

In this post, we’ll explore why this pattern is problematic and how Dependency Injection (DI) provides a cleaner, more scalable, and testable alternative.


The Magic Behind __get

The __get() magic method in PHP is called whenever an inaccessible or non-existent property is read from an object. Developers often use this to “automatically” fetch service instances:

The __get() method then returns a singleton or service instance based on the property name. While clever, this introduces invisible dependencies into your class. It looks like $this->api is a regular property, but it’s actually being constructed or fetched behind the scenes.


The Problem with Magic

Here’s why relying on __get (or any similar magic mechanism) can become a long-term liability:

1. Hidden Dependencies

Developers reading the class can’t easily tell what services it depends on. IDEs and static analyzers can’t either.

2. Poor Testability

Because dependencies are hardcoded and fetched internally (often using singletons), they’re hard to mock or replace in unit tests.

3. Tightly Coupled Architecture

The class knows too much about how to get its dependencies. It violates the Single Responsibility Principle by managing both its logic and the wiring of its services.

4. Fragile Code

Since the dependency list lives in a switch-case or lookup table inside __get, adding or changing services often requires digging through that logic — and errors won’t be caught until runtime.


Dependency Injection to the Rescue

Instead of hiding dependencies behind magic methods, Dependency Injection (DI) makes them explicit. This means providing required services to a class through its constructor or setter methods.

Constructor Injection Example

Now the class is:

  • Explicit about what it needs
  • Easy to test (you can mock Cart or Order for a unit test)
  • Easy to refactor and evolve
  • Framework-friendly (works naturally with service containers in Laravel, Symfony, etc.)

Testing Becomes Simple

You no longer need to override magic logic or mock static methods.


Use a Dependency Injection Container

Frameworks like Laravel, Symfony, or libraries like PHP-DI can auto-inject dependencies into your class constructors. This means you still enjoy the convenience of automatic wiring — but with all the benefits of clean architecture.


Conclusion

Magic methods like __get can feel like a shortcut, but they often lead to tangled, opaque code that’s difficult to test and maintain. By adopting Dependency Injection, your classes become simpler, clearer, and far more flexible — a huge win for projects of any size.

If you’re refactoring a legacy application or starting something new, take the time to invest in DI. It’s one of the cleanest, most future-proof design decisions you can make in PHP.

Crazy about CRO?

15+ ideas for growing your eCommerce store

Join & get tip & tricks for eCommerce Growth

We don’t spam! Read more in our privacy policy

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *