/src/MasterFactory.php

https://github.com/PHPGangsta/factory · PHP · 194 lines · 76 code · 22 blank · 96 comment · 6 complexity · badc7a6452fd7c8da1a44c03102f1210 MD5 · raw file

  1. <?php
  2. /**
  3. * Copyright (c) 2011 Stefan Priebsch <stefan@priebsch.de>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright notice,
  10. * this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright notice,
  13. * this list of conditions and the following disclaimer in the documentation
  14. * and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of Stefan Priebsch nor the names of contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
  24. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  25. * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  26. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  27. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  29. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30. * POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. * @package factory
  33. * @author Stefan Priebsch <stefan@priebsch.de>
  34. * @copyright Stefan Priebsch <stefan@priebsch.de>. All rights reserved.
  35. * @license BSD License
  36. */
  37. namespace spriebsch\factory;
  38. /**
  39. * The application factory is a facade to all factories.
  40. * It cannot create any instances itself, but knows which factory is
  41. * responsible for creating which instance and delegates instantiation.
  42. *
  43. * @author Stefan Priebsch <stefan@priebsch.de>
  44. * @copyright Stefan Priebsch <stefan@priebsch.de>. All rights reserved.
  45. */
  46. class MasterFactory implements MasterFactoryInterface
  47. {
  48. /**
  49. * Associative array tracking which factory is responsible for a type.
  50. * Array keys are types, array values are references to factory instances.
  51. *
  52. * @var array
  53. */
  54. private $typeMap = array();
  55. /**
  56. * Returns an instance of the requested type by delegating call to
  57. * responsible child factory instance.
  58. *
  59. * @param string $type
  60. * @return NULL
  61. */
  62. public function getInstanceFor($type)
  63. {
  64. if (!$this->hasType($type)) {
  65. throw new FactoryException('Cannot instantiate type "' . $type . '"',
  66. FactoryException::TYPE_CANNOT_INSTANTIATE);
  67. }
  68. $factory = $this->getFactoryFor($type);
  69. return call_user_func_array(array($factory, 'getInstanceFor'), func_get_args());
  70. }
  71. /**
  72. * Registers a child factory by setting the master in the child
  73. * and registering each type the child factory can instantiate.
  74. *
  75. * @param ChildFactoryInterface $factory
  76. * @return NULL
  77. */
  78. public function register(ChildFactoryInterface $factory)
  79. {
  80. if ($this->isRegistered($factory)) {
  81. throw new FactoryException('Child factory instance of "' . get_class($factory) . '" is already registered',
  82. FactoryException::TYPE_ALREADY_REGISTERED);
  83. }
  84. $factory->setMaster($this);
  85. $types = $factory->getTypes();
  86. if (count($types) == 0) {
  87. throw new FactoryException('Child Factory instance of "' . get_class($factory) . '" cannot instantiate any types',
  88. FactoryException::TYPE_NO_TYPES);
  89. }
  90. foreach ($types as $type) {
  91. $this->addType($type, $factory);
  92. }
  93. }
  94. /**
  95. * Returns an array of types all registered factories are capable of creating.
  96. * Each value in the array is unique.
  97. *
  98. * @returns array
  99. */
  100. public function getTypes()
  101. {
  102. return array_keys($this->typeMap);
  103. }
  104. public function __toString()
  105. {
  106. $result = '';
  107. $reverseMap = array();
  108. $typeMap = array_map(function($instance) { return get_class($instance); }, $this->typeMap);
  109. foreach ($typeMap as $key => $value) {
  110. if (!isset($reverseMap[$value])) {
  111. $reverseMap[$value] = array();
  112. }
  113. $reverseMap[$value][] = $key;
  114. }
  115. $result .= 'Master Factory: "' . get_class($this) . '"' . PHP_EOL;
  116. foreach ($reverseMap as $factory => $types) {
  117. $result .= PHP_EOL . 'Factory "' . $factory . '" can instantiate the following types:' . PHP_EOL;
  118. foreach ($types as $type) {
  119. $result .= '- ' . $type . PHP_EOL;
  120. }
  121. }
  122. return $result . PHP_EOL;
  123. }
  124. /**
  125. * Adds a type and a reference to the factory that can instantiate this type
  126. *
  127. * @param string $type
  128. * @param ChildFactoryInterface $factory
  129. * @return NULL
  130. */
  131. protected function addType($type, ChildFactoryInterface $factory)
  132. {
  133. $this->typeMap[$type] = $factory;
  134. }
  135. /**
  136. * Checks whether given type is already registered
  137. *
  138. * @param string $type
  139. * @return bool
  140. */
  141. protected function hasType($type)
  142. {
  143. return array_key_exists($type, $this->typeMap);
  144. }
  145. /**
  146. * Returns the factory instance that can instantiate the given type
  147. * hasType() check deliberately left out, as it would be untestable due
  148. * to the "already registered" guard clause in getInstance().
  149. *
  150. * @param $type
  151. * @return NULL
  152. */
  153. protected function getFactoryFor($type)
  154. {
  155. return $this->typeMap[$type];
  156. }
  157. /**
  158. * Checks whether given factory is already registered
  159. *
  160. * @param ChildFactoryInterface $factory
  161. * @return bool
  162. */
  163. protected function isRegistered(ChildFactoryInterface $factory)
  164. {
  165. foreach (array_values($this->typeMap) as $f) {
  166. if ($factory === $f) {
  167. return TRUE;
  168. }
  169. }
  170. return FALSE;
  171. }
  172. }