1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.controller.exceptions">
4 <title>Исключения</title>
6 <sect2 id="zend.controller.exceptions.introduction">
7 <title>Введение</title>
10 Компоненты MVC в Zend Framework используют фронт-контроллер, это
11 означает, что все запросы к сайту проходят через единственную
12 точку входа. Поэтому все исключения, брошенные внутри компонент, в
13 конечном итоге доходят до фронт-контроллера, что позволяет
14 разработчикам обрабатывать их в одном месте.
18 Сообщения исключений и данные обратной трассировки часто содержат
19 важную системную информацию, такую, как текст запросов SQL,
20 местонахождение файлов и т.п. Поэтому в целях защиты вашего сайта
21 <classname>Zend_Controller_Front</classname> по умолчанию отлавливает все
22 исключения и регистрирует их в объектах ответа; в свою очередь,
23 объект ответа по умолчанию не отображает сообщения исключений.
27 <sect2 id="zend.controller.exceptions.handling">
28 <title>Обработка исключений</title>
31 В компоненты MVC уже встроено несколько механизмов, дающих
32 возможность обрабатывать исключения.
39 linkend="zend.controller.plugins.standard.errorhandler">плагин
40 ErrorHandler</link> зарегистрирован и включен. Этот плагин
41 предназначен для обработки:
46 Ошибок, вызванных отсутствием запрошенного контроллера
51 Ошибок, произошедших внутри котроллеров действий
56 Он действует как плагин <code>postDispatch()</code> и
57 производит проверку, есть ли исключение диспетчера,
58 контроллера действий и др. Если обнаружено
59 исключение, то производится переход на контроллер-обработчик
64 Этот обработчик охватывает большинство исключительных
65 ситуаций и позволяет элегантно обработать случаи
66 отсутствующего контроллера или действия.
72 <code>Zend_Controller_Front::throwExceptions()</code>
76 Посредством передачи булевого значения <constant>TRUE</constant>
77 этому методу вы говорите фронт-контроллеру, что будете
78 самостоятельно обрабатывать исключения вместо того, чтобы
79 они собирались объектом ответа или обрабатывались плагином
80 ErrorHandler. Например:
83 <programlisting language="php"><![CDATA[
84 $front->throwExceptions(true);
87 } catch (Exception $e) {
88 // самостоятельная обработка исключения
93 Возможно, этот метод является наиболее легким способом
94 добавления собственной обработки исключений, охватывающей
95 весь диапазон возможных исключений в вашем приложении.
101 <code>Zend_Controller_Response_Abstract::renderExceptions()</code>
105 Путем передачи булевого значения <constant>TRUE</constant>
106 этому методу, вы говорите объекту ответа, что он должен
107 вместе с заголовком и содержимым ответа выводить сообщения
108 исключений и данные обратной трассировки. В этом случае
109 будут отображены любые исключения, сгенерированные вашим
110 приложением. Это рекомендуется только для непроизводственной
117 <code>Zend_Controller_Front::returnResponse()</code> и
118 <code>Zend_Controller_Response_Abstract::isException()</code>.
122 После передачи булевого значения <constant>TRUE</constant> методу
123 <code>Zend_Controller_Front::returnResponse()</code> метод
124 <code>Zend_Controller_Front::dispatch()</code> будет не
125 выводить ответ, а возвращать его. Имея объект ответа, вы
126 можете проверить, были ли отловлены исключения, используя
127 его метод <code>isException()</code>, и извлекая их через
128 метод <code>getException()</code>. Например:
131 <programlisting language="php"><![CDATA[
132 $front->returnResponse(true);
133 $response = $front->dispatch();
134 if ($response->isException()) {
135 $exceptions = $response->getException();
136 // обработка исключений ...
138 $response->sendHeaders();
139 $response->outputBody();
144 Основное преимущество этого метода по сравнению с
145 <code>Zend_Controller_Front::throwExceptions()</code>
146 состоит в том, что он позволяет управлять выводом ответа
147 после обработки исключений. Это позволит отлавливать любые
148 исключения в цепочке контроллеров, в отличие от плагина
155 <sect2 id="zend.controller.exceptions.internal">
156 <title>Исключения в MVC, с которыми вы можете встретиться</title>
159 Различные компоненты MVC - объекты запросов, маршрутизаторов,
160 диспетчеров, контроллеров действий, ответов могут бросать свои
161 исключения. Одни исключения при определенных условиях могут быть
162 замещены, другие используются для указания разработчику, что
163 структура приложения, возможно, нуждается в рассмотрении.
173 <code>Zend_Controller_Dispatcher::dispatch()</code> по
174 умолчанию будет бросать исключение, если запрошен
175 недействительный контроллер. Есть два способа решения этой
183 <code>useDefaultControllerAlways</code>.
187 Добавить в своем фронт-контроллере или диспетчере
191 <programlisting language="php"><![CDATA[
192 $front->setParam('useDefaultControllerAlways', true);
196 $dispatcher->setParam('useDefaultControllerAlways', true);
200 Когда этот флаг установлен, то диспетчер будет
201 использовать контроллер и действие, приятые по
202 умолчанию, вместо генерации исключения. Недостаток
203 этого метода состоит в том, что на любые опечатки
204 посетителя вашего сайта будет выводиться ваша
205 главная страница, что может нанести вред в плане
206 поисковой оптимизации сайта.
212 Исключение, бросаемое методом
213 <code>dispatch()</code>, является экземпляром класса
214 <classname>Zend_Controller_Dispatcher_Exception</classname>,
215 содержащим текст 'Invalid controller specified'
216 (указан недействительный контроллер).
217 Используйте один из методов, описанных в
219 linkend="zend.controller.exceptions.handling">предыдущем разделе</link>,
220 для отлова исключений и перенаправления на общую
221 страницу ошибки или главную страницу.
229 <code>Zend_Controller_Action::__call()</code> будет
230 бросать исключение, если в классе контроллера нет метода,
231 соответствующего запрошенному действию. Весьма вероятно, что
232 вы захотите, чтобы в этом случае в контроллере вызывалось
233 действие по умолчанию. Ниже перечислены некоторые способы
240 Создать подкласс <classname>Zend_Controller_Action</classname>
241 и переопределить в нем метод <code>__call()</code>.
245 <programlisting language="php"><![CDATA[
246 class My_Controller_Action extends Zend_Controller_Action
248 public function __call($method, $args)
250 if ('Action' == substr($method, -6)) {
251 $controller = $this->getRequest()->getControllerName();
252 $url = '/' . $controller . '/index';
253 return $this->_redirect($url);
256 throw new Exception('Invalid method');
261 Пример выше перехватывает любые вызовы
262 несуществующих методов действий и перенаправляет их
263 на действие по умолчанию в контроллере.
270 <classname>Zend_Controller_Dispatcher</classname> и
271 переопределить в нем метод <code>getAction()</code>
272 так, чтобы он проводил проверку того, существует ли
273 запрошенное действие. Например:
276 <programlisting language="php"><![CDATA[
277 class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
279 public function getAction($request)
281 $action = $request->getActionName();
282 if (empty($action)) {
283 $action = $this->getDefaultAction();
284 $request->setActionName($action);
285 $action = $this->formatActionName($action);
287 $controller = $this->getController();
288 $action = $this->formatActionName($action);
289 if (!method_exists($controller, $action)) {
290 $action = $this->getDefaultAction();
291 $request->setActionName($action);
292 $action = $this->formatActionName($action);
302 Код выше проверяет существование запрошенного
303 действия в классе контроллера; если это действие не
304 существует, то оно заменяется на действие по
309 Этот способ хорош тем, что можно прозрачно заменить
310 действие до финальной диспетчеризации. Тем не менее,
311 он также подразумевает, что опечатки в URL могут
312 обрабатываться как корректные, что не хорошо с точки
313 зрения поисковой оптимизации.
320 <code>Zend_Controller_Action::preDispatch()</code>
322 <code>Zend_Controller_Plugin_Abstract::preDispatch()</code>
323 для определения ошибочно запрошенных действий.
327 Создав подкласс <classname>Zend_Controller_Action</classname>
328 и модифицировав метод <code>preDispatch()</code>, вы
330 свои контроллеры так, чтобы они переходили
331 к другому действию или производили
332 HTTP-перенаправление до того, как будет выполнено
333 текущее действие. Код для этого похож на код,
334 приведенный выше, с переопределением
335 <code>__call()</code>.
339 Альтернативно, вы можете производить данную проверку
340 в общем плагине. Преимущество этого подхода состоит
341 в независимости контроллеров действий. Если ваше
342 приложение содержит различные контроллеры действий и
343 не все из них наследуют от одного и того же класса,
344 то этот метод может обеспечить единообразие в
345 обработке различных классов.
352 <programlisting language="php"><![CDATA[
353 class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
355 public function preDispatch(Zend_Controller_Request_Abstract $request)
357 $front = Zend_Controller_Front::getInstance();
358 $dispatcher = $front->getDispatcher();
359 $class = $dispatcher->getControllerClass($request);
361 $class = $dispatcher->getDefaultControllerClass($request);
364 $r = new ReflectionClass($class);
365 $action = $dispatcher->getActionMethod($request);
367 if (!$r->hasMethod($action)) {
368 $defaultAction = $dispatcher->getDefaultAction();
369 $controllerName = $request->getControllerName();
370 $response = $front->getResponse();
371 $response->setRedirect('/' . $controllerName
372 . '/' . $defaultAction);
373 $response->sendHeaders();
381 В этом примере мы проверяем, доступно ли в
382 контроллере запрошенное действие. Если нет, то
383 производится перенаправление на действие по
384 умолчанию в этом контроллере и сразу же завершается