2.11. Registry

2.11.1. Purpose

To implement a central storage for objects often used throughout theapplication, is typically implemented using an abstract class with onlystatic methods (or using the Singleton pattern). Remember that this introducesglobal state, which should be avoided at all times! Instead implement it using Dependency Injection!

2.11.2. Examples

  • Zend Framework 1: Zend_Registry holds the application’s loggerobject, front controller etc.
  • Yii Framework: CWebApplication holds all the applicationcomponents, such as CWebUser, CUrlManager, etc.

2.11.3. UML Diagram

Alt Registry UML Diagram

2.11.4. Code

You can also find this code on GitHub

Registry.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Structural\Registry;
  4.  
  5. abstract class Registry
  6. {
  7. const LOGGER = 'logger';
  8.  
  9. /**
  10. * this introduces global state in your application which can not be mocked up for testing
  11. * and is therefor considered an anti-pattern! Use dependency injection instead!
  12. *
  13. * @var array
  14. */
  15. private static $storedValues = [];
  16.  
  17. /**
  18. * @var array
  19. */
  20. private static $allowedKeys = [
  21. self::LOGGER,
  22. ];
  23.  
  24. /**
  25. * @param string $key
  26. * @param mixed $value
  27. *
  28. * @return void
  29. */
  30. public static function set(string $key, $value)
  31. {
  32. if (!in_array($key, self::$allowedKeys)) {
  33. throw new \InvalidArgumentException('Invalid key given');
  34. }
  35.  
  36. self::$storedValues[$key] = $value;
  37. }
  38.  
  39. /**
  40. * @param string $key
  41. *
  42. * @return mixed
  43. */
  44. public static function get(string $key)
  45. {
  46. if (!in_array($key, self::$allowedKeys) || !isset(self::$storedValues[$key])) {
  47. throw new \InvalidArgumentException('Invalid key given');
  48. }
  49.  
  50. return self::$storedValues[$key];
  51. }
  52. }

2.11.5. Test

Tests/RegistryTest.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Structural\Registry\Tests;
  4.  
  5. use DesignPatterns\Structural\Registry\Registry;
  6. use stdClass;
  7. use PHPUnit\Framework\TestCase;
  8.  
  9. class RegistryTest extends TestCase
  10. {
  11. public function testSetAndGetLogger()
  12. {
  13. $key = Registry::LOGGER;
  14. $logger = new stdClass();
  15.  
  16. Registry::set($key, $logger);
  17. $storedLogger = Registry::get($key);
  18.  
  19. $this->assertSame($logger, $storedLogger);
  20. $this->assertInstanceOf(stdClass::class, $storedLogger);
  21. }
  22.  
  23. /**
  24. * @expectedException \InvalidArgumentException
  25. */
  26. public function testThrowsExceptionWhenTryingToSetInvalidKey()
  27. {
  28. Registry::set('foobar', new stdClass());
  29. }
  30.  
  31. /**
  32. * notice @runInSeparateProcess here: without it, a previous test might have set it already and
  33. * testing would not be possible. That's why you should implement Dependency Injection where an
  34. * injected class may easily be replaced by a mockup
  35. *
  36. * @runInSeparateProcess
  37. * @expectedException \InvalidArgumentException
  38. */
  39. public function testThrowsExceptionWhenTryingToGetNotSetKey()
  40. {
  41. Registry::get(Registry::LOGGER);
  42. }
  43. }