Compare commits

...

26 Commits

Author SHA1 Message Date
Simon Sessingø 5d330643e7 Merge pull request #186 from skipperbent/v2-development
Development
2016-11-25 03:28:49 +02:00
Simon Sessingø 2dd2d95af5 Development
- Fixed BootManager not loading.
- Optimised for-loops.
2016-11-25 02:27:46 +01:00
Simon Sessingø 57aa8eac1e Merge pull request #184 from skipperbent/v2-development
Fixed Exception when using Request
2016-11-25 03:05:17 +02:00
Simon Sessingø 7edee8e6d3 Fixed Exception when using Request 2016-11-25 02:04:42 +01:00
Simon Sessingø 4efc72d013 Merge pull request #182 from skipperbent/v2-development
Development
2016-11-25 02:58:43 +02:00
Simon Sessingø e360fb5438 Removed demo-project. 2016-11-25 01:57:39 +01:00
Simon Sessingø c6bce8a420 Development
- Began work on new documentation.
- BaseCsrfVerifier now only matches `POST`, `PUT` and `DELETE`.
- Parameters are now parsed on custom regex-matches.
- Added `$type` option to `get` method in `Input` class.
2016-11-25 01:53:02 +01:00
Simon Sessingø fb6da37963 Simplified parseInput method with new handleGetPost helper method. 2016-11-24 22:58:32 +01:00
Simon Sessingø abe427ff59 Development
- Optimised Input and Input-related features.
- Removed InputCollection class.
- Changed more foreach to for.
- Updated documentation.
2016-11-24 22:44:58 +01:00
Simon Sessingø 49fc991f9a Merge pull request #180 from skipperbent/v2-development
Updated documentation
2016-11-24 14:51:07 +02:00
Simon Sessingø b2f23c6c7d Updated documentation 2016-11-24 13:50:45 +01:00
Simon Sessingø 20353c6e4d Merge pull request #178 from skipperbent/v2-development
Updated documentation to use Demo instead of MyWebsite
2016-11-24 14:44:56 +02:00
Simon Sessingø 53ece9a7fd Updated documentation to use Demo instead of MyWebsite 2016-11-24 13:44:20 +01:00
Simon Sessingø 4c62f86a26 Merge pull request #176 from skipperbent/v2-development
Fixed HttpException not thrown as NotFoundHttpException
2016-11-24 13:18:52 +02:00
Simon Sessingø 132cf1a10d Fixed HttpException not thrown as NotFoundHttpException 2016-11-24 12:17:47 +01:00
Simon Sessingø 6445746324 Merge pull request #174 from skipperbent/v2-development
Optimisations + bugfixes
2016-11-24 11:05:57 +02:00
Simon Sessingø ff1f027bda Make sure parameter-modifier is found before parsing parameters. 2016-11-24 10:04:47 +01:00
Simon Sessingø 9418d54c8e No need to return getValue as it will be returned on render. 2016-11-24 09:56:21 +01:00
Simon Sessingø e4ab14a2cb Ensure that setError is always int. 2016-11-24 09:42:37 +01:00
Simon Sessingø 258e0e0f13 Optimisations + bugfixes 2016-11-24 09:36:10 +01:00
Simon Sessingø 1a2921acb4 Merge pull request #173 from skipperbent/v2-development
Development
2016-11-21 09:30:58 +02:00
Simon Sessingø f1a9a50ee5 Development
- Only add $_GET param is every other parameter is null when calling getUrl.
2016-11-21 08:29:26 +01:00
Simon Sessingø 258d9d05c7 Merge pull request #171 from skipperbent/v2-development
Enhancements
2016-11-21 09:04:22 +02:00
Simon Sessingø 2cc97c120f Enhancements
- Added example on how to get current url in documentation.
- Fixed so urls always ends with /.
2016-11-21 07:56:37 +01:00
Simon Sessingø 7e7319de06 Merge pull request #169 from skipperbent/v2-development
Fixed // on currentRoute urls.
2016-11-21 05:27:53 +02:00
Simon Sessingø 5ad7dcf9fd Fixed // on currentRoute urls. 2016-11-21 04:27:06 +01:00
30 changed files with 1152 additions and 970 deletions
+720 -302
View File
File diff suppressed because it is too large Load Diff
-100
View File
@@ -1,100 +0,0 @@
# Simple PHP router demo project
This project is here to give you a basic understanding of how to setup and using simple-php-router.
Please note that this demo-project only covers how to integrate the `simple-php-router` in a project without a framework. If you are using some sort of PHP framework in your project the implementation might vary.
**What we won't cover:**
- How to setup a solution that fits your need. This is a basic demo to help you get started.
- Understanding of MVC; including Controllers, Middlewares or ExceptionHandlers.
- How to integrate into third party frameworks.
**What we cover:**
- How to get up and running fast - from scratch.
- How to get ExceptionHandlers, Middlewares and Controllers working.
- How to setup your webservers.
## Installation
- Navigate to the `demo-project` folder in terminal and run `composer update` to install the latest version.
- Point your webserver to `demo-project/public`.
### Setting up Nginx
If you are using Nginx please make sure that url-rewriting is enabled.
You can easily enable url-rewriting by adding the following configuration for the Nginx configuration-file for the demo-project.
```
location / {
try_files $uri $uri/ /index.php?$query_string;
}
```
### Setting up Apache
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
## Folder structure
| Folder | Description |
| ------------- |-------------|
| app |Contains projects-specific PHP classes|
| public |Public folder which are accessible through the web.|
## Notes
The demo project has it's own `Router` class implementation which extends the `SimpleRouter` class with further functionality.
This class can be useful adding additional functionality that are required before and after routing occurs or any extra functionality belonging to the router itself.
In this project we also use our custom router-class to autoload the `routes.php` file from our custom location (`app/routes.php`).
Please check the `routes.php` file in `demo-project/app` for all the urls/rules available in the project.
### CSRF-verifier
For the purpose of this demo, we've added a custom CSRF-verifier middleware called `CsrfVerifier` and disabled CSRF checks for all calls to `/api/*`. This will ensure that CSRF form-checks are not applied when calling our demo api url.
### Exception handlers
The included `CustomExceptionHandler` class returns a very basic json response for errors received on calls to `/api/*` or otherwise just a simple formatted error response.
### Middlewares
`ApiVerification` class is added to all calls to `/api/*`. This simple class just adds some data to the `Request` object, which is returned in one of the methods in the `ApiController` class. We've added this class to demonstrate that you can use middlewares to ensure that the user has the correct authentication - before router loads the controller itself.
### Urls
Please see `routes.php` for all routes and rules.
| URL |
| ------------- |
| / |
| /api/demo |
| /companies |
| /companies/[id] |
| /contact |
## The MIT License (MIT)
Copyright (c) 2016 Simon Sessingø / simple-php-router
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,16 +0,0 @@
<?php
namespace Demo\Controllers;
class ApiController
{
public function index()
{
// The variable authenticated is set to true in the ApiVerification middleware class.
header('content-type: application/json');
echo json_encode([
'authenticated' => request()->authenticated
]);
}
}
@@ -1,27 +0,0 @@
<?php
namespace Demo\Controllers;
class DefaultController
{
public function index()
{
// implement
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
public function contact()
{
echo 'DefaultController -> contact';
}
public function companies($id = null)
{
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound()
{
echo 'Page not found';
}
}
@@ -1,44 +0,0 @@
<?php
namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error)
{
/* You can use the exception handler to format errors depending on the request and type. */
if (stripos($request->getUri(), '/api') !== false) {
response()->json([
'error' => $error->getMessage(),
'code' => $error->getCode(),
]);
}
/* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
/*
* Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
$request->setUri(url('page.notfound'));
return $request;
}
throw $error;
}
}
@@ -1,18 +0,0 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class ApiVerification implements IMiddleware
{
public function handle(Request $request, ILoadableRoute &$route)
{
// Do authentication
$request->authenticated = true;
return $request;
}
}
@@ -1,13 +0,0 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier
{
/**
* CSRF validation will be ignored on the following urls.
*/
protected $except = ['/api/*'];
}
-26
View File
@@ -1,26 +0,0 @@
<?php
/**
* Custom router which handles default middlewares, default exceptions and things
* that should be happen before and after the router is initialised.
*/
namespace Demo;
use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter
{
public static function start($defaultNamespace = null)
{
// Load our helpers
require_once 'helpers.php';
// Load our custom routes
require_once 'routes.php';
parent::setDefaultNamespace('\Demo');
// Do initial stuff
parent::start();
}
}
-44
View File
@@ -1,44 +0,0 @@
<?php
use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null)
{
SimpleRouter::getUrl($controller, $parameters, $getParams);
}
/**
* Get current csrf-token
* @return null|string
*/
function csrf_token()
{
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request()
{
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response()
{
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input()
{
return SimpleRouter::request()->getInput();
}
-25
View File
@@ -1,25 +0,0 @@
<?php
/**
* This file contains all the routes for the project
*/
use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function () {
Router::get('/', 'DefaultController@index')->setName('home');
Router::get('/contact', 'DefaultController@contact')->setName('contact');
Router::get('/404', 'DefaultController@notFound')->setName('404');
Router::basic('/companies/{id?}', 'DefaultController@companies')->setName('companies');
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function () {
Router::resource('/demo', 'ApiController');
});
});
-26
View File
@@ -1,26 +0,0 @@
{
"name": "pecee/simple-router-demo",
"description": "Simple router demo project",
"keywords": [
"simple-router",
"php",
"php-simple-router"
],
"license": "MIT",
"type": "project",
"require": {
"php": ">=5.4.0",
"pecee/simple-router": "2.*"
},
"require-dev": {
},
"config": {
"preferred-install": "dist"
},
"autoload": {
"psr-4": {
"Demo\\": "app/"
}
}
}
-5
View File
@@ -1,5 +0,0 @@
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
-7
View File
@@ -1,7 +0,0 @@
<?php
// load composer dependencies
require '../vendor/autoload.php';
// Start the routing
\Demo\Router::start();
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Pecee\Http\Input;
interface IInputItem
{
public function getIndex();
public function getName();
public function getValue();
public function __toString();
}
+189 -149
View File
@@ -6,31 +6,206 @@ use Pecee\Http\Request;
class Input
{
/**
* @var \Pecee\Http\Input\InputCollection
* @var array
*/
public $get;
public $get = [];
/**
* @var \Pecee\Http\Input\InputCollection
* @var array
*/
public $post;
public $post = [];
/**
* @var \Pecee\Http\Input\InputCollection
* @var array
*/
public $file;
public $file = [];
/**
* @var Request
*/
protected $request;
protected $invalidContentType = false;
protected $invalidContentTypes = [
'text/plain',
'application/x-www-form-urlencoded',
];
public function __construct(Request $request)
{
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
if ($request->getMethod() !== 'get') {
$requestContentType = $request->getHeader('http-content-type');
$max = count($this->invalidContentTypes) - 1;
for ($i = $max; $i >= 0; $i--) {
$contentType = $this->invalidContentType[$i];
if (stripos($requestContentType, $contentType) === 0) {
$this->invalidContentType = true;
break;
}
}
}
if ($this->invalidContentType === false) {
$this->parseInputs();
}
}
protected function parseInputs()
{
/* Parse get requests */
if (count($_GET) > 0) {
$this->get = $this->handleGetPost($_GET);
}
/* Parse post requests */
$postVars = $_POST;
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
if (count($postVars) > 0) {
$this->post = $this->handleGetPost($postVars);
}
/* Parse get requests */
if (count($_FILES) > 0) {
$max = count($_FILES) - 1;
$keys = array_keys($_FILES);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $_FILES[$key];
// Handle array input
if (is_array($value['name']) === false) {
$values['index'] = $key;
$this->file[$key] = InputFile::createFromArray(array_merge($value, $values));
continue;
}
$subMax = count($value['name']) - 1;
$keys = array_keys($value['name']);
$output = [];
for ($i = $subMax; $i >= 0; $i--) {
$output[$keys[$i]] = InputFile::createFromArray([
'index' => $key,
'error' => $value['error'][$keys[$i]],
'tmp_name' => $value['tmp_name'][$keys[$i]],
'type' => $value['type'][$keys[$i]],
'size' => $value['size'][$keys[$i]],
'name' => $value['name'][$keys[$i]],
]);
}
$this->file[$key] = $output;
}
}
}
protected function handleGetPost($array)
{
$tmp = [];
$max = count($array) - 1;
$keys = array_keys($array);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $array[$key];
// Handle array input
if (is_array($value) === false) {
$tmp[$key] = new InputItem($key, $value);
continue;
}
$subMax = count($value) - 1;
$keys = array_keys($value);
$output = [];
for ($i = $subMax; $i >= 0; $i--) {
$output[$keys[$i]] = new InputItem($key, $value[$keys[$i]]);
}
$tmp[$key] = $output;
}
return $tmp;
}
public function findPost($index, $default = null)
{
return isset($this->post[$index]) ? $this->post[$index] : $default;
}
public function findFile($index, $default = null)
{
return isset($this->file[$index]) ? $this->file[$index] : $default;
}
public function findGet($index, $default = null)
{
return isset($this->get[$index]) ? $this->get[$index] : $default;
}
public function getObject($index, $default = null, $method = null)
{
$element = null;
if ($method === null || strtolower($method) === 'get') {
$element = $this->findGet($index);
}
if ($element === null && $method === null || strtolower($method) === 'post') {
$element = $this->findPost($index);
}
if ($element === null && $method === null || strtolower($method) === 'file') {
$element = $this->findFile($index);
}
return ($element === null) ? $default : $element;
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $default
* @param string|null $method
* @return InputItem|string
*/
public function get($index, $default = null, $method = null)
{
$input = $this->getObject($index, $default, $method);
if ($input instanceof InputItem) {
return (trim($input->getValue()) === '') ? $default : $input->getValue();
}
return $input;
}
public function exists($index)
{
return ($this->getObject($index) !== null);
}
/**
@@ -40,6 +215,10 @@ class Input
*/
public function all(array $filter = null)
{
if ($this->invalidContentType === true) {
return [];
}
$output = $_POST;
if ($this->request->getMethod() === 'post') {
@@ -49,7 +228,7 @@ class Input
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = array();
$output = [];
}
}
}
@@ -69,143 +248,4 @@ class Input
return $output;
}
public function getObject($index, $default = null)
{
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[') + 1, strpos($index, ']') - strlen($index)) : null;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index;
$element = $this->get->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
if ($this->request->getMethod() !== 'get') {
$element = $this->post->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
$element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
return $default;
}
/**
* Get input element value matching index
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null)
{
$item = $this->getObject($index);
if ($item !== null) {
if ($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
return (trim($item->getValue()) === '') ? $default : $item->getValue();
}
return $default;
}
public function exists($index)
{
return ($this->getObject($index) !== null);
}
public function setGet()
{
$this->get = new InputCollection();
if (count($_GET) > 0) {
foreach ($_GET as $key => $get) {
if (is_array($get) === false) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
$output = new InputCollection();
foreach ($get as $k => $g) {
$output->{$k} = new InputItem($k, $g);
}
$this->get->{$key} = $output;
}
}
}
public function setPost()
{
$this->post = new InputCollection();
$postVars = $_POST;
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
if (count($postVars) > 0) {
foreach ($postVars as $key => $post) {
if (is_array($post) === false) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
$output = new InputCollection();
foreach ($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
$this->post->{strtolower($key)} = $output;
}
}
}
public function setFile()
{
$this->file = new InputCollection();
if (count($_FILES) > 0) {
foreach ($_FILES as $key => $values) {
// Handle array input
if (is_array($values['name']) === false && trim($values['error']) !== '4') {
$values['index'] = $key;
$this->file->{strtolower($key)} = InputFile::createFromArray($values);
continue;
}
$output = new InputCollection();
foreach ($values['name'] as $k => $val) {
if (trim($val['error'][$k]) !== '4') {
$output->{$k} = InputFile::createFromArray([
'index' => $k,
'error' => $val['error'][$k],
'tmp_name' => $val['tmp_name'][$k],
'type' => $val['type'][$k],
'size' => $val['size'][$k],
'name' => $val['name'][$k]
]);
}
}
$this->file->{strtolower($key)} = $output;
}
}
}
}
-94
View File
@@ -1,94 +0,0 @@
<?php
namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate
{
protected $data = array();
/**
* Search for input element matching index.
*
* @param string $index
* @param string|null $default
* @return InputItem|mixed
*/
public function findFirst($index, $default = null)
{
if (count($this->data) > 0) {
if (isset($this->data[$index])) {
return $this->data[$index];
}
foreach ($this->data as $key => $input) {
if (strtolower($index) === strtolower($key)) {
return $input;
}
}
}
return $default;
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null)
{
$input = $this->findFirst($index);
if($input !== null && trim($input->getValue()) !== '') {
return $input->getValue();
}
return $default;
}
public function getValue($index, $default = null) {
}
/**
* @param string $index
* @throws \InvalidArgumentException
* @return InputItem
*/
public function __get($index)
{
$item = $this->findFirst($index);
// Ensure that item are always available
if ($item === null) {
$this->data[$index] = new InputItem($index, null);
return $this->data[$index];
}
return $item;
}
public function __set($index, $value)
{
$this->data[$index] = $value;
}
public function getData()
{
return $this->data;
}
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return \Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator()
{
return new \ArrayIterator($this->data);
}
}
+67 -13
View File
@@ -1,13 +1,39 @@
<?php
namespace Pecee\Http\Input;
class InputFile extends InputItem
class InputFile implements IInputItem
{
public $index;
public $name;
public $size;
public $type;
public $error;
public $tmpName;
public function __construct($index)
{
$this->index = $index;
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
@@ -60,6 +86,13 @@ class InputFile extends InputItem
return file_get_contents($this->tmpName);
}
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set file temp. name
* @param string $name
@@ -68,6 +101,7 @@ class InputFile extends InputItem
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
@@ -79,6 +113,7 @@ class InputFile extends InputItem
public function setSize($size)
{
$this->size = $size;
return $this;
}
@@ -90,6 +125,7 @@ class InputFile extends InputItem
public function setType($type)
{
$this->type = $type;
return $this;
}
@@ -100,10 +136,21 @@ class InputFile extends InputItem
*/
public function setError($error)
{
$this->error = $error;
$this->error = (int)$error;
return $this;
}
public function getValue()
{
return $this->getTmpName();
}
public function hasError()
{
return ($this->getError() !== 0);
}
/**
* Create from array
* @param array $values
@@ -111,26 +158,33 @@ class InputFile extends InputItem
*/
public static function createFromArray(array $values)
{
if(!isset($values['index'])) {
if (!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required');
}
$input = new static($values['index']);
$input->setTmpName((isset($values['error']) ? $values['error'] : null));
$input->setName((isset($values['name']) ? $values['name'] : null));
$input->setSize((isset($values['size']) ? $values['size'] : null));
$input->setType((isset($values['type']) ? $values['type'] : null));
$input->setError((isset($values['tmp_name']) ? $values['tmp_name'] : null));
$input->setError(isset($values['error']) ? $values['error'] : null);
$input->setName(isset($values['name']) ? $values['name'] : null);
$input->setSize(isset($values['size']) ? $values['size'] : null);
$input->setType(isset($values['type']) ? $values['type'] : null);
$input->setTmpName(isset($values['tmp_name']) ? $values['tmp_name'] : null);
return $input;
}
/**
* @return string
*/
public function getValue()
public function toArray()
{
return $this->tmpName;
return [
'tmp_name' => $this->tmpName,
'type' => $this->type,
'size' => $this->size,
'name' => $this->name,
'error' => $this->error,
];
}
public function __toString()
{
return $this->getValue();
}
}
+9 -9
View File
@@ -1,7 +1,7 @@
<?php
namespace Pecee\Http\Input;
class InputItem
class InputItem implements IInputItem
{
public $index;
public $name;
@@ -16,6 +16,14 @@ class InputItem
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string
*/
@@ -32,14 +40,6 @@ class InputItem
return $this->value;
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* Set input name
* @param string $name
+11 -7
View File
@@ -2,7 +2,7 @@
namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Exceptions\TokenMismatchException;
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
@@ -20,7 +20,7 @@ class BaseCsrfVerifier implements IMiddleware
$this->csrfToken = new CsrfToken();
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
$this->token = ($this->hasToken() === false) ? $this->generateToken() : $this->csrfToken->getToken();
}
/**
@@ -34,7 +34,11 @@ class BaseCsrfVerifier implements IMiddleware
return false;
}
foreach ($this->except as $url) {
$max = count($this->except) - 1;
for ($i = $max; $i >= 0; $i--) {
$url = $this->except[$i];
$url = rtrim($url, '/');
if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*');
@@ -43,7 +47,7 @@ class BaseCsrfVerifier implements IMiddleware
$skip = ($url === rtrim($request->getUri(), '/'));
}
if ($skip) {
if ($skip === true) {
return true;
}
}
@@ -54,16 +58,16 @@ class BaseCsrfVerifier implements IMiddleware
public function handle(Request $request, ILoadableRoute &$route = null)
{
if ($request->getMethod() !== 'get' && !$this->skip($request)) {
if (in_array($request->getMethod(), ['post', 'put', 'delete']) === true && $this->skip($request) === false) {
$token = $request->getInput()->post->get(static::POST_KEY);
$token = $request->getInput()->get(static::POST_KEY, null, 'post');
// If the token is not posted, check headers for valid x-csrf-token
if ($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
if (!$this->csrfToken->validate($token)) {
if ($this->csrfToken->validate($token) === false) {
throw new TokenMismatchException('Invalid csrf-token.');
}
@@ -1,5 +1,5 @@
<?php
namespace Pecee\Exceptions;
namespace Pecee\Http\Middleware\Exceptions;
class TokenMismatchException extends \Exception
{
+32 -8
View File
@@ -5,7 +5,7 @@ use Pecee\Http\Input\Input;
class Request
{
protected $data = array();
protected $data = [];
protected $headers;
protected $host;
protected $uri;
@@ -15,20 +15,27 @@ class Request
public function __construct()
{
$this->parseHeaders();
$this->input = new Input($this);
$this->host = $this->getHeader('http-host');;
$this->uri = $this->getHeader('request-uri');
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request-method')));
$this->input = new Input($this);
$this->method = $this->input->get('_method', strtolower($this->getHeader('request-method')));
}
protected function parseHeaders()
{
$this->headers = array();
$this->headers = [];
foreach ($_SERVER as $name => $value) {
$this->headers[strtolower($name)] = $value;
$this->headers[strtolower(str_replace('_', '-', $name))] = $value;
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
for($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $_SERVER[$key];
$this->headers[strtolower($key)] = $value;
$this->headers[strtolower(str_replace('_', '-', $key))] = $value;
}
}
public function isSecure()
@@ -136,7 +143,24 @@ class Request
*/
public function getHeader($name, $defaultValue = null)
{
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
if (isset($this->headers[strtolower($name)])) {
return $this->headers[strtolower($name)];
}
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$name = $_SERVER[$key];
if ($key === $name) {
return $name;
}
}
return $defaultValue;
}
/**
+9 -9
View File
@@ -66,13 +66,12 @@ class Response
'Etag: ' . $eTag
]);
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified ||
isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag
) {
$httpModified = $this->request->getHeader('http-if-modified-since');
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
$this->headers([
'HTTP/1.1 304 Not Modified'
]);
if ($httpModified !== null && strtotime($httpModified) == $lastModified || $httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) {
$this->header('HTTP/1.1 304 Not Modified');
exit();
}
@@ -86,7 +85,7 @@ class Response
*/
public function json(array $value)
{
$this->header('Content-type: application/json');
$this->header('Content-Type: application/json');
echo json_encode($value);
die();
}
@@ -109,8 +108,9 @@ class Response
*/
public function headers(array $headers)
{
foreach ($headers as $header) {
header($header);
$max = count($headers);
for($i = 0; $i < $max; $i++) {
$this->header($headers[$i]);
}
return $this;
}
+27 -6
View File
@@ -22,7 +22,12 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
public function loadMiddleware(Request $request, ILoadableRoute &$route)
{
if (count($this->getMiddlewares()) > 0) {
foreach ($this->getMiddlewares() as $middleware) {
$max = count($this->getMiddlewares());
for ($i = 0; $i < $max; $i++) {
$middleware = $this->getMiddlewares()[$i];
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
@@ -43,12 +48,21 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
public function setUrl($url)
{
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
foreach ($matches[1] as $key) {
$this->parameters[$key] = null;
if(strpos($this->url, $this->paramModifiers[0]) !== false) {
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
$max = count($matches[1]);
for ($i = 0; $i < $max; $i++) {
$this->parameters[$matches[1][$i]] = null;
}
}
}
return $this;
@@ -92,7 +106,14 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Let's parse the values of any {} parameter in the url */
foreach ($params as $param => $value) {
$max = count($params) - 1;
$keys = array_keys($params);
for ($i = $max; $i >= 0; $i--) {
$param = $keys[$i];
$value = $params[$param];
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
+10 -4
View File
@@ -77,10 +77,11 @@ abstract class Route implements IRoute
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route) - 1;
for ($i = 0; $i < strlen($route); $i++) {
for ($i = $routeLength; $i >= 0; $i--) {
$character = $route[$i];
$character = strrev($route)[$i];
if ($character === '{') {
/* Remove "/" and "\" from regex */
@@ -129,7 +130,12 @@ abstract class Route implements IRoute
$parameters = [];
foreach ($parameterNames as $name) {
$max = count($parameterNames) - 1;
for ($i = $max; $i >= 0; $i--) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if ($name['required'] && $parameterValue === null) {
@@ -152,7 +158,7 @@ abstract class Route implements IRoute
protected function loadClass($name)
{
if (!class_exists($name)) {
throw new HttpException(sprintf('Class %s does not exist', $name), 500);
throw new NotFoundHttpException(sprintf('Class %s does not exist', $name), 404);
}
return new $name();
@@ -59,12 +59,19 @@ class RouteController extends LoadableRoute implements IControllerRoute
/* Remove requestType from method-name, if it exists */
if ($method !== null) {
foreach (static::$requestTypes as $requestType) {
$max = count(static::$requestTypes);
for ($i = 0; $i < $max; $i++) {
$requestType = static::$requestTypes[$i];
if (stripos($method, $requestType) === 0) {
$method = substr($method, strlen($requestType));
break;
}
}
$method .= '/';
}
+5 -1
View File
@@ -19,8 +19,12 @@ class RouteGroup extends Route implements IGroupRoute
public function matchDomain(Request $request)
{
if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
$max = count($this->domains) - 1;
for ($i = $max; $i >= 0; $i--) {
$domain = $this->domains[$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null) {
@@ -107,7 +107,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
$method = request()->getMethod();
$method = $request->getMethod();
// Delete
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_DELETE) {
+6 -2
View File
@@ -19,8 +19,12 @@ class RouteUrl extends LoadableRoute
// Match on custom defined regular expression
if ($this->regex !== null) {
$parameters = [];
if (preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (array)$parameters[0];
if (preg_match($this->regex, $request->getHost() . $url, $parameters)) {
/* Remove global match */
if(count($parameters) > 1) {
array_shift($parameters);
$this->parameters = $parameters;
}
return true;
}
+41 -11
View File
@@ -140,8 +140,12 @@ class Router
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
// Loop through each route-request
$max = count($routes) - 1;
/* @var $route IRoute */
foreach ($routes as $route) {
for ($i = $max; $i >= 0; $i--) {
$route = $routes[$i];
if ($route instanceof IGroupRoute) {
@@ -208,8 +212,14 @@ class Router
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
foreach ($this->bootManagers as $manager) {
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$this->request = $manager->boot($this->request);
if (!($this->request instanceof Request)) {
@@ -232,8 +242,12 @@ class Router
$this->originalUrl = $this->request->getUri();
}
$max = count($this->processedRoutes) - 1;
/* @var $route IRoute */
foreach ($this->processedRoutes as $route) {
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* If the route matches */
if ($route->matchRoute($this->request)) {
@@ -279,8 +293,12 @@ class Router
protected function handleException(\Exception $e)
{
$max = count($this->exceptionHandlers);
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $handler) {
for ($i = 0; $i < $max; $i++) {
$handler = $this->exceptionHandlers[$i];
$handler = new $handler();
@@ -327,8 +345,12 @@ class Router
*/
public function findRoute($name)
{
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
foreach ($this->processedRoutes as $route) {
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the name matches with a name on the route. Should match either router alias or controller alias. */
if ($route->hasName($name)) {
@@ -384,19 +406,23 @@ class Router
* @param array|null $getParams
* @return string
*/
public function getUrl($name = null, $parameters = null, $getParams = [])
public function getUrl($name = null, $parameters = null, $getParams = null)
{
if ($getParams !== null && is_array($getParams) === false) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
if ($getParams === null) {
/* Only merge $_GET when all parameters are null */
if ($name === null && $parameters === null && $getParams === null) {
$getParams = $_GET;
} else {
$getParams = (array)$getParams;
}
/* Return current route if no options has been specified */
if ($name === null && $parameters === null) {
return '/' . trim(parse_url($this->request->getUri(), PHP_URL_PATH), '/') . '/' . $this->arrayToParams($getParams);
$url = rtrim(parse_url($this->request->getUri(), PHP_URL_PATH), '/');
return (($url === '') ? '/' : $url . '/') . $this->arrayToParams($getParams);
}
/* If nothing is defined and a route is loaded we use that */
@@ -417,8 +443,12 @@ class Router
/* Loop through all the routes to see if we can find a match */
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
foreach ($this->processedRoutes as $route) {
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the route contains the name/alias */
if ($route->hasName($controller)) {
@@ -434,8 +464,8 @@ class Router
}
/* No result so we assume that someone is using a hardcoded url and join everything together. */
return '/' . trim(join('/', array_merge((array)$name, (array)$parameters)), '/') . '/' . $this->arrayToParams($getParams);
$url = trim(join('/', array_merge((array)$name, (array)$parameters)), '/');
return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams);
}
/**
+1 -1
View File
@@ -311,7 +311,7 @@ class SimpleRouter
* @param array|null $getParams
* @return string
*/
public static function getUrl($name = null, $parameters = null, $getParams = [])
public static function getUrl($name = null, $parameters = null, $getParams = null)
{
return static::router()->getUrl($name, $parameters, $getParams);
}