diff --git a/src/Pecee/Http/Input/Input.php b/src/Pecee/Http/Input/Input.php index b094f63..810bb6c 100644 --- a/src/Pecee/Http/Input/Input.php +++ b/src/Pecee/Http/Input/Input.php @@ -25,48 +25,123 @@ class Input */ 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(); - } - /** - * Get all get/post items - * @param array|null $filter Only take items in filter - * @return array - */ - public function all(array $filter = null) - { - $output = $_POST; + $this->post = new InputCollection(); + $this->get = new InputCollection(); + $this->file = new InputCollection(); - if ($this->request->getMethod() === 'post') { + if ($request->getMethod() !== 'get') { - $contents = file_get_contents('php://input'); + $requestContentType = $request->getHeader('http-content-type'); - if (stripos(trim($contents), '{') === 0) { - $output = json_decode($contents, true); - if ($output === false) { - $output = array(); + $max = count($this->invalidContentTypes) - 1; + + for ($i = $max; $i >= 0; $i--) { + + $contentType = $this->invalidContentType[$i]; + + if (stripos($requestContentType, $contentType) === 0) { + $this->invalidContentType = true; + break; } } } - $output = array_merge($_GET, $output); + if ($this->invalidContentType === false) { + $this->parseInputs(); + } + } - if ($filter !== null) { - $output = array_filter($output, function ($key) use ($filter) { - if (in_array($key, $filter)) { - return true; - } + protected function parseInputs() + { + /* Parse get requests */ + + if (count($_GET) > 0) { + + $max = count($_GET) - 1; + $keys = array_keys($_GET); + + for ($i = $max; $i >= 0; $i--) { + + $key = $keys[$i]; + $value = $_GET[$key]; + + $this->get->{$key} = new InputItem($key, $value); + } - return false; - }, ARRAY_FILTER_USE_KEY); } - return $output; + /* 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) { + + $max = count($postVars) - 1; + $keys = array_keys($postVars); + + for ($i = $max; $i >= 0; $i--) { + + $key = $keys[$i]; + $value = $postVars[$key]; + + $this->post->{strtolower($key)} = new InputItem($key, $value); + } + + } + + /* 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->{strtolower($key)} = InputFile::createFromArray($values); + continue; + } + + $output = new InputCollection(); + + foreach ($value['name'] as $k => $val) { + + $output->{$k} = 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], + ]); + + } + + $this->file->{strtolower($key)} = $output; + } + } + } public function getObject($index, $default = null) @@ -98,9 +173,10 @@ class Input /** * Get input element value matching index + * * @param string $index * @param string|null $default - * @return string|null + * @return InputItem|string */ public function get($index, $default = null) { @@ -112,7 +188,7 @@ class Input return $item; } - return (trim($item->getValue()) === '') ? $default : $item->getValue(); + return (!is_array($item->getValue()) && trim($item->getValue()) === '') ? $default : $item->getValue(); } return $default; @@ -123,89 +199,44 @@ class Input return ($this->getObject($index) !== null); } - public function setGet() + /** + * Get all get/post items + * @param array|null $filter Only take items in filter + * @return array + */ + public function all(array $filter = null) { - $this->get = new InputCollection(); + if ($this->invalidContentType === true) { + return []; + } - if (count($_GET) > 0) { - foreach ($_GET as $key => $get) { - if (is_array($get) === false) { - $this->get->{$key} = new InputItem($key, $get); - continue; + $output = $_POST; + + if ($this->request->getMethod() === 'post') { + + $contents = file_get_contents('php://input'); + + if (stripos(trim($contents), '{') === 0) { + $output = json_decode($contents, true); + if ($output === false) { + $output = []; } - - $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(); + $output = array_merge($_GET, $output); - $postVars = $_POST; + if ($filter !== null) { + $output = array_filter($output, function ($key) use ($filter) { + if (in_array($key, $filter)) { + return true; + } - if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) { - parse_str(file_get_contents('php://input'), $postVars); + return false; + }, ARRAY_FILTER_USE_KEY); } - 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; - } - } + return $output; } } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputCollection.php b/src/Pecee/Http/Input/InputCollection.php index 1e2f480..b4227c2 100644 --- a/src/Pecee/Http/Input/InputCollection.php +++ b/src/Pecee/Http/Input/InputCollection.php @@ -3,7 +3,7 @@ namespace Pecee\Http\Input; class InputCollection implements \IteratorAggregate { - protected $data = array(); + protected $data = []; /** * Search for input element matching index. @@ -41,17 +41,13 @@ class InputCollection implements \IteratorAggregate { $input = $this->findFirst($index); - if($input !== null && trim($input->getValue()) !== '') { + if ($input !== null && trim($input->getValue()) !== '') { return $input->getValue(); } return $default; } - public function getValue($index, $default = null) { - - } - /** * @param string $index * @throws \InvalidArgumentException @@ -63,6 +59,7 @@ class InputCollection implements \IteratorAggregate // Ensure that item are always available if ($item === null) { $this->data[$index] = new InputItem($index, null); + return $this->data[$index]; } diff --git a/src/Pecee/Http/Input/InputFile.php b/src/Pecee/Http/Input/InputFile.php index a7668c5..fa6d6b8 100644 --- a/src/Pecee/Http/Input/InputFile.php +++ b/src/Pecee/Http/Input/InputFile.php @@ -104,6 +104,15 @@ class InputFile extends InputItem return $this; } + public function getValue() + { + return $this->getTmpName(); + } + + public function hasError() { + return ($this->getError() !== 0); + } + /** * Create from array * @param array $values @@ -116,21 +125,23 @@ class InputFile extends InputItem } $input = new static($values['index']); - $input->setTmpName((isset($values['error']) ? $values['error'] : 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->setError((isset($values['tmp_name']) ? $values['tmp_name'] : null)); + $input->setTmpName((isset($values['tmp_name']) ? $values['tmp_name'] : null)); return $input; } - /** - * @return string - */ - public function getValue() - { - return $this->tmpName; + public function toArray() { + return [ + 'tmp_name' => $this->tmpName, + 'type' => $this->type, + 'size' => $this->size, + 'name' => $this->name, + 'error' => $this->error, + ]; } } \ No newline at end of file diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index ed96b69..14c6732 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -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; diff --git a/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php b/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php index ceb12a6..8f21c9e 100644 --- a/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php +++ b/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php @@ -1,5 +1,5 @@ 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->method = strtolower($this->getHeader('request-method')); + $this->input = new Input($this); } 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; } + } public function isSecure() @@ -136,7 +137,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; } /** diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php index b50918e..3fe16bb 100644 --- a/src/Pecee/Http/Response.php +++ b/src/Pecee/Http/Response.php @@ -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; } diff --git a/src/Pecee/SimpleRouter/Route/LoadableRoute.php b/src/Pecee/SimpleRouter/Route/LoadableRoute.php index 4d21b52..3971cf9 100644 --- a/src/Pecee/SimpleRouter/Route/LoadableRoute.php +++ b/src/Pecee/SimpleRouter/Route/LoadableRoute.php @@ -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)) { @@ -46,9 +51,13 @@ abstract class LoadableRoute extends Route implements ILoadableRoute $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; + + $max = count($matches[1]); + + for($i = 0; $i < $max; $i++) { + $this->parameters[$matches[1][$i]] = null; } + } return $this; @@ -92,7 +101,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); + $keys = array_keys($params); + + for ($i = 0; $i < $max; $i++) { + $param = $keys[$i]; + $value = $params[$param]; + $value = (isset($parameters[$param])) ? $parameters[$param] : $value; if (stripos($url, $param1) !== false || stripos($url, $param) !== false) { diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 0b9ac87..36d5973 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -129,7 +129,12 @@ abstract class Route implements IRoute $parameters = []; - foreach ($parameterNames as $name) { + $max = count($parameterNames); + + for ($i = 0; $i < $max; $i++) { + + $name = $parameterNames[$i]; + $parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null; if ($name['required'] && $parameterValue === null) { diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 9a0853e..f395a6e 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -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) { diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 8b53d6d..a5c867c 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -208,8 +208,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 = 0; $i < $max; $i++) { + + $manager = $this->bootManagers[$i]; + $this->request = $manager->boot($this->request); if (!($this->request instanceof Request)) { @@ -232,8 +238,12 @@ class Router $this->originalUrl = $this->request->getUri(); } + $max = count($this->processedRoutes); + /* @var $route IRoute */ - foreach ($this->processedRoutes as $route) { + for ($i = 0; $i < $max; $i++) { + + $route = $this->processedRoutes[$i]; /* If the route matches */ if ($route->matchRoute($this->request)) { @@ -279,8 +289,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 +341,12 @@ class Router */ public function findRoute($name) { + $max = count($this->processedRoutes); + /* @var $route ILoadableRoute */ - foreach ($this->processedRoutes as $route) { + for ($i = 0; $i < $max; $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)) { @@ -421,8 +439,12 @@ class Router /* Loop through all the routes to see if we can find a match */ + $max = count($this->processedRoutes); + /* @var $route ILoadableRoute */ - foreach ($this->processedRoutes as $route) { + for ($i = 0; $i < $max; $i++) { + + $route = $this->processedRoutes[$i]; /* Check if the route contains the name/alias */ if ($route->hasName($controller)) {