PageRenderTime 44ms CodeModel.GetById 19ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/server/receptors/IdentityDispatcher.class.php

http://floe.googlecode.com/
PHP | 130 lines | 76 code | 10 blank | 44 comment | 25 complexity | 9fafc889ad26d1171ec69a7c8eeb76e1 MD5 | raw file
  1<?php
  2/**
  3 * This file is part of Floe, a graceful PHP framework.
  4 * Copyright (C) 2005-2009 Mark Rickerby <http://maetl.net>
  5 *
  6 * See the LICENSE file distributed with this software for full copyright, disclaimer
  7 * of liability, and the specific limitations that govern the use of this software.
  8 *
  9 * @version $Id: IdentityDispatcher.class.php 396 2010-05-19 13:13:55Z coretxt $
 10 * @package server
 11 * @subpackage receptors
 12 */
 13
 14/**#@+
 15 * Required dependency.
 16 */
 17require_once dirname(__FILE__) .'/../../framework/EventLog.class.php';
 18require_once dirname(__FILE__) .'/../../language/en/Inflect.class.php';
 19require_once dirname(__FILE__) .'/../controllers/IdentityController.class.php';
 20require_once dirname(__FILE__).'/../ResourceNotFound.class.php';
 21/**#@-*/
 22
 23if (defined('DefaultMethodBinding')) {
 24	throw new Exception("Deprecated constant [DefaultMethodBinding]. Please use [IdentityDispatcher_DefaultBinding]");
 25}
 26
 27if (defined('BindMissingDefault')) {
 28	throw new Exception("Deprecated constant [BindMissingDefault]. Please use [IdentityDispatcher_BindMissing]");
 29}
 30
 31/**
 32 * Bind base URL requests to this controller method by default.
 33 */
 34if (!defined('IdentityDispatcher_DefaultBinding')) define('IdentityDispatcher_DefaultBinding', 'index');
 35
 36/**
 37 * Delegates request binding to a controller based on URI identity.
 38 * 
 39 * <p>The identity binding is based on a simple heirachical convention
 40 * for invoking controller methods:</p>
 41 * 
 42 * - /thing/action => maps to the ThingController::action() method
 43 * - /thing/action/id => maps to the ThingController:action() method and passes the ID as a parameter
 44 * - /thing => maps to the ThingController::index() method
 45 * - / => maps to the IndexController::index() method
 46 * 
 47 * <p>The default URL mapping will throw a ResourceNotFound exception if no controller exists by that name.
 48 * To override this behavior, and route all requests to a controller method binding, define a constant
 49 * <code>BindMissingDefault</code> in your app configuration. This will load the default controller on
 50 * all URL requests, and leaves 404 handling up to you.</p>
 51 * 
 52 * @package server
 53 * @subpackage receptors
 54 * @todo document the precedence heirachy and refactor to better communicate what this code does
 55 */
 56class IdentityDispatcher implements Receptor {
 57
 58	/**
 59	 * Look for a controller that maps to the URI 
 60	 * and invoke its identity method.
 61	 */
 62	public function run(Request $request, Response $response) {
 63		$base = (count($request->uri->segments()) == 1) ? $request->uri->identity() : $request->uri->segment(0);
 64		$identity = $request->uri->segment(1);
 65		$params = $request->uri->segmentsFrom(2);
 66		if ($base == '') $base = IdentityDispatcher_DefaultBinding;
 67		if ($identity == '') $identity = $base;
 68		$path = CTR_DIR ."/$base.controller.php";
 69		if (!file_exists($path)) {
 70			$path = CTR_DIR ."/$base/$identity.controller.php";
 71			$base = $identity;
 72			$identity = $request->uri->segment(2);
 73			if ($identity == '') $identity = $base;
 74			$params = $request->uri->segmentsFrom(3);
 75			if (!file_exists($path) && defined('IdentityDispatcher_BindMissing')) {
 76				$base = IdentityDispatcher_DefaultBinding;
 77				$path = CTR_DIR ."/$base.controller.php";
 78				$params = $request->uri->segmentsFrom(0);
 79				if (!$path) {
 80					throw new ResourceNotFound("Controller not found", $path);
 81				}
 82			}
 83		}
 84		if (file_exists($path)) {
 85			include_once $path;
 86		} else {
 87			if (file_exists(TPL_DIR.'/'.$base.'.php')) {
 88				$response->render($base);
 89				return;
 90			} else {
 91				throw new ResourceNotFound("Controller file not found", $path);
 92			}
 93		}
 94		$classname = Inflect::toClassName($base).'Controller';
 95		if (class_exists($classname)) {
 96			$controller = new $classname($request, $response);
 97		} else {
 98			throw new ResourceNotFound("Controller $classname not defined", $path);
 99		}
100		$identity = $this->stripActionIdentifier($identity);
101		if (method_exists($controller, $identity)) {
102			$this->invoke($controller, $identity, $params);
103		} elseif (method_exists($controller, IdentityDispatcher_DefaultBinding)) {
104			$this->invoke($controller, IdentityDispatcher_DefaultBinding, $request->uri->segmentsFrom(1));
105		} else {
106			throw new ResourceNotFound("Method $identity not defined in $classname", $path);
107		}
108	}
109	
110	private function stripActionIdentifier($identity) {
111		// converts base action to a compatible format
112		$identity = strtolower(Inflect::underscore(Inflect::decodeUriPart($identity)));
113		// returns the base action name without a file extension
114		if (strstr($identity, '.')) {
115			$identity = explode('.', $identity);
116			$identity = $identity[0];
117		}
118		return $identity;
119	}
120	
121	private function invoke($controller, $identity, $params) {
122		EventLog::info(sprintf("Invoked [%s->%s(%s)]", get_class($controller), $identity, implode(",", $params)));
123		if (method_exists($controller, 'before')) call_user_func(array($controller, 'before'));
124		call_user_func_array(array($controller, $identity), $params);
125		if (method_exists($controller, 'after')) call_user_func(array($controller, 'after'));			
126	}
127	
128}
129
130?>