[ZF-10089] Zend_Log
[zend.git] / documentation / manual / ru / module_specs / Zend_Controller-Exceptions.xml
blob89651d22d3f3ab1b063a9d978e0af5faec0e7374
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Reviewed: no -->
3 <sect1 id="zend.controller.exceptions">
4     <title>Исключения</title>
6     <sect2 id="zend.controller.exceptions.introduction">
7         <title>Введение</title>
9         <para>
10             Компоненты MVC в Zend Framework используют фронт-контроллер, это
11             означает, что все запросы к сайту проходят через единственную
12             точку входа. Поэтому все исключения, брошенные внутри компонент, в
13             конечном итоге доходят до фронт-контроллера, что позволяет
14             разработчикам обрабатывать их в одном месте.
15         </para>
17         <para>
18             Сообщения исключений и данные обратной трассировки часто содержат
19             важную системную информацию, такую, как текст запросов SQL,
20             местонахождение файлов и т.п. Поэтому в целях защиты вашего сайта
21             <classname>Zend_Controller_Front</classname> по умолчанию отлавливает все
22             исключения и регистрирует их в объектах ответа; в свою очередь,
23             объект ответа по умолчанию не отображает сообщения исключений.
24         </para>
25     </sect2>
27     <sect2 id="zend.controller.exceptions.handling">
28         <title>Обработка исключений</title>
30         <para>
31             В компоненты MVC уже встроено несколько механизмов, дающих
32             возможность обрабатывать исключения.
33         </para>
35         <itemizedlist>
36             <listitem>
37                 <para>
38                     По умолчанию <link
39                     linkend="zend.controller.plugins.standard.errorhandler">плагин
40                     ErrorHandler</link> зарегистрирован и включен. Этот плагин
41                     предназначен для обработки:
42                 </para>
44                 <itemizedlist>
45                     <listitem><para>
46                         Ошибок, вызванных отсутствием запрошенного контроллера
47                         или действия
48                     </para></listitem>
50                     <listitem><para>
51                         Ошибок, произошедших внутри котроллеров действий
52                     </para></listitem>
53                 </itemizedlist>
55                 <para>
56                     Он действует как плагин <code>postDispatch()</code> и
57                     производит проверку, есть ли исключение диспетчера,
58                     контроллера действий и др. Если обнаружено
59                     исключение, то производится переход на контроллер-обработчик
60                     ошибок.
61                 </para>
63                 <para>
64                     Этот обработчик охватывает большинство исключительных
65                     ситуаций и позволяет элегантно обработать случаи
66                     отсутствующего контроллера или действия.
67                 </para>
68             </listitem>
70             <listitem>
71                 <para>
72                     <code>Zend_Controller_Front::throwExceptions()</code>
73                 </para>
75                 <para>
76                     Посредством передачи булевого значения <constant>TRUE</constant>
77                     этому методу вы говорите фронт-контроллеру, что будете
78                     самостоятельно обрабатывать исключения вместо того, чтобы
79                     они собирались объектом ответа или обрабатывались плагином
80                     ErrorHandler. Например:
81                 </para>
83                 <programlisting language="php"><![CDATA[
84 $front->throwExceptions(true);
85 try {
86     $front->dispatch();
87 } catch (Exception $e) {
88     // самостоятельная обработка исключения
90 ]]></programlisting>
92                 <para>
93                     Возможно, этот метод является наиболее легким способом
94                     добавления собственной обработки исключений, охватывающей
95                     весь диапазон возможных исключений в вашем приложении.
96                 </para>
97             </listitem>
99             <listitem>
100                 <para>
101                     <code>Zend_Controller_Response_Abstract::renderExceptions()</code>
102                 </para>
104                 <para>
105                     Путем передачи булевого значения <constant>TRUE</constant>
106                     этому методу, вы говорите объекту ответа, что он должен
107                     вместе с заголовком и содержимым ответа выводить сообщения
108                     исключений и данные обратной трассировки. В этом случае
109                     будут отображены любые исключения, сгенерированные вашим
110                     приложением. Это рекомендуется только для непроизводственной
111                     среды.
112                 </para>
113             </listitem>
115             <listitem>
116                 <para>
117                     <code>Zend_Controller_Front::returnResponse()</code> и
118                     <code>Zend_Controller_Response_Abstract::isException()</code>.
119                 </para>
121                 <para>
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>. Например:
129                 </para>
131                 <programlisting language="php"><![CDATA[
132 $front->returnResponse(true);
133 $response = $front->dispatch();
134 if ($response->isException()) {
135     $exceptions = $response->getException();
136     // обработка исключений ...
137 } else {
138     $response->sendHeaders();
139     $response->outputBody();
141 ]]></programlisting>
143                 <para>
144                     Основное преимущество этого метода по сравнению с
145                     <code>Zend_Controller_Front::throwExceptions()</code>
146                     состоит в том, что он позволяет управлять выводом ответа
147                     после обработки исключений. Это позволит отлавливать любые
148                     исключения в цепочке контроллеров, в отличие от плагина
149                     ErrorHandler.
150                 </para>
151             </listitem>
152         </itemizedlist>
153     </sect2>
155     <sect2 id="zend.controller.exceptions.internal">
156         <title>Исключения в MVC, с которыми вы можете встретиться</title>
158         <para>
159             Различные компоненты MVC - объекты запросов, маршрутизаторов,
160             диспетчеров, контроллеров действий, ответов могут бросать свои
161             исключения. Одни исключения при определенных условиях могут быть
162             замещены, другие используются для указания разработчику, что
163             структура приложения, возможно, нуждается в рассмотрении.
164         </para>
166         <para>
167             Некоторые примеры:
168         </para>
170         <itemizedlist>
171             <listitem>
172                 <para>
173                     <code>Zend_Controller_Dispatcher::dispatch()</code> по
174                     умолчанию будет бросать исключение, если запрошен
175                     недействительный контроллер. Есть два способа решения этой
176                     проблемы.
177                 </para>
179                 <itemizedlist>
180                     <listitem>
181                         <para>
182                             Установить параметр
183                             <code>useDefaultControllerAlways</code>.
184                         </para>
186                         <para>
187                             Добавить в своем фронт-контроллере или диспетчере
188                             следующую директиву:
189                         </para>
191                         <programlisting language="php"><![CDATA[
192 $front->setParam('useDefaultControllerAlways', true);
194 // или
196 $dispatcher->setParam('useDefaultControllerAlways', true);
197 ]]></programlisting>
199                         <para>
200                             Когда этот флаг установлен, то диспетчер будет
201                             использовать контроллер и действие, приятые по
202                             умолчанию, вместо генерации исключения. Недостаток
203                             этого метода состоит в том, что на любые опечатки
204                             посетителя вашего сайта будет выводиться ваша
205                             главная страница, что может нанести вред в плане
206                             поисковой оптимизации сайта.
207                         </para>
208                     </listitem>
210                     <listitem>
211                         <para>
212                             Исключение, бросаемое методом
213                             <code>dispatch()</code>, является экземпляром класса
214                             <classname>Zend_Controller_Dispatcher_Exception</classname>,
215                             содержащим текст 'Invalid controller specified'
216                             (указан недействительный контроллер).
217                             Используйте один из методов, описанных в
218                             <link
219                             linkend="zend.controller.exceptions.handling">предыдущем разделе</link>,
220                             для отлова исключений и перенаправления на общую
221                             страницу ошибки или главную страницу.
222                         </para>
223                     </listitem>
224                 </itemizedlist>
225             </listitem>
227             <listitem>
228                 <para>
229                     <code>Zend_Controller_Action::__call()</code> будет
230                     бросать исключение, если в классе контроллера нет метода,
231                     соответствующего запрошенному действию. Весьма вероятно, что
232                     вы захотите, чтобы в этом случае в контроллере вызывалось
233                     действие по умолчанию. Ниже перечислены некоторые способы
234                     достичь этого:
235                 </para>
237                 <itemizedlist>
238                     <listitem>
239                         <para>
240                             Создать подкласс <classname>Zend_Controller_Action</classname>
241                             и переопределить в нем метод <code>__call()</code>.
242                             Например:
243                         </para>
245                         <programlisting language="php"><![CDATA[
246 class My_Controller_Action extends Zend_Controller_Action
248     public function __call($method, $args)
249     {
250         if ('Action' == substr($method, -6)) {
251             $controller = $this->getRequest()->getControllerName();
252             $url = '/' . $controller . '/index';
253             return $this->_redirect($url);
254         }
256         throw new Exception('Invalid method');
257     }
259 ]]></programlisting>
260                         <para>
261                             Пример выше перехватывает любые вызовы
262                             несуществующих методов действий и перенаправляет их
263                             на действие по умолчанию в контроллере.
264                         </para>
265                     </listitem>
267                     <listitem>
268                         <para>
269                             Создать подкласс
270                             <classname>Zend_Controller_Dispatcher</classname> и
271                             переопределить в нем метод <code>getAction()</code>
272                             так, чтобы он проводил проверку того, существует ли
273                             запрошенное действие. Например:
274                         </para>
276                         <programlisting language="php"><![CDATA[
277 class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
279     public function getAction($request)
280     {
281         $action = $request->getActionName();
282         if (empty($action)) {
283             $action = $this->getDefaultAction();
284             $request->setActionName($action);
285             $action = $this->formatActionName($action);
286         } else {
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);
293             }
294         }
296         return $action;
297     }
299 ]]></programlisting>
301                         <para>
302                             Код выше проверяет существование запрошенного
303                             действия в классе контроллера; если это действие не
304                             существует, то оно заменяется на действие по
305                             умолчанию.
306                         </para>
308                         <para>
309                             Этот способ хорош тем, что можно прозрачно заменить
310                             действие до финальной диспетчеризации. Тем не менее,
311                             он также подразумевает, что опечатки в URL могут
312                             обрабатываться как корректные, что не хорошо с точки
313                             зрения поисковой оптимизации.
314                         </para>
315                     </listitem>
317                     <listitem>
318                         <para>
319                             Использовать
320                             <code>Zend_Controller_Action::preDispatch()</code>
321                             или
322                             <code>Zend_Controller_Plugin_Abstract::preDispatch()</code>
323                             для определения ошибочно запрошенных действий.
324                         </para>
326                         <para>
327                             Создав подкласс <classname>Zend_Controller_Action</classname>
328                             и модифицировав метод <code>preDispatch()</code>, вы
329                             можете изменить все
330                             свои контроллеры так, чтобы они переходили
331                             к другому действию или производили
332                             HTTP-перенаправление до того, как будет выполнено
333                             текущее действие. Код для этого похож на код,
334                             приведенный выше, с переопределением
335                             <code>__call()</code>.
336                         </para>
338                         <para>
339                             Альтернативно, вы можете производить данную проверку
340                             в общем плагине. Преимущество этого подхода состоит
341                             в независимости контроллеров действий. Если ваше
342                             приложение содержит различные контроллеры действий и
343                             не все из них наследуют от одного и того же класса,
344                             то этот метод может обеспечить единообразие в
345                             обработке различных классов.
346                         </para>
348                         <para>
349                             Например:
350                         </para>
352                         <programlisting language="php"><![CDATA[
353 class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
355     public function preDispatch(Zend_Controller_Request_Abstract $request)
356     {
357         $front      = Zend_Controller_Front::getInstance();
358         $dispatcher = $front->getDispatcher();
359         $class      = $dispatcher->getControllerClass($request);
360         if (!$controller) {
361             $class = $dispatcher->getDefaultControllerClass($request);
362         }
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();
374             exit;
375         }
376     }
378 ]]></programlisting>
380                         <para>
381                             В этом примере мы проверяем, доступно ли в
382                             контроллере запрошенное действие. Если нет, то
383                             производится перенаправление на действие по
384                             умолчанию в этом контроллере и сразу же завершается
385                             выполнение скрипта.
386                         </para>
387                     </listitem>
388                 </itemizedlist>
389             </listitem>
390         </itemizedlist>
391     </sect2>
392 </sect1>
393 <!--
394 vim:se ts=4 sw=4 et: