Development

- Optimised Input and Input-related features.
- Removed InputCollection class.
- Changed more foreach to for.
- Updated documentation.
This commit is contained in:
Simon Sessingø
2016-11-24 22:44:58 +01:00
parent b2f23c6c7d
commit abe427ff59
11 changed files with 263 additions and 188 deletions
+69 -14
View File
@@ -341,9 +341,11 @@ By default all controller and resource routes will use a simplified version of t
SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']); SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
url('product', ['id' => 22], ['category' => 'shoes']); url('product', ['id' => 22], ['category' => 'shoes']);
url('product', null, ['category' => 'shoes']);
# output # output
# /product-view/22/?category=shoes # /product-view/22/?category=shoes
# /product-view/?category=shoes
``` ```
**Getting the url using the name (controller route)** **Getting the url using the name (controller route)**
@@ -353,10 +355,12 @@ SimpleRouter::controller('/images', 'ImagesController', ['as' => 'picture']);
url('picture@getView', null, ['category' => 'shoes']); url('picture@getView', null, ['category' => 'shoes']);
url('picture', 'getView', ['category' => 'shoes']); url('picture', 'getView', ['category' => 'shoes']);
url('picture', 'view');
# output # output
# /images/view/?category=shows # /images/view/?category=shows
# /images/view/?category=shows # /images/view/?category=shows
# /images/view/
``` ```
**Getting the url using class** **Getting the url using class**
@@ -370,7 +374,7 @@ url('ImagesController@getImage', null, ['id' => 22]);
# output # output
# /product-view/22/?category=shoes # /product-view/22/?category=shoes
# /images/image/?category=shows # /images/image/?id=22
``` ```
**Using custom names for methods on a controller/resource route** **Using custom names for methods on a controller/resource route**
@@ -405,6 +409,11 @@ url('phones.edit');
**Return the current url** **Return the current url**
```php ```php
url(); url();
url(null, null, ['q' => 'cars']);
# output
# /CURRENT-URL/
# /CURRENT-URL/?q=cars
``` ```
## Custom CSRF verifier ## Custom CSRF verifier
@@ -466,7 +475,6 @@ class CustomRouterRules implement IRouterBootManager {
} }
} }
``` ```
The above should be pretty self-explanatory and can easily be changed to loop through urls store in the database, file or cache. The above should be pretty self-explanatory and can easily be changed to loop through urls store in the database, file or cache.
@@ -526,7 +534,6 @@ class CustomMiddleware implements Middleware {
} }
} }
``` ```
#### Changing callback #### Changing callback
@@ -558,32 +565,78 @@ class CustomMiddleware implements Middleware {
## Using the Input class to manage parameters ## Using the Input class to manage parameters
We've added the `Input` class to easy access parameters from your Controller-classes. We've added the `Input` class to easy access and manage parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):** **Return single parameter value (matches both GET, POST, FILE):**
If items is grouped in the html, it will return an array of items.
**Note:** `get` will automatically trim the value and ensure that it's not empty. If it's empty the `$defaultValue` will be returned.
```php ```php
$value = input()->get('name'); $value = input()->get($index, $defaultValue);
``` ```
**Return parameter object (matches both GET, POST, FILE):** **Return parameter object (matches both GET, POST, FILE):**
Will return an instance of `InputItem` or `InputFile` depending on the type.
You can use this in your html as it will render the value of the item.
However if you want to compare value in your if statements, you have to use
the `getValue` or use the `input()->get()` instead.
If items is grouped in the html, it will return an array of items.
**Note:** `getObject` will only return `$defaultValue` if the item doesn't exist. If you want `$defaultValue` to be returned if the item is empty, please use `input()->get()` instead.
```php ```php
$object = input()->getObject('name'); $object = input()->getObject($index);
``` ```
**Return specific GET parameter (where name is the name of your parameter):** **Return specific GET parameter (where name is the name of your parameter):**
```php ```php
$object = input()->get->name; # -- match any (default) --
$object = input()->post->name;
$object = input()->file->name; /*
* This is the recommended way to go for normal usage
* as it will strip empty values, ensuring that
* $defaultValue is returned if the value is empty.
*/
$id = input()->get($index, $defaultValue);
# -- match specific --
$object = input()->get($index, $defaultValue, 'get');
$object = input()->get($index, $defaultValue, 'post');
$object = input()->get($index, $defaultValue, 'file');
# -- or -- # -- or --
$object = input()->get->get($key, $defaultValue); $object = input()->findGet($index, $defaultValue);
$object = input()->post->get($key, $defaultValue); $object = input()->findPost($index, $defaultValue);
$object = input()->file->get($key, $defaultValue); $object = input()->findFile($index, $defaultValue);
# -- examples --
/**
* In this small example we loop through a collection of files
* added on the page like this
* <input type="file" name="images[]" />
*/
/* @var $image \Pecee\Http\Input\InputFile */
foreach(input()->get('images', []) as $image)
{
if($image->getMime() === 'image/jpeg') {
$destinationFilname = sprintf('%s.%s', uniqid(), $image->getExtension());
$image->move('/uploads/' . $destinationFilename);
}
}
``` ```
**Return all parameters:** **Return all parameters:**
@@ -599,11 +652,11 @@ $values = input()->all([
]); ]);
``` ```
All object inherits from `InputItem` class and will always contain these methods: All object implements the `IInputItem` interface and will always contain these methods:
- `getValue()` - returns the value of the input.
- `getIndex()` - returns the index/key of the input. - `getIndex()` - returns the index/key of the input.
- `getName()` - returns a human friendly name for the input (company_name will be Company Name etc). - `getName()` - returns a human friendly name for the input (company_name will be Company Name etc).
- `getValue()` - returns the value of the input.
`InputFile` has the same methods as above along with some other file-specific methods like: `InputFile` has the same methods as above along with some other file-specific methods like:
- `getTmpName()` - get file temporary name. - `getTmpName()` - get file temporary name.
@@ -612,6 +665,8 @@ All object inherits from `InputItem` class and will always contain these methods
- `getContents()` - get file content. - `getContents()` - get file content.
- `getType()` - get mime-type for file. - `getType()` - get mime-type for file.
- `getError()` - get file upload error. - `getError()` - get file upload error.
- `hasError()` - returns `bool` if an error occurred while uploading (if getError is not 0).
- `toArray()` - returns raw array
Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation. Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation.
+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();
}
+79 -49
View File
@@ -6,19 +6,19 @@ use Pecee\Http\Request;
class Input 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 * @var Request
@@ -36,10 +36,6 @@ class Input
{ {
$this->request = $request; $this->request = $request;
$this->post = new InputCollection();
$this->get = new InputCollection();
$this->file = new InputCollection();
if ($request->getMethod() !== 'get') { if ($request->getMethod() !== 'get') {
$requestContentType = $request->getHeader('http-content-type'); $requestContentType = $request->getHeader('http-content-type');
@@ -60,6 +56,7 @@ class Input
if ($this->invalidContentType === false) { if ($this->invalidContentType === false) {
$this->parseInputs(); $this->parseInputs();
} }
} }
protected function parseInputs() protected function parseInputs()
@@ -76,7 +73,21 @@ class Input
$key = $keys[$i]; $key = $keys[$i];
$value = $_GET[$key]; $value = $_GET[$key];
$this->get->{$key} = new InputItem($key, $value); // Handle array input
if (is_array($value) === false) {
$this->get[$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]]);
}
$this->get[$key] = $output;
} }
} }
@@ -99,7 +110,21 @@ class Input
$key = $keys[$i]; $key = $keys[$i];
$value = $postVars[$key]; $value = $postVars[$key];
$this->post->{strtolower($key)} = new InputItem($key, $value); // Handle array input
if (is_array($value) === false) {
$this->post[$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]]);
}
$this->post[$key] = $output;
} }
} }
@@ -119,56 +144,66 @@ class Input
// Handle array input // Handle array input
if (is_array($value['name']) === false) { if (is_array($value['name']) === false) {
$values['index'] = $key; $values['index'] = $key;
$this->file->{strtolower($key)} = InputFile::createFromArray($values); $this->file[$key] = InputFile::createFromArray(array_merge($value, $values));
continue; continue;
} }
$output = new InputCollection(); $subMax = count($value['name']) - 1;
$keys = array_keys($value['name']);
$output = [];
foreach ($value['name'] as $k => $val) { for ($i = $subMax; $i >= 0; $i--) {
$output->{$k} = InputFile::createFromArray([ $output[$keys[$i]] = InputFile::createFromArray([
'index' => $key, 'index' => $key,
'error' => $value['error'][$k], 'error' => $value['error'][$keys[$i]],
'tmp_name' => $value['tmp_name'][$k], 'tmp_name' => $value['tmp_name'][$keys[$i]],
'type' => $value['type'][$k], 'type' => $value['type'][$keys[$i]],
'size' => $value['size'][$k], 'size' => $value['size'][$keys[$i]],
'name' => $value['name'][$k], 'name' => $value['name'][$keys[$i]],
]); ]);
} }
$this->file->{strtolower($key)} = $output; $this->file[$key] = $output;
} }
} }
} }
public function getObject($index, $default = null) public function findPost($index, $default = null)
{ {
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[') + 1, strpos($index, ']') - strlen($index)) : null; return isset($this->post[$index]) ? $this->post[$index] : $default;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index; }
$element = $this->get->findFirst($index); public function findFile($index, $default = null)
{
return isset($this->file[$index]) ? $this->file[$index] : $default;
}
if ($element !== null) { public function findGet($index, $default = null)
return ($key !== null) ? $element[$key] : $element; {
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 ($this->request->getMethod() !== 'get') { if ($element === null && $method === null || strtolower($method) === 'post') {
$element = $this->findPost($index);
$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; if ($element === null && $method === null || strtolower($method) === 'file') {
$element = $this->findFile($index);
}
return ($element === null) ? $default : $element;
} }
/** /**
@@ -180,18 +215,13 @@ class Input
*/ */
public function get($index, $default = null) public function get($index, $default = null)
{ {
$item = $this->getObject($index); $input = $this->getObject($index, $default);
if ($item !== null) { if ($input instanceof InputItem) {
return (trim($input->getValue()) === '') ? $default : $input->getValue();
if ($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
return (!is_array($item->getValue()) && trim($item->getValue()) === '') ? $default : $item;
} }
return $default; return $input;
} }
public function exists($index) public function exists($index)
-91
View File
@@ -1,91 +0,0 @@
<?php
namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate
{
protected $data = [];
/**
* 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;
}
/**
* @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);
}
}
+56 -13
View File
@@ -1,13 +1,39 @@
<?php <?php
namespace Pecee\Http\Input; namespace Pecee\Http\Input;
class InputFile extends InputItem class InputFile implements IInputItem
{ {
public $index;
public $name;
public $size; public $size;
public $type; public $type;
public $error; public $error;
public $tmpName; 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 * @return string
*/ */
@@ -60,6 +86,13 @@ class InputFile extends InputItem
return file_get_contents($this->tmpName); return file_get_contents($this->tmpName);
} }
public function setName($name)
{
$this->name = $name;
return $this;
}
/** /**
* Set file temp. name * Set file temp. name
* @param string $name * @param string $name
@@ -68,6 +101,7 @@ class InputFile extends InputItem
public function setTmpName($name) public function setTmpName($name)
{ {
$this->tmpName = $name; $this->tmpName = $name;
return $this; return $this;
} }
@@ -79,6 +113,7 @@ class InputFile extends InputItem
public function setSize($size) public function setSize($size)
{ {
$this->size = $size; $this->size = $size;
return $this; return $this;
} }
@@ -90,6 +125,7 @@ class InputFile extends InputItem
public function setType($type) public function setType($type)
{ {
$this->type = $type; $this->type = $type;
return $this; return $this;
} }
@@ -101,6 +137,7 @@ class InputFile extends InputItem
public function setError($error) public function setError($error)
{ {
$this->error = (int)$error; $this->error = (int)$error;
return $this; return $this;
} }
@@ -109,7 +146,8 @@ class InputFile extends InputItem
return $this->getTmpName(); return $this->getTmpName();
} }
public function hasError() { public function hasError()
{
return ($this->getError() !== 0); return ($this->getError() !== 0);
} }
@@ -120,28 +158,33 @@ class InputFile extends InputItem
*/ */
public static function createFromArray(array $values) public static function createFromArray(array $values)
{ {
if(!isset($values['index'])) { if (!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required'); throw new \InvalidArgumentException('Index key is required');
} }
$input = new static($values['index']); $input = new static($values['index']);
$input->setError((isset($values['error']) ? $values['error'] : null)); $input->setError(isset($values['error']) ? $values['error'] : null);
$input->setName((isset($values['name']) ? $values['name'] : null)); $input->setName(isset($values['name']) ? $values['name'] : null);
$input->setSize((isset($values['size']) ? $values['size'] : null)); $input->setSize(isset($values['size']) ? $values['size'] : null);
$input->setType((isset($values['type']) ? $values['type'] : null)); $input->setType(isset($values['type']) ? $values['type'] : null);
$input->setTmpName((isset($values['tmp_name']) ? $values['tmp_name'] : null)); $input->setTmpName(isset($values['tmp_name']) ? $values['tmp_name'] : null);
return $input; return $input;
} }
public function toArray() { public function toArray()
{
return [ return [
'tmp_name' => $this->tmpName, 'tmp_name' => $this->tmpName,
'type' => $this->type, 'type' => $this->type,
'size' => $this->size, 'size' => $this->size,
'name' => $this->name, 'name' => $this->name,
'error' => $this->error, 'error' => $this->error,
]; ];
} }
public function __toString()
{
return $this->getValue();
}
} }
+9 -9
View File
@@ -1,7 +1,7 @@
<?php <?php
namespace Pecee\Http\Input; namespace Pecee\Http\Input;
class InputItem class InputItem implements IInputItem
{ {
public $index; public $index;
public $name; public $name;
@@ -16,6 +16,14 @@ class InputItem
$this->name = ucfirst(str_replace('_', ' ', $this->index)); $this->name = ucfirst(str_replace('_', ' ', $this->index));
} }
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/** /**
* @return string * @return string
*/ */
@@ -32,14 +40,6 @@ class InputItem
return $this->value; return $this->value;
} }
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/** /**
* Set input name * Set input name
* @param string $name * @param string $name
@@ -34,7 +34,11 @@ class BaseCsrfVerifier implements IMiddleware
return false; 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, '/'); $url = rtrim($url, '/');
if ($url[strlen($url) - 1] === '*') { if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*'); $url = rtrim($url, '*');
@@ -43,7 +47,7 @@ class BaseCsrfVerifier implements IMiddleware
$skip = ($url === rtrim($request->getUri(), '/')); $skip = ($url === rtrim($request->getUri(), '/'));
} }
if ($skip) { if ($skip === true) {
return true; return true;
} }
} }
@@ -56,14 +60,14 @@ class BaseCsrfVerifier implements IMiddleware
if ($request->getMethod() !== 'get' && !$this->skip($request)) { if ($request->getMethod() !== 'get' && !$this->skip($request)) {
$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 the token is not posted, check headers for valid x-csrf-token
if ($token === null) { if ($token === null) {
$token = $request->getHeader(static::HEADER_KEY); $token = $request->getHeader(static::HEADER_KEY);
} }
if (!$this->csrfToken->validate($token)) { if ($this->csrfToken->validate($token) === false) {
throw new TokenMismatchException('Invalid csrf-token.'); throw new TokenMismatchException('Invalid csrf-token.');
} }
+9 -3
View File
@@ -25,9 +25,15 @@ class Request
{ {
$this->headers = []; $this->headers = [];
foreach ($_SERVER as $name => $value) { $max = count($_SERVER) - 1;
$this->headers[strtolower($name)] = $value; $keys = array_keys($_SERVER);
$this->headers[strtolower(str_replace('_', '-', $name))] = $value;
for($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $_SERVER[$key];
$this->headers[strtolower($key)] = $value;
$this->headers[strtolower(str_replace('_', '-', $key))] = $value;
} }
} }
@@ -59,7 +59,12 @@ class RouteController extends LoadableRoute implements IControllerRoute
/* Remove requestType from method-name, if it exists */ /* Remove requestType from method-name, if it exists */
if ($method !== null) { 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) { if (stripos($method, $requestType) === 0) {
$method = substr($method, strlen($requestType)); $method = substr($method, strlen($requestType));
break; break;
+5 -1
View File
@@ -19,8 +19,12 @@ class RouteGroup extends Route implements IGroupRoute
public function matchDomain(Request $request) public function matchDomain(Request $request)
{ {
if (count($this->domains) > 0) { if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
$max = count($this->domains);
for ($i = 0; $i < $max; $i++) {
$domain = $this->domains[$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '.*'); $parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null) { if ($parameters !== null) {
+7 -3
View File
@@ -140,8 +140,12 @@ class Router
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null) protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{ {
// Loop through each route-request // Loop through each route-request
$max = count($routes) - 1;
/* @var $route IRoute */ /* @var $route IRoute */
foreach ($routes as $route) { for ($i = $max; $i >= 0; $i--) {
$route = $routes[$i];
if ($route instanceof IGroupRoute) { if ($route instanceof IGroupRoute) {
@@ -238,10 +242,10 @@ class Router
$this->originalUrl = $this->request->getUri(); $this->originalUrl = $this->request->getUri();
} }
$max = count($this->processedRoutes); $max = count($this->processedRoutes) - 1;
/* @var $route IRoute */ /* @var $route IRoute */
for ($i = 0; $i < $max; $i++) { for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i]; $route = $this->processedRoutes[$i];