InputRegistry.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since CakePHP(tm) v3.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View\Input;
  16. use Cake\Core\App;
  17. use Cake\View\Input\InputInterface;
  18. use Cake\View\StringTemplate;
  19. use \ReflectionClass;
  20. /**
  21. * A registry/factory for input widgets.
  22. *
  23. * Can be used by helpers/view logic to build form widgets
  24. * and other HTML widgets.
  25. *
  26. * This class handles the mapping between names and concrete classes.
  27. * It also has a basic name based dependency resolver that allows
  28. * widgets to depend on each other.
  29. *
  30. * Each widget should expect a StringTemplate instance as their first
  31. * argument. All other dependencies will be included after.
  32. */
  33. class InputRegistry {
  34. /**
  35. * Array of widgets + widget configuration.
  36. *
  37. * @var array
  38. */
  39. protected $_widgets = [
  40. 'checkbox' => ['Cake\View\Input\Checkbox'],
  41. 'label' => ['Cake\View\Input\Label'],
  42. 'multicheckbox' => ['Cake\View\Input\MultiCheckbox', 'label'],
  43. 'radio' => ['Cake\View\Input\Radio', 'label'],
  44. 'select' => ['Cake\View\Input\SelectBox'],
  45. '_default' => ['Cake\View\Input\Basic'],
  46. ];
  47. /**
  48. * Templates to use.
  49. *
  50. * @var Cake\View\StringTemplate
  51. */
  52. protected $_templates;
  53. /**
  54. * Constructor
  55. *
  56. * @param StringTemplate $templates Templates instance to use.
  57. * @param array $widgets See add() method for more information.
  58. */
  59. public function __construct(StringTemplate $templates, array $widgets = []) {
  60. $this->_templates = $templates;
  61. if (!empty($widgets)) {
  62. $this->add($widgets);
  63. }
  64. }
  65. /**
  66. * Adds or replaces existing widget instances/configuration with new ones.
  67. *
  68. * Widget arrays can either be descriptions or instances. For example:
  69. *
  70. * {{{
  71. * $registry->add([
  72. * 'label' => new MyLabel($templates),
  73. * 'checkbox' => ['Fancy.MyCheckbox', 'label']
  74. * ]);
  75. * }}}
  76. *
  77. * The above shows how to define widgets as instances or as
  78. * descriptions including dependencies. Classes can be defined
  79. * with plugin notation, or fully namespaced class names.
  80. *
  81. * @param array $widgets Array of widgets to use.
  82. * @return void
  83. */
  84. public function add(array $widgets) {
  85. $this->_widgets = $widgets + $this->_widgets;
  86. }
  87. /**
  88. * Get a widget.
  89. *
  90. * Will either fetch an already created widget, or create a new instance
  91. * if the widget has been defined. If the widget is undefined an instance of
  92. * the `_default` widget will be returned. An exception will be thrown if
  93. * the `_default` widget is undefined.
  94. *
  95. * @param string $name The widget name to get.
  96. * @return mixed InputInterface widget interface class.
  97. * @throws \RuntimeException when widget is undefined.
  98. */
  99. public function get($name) {
  100. if (!isset($this->_widgets[$name]) && empty($this->_widgets['_default'])) {
  101. throw new \RuntimeException(sprintf('Unknown widget "%s"', $name));
  102. }
  103. if (!isset($this->_widgets[$name])) {
  104. $name = '_default';
  105. }
  106. $this->_widgets[$name] = $this->_resolveWidget($this->_widgets[$name]);
  107. return $this->_widgets[$name];
  108. }
  109. /**
  110. * Clear the registry and reset the widgets.
  111. *
  112. * @return void
  113. */
  114. public function clear() {
  115. $this->_widgets = [];
  116. }
  117. /**
  118. * Resolves a widget spec into an instance.
  119. *
  120. * @param mixed $widget The widget to get
  121. * @return InputInterface
  122. * @throws \RuntimeException when class cannot be loaded or does not
  123. * implement InputInterface.
  124. */
  125. protected function _resolveWidget($widget) {
  126. if (is_object($widget)) {
  127. return $widget;
  128. }
  129. $class = array_shift($widget);
  130. $className = App::classname($class, 'View/Input');
  131. if ($className === false || !class_exists($className)) {
  132. throw new \RuntimeException(sprintf('Unable to locate widget class "%s"', $class));
  133. }
  134. if (count($widget)) {
  135. $reflection = new ReflectionClass($className);
  136. $arguments = [$this->_templates];
  137. foreach ($widget as $requirement) {
  138. $arguments[] = $this->get($requirement);
  139. }
  140. $instance = $reflection->newInstanceArgs($arguments);
  141. } else {
  142. $instance = new $className($this->_templates);
  143. }
  144. if (!($instance instanceof InputInterface)) {
  145. throw new \RuntimeException(sprintf('"%s" does not implement the InputInterface', $className));
  146. }
  147. return $instance;
  148. }
  149. }