diff --git a/README.md b/README.md index 58f4919..7b6a68d 100644 --- a/README.md +++ b/README.md @@ -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']); url('product', ['id' => 22], ['category' => 'shoes']); +url('product', null, ['category' => 'shoes']); # output # /product-view/22/?category=shoes +# /product-view/?category=shoes ``` **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', ['category' => 'shoes']); +url('picture', 'view'); # output # /images/view/?category=shows # /images/view/?category=shows +# /images/view/ ``` **Getting the url using class** @@ -370,7 +374,7 @@ url('ImagesController@getImage', null, ['id' => 22]); # output # /product-view/22/?category=shoes -# /images/image/?category=shows +# /images/image/?id=22 ``` **Using custom names for methods on a controller/resource route** @@ -405,6 +409,11 @@ url('phones.edit'); **Return the current url** ```php url(); +url(null, null, ['q' => 'cars']); + +# output +# /CURRENT-URL/ +# /CURRENT-URL/?q=cars ``` ## 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. @@ -526,7 +534,6 @@ class CustomMiddleware implements Middleware { } } - ``` #### Changing callback @@ -558,32 +565,78 @@ class CustomMiddleware implements Middleware { ## 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):** +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 -$value = input()->get('name'); +$value = input()->get($index, $defaultValue); ``` **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 -$object = input()->getObject('name'); +$object = input()->getObject($index); ``` **Return specific GET parameter (where name is the name of your parameter):** ```php -$object = input()->get->name; -$object = input()->post->name; -$object = input()->file->name; +# -- match any (default) -- + +/* + * 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 -- -$object = input()->get->get($key, $defaultValue); -$object = input()->post->get($key, $defaultValue); -$object = input()->file->get($key, $defaultValue); +$object = input()->findGet($index, $defaultValue); +$object = input()->findPost($index, $defaultValue); +$object = input()->findFile($index, $defaultValue); + +# -- examples -- + +/** + * In this small example we loop through a collection of files + * added on the page like this + * + */ + +/* @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:** @@ -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. - `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: - `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. - `getType()` - get mime-type for file. - `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. diff --git a/src/Pecee/Http/Input/IInputItem.php b/src/Pecee/Http/Input/IInputItem.php new file mode 100644 index 0000000..5846788 --- /dev/null +++ b/src/Pecee/Http/Input/IInputItem.php @@ -0,0 +1,15 @@ +request = $request; - $this->post = new InputCollection(); - $this->get = new InputCollection(); - $this->file = new InputCollection(); - if ($request->getMethod() !== 'get') { $requestContentType = $request->getHeader('http-content-type'); @@ -60,6 +56,7 @@ class Input if ($this->invalidContentType === false) { $this->parseInputs(); } + } protected function parseInputs() @@ -76,7 +73,21 @@ class Input $key = $keys[$i]; $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]; $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 if (is_array($value['name']) === false) { $values['index'] = $key; - $this->file->{strtolower($key)} = InputFile::createFromArray($values); + $this->file[$key] = InputFile::createFromArray(array_merge($value, $values)); 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, - 'error' => $value['error'][$k], - 'tmp_name' => $value['tmp_name'][$k], - 'type' => $value['type'][$k], - 'size' => $value['size'][$k], - 'name' => $value['name'][$k], + '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->{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; - $index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index; + return isset($this->post[$index]) ? $this->post[$index] : $default; + } - $element = $this->get->findFirst($index); + public function findFile($index, $default = null) + { + return isset($this->file[$index]) ? $this->file[$index] : $default; + } - if ($element !== null) { - return ($key !== null) ? $element[$key] : $element; + 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 ($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; - } + if ($element === null && $method === null || strtolower($method) === 'post') { + $element = $this->findPost($index); } - 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) { - $item = $this->getObject($index); + $input = $this->getObject($index, $default); - if ($item !== null) { - - if ($item instanceof InputCollection || $item instanceof InputFile) { - return $item; - } - - return (!is_array($item->getValue()) && trim($item->getValue()) === '') ? $default : $item; + if ($input instanceof InputItem) { + return (trim($input->getValue()) === '') ? $default : $input->getValue(); } - return $default; + return $input; } public function exists($index) diff --git a/src/Pecee/Http/Input/InputCollection.php b/src/Pecee/Http/Input/InputCollection.php deleted file mode 100644 index b4227c2..0000000 --- a/src/Pecee/Http/Input/InputCollection.php +++ /dev/null @@ -1,91 +0,0 @@ -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 Iterator or - * Traversable - * @since 5.0.0 - */ - public function getIterator() - { - return new \ArrayIterator($this->data); - } - -} \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputFile.php b/src/Pecee/Http/Input/InputFile.php index 6cd1b35..8a38794 100644 --- a/src/Pecee/Http/Input/InputFile.php +++ b/src/Pecee/Http/Input/InputFile.php @@ -1,13 +1,39 @@ 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; } @@ -101,6 +137,7 @@ class InputFile extends InputItem public function setError($error) { $this->error = (int)$error; + return $this; } @@ -109,7 +146,8 @@ class InputFile extends InputItem return $this->getTmpName(); } - public function hasError() { + public function hasError() + { return ($this->getError() !== 0); } @@ -120,28 +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->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)); + $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; } - public function toArray() { + public function toArray() + { return [ 'tmp_name' => $this->tmpName, - 'type' => $this->type, - 'size' => $this->size, - 'name' => $this->name, - 'error' => $this->error, + 'type' => $this->type, + 'size' => $this->size, + 'name' => $this->name, + 'error' => $this->error, ]; } + public function __toString() + { + return $this->getValue(); + } } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index 71d0253..934477b 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -1,7 +1,7 @@ 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 diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 14c6732..af58695 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -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; } } @@ -56,14 +60,14 @@ class BaseCsrfVerifier implements IMiddleware 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 ($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.'); } diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 0f14387..4b2a06c 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -25,9 +25,15 @@ class Request { $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; } } diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index e62d1f3..f8657da 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -59,7 +59,12 @@ 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; diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 13a83d8..0cbe242 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -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); + + for ($i = 0; $i < $max; $i++) { + + $domain = $this->domains[$i]; $parameters = $this->parseParameters($domain, $request->getHost(), '.*'); if ($parameters !== null) { diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index a5c867c..a34e66c 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -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) { @@ -238,10 +242,10 @@ class Router $this->originalUrl = $this->request->getUri(); } - $max = count($this->processedRoutes); + $max = count($this->processedRoutes) - 1; /* @var $route IRoute */ - for ($i = 0; $i < $max; $i++) { + for ($i = $max; $i >= 0; $i--) { $route = $this->processedRoutes[$i];