diff --git a/src/Pecee/CsrfToken.php b/src/Pecee/CsrfToken.php new file mode 100644 index 0000000..e3cd7a6 --- /dev/null +++ b/src/Pecee/CsrfToken.php @@ -0,0 +1,74 @@ +lastToken = isset($_SESSION[self::CSRF_KEY]) ? $_SESSION[self::CSRF_KEY] : null; + $this->currentToken = $this->generate(); + + // Initialise session, if it hasn't been initialised. + if(!isset($_SESSION)) { + session_start(); + } + + $_SESSION['csrf_token'] = $this->currentToken; + } + + /** + * Generate random identifier for CSRF token + * @return string + */ + public static function generate() { + if (function_exists('mcrypt_create_iv')) { + return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); + } + return bin2hex(openssl_random_pseudo_bytes(32)); + } + + /** + * Validate valid CSRF token + * + * @param string $token + * @return bool + */ + public function validate($token) { + return hash_equals($token, $_SESSION[self::CSRF_KEY]); + } + + /** + * @return string|null + */ + public function getLastToken(){ + return $this->lastToken; + } + + /** + * @param string|null $lastToken + */ + public function setLastToken($lastToken){ + $this->lastToken = $lastToken; + } + + /** + * @return string|null + */ + public function getCurrentToken(){ + return $this->currentToken; + } + +} \ No newline at end of file diff --git a/src/Pecee/Http/Middleware/Middleware.php b/src/Pecee/Http/Middleware/Middleware.php new file mode 100644 index 0000000..3f04715 --- /dev/null +++ b/src/Pecee/Http/Middleware/Middleware.php @@ -0,0 +1,13 @@ +host = $_SERVER['HTTP_HOST']; + $this->uri = rtrim($_SERVER['REQUEST_URI'], '/') . '/'; + $this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']); + } + + /** + * @return string + */ + public function getUri() { + return $this->uri; + } + + /** + * @return string + */ + public function getHost() { + return $this->host; + } + + /** + * @return string + */ + public function getMethod() { + return $this->method; + } + + /** + * Get http basic auth user + * @return string|null + */ + public function getUser() { + $data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST']); + return (isset($data['username'])) ? $data['username'] : null; + } + +} \ No newline at end of file diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php new file mode 100644 index 0000000..566aa5e --- /dev/null +++ b/src/Pecee/Http/Response.php @@ -0,0 +1,27 @@ +routes = array(); $this->backstack = array(); $this->controllerUrlMap = array(); - $this->requestUri = $_SERVER['REQUEST_URI']; - $this->requestMethod = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']); + $this->request = new Request(); } public function addRoute(RouterEntry $route) { @@ -42,9 +41,8 @@ class RouterBase { /* @var $route RouterEntry */ foreach($routes as $route) { - if($this->defaultNamespace) { + if($this->defaultNamespace && !$route->getNamespace()) { $namespace = null; - if ($route->getNamespace()) { $namespace = $this->defaultNamespace . '\\' . $route->getNamespace(); } else { @@ -71,7 +69,7 @@ class RouterBase { $this->currentRoute = $route; if($route instanceof RouterGroup && is_callable($route->getCallback())) { - $route->renderRoute($this->requestMethod); + $route->renderRoute($this->request); } $this->currentRoute = null; @@ -96,17 +94,17 @@ class RouterBase { }); foreach($this->controllerUrlMap as $route) { - $routeMatch = $route->matchRoute($this->requestMethod, rtrim($this->requestUri, '/') . '/'); + $routeMatch = $route->matchRoute($this->request); if($routeMatch && !($routeMatch instanceof RouterGroup)) { $this->loadedRoute = $routeMatch; - $routeMatch->renderRoute($this->requestMethod); + $routeMatch->renderRoute($this->request); break; } } if(!$this->loadedRoute) { - throw new RouterException(sprintf('Route not found: %s', $this->requestUri), 404); + throw new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404); } } @@ -134,20 +132,6 @@ class RouterBase { return null; } - /** - * @return string - */ - public function getRequestMethod() { - return $this->requestMethod; - } - - /** - * @return string - */ - public function getRequestUri() { - return $this->requestUri; - } - /** * @return array */ @@ -169,10 +153,20 @@ class RouterBase { return $this->routes; } + /** + * Get current request + * + * @return Request + */ + public function getRequest() { + return $this->request; + } + protected function processUrl($route, $method = null, $parameters = null, $getParams = null) { + $url = rtrim($route->getUrl(), '/') . '/'; - if($method !== null) { + if(($route instanceof RouterController || $route instanceof RouterRessource) && $method !== null) { $url .= $method . '/'; } @@ -226,24 +220,16 @@ class RouterBase { } if($c === $controller || strpos($c, $controller) === 0) { - if(stripos($c, '@') !== false) { - $tmp = explode('@', $route->getCallback()); - $method = strtolower($tmp[1]); - } - return $this->processUrl($route, $method, $parameters, $getParams); + return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); } } + $c = ''; + // No match has yet been found, let's try to guess what url that should be returned foreach($this->controllerUrlMap as $route) { if($route instanceof RouterRoute && !is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) { - $c = $route->getCallback(); - - if(stripos($controller, '@') !== false) { - $tmp = explode('@', $controller); - $c = $tmp[0]; - } - + $c = $route->getClass(); } else if($route instanceof RouterController || $route instanceof RouterRessource) { $c = $route->getController(); } @@ -254,7 +240,7 @@ class RouterBase { $method = $tmp[1]; } - if($controller == $c) { + if($controller === $c) { return $this->processUrl($route, $method, $parameters, $getParams); } } diff --git a/src/Pecee/SimpleRouter/RouterController.php b/src/Pecee/SimpleRouter/RouterController.php index d97c3bd..dd69364 100644 --- a/src/Pecee/SimpleRouter/RouterController.php +++ b/src/Pecee/SimpleRouter/RouterController.php @@ -1,6 +1,8 @@ controller = $controller; } - public function matchRoute($requestMethod, $url) { - $url = parse_url($url); + public function matchRoute(Request $request) { + $url = parse_url($request->getUri()); $url = rtrim($url['path'], '/') . '/'; if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) { diff --git a/src/Pecee/SimpleRouter/RouterEntry.php b/src/Pecee/SimpleRouter/RouterEntry.php index 3dffcb5..7830011 100644 --- a/src/Pecee/SimpleRouter/RouterEntry.php +++ b/src/Pecee/SimpleRouter/RouterEntry.php @@ -2,6 +2,9 @@ namespace Pecee\SimpleRouter; +use Pecee\Http\Middleware\Middleware; +use Pecee\Http\Request; + abstract class RouterEntry { const REQUEST_TYPE_POST = 'post'; @@ -27,14 +30,6 @@ abstract class RouterEntry { $this->parametersRegex = array(); } - protected function loadClass($name) { - if(!class_exists($name)) { - throw new RouterException(sprintf('Class %s does not exist', $name)); - } - - return new $name(); - } - /** * Returns callback name/identifier for the current route based on the callback. * Useful if you need to get a unique identifier for the loaded route, for instance @@ -65,6 +60,22 @@ abstract class RouterEntry { return $this->callback; } + public function getMethod() { + if(strpos($this->callback, '@') !== false) { + $tmp = explode('@', $this->callback); + return $tmp[1]; + } + return null; + } + + public function getClass() { + if(strpos($this->callback, '@') !== false) { + $tmp = explode('@', $this->callback); + return $tmp[0]; + } + return null; + } + /** * @param string $prefix * @return self @@ -171,7 +182,7 @@ abstract class RouterEntry { * @return self */ public function addSettings(array $settings) { - array_merge($this->settings, $settings); + $this->settings = array_merge($this->settings, $settings); return $this; } @@ -209,12 +220,30 @@ abstract class RouterEntry { $this->settings[$name] = $value; } - public function renderRoute($requestMethod) { - // Load middleware - if($this->getMiddleware()) { - $this->loadClass($this->getMiddleware()); + protected function loadClass($name) { + if(!class_exists($name)) { + throw new RouterException(sprintf('Class %s does not exist', $name)); } + return new $name(); + } + + protected function loadMiddleware(Request $request) { + if($this->getMiddleware()) { + if (!($this->getMiddleware() instanceof Middleware)) { + throw new RouterException($this->getMiddleware() . ' must be instance of Middleware'); + } + + /* @var $class Middleware */ + $class = $this->loadClass($this->getMiddleware()); + $class->handle($request); + } + } + + public function renderRoute(Request $request) { + // Load middleware + $this->loadMiddleware($request); + if(is_object($this->getCallback()) && is_callable($this->getCallback())) { // When the callback is a function @@ -223,8 +252,9 @@ abstract class RouterEntry { // When the callback is a method $controller = explode('@', $this->getCallback()); $className = $this->getNamespace() . '\\' . $controller[0]; + $class = $this->loadClass($className); - $method = $requestMethod . ucfirst($controller[1]); + $method = $request->getMethod() . ucfirst($controller[1]); if (!method_exists($class, $method)) { throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); @@ -238,6 +268,6 @@ abstract class RouterEntry { return null; } - abstract function matchRoute($requestMethod, $url); + abstract function matchRoute(Request $request); } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterGroup.php b/src/Pecee/SimpleRouter/RouterGroup.php index 2624706..49de9b5 100644 --- a/src/Pecee/SimpleRouter/RouterGroup.php +++ b/src/Pecee/SimpleRouter/RouterGroup.php @@ -2,24 +2,26 @@ namespace Pecee\SimpleRouter; +use Pecee\Http\Request; + class RouterGroup extends RouterEntry { public function __construct() { parent::__construct(); } - public function matchRoute($requestMethod, $url) { + public function matchRoute(Request $request) { // Check if request method is allowed - if(strtolower($url) == strtolower($this->prefix) || stripos($url, $this->prefix) === 0) { + if(strtolower($request->getUri()) == strtolower($this->prefix) || stripos($request->getUri(), $this->prefix) === 0) { $hasAccess = (!$this->method); if($this->method) { if(is_array($this->method)) { - $hasAccess = (in_array($requestMethod, $this->method)); + $hasAccess = (in_array($request->getMethod(), $this->method)); } else { - $hasAccess = strtolower($this->method) == strtolower($requestMethod); + $hasAccess = strtolower($this->method) == strtolower($request->getMethod()); } } diff --git a/src/Pecee/SimpleRouter/RouterRessource.php b/src/Pecee/SimpleRouter/RouterRessource.php index 683400c..e3e929e 100644 --- a/src/Pecee/SimpleRouter/RouterRessource.php +++ b/src/Pecee/SimpleRouter/RouterRessource.php @@ -1,6 +1,8 @@ postMethod = strtolower(($_SERVER['REQUEST_METHOD'] != 'GET') ? 'post' : 'get'); } - public function renderRoute($requestMethod) { + public function renderRoute(Request $request) { // Load middleware - if($this->getMiddleware()) { - $this->loadClass($this->getMiddleware()); - } + $this->loadMiddleware($request); if(is_object($this->getCallback()) && is_callable($this->getCallback())) { // When the callback is a function @@ -51,8 +51,8 @@ class RouterRessource extends RouterEntry { return $this; } - public function matchRoute($requestMethod, $url) { - $url = parse_url($url); + public function matchRoute(Request $request) { + $url = parse_url($request->getUri()); $url = rtrim($url['path'], '/') . '/'; if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) { @@ -72,12 +72,12 @@ class RouterRessource extends RouterEntry { if (count($path)) { // Delete - if($requestMethod === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) { + if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) { return $this->call('destroy', $args); } // Update - if(in_array($requestMethod, array('put', 'patch')) && $this->postMethod === self::REQUEST_TYPE_POST) { + if(in_array($request->getMethod(), array('put', 'patch')) && $this->postMethod === self::REQUEST_TYPE_POST) { return $this->call('update', array_merge(array($action), $args)); } @@ -87,7 +87,7 @@ class RouterRessource extends RouterEntry { } // Create - if(strtolower($action) === 'create' && $requestMethod === self::REQUEST_TYPE_GET) { + if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) { return $this->call('create', $args); } diff --git a/src/Pecee/SimpleRouter/RouterRoute.php b/src/Pecee/SimpleRouter/RouterRoute.php index 580a94c..b1db9b6 100644 --- a/src/Pecee/SimpleRouter/RouterRoute.php +++ b/src/Pecee/SimpleRouter/RouterRoute.php @@ -2,8 +2,7 @@ namespace Pecee\SimpleRouter; -use Pecee\Registry; -use Pecee\Router; +use Pecee\Http\Request; class RouterRoute extends RouterEntry { @@ -19,10 +18,14 @@ class RouterRoute extends RouterEntry { $this->requestTypes = array(); } - protected function parseParameters($url) { + protected function parseParameters($url, $multiple = false) { $parameters = array(); - preg_match_all('/{([A-Za-z\-\_]*?)}/is', $url, $parameters); + if($multiple) { + preg_match_all('/{([A-Za-z\-\_]*?)}/is', $url, $parameters); + } else { + preg_match('/{([A-Za-z\-\_]*?)}/is', $url, $parameters); + } if(isset($parameters[1]) && count($parameters[1]) > 0) { return $parameters[1]; @@ -31,24 +34,12 @@ class RouterRoute extends RouterEntry { return null; } - protected function parseParameter($path) { - $parameters = array(); - - preg_match('/{([A-Za-z\-\_]*?)}/is', $path, $parameters); - - if(isset($parameters[1]) && count($parameters[1]) > 0) { - return $parameters[1]; - } - - return null; - } - - public function matchRoute($requestMethod, $url) { + public function matchRoute(Request $request) { // Check if request method is allowed - if(count($this->requestTypes) === 0 || in_array($requestMethod, $this->requestTypes)) { + if(count($this->requestTypes) === 0 || in_array($request->getMethod(), $this->requestTypes)) { - $url = parse_url($url); + $url = parse_url($request->getUri()); $url = $url['path']; $url = explode('/', trim($url, '/')); @@ -63,7 +54,7 @@ class RouterRoute extends RouterEntry { // Check if url matches foreach($route as $i => $path) { - $parameter = $this->parseParameter($path); + $parameter = $this->parseParameters($path); // Check if parameter of path matches, otherwise quit.. if(is_null($parameter) && strtolower($path) != strtolower($url[$i])) { @@ -116,7 +107,7 @@ class RouterRoute extends RouterEntry { */ public function setUrl($url) { - $parameters = $this->parseParameters($url); + $parameters = $this->parseParameters($url, true); if($parameters !== null) { foreach($parameters as $param) { @@ -144,7 +135,9 @@ class RouterRoute extends RouterEntry { * @return self */ public function addAlias($alias) { - $this->aliases[] = $alias; + $arr = $this->aliases; + $arr[] = $alias; + $this->aliases = $arr; return $this; }