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