Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
In the middle of significant refactoring - branching.
- Loading branch information
1 parent
691f68d
commit f7bf1dd
Showing
19 changed files
with
654 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ramework/interfaces/IConvention.class.php → ...ss/framework/interfaces/IPolicy.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
Library::import('recess.framework.policies.default.DefaultPreprocessor'); | ||
Library::import('recess.framework.policies.default.DefaultRequest'); | ||
Library::import('recess.framework.policies.default.DefaultResponse'); | ||
Library::import('recess.framework.policies.default.Controller'); | ||
Library::import('recess.framework.policies.default.View'); | ||
Library::import('recess.framework.interfaces.IPolicy'); | ||
|
||
class DefaultPolicy implements IPolicy { | ||
|
||
public function getPreprocessor() { | ||
return new DefaultPreprocessor(); | ||
} | ||
|
||
public function getControllerFor(Request $request) { | ||
Application::loadController($request->meta->controllerClass); | ||
return new $request->meta->controllerClass; | ||
} | ||
|
||
public function getViewFor(Response $response) { | ||
if(Library::classExists('recess.framework.policies.default.' . $response->meta->viewClass)) { | ||
return new $response->meta->viewClass; | ||
} else { | ||
Application::loadView($response->meta->viewClass); | ||
return new $response->meta->viewClass; | ||
} | ||
} | ||
} | ||
|
||
?> |
152 changes: 152 additions & 0 deletions
152
lib/recess/framework/policies/default/Controller.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
<?php | ||
Library::import('recess.lang.RecessClass'); | ||
Library::import('recess.lang.RecessReflectionClass'); | ||
Library::import('recess.framework.policies.default.annotations.ViewAnnotation', true); | ||
Library::import('recess.framework.policies.default.annotations.RouteAnnotation', true); | ||
class ControllerDescriptor extends RecessClassDescriptor { | ||
public $routes; | ||
public $viewClass = 'NativeView'; | ||
public $viewPrefix = ''; | ||
} | ||
|
||
/** | ||
* The controller is responsible for interpretting a preprocessed Request, | ||
* performing some action in response to the Request (usually CRUDS), and | ||
* returning a Response which contains relevant state for a view to render | ||
* the Response. | ||
* | ||
* @author Kris Jordan | ||
*/ | ||
abstract class Controller extends RecessClass { | ||
protected $request; | ||
protected $data; | ||
|
||
public static function getViewClass($class) { | ||
return self::getClassDescriptor($class)->viewClass; | ||
} | ||
|
||
public static function getViewPrefix($class) { | ||
return self::getClassDescriptor($class)->viewPrefix; | ||
} | ||
|
||
public static function getRoutes($class) { | ||
return self::getClassDescriptor($class)->routes; | ||
} | ||
|
||
protected static function buildClassDescriptor($class) { | ||
$descriptor = new ControllerDescriptor($class); | ||
|
||
try { | ||
$reflection = new RecessReflectionClass($class); | ||
} catch(ReflectionException $e) { | ||
throw new RecessException('Class "' . $class . '" has not been declared.', get_defined_vars()); | ||
} | ||
|
||
$annotations = $reflection->getAnnotations(); | ||
foreach($annotations as $annotation) { | ||
if($annotation instanceof ControllerAnnotation) { | ||
$annotation->massage($class, '', $descriptor); | ||
} | ||
} | ||
|
||
$reflectedMethods = $reflection->getMethods(false); | ||
$methods = array(); | ||
foreach($reflectedMethods as $reflectedMethod) { | ||
$annotations = $reflectedMethod->getAnnotations(); | ||
foreach($annotations as $annotation) { | ||
if($annotation instanceof ControllerAnnotation) { | ||
$annotation->massage($class, $reflectedMethod->name, $descriptor); | ||
} | ||
} | ||
} | ||
|
||
return $descriptor; | ||
} | ||
|
||
/** | ||
* The serve method is where inversion of control occurs which delegates | ||
* control to another method in the controller. | ||
* | ||
* The method name and arguments should have been extracted in the | ||
* preprocessing step. Here we ensure that the method exists and that all | ||
* required parameters are provided as arguments from the request string. | ||
* | ||
* Call the method and return its response. | ||
* | ||
* @param DefaultRequest $request The HTTP request being served. | ||
* @final | ||
*/ | ||
final function serve(Request $request) { | ||
$this->request = $request; | ||
|
||
$methodName = $request->meta->controllerMethod; | ||
$methodArguments = $request->meta->controllerMethodArguments; | ||
$useAssociativeArguments = $request->meta->useAssociativeArguments; | ||
|
||
// Does method exist? Do arguments match? | ||
if (method_exists($this, $methodName)) { | ||
$method = new ReflectionMethod($this, $methodName); | ||
$parameters = $method->getParameters(); | ||
|
||
$callArguments = array(); | ||
try { | ||
if($useAssociativeArguments) { | ||
$callArguments = $this->getCallArgumentsAssociative($parameters, $methodArguments); | ||
} else { | ||
$callArguments = $this->getCallArgumentsSequential($parameters, $methodArguments); | ||
} | ||
} catch(RecessException $e) { | ||
throw new RecessException('Error calling method "' . $methodName . '" in "' . get_class($this) . '". ' . $e->getMessage(), array()); | ||
} | ||
|
||
$response = $method->invokeArgs($this, $callArguments); | ||
} else { | ||
throw new RecessException('Error calling method "' . $methodName . '" in "' . get_class($this) . '". Method does not exist.', array()); | ||
} | ||
|
||
if($response instanceof Response) { | ||
$descriptor = self::getClassDescriptor($this); | ||
$response->meta->viewClass = $descriptor->viewClass; | ||
$response->meta->viewPrefix = $descriptor->viewPrefix; | ||
$response->meta->viewName = $methodName; | ||
return $response; | ||
} else { | ||
return new BadRequestResponse($request); | ||
} | ||
} | ||
|
||
private function getCallArgumentsAssociative($parameters, $arguments) { | ||
$callArgs = array(); | ||
foreach($parameters as $parameter) { | ||
if(!isset($arguments[$parameter->getName()])) { | ||
if(!$parameter->isOptional()) { | ||
throw new RecessException('Expects ' . count($parameters) . ' arguments, given ' . count($arguments) . ' and missing required parameter: "' . $parameter->name . '"', array()); | ||
} | ||
} else { | ||
$callArgs[] = $arguments[$parameter->getName()]; | ||
} | ||
} | ||
return $callArgs; | ||
} | ||
|
||
private function getCallArgumentsSequential($parameters, $arguments) { | ||
$callArgs = array(); | ||
$parameterCount = count($parameters); | ||
for($i = 0; $i < $parameterCount; $i++) { | ||
if(!isset($arguments[$i])) { | ||
if(!$parameters[$i]->isOptional()) { | ||
throw new RecessException('Expects ' . count($parameters) . ' arguments, given ' . count($arguments) . ' and missing required parameter # ' . ($i + 1) . ' named: "' . $parameters[$i]->name . '"', array()); | ||
} | ||
} else { | ||
$callArgs[] = $arguments[$i]; | ||
} | ||
} | ||
return $callArgs; | ||
} | ||
|
||
protected function ok() { | ||
return new OkResponse($this->request, $this->data); | ||
} | ||
|
||
} | ||
?> |
126 changes: 126 additions & 0 deletions
126
lib/recess/framework/policies/default/DefaultPreprocessor.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
<?php | ||
|
||
Library::import('recess.framework.interfaces.IPreprocessor'); | ||
|
||
class DefaultPreprocessor implements IPreprocessor { | ||
|
||
/** | ||
* Used to pre-process a request. | ||
* This may involve extracting information and transforming values. | ||
* For example, Transforming the HTTP method from POST to PUT based on a POSTed field. | ||
* | ||
* @param Request The Request to refine. | ||
* @return Request The refined Request. | ||
*/ | ||
public function process(Request &$request) { | ||
|
||
$this->getHttpMethodFromPost($request); | ||
|
||
$this->getFormatFromResourceString($request); | ||
|
||
if($request->format != Formats::xhtml) { | ||
$this->reparameterizeForFormat($request); | ||
} | ||
|
||
$this->selectControllerAndView($request); | ||
|
||
return $request; | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////// | ||
// Helper Methods | ||
|
||
const HTTP_METHOD_FIELD = '_METHOD'; | ||
|
||
protected function getHttpMethodFromPost(Request &$request) { | ||
if(array_key_exists(self::HTTP_METHOD_FIELD, $request->post)) { | ||
$request->method = $request->post[self::HTTP_METHOD_FIELD]; | ||
unset($request->post[self::HTTP_METHOD_FIELD]); | ||
if($request->method == Methods::PUT) { | ||
$request->put = $request->post; | ||
} | ||
} | ||
return $request; | ||
} | ||
|
||
protected function getFormatFromResourceString(Request &$request) { | ||
$lastPartIndex = count($request->resourceParts) - 1; | ||
if($lastPartIndex < 0) return $request; | ||
|
||
$lastPart = $request->resourceParts[$lastPartIndex]; | ||
|
||
$lastDotPosition = strrpos($lastPart, Library::dotSeparator); | ||
if($lastDotPosition !== false) { | ||
$substring = substr($lastPart, $lastDotPosition + 1); | ||
if(in_array($substring, Formats::$all)) { | ||
$request->format = $substring; | ||
$request->setResource(substr($request->resource, 0, strrpos($request->resource, Library::dotSeparator))); | ||
} else { | ||
$request->format = Formats::xhtml; | ||
} | ||
} | ||
return $request; | ||
} | ||
|
||
protected function reparameterizeForFormat(Request &$request) { | ||
// TODO: Think about how parameter passing via json/xml/post-vars can be streamlined | ||
if($request->format == Formats::json) { | ||
if(array_key_exists('json', $request->post)){ | ||
$request->post = json_decode($request->post['json']); | ||
} | ||
} else if ($request->format == Formats::xml) { | ||
// TODO: XML reparameterization in request transformer | ||
} | ||
return $request; | ||
} | ||
|
||
protected function selectControllerAndView(Request &$request) { | ||
$router = Application::getRouter(); // TODO: change this | ||
|
||
$routerResult = $router->findRouteFor($request); | ||
if($routerResult->routeExists) { | ||
$request = $this->selectControllerAndViewUsingRoutingResult($request, $routerResult); | ||
} else { | ||
$request = $this->selectControllerAndViewUsingDefaults($request); | ||
} | ||
|
||
return $request; | ||
} | ||
|
||
protected function selectControllerAndViewUsingRoutingResult(Request &$request, RoutingResult &$routerResult) { | ||
$request->meta->useAssociativeArguments = true; | ||
if($routerResult->methodIsSupported) { | ||
$request->meta->controllerClass = $routerResult->route->controllerClass; | ||
$request->meta->controllerMethod = $routerResult->route->function; | ||
$request->meta->controllerMethodArguments = $routerResult->arguments; | ||
} else { | ||
// TODO: Exception Case - Need a means of shortcutting a "METHOD NOT SUPPORTED" | ||
throw new RecessException($request->method . ' HTTP method is not supported for url: ' . $request->resource); | ||
} | ||
return $request; | ||
} | ||
|
||
protected function selectControllerAndViewUsingDefaults(Request &$request) { | ||
$request->meta->useAssociativeArguments = false; | ||
if(isset($request->resourceParts[0])) { | ||
$request->meta->controllerClass = $request->resourceParts[0] . 'Controller'; | ||
if(isset($request->resourceParts[1])) { | ||
$request->meta->controllerMethod = $request->resourceParts[1]; | ||
} else { | ||
$request->meta->controllerMethod = 'home'; | ||
} | ||
if(count($request->resourceParts) > 2) { | ||
$request->meta->controllerMethodArguments = array_slice($request->resourceParts, 2); | ||
} else { | ||
$request->meta->controllerMethodArguments = array(); | ||
} | ||
} else { | ||
$request->meta->controllerClass = 'HomeController'; | ||
$request->meta->controllerMethod = 'home'; | ||
$request->meta->controllerMethodArguments = array(); | ||
} | ||
return $request; | ||
} | ||
} | ||
|
||
?> |
11 changes: 11 additions & 0 deletions
11
lib/recess/framework/policies/default/DefaultRequest.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
Library::import('recess.http.Request'); | ||
|
||
class DefaultRequest extends Request { | ||
public $controllerClass; | ||
public $controllerMethod; | ||
public $controllerMethodArguments; | ||
public $viewClass; | ||
public $viewMethod; | ||
} | ||
?> |
20 changes: 20 additions & 0 deletions
20
lib/recess/framework/policies/default/NativeView.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
Library::import('recess.framework.views.AbstractView'); | ||
|
||
class NativeView extends AbstractView { | ||
/** | ||
* Realizes HTTP's body content based on the Response parameter. Responsible | ||
* for returning content in the format desired. The render method likely uses | ||
* inversion of control which delegates to another method within the view to | ||
* realize the Response. | ||
* | ||
* @param Response $response | ||
* @abstract | ||
*/ | ||
protected function render(Response $response) { | ||
$data = $response->data; | ||
// TODO: Set more interesting variables here. | ||
include_once(Application::getSetting('View.dir') . $response->meta->viewPrefix . $response->meta->viewName . '.php'); | ||
} | ||
} | ||
?> |
9 changes: 9 additions & 0 deletions
9
lib/recess/framework/policies/default/annotations/ControllerAnnotation.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
Library::import('recess.lang.Annotation'); | ||
|
||
abstract class ControllerAnnotation extends Annotation { | ||
|
||
abstract function massage($controller, $method, ControllerDescriptor $descriptor); | ||
} | ||
|
||
?> |
Oops, something went wrong.