3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Mvc\View\Http
;
12 use Zend\EventManager\AbstractListenerAggregate
;
13 use Zend\EventManager\EventManagerInterface
as Events
;
14 use Zend\Filter\Word\CamelCaseToDash
as CamelCaseToDashFilter
;
15 use Zend\Mvc\MvcEvent
;
16 use Zend\Mvc\ModuleRouteListener
;
17 use Zend\View\Model\ModelInterface
as ViewModel
;
19 class InjectTemplateListener
extends AbstractListenerAggregate
22 * FilterInterface/inflector used to normalize names for use as template identifiers
29 * Array of controller namespace -> template mappings
33 protected $controllerMap = [];
36 * Flag to force the use of the route match controller param
40 protected $preferRouteMatchController = false;
45 public function attach(Events
$events, $priority = 1)
47 $this->listeners
[] = $events->attach(MvcEvent
::EVENT_DISPATCH
, [$this, 'injectTemplate'], -90);
51 * Inject a template into the view model, if none present
53 * Template is derived from the controller found in the route match, and,
54 * optionally, the action, if present.
59 public function injectTemplate(MvcEvent
$e)
61 $model = $e->getResult();
62 if (!$model instanceof ViewModel
) {
66 $template = $model->getTemplate();
67 if (!empty($template)) {
71 $routeMatch = $e->getRouteMatch();
72 $controller = $e->getTarget();
73 if (is_object($controller)) {
74 $controller = get_class($controller);
77 $routeMatchController = $routeMatch->getParam('controller', '');
78 if (!$controller ||
($this->preferRouteMatchController
&& $routeMatchController)) {
79 $controller = $routeMatchController;
82 $template = $this->mapController($controller);
84 $module = $this->deriveModuleNamespace($controller);
86 if ($namespace = $routeMatch->getParam(ModuleRouteListener
::MODULE_NAMESPACE
)) {
87 $controllerSubNs = $this->deriveControllerSubNamespace($namespace);
88 if (!empty($controllerSubNs)) {
89 if (!empty($module)) {
90 $module .= '/' . $controllerSubNs;
92 $module = $controllerSubNs;
97 $controller = $this->deriveControllerClass($controller);
98 $template = $this->inflectName($module);
100 if (!empty($template)) {
103 $template .= $this->inflectName($controller);
106 $action = $routeMatch->getParam('action');
107 if (null !== $action) {
108 $template .= '/' . $this->inflectName($action);
110 $model->setTemplate($template);
114 * Set map of controller namespace -> template pairs
119 public function setControllerMap(array $map)
122 $this->controllerMap
= $map;
127 * Maps controller to template if controller namespace is whitelisted or mapped
129 * @param string $controller controller FQCN
130 * @return string|false template name or false if controller was not matched
132 public function mapController($controller)
134 if (! is_string($controller)) {
138 foreach ($this->controllerMap
as $namespace => $replacement) {
139 if (// Allow disabling rule by setting value to false since config
140 // merging have no feature to remove entries
141 false == $replacement
142 // Match full class or full namespace
143 ||
!($controller === $namespace ||
strpos($controller, $namespace . '\\') === 0)
149 // Map namespace to $replacement if its value is string
150 if (is_string($replacement)) {
151 $map = rtrim($replacement, '/') . '/';
152 $controller = substr($controller, strlen($namespace) +
1) ?
: '';
155 //strip Controller namespace(s) (but not classname)
156 $parts = explode('\\', $controller);
158 $parts = array_diff($parts, ['Controller']);
159 //strip trailing Controller in class name
160 $parts[] = $this->deriveControllerClass($controller);
161 $controller = implode('/', $parts);
163 $template = trim($map . $controller, '/');
165 //inflect CamelCase to dash
166 return $this->inflectName($template);
172 * Inflect a name to a normalized value
174 * @param string $name
177 protected function inflectName($name)
179 if (!$this->inflector
) {
180 $this->inflector
= new CamelCaseToDashFilter();
182 $name = $this->inflector
->filter($name);
183 return strtolower($name);
187 * Determine the top-level namespace of the controller
189 * @param string $controller
192 protected function deriveModuleNamespace($controller)
194 if (!strstr($controller, '\\')) {
197 $module = substr($controller, 0, strpos($controller, '\\'));
205 protected function deriveControllerSubNamespace($namespace)
207 if (!strstr($namespace, '\\')) {
210 $nsArray = explode('\\', $namespace);
212 // Remove the first two elements representing the module and controller directory.
213 $subNsArray = array_slice($nsArray, 2);
214 if (empty($subNsArray)) {
217 return implode('/', $subNsArray);
221 * Determine the name of the controller
223 * Strip the namespace, and the suffix "Controller" if present.
225 * @param string $controller
228 protected function deriveControllerClass($controller)
230 if (strstr($controller, '\\')) {
231 $controller = substr($controller, strrpos($controller, '\\') +
1);
234 if ((10 < strlen($controller))
235 && ('Controller' == substr($controller, -10))
237 $controller = substr($controller, 0, -10);
244 * Sets the flag to instruct the listener to prefer the route match controller param
245 * over the class name
247 * @param boolean $preferRouteMatchController
249 public function setPreferRouteMatchController($preferRouteMatchController)
251 $this->preferRouteMatchController
= (bool) $preferRouteMatchController;
257 public function isPreferRouteMatchController()
259 return $this->preferRouteMatchController
;