1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Application;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22:
23: class Application extends Nette\Object
24: {
25:
26: public static $maxLoop = 20;
27:
28:
29: public $catchExceptions;
30:
31:
32: public $errorPresenter;
33:
34:
35: public $onStartup;
36:
37:
38: public $onShutdown;
39:
40:
41: public $onRequest;
42:
43:
44: public $onResponse;
45:
46:
47: public $onError;
48:
49:
50: public $allowedMethods = array('GET', 'POST', 'HEAD', 'PUT', 'DELETE');
51:
52:
53: private $requests = array();
54:
55:
56: private $presenter;
57:
58:
59: private $context;
60:
61:
62:
63: 64: 65: 66:
67: public function run()
68: {
69: $httpRequest = $this->getHttpRequest();
70: $httpResponse = $this->getHttpResponse();
71:
72: $httpRequest->setEncoding('UTF-8');
73:
74: 75: $session = $this->getSession();
76: if (!$session->isStarted() && $session->exists()) {
77: $session->start();
78: }
79:
80: 81: if ($this->allowedMethods) {
82: $method = $httpRequest->getMethod();
83: if (!in_array($method, $this->allowedMethods, TRUE)) {
84: $httpResponse->setCode(Nette\Web\IHttpResponse::S501_NOT_IMPLEMENTED);
85: $httpResponse->setHeader('Allow', implode(',', $this->allowedMethods));
86: echo '<h1>Method ' . htmlSpecialChars($method) . ' is not implemented</h1>';
87: return;
88: }
89: }
90:
91: 92: $request = NULL;
93: $repeatedError = FALSE;
94: do {
95: try {
96: if (count($this->requests) > self::$maxLoop) {
97: throw new ApplicationException('Too many loops detected in application life cycle.');
98: }
99:
100: if (!$request) {
101: $this->onStartup($this);
102:
103: 104: $router = $this->getRouter();
105:
106: 107: Nette\Debug::addPanel(new RoutingDebugger($router, $httpRequest));
108:
109: $request = $router->match($httpRequest);
110: if (!($request instanceof PresenterRequest)) {
111: $request = NULL;
112: throw new BadRequestException('No route for HTTP request.');
113: }
114:
115: if (strcasecmp($request->getPresenterName(), $this->errorPresenter) === 0) {
116: throw new BadRequestException('Invalid request. Presenter is not achievable.');
117: }
118: }
119:
120: $this->requests[] = $request;
121: $this->onRequest($this, $request);
122:
123: 124: $presenter = $request->getPresenterName();
125: try {
126: $class = $this->getPresenterLoader()->getPresenterClass($presenter);
127: $request->setPresenterName($presenter);
128: } catch (InvalidPresenterException $e) {
129: throw new BadRequestException($e->getMessage(), 404, $e);
130: }
131: $request->freeze();
132:
133: 134: $this->presenter = new $class;
135: $response = $this->presenter->run($request);
136: $this->onResponse($this, $response);
137:
138: 139: if ($response instanceof ForwardingResponse) {
140: $request = $response->getRequest();
141: continue;
142:
143: } elseif ($response instanceof IPresenterResponse) {
144: $response->send();
145: }
146: break;
147:
148: } catch (\Exception $e) {
149: 150: $this->onError($this, $e);
151:
152: if (!$this->catchExceptions) {
153: $this->onShutdown($this, $e);
154: throw $e;
155: }
156:
157: if ($repeatedError) {
158: $e = new ApplicationException('An error occured while executing error-presenter', 0, $e);
159: }
160:
161: if (!$httpResponse->isSent()) {
162: $httpResponse->setCode($e instanceof BadRequestException ? $e->getCode() : 500);
163: }
164:
165: if (!$repeatedError && $this->errorPresenter) {
166: $repeatedError = TRUE;
167: if ($this->presenter) {
168: try {
169: $this->presenter->forward(":$this->errorPresenter:", array('exception' => $e));
170: } catch (AbortException $foo) {
171: $request = $this->presenter->getLastCreatedRequest();
172: }
173: } else {
174: $request = new PresenterRequest(
175: $this->errorPresenter,
176: PresenterRequest::FORWARD,
177: array('exception' => $e)
178: );
179: }
180: 181:
182: } else { 183: if ($e instanceof BadRequestException) {
184: $code = $e->getCode();
185: } else {
186: $code = 500;
187: Nette\Debug::log($e, Nette\Debug::ERROR);
188: }
189: echo "<!DOCTYPE html><meta name=robots content=noindex><meta name=generator content='Nette Framework'>\n\n";
190: echo "<style>body{color:#333;background:white;width:500px;margin:100px auto}h1{font:bold 47px/1.5 sans-serif;margin:.6em 0}p{font:21px/1.5 Georgia,serif;margin:1.5em 0}small{font-size:70%;color:gray}</style>\n\n";
191: static $messages = array(
192: 0 => array('Oops...', 'Your browser sent a request that this server could not understand or process.'),
193: 403 => array('Access Denied', 'You do not have permission to view this page. Please try contact the web site administrator if you believe you should be able to view this page.'),
194: 404 => array('Page Not Found', 'The page you requested could not be found. It is possible that the address is incorrect, or that the page no longer exists. Please use a search engine to find what you are looking for.'),
195: 405 => array('Method Not Allowed', 'The requested method is not allowed for the URL.'),
196: 410 => array('Page Not Found', 'The page you requested has been taken off the site. We apologize for the inconvenience.'),
197: 500 => array('Server Error', 'We\'re sorry! The server encountered an internal error and was unable to complete your request. Please try again later.'),
198: );
199: $message = isset($messages[$code]) ? $messages[$code] : $messages[0];
200: echo "<title>$message[0]</title>\n\n<h1>$message[0]</h1>\n\n<p>$message[1]</p>\n\n";
201: if ($code) echo "<p><small>error $code</small></p>";
202: break;
203: }
204: }
205: } while (1);
206:
207: $this->onShutdown($this, isset($e) ? $e : NULL);
208: }
209:
210:
211:
212: 213: 214: 215:
216: final public function getRequests()
217: {
218: return $this->requests;
219: }
220:
221:
222:
223: 224: 225: 226:
227: final public function getPresenter()
228: {
229: return $this->presenter;
230: }
231:
232:
233:
234:
235:
236:
237:
238: 239: 240: 241:
242: public function setContext(Nette\IContext $context)
243: {
244: $this->context = $context;
245: return $this;
246: }
247:
248:
249:
250: 251: 252: 253:
254: final public function getContext()
255: {
256: return $this->context;
257: }
258:
259:
260:
261: 262: 263: 264: 265: 266:
267: final public function getService($name, array $options = NULL)
268: {
269: return $this->context->getService($name, $options);
270: }
271:
272:
273:
274: 275: 276: 277:
278: public function getRouter()
279: {
280: return $this->context->getService('Nette\\Application\\IRouter');
281: }
282:
283:
284:
285: 286: 287: 288: 289:
290: public function setRouter(IRouter $router)
291: {
292: $this->context->addService('Nette\\Application\\IRouter', $router);
293: return $this;
294: }
295:
296:
297:
298: 299: 300: 301:
302: public function getPresenterLoader()
303: {
304: return $this->context->getService('Nette\\Application\\IPresenterLoader');
305: }
306:
307:
308:
309: 310: 311:
312: protected function getHttpRequest()
313: {
314: return $this->context->getService('Nette\\Web\\IHttpRequest');
315: }
316:
317:
318:
319: 320: 321:
322: protected function getHttpResponse()
323: {
324: return $this->context->getService('Nette\\Web\\IHttpResponse');
325: }
326:
327:
328:
329: 330: 331:
332: protected function getSession($namespace = NULL)
333: {
334: $handler = $this->context->getService('Nette\\Web\\Session');
335: return $namespace === NULL ? $handler : $handler->getNamespace($namespace);
336: }
337:
338:
339:
340:
341:
342:
343:
344: 345: 346: 347: 348:
349: public function storeRequest($expiration = '+ 10 minutes')
350: {
351: $session = $this->getSession('Nette.Application/requests');
352: do {
353: $key = substr(md5(lcg_value()), 0, 4);
354: } while (isset($session[$key]));
355:
356: $session[$key] = end($this->requests);
357: $session->setExpiration($expiration, $key);
358: return $key;
359: }
360:
361:
362:
363: 364: 365: 366: 367:
368: public function restoreRequest($key)
369: {
370: $session = $this->getSession('Nette.Application/requests');
371: if (isset($session[$key])) {
372: $request = clone $session[$key];
373: unset($session[$key]);
374: $request->setFlag(PresenterRequest::RESTORED, TRUE);
375: $this->presenter->sendResponse(new ForwardingResponse($request));
376: }
377: }
378:
379: }
380: