1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
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
8 */
9
10namespace Zend\Mvc\Controller;
11
12use Zend\EventManager\EventInterface as Event;
13use Zend\EventManager\EventManager;
14use Zend\EventManager\EventManagerAwareInterface;
15use Zend\EventManager\EventManagerInterface;
16use Zend\Http\PhpEnvironment\Response as HttpResponse;
17use Zend\Http\Request as HttpRequest;
18use Zend\Mvc\InjectApplicationEventInterface;
19use Zend\Mvc\MvcEvent;
20use Zend\ServiceManager\ServiceLocatorAwareInterface;
21use Zend\ServiceManager\ServiceLocatorInterface;
22use Zend\Stdlib\DispatchableInterface as Dispatchable;
23use Zend\Stdlib\RequestInterface as Request;
24use Zend\Stdlib\ResponseInterface as Response;
25
26/**
27 * Abstract controller
28 *
29 * Convenience methods for pre-built plugins (@see __call):
30 *
31 * @method \Zend\View\Model\ModelInterface acceptableViewModelSelector(array $matchAgainst = null, bool $returnDefault = true, \Zend\Http\Header\Accept\FieldValuePart\AbstractFieldValuePart $resultReference = null)
32 * @method bool|array|\Zend\Http\Response fileprg(\Zend\Form\FormInterface $form, $redirect = null, $redirectToUrl = false)
33 * @method bool|array|\Zend\Http\Response filePostRedirectGet(\Zend\Form\FormInterface $form, $redirect = null, $redirectToUrl = false)
34 * @method \Zend\Mvc\Controller\Plugin\FlashMessenger flashMessenger()
35 * @method \Zend\Mvc\Controller\Plugin\Forward forward()
36 * @method mixed|null identity()
37 * @method \Zend\Mvc\Controller\Plugin\Layout|\Zend\View\Model\ModelInterface layout(string $template = null)
38 * @method \Zend\Mvc\Controller\Plugin\Params|mixed params(string $param = null, mixed $default = null)
39 * @method \Zend\Http\Response|array prg(string $redirect = null, bool $redirectToUrl = false)
40 * @method \Zend\Http\Response|array postRedirectGet(string $redirect = null, bool $redirectToUrl = false)
41 * @method \Zend\Mvc\Controller\Plugin\Redirect redirect()
42 * @method \Zend\Mvc\Controller\Plugin\Url url()
43 * @method \Zend\View\Model\ConsoleModel createConsoleNotFoundModel()
44 * @method \Zend\View\Model\ViewModel createHttpNotFoundModel()
45 */
46abstract class AbstractController implements
47    Dispatchable,
48    EventManagerAwareInterface,
49    InjectApplicationEventInterface,
50    ServiceLocatorAwareInterface
51{
52    /**
53     * @var PluginManager
54     */
55    protected $plugins;
56
57    /**
58     * @var Request
59     */
60    protected $request;
61
62    /**
63     * @var Response
64     */
65    protected $response;
66
67    /**
68     * @var Event
69     */
70    protected $event;
71
72    /**
73     * @var EventManagerInterface
74     */
75    protected $events;
76
77    /**
78     * @var ServiceLocatorInterface
79     */
80    protected $serviceLocator;
81
82    /**
83     * @var null|string|string[]
84     */
85    protected $eventIdentifier;
86
87    /**
88     * Execute the request
89     *
90     * @param  MvcEvent $e
91     * @return mixed
92     */
93    abstract public function onDispatch(MvcEvent $e);
94
95    /**
96     * Dispatch a request
97     *
98     * @events dispatch.pre, dispatch.post
99     * @param  Request $request
100     * @param  null|Response $response
101     * @return Response|mixed
102     */
103    public function dispatch(Request $request, Response $response = null)
104    {
105        $this->request = $request;
106        if (!$response) {
107            $response = new HttpResponse();
108        }
109        $this->response = $response;
110
111        $e = $this->getEvent();
112        $e->setRequest($request)
113          ->setResponse($response)
114          ->setTarget($this);
115
116        $result = $this->getEventManager()->trigger(MvcEvent::EVENT_DISPATCH, $e, function ($test) {
117            return ($test instanceof Response);
118        });
119
120        if ($result->stopped()) {
121            return $result->last();
122        }
123
124        return $e->getResult();
125    }
126
127    /**
128     * Get request object
129     *
130     * @return Request
131     */
132    public function getRequest()
133    {
134        if (!$this->request) {
135            $this->request = new HttpRequest();
136        }
137
138        return $this->request;
139    }
140
141    /**
142     * Get response object
143     *
144     * @return Response
145     */
146    public function getResponse()
147    {
148        if (!$this->response) {
149            $this->response = new HttpResponse();
150        }
151
152        return $this->response;
153    }
154
155    /**
156     * Set the event manager instance used by this context
157     *
158     * @param  EventManagerInterface $events
159     * @return AbstractController
160     */
161    public function setEventManager(EventManagerInterface $events)
162    {
163        $className = get_class($this);
164
165        $nsPos = strpos($className, '\\') ?: 0;
166        $events->setIdentifiers(array_merge(
167            array(
168                __CLASS__,
169                $className,
170                substr($className, 0, $nsPos)
171            ),
172            array_values(class_implements($className)),
173            (array) $this->eventIdentifier
174        ));
175
176        $this->events = $events;
177        $this->attachDefaultListeners();
178
179        return $this;
180    }
181
182    /**
183     * Retrieve the event manager
184     *
185     * Lazy-loads an EventManager instance if none registered.
186     *
187     * @return EventManagerInterface
188     */
189    public function getEventManager()
190    {
191        if (!$this->events) {
192            $this->setEventManager(new EventManager());
193        }
194
195        return $this->events;
196    }
197
198    /**
199     * Set an event to use during dispatch
200     *
201     * By default, will re-cast to MvcEvent if another event type is provided.
202     *
203     * @param  Event $e
204     * @return void
205     */
206    public function setEvent(Event $e)
207    {
208        if (!$e instanceof MvcEvent) {
209            $eventParams = $e->getParams();
210            $e = new MvcEvent();
211            $e->setParams($eventParams);
212            unset($eventParams);
213        }
214        $this->event = $e;
215    }
216
217    /**
218     * Get the attached event
219     *
220     * Will create a new MvcEvent if none provided.
221     *
222     * @return MvcEvent
223     */
224    public function getEvent()
225    {
226        if (!$this->event) {
227            $this->setEvent(new MvcEvent());
228        }
229
230        return $this->event;
231    }
232
233    /**
234     * Set serviceManager instance
235     *
236     * @param  ServiceLocatorInterface $serviceLocator
237     * @return void
238     */
239    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
240    {
241        $this->serviceLocator = $serviceLocator;
242    }
243
244    /**
245     * Retrieve serviceManager instance
246     *
247     * @return ServiceLocatorInterface
248     */
249    public function getServiceLocator()
250    {
251        return $this->serviceLocator;
252    }
253
254    /**
255     * Get plugin manager
256     *
257     * @return PluginManager
258     */
259    public function getPluginManager()
260    {
261        if (!$this->plugins) {
262            $this->setPluginManager(new PluginManager());
263        }
264
265        $this->plugins->setController($this);
266        return $this->plugins;
267    }
268
269    /**
270     * Set plugin manager
271     *
272     * @param  PluginManager $plugins
273     * @return AbstractController
274     */
275    public function setPluginManager(PluginManager $plugins)
276    {
277        $this->plugins = $plugins;
278        $this->plugins->setController($this);
279
280        return $this;
281    }
282
283    /**
284     * Get plugin instance
285     *
286     * @param  string     $name    Name of plugin to return
287     * @param  null|array $options Options to pass to plugin constructor (if not already instantiated)
288     * @return mixed
289     */
290    public function plugin($name, array $options = null)
291    {
292        return $this->getPluginManager()->get($name, $options);
293    }
294
295    /**
296     * Method overloading: return/call plugins
297     *
298     * If the plugin is a functor, call it, passing the parameters provided.
299     * Otherwise, return the plugin instance.
300     *
301     * @param  string $method
302     * @param  array  $params
303     * @return mixed
304     */
305    public function __call($method, $params)
306    {
307        $plugin = $this->plugin($method);
308        if (is_callable($plugin)) {
309            return call_user_func_array($plugin, $params);
310        }
311
312        return $plugin;
313    }
314
315    /**
316     * Register the default events for this controller
317     *
318     * @return void
319     */
320    protected function attachDefaultListeners()
321    {
322        $events = $this->getEventManager();
323        $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
324    }
325
326    /**
327     * Transform an "action" token into a method name
328     *
329     * @param  string $action
330     * @return string
331     */
332    public static function getMethodFromAction($action)
333    {
334        $method  = str_replace(array('.', '-', '_'), ' ', $action);
335        $method  = ucwords($method);
336        $method  = str_replace(' ', '', $method);
337        $method  = lcfirst($method);
338        $method .= 'Action';
339
340        return $method;
341    }
342}
343