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