Zend_Controller-Exceptions.xml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <sect1 id="zend.controller.exceptions">
  2. <title>Wyjątki MVC</title>
  3. <sect2 id="zend.controller.exceptions.introduction">
  4. <title>Wprowadzenie</title>
  5. <para>
  6. Komponenty MVC w Zend Framework używają kontrolera frontowego, co
  7. oznacza, że wszystkie żądania do danej strony przechodzą przez
  8. pojedynczy punkt. W rezultacie wszystkie wyjątki ostatecznie
  9. zbierane są w kontrolerze frontowym, pozwalając programiście na
  10. obsłużenie ich w jednym miejscu.
  11. </para>
  12. <para>
  13. Jakkolwiek, wiadomości o wyjątkach oraz informacje o backtrace
  14. często zawierają wrażliwe informacje o systemie, jak np. zapytania
  15. SQL, informacje o lokacjach plików i wiele innych. Aby pomóc ci
  16. chronić swój serwis, domyślnie <code>Zend_Controller_Front</code>
  17. łapie wszystkie wyjątki i rejestruje je w obiekcie odpowiedzi; z
  18. kolei, obiekt odpowiedzi domyślnie nie wyświetla wiadomości o
  19. wyjątkach.
  20. </para>
  21. </sect2>
  22. <sect2 id="zend.controller.exceptions.handling">
  23. <title>W jaki sposób możesz obsługiwać wyjątki?</title>
  24. <para>
  25. Obecnie w komponentach MVC wbudowanych jest kilka mechanizmów
  26. pozwalających na obsługę wyjątków
  27. </para>
  28. <itemizedlist>
  29. <listitem>
  30. <para>
  31. Domyślnie rejestrowana i aktywna jest <link
  32. linkend="zend.controller.plugins.standard.errorhandler">wtyczka
  33. obsługi błędów</link>. Ta wtyczka została stworzona aby
  34. obsługiwać:
  35. </para>
  36. <itemizedlist>
  37. <listitem><para>Błędy spowodowane brakującym kontrolerem lub akcją</para></listitem>
  38. <listitem><para>Błędy występujące wewnątrz akcji kontrolerów</para></listitem>
  39. </itemizedlist>
  40. <para>
  41. Wtyczka działa w oparciu o metodę <code>postDispatch()</code>,
  42. i sprawdza czy obiekt uruchamiający, kontroler akcji, lub
  43. inny obiekt wyrzucił wyjątek. Jeśli tak, przekazuje ona
  44. żądanie do kontrolera obsługi błędu.
  45. </para>
  46. <para>
  47. Ta wtyczka obsłuży większość sytuacji, w których został
  48. wyrzucony wyjątek, a także poprawnie obsłuży brakujące
  49. kontrolery oraz akcje.
  50. </para>
  51. </listitem>
  52. <listitem>
  53. <para><code>Zend_Controller_Front::throwExceptions()</code></para>
  54. <para>
  55. Przekazująć logiczną wartość true do tej metody, możesz
  56. nakazać kontrolerowi frontowemu aby zamiast składować
  57. wyjątki w obiekcie odpowiedzi, wyrzucił je, żebyś mógł
  58. obsłużyć je samodzielnie. Na przykład:
  59. </para>
  60. <programlisting role="php"><![CDATA[
  61. $front->throwExceptions(true);
  62. try {
  63. $front->dispatch();
  64. } catch (Exception $e) {
  65. // sam obsłuż wyjątki
  66. }
  67. ]]>
  68. </programlisting>
  69. <para>
  70. Ta metoda jest najprawdopodobniej najłatwiejszym sposobem
  71. dodania własnej obsługi wyjątków do twojej aplikacji
  72. używającej kontrolera frontowego.
  73. </para>
  74. </listitem>
  75. <listitem>
  76. <para><code>Zend_Controller_Response_Abstract::renderExceptions()</code></para>
  77. <para>
  78. Przekazując logiczną wartość true do tej metody, możesz
  79. nakazać obiektowi odpowiedzi aby renderował on wyjątki gdy
  80. sam będzie renderowany. W takim scenariuszu, każdy wyjątek
  81. wyrzucony w twojej aplikacji będzie wyświetlony. To jest
  82. jedynie rekomendowane dla nieprodukcyjnych środowisk.
  83. </para>
  84. </listitem>
  85. <listitem>
  86. <para>
  87. <code>Zend_Controller_Front::returnResponse()</code> oraz
  88. <code>Zend_Controller_Response_Abstract::isException()</code>
  89. </para>
  90. <para>
  91. Przekazanie wartości logicznej true do metody
  92. <code>Zend_Controller_Front::returnResponse()</code>,
  93. spowoduje, że obiekt
  94. <code>Zend_Controller_Front::dispatch()</code> nie będzie
  95. renderował odpowiedzi, a zamiast tego ją zwróci. Gdy już
  96. masz odpowiedź, możesz sprawdzić czy są w niej wyjątki
  97. używając metody <code>isException()</code>, a następnie
  98. odebrać wyjątki używając metody <code>getException()</code>.
  99. Na przykład:
  100. </para>
  101. <programlisting role="php"><![CDATA[
  102. $front->returnResponse(true);
  103. $response = $front->dispatch();
  104. if ($response->isException()) {
  105. $exceptions = $response->getException();
  106. // obsługa wyjątków ...
  107. } else {
  108. $response->sendHeaders();
  109. $response->outputBody();
  110. }
  111. ]]>
  112. </programlisting>
  113. <para>
  114. Główną zaletą, dzięki której ta metoda umożliwia więcej niż
  115. <code>Zend_Controller_Front::throwExceptions()</code>, jest
  116. to, że możesz warunkowo wyświetlać odpowiedź po obsłudze
  117. wyjątków.
  118. </para>
  119. </listitem>
  120. </itemizedlist>
  121. </sect2>
  122. <sect2 id="zend.controller.exceptions.internal">
  123. <title>Wyjątki MVC które możesz napotkać</title>
  124. <para>
  125. Różne komponenty MVC -- obiekt żądania, router, obiekt uruchamiający,
  126. kontrolery akcji, oraz obiekt odpowiedzi -- każdy może z różnych
  127. przyczyn wyrzucać wyjątki. Niektóre wyjątki mogą być warunkowo
  128. nadpisane, a inne są używane aby wskazać programiście potrzebę
  129. poprawienia aplikacji.
  130. </para>
  131. <para>Kilka przykładów:</para>
  132. <itemizedlist>
  133. <listitem>
  134. <para>
  135. <code>Zend_Controller_Dispatcher::dispatch()</code>
  136. domyślnie wyrzuci wyjątek jeśli zażądano nieprawidłowego
  137. kontrolera. Są dwa zalecane sposoby na obsłużenie tego:
  138. </para>
  139. <itemizedlist>
  140. <listitem>
  141. <para>Ustawienie parametru <code>useDefaultControllerAlways</code>.</para>
  142. <para>
  143. W twoim kontrolerze frontowym, lub w obiekcie
  144. uruchamiającym, dodaj poniższą dyrektywę:
  145. </para>
  146. <programlisting role="php"><![CDATA[
  147. $front->setParam('useDefaultControllerAlways', true);
  148. // lub
  149. $dispatcher->setParam('useDefaultControllerAlways', true);
  150. ]]>
  151. </programlisting>
  152. <para>
  153. Gdy ta flaga jest ustawiona, obiekt uruchamiający,
  154. użyje domyślnego kontrolera oraz akcji zamiast
  155. wyrzucania wyjątku. Minusem użycia tej metody jest
  156. to, że jakikolwiek błąd literowy w adresie
  157. dostępowym do twojej strony spowoduje wyświetlenie
  158. strony głównej, co może źle wpłynąć na optymalizację
  159. serwisu dla wyszukiwarek internetowych.
  160. </para>
  161. </listitem>
  162. <listitem>
  163. <para>
  164. Wyjątek wyrzucany przez metodę <code>dispatch()</code>
  165. jest wyjątkiem <code>Zend_Controller_Dispatcher_Exception</code>
  166. zawierającym tekst 'Invalid controller specified'.
  167. Użyj jednej z metod opisanych w <link
  168. linkend="zend.controller.exceptions.handling">poprzedniej
  169. sekcji</link> aby złapać wyjątek, a następnie
  170. przekierować do strony błędu lub do strony głownej.
  171. </para>
  172. </listitem>
  173. </itemizedlist>
  174. </listitem>
  175. <listitem>
  176. <para>
  177. Metoda <code>Zend_Controller_Action::__call()</code> wyrzuci
  178. wyjątek <code>Zend_Controller_Action_Exception</code>
  179. jeśli nie może uruchomić nieistniejącej metody akcji.
  180. Najczęściej będziesz chciał użyć jakiejś domyślnej akcji
  181. w kontrolerze w tego typu sprawach. Przykładowe metody
  182. za pomocą których możesz to osiśgnąć:
  183. </para>
  184. <itemizedlist>
  185. <listitem>
  186. <para>
  187. Rozszerzenie klasy <code>Zend_Controller_Action</code>
  188. i nadpisanie metody <code>__call()</code>. Na przykład:
  189. </para>
  190. <programlisting role="php"><![CDATA[
  191. class My_Controller_Action extends Zend_Controller_Action
  192. {
  193. public function __call($method, $args)
  194. {
  195. if ('Action' == substr($method, -6)) {
  196. $controller = $this->getRequest()->getControllerName();
  197. $url = '/' . $controller . '/index';
  198. return $this->_redirect($url);
  199. }
  200. throw new Exception('Invalid method');
  201. }
  202. }
  203. ]]>
  204. </programlisting>
  205. <para>
  206. Powyższa metoda przechwytuje wszystkie wywołane
  207. niezdefiniowane akcje i przekierowuje żądanie do
  208. domyślnej akcji w kontrolerze.
  209. </para>
  210. </listitem>
  211. <listitem>
  212. <para>
  213. Rozszerzenie klasy <code>Zend_Controller_Dispatcher</code> o
  214. nadpisanie metody <code>getAction()</code>, która
  215. sprawdza czy akcja istnieje. Na przykład:
  216. </para>
  217. <programlisting role="php"><![CDATA[
  218. class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
  219. {
  220. public function getAction($request)
  221. {
  222. $action = $request->getActionName();
  223. if (empty($action)) {
  224. $action = $this->getDefaultAction();
  225. $request->setActionName($action);
  226. $action = $this->formatActionName($action);
  227. } else {
  228. $controller = $this->getController();
  229. $action = $this->formatActionName($action);
  230. if (!method_exists($controller, $action)) {
  231. $action = $this->getDefaultAction();
  232. $request->setActionName($action);
  233. $action = $this->formatActionName($action);
  234. }
  235. }
  236. return $action;
  237. }
  238. }
  239. ]]>
  240. </programlisting>
  241. <para>
  242. Powyższy kod sprawdza czy zażądana akcja istnieje w
  243. klasie kontrolera; jeśli nie, resetuje akcję do
  244. akcji domyślnej.
  245. </para>
  246. <para>
  247. Ta metoda jest wygodna ponieważ możesz w niewidoczny
  248. sposób zmienić akcję przed ostatecznym uruchomieniem.
  249. Jednak to także oznacza, że jakikolwiek błąd literowy
  250. w adresie URL może wciąż uruchomić żądanie poprawnie,
  251. co nie jest zbyt dobre dla optymalizacji dla
  252. wyszukiwarek internetowych.
  253. </para>
  254. </listitem>
  255. <listitem>
  256. <para>
  257. Użycie metody <code>Zend_Controller_Action::preDispatch()</code>
  258. lub <code>Zend_Controller_Plugin_Abstract::preDispatch()</code>
  259. do zidentyfikowania nieprawidłowych akcji.
  260. </para>
  261. <para>
  262. Rozszerzając klasę <code>Zend_Controller_Action</code> i
  263. modyfikując metodę <code>preDispatch()</code>, możesz
  264. zmodyfikować wszystkie twoje kontrolery w taki
  265. sposób, aby przenosiły one żądanie do innej akcji
  266. lub przekierowywały zamiast uruchamiać akcję.
  267. Kod wyglądałby podobnie kod nadpisujący metodę
  268. <code>__call()</code>, który został przedstawiony wyżej.
  269. </para>
  270. <para>
  271. Alternatywnie, możesz sprawdzać te informacje we
  272. wtyczce globalnej. Zaletą tego rozwiązania jest to,
  273. że kontroler akcji staje się niezależny; jeśli twoja
  274. aplikacja składa się z różnorodnych kontrolerów
  275. akcji i nie wszystkie dziedziczą z tej samej klasy,
  276. ta metoda może dodać konsekwencji w obsłudze różnych
  277. klas.
  278. </para>
  279. <para>
  280. Przykład:
  281. </para>
  282. <programlisting role="php"><![CDATA[
  283. class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
  284. {
  285. public function preDispatch(Zend_Controller_Request_Abstract $request)
  286. {
  287. $dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
  288. $controller = $dispatcher->getController($request);
  289. if (!$controller) {
  290. $controller = $dispatcher->getDefaultControllerName($request);
  291. }
  292. $action = $dispatcher->getAction($request);
  293. if (!method_exists($controller, $action)) {
  294. $defaultAction = $dispatcher->getDefaultAction();
  295. $controllerName = $request->getControllerName();
  296. $response = Zend_Controller_Front::getInstance()->getResponse();
  297. $response->setRedirect('/' . $controllerName . '/' . $defaultAction);
  298. $response->sendHeaders();
  299. exit;
  300. }
  301. }
  302. }
  303. ]]>
  304. </programlisting>
  305. <para>
  306. W tym przykładzie sprawdzamy czy zażądana akcja
  307. jest dostępna w kontrolerze. Jeśli nie,
  308. przekierujemy żądanie do domyślnej akcji w
  309. kontrolerze, i kończymy wykonywanie skryptu.
  310. </para>
  311. </listitem>
  312. </itemizedlist>
  313. </listitem>
  314. </itemizedlist>
  315. </sect2>
  316. </sect1>