DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Fluent Assertions Reloaded
  • Page Object Model for Performance Testing With Gatling
  • TestCafe Integration With Cucumber
  • AI-Driven Test Automation Techniques for Multimodal Systems

Trending

  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  • Is Agile Right for Every Project? When To Use It and When To Avoid It
  • Start Coding With Google Cloud Workstations
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Patterns: Registry

Practical PHP Patterns: Registry

By 
Giorgio Sironi user avatar
Giorgio Sironi
·
Sep. 22, 10 · Interview
Likes (1)
Comment
Save
Tweet
Share
7.7K Views

Join the DZone community and get the full member experience.

Join For Free
Fowler's definition for the Registry pattern is this one:
A well known object that other ones can use to find related objects or service.

This vague definition leaves open the possibility of abuse.

Implementation

The idea of a Registry is simple: providing a dynamic possibility for discovering collaborator objects, so that we not hardcode static calls to global objects like Singletons in each of them. In the testing environment, we can fill the Registry with mocks.

However, the problem is that then we hardcode static calls to the Registry, which is an effective sinkhole for dependencies in the case of a general purpose implementation. You can't limit which objects of a Registry are accessed by a client, so depending on the Registry may mean depending on each stored component.

In this scenario, the Registry becomes a Service Locator, the poor man's version of Dependency Injection.

Fixing it

My suggestion before implementing this pattern is thinking about how many of the registered objects would the ordinary client object actually need; maybe you can inject them directly.

A constraint I'd like to set for Registry implementations is that all the contained objects should be of the same type (or of a Layer Supertype). With this limitation, incarnations of the Registry become very useful as they can be injected and passed around as a virtual collection of object, even if they do not exist/currently are in persistence/are not all in memory at the same time.

If this reminds you of the Repository, you're right; the Repository pattern is a specialization of the Registry one, with this limitation written with blood in its contract in a night of full moon.

This is what a Registry is supposed to be: not an hidden blackboard like Zend_Registry where you can practice accumulate and fire, and setup time-ticking bombs that will explode later.

Here's an example from our test suite, which used Zend_Registry to set up the locale. One day, uncommenting a test would make another one fail (it used Zend_Registry, and the uncommented one messed with some keys). Fixing it takes some minutes (but it should have taken seconds). Scale that to a test suite with one thousands tests and you can easily destroy your tests isolation. Shortly, one test would pass when executed alone, but explode when the whole test suite (which maybe takes 15 minutes to run) because a previous test, whose identity you do not know, modified some global state. Good luck fixing that and grepping for 'Zend_Registry::'.

Instead, a Registry can be a first-class citizen, an object that can be injected, by itself or hidden behind an interface of a composer, into any client object that sincerely needs access to the whole encapsulated collection.

Why you now feel a generic Registry could be handy

Lifecycle problems can create the need for a generic Registry. How do you access an object A from a client object B if A do not exist when B is created? Use a Registry. Of course, this solution does not go a long way: you're never sure that A would be created in time, and you should revise your design to discover why a long-lived object should hold a reference to a short-lived one.

In fact, higher-level or injected Factories ara a similar, but more powerful solution. If B needs A, and A has the same lifecycle, simply inject it via a request-wide Factory. If B and A have a shorter lifecycle than the one of the request (for example they are view helpers that may not be used at all in certain requests, so they are instantiated only when needed in the presentation layer), provide a Factory that craates both and inject the Factory in the right scope.

However, never inject a generic registry: it is a false solution. The Registry becomes a Service Locator, which is depended on by every piece of your application, and depends again on every piece of it. This is a benignuos form of what I call "hiding dependencies under the carpet", which can be solved by injecting the direct dependency instead of a provider.

Examples

Our code sample is the infamous Zend_Registry component from Zend Framework 1. It is a Singleton, and when testing controllers it is a typical resource that the testing harness must remember to reset between each test. 

My comments are in italic.

<?php
/**
 * Generic storage class helps to manage global data.
 * Here the error is "global". No data should be really global:
 * at most request-wide.
 *
 * @category   Zend
 * @package    Zend_Registry
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Registry extends ArrayObject
{
    /**
     * Class name of the singleton registry object.
     * @var string
     */
    private static $_registryClassName = 'Zend_Registry';

    /**
     * Registry object provides storage for shared objects.
     * @var Zend_Registry
     */
    private static $_registry = null;

    /**
     * Retrieves the default registry instance.
     * A Singleton is a recipe for hiding dependencies to a class.
     *
     * @return Zend_Registry
     */
    public static function getInstance()
    {
        if (self::$_registry === null) {
            self::init();
        }

        return self::$_registry;
    }

    /**
     * Initialize the default registry instance.
     *
     * @return void
     */
    protected static function init()
    {
        self::setInstance(new self::$_registryClassName());
    }

    /**
     * getter method, basically same as offsetGet().
     *
     * This method can be called from an object of type Zend_Registry, or it
     * can be called statically.  In the latter case, it uses the default
     * static instance stored in the class.
     *
     * @param string $index - get the value associated with $index
     * @return mixed
     * @throws Zend_Exception if no entry is registerd for $index.
     */
    public static function get($index)
    {
        $instance = self::getInstance();

        if (!$instance->offsetExists($index)) {
            require_once 'Zend/Exception.php';
            throw new Zend_Exception("No entry is registered for key '$index'");
        }

        return $instance->offsetGet($index);
    }

    /**
     * setter method, basically same as offsetSet().
     *
     * This method can be called from an object of type Zend_Registry, or it
     * can be called statically.  In the latter case, it uses the default
     * static instance stored in the class.
     *
     * @param string $index The location in the ArrayObject in which to store
     *   the value.
     * @param mixed $value The object to store in the ArrayObject.
     * @return void
     */
    public static function set($index, $value)
    {
        $instance = self::getInstance();
        $instance->offsetSet($index, $value);
    }

    /**
     * Returns TRUE if the $index is a named value in the registry,
     * or FALSE if $index was not found in the registry.
     *
     * @param  string $index
     * @return boolean
     */
    public static function isRegistered($index)
    {
        if (self::$_registry === null) {
            return false;
        }
        return self::$_registry->offsetExists($index);
    }

    /**
     * Constructs a parent ArrayObject with default
     * ARRAY_AS_PROPS to allow acces as an object
     *
     * @param array $array data array
     * @param integer $flags ArrayObject flags
     */
    public function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS)
    {
        parent::__construct($array, $flags);
    }
}

 

PHP Object (computer science) Testing

Opinions expressed by DZone contributors are their own.

Related

  • Fluent Assertions Reloaded
  • Page Object Model for Performance Testing With Gatling
  • TestCafe Integration With Cucumber
  • AI-Driven Test Automation Techniques for Multimodal Systems

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!