Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • FrontController
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  *
  5:  * The front controller designed to optionally handle all requests
  6:  *
  7:  * @package alpha::controller::front
  8:  * @since 1.0
  9:  * @author John Collins <dev@alphaframework.org>
 10:  * @version $Id: FrontController.inc 1670 2013-09-12 20:41:08Z alphadevx $
 11:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 12:  * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
 13:  * All rights reserved.
 14:  *
 15:  * <pre>
 16:  * Redistribution and use in source and binary forms, with or
 17:  * without modification, are permitted provided that the
 18:  * following conditions are met:
 19:  *
 20:  * * Redistributions of source code must retain the above
 21:  *   copyright notice, this list of conditions and the
 22:  *   following disclaimer.
 23:  * * Redistributions in binary form must reproduce the above
 24:  *   copyright notice, this list of conditions and the
 25:  *   following disclaimer in the documentation and/or other
 26:  *   materials provided with the distribution.
 27:  * * Neither the name of the Alpha Framework nor the names
 28:  *   of its contributors may be used to endorse or promote
 29:  *   products derived from this software without specific
 30:  *   prior written permission.
 31:  *
 32:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 33:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 34:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 35:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 36:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 37:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 38:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 39:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 40:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 41:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 42:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 43:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 44:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 45:  * </pre>
 46:  *
 47:  */
 48: class FrontController {
 49:     /**
 50:      * The GET query string
 51:      *
 52:      * @var string
 53:      * @since 1.0
 54:      */
 55:     private $queryString;
 56: 
 57:     /**
 58:      * The name of the page controller we want to invoke
 59:      *
 60:      * @var string
 61:      * @since 1.0
 62:      */
 63:     private $pageController;
 64: 
 65:     /**
 66:      * Boolean to flag if the GET query string is encrypted or not
 67:      *
 68:      * @var boolean
 69:      * @since 1.0
 70:      */
 71:     private $encryptedQuery = false;
 72: 
 73:     /**
 74:      * An array of controller alias
 75:      *
 76:      * @var array
 77:      * @since 1.0
 78:      */
 79:     private $controllerAlias = array();
 80: 
 81:     /**
 82:      * An array of HTTP filters applied to each request to the front controller.  Each
 83:      * member must implement AlphaFilterInterface!
 84:      *
 85:      * @var array
 86:      * @since 1.0
 87:      */
 88:     private $filters = array();
 89: 
 90:     /**
 91:      * The name of the current alias
 92:      *
 93:      * @var string
 94:      * @since 1.0
 95:      */
 96:     private $currentAlias;
 97: 
 98:     /**
 99:      * Trace logger
100:      *
101:      * @var Logger
102:      * @since 1.0
103:      */
104:     private static $logger = null;
105: 
106:     /**
107:      * The constructor method
108:      *
109:      * @throws ResourceNotFoundException
110:      * @since 1.0
111:      */
112:     public function __construct() {
113:         self::$logger = new Logger('FrontController');
114: 
115:         self::$logger->debug('>>__construct()');
116: 
117:         global $config;
118: 
119:         self::$logger->debug('Requested URL is ['.$_SERVER['REQUEST_URI'].']');
120: 
121:         // direct calls to the front controller
122:         if (isset($_GET['act'])) {
123:             self::$logger->debug('Processing direct request to the front controller');
124:             $this->pageController = $_GET['act'];
125:         // calls to the front controller via mod_rewrite
126:         }elseif($config->get('app.use.mod.rewrite') && !isset($_GET['tk'])) {
127:             self::$logger->debug('Processing a mod_rewrite request');
128:             $this->handleModRewriteRequests();
129:         // direct calls to the front controller with an encrypted query string
130:         }else{
131:             if (!isset($_GET['tk'])) {
132:                 self::$logger->warn('No controller action set for the front controller, URL is ['.$_SERVER['REQUEST_URI'].']');
133:                 throw new ResourceNotFoundException('The file that you have requested cannot be found!');
134:             }else{
135:                 self::$logger->debug('Processing a direct request to the front controller with an encrypted token param');
136:                 $this->setEncrypt(true);
137:                 try {
138:                     $this->decodeQuery();
139:                     $this->populateGetVars();
140:                     if(isset($_GET['act']))
141:                         $this->pageController = $_GET['act'];
142:                     else
143:                         throw new SecurityException('No act param provided in the secure token!');
144:                 }catch (SecurityException $e) {
145:                     self::$logger->error('Error while attempting to decode a secure token in the FrontController: '.$e->getMessage());
146:                     throw new ResourceNotFoundException('The file that you have requested cannot be found!');
147:                 }
148:             }
149:         }
150: 
151:         self::$logger->debug('<<__construct');
152:     }
153: 
154:     /**
155:      * Sets the encryption flag
156:      *
157:      * @param boolean $encryptedQuery
158:      * @since 1.0
159:      */
160:     public function setEncrypt($encryptedQuery) {
161:         $this->encryptedQuery = $encryptedQuery;
162:     }
163: 
164:     /**
165:      * Method to populate the global _GET and _REQUEST arrays with the decoded
166:      * query string
167:      *
168:      * @since 1.0
169:      */
170:     private function populateGetVars() {
171: 
172:         $pairs = explode('&', $this->queryString);
173: 
174:         foreach($pairs as $pair) {
175:             $keyValue = explode('=', $pair);
176:             if(count($keyValue) == 2) {
177:                 $_GET[$keyValue[0]] = $keyValue[1];
178:                 $_REQUEST[$keyValue[0]] = $keyValue[1];
179:             }
180:         }
181:     }
182: 
183:     /**
184:      * Static method for generating an absolute, secure URL for a page controller
185:      *
186:      * @param string $params
187:      * @return string
188:      * @since 1.0
189:      */
190:     public static function generateSecureURL($params) {
191:         global $config;
192: 
193:         if($config->get('app.use.mod.rewrite'))
194:             return $config->get('app.url').'tk/'.FrontController::encodeQuery($params);
195:         else
196:             return $config->get('app.url').'?tk='.FrontController::encodeQuery($params);
197:     }
198: 
199:     /**
200:      * Static method for encoding a query string
201:      *
202:      * @param string $queryString
203:      * @return string
204:      * @since 1.0
205:      */
206:     public static function encodeQuery($queryString) {
207:         global $config;
208: 
209:         $return = base64_encode(AlphaSecurityUtils::encrypt($queryString));
210:         // remove any characters that are likely to cause trouble on a URL
211:         $return = strtr($return, '+/', '-_');
212: 
213:         return $return;
214:     }
215: 
216:     /**
217:      * Method to decode the current query string
218:      *
219:      * @throws SecurityException
220:      * @since 1.0
221:      */
222:     private function decodeQuery() {
223:         global $config;
224: 
225:         if (!isset($_GET['tk'])) {
226:             throw new SecurityException('No token provided for the front controller!');
227:         }else{
228:             // replace any troublesome characters from the URL with the original values
229:             $token = strtr($_GET['tk'], '-_', '+/');
230:             $token = base64_decode($token);
231:             $this->queryString = trim(AlphaSecurityUtils::decrypt($token));
232:         }
233:     }
234: 
235:     /**
236:      * Static method to return the decoded GET paramters from an encrytpted tk value
237:      *
238:      * @return string
239:      * @since 1.0
240:      */
241:     public static function decodeQueryParams($tk) {
242:         global $config;
243: 
244:         // replace any troublesome characters from the URL with the original values
245:         $token = strtr($tk, '-_', '+/');
246:         $token = base64_decode($token);
247:         $params = trim(AlphaSecurityUtils::decrypt($token));
248: 
249:         return $params;
250:     }
251: 
252:     /**
253:      * Static method to return the decoded GET paramters from an encrytpted tk value as an array of key/value pairs.
254:      *
255:      * @return array
256:      * @since 1.0
257:      */
258:     public static function getDecodeQueryParams($tk) {
259:         global $config;
260: 
261:         // replace any troublesome characters from the URL with the original values
262:         $token = strtr($tk, '-_', '+/');
263:         $token = base64_decode($token);
264:         $params = trim(AlphaSecurityUtils::decrypt($token));
265: 
266:         $pairs = explode('&', $params);
267: 
268:         $parameters = array();
269: 
270:         foreach($pairs as $pair) {
271:             $split = explode('=', $pair);
272:             $parameters[$split[0]] = $split[1];
273:         }
274: 
275:         return $parameters;
276:     }
277: 
278:     /**
279:      * Method to load the page controller
280:      *
281:      * @param boolean $allowRedirects Defaults to true, set to false if you want to prevent the front controller from redirecting the request
282:      * @throws ResourceNotFoundException
283:      * @since 1.0
284:      */
285:     public function loadController($allowRedirects = true) {
286:         global $config;
287: 
288:         if($allowRedirects && $config->get('app.check.installed') && $this->pageController != 'Install' && $this->pageController != 'Login') {
289:             if(!AlphaDAO::isInstalled()) {
290:                 self::$logger->info('Invoking the Install controller as the system DB is not installed...');
291:                 $url = FrontController::generateSecureURL('act=Install');
292:                 self::$logger->info('Redirecting to ['.$url.']');
293:                 header('Location: '.$url);
294:                 exit;
295:             }
296:         }
297: 
298:         // first process any attached filters
299:         foreach ($this->filters as $filter)
300:             $filter->process();
301: 
302:         if($allowRedirects) {
303:             // if there is an alias configured for the above page controller, redirect there
304:             if($config->get('app.force.front.controller') && $this->hasAlias($this->pageController)) {
305:                 // make sure that it is not already an alias-based request to prevent re-direct loop
306:                 if(empty($this->currentAlias)) {
307:                     // set the correct HTTP header for the response
308:                     header('HTTP/1.1 301 Moved Permanently');
309: 
310:                     // see if there are any other GET params appart from the controller name
311:                     if (count($_GET) > 1) {
312:                         $keys = array_keys($_GET);
313:                         $param = $_GET[$keys[1]];
314:                         // if its a title then replace spaces with underscores in the URL
315:                         if($keys[1] == 'title')
316:                             $param = str_replace(' ','_',$param);
317: 
318:                         $URL = $config->get('app.url').'/'.$this->getControllerAlias($this->pageController).'/'.
319:                             $this->getControllerParam($this->pageController).$param;
320:                     }else{
321:                         $URL = $config->get('app.url').'/'.$this->getControllerAlias($this->pageController);
322:                     }
323: 
324:                     header('Location: '.$URL);
325:                     exit;
326:                 }
327:             }
328:         }
329: 
330:         try {
331:             AlphaController::loadControllerDef($this->pageController);
332:             $pageController = new $this->pageController();
333: 
334:             if(!empty($_POST)) {
335:                 $pageController->doPOST($_REQUEST);
336:             }else{
337:                 $pageController->doGET($_GET);
338:             }
339:         }catch (LibraryNotInstalledException $e) {
340:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
341:             throw new LibraryNotInstalledException($e->getMessage());
342:         }catch (ResourceNotAllowedException $e) {
343:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
344:             throw new ResourceNotAllowedException($e->getMessage());
345:         }catch (ResourceNotFoundException $e) {
346:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
347:             throw new ResourceNotFoundException($e->getMessage());
348:         }catch (IllegalArguementException $e) {
349:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
350: 
351:             if($config->get('security.client.temp.blacklist.filter.enabled')) {
352:                 if(isset($_SERVER['HTTP_USER_AGENT']) && isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['REQUEST_URI'])) {
353:                     $request = new BadRequestObject();
354:                     $request->set('client', $_SERVER['HTTP_USER_AGENT']);
355:                     $request->set('IP', $_SERVER['REMOTE_ADDR']);
356:                     $request->set('requestedResource', $_SERVER['REQUEST_URI']);
357:                     $request->save();
358:                 }
359:             }
360: 
361:             throw new ResourceNotFoundException('The file that you have requested cannot be found!');
362:         }catch (AlphaException $e) {
363:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
364: 
365:             if($config->get('security.client.temp.blacklist.filter.enabled')) {
366:                 if(isset($_SERVER['HTTP_USER_AGENT']) && isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['REQUEST_URI'])) {
367:                     $request = new BadRequestObject();
368:                     $request->set('client', $_SERVER['HTTP_USER_AGENT']);
369:                     $request->set('IP', $_SERVER['REMOTE_ADDR']);
370:                     $request->set('requestedResource', $_SERVER['REQUEST_URI']);
371:                     $request->save();
372:                 }
373:             }
374: 
375:             throw new ResourceNotFoundException('The file that you have requested cannot be found!');
376:         }
377:     }
378: 
379:     /**
380:      * Used to register a controller alias to enable shorter URLs with mod_rewrite support enabled.  Note that
381:      * only controllers with a single parameter are supported.
382:      *
383:      * @param string $controller The name of the page controller class
384:      * @param string $alias The URL alias for the page controller
385:      * @param string $param The name of the GET parameter on the alias URL request
386:      * @since 1.0
387:      */
388:     public function registerAlias($controller, $alias, $param=null) {
389:         $this->controllerAlias[$alias] = $controller;
390:         if(isset($param))
391:             $this->controllerAlias[$alias.'_param'] = $param;
392: 
393:         // set up the page controller
394:         $this->handleModRewriteRequests();
395:     }
396: 
397:     /**
398:      * Check to see if an alias exists for the given alias name
399:      *
400:      * @param string $alias
401:      * @return boolean
402:      * @since 1.0
403:      */
404:     public function checkAlias($alias) {
405:         if(array_key_exists($alias, $this->controllerAlias))
406:             return true;
407:         else
408:             return false;
409:     }
410: 
411:     /**
412:      * Check to see if an alias exists for the given controller name
413:      *
414:      * @param string $controller
415:      * @return boolean
416:      * @since 1.0
417:      */
418:     public function hasAlias($controller) {
419:         if(in_array($controller, $this->controllerAlias))
420:             return true;
421:         else
422:             return false;
423:     }
424: 
425:     /**
426:      * Gets the full name of the controller for the given alias
427:      *
428:      * @param string $alias
429:      * @return string
430:      * @since 1.0
431:      */
432:     public function getAliasController($alias) {
433:         if(array_key_exists($alias, $this->controllerAlias))
434:             return $this->controllerAlias[$alias];
435:     }
436: 
437:     /**
438:      * Gets the name of the alias for the given controller
439:      *
440:      * @param string $controller
441:      * @return string
442:      * @since 1.0
443:      */
444:     public function getControllerAlias($controller) {
445:         if(in_array($controller, $this->controllerAlias)) {
446:             $keys = array_keys($this->controllerAlias, $controller);
447:             // there should only ever be one key per controller
448:             return $keys[0];
449:         }
450:     }
451: 
452:     /**
453:      * Gets the parameter name expected in requests to the controller with the given alias
454:      *
455:      * @param string $alias
456:      * @return string
457:      * @since 1.0
458:      */
459:     public function getAliasParam($alias) {
460:         if(array_key_exists($alias.'_param', $this->controllerAlias))
461:             return $this->controllerAlias[$alias.'_param'];
462:         else
463:             return '';
464:     }
465: 
466:     /**
467:      * Gets the parameter name expected in requests to the controller with the given controller name
468:      *
469:      * @param string $controller
470:      * @return string
471:      * @since 1.0
472:      */
473:     public function getControllerParam($controller) {
474:         $alias = $this->getControllerAlias($controller);
475:         if(array_key_exists($alias.'_param', $this->controllerAlias))
476:             return $this->controllerAlias[$alias.'_param'];
477:         else
478:             return '';
479:     }
480: 
481:     /**
482:      * Explodes the provided string into an array based on the array of delimiters provided
483:      *
484:      * @param string $string The string to explode.
485:      * @param array $delimiters An array of delimiters.
486:      * @todo move to string utils class
487:      * @return array
488:      * @since 1.2
489:      */
490:     private static function multipleExplode($string, $delimiters = array()){
491: 
492:         $mainDelim=$delimiters[count($delimiters)-1];
493: 
494:         array_pop($delimiters);
495: 
496:         foreach($delimiters as $delimiter) {
497:             $string = str_replace($delimiter, $mainDelim, $string);
498:         }
499: 
500:         $result = explode($mainDelim, $string);
501: 
502:         return $result;
503:     }
504: 
505:     /**
506:      * Handles all of the rules for mod_rewrite style URL parsing
507:      *
508:      * @since 1.0
509:      */
510:     private function handleModRewriteRequests() {
511:         self::$logger->debug('>>handleModRewriteRequests');
512:         global $config;
513: 
514:         // strip off the system URL from the request URL
515:         $request = str_replace($config->get('app.url'), '', 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
516:         self::$logger->debug('$request is ['.$request.']');
517:         $params = self::multipleExplode($request, array('/','?','&'));
518:         self::$logger->debug('$params are ['.var_export($params, true).']');
519: 
520:         try {
521:             // first param will always be the controller alias
522:             if(empty($this->currentAlias) && !empty($params[0]))
523:                 $this->currentAlias = $params[0];
524: 
525:             // check to see if we can load the page controller without an alias
526:             AlphaController::loadControllerDef($params[0]);
527:             self::$logger->debug('Page controller name set on the request URL is ['.$params[0].']');
528:             $this->pageController = $params[0];
529:         }catch (IllegalArguementException $iae) {
530:             // handle request with alias
531:             self::$logger->debug('The supplied controller alias is ['.$this->currentAlias.']');
532: 
533:             // check to see if the controller is an alias for something
534:             if($this->checkAlias($this->currentAlias)) {
535:                 $this->pageController = $this->getAliasController($this->currentAlias);
536:                 self::$logger->debug('Page controller name obtained from the URL alias is ['.$this->pageController.']');
537: 
538:                 if(isset($params[1])) {
539:                     if(!empty($_POST))
540:                         $_REQUEST[$this->getAliasParam($this->currentAlias)] = $params[1];
541:                     else
542:                         $_GET[$this->getAliasParam($this->currentAlias)] = $params[1];
543:                 }
544:             }
545:         }
546: 
547:         self::$logger->debug('$params are ['.var_export($params, true).']');
548:         self::$logger->debug('currentAlias is ['.$this->currentAlias.']');
549: 
550:         // now populate the _GET vars
551:         if($this->currentAlias == 'tk') {
552:             self::$logger->debug('Setting the GET vars for a mod_rewrite request with a tk param');
553:             $this->setEncrypt(true);
554:             $this->queryString = FrontController::decodeQueryParams($params[1]);
555:             $_GET['tk'] = $params[1];
556:             $this->populateGetVars();
557:             $this->pageController = $_GET['act'];
558:         }else{
559:             $count = count($params);
560: 
561:             for($i = 1; $i < $count; $i+=2) {
562:                 if(isset($params[$i+1])) {
563:                     if(!empty($_POST))
564:                         $_REQUEST[$params[$i]] = $params[$i+1];
565:                     else
566:                         $_GET[$params[$i]] = $params[$i+1];
567:                 }
568:             }
569:         }
570: 
571:         self::$logger->debug('$_GET is ['.var_export($_GET, true).']');
572:         self::$logger->debug('<<handleModRewriteRequests');
573:     }
574: 
575:     /**
576:      * Getter for the page controller
577:      *
578:      * @return string
579:      * @since 1.0
580:      */
581:     public function getPageController() {
582:         return $this->pageController;
583:     }
584: 
585:     /**
586:      * Add the supplied filter object to the list of filters ran on each request to the front controller
587:      *
588:      * @param AlphaFilterInterface $filterObject
589:      * @throws IllegalArguementException
590:      * @since 1.0
591:      */
592:     public function registerFilter($filterObject) {
593:         if($filterObject instanceof AlphaFilterInterface)
594:             array_push($this->filters, $filterObject);
595:         else
596:             throw new IllegalArguementException('Supplied filter object is not a valid AlphaFilterInterface instance!');
597:     }
598: 
599:     /**
600:      * Returns the array of filters currently attached to this FrontController
601:      *
602:      * @return array
603:      * @since 1.0
604:      */
605:     public function getFilters() {
606:         return $this->filters;
607:     }
608: }
609: 
610: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0