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 1548 2012-07-29 17:07:07Z 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:         $td = mcrypt_module_open ('tripledes', '', 'ecb', ''); 
210:         $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND); 
211:         mcrypt_generic_init ($td, $config->get('security.query.string.key'), $iv); 
212:         $encryptedData = mcrypt_generic ($td, $queryString); 
213:         mcrypt_generic_deinit ($td); 
214:         mcrypt_module_close ($td);
215:        
216:         $return = base64_encode($encryptedData);
217:         // remove any characters that are likely to cause trouble on a URL      
218:         $return = strtr($return, '+/', '-_');
219:         
220:         return $return;
221:     }
222:     
223:     /**
224:      * Method to decode the current query string
225:      * 
226:      * @throws SecurityException
227:      * @since 1.0
228:      */
229:     private function decodeQuery() {
230:         global $config;     
231:         
232:         if (!isset($_GET['tk'])) {
233:             throw new SecurityException('No token provided for the front controller!');
234:         }else{          
235:             $td = mcrypt_module_open('tripledes', '', 'ecb', '');
236:             $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
237:             // replace any troublesome characters from the URL with the original values
238:             $token = strtr($_GET['tk'], '-_', '+/');
239:             $token = base64_decode($token);         
240:             $this->queryString = trim(mcrypt_decrypt('tripledes', $config->get('security.query.string.key'), $token, 'ecb', $iv));
241:         }
242:     }
243:     
244:     /**
245:      * Static method to return the decoded GET paramters from an encrytpted tk value
246:      * 
247:      * @return string
248:      * @since 1.0
249:      */
250:     public static function decodeQueryParams($tk) {
251:         global $config;     
252:         
253:         $td = mcrypt_module_open('tripledes', '', 'ecb', '');
254:         $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
255:         // replace any troublesome characters from the URL with the original values
256:         $token = strtr($tk, '-_', '+/');
257:         $token = base64_decode($token);
258:         $params = trim(mcrypt_decrypt('tripledes', $config->get('security.query.string.key'), $token, 'ecb', $iv));
259:         
260:         return $params;
261:     }
262:     
263:     /**
264:      * Static method to return the decoded GET paramters from an encrytpted tk value as an array of key/value pairs.
265:      * 
266:      * @return array
267:      * @since 1.0
268:      */
269:     public static function getDecodeQueryParams($tk) {
270:         global $config;     
271:         
272:         $td = mcrypt_module_open('tripledes', '', 'ecb', '');
273:         $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
274:         // replace any troublesome characters from the URL with the original values
275:         $token = strtr($tk, '-_', '+/');
276:         $token = base64_decode($token);
277:         $params = trim(mcrypt_decrypt('tripledes', $config->get('security.query.string.key'), $token, 'ecb', $iv));
278:         
279:         $pairs = explode('&', $params);
280:         
281:         $parameters = array();
282:         
283:         foreach($pairs as $pair) {
284:             $split = explode('=', $pair);
285:             $parameters[$split[0]] = $split[1]; 
286:         }
287:         
288:         return $parameters;
289:     }
290:     
291:     /**
292:      * Method to load the page controller
293:      * 
294:      * @param boolean $allowRedirects Defaults to true, set to false if you want to prevent the front controller from redirecting the request
295:      * @throws ResourceNotFoundException
296:      * @since 1.0
297:      */
298:     public function loadController($allowRedirects = true) {
299:         global $config;
300:         
301:         if($allowRedirects && $config->get('app.check.installed') && $this->pageController != 'Install' && $this->pageController != 'Login') {
302:             if(!AlphaDAO::isInstalled()) {
303:                 self::$logger->info('Invoking the Install controller as the system DB is not installed...');
304:                 $url = FrontController::generateSecureURL('act=Install');
305:                 self::$logger->info('Redirecting to ['.$url.']');
306:                 header('Location: '.$url);
307:                 exit;
308:             }
309:         }
310:         
311:         // first process any attached filters
312:         foreach ($this->filters as $filter)
313:             $filter->process();
314:         
315:         if($allowRedirects) {
316:             // if there is an alias configured for the above page controller, redirect there
317:             if($config->get('app.force.front.controller') && $this->hasAlias($this->pageController)) {
318:                 // make sure that it is not already an alias-based request to prevent re-direct loop            
319:                 if(empty($this->currentAlias)) {
320:                     // set the correct HTTP header for the response
321:                     header('HTTP/1.1 301 Moved Permanently');
322:                     
323:                     // see if there are any other GET params appart from the controller name
324:                     if (count($_GET) > 1) {
325:                         $keys = array_keys($_GET);
326:                         $param = $_GET[$keys[1]];
327:                         // if its a title then replace spaces with underscores in the URL
328:                         if($keys[1] == 'title')
329:                             $param = str_replace(' ','_',$param);
330:                         
331:                         $URL = $config->get('app.url').'/'.$this->getControllerAlias($this->pageController).'/'.
332:                             $this->getControllerParam($this->pageController).$param;
333:                     }else{
334:                         $URL = $config->get('app.url').'/'.$this->getControllerAlias($this->pageController);
335:                     }
336:                     
337:                     header('Location: '.$URL);
338:                     exit;
339:                 }
340:             }
341:         }
342:         
343:         try {
344:             AlphaController::loadControllerDef($this->pageController);
345:             $pageController = new $this->pageController();
346:             
347:             if(!empty($_POST)) {            
348:                 $pageController->doPOST($_REQUEST);
349:             }else{              
350:                 $pageController->doGET($_GET);
351:             }
352:         }catch (LibraryNotInstalledException $e) {
353:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
354:             throw new LibraryNotInstalledException($e->getMessage());
355:         }catch (ResourceNotAllowedException $e) {
356:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
357:             throw new ResourceNotAllowedException($e->getMessage());
358:         }catch (ResourceNotFoundException $e) {
359:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
360:             throw new ResourceNotFoundException($e->getMessage());
361:         }catch (IllegalArguementException $e) {
362:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
363:             
364:             if($config->get('security.client.temp.blacklist.filter.enabled')) {
365:                 if(isset($_SERVER['HTTP_USER_AGENT']) && isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['REQUEST_URI'])) {
366:                     $request = new BadRequestObject();
367:                     $request->set('client', $_SERVER['HTTP_USER_AGENT']);
368:                     $request->set('IP', $_SERVER['REMOTE_ADDR']);
369:                     $request->set('requestedResource', $_SERVER['REQUEST_URI']);
370:                     $request->save();
371:                 }
372:             }
373:             
374:             throw new ResourceNotFoundException('The file that you have requested cannot be found!');
375:         }catch (AlphaException $e) {
376:             self::$logger->warn($e->getMessage()."\nStacktrace:\n".$e->getTraceAsString()."\nRequest params:\n".var_export($_REQUEST, true)."\nRequested resource:\n".$_SERVER['REQUEST_URI']);
377:             
378:             if($config->get('security.client.temp.blacklist.filter.enabled')) {
379:                 if(isset($_SERVER['HTTP_USER_AGENT']) && isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['REQUEST_URI'])) {
380:                     $request = new BadRequestObject();
381:                     $request->set('client', $_SERVER['HTTP_USER_AGENT']);
382:                     $request->set('IP', $_SERVER['REMOTE_ADDR']);
383:                     $request->set('requestedResource', $_SERVER['REQUEST_URI']);
384:                     $request->save();
385:                 }
386:             }
387:             
388:             throw new ResourceNotFoundException('The file that you have requested cannot be found!');
389:         }
390:     }
391:     
392:     /**
393:      * Used to register a controller alias to enable shorter URLs with mod_rewrite support enabled.  Note that
394:      * only controllers with a single parameter are supported.
395:      * 
396:      * @param string $controller The name of the page controller class
397:      * @param string $alias The URL alias for the page controller
398:      * @param string $param The name of the GET parameter on the alias URL request
399:      * @since 1.0
400:      */
401:     public function registerAlias($controller, $alias, $param=null) {
402:         $this->controllerAlias[$alias] = $controller;
403:         if(isset($param))
404:             $this->controllerAlias[$alias.'_param'] = $param;
405:         
406:         // set up the page controller 
407:         $this->handleModRewriteRequests();
408:     }
409:     
410:     /**
411:      * Check to see if an alias exists for the given alias name
412:      * 
413:      * @param string $alias
414:      * @return boolean
415:      * @since 1.0
416:      */
417:     public function checkAlias($alias) {        
418:         if(array_key_exists($alias, $this->controllerAlias))
419:             return true;
420:         else
421:             return false;
422:     }
423:     
424:     /**
425:      * Check to see if an alias exists for the given controller name
426:      * 
427:      * @param string $controller
428:      * @return boolean
429:      * @since 1.0
430:      */
431:     public function hasAlias($controller) {
432:         if(in_array($controller, $this->controllerAlias))
433:             return true;
434:         else
435:             return false;
436:     }
437:     
438:     /**
439:      * Gets the full name of the controller for the given alias
440:      * 
441:      * @param string $alias
442:      * @return string
443:      * @since 1.0
444:      */
445:     public function getAliasController($alias) {
446:         if(array_key_exists($alias, $this->controllerAlias))
447:             return $this->controllerAlias[$alias];
448:     }
449:     
450:     /**
451:      * Gets the name of the alias for the given controller
452:      * 
453:      * @param string $controller
454:      * @return string
455:      * @since 1.0
456:      */
457:     public function getControllerAlias($controller) {
458:         if(in_array($controller, $this->controllerAlias)) {
459:             $keys = array_keys($this->controllerAlias, $controller);
460:             // there should only ever be one key per controller
461:             return $keys[0];
462:         }
463:     }
464:     
465:     /**
466:      * Gets the parameter name expected in requests to the controller with the given alias
467:      * 
468:      * @param string $alias
469:      * @return string
470:      * @since 1.0
471:      */
472:     public function getAliasParam($alias) {
473:         if(array_key_exists($alias.'_param', $this->controllerAlias))
474:             return $this->controllerAlias[$alias.'_param'];
475:         else
476:             return '';
477:     }
478:     
479:     /**
480:      * Gets the parameter name expected in requests to the controller with the given controller name
481:      * 
482:      * @param string $controller
483:      * @return string
484:      * @since 1.0
485:      */
486:     public function getControllerParam($controller) {
487:         $alias = $this->getControllerAlias($controller);
488:         if(array_key_exists($alias.'_param', $this->controllerAlias))
489:             return $this->controllerAlias[$alias.'_param'];
490:         else
491:             return '';
492:     }
493:     
494:     /**
495:      * Explodes the provided string into an array based on the array of delimiters provided 
496:      * 
497:      * @param string $string The string to explode.
498:      * @param array $delimiters An array of delimiters.
499:      * @todo move to string utils class
500:      * @return array
501:      * @since 1.2
502:      */
503:     private static function multipleExplode($string, $delimiters = array()){
504: 
505:         $mainDelim=$delimiters[count($delimiters)-1];
506:        
507:         array_pop($delimiters);
508:        
509:         foreach($delimiters as $delimiter) {       
510:             $string = str_replace($delimiter, $mainDelim, $string);    
511:         }
512: 
513:         $result = explode($mainDelim, $string);
514:         
515:         return $result;
516:     }
517:     
518:     /**
519:      * Handles all of the rules for mod_rewrite style URL parsing
520:      * 
521:      * @since 1.0
522:      */
523:     private function handleModRewriteRequests() {
524:         self::$logger->debug('>>handleModRewriteRequests');
525:         global $config;
526:         
527:         // strip off the system URL from the request URL
528:         $request = str_replace($config->get('app.url'), '', 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
529:         self::$logger->debug('$request is ['.$request.']');
530:         $params = self::multipleExplode($request, array('/','?','&'));
531:         self::$logger->debug('$params are ['.var_export($params, true).']');
532:         
533:         try {
534:             // first param will always be the controller alias
535:             if(empty($this->currentAlias) && !empty($params[0]))
536:                 $this->currentAlias = $params[0];
537: 
538:             // check to see if we can load the page controller without an alias
539:             AlphaController::loadControllerDef($params[0]);
540:             self::$logger->debug('Page controller name set on the request URL is ['.$params[0].']');
541:             $this->pageController = $params[0];
542:         }catch (IllegalArguementException $iae) {
543:             // handle request with alias        
544:             self::$logger->debug('The supplied controller alias is ['.$this->currentAlias.']');
545:             
546:             // check to see if the controller is an alias for something
547:             if($this->checkAlias($this->currentAlias)) {
548:                 $this->pageController = $this->getAliasController($this->currentAlias);
549:                 self::$logger->debug('Page controller name obtained from the URL alias is ['.$this->pageController.']');
550:                 
551:                 if(isset($params[1])) {
552:                     if(!empty($_POST))
553:                         $_REQUEST[$this->getAliasParam($this->currentAlias)] = $params[1];
554:                     else
555:                         $_GET[$this->getAliasParam($this->currentAlias)] = $params[1];
556:                 }
557:             }
558:         }
559:         
560:         self::$logger->debug('$params are ['.var_export($params, true).']');
561:         self::$logger->debug('currentAlias is ['.$this->currentAlias.']');
562:         
563:         // now populate the _GET vars
564:         if($this->currentAlias == 'tk') {
565:             self::$logger->debug('Setting the GET vars for a mod_rewrite request with a tk param');
566:             $this->setEncrypt(true);
567:             $this->queryString = FrontController::decodeQueryParams($params[1]);
568:             $_GET['tk'] = $params[1];
569:             $this->populateGetVars();
570:             $this->pageController = $_GET['act'];
571:         }else{
572:             $count = count($params);
573:             
574:             for($i = 1; $i < $count; $i+=2) {
575:                 if(isset($params[$i+1])) {
576:                     if(!empty($_POST))
577:                         $_REQUEST[$params[$i]] = $params[$i+1];
578:                     else            
579:                         $_GET[$params[$i]] = $params[$i+1];
580:                 }
581:             }
582:         }
583:         
584:         self::$logger->debug('$_GET is ['.var_export($_GET, true).']');     
585:         self::$logger->debug('<<handleModRewriteRequests');
586:     }
587:     
588:     /**
589:      * Getter for the page controller
590:      * 
591:      * @return string
592:      * @since 1.0
593:      */
594:     public function getPageController() {
595:         return $this->pageController;
596:     }
597:     
598:     /**
599:      * Add the supplied filter object to the list of filters ran on each request to the front controller
600:      * 
601:      * @param AlphaFilterInterface $filterObject
602:      * @throws IllegalArguementException
603:      * @since 1.0
604:      */
605:     public function registerFilter($filterObject) {
606:         if($filterObject instanceof AlphaFilterInterface)
607:             array_push($this->filters, $filterObject);
608:         else
609:             throw new IllegalArguementException('Supplied filter object is not a valid AlphaFilterInterface instance!');
610:     }
611:     
612:     /**
613:      * Returns the array of filters currently attached to this FrontController
614:      * 
615:      * @return array
616:      * @since 1.0
617:      */
618:     public function getFilters() {
619:         return $this->filters;
620:     }
621: }
622: 
623: ?>
Alpha Framework API Documentation API documentation generated by ApiGen 2.8.0