Here is my halo usage examples and classes, at the end there is a
balance of the use of this classes
<?php
class halo_MultiActionController implements halo_IController {
private $methodNameResolver;
private $delegate;
public function __construct() {
$this->methodNameResolver = new
halo_InternalPathMethodNameResolver();
$this->delegate = $this;
}
public function handleRequest(halo_HttpRequest $request,
halo_HttpResponse $response){
$methodName = $this->methodNameResolver-
>getHandlerMethodName($request);
return $this->invokeNamedMethod($methodName, $request, $response);
}
public function setDelegate($delegate) {
$this->delegate = $delegate;
}
public function setMethodNameResolver(halo_IMethodNameResolver
$methodNameResolver) {
$this->methodNameResolver = $methodNameResolver;
}
public function getMethodNameResolver() {
return $this->methodNameResolver;
}
public function bind(halo_HttpRequest $request, $object){
$reflectionClass = new ReflectionClass(get_class($object));
foreach ($request->getParameterNames() as $param) {
if($reflectionClass->hasProperty($param)){
LoggerReflectionUtils::setter($object, $param, $request-
>getParameter($param));
/*$reflectionProperty = $reflectionClass->getProperty($param);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $request-
>getParameter($param));*/
}
}
}
/*private function isHandlerMethod(Method $method) {
Class returnType = $method.getReturnType();
if (ModelAndView.class.equals(returnType) ||
Map.class.equals(returnType) || String.class.equals(returnType) ||
void.class.equals(returnType)) {
Class[] parameterTypes = method.getParameterTypes();
return (parameterTypes.length >= 2 &&
HttpServletRequest.class.equals(parameterTypes[0]) &&
HttpServletResponse.class.equals(parameterTypes[1]) &&
!("handleRequest".equals(method.getName()) &&
parameterTypes.length == 2));
}
return false;
}*/
protected function invokeNamedMethod($methodName, halo_HttpRequest
$request, halo_HttpResponse $response){
$method = new ReflectionMethod(get_class($this->delegate),
$methodName);
$parameters = $method->getParameters();
$params = Array();
$params[] = $request;
$params[] = $response;
if ($method->getNumberOfParameters() >= 3) {
$commandReflectionClass = new ReflectionClass($parameters[2]-
>getClass());
$class = $parameters[2]->getClass()->getName();
$command = new $class;
//$commandReflectionClass->newInstanceArgs();
$this->bind($request, $command);
$params[] = $command;
}
$returnValue = $method->invokeArgs($this->delegate, $params);
return $returnValue;
}
}
?>
<?php
interface halo_IMethodNameResolver {
public function getHandlerMethodName(halo_HttpRequest $request);
}
?>
<?php
class halo_InternalPathMethodNameResolver implements
halo_IMethodNameResolver {
private $prefix = "";
private $suffix = "";
public function setPrefix($prefix) {
$this->prefix = ($prefix !== null ? $prefix : "");
}
protected function getPrefix() {
return $this->prefix;
}
public function setSuffix($suffix) {
$this->suffix = ($suffix !== null ? $suffix : "");
}
protected function getSuffix() {
return $this->suffix;
}
public function getHandlerMethodName(halo_HttpRequest $request){
$urlPath = $request->getRequestedUrl();
$methodName = basename($urlPath, ".php");
$methodName = $this->prefix. $methodName. $this->suffix;
return $methodName;
}
}
?>
<?php
class halo_ParameterMethodNameResolver implements
halo_IMethodNameResolver {
private $paramName = 'action';
private $methodParamNames;
public function setParamName($paramName) {
$this->paramName = $paramName;
}
public function setMethodParamNames(Array $methodParamNames) {
$this->methodParamNames = $methodParamNames;
}
public function getHandlerMethodName(halo_HttpRequest $request){
$methodName = '';
// Check parameter names where the very existence of each parameter
// means that a method of the same name should be invoked, if any.
if ($this->methodParamNames !== null) {
foreach ($this->methodParamNames as $variable) {
if($request->getParameter($variable) !== null){
$methodName = $request->getParameter($variable);
break;
}
}
}
// Check parameter whose value identifies the method to invoke, if
any.
if ($methodName === null && $this->paramName !== null) {
$methodName = $request->getParameter($this->paramName);
}
if ($methodName !== null && $methodName === "") {
$methodName = null;
}
if ($methodName === null) {
if ($this->defaultMethodName !== null) {
// No specific method resolved: use default method.
$methodName = $this->defaultMethodName;
}
else {
//throw new NoSuchRequestHandlingMethodException(request);
}
}
return $methodName;
}
}
?>
<?php
interface halo_IPathMatcher {
public function match($pattern, $path);
}
?>
/** Pur is available in
http://www.php-pop.org as an utility project
library. It's used here for perform an Url match using Ant Style like
in Spring.
<?php
class halo_AntPathMatcher implements halo_IPathMatcher {
public function match($pattern, $path){
return PurSelector::match($pattern, $path, true);
}
}
?>
<?php
class halo_PropertiesMethodNameResolver implements
halo_IMethodNameResolver {
private $mappings;
private $pathMatcher;
public function __construct() {
$this->pathMatcher = new halo_AntPathMatcher();
}
public function setMappings(Array $mappings) {
$this->mappings = $mappings;
}
/**
* Set the PathMatcher implementation to use for matching URL paths
* against registered URL patterns. Default is halo_AntPathMatcher.
* @see halo_AntPathMatcher
*/
public function setPathMatcher(halo_IPathMatcher $pathMatcher) {
$this->pathMatcher = $pathMatcher;
}
public function getHandlerMethodName(halo_HttpRequest $request){
$urlPath = $request->getRequestedUrl();
if(array_key_exists($urlPath, $this->mappings)){
return $this->mappings[$urlPath];
}
foreach ($this->mappings as $key => $value) {
if ($this->pathMatcher->match($key, $urlPath)) {
return $value;
}
}
return null;
}
}
?>
/****
To validate the url patterns in a consistent way, I tweek
halo_HttpRequest for construct a relative url to the script, changing
the way that halo construct the url using __haloUrl parameter. here is
the tweeks
****/
<?php
class halo_HttpRequest {
protected $method;
protected $queryParams;
protected $parameters;
protected $postParams;
protected $postData;
protected $fileData;
protected $env;
protected $attributes = array();
protected $urlParams = array();
protected $scriptPathRoot;
protected $requestedUrl;
public function __construct($method, array $queryParams = null,
array $postParams = null, array $fileData = null, array $env = null) {
if ( isset($queryParams['__haloUrl']) ) {
$this->requestedUrl = $queryParams['__haloUrl'];
unset($queryParams['__haloUrl']);
}
$this->method = $method;
$this->queryParams = $queryParams;
$this->postParams = $postParams;
$this->parameters = array_merge($this->postParams, $this-
>queryParams);
$this->fileData = $fileData;
if ( $env === null ) {
$env = array();
}
if ( isset($env['REQUEST_URI']) ) {
$requestUri = $env['REQUEST_URI'];
if($this->requestedUrl === null){
$expl = explode("/",$requestUri);
$first = array_shift($expl);
array_shift($expl);
array_unshift($expl,$first);
$expl = implode("/", $expl);
$expl = explode("?",$expl);
$expl = array_shift($expl);
$this->requestedUrl = $expl;
}
//$this->scriptPathRoot = $env['SCRIPT_PATH_ROOT'] =
substr($requestUri, 0, strpos($requestUri, $this->requestedUrl));
} else {
$this->scriptPathRoot = $env['SCRIPT_PATH_ROOT'] = null;
}
$this->env = $env;
$_SERVER = $this->env;
}
public function getQueryParams() {
return $this->queryParams;
}
public function getQueryParam($paramName) {
return $this->queryParamExists($paramName) ? $this-
>queryParams[$paramName] : null;
}
public function queryParamExists($paramName) {
return array_key_exists($paramName, $this->queryParams) ?
true : false;
}
public function getPostParams() {
return $this->postParams;
}
public function getPostParam($paramName) {
return $this->postParamExists($paramName) ? $this-
>postParams[$paramName] : null;
}
public function postParamExists($paramName) {
return array_key_exists($paramName, $this->postParams) ?
true : false;
}
public function getAttributeKeys() {
return array_keys($this->attributes);
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
}
public function getAttribute($key) {
return array_key_exists($key, $this->attributes) ? $this-
>attributes[$key] : null;
}
public function unsetAttribute($key) {
unset($this->attributes[$key]);
}
protected function isMethod($methodName) {
return strtoupper($methodName) == strtoupper($this->method);
}
public function isGetMethod() {
return $this->isMethod('GET');
}
public function isPostMethod() {
return $this->isMethod('POST');
}
public function getUrlParams() {
return $this->urlParams;
}
public function getUrlParamNames() {
return array_keys($this->urlParams);
}
public function setUrlParam($paramName, $paramValue = null) {
$this->urlParams[$paramName] = $paramValue;
}
public function getUrlParam($paramName) {
return array_key_exists($paramName, $this->urlParams) ? $this-
>urlParams[$paramName] : null;
}
public function urlParamExists($paramName) {
return array_key_exists($paramName, $this->urlParams);
}
public function getRequestedUrl() {
return $this->requestedUrl;
}
public function getScriptPathRoot() {
return $this->scriptPathRoot;
}
public function getEnv($key) {
return $this->env[$key];
}
public function getParameter($paramName) {
return array_key_exists($paramName, $this->parameters) ? $this-
>parameters[$paramName] : null;
}
public function getParameterNames() {
return array_keys($this->parameters);
}
}
?>
To work with this kind of urls I need that
halo_SimpleUrlHandlerMapping do the same url pattern match that is
perform in halo_MultiActionController so I change it a little:
<?php
hc_core_ClassLoader::load('halo_AbstractUrlHandlerMapping');
class halo_SimpleUrlHandlerMapping extends
halo_AbstractUrlHandlerMapping {
protected $mappings;
protected $useDefault;
private $pathMatcher;
public function __construct(array $mappings, $default = null) {
$this->mappings = $mappings;
$this->default = $default;
if ( $default !== null ) {
$this->useDefault = true;
} else {
$this->useDefault = false;
}
}
protected function getHandlerInternal(halo_HttpRequest
$httpRequest) {
$url = $httpRequest->getRequestedUrl();
if ( $this->useDefault and ( $url === null or $url === '' )
and $this->default !== null ) {
return $this->context->get($this->default);
}
if ( isset($this->registeredHandlers[$url]) ) {
return $this->context->get($this-
>registeredHandlers[$url]);
} else {
foreach ($this->registeredHandlers as $key => $value) {
if ($this->pathMatcher->match($key, $url)) {
return $this->context->get($value);
}
}
}
return null;
}
protected function registerHandlers() {
foreach ( $this->mappings as $url => $objectName ) {
$this->registerHandler($url, $objectName);
}
}
public function setPathMatcher(halo_IPathMatcher $pathMatcher) {
$this->pathMatcher = $pathMatcher;
}
}
?>
All of this is working perfectly and save me a lot of code using the
sustrate parent "bean" (what is the name here) to referr to a
CommonController that is a class that extends from
halo_MultiActionController to bring CRUD and other things
automagically. The regular expressions allow a bunch of urls that
match a pattern to be attended by a single controller
"bean" (seriously sustrate need a name for this).
My architecture know need to resolve just a few problems.
1- View Resolver (the Implementation of my
halo_InternalResourceViewResolver is not tested yet, and have some
bugs due to the lack of time to work on this).
2- Security (I will take a look at
http://www.tomsick.net/projects/spf
because it said that it is like Spring Security formely Acegi, I know
very well boths and I can decide if spf fit my needs)
3- Reports (there are a couple of ideas around here but nothing
robust).
4- In a future Web Service like Apache CXF formely XFire
5- Auditing and Statistics (I need a robust AOP framework that work
with proxies, I will see how repose do the magic)