File manager - Edit - /home/premiey/www/wp-includes/images/media/slim.tar
Back
slim/MAINTAINERS.md 0000666 00000001662 15165376252 0007631 0 ustar 00 # Maintainers There aren't many rules for maintainers of Slim to remember; what we have is listed here. ## We don't merge our own PRs Our code is better if more than one set of eyes looks at it. Therefore we do not merge our own pull requests unless there is an exceptional circumstance. This helps to spot errors in the patch and also enables us to share information about the project around the maintainer team. ## PRs tagged `WIP` are not ready to be merged Sometimes it's helpful to collaborate on a patch before it's ready to be merged. We use the text `WIP` (for _Work in Progress_) in the title to mark these PRs. If a PR has `WIP` in its title, then it is not to be merged. The person who raised the PR will remove the `WIP` text when they are ready for a full review and merge. ## Assign a merged PR to a milestone By ensuring that all merged PRs are assigned to a milestone, we can easily find which PRs were in which release. slim/LICENSE.md 0000666 00000002046 15165376252 0007136 0 ustar 00 Copyright (c) 2011-2019 Josh Lockhart 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. slim/phpstan.neon.dist 0000666 00000000031 15165376252 0011022 0 ustar 00 parameters: level: 1 slim/Slim/RouteGroup.php 0000666 00000001111 15165376252 0011252 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use Slim\Interfaces\RouteGroupInterface; class RouteGroup extends Routable implements RouteGroupInterface { /** * {@inheritdoc} */ public function __invoke(App $app = null) { $callable = $this->resolveCallable($this->callable); if ($callable instanceof Closure && $app !== null) { $callable = $callable->bindTo($app); } $callable($app); } } slim/Slim/App.php 0000666 00000056720 15165376252 0007677 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use BadMethodCallException; use Closure; use Exception; use FastRoute\Dispatcher; use AmeliaPsr\Container\ContainerExceptionInterface as ContainerException; use InvalidArgumentException; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Http\Message\RequestInterface; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Exception\InvalidMethodException; use Slim\Exception\MethodNotAllowedException; use Slim\Exception\NotFoundException; use Slim\Exception\SlimException; use Slim\Http\Body; use Slim\Http\Headers; use Slim\Http\Request; use Slim\Http\Uri; use Slim\Interfaces\RouteGroupInterface; use Slim\Interfaces\RouteInterface; use Slim\Interfaces\RouterInterface; use Throwable; class App { use MiddlewareAwareTrait; /** * Current version * * @var string */ const VERSION = '3.12.3'; /** * @var ContainerInterface */ private $container; /** * @param ContainerInterface|array $container * * @throws InvalidArgumentException When no container is provided that implements ContainerInterface */ public function __construct($container = []) { if (is_array($container)) { $container = new Container($container); } if (!$container instanceof ContainerInterface) { throw new InvalidArgumentException('Expected a ContainerInterface'); } $this->container = $container; } /** * Get container * * @return ContainerInterface */ public function getContainer() { return $this->container; } /** * Add middleware * * This method prepends new middleware to the app's middleware stack. * * @param callable|string $callable The callback routine * * @return static */ public function add($callable) { return $this->addMiddleware(new DeferredCallable($callable, $this->container)); } /** * Calling a non-existent method on App checks to see if there's an item * in the container that is callable and if so, calls it. * * @param string $method * @param array $args * * @return mixed * * @throws BadMethodCallException */ public function __call($method, $args) { if ($this->container->has($method)) { $obj = $this->container->get($method); if (is_callable($obj)) { return call_user_func_array($obj, $args); } } throw new BadMethodCallException("Method $method is not a valid method"); } /** * Add GET route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function get($pattern, $callable) { return $this->map(['GET'], $pattern, $callable); } /** * Add POST route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function post($pattern, $callable) { return $this->map(['POST'], $pattern, $callable); } /** * Add PUT route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function put($pattern, $callable) { return $this->map(['PUT'], $pattern, $callable); } /** * Add PATCH route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function patch($pattern, $callable) { return $this->map(['PATCH'], $pattern, $callable); } /** * Add DELETE route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function delete($pattern, $callable) { return $this->map(['DELETE'], $pattern, $callable); } /** * Add OPTIONS route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function options($pattern, $callable) { return $this->map(['OPTIONS'], $pattern, $callable); } /** * Add route for any HTTP method * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function any($pattern, $callable) { return $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, $callable); } /** * Add route with multiple methods * * @param string[] $methods Numeric array of HTTP method names * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function map(array $methods, $pattern, $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this->container); } $route = $this->container->get('router')->map($methods, $pattern, $callable); if (is_callable([$route, 'setContainer'])) { $route->setContainer($this->container); } if (is_callable([$route, 'setOutputBuffering'])) { $route->setOutputBuffering($this->container->get('settings')['outputBuffering']); } return $route; } /** * Add a route that sends an HTTP redirect * * @param string $from * @param string|UriInterface $to * @param int $status * * @return RouteInterface */ public function redirect($from, $to, $status = 302) { $handler = function ($request, ResponseInterface $response) use ($to, $status) { return $response->withHeader('Location', (string)$to)->withStatus($status); }; return $this->get($from, $handler); } /** * Add a route group * * This method accepts a route pattern and a callback. All route * declarations in the callback will be prepended by the group(s) * that it is in. * * @param string $pattern * @param callable|Closure $callable * * @return RouteGroupInterface */ public function group($pattern, $callable) { /** @var RouterInterface $router */ $router = $this->container->get('router'); /** @var RouteGroup $group */ $group = $router->pushGroup($pattern, $callable); $group->setContainer($this->container); $group($this); $router->popGroup(); return $group; } /** * Run application * * This method traverses the application middleware stack and then sends the * resultant Response object to the HTTP client. * * @param bool|false $silent * * @return ResponseInterface * * @throws Exception * @throws Throwable */ public function run($silent = false) { $response = $this->container->get('response'); try { ob_start(); $response = $this->process($this->container->get('request'), $response); } catch (InvalidMethodException $e) { $response = $this->processInvalidMethod($e->getRequest(), $response); } finally { $output = ob_get_clean(); } if (!empty($output) && $response->getBody()->isWritable()) { $outputBuffering = $this->container->get('settings')['outputBuffering']; if ($outputBuffering === 'prepend') { // prepend output buffer content $body = new Http\Body(fopen('php://temp', 'r+')); $body->write($output . $response->getBody()); $response = $response->withBody($body); } elseif ($outputBuffering === 'append') { // append output buffer content $response->getBody()->write($output); } } $response = $this->finalize($response); if (!$silent) { $this->respond($response); } return $response; } /** * Pull route info for a request with a bad method to decide whether to * return a not-found error (default) or a bad-method error, then run * the handler for that error, returning the resulting response. * * Used for cases where an incoming request has an unrecognized method, * rather than throwing an exception and not catching it all the way up. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws ContainerException */ protected function processInvalidMethod(ServerRequestInterface $request, ResponseInterface $response) { $router = $this->container->get('router'); if (is_callable([$request->getUri(), 'getBasePath']) && is_callable([$router, 'setBasePath'])) { $router->setBasePath($request->getUri()->getBasePath()); } $request = $this->dispatchRouterAndPrepareRoute($request, $router); $routeInfo = $request->getAttribute('routeInfo', [RouterInterface::DISPATCH_STATUS => Dispatcher::NOT_FOUND]); if ($routeInfo[RouterInterface::DISPATCH_STATUS] === Dispatcher::METHOD_NOT_ALLOWED) { return $this->handleException( new MethodNotAllowedException($request, $response, $routeInfo[RouterInterface::ALLOWED_METHODS]), $request, $response ); } return $this->handleException(new NotFoundException($request, $response), $request, $response); } /** * Process a request * * This method traverses the application middleware stack and then returns the * resultant Response object. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Exception * @throws Throwable */ public function process(ServerRequestInterface $request, ResponseInterface $response) { // Ensure basePath is set $router = $this->container->get('router'); if (is_callable([$request->getUri(), 'getBasePath']) && is_callable([$router, 'setBasePath'])) { $router->setBasePath($request->getUri()->getBasePath()); } // Dispatch the Router first if the setting for this is on if ($this->container->get('settings')['determineRouteBeforeAppMiddleware'] === true) { // Dispatch router (note: you won't be able to alter routes after this) $request = $this->dispatchRouterAndPrepareRoute($request, $router); } // Traverse middleware stack try { $response = $this->callMiddlewareStack($request, $response); } catch (Exception $e) { $response = $this->handleException($e, $request, $response); } catch (Throwable $e) { $response = $this->handlePhpError($e, $request, $response); } return $response; } /** * Send the response to the client * * @param ResponseInterface $response */ public function respond(ResponseInterface $response) { // Send response if (!headers_sent()) { // Headers foreach ($response->getHeaders() as $name => $values) { $first = stripos($name, 'Set-Cookie') === 0 ? false : true; foreach ($values as $value) { header(sprintf('%s: %s', $name, $value), $first); $first = false; } } // Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers. // See https://github.com/slimphp/Slim/issues/1730 // Status header(sprintf( 'HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase() ), true, $response->getStatusCode()); } // Body $request = $this->container->get('request'); if (!$this->isEmptyResponse($response) && !$this->isHeadRequest($request)) { $body = $response->getBody(); if ($body->isSeekable()) { $body->rewind(); } $settings = $this->container->get('settings'); $chunkSize = $settings['responseChunkSize']; $contentLength = $response->getHeaderLine('Content-Length'); if (!$contentLength) { $contentLength = $body->getSize(); } if (isset($contentLength)) { $amountToRead = $contentLength; while ($amountToRead > 0 && !$body->eof()) { $data = $body->read(min((int)$chunkSize, (int)$amountToRead)); echo $data; $amountToRead -= strlen($data); if (connection_status() != CONNECTION_NORMAL) { break; } } } else { while (!$body->eof()) { echo $body->read((int)$chunkSize); if (connection_status() != CONNECTION_NORMAL) { break; } } } } } /** * Invoke application * * This method implements the middleware interface. It receives * Request and Response objects, and it returns a Response object * after compiling the routes registered in the Router and dispatching * the Request object to the appropriate Route callback routine. * * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * * @return ResponseInterface * * @throws MethodNotAllowedException * @throws NotFoundException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { // Get the route info $routeInfo = $request->getAttribute('routeInfo'); /** @var RouterInterface $router */ $router = $this->container->get('router'); // If router hasn't been dispatched or the URI changed then dispatch if (null === $routeInfo || ($routeInfo['request'] !== [$request->getMethod(), (string) $request->getUri()])) { $request = $this->dispatchRouterAndPrepareRoute($request, $router); $routeInfo = $request->getAttribute('routeInfo'); } if ($routeInfo[0] === Dispatcher::FOUND) { $route = $router->lookupRoute($routeInfo[1]); return $route->run($request, $response); } elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) { if (!$this->container->has('notAllowedHandler')) { throw new MethodNotAllowedException($request, $response, $routeInfo[1]); } /** @var callable $notAllowedHandler */ $notAllowedHandler = $this->container->get('notAllowedHandler'); return $notAllowedHandler($request, $response, $routeInfo[1]); } if (!$this->container->has('notFoundHandler')) { throw new NotFoundException($request, $response); } /** @var callable $notFoundHandler */ $notFoundHandler = $this->container->get('notFoundHandler'); return $notFoundHandler($request, $response); } /** * Perform a sub-request from within an application route * * This method allows you to prepare and initiate a sub-request, run within * the context of the current request. This WILL NOT issue a remote HTTP * request. Instead, it will route the provided URL, method, headers, * cookies, body, and server variables against the set of registered * application routes. The result response object is returned. * * @param string $method The request method (e.g., GET, POST, PUT, etc.) * @param string $path The request URI path * @param string $query The request URI query string * @param array $headers The request headers (key-value array) * @param array $cookies The request cookies (key-value array) * @param string $bodyContent The request body * @param ResponseInterface $response The response object (optional) * * @return ResponseInterface * * @throws MethodNotAllowedException * @throws NotFoundException */ public function subRequest( $method, $path, $query = '', array $headers = [], array $cookies = [], $bodyContent = '', ResponseInterface $response = null ) { $env = $this->container->get('environment'); $uri = Uri::createFromEnvironment($env)->withPath($path)->withQuery($query); $headers = new Headers($headers); $serverParams = $env->all(); $body = new Body(fopen('php://temp', 'r+')); $body->write($bodyContent); $body->rewind(); $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); if (!$response) { $response = $this->container->get('response'); } return $this($request, $response); } /** * Dispatch the router to find the route. Prepare the route for use. * * @param ServerRequestInterface $request * @param RouterInterface $router * * @return ServerRequestInterface */ protected function dispatchRouterAndPrepareRoute(ServerRequestInterface $request, RouterInterface $router) { $routeInfo = $router->dispatch($request); if ($routeInfo[0] === Dispatcher::FOUND) { $routeArguments = []; foreach ($routeInfo[2] as $k => $v) { $routeArguments[$k] = urldecode($v); } $route = $router->lookupRoute($routeInfo[1]); $route->prepare($request, $routeArguments); // add route to the request's attributes in case a middleware or handler needs access to the route $request = $request->withAttribute('route', $route); } $routeInfo['request'] = [$request->getMethod(), (string) $request->getUri()]; return $request->withAttribute('routeInfo', $routeInfo); } /** * Finalize response * * @param ResponseInterface $response * * @return ResponseInterface * * @throws RuntimeException */ protected function finalize(ResponseInterface $response) { // stop PHP sending a Content-Type automatically ini_set('default_mimetype', ''); $request = $this->container->get('request'); if ($this->isEmptyResponse($response) && !$this->isHeadRequest($request)) { return $response->withoutHeader('Content-Type')->withoutHeader('Content-Length'); } // Add Content-Length header if `addContentLengthHeader` setting is set if (isset($this->container->get('settings')['addContentLengthHeader']) && $this->container->get('settings')['addContentLengthHeader'] == true) { if (ob_get_length() > 0) { throw new RuntimeException("Unexpected data in output buffer. " . "Maybe you have characters before an opening <?php tag?"); } $size = $response->getBody()->getSize(); if ($size !== null && !$response->hasHeader('Content-Length')) { $response = $response->withHeader('Content-Length', (string) $size); } } // clear the body if this is a HEAD request if ($this->isHeadRequest($request)) { return $response->withBody(new Body(fopen('php://temp', 'r+'))); } return $response; } /** * Helper method, which returns true if the provided response must not output a body and false * if the response could have a body. * * @see https://tools.ietf.org/html/rfc7231 * * @param ResponseInterface $response * * @return bool */ protected function isEmptyResponse(ResponseInterface $response) { if (method_exists($response, 'isEmpty')) { return $response->isEmpty(); } return in_array($response->getStatusCode(), [204, 205, 304]); } /** * Helper method to check if the current request is a HEAD request * * @param RequestInterface $request * * @return bool */ protected function isHeadRequest(RequestInterface $request) { return strtoupper($request->getMethod()) === 'HEAD'; } /** * Call relevant handler from the Container if needed. If it doesn't exist, * then just re-throw. * * @param Exception $e * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Exception If a handler is needed and not found */ protected function handleException(Exception $e, ServerRequestInterface $request, ResponseInterface $response) { if ($e instanceof MethodNotAllowedException) { $handler = 'notAllowedHandler'; $params = [$e->getRequest(), $e->getResponse(), $e->getAllowedMethods()]; } elseif ($e instanceof NotFoundException) { $handler = 'notFoundHandler'; $params = [$e->getRequest(), $e->getResponse(), $e]; } elseif ($e instanceof SlimException) { // This is a Stop exception and contains the response return $e->getResponse(); } else { // Other exception, use $request and $response params $handler = 'errorHandler'; $params = [$request, $response, $e]; } if ($this->container->has($handler)) { $callable = $this->container->get($handler); // Call the registered handler return call_user_func_array($callable, $params); } // No handlers found, so just throw the exception throw $e; } /** * Call relevant handler from the Container if needed. If it doesn't exist, * then just re-throw. * * @param Throwable $e * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Throwable */ protected function handlePhpError(Throwable $e, ServerRequestInterface $request, ResponseInterface $response) { $handler = 'phpErrorHandler'; $params = [$request, $response, $e]; if ($this->container->has($handler)) { $callable = $this->container->get($handler); // Call the registered handler return call_user_func_array($callable, $params); } // No handlers found, so just throw the exception throw $e; } } slim/Slim/Collection.php 0000666 00000006247 15165376252 0011251 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use ArrayIterator; use Slim\Interfaces\CollectionInterface; /** * Collection * * This class provides a common interface used by many other * classes in a Slim application that manage "collections" * of data that must be inspected and/or manipulated */ class Collection implements CollectionInterface { /** * The source data * * @var array */ protected $data = []; /** * @param array $items Pre-populate collection with this key-value array */ public function __construct(array $items = []) { $this->replace($items); } /** * {@inheritdoc} */ public function set($key, $value) { $this->data[$key] = $value; } /** * {@inheritdoc} */ public function get($key, $default = null) { return $this->has($key) ? $this->data[$key] : $default; } /** * {@inheritdoc} */ public function replace(array $items) { foreach ($items as $key => $value) { $this->set($key, $value); } } /** * {@inheritdoc} */ public function all() { return $this->data; } /** * Get collection keys * * @return array The collection's source data keys */ public function keys() { return array_keys($this->data); } /** * {@inheritdoc} */ public function has($key) { return array_key_exists($key, $this->data); } /** * {@inheritdoc} */ public function remove($key) { unset($this->data[$key]); } /** * {@inheritdoc} */ public function clear() { $this->data = []; } /** * Does this collection have a given key? * * @param string $key The data key * * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($key) { return $this->has($key); } /** * Get collection item for key * * @param string $key The data key * * @return mixed The key's value, or the default value */ #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->get($key); } /** * Set collection item * * @param string $key The data key * @param mixed $value The data value */ #[\ReturnTypeWillChange] public function offsetSet($key, $value) { $this->set($key, $value); } /** * Remove item from collection * * @param string $key The data key */ #[\ReturnTypeWillChange] public function offsetUnset($key) { $this->remove($key); } /** * Get number of items in collection * * @return int */ #[\ReturnTypeWillChange] public function count() { return count($this->data); } /** * Get collection iterator * * @return ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->data); } } slim/Slim/Exception/NotFoundException.php 0000666 00000000333 15165376252 0014515 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; class NotFoundException extends SlimException { } slim/Slim/Exception/SlimException.php 0000666 00000002031 15165376252 0013662 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; class SlimException extends Exception { /** * @var ServerRequestInterface */ protected $request; /** * @var ResponseInterface */ protected $response; /** * @param ServerRequestInterface $request * @param ResponseInterface $response */ public function __construct(ServerRequestInterface $request, ResponseInterface $response) { parent::__construct(); $this->request = $request; $this->response = $response; } /** * @return ServerRequestInterface */ public function getRequest() { return $this->request; } /** * @return ResponseInterface */ public function getResponse() { return $this->response; } } slim/Slim/Exception/ContainerValueNotFoundException.php 0000666 00000000535 15165376252 0017361 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Container\NotFoundExceptionInterface; use RuntimeException; class ContainerValueNotFoundException extends RuntimeException implements NotFoundExceptionInterface { } slim/Slim/Exception/MethodNotAllowedException.php 0000666 00000001623 15165376252 0016175 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; class MethodNotAllowedException extends SlimException { /** * @var string[] */ protected $allowedMethods; /** * @param ServerRequestInterface $request * @param ResponseInterface $response * @param string[] $allowedMethods */ public function __construct(ServerRequestInterface $request, ResponseInterface $response, array $allowedMethods) { parent::__construct($request, $response); $this->allowedMethods = $allowedMethods; } /** * @return string[] */ public function getAllowedMethods() { return $this->allowedMethods; } } slim/Slim/Exception/InvalidMethodException.php 0000666 00000001510 15165376252 0015506 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; class InvalidMethodException extends InvalidArgumentException { /** * @var ServerRequestInterface */ protected $request; /** * @param ServerRequestInterface $request * @param string $method */ public function __construct(ServerRequestInterface $request, $method) { $this->request = $request; parent::__construct(sprintf('Unsupported HTTP method "%s" provided', $method)); } /** * @return ServerRequestInterface */ public function getRequest() { return $this->request; } } slim/Slim/Exception/ContainerException.php 0000666 00000000542 15165376252 0014705 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Container\ContainerExceptionInterface; use InvalidArgumentException; class ContainerException extends InvalidArgumentException implements ContainerExceptionInterface { } slim/Slim/Routable.php 0000666 00000003701 15165376252 0010723 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Container\ContainerInterface; abstract class Routable { use CallableResolverAwareTrait; /** * Route callable * * @var callable */ protected $callable; /** * Container * * @var ContainerInterface */ protected $container; /** * Route middleware * * @var callable[] */ protected $middleware = []; /** * Route pattern * * @var string */ protected $pattern; /** * @param string $pattern * @param callable $callable */ public function __construct($pattern, $callable) { $this->pattern = $pattern; $this->callable = $callable; } /** * Get the middleware registered for the group * * @return callable[] */ public function getMiddleware() { return $this->middleware; } /** * Get the route pattern * * @return string */ public function getPattern() { return $this->pattern; } /** * Set container for use with resolveCallable * * @param ContainerInterface $container * * @return static */ public function setContainer(ContainerInterface $container) { $this->container = $container; return $this; } /** * Prepend middleware to the middleware collection * * @param callable|string $callable The callback routine * * @return static */ public function add($callable) { $this->middleware[] = new DeferredCallable($callable, $this->container); return $this; } /** * Set the route pattern * * @param string $newPattern */ public function setPattern($newPattern) { $this->pattern = $newPattern; } } slim/Slim/Container.php 0000666 00000013426 15165376252 0011075 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use ArrayAccess; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Container\ContainerExceptionInterface; use InvalidArgumentException; use Pimple\Container as PimpleContainer; use Slim\Exception\ContainerException as SlimContainerException; use Slim\Exception\ContainerValueNotFoundException; /** * Slim's default DI container is Pimple. * * Slim\App expects a container that implements AmeliaPsr\Container\ContainerInterface * with these service keys configured and ready for use: * * `settings` an array or instance of \ArrayAccess * `environment` an instance of \Slim\Http\Environment * `request` an instance of \AmeliaPsr\Http\Message\ServerRequestInterface * `response` an instance of \AmeliaPsr\Http\Message\ResponseInterface * `router` an instance of \Slim\Interfaces\RouterInterface * `foundHandler` an instance of \Slim\Interfaces\InvocationStrategyInterface * `errorHandler` a callable with the signature: function($request, $response, $exception) * `notFoundHandler` a callable with the signature: function($request, $response) * `notAllowedHandler` a callable with the signature: function($request, $response, $allowedHttpMethods) * `callableResolver` an instance of \Slim\Interfaces\CallableResolverInterface */ class Container extends PimpleContainer implements ContainerInterface { /** * Default settings * * @var array */ private $defaultSettings = [ 'httpVersion' => '1.1', 'responseChunkSize' => 4096, 'outputBuffering' => 'append', 'determineRouteBeforeAppMiddleware' => false, 'displayErrorDetails' => false, 'addContentLengthHeader' => true, 'routerCacheFile' => false, ]; /** * @param array $values The parameters or objects. */ public function __construct(array $values = []) { parent::__construct($values); $userSettings = isset($values['settings']) ? $values['settings'] : []; $this->registerDefaultServices($userSettings); } /** * This function registers the default services that Slim needs to work. * * All services are shared, they are registered such that the * same instance is returned on subsequent calls. * * @param array $userSettings Associative array of application settings * * @return void */ private function registerDefaultServices($userSettings) { $defaultSettings = $this->defaultSettings; /** * This service MUST return an array or an instance of ArrayAccess. * * @return array|ArrayAccess */ $this['settings'] = function () use ($userSettings, $defaultSettings) { return new Collection(array_merge($defaultSettings, $userSettings)); }; $defaultProvider = new DefaultServicesProvider(); $defaultProvider->register($this); } /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @return mixed * * @throws InvalidArgumentException Thrown when an offset cannot be found in the Pimple container * @throws SlimContainerException Thrown when an exception is * not an instance of ContainerExceptionInterface * @throws ContainerValueNotFoundException No entry was found for this identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. */ public function get($id) { if (!$this->offsetExists($id)) { throw new ContainerValueNotFoundException(sprintf('Identifier "%s" is not defined.', $id)); } try { return $this->offsetGet($id); } catch (InvalidArgumentException $exception) { if ($this->exceptionThrownByContainer($exception)) { throw new SlimContainerException( sprintf('Container error while retrieving "%s"', $id), null, $exception ); } else { throw $exception; } } } /** * Tests whether an exception needs to be recast for compliance with psr/container. This will be if the * exception was thrown by Pimple. * * @param InvalidArgumentException $exception * * @return bool */ private function exceptionThrownByContainer(InvalidArgumentException $exception) { $trace = $exception->getTrace()[0]; return $trace['class'] === PimpleContainer::class && $trace['function'] === 'offsetGet'; } /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * @param string $id Identifier of the entry to look for. * * @return boolean */ public function has($id) { return $this->offsetExists($id); } /** * @param string $name * * @return mixed * * @throws InvalidArgumentException Thrown when an offset cannot be found in the Pimple container * @throws SlimContainerException Thrown when an exception is not * an instance of ContainerExceptionInterface * @throws ContainerValueNotFoundException No entry was found for this identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. */ public function __get($name) { return $this->get($name); } /** * @param string $name * @return bool */ public function __isset($name) { return $this->has($name); } } slim/Slim/MiddlewareAwareTrait.php 0000666 00000006544 15165376252 0013217 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; use UnexpectedValueException; /** * Middleware * * This is an internal class that enables concentric middleware layers. This * class is an implementation detail and is used only inside of the Slim * application; it is not visible to—and should not be used by—end users. */ trait MiddlewareAwareTrait { /** * Tip of the middleware call stack * * @var callable */ protected $tip; /** * Middleware stack lock * * @var bool */ protected $middlewareLock = false; /** * Add middleware * * This method prepends new middleware to the application middleware stack. * * @param callable $callable Any callable that accepts three arguments: * 1. A Request object * 2. A Response object * 3. A "next" middleware callable * * @return static * * @throws RuntimeException If middleware is added while the stack is dequeuing * @throws UnexpectedValueException If the middleware doesn't return a AmeliaPsr\Http\Message\ResponseInterface */ protected function addMiddleware(callable $callable) { if ($this->middlewareLock) { throw new RuntimeException('Middleware can’t be added once the stack is dequeuing'); } if (is_null($this->tip)) { $this->seedMiddlewareStack(); } $next = $this->tip; $this->tip = function ( ServerRequestInterface $request, ResponseInterface $response ) use ( $callable, $next ) { $result = call_user_func($callable, $request, $response, $next); if ($result instanceof ResponseInterface === false) { throw new UnexpectedValueException( 'Middleware must return instance of \AmeliaPsr\Http\Message\ResponseInterface' ); } return $result; }; return $this; } /** * Seed middleware stack with first callable * * @param callable $kernel The last item to run as middleware * * @throws RuntimeException if the stack is seeded more than once */ protected function seedMiddlewareStack(callable $kernel = null) { if (!is_null($this->tip)) { throw new RuntimeException('MiddlewareStack can only be seeded once.'); } if ($kernel === null) { $kernel = $this; } $this->tip = $kernel; } /** * Call middleware stack * * @param ServerRequestInterface $request A request object * @param ResponseInterface $response A response object * * @return ResponseInterface */ public function callMiddlewareStack(ServerRequestInterface $request, ResponseInterface $response) { if (is_null($this->tip)) { $this->seedMiddlewareStack(); } /** @var callable $start */ $start = $this->tip; $this->middlewareLock = true; $response = $start($request, $response); $this->middlewareLock = false; return $response; } } slim/Slim/Route.php 0000666 00000015141 15165376252 0010245 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Handlers\Strategies\RequestResponse; use Slim\Interfaces\InvocationStrategyInterface; use Slim\Interfaces\RouteInterface; class Route extends Routable implements RouteInterface { use MiddlewareAwareTrait; /** * HTTP methods supported by this route * * @var string[] */ protected $methods = []; /** * Route identifier * * @var string */ protected $identifier; /** * Route name * * @var null|string */ protected $name; /** * Parent route groups * * @var RouteGroup[] */ protected $groups; private $finalized = false; /** * Output buffering mode * * One of: false, 'prepend' or 'append' * * @var boolean|string */ protected $outputBuffering = 'append'; /** * Route parameters * * @var array */ protected $arguments = []; /** * Route arguments parameters * * @var null|array */ protected $savedArguments = []; /** * @param string|string[] $methods The route HTTP methods * @param string $pattern The route pattern * @param callable $callable The route callable * @param RouteGroup[] $groups The parent route groups * @param int $identifier The route identifier */ public function __construct($methods, $pattern, $callable, $groups = [], $identifier = 0) { parent::__construct($pattern, $callable); $this->methods = is_string($methods) ? [$methods] : $methods; $this->groups = $groups; $this->identifier = 'route' . $identifier; } public function finalize() { if ($this->finalized) { return; } $groupMiddleware = []; foreach ($this->getGroups() as $group) { $groupMiddleware = array_merge($group->getMiddleware(), $groupMiddleware); } $this->middleware = array_merge($this->middleware, $groupMiddleware); foreach ($this->getMiddleware() as $middleware) { $this->addMiddleware($middleware); } $this->finalized = true; } /** * Get route callable * * @return callable */ public function getCallable() { return $this->callable; } /** * This method enables you to override the Route's callable * * @param string|Closure $callable */ public function setCallable($callable) { $this->callable = $callable; } /** * Get route methods * * @return string[] */ public function getMethods() { return $this->methods; } /** * Get parent route groups * * @return RouteGroup[] */ public function getGroups() { return $this->groups; } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * Get route identifier * * @return string */ public function getIdentifier() { return $this->identifier; } /** * Get output buffering mode * * @return boolean|string */ public function getOutputBuffering() { return $this->outputBuffering; } /** * {@inheritdoc} */ public function setOutputBuffering($mode) { if (!in_array($mode, [false, 'prepend', 'append'], true)) { throw new InvalidArgumentException('Unknown output buffering mode'); } $this->outputBuffering = $mode; return $this; } /** * {@inheritdoc} */ public function setName($name) { if (!is_string($name)) { throw new InvalidArgumentException('Route name must be a string'); } $this->name = $name; return $this; } /** * {@inheritdoc} */ public function setArgument($name, $value, $includeInSavedArguments = true) { if ($includeInSavedArguments) { $this->savedArguments[$name] = $value; } $this->arguments[$name] = $value; return $this; } /** * {@inheritdoc} */ public function setArguments(array $arguments, $includeInSavedArguments = true) { if ($includeInSavedArguments) { $this->savedArguments = $arguments; } $this->arguments = $arguments; return $this; } /** * {@inheritdoc} */ public function getArguments() { return $this->arguments; } /** * {@inheritdoc} */ public function getArgument($name, $default = null) { if (array_key_exists($name, $this->arguments)) { return $this->arguments[$name]; } return $default; } /** * {@inheritdoc} */ public function prepare(ServerRequestInterface $request, array $arguments) { // Remove temp arguments $this->setArguments($this->savedArguments); // Add the route arguments foreach ($arguments as $k => $v) { $this->setArgument($k, $v, false); } } /** * {@inheritdoc} */ public function run(ServerRequestInterface $request, ResponseInterface $response) { // Finalise route now that we are about to run it $this->finalize(); // Traverse middleware stack and fetch updated response return $this->callMiddlewareStack($request, $response); } /** * {@inheritdoc} */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { $this->callable = $this->resolveCallable($this->callable); /** @var InvocationStrategyInterface $handler */ $handler = isset($this->container) ? $this->container->get('foundHandler') : new RequestResponse(); $newResponse = $handler($this->callable, $request, $response, $this->arguments); if ($newResponse instanceof ResponseInterface) { // if route callback returns a ResponseInterface, then use it $response = $newResponse; } elseif (is_string($newResponse)) { // if route callback returns a string, then append it to the response if ($response->getBody()->isWritable()) { $response->getBody()->write($newResponse); } } return $response; } } slim/Slim/Interfaces/Http/EnvironmentInterface.php 0000666 00000000647 15165376252 0016303 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; interface EnvironmentInterface { /** * Create mock environment * * @param array $settings Array of custom environment keys and values * * @return static */ public static function mock(array $settings = []); } slim/Slim/Interfaces/Http/HeadersInterface.php 0000666 00000002044 15165376252 0015343 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; use Slim\Interfaces\CollectionInterface; interface HeadersInterface extends CollectionInterface { /** * Add HTTP header value * * This method appends a header value. Unlike the set() method, * this method _appends_ this new value to any values * that already exist for this header name. * * @param string $key The case-insensitive header name * @param string|string[] $value The new header value(s) */ public function add($key, $value); /** * Normalize header name * * This method transforms header names into a * normalized form. This is how we enable case-insensitive * header names in the other methods in this class. * * @param string $key The case-insensitive header name * * @return string Normalized header name */ public function normalizeKey($key); } slim/Slim/Interfaces/Http/CookiesInterface.php 0000666 00000002275 15165376252 0015372 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; use InvalidArgumentException; interface CookiesInterface { /** * Get request cookie * * @param string $name Cookie name * @param mixed $default Cookie default value * * @return mixed Cookie value if present, else default */ public function get($name, $default = null); /** * Set response cookie * * @param string $name Cookie name * @param string|array $value Cookie value, or cookie properties */ public function set($name, $value); /** * Convert to array of `Set-Cookie` headers * * @return string[] */ public function toHeaders(); /** * Parse HTTP request `Cookie:` header and extract into a PHP associative array. * * @param string $header The raw HTTP request `Cookie:` header * * @return array Associative array of cookie names and values * * @throws InvalidArgumentException if the cookie data cannot be parsed */ public static function parseHeader($header); } slim/Slim/Interfaces/CollectionInterface.php 0000666 00000002773 15165376252 0015155 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use ArrayAccess; use Countable; use IteratorAggregate; interface CollectionInterface extends ArrayAccess, Countable, IteratorAggregate { /** * Set collection item * * @param string $key The data key * @param mixed $value The data value */ public function set($key, $value); /** * Get collection item for key * * @param string $key The data key * @param mixed $default The default value to return if data key does not exist * * @return mixed The key's value, or the default value */ public function get($key, $default = null); /** * Add item to collection, replacing existing items with the same data key * * @param array $items Key-value array of data to append to this collection */ public function replace(array $items); /** * Get all items in collection * * @return array The collection's source data */ public function all(); /** * Does this collection have a given key? * * @param string $key The data key * * @return bool */ public function has($key); /** * Remove item from collection * * @param string $key The data key */ public function remove($key); /** * Remove all items from collection */ public function clear(); } slim/Slim/Interfaces/CallableResolverInterface.php 0000666 00000000675 15165376252 0016302 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use RuntimeException; interface CallableResolverInterface { /** * Invoke the resolved callable. * * @param callable|string $toResolve * * @return callable * * @throws RuntimeException */ public function resolve($toResolve); } slim/Slim/Interfaces/RouteInterface.php 0000666 00000006302 15165376252 0014150 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; interface RouteInterface { /** * Retrieve a specific route argument * * @param string $name * @param string|null $default * * @return string|null */ public function getArgument($name, $default = null); /** * Get route arguments * * @return string[] */ public function getArguments(); /** * Get route name * * @return null|string */ public function getName(); /** * Get route pattern * * @return string */ public function getPattern(); /** * Set a route argument * * @param string $name * @param string $value * * @return RouteInterface */ public function setArgument($name, $value); /** * Replace route arguments * * @param string[] $arguments * * @return RouteInterface */ public function setArguments(array $arguments); /** * Set output buffering mode * * One of: false, 'prepend' or 'append' * * @param boolean|string $mode * * @throws InvalidArgumentException If an unknown buffering mode is specified */ public function setOutputBuffering($mode); /** * Set route name * * @param string $name * * @return RouteInterface * * @throws InvalidArgumentException if the route name is not a string */ public function setName($name); /** * Add middleware * * This method prepends new middleware to the route's middleware stack. * * @param callable|string $callable The callback routine * * @return RouteInterface */ public function add($callable); /** * Prepare the route for use * * @param ServerRequestInterface $request * @param array $arguments */ public function prepare(ServerRequestInterface $request, array $arguments); /** * Run route * * This method traverses the middleware stack, including the route's callable * and captures the resultant HTTP response object. It then sends the response * back to the Application. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface */ public function run(ServerRequestInterface $request, ResponseInterface $response); /** * Dispatch route callable against current Request and Response objects * * This method invokes the route object's callable. If middleware is * registered for the route, each callable middleware is invoked in * the order specified. * * @param ServerRequestInterface $request The current Request object * @param ResponseInterface $response The current Response object * * @return ResponseInterface */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response); } slim/Slim/Interfaces/RouterInterface.php 0000666 00000005464 15165376252 0014342 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; interface RouterInterface { /** * Route result constants */ const DISPATCH_STATUS = 0; const ALLOWED_METHODS = 1; /** * Add route * * @param string[] $methods Array of HTTP methods * @param string $pattern The route pattern * @param callable $handler The route callable * * @return RouteInterface */ public function map($methods, $pattern, $handler); /** * Dispatch router for HTTP request * * @param ServerRequestInterface $request The current HTTP request object * * @return array * * @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php */ public function dispatch(ServerRequestInterface $request); /** * Add a route group to the array * * @param string $pattern The group pattern * @param callable $callable A group callable * * @return RouteGroupInterface */ public function pushGroup($pattern, $callable); /** * Removes the last route group from the array * * @return bool True if successful, else False */ public function popGroup(); /** * Get named route object * * @param string $name Route name * * @return RouteInterface * * @throws RuntimeException If named route does not exist */ public function getNamedRoute($name); /** * @param string $identifier * * @return RouteInterface */ public function lookupRoute($identifier); /** * Build the path for a named route excluding the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function relativePathFor($name, array $data = [], array $queryParams = []); /** * Build the path for a named route including the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function pathFor($name, array $data = [], array $queryParams = []); } slim/Slim/Interfaces/RouteGroupInterface.php 0000666 00000001451 15165376252 0015165 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use Slim\App; interface RouteGroupInterface { /** * Get route pattern * * @return string */ public function getPattern(); /** * Prepend middleware to the group middleware collection * * @param callable|string $callable The callback routine * * @return RouteGroupInterface */ public function add($callable); /** * Execute route group callable in the context of the Slim App * * This method invokes the route group object's callable, collecting * nested route objects * * @param App $app */ public function __invoke(App $app); } slim/Slim/Interfaces/InvocationStrategyInterface.php 0000666 00000001531 15165376252 0016705 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; interface InvocationStrategyInterface { /** * @param callable $callable The callable to invoke using the strategy. * @param ServerRequestInterface $request The request object. * @param ResponseInterface $response The response object. * @param array $routeArguments The route's placeholder arguments * * @return ResponseInterface|string */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ); } slim/Slim/CallableResolver.php 0000666 00000006123 15165376252 0012370 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Container\ContainerInterface; use RuntimeException; use Slim\Interfaces\CallableResolverInterface; /** * This class resolves a string of the format 'class:method' into a closure * that can be dispatched. */ final class CallableResolver implements CallableResolverInterface { const CALLABLE_PATTERN = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!'; /** * @var ContainerInterface */ private $container; /** * @param ContainerInterface $container */ public function __construct(ContainerInterface $container) { $this->container = $container; } /** * Resolve toResolve into a closure so that the router can dispatch. * * If toResolve is of the format 'class:method', then try to extract 'class' * from the container otherwise instantiate it and then dispatch 'method'. * * @param callable|string $toResolve * * @return callable * * @throws RuntimeException If the callable does not exist * @throws RuntimeException If the callable is not resolvable */ public function resolve($toResolve) { if (is_callable($toResolve)) { return $toResolve; } $resolved = $toResolve; if (is_string($toResolve)) { list($class, $method) = $this->parseCallable($toResolve); $resolved = $this->resolveCallable($class, $method); } $this->assertCallable($resolved); return $resolved; } /** * Extract class and method from toResolve * * @param string $toResolve * * @return array */ protected function parseCallable($toResolve) { if (preg_match(self::CALLABLE_PATTERN, $toResolve, $matches)) { return [$matches[1], $matches[2]]; } return [$toResolve, '__invoke']; } /** * Check if string is something in the DIC * that's callable or is a class name which has an __invoke() method. * * @param string $class * @param string $method * * @return callable * * @throws RuntimeException if the callable does not exist */ protected function resolveCallable($class, $method) { if ($this->container->has($class)) { return [$this->container->get($class), $method]; } if (!class_exists($class)) { throw new RuntimeException(sprintf('Callable %s does not exist', $class)); } return [new $class($this->container), $method]; } /** * @param Callable $callable * * @throws RuntimeException if the callable is not resolvable */ protected function assertCallable($callable) { if (!is_callable($callable)) { throw new RuntimeException(sprintf( '%s is not resolvable', is_array($callable) || is_object($callable) ? json_encode($callable) : $callable )); } } } slim/Slim/Http/Stream.php 0000666 00000026644 15165376252 0011333 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\StreamInterface; use RuntimeException; /** * Represents a data stream as defined in PSR-7. * * @link https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php */ class Stream implements StreamInterface { /** * Bit mask to determine if the stream is a pipe * * This is octal as per header stat.h */ const FSTAT_MODE_S_IFIFO = 0010000; /** * Resource modes * * @var array * @link http://php.net/manual/function.fopen.php */ protected static $modes = [ 'readable' => ['r', 'r+', 'w+', 'a+', 'x+', 'c+'], 'writable' => ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'], ]; /** * The underlying stream resource * * @var resource */ protected $stream; /** * Stream metadata * * @var array */ protected $meta; /** * Is this stream readable? * * @var bool */ protected $readable; /** * Is this stream writable? * * @var bool */ protected $writable; /** * Is this stream seekable? * * @var bool */ protected $seekable; /** * The size of the stream if known * * @var null|int */ protected $size; /** * Is this stream a pipe? * * @var bool */ protected $isPipe; /** * @param resource $stream A PHP resource handle. * * @throws InvalidArgumentException If argument is not a resource. */ public function __construct($stream) { $this->attach($stream); } /** * Get stream metadata as an associative array or retrieve a specific key. * * The keys returned are identical to the keys returned from PHP's stream_get_meta_data() function. * * @link http://php.net/manual/en/function.stream-get-meta-data.php * * @param string $key Specific metadata to retrieve. * * @return array|mixed|null Returns an associative array if no key is * provided. Returns a specific key value if a key is provided and the * value is found, or null if the key is not found. */ public function getMetadata($key = null) { $this->meta = stream_get_meta_data($this->stream); if (is_null($key) === true) { return $this->meta; } return isset($this->meta[$key]) ? $this->meta[$key] : null; } /** * Is a resource attached to this stream? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ protected function isAttached() { return is_resource($this->stream); } /** * Attach new resource to this object. * * Note: This method is not part of the PSR-7 standard. * * @param resource $newStream A PHP resource handle. * * @throws InvalidArgumentException If argument is not a valid PHP resource. */ protected function attach($newStream) { if (is_resource($newStream) === false) { throw new InvalidArgumentException(__METHOD__ . ' argument must be a valid PHP resource'); } if ($this->isAttached() === true) { $this->detach(); } $this->stream = $newStream; } /** * Separates any underlying resources from the stream. * * After the stream has been detached, the stream is in an unusable state. * * @return resource|null Underlying PHP stream, if any */ public function detach() { $oldResource = $this->stream; $this->stream = null; $this->meta = null; $this->readable = null; $this->writable = null; $this->seekable = null; $this->size = null; $this->isPipe = null; return $oldResource; } /** * Reads all data from the stream into a string, from the beginning to end. * * This method MUST attempt to seek to the beginning of the stream before * reading data and read the stream until the end is reached. * * Warning: This could attempt to load a large amount of data into memory. * * This method MUST NOT raise an exception in order to conform with PHP's * string casting operations. * * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring * * @return string */ public function __toString() { if (!$this->isAttached()) { return ''; } try { $this->rewind(); return $this->getContents(); } catch (RuntimeException $e) { return ''; } } /** * Closes the stream and any underlying resources. */ public function close() { if ($this->isAttached() === true) { if ($this->isPipe()) { pclose($this->stream); } else { fclose($this->stream); } } $this->detach(); } /** * Get the size of the stream if known. * * @return int|null Returns the size in bytes if known, or null if unknown. */ public function getSize() { if (!$this->size && $this->isAttached() === true) { $stats = fstat($this->stream); $this->size = isset($stats['size']) && !$this->isPipe() ? $stats['size'] : null; } return $this->size; } /** * Returns the current position of the file read/write pointer * * @return int Position of the file pointer * * @throws RuntimeException on error. */ public function tell() { if (!$this->isAttached() || ($position = ftell($this->stream)) === false || $this->isPipe()) { throw new RuntimeException('Could not get the position of the pointer in stream'); } return $position; } /** * Returns true if the stream is at the end of the stream. * * @return bool */ public function eof() { return $this->isAttached() ? feof($this->stream) : true; } /** * Returns whether or not the stream is readable. * * @return bool */ public function isReadable() { if ($this->readable === null) { if ($this->isPipe()) { $this->readable = true; } else { $this->readable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['readable'] as $mode) { if (strpos($meta['mode'], $mode) === 0) { $this->readable = true; break; } } } } } return $this->readable; } /** * Returns whether or not the stream is writable. * * @return bool */ public function isWritable() { if ($this->writable === null) { $this->writable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['writable'] as $mode) { if (strpos($meta['mode'], $mode) === 0) { $this->writable = true; break; } } } } return $this->writable; } /** * Returns whether or not the stream is seekable. * * @return bool */ public function isSeekable() { if ($this->seekable === null) { $this->seekable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); $this->seekable = !$this->isPipe() && $meta['seekable']; } } return $this->seekable; } /** * Seek to a position in the stream. * * @link http://www.php.net/manual/en/function.fseek.php * * @param int $offset Stream offset * @param int $whence Specifies how the cursor position will be calculated * based on the seek offset. Valid values are identical to the built-in * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * offset bytes SEEK_CUR: Set position to current location plus offset * SEEK_END: Set position to end-of-stream plus offset. * * @throws RuntimeException If stream is not seekable */ public function seek($offset, $whence = SEEK_SET) { // Note that fseek returns 0 on success! if (!$this->isSeekable() || fseek($this->stream, $offset, $whence) === -1) { throw new RuntimeException('Could not seek in stream'); } } /** * Seek to the beginning of the stream. * * If the stream is not seekable, this method will raise an exception; * otherwise, it will perform a seek(0). * * @see seek() * * @link http://www.php.net/manual/en/function.fseek.php * * @throws RuntimeException on failure. */ public function rewind() { if (!$this->isSeekable() || rewind($this->stream) === false) { throw new RuntimeException('Could not rewind stream'); } } /** * Read data from the stream. * * @param int $length Read up to $length bytes from the object and return * them. Fewer than $length bytes may be returned if underlying stream * call returns fewer bytes. * * @return string Returns the data read from the stream, or an empty string if no bytes are available. * * @throws RuntimeException if an error occurs. */ public function read($length) { if (!$this->isReadable() || ($data = fread($this->stream, $length)) === false) { throw new RuntimeException('Could not read from stream'); } return $data; } /** * Write data to the stream. * * @param string $string The string that is to be written. * * @return int Returns the number of bytes written to the stream. * * @throws RuntimeException If stream is not writable */ public function write($string) { if (!$this->isWritable() || ($written = fwrite($this->stream, $string)) === false) { throw new RuntimeException('Could not write to stream'); } // reset size so that it will be recalculated on next call to getSize() $this->size = null; return $written; } /** * Returns the remaining contents in a string * * @return string * * @throws RuntimeException If stream is not readable */ public function getContents() { if (!$this->isReadable() || ($contents = stream_get_contents($this->stream)) === false) { throw new RuntimeException('Could not get contents of stream'); } return $contents; } /** * Returns whether or not the stream is a pipe. * * @return bool */ public function isPipe() { if ($this->isPipe === null) { $this->isPipe = false; if ($this->isAttached()) { $mode = fstat($this->stream)['mode']; $this->isPipe = ($mode & self::FSTAT_MODE_S_IFIFO) !== 0; } } return $this->isPipe; } } slim/Slim/Http/Message.php 0000666 00000020647 15165376252 0011461 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\MessageInterface; use AmeliaPsr\Http\Message\StreamInterface; use Slim\Interfaces\Http\HeadersInterface; /** * Abstract message (base class for Request and Response) * * This class represents a general HTTP message. It provides common properties and methods for * the HTTP request and response, as defined in the PSR-7 MessageInterface. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @see \Slim\Http\Request * @see \Slim\Http\Response */ abstract class Message implements MessageInterface { /** * @var string */ protected $protocolVersion = '1.1'; /** * @var array */ protected static $validProtocolVersions = [ '1.0' => true, '1.1' => true, '2.0' => true, '2' => true, ]; /** * @var HeadersInterface */ protected $headers; /** * @var StreamInterface */ protected $body; /** * Disable magic setter to ensure immutability */ public function __set($name, $value) { // Do nothing } /** * Retrieves the HTTP protocol version as a string. * * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). * * @return string */ public function getProtocolVersion() { return $this->protocolVersion; } /** * Return an instance with the specified HTTP protocol version. * * The version string MUST contain only the HTTP version number (e.g., * "1.1", "1.0"). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new protocol version. * * @param string $version HTTP protocol version * * @return static * * @throws InvalidArgumentException if the http version is an invalid number */ public function withProtocolVersion($version) { if (!isset(self::$validProtocolVersions[$version])) { throw new InvalidArgumentException( 'Invalid HTTP version. Must be one of: ' . implode(', ', array_keys(self::$validProtocolVersions)) ); } $clone = clone $this; $clone->protocolVersion = $version; return $clone; } /** * Retrieves all message header values. * * Returns an associative array of the message's headers. * Each key MUST be a header name, and each value MUST be an array of strings for that header. * * The keys represent the header name as it will be sent over the wire, and * each value is an array of strings associated with the header. * * // Represent the headers as a string * foreach ($message->getHeaders() as $name => $values) { * echo $name . ": " . implode(", ", $values); * } * * // Emit headers iteratively: * foreach ($message->getHeaders() as $name => $values) { * foreach ($values as $value) { * header(sprintf('%s: %s', $name, $value), false); * } * } * * While header names are not case-sensitive, getHeaders() will preserve the * exact case in which headers were originally specified. * * @return array */ public function getHeaders() { return $this->headers->all(); } /** * Checks if a header exists by the given case-insensitive name. * * Returns true if any header names match the given header name using a case-insensitive string comparison. * Returns false if no matching header name is found in the message. * * @param string $name Case-insensitive header field name. * * @return bool */ public function hasHeader($name) { return $this->headers->has($name); } /** * Retrieves a message header value by the given case-insensitive name. * * This method returns an array of all the header values of the given * case-insensitive header name. * * If the header does not appear in the message, this method MUST return an * empty array. * * @param string $name Case-insensitive header field name. * * @return string[] */ public function getHeader($name) { return $this->headers->get($name, []); } /** * Retrieves a comma-separated string of the values for a single header. * * This method returns a string of all of the header values of the given * case-insensitive header name as a string concatenated together using * a comma. * * NOTE: Not all header values may be appropriately represented using * comma concatenation. For such headers, use getHeader() instead * and supply your own delimiter when concatenating. * * If the header does not appear in the message, this method MUST return * an empty string. * * @param string $name Case-insensitive header field name. * * @return string */ public function getHeaderLine($name) { return implode(',', $this->headers->get($name, [])); } /** * Return an instance with the provided value replacing the specified header. * * While header names are case-insensitive, the casing of the header will * be preserved by this function, and returned from getHeaders(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * * @return static */ public function withHeader($name, $value) { $clone = clone $this; $clone->headers->set($name, $value); return $clone; } /** * Return an instance with the specified header appended with the given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * * @return static */ public function withAddedHeader($name, $value) { $clone = clone $this; $clone->headers->add($name, $value); if ($this instanceof Response && $this->body instanceof NonBufferedBody) { header(sprintf('%s: %s', $name, $clone->getHeaderLine($name))); } return $clone; } /** * Return an instance without the specified header. * * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the named header. * * @param string $name Case-insensitive header field name to remove. * * @return static */ public function withoutHeader($name) { $clone = clone $this; $clone->headers->remove($name); if ($this instanceof Response && $this->body instanceof NonBufferedBody) { header_remove($name); } return $clone; } /** * Gets the body of the message. * * @return StreamInterface Returns the body as a stream. */ public function getBody() { return $this->body; } /** * Return an instance with the specified message body. * * The body MUST be a StreamInterface object. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * new body stream. * * @param StreamInterface $body Body. * * @return static */ public function withBody(StreamInterface $body) { $clone = clone $this; $clone->body = $body; return $clone; } } slim/Slim/Http/StatusCode.php 0000666 00000005207 15165376252 0012146 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; class StatusCode { const HTTP_CONTINUE = 100; const HTTP_SWITCHING_PROTOCOLS = 101; const HTTP_PROCESSING = 102; const HTTP_OK = 200; const HTTP_CREATED = 201; const HTTP_ACCEPTED = 202; const HTTP_NONAUTHORITATIVE_INFORMATION = 203; const HTTP_NO_CONTENT = 204; const HTTP_RESET_CONTENT = 205; const HTTP_PARTIAL_CONTENT = 206; const HTTP_MULTI_STATUS = 207; const HTTP_ALREADY_REPORTED = 208; const HTTP_IM_USED = 226; const HTTP_MULTIPLE_CHOICES = 300; const HTTP_MOVED_PERMANENTLY = 301; const HTTP_FOUND = 302; const HTTP_SEE_OTHER = 303; const HTTP_NOT_MODIFIED = 304; const HTTP_USE_PROXY = 305; const HTTP_UNUSED= 306; const HTTP_TEMPORARY_REDIRECT = 307; const HTTP_PERMANENT_REDIRECT = 308; const HTTP_BAD_REQUEST = 400; const HTTP_UNAUTHORIZED = 401; const HTTP_PAYMENT_REQUIRED = 402; const HTTP_FORBIDDEN = 403; const HTTP_NOT_FOUND = 404; const HTTP_METHOD_NOT_ALLOWED = 405; const HTTP_NOT_ACCEPTABLE = 406; const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; const HTTP_REQUEST_TIMEOUT = 408; const HTTP_CONFLICT = 409; const HTTP_GONE = 410; const HTTP_LENGTH_REQUIRED = 411; const HTTP_PRECONDITION_FAILED = 412; const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; const HTTP_REQUEST_URI_TOO_LONG = 414; const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; const HTTP_EXPECTATION_FAILED = 417; const HTTP_IM_A_TEAPOT = 418; const HTTP_MISDIRECTED_REQUEST = 421; const HTTP_UNPROCESSABLE_ENTITY = 422; const HTTP_LOCKED = 423; const HTTP_FAILED_DEPENDENCY = 424; const HTTP_UPGRADE_REQUIRED = 426; const HTTP_PRECONDITION_REQUIRED = 428; const HTTP_TOO_MANY_REQUESTS = 429; const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; const HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE = 444; const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; const HTTP_CLIENT_CLOSED_REQUEST = 499; const HTTP_INTERNAL_SERVER_ERROR = 500; const HTTP_NOT_IMPLEMENTED = 501; const HTTP_BAD_GATEWAY = 502; const HTTP_SERVICE_UNAVAILABLE = 503; const HTTP_GATEWAY_TIMEOUT = 504; const HTTP_VERSION_NOT_SUPPORTED = 505; const HTTP_VARIANT_ALSO_NEGOTIATES = 506; const HTTP_INSUFFICIENT_STORAGE = 507; const HTTP_LOOP_DETECTED = 508; const HTTP_NOT_EXTENDED = 510; const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; const HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR = 599; } slim/Slim/Http/UploadedFile.php 0000666 00000025050 15165376252 0012423 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UploadedFileInterface; use RuntimeException; /** * Represents an uploaded file according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/UploadedFileInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php */ class UploadedFile implements UploadedFileInterface { /** * The client-provided full path to the file * * @note this is public to maintain BC with 3.1.0 and earlier. * * @var string */ public $file; /** * The client-provided file name. * * @var string */ protected $name; /** * The client-provided media type of the file. * * @var string */ protected $type; /** * The size of the file in bytes. * * @var int */ protected $size; /** * A valid PHP UPLOAD_ERR_xxx code for the file upload. * * @var int */ protected $error = UPLOAD_ERR_OK; /** * Indicates if the upload is from a SAPI environment. * * @var bool */ protected $sapi = false; /** * An optional StreamInterface wrapping the file resource. * * @var StreamInterface */ protected $stream; /** * Indicates if the uploaded file has already been moved. * * @var bool */ protected $moved = false; /** * Create a normalized tree of UploadedFile instances from the Environment. * * Returns a normalized tree of UploadedFile instances or null if none are provided. * * @param Environment $env The environment * * @return array|null */ public static function createFromEnvironment(Environment $env) { if (is_array($env['slim.files']) && $env->has('slim.files')) { return $env['slim.files']; } elseif (! empty($_FILES)) { return static::parseUploadedFiles($_FILES); } return []; } /** * Parse a non-normalized, i.e. $_FILES superglobal, tree of uploaded file data. * * Returns a normalized tree of UploadedFile instances. * * @param array $uploadedFiles The non-normalized tree of uploaded file data. * * @return array */ private static function parseUploadedFiles(array $uploadedFiles) { $parsed = []; foreach ($uploadedFiles as $field => $uploadedFile) { if (!isset($uploadedFile['error'])) { if (is_array($uploadedFile)) { $parsed[$field] = static::parseUploadedFiles($uploadedFile); } continue; } $parsed[$field] = []; if (!is_array($uploadedFile['error'])) { $parsed[$field] = new static( $uploadedFile['tmp_name'], isset($uploadedFile['name']) ? $uploadedFile['name'] : null, isset($uploadedFile['type']) ? $uploadedFile['type'] : null, isset($uploadedFile['size']) ? $uploadedFile['size'] : null, $uploadedFile['error'], true ); } else { $subArray = []; foreach ($uploadedFile['error'] as $fileIdx => $error) { // normalise subarray and re-parse to move the input's keyname up a level $subArray[$fileIdx]['name'] = $uploadedFile['name'][$fileIdx]; $subArray[$fileIdx]['type'] = $uploadedFile['type'][$fileIdx]; $subArray[$fileIdx]['tmp_name'] = $uploadedFile['tmp_name'][$fileIdx]; $subArray[$fileIdx]['error'] = $uploadedFile['error'][$fileIdx]; $subArray[$fileIdx]['size'] = $uploadedFile['size'][$fileIdx]; $parsed[$field] = static::parseUploadedFiles($subArray); } } } return $parsed; } /** * @param string $file The full path to the uploaded file provided by the client. * @param string|null $name The file name. * @param string|null $type The file media type. * @param int|null $size The file size in bytes. * @param int $error The UPLOAD_ERR_XXX code representing the status of the upload. * @param bool $sapi Indicates if the upload is in a SAPI environment. */ public function __construct($file, $name = null, $type = null, $size = null, $error = UPLOAD_ERR_OK, $sapi = false) { $this->file = $file; $this->name = $name; $this->type = $type; $this->size = $size; $this->error = $error; $this->sapi = $sapi; } /** * Retrieve a stream representing the uploaded file. * * This method MUST return a StreamInterface instance, representing the * uploaded file. The purpose of this method is to allow utilizing native PHP * stream functionality to manipulate the file upload, such as * stream_copy_to_stream() (though the result will need to be decorated in a * native PHP stream wrapper to work with such functions). * * If the moveTo() method has been called previously, this method MUST raise * an exception. * * @return StreamInterface * * @throws RuntimeException in cases when no stream is available or can be created. */ public function getStream() { if ($this->moved) { throw new RuntimeException(sprintf('Uploaded file %s has already been moved', $this->name)); } if ($this->stream === null) { $this->stream = new Stream(fopen($this->file, 'r')); } return $this->stream; } /** * Move the uploaded file to a new location. * * Use this method as an alternative to move_uploaded_file(). This method is * guaranteed to work in both SAPI and non-SAPI environments. * Implementations must determine which environment they are in, and use the * appropriate method (move_uploaded_file(), rename(), or a stream * operation) to perform the operation. * * $targetPath may be an absolute path, or a relative path. If it is a * relative path, resolution should be the same as used by PHP's rename() * function. * * The original file or stream MUST be removed on completion. * * If this method is called more than once, any subsequent calls MUST raise * an exception. * * When used in an SAPI environment where $_FILES is populated, when writing * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be * used to ensure permissions and upload status are verified correctly. * * If you wish to move to a stream, use getStream(), as SAPI operations * cannot guarantee writing to stream destinations. * * @see http://php.net/is_uploaded_file * @see http://php.net/move_uploaded_file * * @param string $targetPath Path to which to move the uploaded file. * * @throws InvalidArgumentException If the $path specified is invalid. * @throws RuntimeException On any error during the move operation or on the second subsequent call to the method. */ public function moveTo($targetPath) { if ($this->moved) { throw new RuntimeException('Uploaded file already moved'); } $targetIsStream = strpos($targetPath, '://') > 0; if (!$targetIsStream && !is_writable(dirname($targetPath))) { throw new InvalidArgumentException('Upload target path is not writable'); } if ($targetIsStream) { if (!copy($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } if (!unlink($this->file)) { throw new RuntimeException(sprintf('Error removing uploaded file %s', $this->name)); } } elseif ($this->sapi) { if (!is_uploaded_file($this->file)) { throw new RuntimeException(sprintf('%s is not a valid uploaded file', $this->file)); } if (!move_uploaded_file($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } } else { if (!rename($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } } $this->moved = true; } /** * Retrieve the error associated with the uploaded file. * * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants. * * If the file was uploaded successfully, this method MUST return * UPLOAD_ERR_OK. * * Implementations SHOULD return the value stored in the "error" key of * the file in the $_FILES array. * * @see http://php.net/manual/en/features.file-upload.errors.php * * @return int */ public function getError() { return $this->error; } /** * Retrieve the filename sent by the client. * * Do not trust the value returned by this method. A client could send * a malicious filename with the intention to corrupt or hack your * application. * * Implementations SHOULD return the value stored in the "name" key of * the file in the $_FILES array. * * @return string|null */ public function getClientFilename() { return $this->name; } /** * Retrieve the media type sent by the client. * * Do not trust the value returned by this method. A client could send * a malicious media type with the intention to corrupt or hack your * application. * * Implementations SHOULD return the value stored in the "type" key of * the file in the $_FILES array. * * @return string|null */ public function getClientMediaType() { return $this->type; } /** * Retrieve the file size. * * Implementations SHOULD return the value stored in the "size" key of * the file in the $_FILES array if available, as PHP calculates this based * on the actual size transmitted. * * @return int|null */ public function getSize() { return $this->size; } } slim/Slim/Http/Response.php 0000666 00000042072 15165376252 0011667 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Interfaces\Http\HeadersInterface; /** * Response * * This class represents an HTTP response. It manages * the response status, headers, and body * according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/ResponseInterface.php */ class Response extends Message implements ResponseInterface { /** * Status code * * @var int */ protected $status = StatusCode::HTTP_OK; /** * Reason phrase * * @var string */ protected $reasonPhrase = ''; /** * Status codes and reason phrases * * @var array */ protected static $messages = [ //Informational 1xx StatusCode::HTTP_CONTINUE => 'Continue', StatusCode::HTTP_SWITCHING_PROTOCOLS => 'Switching Protocols', StatusCode::HTTP_PROCESSING => 'Processing', //Successful 2xx StatusCode::HTTP_OK => 'OK', StatusCode::HTTP_CREATED => 'Created', StatusCode::HTTP_ACCEPTED => 'Accepted', StatusCode::HTTP_NONAUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', StatusCode::HTTP_NO_CONTENT => 'No Content', StatusCode::HTTP_RESET_CONTENT => 'Reset Content', StatusCode::HTTP_PARTIAL_CONTENT => 'Partial Content', StatusCode::HTTP_MULTI_STATUS => 'Multi-Status', StatusCode::HTTP_ALREADY_REPORTED => 'Already Reported', StatusCode::HTTP_IM_USED => 'IM Used', //Redirection 3xx StatusCode::HTTP_MULTIPLE_CHOICES => 'Multiple Choices', StatusCode::HTTP_MOVED_PERMANENTLY => 'Moved Permanently', StatusCode::HTTP_FOUND => 'Found', StatusCode::HTTP_SEE_OTHER => 'See Other', StatusCode::HTTP_NOT_MODIFIED => 'Not Modified', StatusCode::HTTP_USE_PROXY => 'Use Proxy', StatusCode::HTTP_UNUSED => '(Unused)', StatusCode::HTTP_TEMPORARY_REDIRECT => 'Temporary Redirect', StatusCode::HTTP_PERMANENT_REDIRECT => 'Permanent Redirect', //Client Error 4xx StatusCode::HTTP_BAD_REQUEST => 'Bad Request', StatusCode::HTTP_UNAUTHORIZED => 'Unauthorized', StatusCode::HTTP_PAYMENT_REQUIRED => 'Payment Required', StatusCode::HTTP_FORBIDDEN => 'Forbidden', StatusCode::HTTP_NOT_FOUND => 'Not Found', StatusCode::HTTP_METHOD_NOT_ALLOWED => 'Method Not Allowed', StatusCode::HTTP_NOT_ACCEPTABLE => 'Not Acceptable', StatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', StatusCode::HTTP_REQUEST_TIMEOUT => 'Request Timeout', StatusCode::HTTP_CONFLICT => 'Conflict', StatusCode::HTTP_GONE => 'Gone', StatusCode::HTTP_LENGTH_REQUIRED => 'Length Required', StatusCode::HTTP_PRECONDITION_FAILED => 'Precondition Failed', StatusCode::HTTP_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', StatusCode::HTTP_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', StatusCode::HTTP_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', StatusCode::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', StatusCode::HTTP_EXPECTATION_FAILED => 'Expectation Failed', StatusCode::HTTP_IM_A_TEAPOT => 'I\'m a teapot', StatusCode::HTTP_MISDIRECTED_REQUEST => 'Misdirected Request', StatusCode::HTTP_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', StatusCode::HTTP_LOCKED => 'Locked', StatusCode::HTTP_FAILED_DEPENDENCY => 'Failed Dependency', StatusCode::HTTP_UPGRADE_REQUIRED => 'Upgrade Required', StatusCode::HTTP_PRECONDITION_REQUIRED => 'Precondition Required', StatusCode::HTTP_TOO_MANY_REQUESTS => 'Too Many Requests', StatusCode::HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', StatusCode::HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE => 'Connection Closed Without Response', StatusCode::HTTP_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', StatusCode::HTTP_CLIENT_CLOSED_REQUEST => 'Client Closed Request', //Server Error 5xx StatusCode::HTTP_INTERNAL_SERVER_ERROR => 'Internal Server Error', StatusCode::HTTP_NOT_IMPLEMENTED => 'Not Implemented', StatusCode::HTTP_BAD_GATEWAY => 'Bad Gateway', StatusCode::HTTP_SERVICE_UNAVAILABLE => 'Service Unavailable', StatusCode::HTTP_GATEWAY_TIMEOUT => 'Gateway Timeout', StatusCode::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', StatusCode::HTTP_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', StatusCode::HTTP_INSUFFICIENT_STORAGE => 'Insufficient Storage', StatusCode::HTTP_LOOP_DETECTED => 'Loop Detected', StatusCode::HTTP_NOT_EXTENDED => 'Not Extended', StatusCode::HTTP_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', StatusCode::HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR => 'Network Connect Timeout Error', ]; /** * EOL characters used for HTTP response. * * @var string */ const EOL = "\r\n"; /** * @param int $status The response status code. * @param HeadersInterface|null $headers The response headers. * @param StreamInterface|null $body The response body. */ public function __construct( $status = StatusCode::HTTP_OK, HeadersInterface $headers = null, StreamInterface $body = null ) { $this->status = $this->filterStatus($status); $this->headers = $headers ? $headers : new Headers(); $this->body = $body ? $body : new Body(fopen('php://temp', 'r+')); } /** * This method is applied to the cloned object * after PHP performs an initial shallow-copy. This * method completes a deep-copy by creating new objects * for the cloned object's internal reference pointers. */ public function __clone() { $this->headers = clone $this->headers; } /** * Gets the response status code. * * The status code is a 3-digit integer result code of the server's attempt * to understand and satisfy the request. * * @return int */ public function getStatusCode() { return $this->status; } /** * Return an instance with the specified status code and, optionally, reason phrase. * * If no reason phrase is specified, implementations MAY choose to default * to the RFC 7231 or IANA recommended reason phrase for the response's * status code. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated status and reason phrase. * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * * @param int $code The 3-digit integer result code to set. * @param string $reasonPhrase The reason phrase to use with the * provided status code; if none is provided, implementations MAY * use the defaults as suggested in the HTTP specification. * * @return static * * @throws InvalidArgumentException For invalid status code arguments. */ public function withStatus($code, $reasonPhrase = '') { $code = $this->filterStatus($code); if (!is_string($reasonPhrase) && !method_exists($reasonPhrase, '__toString')) { throw new InvalidArgumentException('ReasonPhrase must be a string'); } $clone = clone $this; $clone->status = $code; if ($reasonPhrase === '' && isset(static::$messages[$code])) { $reasonPhrase = static::$messages[$code]; } if ($reasonPhrase === '') { throw new InvalidArgumentException('ReasonPhrase must be supplied for this code'); } $clone->reasonPhrase = $reasonPhrase; return $clone; } /** * Filter HTTP status code. * * @param int $status HTTP status code. * * @return int * * @throws InvalidArgumentException If an invalid HTTP status code is provided. */ protected function filterStatus($status) { if (!is_integer($status) || $status<StatusCode::HTTP_CONTINUE || $status>StatusCode::HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR ) { throw new InvalidArgumentException('Invalid HTTP status code'); } return $status; } /** * Gets the response reason phrase associated with the status code. * * Because a reason phrase is not a required element in a response * status line, the reason phrase value MAY be null. Implementations MAY * choose to return the default RFC 7231 recommended reason phrase (or those * listed in the IANA HTTP Status Code Registry) for the response's * status code. * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * * @return string Reason phrase; must return an empty string if none present. */ public function getReasonPhrase() { if ($this->reasonPhrase) { return $this->reasonPhrase; } if (isset(static::$messages[$this->status])) { return static::$messages[$this->status]; } return ''; } /** * Return an instance with the provided value replacing the specified header. * * If a Location header is set and the status code is 200, then set the status * code to 302 to mimic what PHP does. See https://github.com/slimphp/Slim/issues/1730 * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * * @return static * * @throws InvalidArgumentException For invalid header names or values. */ public function withHeader($name, $value) { $clone = clone $this; $clone->headers->set($name, $value); if ($this->body instanceof NonBufferedBody) { header(sprintf('%s: %s', $name, $clone->getHeaderLine($name))); } if ($clone->getStatusCode() === StatusCode::HTTP_OK && strtolower($name) === 'location') { $clone = $clone->withStatus(StatusCode::HTTP_FOUND); } return $clone; } /** * Write data to the response body. * * Note: This method is not part of the PSR-7 standard. * * Proxies to the underlying stream and writes the provided data to it. * * @param string $data * * @return static */ public function write($data) { $this->getBody()->write($data); return $this; } /** * Redirect. * * Note: This method is not part of the PSR-7 standard. * * This method prepares the response object to return an HTTP Redirect * response to the client. * * @param string|UriInterface $url The redirect destination. * @param int|null $status The redirect HTTP status code. * * @return static */ public function withRedirect($url, $status = null) { $responseWithRedirect = $this->withHeader('Location', (string)$url); if (is_null($status) && $this->getStatusCode() === StatusCode::HTTP_OK) { $status = StatusCode::HTTP_FOUND; } if (!is_null($status)) { return $responseWithRedirect->withStatus($status); } return $responseWithRedirect; } /** * Json. * * Note: This method is not part of the PSR-7 standard. * * This method prepares the response object to return an HTTP Json * response to the client. * * @param mixed $data The data * @param int $status The HTTP status code. * @param int $encodingOptions Json encoding options * * @return static * * @throws RuntimeException */ public function withJson($data, $status = null, $encodingOptions = 0) { $response = $this->withBody(new Body(fopen('php://temp', 'r+'))); $response->body->write($json = json_encode($data, $encodingOptions)); // Ensure that the json encoding passed successfully if ($json === false) { throw new RuntimeException(json_last_error_msg(), json_last_error()); } $responseWithJson = $response->withHeader('Content-Type', 'application/json'); if (isset($status)) { return $responseWithJson->withStatus($status); } return $responseWithJson; } /** * Is this response empty? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isEmpty() { return in_array( $this->getStatusCode(), [StatusCode::HTTP_NO_CONTENT, StatusCode::HTTP_RESET_CONTENT, StatusCode::HTTP_NOT_MODIFIED] ); } /** * Is this response informational? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isInformational() { return $this->getStatusCode() >= StatusCode::HTTP_CONTINUE && $this->getStatusCode() < StatusCode::HTTP_OK; } /** * Is this response OK? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isOk() { return $this->getStatusCode() === StatusCode::HTTP_OK; } /** * Is this response successful? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isSuccessful() { return $this->getStatusCode() >= StatusCode::HTTP_OK && $this->getStatusCode() < StatusCode::HTTP_MULTIPLE_CHOICES; } /** * Is this response a redirect? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isRedirect() { return in_array( $this->getStatusCode(), [ StatusCode::HTTP_MOVED_PERMANENTLY, StatusCode::HTTP_FOUND, StatusCode::HTTP_SEE_OTHER, StatusCode::HTTP_TEMPORARY_REDIRECT, StatusCode::HTTP_PERMANENT_REDIRECT ] ); } /** * Is this response a redirection? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isRedirection() { return $this->getStatusCode() >= StatusCode::HTTP_MULTIPLE_CHOICES && $this->getStatusCode() < StatusCode::HTTP_BAD_REQUEST; } /** * Is this response forbidden? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isForbidden() { return $this->getStatusCode() === StatusCode::HTTP_FORBIDDEN; } /** * Is this response not Found? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isNotFound() { return $this->getStatusCode() === StatusCode::HTTP_NOT_FOUND; } /** * Is this a bad request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isBadRequest() { return $this->getStatusCode() === StatusCode::HTTP_BAD_REQUEST; } /** * Is this response a client error? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isClientError() { return $this->getStatusCode() >= StatusCode::HTTP_BAD_REQUEST && $this->getStatusCode() < StatusCode::HTTP_INTERNAL_SERVER_ERROR; } /** * Is this response a server error? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isServerError() { return $this->getStatusCode() >= StatusCode::HTTP_INTERNAL_SERVER_ERROR && $this->getStatusCode() < 600; } /** * Convert response to string. * * Note: This method is not part of the PSR-7 standard. * * @return string */ public function __toString() { $output = sprintf( 'HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReasonPhrase() ); $output .= Response::EOL; foreach ($this->getHeaders() as $name => $values) { $output .= sprintf('%s: %s', $name, $this->getHeaderLine($name)) . Response::EOL; } $output .= Response::EOL; $output .= (string)$this->getBody(); return $output; } } slim/Slim/Http/RequestBody.php 0000666 00000000757 15165376252 0012343 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; /** * Provides a PSR-7 implementation of a reusable raw request body */ class RequestBody extends Body { public function __construct() { $stream = fopen('php://temp', 'w+'); stream_copy_to_stream(fopen('php://input', 'r'), $stream); rewind($stream); parent::__construct($stream); } } slim/Slim/Http/Request.php 0000666 00000105321 15165376252 0011516 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Closure; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UploadedFileInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Collection; use Slim\Exception\InvalidMethodException; use Slim\Interfaces\Http\HeadersInterface; /** * This class represents an HTTP request. * It manages the request method, URI, headers, cookies, and body according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/RequestInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/ServerRequestInterface.php */ class Request extends Message implements ServerRequestInterface { /** * The request method * * @var string */ protected $method; /** * The original request method (ignoring override) * * @var string */ protected $originalMethod; /** * The request URI object * * @var UriInterface */ protected $uri; /** * The request URI target (path + query string) * * @var string */ protected $requestTarget; /** * The request query string params * * @var array */ protected $queryParams; /** * The request cookies * * @var array */ protected $cookies; /** * The server environment variables at the time the request was created. * * @var array */ protected $serverParams; /** * The request attributes (route segment names and values) * * @var Collection */ protected $attributes; /** * The request body parsed (if possible) into a PHP array or object * * @var null|array|object */ protected $bodyParsed = false; /** * List of request body parsers (e.g., url-encoded, JSON, XML, multipart) * * @var callable[] */ protected $bodyParsers = []; /** * List of uploaded files * * @var UploadedFileInterface[] */ protected $uploadedFiles; /** * Valid request methods * * @var string[] * @deprecated */ protected $validMethods = [ 'CONNECT' => 1, 'DELETE' => 1, 'GET' => 1, 'HEAD' => 1, 'OPTIONS' => 1, 'PATCH' => 1, 'POST' => 1, 'PUT' => 1, 'TRACE' => 1, ]; /** * Create new HTTP request with data extracted from the application * Environment object * * @param Environment $environment The Slim application Environment * * @return static */ public static function createFromEnvironment(Environment $environment) { $method = $environment['REQUEST_METHOD']; $uri = Uri::createFromEnvironment($environment); $headers = Headers::createFromEnvironment($environment); $cookies = Cookies::parseHeader($headers->get('Cookie', [])); $serverParams = $environment->all(); $body = new RequestBody(); $uploadedFiles = UploadedFile::createFromEnvironment($environment); $request = new static($method, $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles); if ($method === 'POST' && in_array($request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data']) ) { // parsed body must be $_POST $request = $request->withParsedBody($_POST); } return $request; } /** * @param string $method The request method * @param UriInterface $uri The request URI object * @param HeadersInterface $headers The request headers collection * @param array $cookies The request cookies collection * @param array $serverParams The server environment variables * @param StreamInterface $body The request body object * @param array $uploadedFiles The request uploadedFiles collection * * @throws InvalidMethodException on invalid HTTP method */ public function __construct( $method, UriInterface $uri, HeadersInterface $headers, array $cookies, array $serverParams, StreamInterface $body, array $uploadedFiles = [] ) { try { $this->originalMethod = $this->filterMethod($method); } catch (InvalidMethodException $e) { $this->originalMethod = $method; } $this->uri = $uri; $this->headers = $headers; $this->cookies = $cookies; $this->serverParams = $serverParams; $this->attributes = new Collection(); $this->body = $body; $this->uploadedFiles = $uploadedFiles; if (isset($serverParams['SERVER_PROTOCOL'])) { $this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']); } if (!$this->headers->has('Host') && $this->uri->getHost() !== '') { $port = $this->uri->getPort() ? ":{$this->uri->getPort()}" : ''; $this->headers->set('Host', $this->uri->getHost() . $port); } $this->registerMediaTypeParser('application/json', function ($input) { $result = json_decode($input, true); if (!is_array($result)) { return null; } return $result; }); $this->registerMediaTypeParser('application/xml', function ($input) { $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); if ($result === false) { return null; } return $result; }); $this->registerMediaTypeParser('text/xml', function ($input) { $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); if ($result === false) { return null; } return $result; }); $this->registerMediaTypeParser('application/x-www-form-urlencoded', function ($input) { parse_str($input, $data); return $data; }); // if the request had an invalid method, we can throw it now if (isset($e) && $e instanceof InvalidMethodException) { throw $e; } } /** * This method is applied to the cloned object after PHP performs an initial shallow-copy. * This method completes a deep-copy by creating new objects for the cloned object's internal reference pointers. */ public function __clone() { $this->headers = clone $this->headers; $this->attributes = clone $this->attributes; $this->body = clone $this->body; } /** * Retrieves the HTTP method of the request. * * @return string */ public function getMethod() { if ($this->method === null) { $this->method = $this->originalMethod; $customMethod = $this->getHeaderLine('X-Http-Method-Override'); if ($customMethod) { $this->method = $this->filterMethod($customMethod); } elseif ($this->originalMethod === 'POST') { $overrideMethod = $this->filterMethod($this->getParsedBodyParam('_METHOD')); if ($overrideMethod !== null) { $this->method = $overrideMethod; } if ($this->getBody()->eof()) { $this->getBody()->rewind(); } } } return $this->method; } /** * Get the original HTTP method (ignore override). * * Note: This method is not part of the PSR-7 standard. * * @return string */ public function getOriginalMethod() { return $this->originalMethod; } /** * Return an instance with the provided HTTP method. * * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT * modify the given string. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request method. * * @param string $method Case-sensitive method. * * @return static * * @throws InvalidArgumentException for invalid HTTP methods. */ public function withMethod($method) { $method = $this->filterMethod($method); $clone = clone $this; $clone->originalMethod = $method; $clone->method = $method; return $clone; } /** * Validate the HTTP method * * @param null|string $method * * @return null|string * * @throws InvalidArgumentException on invalid HTTP method. */ protected function filterMethod($method) { if ($method === null) { return $method; } if (!is_string($method)) { throw new InvalidArgumentException(sprintf( 'Unsupported HTTP method; must be a string, received %s', (is_object($method) ? get_class($method) : gettype($method)) )); } $method = strtoupper($method); if (preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method) !== 1) { throw new InvalidMethodException($this, $method); } return $method; } /** * Does this request use a given method? * * Note: This method is not part of the PSR-7 standard. * * @param string $method HTTP method * * @return bool */ public function isMethod($method) { return $this->getMethod() === $method; } /** * Is this a GET request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isGet() { return $this->isMethod('GET'); } /** * Is this a POST request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPost() { return $this->isMethod('POST'); } /** * Is this a PUT request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPut() { return $this->isMethod('PUT'); } /** * Is this a PATCH request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPatch() { return $this->isMethod('PATCH'); } /** * Is this a DELETE request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isDelete() { return $this->isMethod('DELETE'); } /** * Is this a HEAD request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isHead() { return $this->isMethod('HEAD'); } /** * Is this a OPTIONS request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isOptions() { return $this->isMethod('OPTIONS'); } /** * Is this an XHR request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isXhr() { return $this->getHeaderLine('X-Requested-With') === 'XMLHttpRequest'; } /** * Retrieves the message's request target. * * Retrieves the message's request-target either as it will appear (for * clients), as it appeared at request (for servers), or as it was * specified for the instance (see withRequestTarget()). * * In most cases, this will be the origin-form of the composed URI, * unless a value was provided to the concrete implementation (see * withRequestTarget() below). * * If no URI is available, and no request-target has been specifically * provided, this method MUST return the string "/". * * @return string */ public function getRequestTarget() { if ($this->requestTarget) { return $this->requestTarget; } if ($this->uri === null) { return '/'; } if ($this->uri instanceof Uri) { $basePath = $this->uri->getBasePath(); } else { $basePath = ''; } $path = $this->uri->getPath(); $path = $basePath . '/' . ltrim($path, '/'); $query = $this->uri->getQuery(); if ($query) { $path .= '?' . $query; } $this->requestTarget = $path; return $this->requestTarget; } /** * Return an instance with the specific request-target. * * If the request needs a non-origin-form request-target — e.g., for * specifying an absolute-form, authority-form, or asterisk-form — * this method may be used to create an instance with the specified * request-target, verbatim. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request target. * * @link http://tools.ietf.org/html/rfc7230#section-2.7 * (for the various request-target forms allowed in request messages) * * @param string $requestTarget * * @return static * * @throws InvalidArgumentException if the request target is invalid */ public function withRequestTarget($requestTarget) { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException( 'Invalid request target provided; must be a string and cannot contain whitespace' ); } $clone = clone $this; $clone->requestTarget = $requestTarget; return $clone; } /** * Retrieves the URI instance. * * This method MUST return a UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * * @return UriInterface */ public function getUri() { return $this->uri; } /** * Returns an instance with the provided URI. * * This method MUST update the Host header of the returned request by * default if the URI contains a host component. If the URI does not * contain a host component, any pre-existing Host header MUST be carried * over to the returned request. * * You can opt-in to preserving the original state of the Host header by * setting `$preserveHost` to `true`. When `$preserveHost` is set to * `true`, this method interacts with the Host header in the following ways: * * - If the the Host header is missing or empty, and the new URI contains * a host component, this method MUST update the Host header in the returned * request. * - If the Host header is missing or empty, and the new URI does not contain a * host component, this method MUST NOT update the Host header in the returned * request. * - If a Host header is present and non-empty, this method MUST NOT update * the Host header in the returned request. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * * @param UriInterface $uri New request URI to use. * @param bool $preserveHost Preserve the original state of the Host header. * * @return static */ public function withUri(UriInterface $uri, $preserveHost = false) { $clone = clone $this; $clone->uri = $uri; if (!$preserveHost) { if ($uri->getHost() !== '') { $clone->headers->set('Host', $uri->getHost()); } } else { if ($uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeaderLine('Host') === '')) { $clone->headers->set('Host', $uri->getHost()); } } return $clone; } /** * Get request content type. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getContentType() { $result = $this->getHeader('Content-Type'); return $result ? $result[0] : null; } /** * Get request media type, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getMediaType() { $contentType = $this->getContentType(); if ($contentType) { $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); return strtolower($contentTypeParts[0]); } return null; } /** * Get request media type params, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string[] */ public function getMediaTypeParams() { $contentType = $this->getContentType(); $contentTypeParams = []; if ($contentType) { $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); $contentTypePartsLength = count($contentTypeParts); for ($i = 1; $i < $contentTypePartsLength; $i++) { $paramParts = explode('=', $contentTypeParts[$i] ? $contentTypeParts[$i] : ""); $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1]; } } return $contentTypeParams; } /** * Get request content character set, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getContentCharset() { $mediaTypeParams = $this->getMediaTypeParams(); if (isset($mediaTypeParams['charset'])) { return $mediaTypeParams['charset']; } return null; } /** * Get request content length, if known. * * Note: This method is not part of the PSR-7 standard. * * @return int|null */ public function getContentLength() { $result = $this->headers->get('Content-Length'); return $result ? (int)$result[0] : null; } /** * Retrieve cookies. * * Retrieves cookies sent by the client to the server. * * The data MUST be compatible with the structure of the $_COOKIE superglobal. * * @return array */ public function getCookieParams() { return $this->cookies; } /** * Fetch cookie value from cookies sent by the client to the server. * * Note: This method is not part of the PSR-7 standard. * * @param string $key The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * * @return mixed */ public function getCookieParam($key, $default = null) { $cookies = $this->getCookieParams(); $result = $default; if (isset($cookies[$key])) { $result = $cookies[$key]; } return $result; } /** * Return an instance with the specified cookies. * * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST * be compatible with the structure of $_COOKIE. Typically, this data will * be injected at instantiation. * * This method MUST NOT update the related Cookie header of the request * instance, nor related values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated cookie values. * * @param array $cookies Array of key/value pairs representing cookies. * * @return static */ public function withCookieParams(array $cookies) { $clone = clone $this; $clone->cookies = $cookies; return $clone; } /** * Retrieve query string arguments. * * Retrieves the deserialized query string arguments, if any. * * Note: the query params might not be in sync with the URI or server * params. If you need to ensure you are only getting the original * values, you may need to parse the query string from `getUri()->getQuery()` * or from the `QUERY_STRING` server param. * * @return array */ public function getQueryParams() { if (is_array($this->queryParams)) { return $this->queryParams; } if ($this->uri === null) { return []; } parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data return $this->queryParams; } /** * Return an instance with the specified query string arguments. * * These values SHOULD remain immutable over the course of the incoming * request. They MAY be injected during instantiation, such as from PHP's * $_GET superglobal, or MAY be derived from some other value such as the * URI. In cases where the arguments are parsed from the URI, the data * MUST be compatible with what PHP's parse_str() would return for * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * * Setting query string arguments MUST NOT change the URI stored by the * request, nor the values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated query string arguments. * * @param array $query Array of query string arguments, typically from * $_GET. * * @return static */ public function withQueryParams(array $query) { $clone = clone $this; $clone->queryParams = $query; return $clone; } /** * Retrieve normalized file upload data. * * This method returns upload metadata in a normalized tree, with each leaf * an instance of AmeliaPsr\Http\Message\UploadedFileInterface. * * These values MAY be prepared from $_FILES or the message body during * instantiation, or MAY be injected via withUploadedFiles(). * * @return array */ public function getUploadedFiles() { return $this->uploadedFiles; } /** * Create a new instance with the specified uploaded files. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param array $uploadedFiles An array tree of UploadedFileInterface instances. * * @return static * * @throws InvalidArgumentException if an invalid structure is provided. */ public function withUploadedFiles(array $uploadedFiles) { $clone = clone $this; $clone->uploadedFiles = $uploadedFiles; return $clone; } /** * Retrieve server parameters. * * Retrieves data related to the incoming request environment, * typically derived from PHP's $_SERVER superglobal. The data IS NOT * REQUIRED to originate from $_SERVER. * * @return array */ public function getServerParams() { return $this->serverParams; } /** * Retrieve a server parameter. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getServerParam($key, $default = null) { $serverParams = $this->getServerParams(); return isset($serverParams[$key]) ? $serverParams[$key] : $default; } /** * Retrieve attributes derived from the request. * * The request "attributes" may be used to allow injection of any * parameters derived from the request: e.g., the results of path * match operations; the results of decrypting cookies; the results of * deserializing non-form-encoded message bodies; etc. Attributes * will be application and request specific, and CAN be mutable. * * @return array */ public function getAttributes() { return $this->attributes->all(); } /** * Retrieve a single derived request attribute. * * Retrieves a single derived request attribute as described in * getAttributes(). If the attribute has not been previously set, returns * the default value as provided. * * This method obviates the need for a hasAttribute() method, as it allows * specifying a default value to return if the attribute is not found. * * @see getAttributes() * * @param string $name The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * * @return mixed */ public function getAttribute($name, $default = null) { return $this->attributes->get($name, $default); } /** * Return an instance with the specified derived request attribute. * * This method allows setting a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated attribute. * * @see getAttributes() * * @param string $name The attribute name. * @param mixed $value The value of the attribute. * * @return static */ public function withAttribute($name, $value) { $clone = clone $this; $clone->attributes->set($name, $value); return $clone; } /** * Create a new instance with the specified derived request attributes. * * Note: This method is not part of the PSR-7 standard. * * This method allows setting all new derived request attributes as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * updated attributes. * * @param array $attributes New attributes * * @return static */ public function withAttributes(array $attributes) { $clone = clone $this; $clone->attributes = new Collection($attributes); return $clone; } /** * Return an instance that removes the specified derived request attribute. * * This method allows removing a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the attribute. * * @see getAttributes() * * @param string $name The attribute name. * * @return static */ public function withoutAttribute($name) { $clone = clone $this; $clone->attributes->remove($name); return $clone; } /** * Retrieve any parameters provided in the request body. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, this method MUST * return the contents of $_POST. * * Otherwise, this method may return any results of deserializing * the request body content; as parsing returns structured content, the * potential types MUST be arrays or objects only. A null value indicates * the absence of body content. * * @return null|array|object * * @throws RuntimeException if the request body media type parser returns an invalid value */ public function getParsedBody() { if ($this->bodyParsed !== false) { return $this->bodyParsed; } if (!$this->body) { return null; } $mediaType = $this->getMediaType(); // Check if this specific media type has a parser registered first if (!isset($this->bodyParsers[$mediaType])) { // If not, look for a media type with a structured syntax suffix (RFC 6839) $parts = explode('+', $mediaType ? $mediaType : ""); if (count($parts) >= 2) { $mediaType = 'application/' . $parts[count($parts)-1]; } } if (isset($this->bodyParsers[$mediaType])) { $body = (string)$this->getBody(); $parsed = $this->bodyParsers[$mediaType]($body); if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) { throw new RuntimeException( 'Request body media type parser return value must be an array, an object, or null' ); } $this->bodyParsed = $parsed; return $this->bodyParsed; } return null; } /** * Return an instance with the specified body parameters. * * These MAY be injected during instantiation. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, use this method * ONLY to inject the contents of $_POST. * * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of * deserializing the request body content. Deserialization/parsing returns * structured data, and, as such, this method ONLY accepts arrays or objects, * or a null value if nothing was available to parse. * * As an example, if content negotiation determines that the request data * is a JSON payload, this method could be used to create a request * instance with the deserialized parameters. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param null|array|object $data The deserialized body data. This will typically be in an array or object. * * @return static * * @throws InvalidArgumentException if an unsupported argument type is * provided. */ public function withParsedBody($data) { if (!is_null($data) && !is_object($data) && !is_array($data)) { throw new InvalidArgumentException('Parsed body value must be an array, an object, or null'); } $clone = clone $this; $clone->bodyParsed = $data; return $clone; } /** * Force Body to be parsed again. * * Note: This method is not part of the PSR-7 standard. * * @return $this */ public function reparseBody() { $this->bodyParsed = false; return $this; } /** * Register media type parser. * * Note: This method is not part of the PSR-7 standard. * * @param string $mediaType A HTTP media type (excluding content-type params). * @param callable $callable A callable that returns parsed contents for media type. */ public function registerMediaTypeParser($mediaType, callable $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this); } $this->bodyParsers[(string)$mediaType] = $callable; } /** * Fetch request parameter value from body or query string (in that order). * * Note: This method is not part of the PSR-7 standard. * * @param string $key The parameter key. * @param mixed $default The default value. * * @return mixed */ public function getParam($key, $default = null) { $postParams = $this->getParsedBody(); $getParams = $this->getQueryParams(); $result = $default; if (is_array($postParams) && isset($postParams[$key])) { $result = $postParams[$key]; } elseif (is_object($postParams) && property_exists($postParams, $key)) { $result = $postParams->$key; } elseif (isset($getParams[$key])) { $result = $getParams[$key]; } return $result; } /** * Fetch parameter value from request body. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getParsedBodyParam($key, $default = null) { $postParams = $this->getParsedBody(); $result = $default; if (is_array($postParams) && isset($postParams[$key])) { $result = $postParams[$key]; } elseif (is_object($postParams) && property_exists($postParams, $key)) { $result = $postParams->$key; } return $result; } /** * Fetch parameter value from query string. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getQueryParam($key, $default = null) { $getParams = $this->getQueryParams(); $result = $default; if (isset($getParams[$key])) { $result = $getParams[$key]; } return $result; } /** * Fetch associative array of body and query string parameters. * * Note: This method is not part of the PSR-7 standard. * * @param array|null $only list the keys to retrieve. * * @return array|null */ public function getParams(array $only = null) { $params = $this->getQueryParams(); $postParams = $this->getParsedBody(); if ($postParams) { $params = array_replace($params, (array)$postParams); } if ($only) { $onlyParams = []; foreach ($only as $key) { if (array_key_exists($key, $params)) { $onlyParams[$key] = $params[$key]; } } return $onlyParams; } return $params; } } slim/Slim/Http/Environment.php 0000666 00000003624 15165376252 0012375 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Slim\Collection; use Slim\Interfaces\Http\EnvironmentInterface; /** * This class decouples the Slim application from the global PHP environment. * This is particularly useful for unit testing, but it also lets us create * custom sub-requests. */ class Environment extends Collection implements EnvironmentInterface { /** * {@inheritdoc} */ public static function mock(array $settings = []) { //Validates if default protocol is HTTPS to set default port 443 if ((isset($settings['HTTPS']) && $settings['HTTPS'] !== 'off') || ((isset($settings['REQUEST_SCHEME']) && $settings['REQUEST_SCHEME'] === 'https'))) { $defscheme = 'https'; $defport = 443; } else { $defscheme = 'http'; $defport = 80; } $data = array_merge([ 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_METHOD' => 'GET', 'REQUEST_SCHEME' => $defscheme, 'SCRIPT_NAME' => '', 'REQUEST_URI' => '', 'QUERY_STRING' => '', 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => $defport, 'HTTP_HOST' => 'localhost', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'HTTP_USER_AGENT' => 'Slim Framework', 'REMOTE_ADDR' => '127.0.0.1', 'REQUEST_TIME' => time(), 'REQUEST_TIME_FLOAT' => microtime(true), ], $settings); return new static($data); } } slim/Slim/Http/Cookies.php 0000666 00000010515 15165376252 0011462 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use Slim\Interfaces\Http\CookiesInterface; class Cookies implements CookiesInterface { /** * Cookies from HTTP request * * @var array */ protected $requestCookies = []; /** * Cookies for HTTP response * * @var array */ protected $responseCookies = []; /** * Default cookie properties * * @var array */ protected $defaults = [ 'value' => '', 'domain' => null, 'hostonly' => null, 'path' => null, 'expires' => null, 'secure' => false, 'httponly' => false, 'samesite' => null ]; /** * @param array $cookies */ public function __construct(array $cookies = []) { $this->requestCookies = $cookies; } /** * Set default cookie properties * * @param array $settings */ public function setDefaults(array $settings) { $this->defaults = array_replace($this->defaults, $settings); } /** * {@inheritdoc} */ public function get($name, $default = null) { return isset($this->requestCookies[$name]) ? $this->requestCookies[$name] : $default; } /** * {@inheritdoc} */ public function set($name, $value) { if (!is_array($value)) { $value = ['value' => (string)$value]; } $this->responseCookies[$name] = array_replace($this->defaults, $value); } /** * {@inheritdoc} */ public function toHeaders() { $headers = []; foreach ($this->responseCookies as $name => $properties) { $headers[] = $this->toHeader($name, $properties); } return $headers; } /** * Convert to `Set-Cookie` header * * @param string $name Cookie name * @param array $properties Cookie properties * * @return string */ protected function toHeader($name, array $properties) { $result = urlencode($name) . '=' . urlencode($properties['value']); if (isset($properties['domain'])) { $result .= '; domain=' . $properties['domain']; } if (isset($properties['path'])) { $result .= '; path=' . $properties['path']; } if (isset($properties['expires'])) { if (is_string($properties['expires'])) { $timestamp = strtotime($properties['expires']); } else { $timestamp = (int)$properties['expires']; } if ($timestamp !== 0) { $result .= '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp); } } if (isset($properties['secure']) && $properties['secure']) { $result .= '; secure'; } if (isset($properties['hostonly']) && $properties['hostonly']) { $result .= '; HostOnly'; } if (isset($properties['httponly']) && $properties['httponly']) { $result .= '; HttpOnly'; } if (isset($properties['samesite']) && in_array(strtolower($properties['samesite']), ['lax', 'strict'], true)) { // While strtolower is needed for correct comparison, the RFC doesn't care about case $result .= '; SameSite=' . $properties['samesite']; } return $result; } /** * {@inheritdoc} */ public static function parseHeader($header) { if (is_array($header) === true) { $header = isset($header[0]) ? $header[0] : ''; } if (is_string($header) === false) { throw new InvalidArgumentException('Cannot parse Cookie data. Header value must be a string.'); } $header = rtrim($header, "\r\n"); $pieces = preg_split('@[;]\s*@', $header); $cookies = []; foreach ($pieces as $cookie) { $cookie = explode('=', $cookie, 2); if (count($cookie) === 2) { $key = urldecode($cookie[0]); $value = urldecode($cookie[1]); if (!isset($cookies[$key])) { $cookies[$key] = $value; } } } return $cookies; } } slim/Slim/Http/Headers.php 0000666 00000012455 15165376252 0011446 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Slim\Collection; use Slim\Interfaces\Http\HeadersInterface; /** * This class represents a collection of HTTP headers * that is used in both the HTTP request and response objects. * It also enables header name case-insensitivity when * getting or setting a header value. * * Each HTTP header can have multiple values. This class * stores values into an array for each header name. When * you request a header value, you receive an array of values * for that header. */ class Headers extends Collection implements HeadersInterface { /** * Special HTTP headers that do not have the "HTTP_" prefix * * @var array */ protected static $special = [ 'CONTENT_TYPE' => 1, 'CONTENT_LENGTH' => 1, 'PHP_AUTH_USER' => 1, 'PHP_AUTH_PW' => 1, 'PHP_AUTH_DIGEST' => 1, 'AUTH_TYPE' => 1, ]; /** * Create new headers collection with data extracted from the application Environment object * * @param Environment $environment The Slim application Environment * * @return self */ public static function createFromEnvironment(Environment $environment) { $data = []; $environment = self::determineAuthorization($environment); foreach ($environment as $key => $value) { $key = strtoupper($key); if (isset(static::$special[$key]) || strpos($key, 'HTTP_') === 0) { if ($key !== 'HTTP_CONTENT_LENGTH') { $data[$key] = $value; } } } return new static($data); } /** * If HTTP_AUTHORIZATION does not exist tries to get it from getallheaders() when available. * * @param Environment $environment The Slim application Environment * * @return Environment */ public static function determineAuthorization(Environment $environment) { $authorization = $environment->get('HTTP_AUTHORIZATION'); if (!empty($authorization) || !is_callable('getallheaders')) { return $environment; } $headers = getallheaders(); if (!is_array($headers)) { return $environment; } $headers = array_change_key_case($headers, CASE_LOWER); if (isset($headers['authorization'])) { $environment->set('HTTP_AUTHORIZATION', $headers['authorization']); } return $environment; } /** * Return array of HTTP header names and values. * This method returns the _original_ header name as specified by the end user. * * @return array */ public function all() { $all = parent::all(); $out = []; foreach ($all as $key => $props) { $out[$props['originalKey']] = $props['value']; } return $out; } /** * Set HTTP header value * * This method sets a header value. It replaces * any values that may already exist for the header name. * * @param string $key The case-insensitive header name * @param array|string $value The header value */ public function set($key, $value) { if (!is_array($value)) { $value = [$value]; } parent::set($this->normalizeKey($key), [ 'value' => $value, 'originalKey' => $key ]); } /** * Get HTTP header value * * @param string $key The case-insensitive header name * @param mixed $default The default value if key does not exist * * @return string[] */ public function get($key, $default = null) { if ($this->has($key)) { return parent::get($this->normalizeKey($key))['value']; } return $default; } /** * Get HTTP header key as originally specified * * @param string $key The case-insensitive header name * @param mixed $default The default value if key does not exist * * @return string */ public function getOriginalKey($key, $default = null) { if ($this->has($key)) { return parent::get($this->normalizeKey($key))['originalKey']; } return $default; } /** * {@inheritdoc} */ public function add($key, $value) { $oldValues = $this->get($key, []); $newValues = is_array($value) ? $value : [$value]; $this->set($key, array_merge($oldValues, array_values($newValues))); } /** * Does this collection have a given header? * * @param string $key The case-insensitive header name * * @return bool */ public function has($key) { return parent::has($this->normalizeKey($key)); } /** * Remove header from collection * * @param string $key The case-insensitive header name */ public function remove($key) { parent::remove($this->normalizeKey($key)); } /** * {@inheritdoc} */ public function normalizeKey($key) { $key = strtr(strtolower($key), '_', '-'); if (strpos($key, 'http-') === 0) { $key = substr($key, 5); } return $key; } } slim/Slim/Http/NonBufferedBody.php 0000666 00000004053 15165376252 0013101 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use AmeliaPsr\Http\Message\StreamInterface; /** * Represents a non-readable stream that whenever it is written pushes * the data back to the browser immediately. */ class NonBufferedBody implements StreamInterface { /** * {@inheritdoc} */ public function __toString() { return ''; } /** * {@inheritdoc} */ public function close() { } /** * {@inheritdoc} */ public function detach() { return null; } /** * {@inheritdoc} */ public function getSize() { return null; } /** * {@inheritdoc} */ public function tell() { return 0; } /** * {@inheritdoc} */ public function eof() { return true; } /** * {@inheritdoc} */ public function isSeekable() { return false; } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) { } /** * {@inheritdoc} */ public function rewind() { } /** * {@inheritdoc} */ public function isWritable() { return true; } /** * {@inheritdoc} */ public function write($string) { $buffered = ''; while (0 < ob_get_level()) { $buffered = ob_get_clean() . $buffered; } echo $buffered . $string; flush(); return strlen($string) + strlen($buffered); } /** * {@inheritdoc} */ public function isReadable() { return false; } /** * {@inheritdoc} */ public function read($length) { return ''; } /** * {@inheritdoc} */ public function getContents() { return ''; } /** * {@inheritdoc} */ public function getMetadata($key = null) { return null; } } slim/Slim/Http/Body.php 0000666 00000000302 15165376252 0010754 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; class Body extends Stream { } slim/Slim/Http/Uri.php 0000666 00000063037 15165376252 0010634 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\UriInterface; /** * Value object representing a URI. * * This interface is meant to represent URIs according to RFC 3986 and to * provide methods for most common operations. Additional functionality for * working with URIs can be provided on top of the interface or externally. * Its primary use is for HTTP requests, but may also be used in other * contexts. * * Instances of this interface are considered immutable; all methods that * might change state MUST be implemented such that they retain the internal * state of the current instance and return an instance that contains the * changed state. * * Typically the Host header will be also be present in the request message. * For server-side requests, the scheme will typically be discoverable in the * server parameters. * * @link http://tools.ietf.org/html/rfc3986 (the URI specification) */ class Uri implements UriInterface { /** * Uri scheme (without "://" suffix) * * @var string */ protected $scheme = ''; /** * Uri user * * @var string */ protected $user = ''; /** * Uri password * * @var string */ protected $password = ''; /** * Uri host * * @var string */ protected $host = ''; /** * Uri port number * * @var null|int */ protected $port; /** * Uri base path * * @var string */ protected $basePath = ''; /** * Uri path * * @var string */ protected $path = ''; /** * Uri query string (without "?" prefix) * * @var string */ protected $query = ''; /** * Uri fragment string (without "#" prefix) * * @var string */ protected $fragment = ''; /** * @param string $scheme Uri scheme. * @param string $host Uri host. * @param int $port Uri port number. * @param string $path Uri path. * @param string $query Uri query string. * @param string $fragment Uri fragment. * @param string $user Uri user. * @param string $password Uri password. */ public function __construct( $scheme, $host, $port = null, $path = '/', $query = '', $fragment = '', $user = '', $password = '' ) { $this->scheme = $this->filterScheme($scheme); $this->host = $host; $this->port = $this->filterPort($port); $this->path = ($path === null || !strlen($path)) ? '/' : $this->filterPath($path); $this->query = $this->filterQuery($query); $this->fragment = $this->filterQuery($fragment); $this->user = $user; $this->password = $password; } /** * Create new Uri from string. * * @param string $uri Complete Uri string (i.e., https://user:pass@host:443/path?query). * * @return self */ public static function createFromString($uri) { if (!is_string($uri) && !method_exists($uri, '__toString')) { throw new InvalidArgumentException('Uri must be a string'); } $parts = parse_url($uri); $scheme = isset($parts['scheme']) ? $parts['scheme'] : ''; $user = isset($parts['user']) ? $parts['user'] : ''; $pass = isset($parts['pass']) ? $parts['pass'] : ''; $host = isset($parts['host']) ? $parts['host'] : ''; $port = isset($parts['port']) ? $parts['port'] : null; $path = isset($parts['path']) ? $parts['path'] : ''; $query = isset($parts['query']) ? $parts['query'] : ''; $fragment = isset($parts['fragment']) ? $parts['fragment'] : ''; return new static($scheme, $host, $port, $path, $query, $fragment, $user, $pass); } /** * Create new Uri from environment. * * @param Environment $env * * @return self */ public static function createFromEnvironment(Environment $env) { // Scheme $isSecure = $env->get('HTTPS'); $scheme = (empty($isSecure) || $isSecure === 'off') ? 'http' : 'https'; // Authority: Username and password $username = $env->get('PHP_AUTH_USER', ''); $password = $env->get('PHP_AUTH_PW', ''); // Authority: Host and Port if ($env->has('HTTP_HOST')) { $host = $env->get('HTTP_HOST'); // set a port default $port = null; } else { $host = $env->get('SERVER_NAME'); // set a port default $port = (int)$env->get('SERVER_PORT', 80); } if (preg_match('/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/', $host, $matches)) { $host = $matches[1]; if (isset($matches[2])) { $port = (int) substr($matches[2], 1); } } else { $pos = strpos($host, ':'); if ($pos !== false) { $port = (int) substr($host, $pos + 1); $host = strstr($host, ':', true); } } // Path $requestScriptName = (string) parse_url($env->get('SCRIPT_NAME'), PHP_URL_PATH); $requestScriptDir = dirname($requestScriptName); // parse_url() requires a full URL. As we don't extract the domain name or scheme, // we use a stand-in. $requestUri = (string) parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_PATH); $basePath = ''; $virtualPath = $requestUri; if (stripos($requestUri, $requestScriptName) === 0) { $basePath = $requestScriptName; } elseif ($requestScriptDir !== '/' && stripos($requestUri, $requestScriptDir) === 0) { $basePath = $requestScriptDir; } if ($basePath) { $virtualPath = ltrim(substr($requestUri, strlen($basePath)), '/'); } // Query string $queryString = $env->get('QUERY_STRING', ''); if ($queryString === '') { $queryString = parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_QUERY); } // Fragment $fragment = ''; // Build Uri $uri = new static($scheme, $host, $port, $virtualPath, $queryString, $fragment, $username, $password); if ($basePath) { $uri = $uri->withBasePath($basePath); } return $uri; } /** * Retrieve the scheme component of the URI. * * If no scheme is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.1. * * The trailing ":" character is not part of the scheme and MUST NOT be * added. * * @see https://tools.ietf.org/html/rfc3986#section-3.1 * * @return string The URI scheme. */ public function getScheme() { return $this->scheme; } /** * Return an instance with the specified scheme. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified scheme. * * Implementations MUST support the schemes "http" and "https" case * insensitively, and MAY accommodate other schemes if required. * * An empty scheme is equivalent to removing the scheme. * * @param string $scheme The scheme to use with the new instance. * * @return self A new instance with the specified scheme. * * @throws InvalidArgumentException for invalid or unsupported schemes. */ public function withScheme($scheme) { $scheme = $this->filterScheme($scheme); $clone = clone $this; $clone->scheme = $scheme; return $clone; } /** * Filter Uri scheme. * * @param string $scheme Raw Uri scheme. * @return string * * @throws InvalidArgumentException If the Uri scheme is not a string. * @throws InvalidArgumentException If Uri scheme is not "", "https", or "http". */ protected function filterScheme($scheme) { static $valid = [ '' => true, 'https' => true, 'http' => true, ]; if (!is_string($scheme) && !method_exists($scheme, '__toString')) { throw new InvalidArgumentException('Uri scheme must be a string'); } $scheme = str_replace('://', '', strtolower((string)$scheme)); if (!isset($valid[$scheme])) { throw new InvalidArgumentException('Uri scheme must be one of: "", "https", "http"'); } return $scheme; } /** * Retrieve the authority component of the URI. * * If no authority information is present, this method MUST return an empty * string. * * The authority syntax of the URI is: * * <pre> * [user-info@]host[:port] * </pre> * * If the port component is not set or is the standard port for the current * scheme, it SHOULD NOT be included. * * @see https://tools.ietf.org/html/rfc3986#section-3.2 * * @return string The URI authority, in "[user-info@]host[:port]" format. */ public function getAuthority() { $userInfo = $this->getUserInfo(); $host = $this->getHost(); $port = $this->getPort(); return ($userInfo !== '' ? $userInfo . '@' : '') . $host . ($port !== null ? ':' . $port : ''); } /** * Retrieve the user information component of the URI. * * If no user information is present, this method MUST return an empty * string. * * If a user is present in the URI, this will return that value; * additionally, if the password is also present, it will be appended to the * user value, with a colon (":") separating the values. * * The trailing "@" character is not part of the user information and MUST * NOT be added. * * @return string The URI user information, in "username[:password]" format. */ public function getUserInfo() { return $this->user . ($this->password !== '' ? ':' . $this->password : ''); } /** * Return an instance with the specified user information. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified user information. * * Password is optional, but the user information MUST include the * user; an empty string for the user is equivalent to removing user * information. * * @param string $user The user name to use for authority. * @param null|string $password The password associated with $user. * * @return self A new instance with the specified user information. */ public function withUserInfo($user, $password = null) { $clone = clone $this; $clone->user = $this->filterUserInfo($user); if ('' !== $clone->user) { $clone->password = !in_array($password, [null, ''], true) ? $this->filterUserInfo($password) : ''; } else { $clone->password = ''; } return $clone; } /** * Filters the user info string. * * @param string $query The raw uri query string. * * @return string The percent-encoded query string. */ protected function filterUserInfo($query) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u', function ($match) { return rawurlencode($match[0]); }, $query ); } /** * Retrieve the host component of the URI. * * If no host is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.2.2. * * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 * * @return string The URI host. */ public function getHost() { return $this->host; } /** * Return an instance with the specified host. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified host. * * An empty host value is equivalent to removing the host. * * @param string $host The hostname to use with the new instance. * * @return self A new instance with the specified host. */ public function withHost($host) { $clone = clone $this; $clone->host = $host; return $clone; } /** * Retrieve the port component of the URI. * * If a port is present, and it is non-standard for the current scheme, * this method MUST return it as an integer. If the port is the standard port * used with the current scheme, this method SHOULD return null. * * If no port is present, and no scheme is present, this method MUST return * a null value. * * If no port is present, but a scheme is present, this method MAY return * the standard port for that scheme, but SHOULD return null. * * @return null|int The URI port. */ public function getPort() { return $this->port && !$this->hasStandardPort() ? $this->port : null; } /** * Return an instance with the specified port. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified port. * * Implementations MUST raise an exception for ports outside the * established TCP and UDP port ranges. * * A null value provided for the port is equivalent to removing the port * information. * * @param null|int $port The port to use with the new instance; a null value * removes the port information. * * @return self A new instance with the specified port. */ public function withPort($port) { $port = $this->filterPort($port); $clone = clone $this; $clone->port = $port; return $clone; } /** * Does this Uri use a standard port? * * @return bool */ protected function hasStandardPort() { return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); } /** * Filter Uri port. * * @param null|int $port The Uri port number. * @return null|int * * @throws InvalidArgumentException If the port is invalid. */ protected function filterPort($port) { if (is_null($port) || (is_integer($port) && ($port >= 1 && $port <= 65535))) { return $port; } throw new InvalidArgumentException('Uri port must be null or an integer between 1 and 65535 (inclusive)'); } /** * Retrieve the path component of the URI. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * Normally, the empty path "" and absolute path "/" are considered equal as * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically * do this normalization because in contexts with a trimmed base path, e.g. * the front controller, this difference becomes significant. It's the task * of the user to handle both "" and "/". * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.3. * * As an example, if the value should include a slash ("/") not intended as * delimiter between path segments, that value MUST be passed in encoded * form (e.g., "%2F") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.3 * * @return string The URI path. */ public function getPath() { return $this->path; } /** * Return an instance with the specified path. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified path. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all three syntaxes. * * If the path is intended to be domain-relative rather than path relative then * it must begin with a slash ("/"). Paths not starting with a slash ("/") * are assumed to be relative to some base path known to the application or * consumer. * * Users can provide both encoded and decoded path characters. * Implementations ensure the correct encoding as outlined in getPath(). * * @param string $path The path to use with the new instance. * * @return static A new instance with the specified path. * * @throws InvalidArgumentException For invalid paths. */ public function withPath($path) { if (!is_string($path)) { throw new InvalidArgumentException('Uri path must be a string'); } $clone = clone $this; $clone->path = $this->filterPath($path); // if the path is absolute, then clear basePath if (substr($path, 0, 1) == '/') { $clone->basePath = ''; } return $clone; } /** * Retrieve the base path segment of the URI. * * Note: This method is not part of the PSR-7 standard. * * This method MUST return a string; if no path is present it MUST return * an empty string. * * @return string The base path segment of the URI. */ public function getBasePath() { return $this->basePath; } /** * Set base path. * * Note: This method is not part of the PSR-7 standard. * * @param string $basePath * * @return static */ public function withBasePath($basePath) { if (!is_string($basePath)) { throw new InvalidArgumentException('Uri path must be a string'); } if (!empty($basePath)) { $basePath = '/' . trim($basePath, '/'); // <-- Trim on both sides } $clone = clone $this; if ($basePath !== '/') { $clone->basePath = $this->filterPath($basePath); } return $clone; } /** * Filter Uri path. * * Returns a RFC 3986 percent-encoded uri path. * * This method percent-encodes all reserved * characters in the provided path string. This method * will NOT double-encode characters that are already * percent-encoded. * * @param string $path The raw uri path. * * @return string * * @link http://www.faqs.org/rfcs/rfc3986.html */ protected function filterPath($path) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/', function ($match) { return rawurlencode($match[0]); }, $path ); } /** * Retrieve the query string of the URI. * * If no query string is present, this method MUST return an empty string. * * The leading "?" character is not part of the query and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.4. * * As an example, if a value in a key/value pair of the query string should * include an ampersand ("&") not intended as a delimiter between values, * that value MUST be passed in encoded form (e.g., "%26") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.4 * * @return string */ public function getQuery() { return $this->query; } /** * Return an instance with the specified query string. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified query string. * * Users can provide both encoded and decoded query characters. * Implementations ensure the correct encoding as outlined in getQuery(). * * An empty query string value is equivalent to removing the query string. * * @param string $query The query string to use with the new instance. * * @return self A new instance with the specified query string. * * @throws InvalidArgumentException For invalid query strings. */ public function withQuery($query) { if (!is_string($query) && !method_exists($query, '__toString')) { throw new InvalidArgumentException('Uri query must be a string'); } $query = ltrim((string)$query, '?'); $clone = clone $this; $clone->query = $this->filterQuery($query); return $clone; } /** * Filters the query string or fragment of a URI. * * @param string $query The raw uri query string. * * @return string The percent-encoded query string. */ protected function filterQuery($query) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/', function ($match) { return rawurlencode($match[0]); }, $query ); } /** * Retrieve the fragment component of the URI. * * If no fragment is present, this method MUST return an empty string. * * The leading "#" character is not part of the fragment and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.5. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.5 * * @return string The URI fragment. */ public function getFragment() { return $this->fragment; } /** * Return an instance with the specified URI fragment. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified URI fragment. * * Users can provide both encoded and decoded fragment characters. * Implementations ensure the correct encoding as outlined in getFragment(). * * An empty fragment value is equivalent to removing the fragment. * * @param string $fragment The fragment to use with the new instance. * * @return static A new instance with the specified fragment. */ public function withFragment($fragment) { if (!is_string($fragment) && !method_exists($fragment, '__toString')) { throw new InvalidArgumentException('Uri fragment must be a string'); } $fragment = ltrim((string)$fragment, '#'); $clone = clone $this; $clone->fragment = $this->filterQuery($fragment); return $clone; } /** * Return the string representation as a URI reference. * * Depending on which components of the URI are present, the resulting * string is either a full URI or relative reference according to RFC 3986, * Section 4.1. The method concatenates the various components of the URI, * using the appropriate delimiters: * * - If a scheme is present, it MUST be suffixed by ":". * - If an authority is present, it MUST be prefixed by "//". * - The path can be concatenated without delimiters. But there are two * cases where the path has to be adjusted to make the URI reference * valid as PHP does not allow to throw an exception in __toString(): * - If the path is rootless and an authority is present, the path MUST * be prefixed by "/". * - If the path is starting with more than one "/" and no authority is * present, the starting slashes MUST be reduced to one. * - If a query is present, it MUST be prefixed by "?". * - If a fragment is present, it MUST be prefixed by "#". * * @see http://tools.ietf.org/html/rfc3986#section-4.1 * * @return string */ public function __toString() { $scheme = $this->getScheme(); $authority = $this->getAuthority(); $basePath = $this->getBasePath(); $path = $this->getPath(); $query = $this->getQuery(); $fragment = $this->getFragment(); $path = $basePath . '/' . ltrim($path, '/'); return ($scheme !== '' ? $scheme . ':' : '') . ($authority !== '' ? '//' . $authority : '') . $path . ($query !== '' ? '?' . $query : '') . ($fragment !== '' ? '#' . $fragment : ''); } /** * Return the fully qualified base URL. * * Note that this method never includes a trailing / * * This method is not part of PSR-7. * * @return string */ public function getBaseUrl() { $scheme = $this->getScheme(); $authority = $this->getAuthority(); $basePath = $this->getBasePath(); if ($authority !== '' && substr($basePath, 0, 1) !== '/') { $basePath = $basePath . '/' . $basePath; } return ($scheme !== '' ? $scheme . ':' : '') . ($authority ? '//' . $authority : '') . rtrim($basePath, '/'); } } slim/Slim/DeferredCallable.php 0000666 00000002213 15165376252 0012303 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use AmeliaPsr\Container\ContainerInterface; class DeferredCallable { use CallableResolverAwareTrait; /** * @var callable|string */ private $callable; /** * @var ContainerInterface */ private $container; /** * @param callable|string $callable * @param ContainerInterface $container */ public function __construct($callable, ContainerInterface $container = null) { $this->callable = $callable; $this->container = $container; } /** * @return callable|string */ public function getCallable() { return $this->callable; } /** * @return mixed */ public function __invoke() { $callable = $this->resolveCallable($this->callable); if ($callable instanceof Closure) { $callable = $callable->bindTo($this->container); } $args = func_get_args(); return call_user_func_array($callable, $args); } } slim/Slim/DefaultServicesProvider.php 0000666 00000014410 15165376252 0013750 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Handlers\Error; use Slim\Handlers\NotAllowed; use Slim\Handlers\NotFound; use Slim\Handlers\PhpError; use Slim\Handlers\Strategies\RequestResponse; use Slim\Http\Environment; use Slim\Http\Headers; use Slim\Http\Request; use Slim\Http\Response; use Slim\Interfaces\CallableResolverInterface; use Slim\Interfaces\InvocationStrategyInterface; use Slim\Interfaces\RouterInterface; class DefaultServicesProvider { /** * Register Slim's default services. * * @param Container $container A DI container implementing ArrayAccess and psr/container. */ public function register($container) { if (!isset($container['environment'])) { /** * This service MUST return a shared instance of \Slim\Http\Environment. * * @return Environment */ $container['environment'] = function () { return new Environment($_SERVER); }; } if (!isset($container['request'])) { /** * PSR-7 Request object * * @param Container $container * * @return ServerRequestInterface */ $container['request'] = function ($container) { return Request::createFromEnvironment($container->get('environment')); }; } if (!isset($container['response'])) { /** * PSR-7 Response object * * @param Container $container * * @return ResponseInterface */ $container['response'] = function ($container) { $headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']); $response = new Response(200, $headers); return $response->withProtocolVersion($container->get('settings')['httpVersion']); }; } if (!isset($container['router'])) { /** * This service MUST return a shared instance of \Slim\Interfaces\RouterInterface. * * @param Container $container * * @return RouterInterface */ $container['router'] = function ($container) { $routerCacheFile = false; if (isset($container->get('settings')['routerCacheFile'])) { $routerCacheFile = $container->get('settings')['routerCacheFile']; } $router = (new Router)->setCacheFile($routerCacheFile); if (method_exists($router, 'setContainer')) { $router->setContainer($container); } return $router; }; } if (!isset($container['foundHandler'])) { /** * This service MUST return a SHARED instance InvocationStrategyInterface. * * @return InvocationStrategyInterface */ $container['foundHandler'] = function () { return new RequestResponse; }; } if (!isset($container['phpErrorHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * 3. Instance of Error * * The callable MUST return an instance of ResponseInterface. * * @param Container $container * * @return callable */ $container['phpErrorHandler'] = function ($container) { return new PhpError($container->get('settings')['displayErrorDetails']); }; } if (!isset($container['errorHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of \AmeliaPsr\Http\Message\ServerRequestInterface * 2. Instance of \AmeliaPsr\Http\Message\ResponseInterface * 3. Instance of \Exception * * The callable MUST return an instance of ResponseInterface. * * @param Container $container * * @return callable */ $container['errorHandler'] = function ($container) { return new Error( $container->get('settings')['displayErrorDetails'] ); }; } if (!isset($container['notFoundHandler'])) { /** * This service MUST return a callable that accepts two arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * * The callable MUST return an instance of ResponseInterface. * * @return callable */ $container['notFoundHandler'] = function () { return new NotFound; }; } if (!isset($container['notAllowedHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * 3. Array of allowed HTTP methods * * The callable MUST return an instance of ResponseInterface. * * @return callable */ $container['notAllowedHandler'] = function () { return new NotAllowed; }; } if (!isset($container['callableResolver'])) { /** * Instance of CallableResolverInterface * * @param Container $container * * @return CallableResolverInterface */ $container['callableResolver'] = function ($container) { return new CallableResolver($container); }; } } } slim/Slim/Router.php 0000666 00000027321 15165376252 0010432 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser; use FastRoute\RouteParser\Std as StdParser; use InvalidArgumentException; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Interfaces\RouteInterface; use Slim\Interfaces\RouterInterface; /** * This class organizes Slim application route objects. It is responsible * for registering route objects, assigning names to route objects, * finding routes that match the current HTTP request, and creating * URLs for a named route. */ class Router implements RouterInterface { /** * Container Interface * * @var ContainerInterface */ protected $container; /** * Parser * * @var RouteParser */ protected $routeParser; /** * Base path used in pathFor() * * @var string */ protected $basePath = ''; /** * Path to fast route cache file. Set to false to disable route caching * * @var string|False */ protected $cacheFile = false; /** * Routes * * @var Route[] */ protected $routes = []; /** * Route counter incrementer * @var int */ protected $routeCounter = 0; /** * Route groups * * @var RouteGroup[] */ protected $routeGroups = []; /** * @var Dispatcher */ protected $dispatcher; /** * @param RouteParser $parser */ public function __construct(RouteParser $parser = null) { $this->routeParser = $parser ?: new StdParser; } /** * Set the base path used in pathFor() * * @param string $basePath * * @return static * @throws InvalidArgumentException */ public function setBasePath($basePath) { if (!is_string($basePath)) { throw new InvalidArgumentException('Router basePath must be a string'); } $this->basePath = $basePath; return $this; } /** * Get the base path used in pathFor() * * @return string */ public function getBasePath() { return $this->basePath; } /** * Set path to fast route cache file. If this is false then route caching is disabled. * * @param string|false $cacheFile * * @return static * * @throws InvalidArgumentException If cacheFile is not a string or not false * @throws RuntimeException If cacheFile directory is not writable */ public function setCacheFile($cacheFile) { if (!is_string($cacheFile) && $cacheFile !== false) { throw new InvalidArgumentException('Router cache file must be a string or false'); } if ($cacheFile && file_exists($cacheFile) && !is_readable($cacheFile)) { throw new RuntimeException( sprintf('Router cache file `%s` is not readable', $cacheFile) ); } if ($cacheFile && !file_exists($cacheFile) && !is_writable(dirname($cacheFile))) { throw new RuntimeException( sprintf('Router cache file directory `%s` is not writable', dirname($cacheFile)) ); } $this->cacheFile = $cacheFile; return $this; } /** * @param ContainerInterface $container */ public function setContainer(ContainerInterface $container) { $this->container = $container; } /** * {@inheritdoc} */ public function map($methods, $pattern, $handler) { if (!is_string($pattern)) { throw new InvalidArgumentException('Route pattern must be a string'); } // Prepend parent group pattern(s) if ($this->routeGroups) { $pattern = $this->processGroups() . $pattern; } // According to RFC methods are defined in uppercase (See RFC 7231) $methods = array_map("strtoupper", $methods); /** @var Route $route */ $route = $this->createRoute($methods, $pattern, $handler); $this->routes[$route->getIdentifier()] = $route; $this->routeCounter++; return $route; } /** * {@inheritdoc} */ public function dispatch(ServerRequestInterface $request) { $uri = '/' . ltrim($request->getUri()->getPath(), '/'); return $this->createDispatcher()->dispatch( $request->getMethod(), $uri ); } /** * Create a new Route object * * @param string[] $methods Array of HTTP methods * @param string $pattern The route pattern * @param callable $callable The route callable * * @return RouteInterface */ protected function createRoute($methods, $pattern, $callable) { $route = new Route($methods, $pattern, $callable, $this->routeGroups, $this->routeCounter); if (!empty($this->container)) { $route->setContainer($this->container); } return $route; } /** * @return Dispatcher */ protected function createDispatcher() { if ($this->dispatcher) { return $this->dispatcher; } $routeDefinitionCallback = function (RouteCollector $r) { foreach ($this->getRoutes() as $route) { $r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier()); } }; if ($this->cacheFile) { $this->dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [ 'routeParser' => $this->routeParser, 'cacheFile' => $this->cacheFile, ]); } else { $this->dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [ 'routeParser' => $this->routeParser, ]); } return $this->dispatcher; } /** * @param Dispatcher $dispatcher */ public function setDispatcher(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } /** * Get route objects * * @return Route[] */ public function getRoutes() { return $this->routes; } /** * {@inheritdoc} */ public function getNamedRoute($name) { foreach ($this->routes as $route) { if ($name == $route->getName()) { return $route; } } throw new RuntimeException('Named route does not exist for name: ' . $name); } /** * Remove named route * * @param string $name Route name * * @throws RuntimeException If named route does not exist */ public function removeNamedRoute($name) { $route = $this->getNamedRoute($name); // no exception, route exists, now remove by id unset($this->routes[$route->getIdentifier()]); } /** * Process route groups * * @return string A group pattern to prefix routes with */ protected function processGroups() { $pattern = ""; foreach ($this->routeGroups as $group) { $pattern .= $group->getPattern(); } return $pattern; } /** * {@inheritdoc} */ public function pushGroup($pattern, $callable) { $group = new RouteGroup($pattern, $callable); array_push($this->routeGroups, $group); return $group; } /** * {@inheritdoc} */ public function popGroup() { $group = array_pop($this->routeGroups); return $group instanceof RouteGroup ? $group : false; } /** * {@inheritdoc} */ public function lookupRoute($identifier) { if (!isset($this->routes[$identifier])) { throw new RuntimeException('Route not found, looks like your route cache is stale.'); } return $this->routes[$identifier]; } /** * {@inheritdoc} */ public function relativePathFor($name, array $data = [], array $queryParams = []) { $route = $this->getNamedRoute($name); $pattern = $route->getPattern(); $routeDatas = $this->routeParser->parse($pattern); // $routeDatas is an array of all possible routes that can be made. There is // one routedata for each optional parameter plus one for no optional parameters. // // The most specific is last, so we look for that first. $routeDatas = array_reverse($routeDatas); $segments = []; $segmentName = ''; foreach ($routeDatas as $routeData) { foreach ($routeData as $item) { if (is_string($item)) { // this segment is a static string $segments[] = $item; continue; } // This segment has a parameter: first element is the name if (!array_key_exists($item[0], $data)) { // we don't have a data element for this segment: cancel // testing this routeData item, so that we can try a less // specific routeData item. $segments = []; $segmentName = $item[0]; break; } $segments[] = $data[$item[0]]; } if (!empty($segments)) { // we found all the parameters for this route data, no need to check // less specific ones break; } } if (empty($segments)) { throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName); } $url = implode('', $segments); $hasQueryParams = array_filter($queryParams, function ($value) { return $value !== null; }) !== []; if ($hasQueryParams) { $url .= '?' . http_build_query($queryParams); } return $url; } /** * {@inheritdoc} */ public function pathFor($name, array $data = [], array $queryParams = []) { return $this->urlFor($name, $data, $queryParams); } /** * Build the path for a named route including the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function urlFor($name, array $data = [], array $queryParams = []) { $url = $this->relativePathFor($name, $data, $queryParams); if ($this->basePath) { $url = $this->basePath . $url; } return $url; } /** * Get fully qualified URL for named route * * @param UriInterface $uri * @param string $routeName * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function fullUrlFor(UriInterface $uri, $routeName, array $data = [], array $queryParams = []) { $path = $this->urlFor($routeName, $data, $queryParams); $scheme = $uri->getScheme(); $authority = $uri->getAuthority(); $protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : ''); return $protocol . $path; } } slim/Slim/Handlers/AbstractHandler.php 0000666 00000003031 15165376252 0013743 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ServerRequestInterface; abstract class AbstractHandler { /** * Known handled content types * * @var array */ protected $knownContentTypes = [ 'application/json', 'application/xml', 'text/xml', 'text/html', ]; /** * Determine which content type we know about is wanted using `Accept` header * * Note: This method is a bare-bones implementation designed specifically for * Slim's error handling requirements. Consider a fully-feature solution such * as willdurand/negotiation for any other situation. * * @param ServerRequestInterface $request * * @return string */ protected function determineContentType(ServerRequestInterface $request) { $acceptHeader = $request->getHeaderLine('Accept'); $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes); if (count($selectedContentTypes)) { return current($selectedContentTypes); } // handle +json and +xml specially if (preg_match('/\+(json|xml)/', $acceptHeader, $matches)) { $mediaType = 'application/' . $matches[1]; if (in_array($mediaType, $this->knownContentTypes)) { return $mediaType; } } return 'text/html'; } } slim/Slim/Handlers/Strategies/RequestResponseArgs.php 0000666 00000002200 15165376252 0016775 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers\Strategies; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Interfaces\InvocationStrategyInterface; /** * Route callback strategy with route parameters as individual arguments. */ class RequestResponseArgs implements InvocationStrategyInterface { /** * Invoke a route callable with request, response and all route parameters * as individual arguments. * * @param array|callable $callable * @param ServerRequestInterface $request * @param ResponseInterface $response * @param array $routeArguments * * @return ResponseInterface */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ) { array_unshift($routeArguments, $request, $response); return call_user_func_array($callable, $routeArguments); } } slim/Slim/Handlers/Strategies/RequestResponse.php 0000666 00000002312 15165376252 0016164 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers\Strategies; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Interfaces\InvocationStrategyInterface; /** * Default route callback strategy with route parameters as an array of arguments. */ class RequestResponse implements InvocationStrategyInterface { /** * Invoke a route callable with request, response, and all route parameters * as an array of arguments. * * @param array|callable $callable * @param ServerRequestInterface $request * @param ResponseInterface $response * @param array $routeArguments * * @return ResponseInterface */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ) { foreach ($routeArguments as $k => $v) { $request = $request->withAttribute($k, $v); } return call_user_func($callable, $request, $response, $routeArguments); } } slim/Slim/Handlers/Error.php 0000666 00000015534 15165376252 0012006 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; use Slim\Http\Body; use UnexpectedValueException; class Error extends AbstractError { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param Exception $exception The caught Exception object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Exception $exception) { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonErrorMessage($exception); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlErrorMessage($exception); break; case 'text/html': $output = $this->renderHtmlErrorMessage($exception); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } $this->writeToErrorLog($exception); $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response ->withStatus(500) ->withHeader('Content-type', $contentType) ->withBody($body); } /** * Render HTML error page * * @param Exception $exception * * @return string */ protected function renderHtmlErrorMessage(Exception $exception) { $title = 'Slim Application Error'; if ($this->displayErrorDetails) { $html = '<p>The application could not run because of the following error:</p>'; $html .= '<h2>Details</h2>'; $html .= $this->renderHtmlException($exception); while ($exception = $exception->getPrevious()) { $html .= '<h2>Previous exception</h2>'; $html .= $this->renderHtmlExceptionOrError($exception); } } else { $html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>'; } $output = sprintf( "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," . "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" . "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>", $title, $title, $html ); return $output; } /** * Render exception as HTML. * * Provided for backwards compatibility; use renderHtmlExceptionOrError(). * * @param Exception $exception * * @return string */ protected function renderHtmlException(Exception $exception) { return $this->renderHtmlExceptionOrError($exception); } /** * Render exception or error as HTML. * * @param Exception|\Error $exception * * @return string * * @throws RuntimeException */ protected function renderHtmlExceptionOrError($exception) { if (!$exception instanceof Exception && !$exception instanceof \Error) { throw new RuntimeException("Unexpected type. Expected Exception or Error."); } $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception)); if (($code = $exception->getCode())) { $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); } if (($message = $exception->getMessage())) { $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message)); } if (($file = $exception->getFile())) { $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); } if (($line = $exception->getLine())) { $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); } if (($trace = $exception->getTraceAsString())) { $html .= '<h2>Trace</h2>'; $html .= sprintf('<pre>%s</pre>', htmlentities($trace)); } return $html; } /** * Render JSON error * * @param Exception $exception * * @return string */ protected function renderJsonErrorMessage(Exception $exception) { $error = [ 'message' => 'Slim Application Error', ]; if ($this->displayErrorDetails) { $error['exception'] = []; do { $error['exception'][] = [ 'type' => get_class($exception), 'code' => $exception->getCode(), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => explode("\n", $exception->getTraceAsString()), ]; } while ($exception = $exception->getPrevious()); } return json_encode($error, JSON_PRETTY_PRINT); } /** * Render XML error * * @param Exception $exception * * @return string */ protected function renderXmlErrorMessage(Exception $exception) { $xml = "<error>\n <message>Slim Application Error</message>\n"; if ($this->displayErrorDetails) { do { $xml .= " <exception>\n"; $xml .= " <type>" . get_class($exception) . "</type>\n"; $xml .= " <code>" . $exception->getCode() . "</code>\n"; $xml .= " <message>" . $this->createCdataSection($exception->getMessage()) . "</message>\n"; $xml .= " <file>" . $exception->getFile() . "</file>\n"; $xml .= " <line>" . $exception->getLine() . "</line>\n"; $xml .= " <trace>" . $this->createCdataSection($exception->getTraceAsString()) . "</trace>\n"; $xml .= " </exception>\n"; } while ($exception = $exception->getPrevious()); } $xml .= "</error>"; return $xml; } /** * Returns a CDATA section with the given content. * * @param string $content * * @return string */ private function createCdataSection($content) { return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content)); } } slim/Slim/Handlers/NotAllowed.php 0000666 00000007466 15165376252 0012772 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use UnexpectedValueException; class NotAllowed extends AbstractHandler { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param string[] $methods Allowed HTTP methods * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $methods) { if ($request->getMethod() === 'OPTIONS') { $status = 200; $contentType = 'text/plain'; $output = $this->renderPlainOptionsMessage($methods); } else { $status = 405; $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonNotAllowedMessage($methods); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlNotAllowedMessage($methods); break; case 'text/html': $output = $this->renderHtmlNotAllowedMessage($methods); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } } $body = new Body(fopen('php://temp', 'r+')); $body->write($output); $allow = implode(', ', $methods); return $response ->withStatus($status) ->withHeader('Content-type', $contentType) ->withHeader('Allow', $allow) ->withBody($body); } /** * Render plain message for OPTIONS response * * @param string[] $methods * * @return string */ protected function renderPlainOptionsMessage($methods) { $allow = implode(', ', $methods); return 'Allowed methods: ' . $allow; } /** * Render JSON not allowed message * * @param string[] $methods * * @return string */ protected function renderJsonNotAllowedMessage($methods) { $allow = implode(', ', $methods); return '{"message":"Method not allowed. Must be one of: ' . $allow . '"}'; } /** * Render XML not allowed message * * @param string[] $methods * * @return string */ protected function renderXmlNotAllowedMessage($methods) { $allow = implode(', ', $methods); return "<root><message>Method not allowed. Must be one of: $allow</message></root>"; } /** * Render HTML not allowed message * * @param string[] $methods * * @return string */ protected function renderHtmlNotAllowedMessage($methods) { $allow = implode(', ', $methods); $output = <<<END <html> <head> <title>Method not allowed</title> <style> body{ margin:0; padding:30px; font:12px/1.5 Helvetica,Arial,Verdana,sans-serif; } h1{ margin:0; font-size:48px; font-weight:normal; line-height:48px; } </style> </head> <body> <h1>Method not allowed</h1> <p>Method not allowed. Must be one of: <strong>$allow</strong></p> </body> </html> END; return $output; } } slim/Slim/Handlers/NotFound.php 0000666 00000007165 15165376252 0012452 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use UnexpectedValueException; class NotFound extends AbstractHandler { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { if ($request->getMethod() === 'OPTIONS') { $contentType = 'text/plain'; $output = $this->renderPlainNotFoundOutput(); } else { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonNotFoundOutput(); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlNotFoundOutput(); break; case 'text/html': $output = $this->renderHtmlNotFoundOutput($request); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } } $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response->withStatus(404) ->withHeader('Content-Type', $contentType) ->withBody($body); } /** * Render plain not found message * * @return string */ protected function renderPlainNotFoundOutput() { return 'Not found'; } /** * Return a response for application/json content not found * * @return string */ protected function renderJsonNotFoundOutput() { return '{"message":"Not found"}'; } /** * Return a response for xml content not found * * @return string */ protected function renderXmlNotFoundOutput() { return '<root><message>Not found</message></root>'; } /** * Return a response for text/html content not found * * @param ServerRequestInterface $request The most recent Request object * * @return string */ protected function renderHtmlNotFoundOutput(ServerRequestInterface $request) { $homeUrl = (string)($request->getUri()->withPath('')->withQuery('')->withFragment('')); return <<<END <html> <head> <title>Page Not Found</title> <style> body{ margin:0; padding:30px; font:12px/1.5 Helvetica,Arial,Verdana,sans-serif; } h1{ margin:0; font-size:48px; font-weight:normal; line-height:48px; } strong{ display:inline-block; width:65px; } </style> </head> <body> <h1>Page Not Found</h1> <p> The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below. </p> <a href='$homeUrl'>Visit the Home Page</a> </body> </html> END; } } slim/Slim/Handlers/PhpError.php 0000666 00000014066 15165376252 0012455 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use Throwable; use UnexpectedValueException; class PhpError extends AbstractError { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param Throwable $error The caught Throwable object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Throwable $error) { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonErrorMessage($error); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlErrorMessage($error); break; case 'text/html': $output = $this->renderHtmlErrorMessage($error); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } $this->writeToErrorLog($error); $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response ->withStatus(500) ->withHeader('Content-type', $contentType) ->withBody($body); } /** * Render HTML error page * * @param Throwable $error * * @return string */ protected function renderHtmlErrorMessage(Throwable $error) { $title = 'Slim Application Error'; if ($this->displayErrorDetails) { $html = '<p>The application could not run because of the following error:</p>'; $html .= '<h2>Details</h2>'; $html .= $this->renderHtmlError($error); while ($error = $error->getPrevious()) { $html .= '<h2>Previous error</h2>'; $html .= $this->renderHtmlError($error); } } else { $html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>'; } $output = sprintf( "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," . "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" . "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>", $title, $title, $html ); return $output; } /** * Render error as HTML. * * @param Throwable $error * * @return string */ protected function renderHtmlError(Throwable $error) { $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($error)); if (($code = $error->getCode())) { $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); } if (($message = $error->getMessage())) { $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message)); } if (($file = $error->getFile())) { $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); } if (($line = $error->getLine())) { $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); } if (($trace = $error->getTraceAsString())) { $html .= '<h2>Trace</h2>'; $html .= sprintf('<pre>%s</pre>', htmlentities($trace)); } return $html; } /** * Render JSON error * * @param Throwable $error * * @return string */ protected function renderJsonErrorMessage(Throwable $error) { $json = [ 'message' => 'Slim Application Error', ]; if ($this->displayErrorDetails) { $json['error'] = []; do { $json['error'][] = [ 'type' => get_class($error), 'code' => $error->getCode(), 'message' => $error->getMessage(), 'file' => $error->getFile(), 'line' => $error->getLine(), 'trace' => explode("\n", $error->getTraceAsString()), ]; } while ($error = $error->getPrevious()); } return json_encode($json, JSON_PRETTY_PRINT); } /** * Render XML error * * @param Throwable $error * * @return string */ protected function renderXmlErrorMessage(Throwable $error) { $xml = "<error>\n <message>Slim Application Error</message>\n"; if ($this->displayErrorDetails) { do { $xml .= " <error>\n"; $xml .= " <type>" . get_class($error) . "</type>\n"; $xml .= " <code>" . $error->getCode() . "</code>\n"; $xml .= " <message>" . $this->createCdataSection($error->getMessage()) . "</message>\n"; $xml .= " <file>" . $error->getFile() . "</file>\n"; $xml .= " <line>" . $error->getLine() . "</line>\n"; $xml .= " <trace>" . $this->createCdataSection($error->getTraceAsString()) . "</trace>\n"; $xml .= " </error>\n"; } while ($error = $error->getPrevious()); } $xml .= "</error>"; return $xml; } /** * Returns a CDATA section with the given content. * * @param string $content * * @return string */ private function createCdataSection($content) { return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content)); } } slim/Slim/Handlers/AbstractError.php 0000666 00000004560 15165376252 0013467 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use Exception; use Throwable; abstract class AbstractError extends AbstractHandler { /** * @var bool */ protected $displayErrorDetails; /** * @param bool $displayErrorDetails Set to true to display full details */ public function __construct($displayErrorDetails = false) { $this->displayErrorDetails = (bool) $displayErrorDetails; } /** * Write to the error log if displayErrorDetails is false * * @param Exception|Throwable $throwable * * @return void */ protected function writeToErrorLog($throwable) { if ($this->displayErrorDetails) { return; } $message = 'Slim Application Error:' . PHP_EOL; $message .= $this->renderThrowableAsText($throwable); while ($throwable = $throwable->getPrevious()) { $message .= PHP_EOL . 'Previous error:' . PHP_EOL; $message .= $this->renderThrowableAsText($throwable); } $message .= PHP_EOL . 'View in rendered output by enabling the "displayErrorDetails" setting.' . PHP_EOL; $this->logError($message); } /** * Render error as Text. * * @param Exception|Throwable $throwable * * @return string */ protected function renderThrowableAsText($throwable) { $text = sprintf('Type: %s' . PHP_EOL, get_class($throwable)); if ($code = $throwable->getCode()) { $text .= sprintf('Code: %s' . PHP_EOL, $code); } if ($message = $throwable->getMessage()) { $text .= sprintf('Message: %s' . PHP_EOL, htmlentities($message)); } if ($file = $throwable->getFile()) { $text .= sprintf('File: %s' . PHP_EOL, $file); } if ($line = $throwable->getLine()) { $text .= sprintf('Line: %s' . PHP_EOL, $line); } if ($trace = $throwable->getTraceAsString()) { $text .= sprintf('Trace: %s', $trace); } return $text; } /** * Wraps the error_log function so that this can be easily tested * * @param string $message */ protected function logError($message) { error_log($message); } } slim/Slim/CallableResolverAwareTrait.php 0000666 00000002160 15165376252 0014351 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use AmeliaPsr\Container\ContainerInterface; use RuntimeException; use Slim\Interfaces\CallableResolverInterface; /** * This is an internal class that enables resolution of 'class:method' strings * into a closure. This class is an implementation detail and is used only inside * of the Slim application. */ trait CallableResolverAwareTrait { /** * Resolve a string of the format 'class:method' into a closure that the * router can dispatch. * * @param callable|string $callable * * @return Closure * * @throws RuntimeException If the string cannot be resolved as a callable */ protected function resolveCallable($callable) { if (!$this->container instanceof ContainerInterface) { return $callable; } /** @var CallableResolverInterface $resolver */ $resolver = $this->container->get('callableResolver'); return $resolver->resolve($callable); } } slim/composer.json 0000666 00000003164 15165376252 0010256 0 ustar 00 { "name": "slim/slim", "type": "library", "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", "keywords": ["framework","micro","api","router"], "homepage": "https://slimframework.com", "license": "MIT", "authors": [ { "name": "Josh Lockhart", "email": "hello@joshlockhart.com", "homepage": "https://joshlockhart.com" }, { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", "homepage": "http://silentworks.co.uk" }, { "name": "Rob Allen", "email": "rob@akrabat.com", "homepage": "http://akrabat.com" }, { "name": "Gabriel Manricks", "email": "gmanricks@me.com", "homepage": "http://gabrielmanricks.com" } ], "require": { "php": ">=5.5.0", "ext-json": "*", "ext-libxml": "*", "ext-simplexml": "*", "pimple/pimple": "^3.0", "psr/http-message": "^1.0", "nikic/fast-route": "^1.0", "psr/container": "^1.0" }, "require-dev": { "squizlabs/php_codesniffer": "^2.5", "phpunit/phpunit": "^4.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "autoload": { "psr-4": { "Slim\\": "Slim" } }, "autoload-dev": { "psr-4": { "Slim\\Tests\\": "tests" } }, "scripts": { "test": [ "phpunit", "phpcs" ] } } MAINTAINERS.md 0000666 00000001662 15165376503 0006664 0 ustar 00 # Maintainers There aren't many rules for maintainers of Slim to remember; what we have is listed here. ## We don't merge our own PRs Our code is better if more than one set of eyes looks at it. Therefore we do not merge our own pull requests unless there is an exceptional circumstance. This helps to spot errors in the patch and also enables us to share information about the project around the maintainer team. ## PRs tagged `WIP` are not ready to be merged Sometimes it's helpful to collaborate on a patch before it's ready to be merged. We use the text `WIP` (for _Work in Progress_) in the title to mark these PRs. If a PR has `WIP` in its title, then it is not to be merged. The person who raised the PR will remove the `WIP` text when they are ready for a full review and merge. ## Assign a merged PR to a milestone By ensuring that all merged PRs are assigned to a milestone, we can easily find which PRs were in which release. LICENSE.md 0000666 00000002046 15165376503 0006171 0 ustar 00 Copyright (c) 2011-2019 Josh Lockhart 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. phpstan.neon.dist 0000666 00000000031 15165376503 0010055 0 ustar 00 parameters: level: 1 Slim/RouteGroup.php 0000666 00000001111 15165376503 0010305 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use Slim\Interfaces\RouteGroupInterface; class RouteGroup extends Routable implements RouteGroupInterface { /** * {@inheritdoc} */ public function __invoke(App $app = null) { $callable = $this->resolveCallable($this->callable); if ($callable instanceof Closure && $app !== null) { $callable = $callable->bindTo($app); } $callable($app); } } Slim/App.php 0000666 00000056720 15165376503 0006732 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use BadMethodCallException; use Closure; use Exception; use FastRoute\Dispatcher; use AmeliaPsr\Container\ContainerExceptionInterface as ContainerException; use InvalidArgumentException; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Http\Message\RequestInterface; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Exception\InvalidMethodException; use Slim\Exception\MethodNotAllowedException; use Slim\Exception\NotFoundException; use Slim\Exception\SlimException; use Slim\Http\Body; use Slim\Http\Headers; use Slim\Http\Request; use Slim\Http\Uri; use Slim\Interfaces\RouteGroupInterface; use Slim\Interfaces\RouteInterface; use Slim\Interfaces\RouterInterface; use Throwable; class App { use MiddlewareAwareTrait; /** * Current version * * @var string */ const VERSION = '3.12.3'; /** * @var ContainerInterface */ private $container; /** * @param ContainerInterface|array $container * * @throws InvalidArgumentException When no container is provided that implements ContainerInterface */ public function __construct($container = []) { if (is_array($container)) { $container = new Container($container); } if (!$container instanceof ContainerInterface) { throw new InvalidArgumentException('Expected a ContainerInterface'); } $this->container = $container; } /** * Get container * * @return ContainerInterface */ public function getContainer() { return $this->container; } /** * Add middleware * * This method prepends new middleware to the app's middleware stack. * * @param callable|string $callable The callback routine * * @return static */ public function add($callable) { return $this->addMiddleware(new DeferredCallable($callable, $this->container)); } /** * Calling a non-existent method on App checks to see if there's an item * in the container that is callable and if so, calls it. * * @param string $method * @param array $args * * @return mixed * * @throws BadMethodCallException */ public function __call($method, $args) { if ($this->container->has($method)) { $obj = $this->container->get($method); if (is_callable($obj)) { return call_user_func_array($obj, $args); } } throw new BadMethodCallException("Method $method is not a valid method"); } /** * Add GET route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function get($pattern, $callable) { return $this->map(['GET'], $pattern, $callable); } /** * Add POST route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function post($pattern, $callable) { return $this->map(['POST'], $pattern, $callable); } /** * Add PUT route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function put($pattern, $callable) { return $this->map(['PUT'], $pattern, $callable); } /** * Add PATCH route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function patch($pattern, $callable) { return $this->map(['PATCH'], $pattern, $callable); } /** * Add DELETE route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function delete($pattern, $callable) { return $this->map(['DELETE'], $pattern, $callable); } /** * Add OPTIONS route * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function options($pattern, $callable) { return $this->map(['OPTIONS'], $pattern, $callable); } /** * Add route for any HTTP method * * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function any($pattern, $callable) { return $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, $callable); } /** * Add route with multiple methods * * @param string[] $methods Numeric array of HTTP method names * @param string $pattern The route URI pattern * @param callable|string $callable The route callback routine * * @return RouteInterface */ public function map(array $methods, $pattern, $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this->container); } $route = $this->container->get('router')->map($methods, $pattern, $callable); if (is_callable([$route, 'setContainer'])) { $route->setContainer($this->container); } if (is_callable([$route, 'setOutputBuffering'])) { $route->setOutputBuffering($this->container->get('settings')['outputBuffering']); } return $route; } /** * Add a route that sends an HTTP redirect * * @param string $from * @param string|UriInterface $to * @param int $status * * @return RouteInterface */ public function redirect($from, $to, $status = 302) { $handler = function ($request, ResponseInterface $response) use ($to, $status) { return $response->withHeader('Location', (string)$to)->withStatus($status); }; return $this->get($from, $handler); } /** * Add a route group * * This method accepts a route pattern and a callback. All route * declarations in the callback will be prepended by the group(s) * that it is in. * * @param string $pattern * @param callable|Closure $callable * * @return RouteGroupInterface */ public function group($pattern, $callable) { /** @var RouterInterface $router */ $router = $this->container->get('router'); /** @var RouteGroup $group */ $group = $router->pushGroup($pattern, $callable); $group->setContainer($this->container); $group($this); $router->popGroup(); return $group; } /** * Run application * * This method traverses the application middleware stack and then sends the * resultant Response object to the HTTP client. * * @param bool|false $silent * * @return ResponseInterface * * @throws Exception * @throws Throwable */ public function run($silent = false) { $response = $this->container->get('response'); try { ob_start(); $response = $this->process($this->container->get('request'), $response); } catch (InvalidMethodException $e) { $response = $this->processInvalidMethod($e->getRequest(), $response); } finally { $output = ob_get_clean(); } if (!empty($output) && $response->getBody()->isWritable()) { $outputBuffering = $this->container->get('settings')['outputBuffering']; if ($outputBuffering === 'prepend') { // prepend output buffer content $body = new Http\Body(fopen('php://temp', 'r+')); $body->write($output . $response->getBody()); $response = $response->withBody($body); } elseif ($outputBuffering === 'append') { // append output buffer content $response->getBody()->write($output); } } $response = $this->finalize($response); if (!$silent) { $this->respond($response); } return $response; } /** * Pull route info for a request with a bad method to decide whether to * return a not-found error (default) or a bad-method error, then run * the handler for that error, returning the resulting response. * * Used for cases where an incoming request has an unrecognized method, * rather than throwing an exception and not catching it all the way up. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws ContainerException */ protected function processInvalidMethod(ServerRequestInterface $request, ResponseInterface $response) { $router = $this->container->get('router'); if (is_callable([$request->getUri(), 'getBasePath']) && is_callable([$router, 'setBasePath'])) { $router->setBasePath($request->getUri()->getBasePath()); } $request = $this->dispatchRouterAndPrepareRoute($request, $router); $routeInfo = $request->getAttribute('routeInfo', [RouterInterface::DISPATCH_STATUS => Dispatcher::NOT_FOUND]); if ($routeInfo[RouterInterface::DISPATCH_STATUS] === Dispatcher::METHOD_NOT_ALLOWED) { return $this->handleException( new MethodNotAllowedException($request, $response, $routeInfo[RouterInterface::ALLOWED_METHODS]), $request, $response ); } return $this->handleException(new NotFoundException($request, $response), $request, $response); } /** * Process a request * * This method traverses the application middleware stack and then returns the * resultant Response object. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Exception * @throws Throwable */ public function process(ServerRequestInterface $request, ResponseInterface $response) { // Ensure basePath is set $router = $this->container->get('router'); if (is_callable([$request->getUri(), 'getBasePath']) && is_callable([$router, 'setBasePath'])) { $router->setBasePath($request->getUri()->getBasePath()); } // Dispatch the Router first if the setting for this is on if ($this->container->get('settings')['determineRouteBeforeAppMiddleware'] === true) { // Dispatch router (note: you won't be able to alter routes after this) $request = $this->dispatchRouterAndPrepareRoute($request, $router); } // Traverse middleware stack try { $response = $this->callMiddlewareStack($request, $response); } catch (Exception $e) { $response = $this->handleException($e, $request, $response); } catch (Throwable $e) { $response = $this->handlePhpError($e, $request, $response); } return $response; } /** * Send the response to the client * * @param ResponseInterface $response */ public function respond(ResponseInterface $response) { // Send response if (!headers_sent()) { // Headers foreach ($response->getHeaders() as $name => $values) { $first = stripos($name, 'Set-Cookie') === 0 ? false : true; foreach ($values as $value) { header(sprintf('%s: %s', $name, $value), $first); $first = false; } } // Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers. // See https://github.com/slimphp/Slim/issues/1730 // Status header(sprintf( 'HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase() ), true, $response->getStatusCode()); } // Body $request = $this->container->get('request'); if (!$this->isEmptyResponse($response) && !$this->isHeadRequest($request)) { $body = $response->getBody(); if ($body->isSeekable()) { $body->rewind(); } $settings = $this->container->get('settings'); $chunkSize = $settings['responseChunkSize']; $contentLength = $response->getHeaderLine('Content-Length'); if (!$contentLength) { $contentLength = $body->getSize(); } if (isset($contentLength)) { $amountToRead = $contentLength; while ($amountToRead > 0 && !$body->eof()) { $data = $body->read(min((int)$chunkSize, (int)$amountToRead)); echo $data; $amountToRead -= strlen($data); if (connection_status() != CONNECTION_NORMAL) { break; } } } else { while (!$body->eof()) { echo $body->read((int)$chunkSize); if (connection_status() != CONNECTION_NORMAL) { break; } } } } } /** * Invoke application * * This method implements the middleware interface. It receives * Request and Response objects, and it returns a Response object * after compiling the routes registered in the Router and dispatching * the Request object to the appropriate Route callback routine. * * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * * @return ResponseInterface * * @throws MethodNotAllowedException * @throws NotFoundException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { // Get the route info $routeInfo = $request->getAttribute('routeInfo'); /** @var RouterInterface $router */ $router = $this->container->get('router'); // If router hasn't been dispatched or the URI changed then dispatch if (null === $routeInfo || ($routeInfo['request'] !== [$request->getMethod(), (string) $request->getUri()])) { $request = $this->dispatchRouterAndPrepareRoute($request, $router); $routeInfo = $request->getAttribute('routeInfo'); } if ($routeInfo[0] === Dispatcher::FOUND) { $route = $router->lookupRoute($routeInfo[1]); return $route->run($request, $response); } elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) { if (!$this->container->has('notAllowedHandler')) { throw new MethodNotAllowedException($request, $response, $routeInfo[1]); } /** @var callable $notAllowedHandler */ $notAllowedHandler = $this->container->get('notAllowedHandler'); return $notAllowedHandler($request, $response, $routeInfo[1]); } if (!$this->container->has('notFoundHandler')) { throw new NotFoundException($request, $response); } /** @var callable $notFoundHandler */ $notFoundHandler = $this->container->get('notFoundHandler'); return $notFoundHandler($request, $response); } /** * Perform a sub-request from within an application route * * This method allows you to prepare and initiate a sub-request, run within * the context of the current request. This WILL NOT issue a remote HTTP * request. Instead, it will route the provided URL, method, headers, * cookies, body, and server variables against the set of registered * application routes. The result response object is returned. * * @param string $method The request method (e.g., GET, POST, PUT, etc.) * @param string $path The request URI path * @param string $query The request URI query string * @param array $headers The request headers (key-value array) * @param array $cookies The request cookies (key-value array) * @param string $bodyContent The request body * @param ResponseInterface $response The response object (optional) * * @return ResponseInterface * * @throws MethodNotAllowedException * @throws NotFoundException */ public function subRequest( $method, $path, $query = '', array $headers = [], array $cookies = [], $bodyContent = '', ResponseInterface $response = null ) { $env = $this->container->get('environment'); $uri = Uri::createFromEnvironment($env)->withPath($path)->withQuery($query); $headers = new Headers($headers); $serverParams = $env->all(); $body = new Body(fopen('php://temp', 'r+')); $body->write($bodyContent); $body->rewind(); $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); if (!$response) { $response = $this->container->get('response'); } return $this($request, $response); } /** * Dispatch the router to find the route. Prepare the route for use. * * @param ServerRequestInterface $request * @param RouterInterface $router * * @return ServerRequestInterface */ protected function dispatchRouterAndPrepareRoute(ServerRequestInterface $request, RouterInterface $router) { $routeInfo = $router->dispatch($request); if ($routeInfo[0] === Dispatcher::FOUND) { $routeArguments = []; foreach ($routeInfo[2] as $k => $v) { $routeArguments[$k] = urldecode($v); } $route = $router->lookupRoute($routeInfo[1]); $route->prepare($request, $routeArguments); // add route to the request's attributes in case a middleware or handler needs access to the route $request = $request->withAttribute('route', $route); } $routeInfo['request'] = [$request->getMethod(), (string) $request->getUri()]; return $request->withAttribute('routeInfo', $routeInfo); } /** * Finalize response * * @param ResponseInterface $response * * @return ResponseInterface * * @throws RuntimeException */ protected function finalize(ResponseInterface $response) { // stop PHP sending a Content-Type automatically ini_set('default_mimetype', ''); $request = $this->container->get('request'); if ($this->isEmptyResponse($response) && !$this->isHeadRequest($request)) { return $response->withoutHeader('Content-Type')->withoutHeader('Content-Length'); } // Add Content-Length header if `addContentLengthHeader` setting is set if (isset($this->container->get('settings')['addContentLengthHeader']) && $this->container->get('settings')['addContentLengthHeader'] == true) { if (ob_get_length() > 0) { throw new RuntimeException("Unexpected data in output buffer. " . "Maybe you have characters before an opening <?php tag?"); } $size = $response->getBody()->getSize(); if ($size !== null && !$response->hasHeader('Content-Length')) { $response = $response->withHeader('Content-Length', (string) $size); } } // clear the body if this is a HEAD request if ($this->isHeadRequest($request)) { return $response->withBody(new Body(fopen('php://temp', 'r+'))); } return $response; } /** * Helper method, which returns true if the provided response must not output a body and false * if the response could have a body. * * @see https://tools.ietf.org/html/rfc7231 * * @param ResponseInterface $response * * @return bool */ protected function isEmptyResponse(ResponseInterface $response) { if (method_exists($response, 'isEmpty')) { return $response->isEmpty(); } return in_array($response->getStatusCode(), [204, 205, 304]); } /** * Helper method to check if the current request is a HEAD request * * @param RequestInterface $request * * @return bool */ protected function isHeadRequest(RequestInterface $request) { return strtoupper($request->getMethod()) === 'HEAD'; } /** * Call relevant handler from the Container if needed. If it doesn't exist, * then just re-throw. * * @param Exception $e * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Exception If a handler is needed and not found */ protected function handleException(Exception $e, ServerRequestInterface $request, ResponseInterface $response) { if ($e instanceof MethodNotAllowedException) { $handler = 'notAllowedHandler'; $params = [$e->getRequest(), $e->getResponse(), $e->getAllowedMethods()]; } elseif ($e instanceof NotFoundException) { $handler = 'notFoundHandler'; $params = [$e->getRequest(), $e->getResponse(), $e]; } elseif ($e instanceof SlimException) { // This is a Stop exception and contains the response return $e->getResponse(); } else { // Other exception, use $request and $response params $handler = 'errorHandler'; $params = [$request, $response, $e]; } if ($this->container->has($handler)) { $callable = $this->container->get($handler); // Call the registered handler return call_user_func_array($callable, $params); } // No handlers found, so just throw the exception throw $e; } /** * Call relevant handler from the Container if needed. If it doesn't exist, * then just re-throw. * * @param Throwable $e * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface * * @throws Throwable */ protected function handlePhpError(Throwable $e, ServerRequestInterface $request, ResponseInterface $response) { $handler = 'phpErrorHandler'; $params = [$request, $response, $e]; if ($this->container->has($handler)) { $callable = $this->container->get($handler); // Call the registered handler return call_user_func_array($callable, $params); } // No handlers found, so just throw the exception throw $e; } } Slim/Collection.php 0000666 00000006247 15165376503 0010304 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use ArrayIterator; use Slim\Interfaces\CollectionInterface; /** * Collection * * This class provides a common interface used by many other * classes in a Slim application that manage "collections" * of data that must be inspected and/or manipulated */ class Collection implements CollectionInterface { /** * The source data * * @var array */ protected $data = []; /** * @param array $items Pre-populate collection with this key-value array */ public function __construct(array $items = []) { $this->replace($items); } /** * {@inheritdoc} */ public function set($key, $value) { $this->data[$key] = $value; } /** * {@inheritdoc} */ public function get($key, $default = null) { return $this->has($key) ? $this->data[$key] : $default; } /** * {@inheritdoc} */ public function replace(array $items) { foreach ($items as $key => $value) { $this->set($key, $value); } } /** * {@inheritdoc} */ public function all() { return $this->data; } /** * Get collection keys * * @return array The collection's source data keys */ public function keys() { return array_keys($this->data); } /** * {@inheritdoc} */ public function has($key) { return array_key_exists($key, $this->data); } /** * {@inheritdoc} */ public function remove($key) { unset($this->data[$key]); } /** * {@inheritdoc} */ public function clear() { $this->data = []; } /** * Does this collection have a given key? * * @param string $key The data key * * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($key) { return $this->has($key); } /** * Get collection item for key * * @param string $key The data key * * @return mixed The key's value, or the default value */ #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->get($key); } /** * Set collection item * * @param string $key The data key * @param mixed $value The data value */ #[\ReturnTypeWillChange] public function offsetSet($key, $value) { $this->set($key, $value); } /** * Remove item from collection * * @param string $key The data key */ #[\ReturnTypeWillChange] public function offsetUnset($key) { $this->remove($key); } /** * Get number of items in collection * * @return int */ #[\ReturnTypeWillChange] public function count() { return count($this->data); } /** * Get collection iterator * * @return ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->data); } } Slim/Exception/NotFoundException.php 0000666 00000000333 15165376503 0013550 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; class NotFoundException extends SlimException { } Slim/Exception/SlimException.php 0000666 00000002031 15165376503 0012715 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; class SlimException extends Exception { /** * @var ServerRequestInterface */ protected $request; /** * @var ResponseInterface */ protected $response; /** * @param ServerRequestInterface $request * @param ResponseInterface $response */ public function __construct(ServerRequestInterface $request, ResponseInterface $response) { parent::__construct(); $this->request = $request; $this->response = $response; } /** * @return ServerRequestInterface */ public function getRequest() { return $this->request; } /** * @return ResponseInterface */ public function getResponse() { return $this->response; } } Slim/Exception/ContainerValueNotFoundException.php 0000666 00000000535 15165376503 0016414 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Container\NotFoundExceptionInterface; use RuntimeException; class ContainerValueNotFoundException extends RuntimeException implements NotFoundExceptionInterface { } Slim/Exception/MethodNotAllowedException.php 0000666 00000001623 15165376503 0015230 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; class MethodNotAllowedException extends SlimException { /** * @var string[] */ protected $allowedMethods; /** * @param ServerRequestInterface $request * @param ResponseInterface $response * @param string[] $allowedMethods */ public function __construct(ServerRequestInterface $request, ResponseInterface $response, array $allowedMethods) { parent::__construct($request, $response); $this->allowedMethods = $allowedMethods; } /** * @return string[] */ public function getAllowedMethods() { return $this->allowedMethods; } } Slim/Exception/InvalidMethodException.php 0000666 00000001510 15165376503 0014541 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; class InvalidMethodException extends InvalidArgumentException { /** * @var ServerRequestInterface */ protected $request; /** * @param ServerRequestInterface $request * @param string $method */ public function __construct(ServerRequestInterface $request, $method) { $this->request = $request; parent::__construct(sprintf('Unsupported HTTP method "%s" provided', $method)); } /** * @return ServerRequestInterface */ public function getRequest() { return $this->request; } } Slim/Exception/ContainerException.php 0000666 00000000542 15165376503 0013740 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Exception; use AmeliaPsr\Container\ContainerExceptionInterface; use InvalidArgumentException; class ContainerException extends InvalidArgumentException implements ContainerExceptionInterface { } Slim/Routable.php 0000666 00000003701 15165376503 0007756 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Container\ContainerInterface; abstract class Routable { use CallableResolverAwareTrait; /** * Route callable * * @var callable */ protected $callable; /** * Container * * @var ContainerInterface */ protected $container; /** * Route middleware * * @var callable[] */ protected $middleware = []; /** * Route pattern * * @var string */ protected $pattern; /** * @param string $pattern * @param callable $callable */ public function __construct($pattern, $callable) { $this->pattern = $pattern; $this->callable = $callable; } /** * Get the middleware registered for the group * * @return callable[] */ public function getMiddleware() { return $this->middleware; } /** * Get the route pattern * * @return string */ public function getPattern() { return $this->pattern; } /** * Set container for use with resolveCallable * * @param ContainerInterface $container * * @return static */ public function setContainer(ContainerInterface $container) { $this->container = $container; return $this; } /** * Prepend middleware to the middleware collection * * @param callable|string $callable The callback routine * * @return static */ public function add($callable) { $this->middleware[] = new DeferredCallable($callable, $this->container); return $this; } /** * Set the route pattern * * @param string $newPattern */ public function setPattern($newPattern) { $this->pattern = $newPattern; } } Slim/Container.php 0000666 00000013426 15165376503 0010130 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use ArrayAccess; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Container\ContainerExceptionInterface; use InvalidArgumentException; use Pimple\Container as PimpleContainer; use Slim\Exception\ContainerException as SlimContainerException; use Slim\Exception\ContainerValueNotFoundException; /** * Slim's default DI container is Pimple. * * Slim\App expects a container that implements AmeliaPsr\Container\ContainerInterface * with these service keys configured and ready for use: * * `settings` an array or instance of \ArrayAccess * `environment` an instance of \Slim\Http\Environment * `request` an instance of \AmeliaPsr\Http\Message\ServerRequestInterface * `response` an instance of \AmeliaPsr\Http\Message\ResponseInterface * `router` an instance of \Slim\Interfaces\RouterInterface * `foundHandler` an instance of \Slim\Interfaces\InvocationStrategyInterface * `errorHandler` a callable with the signature: function($request, $response, $exception) * `notFoundHandler` a callable with the signature: function($request, $response) * `notAllowedHandler` a callable with the signature: function($request, $response, $allowedHttpMethods) * `callableResolver` an instance of \Slim\Interfaces\CallableResolverInterface */ class Container extends PimpleContainer implements ContainerInterface { /** * Default settings * * @var array */ private $defaultSettings = [ 'httpVersion' => '1.1', 'responseChunkSize' => 4096, 'outputBuffering' => 'append', 'determineRouteBeforeAppMiddleware' => false, 'displayErrorDetails' => false, 'addContentLengthHeader' => true, 'routerCacheFile' => false, ]; /** * @param array $values The parameters or objects. */ public function __construct(array $values = []) { parent::__construct($values); $userSettings = isset($values['settings']) ? $values['settings'] : []; $this->registerDefaultServices($userSettings); } /** * This function registers the default services that Slim needs to work. * * All services are shared, they are registered such that the * same instance is returned on subsequent calls. * * @param array $userSettings Associative array of application settings * * @return void */ private function registerDefaultServices($userSettings) { $defaultSettings = $this->defaultSettings; /** * This service MUST return an array or an instance of ArrayAccess. * * @return array|ArrayAccess */ $this['settings'] = function () use ($userSettings, $defaultSettings) { return new Collection(array_merge($defaultSettings, $userSettings)); }; $defaultProvider = new DefaultServicesProvider(); $defaultProvider->register($this); } /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @return mixed * * @throws InvalidArgumentException Thrown when an offset cannot be found in the Pimple container * @throws SlimContainerException Thrown when an exception is * not an instance of ContainerExceptionInterface * @throws ContainerValueNotFoundException No entry was found for this identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. */ public function get($id) { if (!$this->offsetExists($id)) { throw new ContainerValueNotFoundException(sprintf('Identifier "%s" is not defined.', $id)); } try { return $this->offsetGet($id); } catch (InvalidArgumentException $exception) { if ($this->exceptionThrownByContainer($exception)) { throw new SlimContainerException( sprintf('Container error while retrieving "%s"', $id), null, $exception ); } else { throw $exception; } } } /** * Tests whether an exception needs to be recast for compliance with psr/container. This will be if the * exception was thrown by Pimple. * * @param InvalidArgumentException $exception * * @return bool */ private function exceptionThrownByContainer(InvalidArgumentException $exception) { $trace = $exception->getTrace()[0]; return $trace['class'] === PimpleContainer::class && $trace['function'] === 'offsetGet'; } /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * @param string $id Identifier of the entry to look for. * * @return boolean */ public function has($id) { return $this->offsetExists($id); } /** * @param string $name * * @return mixed * * @throws InvalidArgumentException Thrown when an offset cannot be found in the Pimple container * @throws SlimContainerException Thrown when an exception is not * an instance of ContainerExceptionInterface * @throws ContainerValueNotFoundException No entry was found for this identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. */ public function __get($name) { return $this->get($name); } /** * @param string $name * @return bool */ public function __isset($name) { return $this->has($name); } } Slim/MiddlewareAwareTrait.php 0000666 00000006544 15165376503 0012252 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; use UnexpectedValueException; /** * Middleware * * This is an internal class that enables concentric middleware layers. This * class is an implementation detail and is used only inside of the Slim * application; it is not visible to—and should not be used by—end users. */ trait MiddlewareAwareTrait { /** * Tip of the middleware call stack * * @var callable */ protected $tip; /** * Middleware stack lock * * @var bool */ protected $middlewareLock = false; /** * Add middleware * * This method prepends new middleware to the application middleware stack. * * @param callable $callable Any callable that accepts three arguments: * 1. A Request object * 2. A Response object * 3. A "next" middleware callable * * @return static * * @throws RuntimeException If middleware is added while the stack is dequeuing * @throws UnexpectedValueException If the middleware doesn't return a AmeliaPsr\Http\Message\ResponseInterface */ protected function addMiddleware(callable $callable) { if ($this->middlewareLock) { throw new RuntimeException('Middleware can’t be added once the stack is dequeuing'); } if (is_null($this->tip)) { $this->seedMiddlewareStack(); } $next = $this->tip; $this->tip = function ( ServerRequestInterface $request, ResponseInterface $response ) use ( $callable, $next ) { $result = call_user_func($callable, $request, $response, $next); if ($result instanceof ResponseInterface === false) { throw new UnexpectedValueException( 'Middleware must return instance of \AmeliaPsr\Http\Message\ResponseInterface' ); } return $result; }; return $this; } /** * Seed middleware stack with first callable * * @param callable $kernel The last item to run as middleware * * @throws RuntimeException if the stack is seeded more than once */ protected function seedMiddlewareStack(callable $kernel = null) { if (!is_null($this->tip)) { throw new RuntimeException('MiddlewareStack can only be seeded once.'); } if ($kernel === null) { $kernel = $this; } $this->tip = $kernel; } /** * Call middleware stack * * @param ServerRequestInterface $request A request object * @param ResponseInterface $response A response object * * @return ResponseInterface */ public function callMiddlewareStack(ServerRequestInterface $request, ResponseInterface $response) { if (is_null($this->tip)) { $this->seedMiddlewareStack(); } /** @var callable $start */ $start = $this->tip; $this->middlewareLock = true; $response = $start($request, $response); $this->middlewareLock = false; return $response; } } Slim/Route.php 0000666 00000015141 15165376503 0007300 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Handlers\Strategies\RequestResponse; use Slim\Interfaces\InvocationStrategyInterface; use Slim\Interfaces\RouteInterface; class Route extends Routable implements RouteInterface { use MiddlewareAwareTrait; /** * HTTP methods supported by this route * * @var string[] */ protected $methods = []; /** * Route identifier * * @var string */ protected $identifier; /** * Route name * * @var null|string */ protected $name; /** * Parent route groups * * @var RouteGroup[] */ protected $groups; private $finalized = false; /** * Output buffering mode * * One of: false, 'prepend' or 'append' * * @var boolean|string */ protected $outputBuffering = 'append'; /** * Route parameters * * @var array */ protected $arguments = []; /** * Route arguments parameters * * @var null|array */ protected $savedArguments = []; /** * @param string|string[] $methods The route HTTP methods * @param string $pattern The route pattern * @param callable $callable The route callable * @param RouteGroup[] $groups The parent route groups * @param int $identifier The route identifier */ public function __construct($methods, $pattern, $callable, $groups = [], $identifier = 0) { parent::__construct($pattern, $callable); $this->methods = is_string($methods) ? [$methods] : $methods; $this->groups = $groups; $this->identifier = 'route' . $identifier; } public function finalize() { if ($this->finalized) { return; } $groupMiddleware = []; foreach ($this->getGroups() as $group) { $groupMiddleware = array_merge($group->getMiddleware(), $groupMiddleware); } $this->middleware = array_merge($this->middleware, $groupMiddleware); foreach ($this->getMiddleware() as $middleware) { $this->addMiddleware($middleware); } $this->finalized = true; } /** * Get route callable * * @return callable */ public function getCallable() { return $this->callable; } /** * This method enables you to override the Route's callable * * @param string|Closure $callable */ public function setCallable($callable) { $this->callable = $callable; } /** * Get route methods * * @return string[] */ public function getMethods() { return $this->methods; } /** * Get parent route groups * * @return RouteGroup[] */ public function getGroups() { return $this->groups; } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * Get route identifier * * @return string */ public function getIdentifier() { return $this->identifier; } /** * Get output buffering mode * * @return boolean|string */ public function getOutputBuffering() { return $this->outputBuffering; } /** * {@inheritdoc} */ public function setOutputBuffering($mode) { if (!in_array($mode, [false, 'prepend', 'append'], true)) { throw new InvalidArgumentException('Unknown output buffering mode'); } $this->outputBuffering = $mode; return $this; } /** * {@inheritdoc} */ public function setName($name) { if (!is_string($name)) { throw new InvalidArgumentException('Route name must be a string'); } $this->name = $name; return $this; } /** * {@inheritdoc} */ public function setArgument($name, $value, $includeInSavedArguments = true) { if ($includeInSavedArguments) { $this->savedArguments[$name] = $value; } $this->arguments[$name] = $value; return $this; } /** * {@inheritdoc} */ public function setArguments(array $arguments, $includeInSavedArguments = true) { if ($includeInSavedArguments) { $this->savedArguments = $arguments; } $this->arguments = $arguments; return $this; } /** * {@inheritdoc} */ public function getArguments() { return $this->arguments; } /** * {@inheritdoc} */ public function getArgument($name, $default = null) { if (array_key_exists($name, $this->arguments)) { return $this->arguments[$name]; } return $default; } /** * {@inheritdoc} */ public function prepare(ServerRequestInterface $request, array $arguments) { // Remove temp arguments $this->setArguments($this->savedArguments); // Add the route arguments foreach ($arguments as $k => $v) { $this->setArgument($k, $v, false); } } /** * {@inheritdoc} */ public function run(ServerRequestInterface $request, ResponseInterface $response) { // Finalise route now that we are about to run it $this->finalize(); // Traverse middleware stack and fetch updated response return $this->callMiddlewareStack($request, $response); } /** * {@inheritdoc} */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { $this->callable = $this->resolveCallable($this->callable); /** @var InvocationStrategyInterface $handler */ $handler = isset($this->container) ? $this->container->get('foundHandler') : new RequestResponse(); $newResponse = $handler($this->callable, $request, $response, $this->arguments); if ($newResponse instanceof ResponseInterface) { // if route callback returns a ResponseInterface, then use it $response = $newResponse; } elseif (is_string($newResponse)) { // if route callback returns a string, then append it to the response if ($response->getBody()->isWritable()) { $response->getBody()->write($newResponse); } } return $response; } } Slim/Interfaces/Http/EnvironmentInterface.php 0000666 00000000647 15165376503 0015336 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; interface EnvironmentInterface { /** * Create mock environment * * @param array $settings Array of custom environment keys and values * * @return static */ public static function mock(array $settings = []); } Slim/Interfaces/Http/HeadersInterface.php 0000666 00000002044 15165376503 0014376 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; use Slim\Interfaces\CollectionInterface; interface HeadersInterface extends CollectionInterface { /** * Add HTTP header value * * This method appends a header value. Unlike the set() method, * this method _appends_ this new value to any values * that already exist for this header name. * * @param string $key The case-insensitive header name * @param string|string[] $value The new header value(s) */ public function add($key, $value); /** * Normalize header name * * This method transforms header names into a * normalized form. This is how we enable case-insensitive * header names in the other methods in this class. * * @param string $key The case-insensitive header name * * @return string Normalized header name */ public function normalizeKey($key); } Slim/Interfaces/Http/CookiesInterface.php 0000666 00000002275 15165376503 0014425 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces\Http; use InvalidArgumentException; interface CookiesInterface { /** * Get request cookie * * @param string $name Cookie name * @param mixed $default Cookie default value * * @return mixed Cookie value if present, else default */ public function get($name, $default = null); /** * Set response cookie * * @param string $name Cookie name * @param string|array $value Cookie value, or cookie properties */ public function set($name, $value); /** * Convert to array of `Set-Cookie` headers * * @return string[] */ public function toHeaders(); /** * Parse HTTP request `Cookie:` header and extract into a PHP associative array. * * @param string $header The raw HTTP request `Cookie:` header * * @return array Associative array of cookie names and values * * @throws InvalidArgumentException if the cookie data cannot be parsed */ public static function parseHeader($header); } Slim/Interfaces/CollectionInterface.php 0000666 00000002773 15165376503 0014210 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use ArrayAccess; use Countable; use IteratorAggregate; interface CollectionInterface extends ArrayAccess, Countable, IteratorAggregate { /** * Set collection item * * @param string $key The data key * @param mixed $value The data value */ public function set($key, $value); /** * Get collection item for key * * @param string $key The data key * @param mixed $default The default value to return if data key does not exist * * @return mixed The key's value, or the default value */ public function get($key, $default = null); /** * Add item to collection, replacing existing items with the same data key * * @param array $items Key-value array of data to append to this collection */ public function replace(array $items); /** * Get all items in collection * * @return array The collection's source data */ public function all(); /** * Does this collection have a given key? * * @param string $key The data key * * @return bool */ public function has($key); /** * Remove item from collection * * @param string $key The data key */ public function remove($key); /** * Remove all items from collection */ public function clear(); } Slim/Interfaces/CallableResolverInterface.php 0000666 00000000675 15165376503 0015335 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use RuntimeException; interface CallableResolverInterface { /** * Invoke the resolved callable. * * @param callable|string $toResolve * * @return callable * * @throws RuntimeException */ public function resolve($toResolve); } Slim/Interfaces/RouteInterface.php 0000666 00000006302 15165376503 0013203 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; interface RouteInterface { /** * Retrieve a specific route argument * * @param string $name * @param string|null $default * * @return string|null */ public function getArgument($name, $default = null); /** * Get route arguments * * @return string[] */ public function getArguments(); /** * Get route name * * @return null|string */ public function getName(); /** * Get route pattern * * @return string */ public function getPattern(); /** * Set a route argument * * @param string $name * @param string $value * * @return RouteInterface */ public function setArgument($name, $value); /** * Replace route arguments * * @param string[] $arguments * * @return RouteInterface */ public function setArguments(array $arguments); /** * Set output buffering mode * * One of: false, 'prepend' or 'append' * * @param boolean|string $mode * * @throws InvalidArgumentException If an unknown buffering mode is specified */ public function setOutputBuffering($mode); /** * Set route name * * @param string $name * * @return RouteInterface * * @throws InvalidArgumentException if the route name is not a string */ public function setName($name); /** * Add middleware * * This method prepends new middleware to the route's middleware stack. * * @param callable|string $callable The callback routine * * @return RouteInterface */ public function add($callable); /** * Prepare the route for use * * @param ServerRequestInterface $request * @param array $arguments */ public function prepare(ServerRequestInterface $request, array $arguments); /** * Run route * * This method traverses the middleware stack, including the route's callable * and captures the resultant HTTP response object. It then sends the response * back to the Application. * * @param ServerRequestInterface $request * @param ResponseInterface $response * * @return ResponseInterface */ public function run(ServerRequestInterface $request, ResponseInterface $response); /** * Dispatch route callable against current Request and Response objects * * This method invokes the route object's callable. If middleware is * registered for the route, each callable middleware is invoked in * the order specified. * * @param ServerRequestInterface $request The current Request object * @param ResponseInterface $response The current Response object * * @return ResponseInterface */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response); } Slim/Interfaces/RouterInterface.php 0000666 00000005464 15165376503 0013375 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; interface RouterInterface { /** * Route result constants */ const DISPATCH_STATUS = 0; const ALLOWED_METHODS = 1; /** * Add route * * @param string[] $methods Array of HTTP methods * @param string $pattern The route pattern * @param callable $handler The route callable * * @return RouteInterface */ public function map($methods, $pattern, $handler); /** * Dispatch router for HTTP request * * @param ServerRequestInterface $request The current HTTP request object * * @return array * * @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php */ public function dispatch(ServerRequestInterface $request); /** * Add a route group to the array * * @param string $pattern The group pattern * @param callable $callable A group callable * * @return RouteGroupInterface */ public function pushGroup($pattern, $callable); /** * Removes the last route group from the array * * @return bool True if successful, else False */ public function popGroup(); /** * Get named route object * * @param string $name Route name * * @return RouteInterface * * @throws RuntimeException If named route does not exist */ public function getNamedRoute($name); /** * @param string $identifier * * @return RouteInterface */ public function lookupRoute($identifier); /** * Build the path for a named route excluding the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function relativePathFor($name, array $data = [], array $queryParams = []); /** * Build the path for a named route including the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function pathFor($name, array $data = [], array $queryParams = []); } Slim/Interfaces/RouteGroupInterface.php 0000666 00000001451 15165376503 0014220 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use Slim\App; interface RouteGroupInterface { /** * Get route pattern * * @return string */ public function getPattern(); /** * Prepend middleware to the group middleware collection * * @param callable|string $callable The callback routine * * @return RouteGroupInterface */ public function add($callable); /** * Execute route group callable in the context of the Slim App * * This method invokes the route group object's callable, collecting * nested route objects * * @param App $app */ public function __invoke(App $app); } Slim/Interfaces/InvocationStrategyInterface.php 0000666 00000001531 15165376503 0015740 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Interfaces; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; interface InvocationStrategyInterface { /** * @param callable $callable The callable to invoke using the strategy. * @param ServerRequestInterface $request The request object. * @param ResponseInterface $response The response object. * @param array $routeArguments The route's placeholder arguments * * @return ResponseInterface|string */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ); } Slim/CallableResolver.php 0000666 00000006123 15165376503 0011423 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Container\ContainerInterface; use RuntimeException; use Slim\Interfaces\CallableResolverInterface; /** * This class resolves a string of the format 'class:method' into a closure * that can be dispatched. */ final class CallableResolver implements CallableResolverInterface { const CALLABLE_PATTERN = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!'; /** * @var ContainerInterface */ private $container; /** * @param ContainerInterface $container */ public function __construct(ContainerInterface $container) { $this->container = $container; } /** * Resolve toResolve into a closure so that the router can dispatch. * * If toResolve is of the format 'class:method', then try to extract 'class' * from the container otherwise instantiate it and then dispatch 'method'. * * @param callable|string $toResolve * * @return callable * * @throws RuntimeException If the callable does not exist * @throws RuntimeException If the callable is not resolvable */ public function resolve($toResolve) { if (is_callable($toResolve)) { return $toResolve; } $resolved = $toResolve; if (is_string($toResolve)) { list($class, $method) = $this->parseCallable($toResolve); $resolved = $this->resolveCallable($class, $method); } $this->assertCallable($resolved); return $resolved; } /** * Extract class and method from toResolve * * @param string $toResolve * * @return array */ protected function parseCallable($toResolve) { if (preg_match(self::CALLABLE_PATTERN, $toResolve, $matches)) { return [$matches[1], $matches[2]]; } return [$toResolve, '__invoke']; } /** * Check if string is something in the DIC * that's callable or is a class name which has an __invoke() method. * * @param string $class * @param string $method * * @return callable * * @throws RuntimeException if the callable does not exist */ protected function resolveCallable($class, $method) { if ($this->container->has($class)) { return [$this->container->get($class), $method]; } if (!class_exists($class)) { throw new RuntimeException(sprintf('Callable %s does not exist', $class)); } return [new $class($this->container), $method]; } /** * @param Callable $callable * * @throws RuntimeException if the callable is not resolvable */ protected function assertCallable($callable) { if (!is_callable($callable)) { throw new RuntimeException(sprintf( '%s is not resolvable', is_array($callable) || is_object($callable) ? json_encode($callable) : $callable )); } } } Slim/Http/Stream.php 0000666 00000026644 15165376503 0010366 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\StreamInterface; use RuntimeException; /** * Represents a data stream as defined in PSR-7. * * @link https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php */ class Stream implements StreamInterface { /** * Bit mask to determine if the stream is a pipe * * This is octal as per header stat.h */ const FSTAT_MODE_S_IFIFO = 0010000; /** * Resource modes * * @var array * @link http://php.net/manual/function.fopen.php */ protected static $modes = [ 'readable' => ['r', 'r+', 'w+', 'a+', 'x+', 'c+'], 'writable' => ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'], ]; /** * The underlying stream resource * * @var resource */ protected $stream; /** * Stream metadata * * @var array */ protected $meta; /** * Is this stream readable? * * @var bool */ protected $readable; /** * Is this stream writable? * * @var bool */ protected $writable; /** * Is this stream seekable? * * @var bool */ protected $seekable; /** * The size of the stream if known * * @var null|int */ protected $size; /** * Is this stream a pipe? * * @var bool */ protected $isPipe; /** * @param resource $stream A PHP resource handle. * * @throws InvalidArgumentException If argument is not a resource. */ public function __construct($stream) { $this->attach($stream); } /** * Get stream metadata as an associative array or retrieve a specific key. * * The keys returned are identical to the keys returned from PHP's stream_get_meta_data() function. * * @link http://php.net/manual/en/function.stream-get-meta-data.php * * @param string $key Specific metadata to retrieve. * * @return array|mixed|null Returns an associative array if no key is * provided. Returns a specific key value if a key is provided and the * value is found, or null if the key is not found. */ public function getMetadata($key = null) { $this->meta = stream_get_meta_data($this->stream); if (is_null($key) === true) { return $this->meta; } return isset($this->meta[$key]) ? $this->meta[$key] : null; } /** * Is a resource attached to this stream? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ protected function isAttached() { return is_resource($this->stream); } /** * Attach new resource to this object. * * Note: This method is not part of the PSR-7 standard. * * @param resource $newStream A PHP resource handle. * * @throws InvalidArgumentException If argument is not a valid PHP resource. */ protected function attach($newStream) { if (is_resource($newStream) === false) { throw new InvalidArgumentException(__METHOD__ . ' argument must be a valid PHP resource'); } if ($this->isAttached() === true) { $this->detach(); } $this->stream = $newStream; } /** * Separates any underlying resources from the stream. * * After the stream has been detached, the stream is in an unusable state. * * @return resource|null Underlying PHP stream, if any */ public function detach() { $oldResource = $this->stream; $this->stream = null; $this->meta = null; $this->readable = null; $this->writable = null; $this->seekable = null; $this->size = null; $this->isPipe = null; return $oldResource; } /** * Reads all data from the stream into a string, from the beginning to end. * * This method MUST attempt to seek to the beginning of the stream before * reading data and read the stream until the end is reached. * * Warning: This could attempt to load a large amount of data into memory. * * This method MUST NOT raise an exception in order to conform with PHP's * string casting operations. * * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring * * @return string */ public function __toString() { if (!$this->isAttached()) { return ''; } try { $this->rewind(); return $this->getContents(); } catch (RuntimeException $e) { return ''; } } /** * Closes the stream and any underlying resources. */ public function close() { if ($this->isAttached() === true) { if ($this->isPipe()) { pclose($this->stream); } else { fclose($this->stream); } } $this->detach(); } /** * Get the size of the stream if known. * * @return int|null Returns the size in bytes if known, or null if unknown. */ public function getSize() { if (!$this->size && $this->isAttached() === true) { $stats = fstat($this->stream); $this->size = isset($stats['size']) && !$this->isPipe() ? $stats['size'] : null; } return $this->size; } /** * Returns the current position of the file read/write pointer * * @return int Position of the file pointer * * @throws RuntimeException on error. */ public function tell() { if (!$this->isAttached() || ($position = ftell($this->stream)) === false || $this->isPipe()) { throw new RuntimeException('Could not get the position of the pointer in stream'); } return $position; } /** * Returns true if the stream is at the end of the stream. * * @return bool */ public function eof() { return $this->isAttached() ? feof($this->stream) : true; } /** * Returns whether or not the stream is readable. * * @return bool */ public function isReadable() { if ($this->readable === null) { if ($this->isPipe()) { $this->readable = true; } else { $this->readable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['readable'] as $mode) { if (strpos($meta['mode'], $mode) === 0) { $this->readable = true; break; } } } } } return $this->readable; } /** * Returns whether or not the stream is writable. * * @return bool */ public function isWritable() { if ($this->writable === null) { $this->writable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['writable'] as $mode) { if (strpos($meta['mode'], $mode) === 0) { $this->writable = true; break; } } } } return $this->writable; } /** * Returns whether or not the stream is seekable. * * @return bool */ public function isSeekable() { if ($this->seekable === null) { $this->seekable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); $this->seekable = !$this->isPipe() && $meta['seekable']; } } return $this->seekable; } /** * Seek to a position in the stream. * * @link http://www.php.net/manual/en/function.fseek.php * * @param int $offset Stream offset * @param int $whence Specifies how the cursor position will be calculated * based on the seek offset. Valid values are identical to the built-in * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * offset bytes SEEK_CUR: Set position to current location plus offset * SEEK_END: Set position to end-of-stream plus offset. * * @throws RuntimeException If stream is not seekable */ public function seek($offset, $whence = SEEK_SET) { // Note that fseek returns 0 on success! if (!$this->isSeekable() || fseek($this->stream, $offset, $whence) === -1) { throw new RuntimeException('Could not seek in stream'); } } /** * Seek to the beginning of the stream. * * If the stream is not seekable, this method will raise an exception; * otherwise, it will perform a seek(0). * * @see seek() * * @link http://www.php.net/manual/en/function.fseek.php * * @throws RuntimeException on failure. */ public function rewind() { if (!$this->isSeekable() || rewind($this->stream) === false) { throw new RuntimeException('Could not rewind stream'); } } /** * Read data from the stream. * * @param int $length Read up to $length bytes from the object and return * them. Fewer than $length bytes may be returned if underlying stream * call returns fewer bytes. * * @return string Returns the data read from the stream, or an empty string if no bytes are available. * * @throws RuntimeException if an error occurs. */ public function read($length) { if (!$this->isReadable() || ($data = fread($this->stream, $length)) === false) { throw new RuntimeException('Could not read from stream'); } return $data; } /** * Write data to the stream. * * @param string $string The string that is to be written. * * @return int Returns the number of bytes written to the stream. * * @throws RuntimeException If stream is not writable */ public function write($string) { if (!$this->isWritable() || ($written = fwrite($this->stream, $string)) === false) { throw new RuntimeException('Could not write to stream'); } // reset size so that it will be recalculated on next call to getSize() $this->size = null; return $written; } /** * Returns the remaining contents in a string * * @return string * * @throws RuntimeException If stream is not readable */ public function getContents() { if (!$this->isReadable() || ($contents = stream_get_contents($this->stream)) === false) { throw new RuntimeException('Could not get contents of stream'); } return $contents; } /** * Returns whether or not the stream is a pipe. * * @return bool */ public function isPipe() { if ($this->isPipe === null) { $this->isPipe = false; if ($this->isAttached()) { $mode = fstat($this->stream)['mode']; $this->isPipe = ($mode & self::FSTAT_MODE_S_IFIFO) !== 0; } } return $this->isPipe; } } Slim/Http/Message.php 0000666 00000020647 15165376503 0010514 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\MessageInterface; use AmeliaPsr\Http\Message\StreamInterface; use Slim\Interfaces\Http\HeadersInterface; /** * Abstract message (base class for Request and Response) * * This class represents a general HTTP message. It provides common properties and methods for * the HTTP request and response, as defined in the PSR-7 MessageInterface. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @see \Slim\Http\Request * @see \Slim\Http\Response */ abstract class Message implements MessageInterface { /** * @var string */ protected $protocolVersion = '1.1'; /** * @var array */ protected static $validProtocolVersions = [ '1.0' => true, '1.1' => true, '2.0' => true, '2' => true, ]; /** * @var HeadersInterface */ protected $headers; /** * @var StreamInterface */ protected $body; /** * Disable magic setter to ensure immutability */ public function __set($name, $value) { // Do nothing } /** * Retrieves the HTTP protocol version as a string. * * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). * * @return string */ public function getProtocolVersion() { return $this->protocolVersion; } /** * Return an instance with the specified HTTP protocol version. * * The version string MUST contain only the HTTP version number (e.g., * "1.1", "1.0"). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new protocol version. * * @param string $version HTTP protocol version * * @return static * * @throws InvalidArgumentException if the http version is an invalid number */ public function withProtocolVersion($version) { if (!isset(self::$validProtocolVersions[$version])) { throw new InvalidArgumentException( 'Invalid HTTP version. Must be one of: ' . implode(', ', array_keys(self::$validProtocolVersions)) ); } $clone = clone $this; $clone->protocolVersion = $version; return $clone; } /** * Retrieves all message header values. * * Returns an associative array of the message's headers. * Each key MUST be a header name, and each value MUST be an array of strings for that header. * * The keys represent the header name as it will be sent over the wire, and * each value is an array of strings associated with the header. * * // Represent the headers as a string * foreach ($message->getHeaders() as $name => $values) { * echo $name . ": " . implode(", ", $values); * } * * // Emit headers iteratively: * foreach ($message->getHeaders() as $name => $values) { * foreach ($values as $value) { * header(sprintf('%s: %s', $name, $value), false); * } * } * * While header names are not case-sensitive, getHeaders() will preserve the * exact case in which headers were originally specified. * * @return array */ public function getHeaders() { return $this->headers->all(); } /** * Checks if a header exists by the given case-insensitive name. * * Returns true if any header names match the given header name using a case-insensitive string comparison. * Returns false if no matching header name is found in the message. * * @param string $name Case-insensitive header field name. * * @return bool */ public function hasHeader($name) { return $this->headers->has($name); } /** * Retrieves a message header value by the given case-insensitive name. * * This method returns an array of all the header values of the given * case-insensitive header name. * * If the header does not appear in the message, this method MUST return an * empty array. * * @param string $name Case-insensitive header field name. * * @return string[] */ public function getHeader($name) { return $this->headers->get($name, []); } /** * Retrieves a comma-separated string of the values for a single header. * * This method returns a string of all of the header values of the given * case-insensitive header name as a string concatenated together using * a comma. * * NOTE: Not all header values may be appropriately represented using * comma concatenation. For such headers, use getHeader() instead * and supply your own delimiter when concatenating. * * If the header does not appear in the message, this method MUST return * an empty string. * * @param string $name Case-insensitive header field name. * * @return string */ public function getHeaderLine($name) { return implode(',', $this->headers->get($name, [])); } /** * Return an instance with the provided value replacing the specified header. * * While header names are case-insensitive, the casing of the header will * be preserved by this function, and returned from getHeaders(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * * @return static */ public function withHeader($name, $value) { $clone = clone $this; $clone->headers->set($name, $value); return $clone; } /** * Return an instance with the specified header appended with the given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * * @return static */ public function withAddedHeader($name, $value) { $clone = clone $this; $clone->headers->add($name, $value); if ($this instanceof Response && $this->body instanceof NonBufferedBody) { header(sprintf('%s: %s', $name, $clone->getHeaderLine($name))); } return $clone; } /** * Return an instance without the specified header. * * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the named header. * * @param string $name Case-insensitive header field name to remove. * * @return static */ public function withoutHeader($name) { $clone = clone $this; $clone->headers->remove($name); if ($this instanceof Response && $this->body instanceof NonBufferedBody) { header_remove($name); } return $clone; } /** * Gets the body of the message. * * @return StreamInterface Returns the body as a stream. */ public function getBody() { return $this->body; } /** * Return an instance with the specified message body. * * The body MUST be a StreamInterface object. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * new body stream. * * @param StreamInterface $body Body. * * @return static */ public function withBody(StreamInterface $body) { $clone = clone $this; $clone->body = $body; return $clone; } } Slim/Http/StatusCode.php 0000666 00000005207 15165376503 0011201 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; class StatusCode { const HTTP_CONTINUE = 100; const HTTP_SWITCHING_PROTOCOLS = 101; const HTTP_PROCESSING = 102; const HTTP_OK = 200; const HTTP_CREATED = 201; const HTTP_ACCEPTED = 202; const HTTP_NONAUTHORITATIVE_INFORMATION = 203; const HTTP_NO_CONTENT = 204; const HTTP_RESET_CONTENT = 205; const HTTP_PARTIAL_CONTENT = 206; const HTTP_MULTI_STATUS = 207; const HTTP_ALREADY_REPORTED = 208; const HTTP_IM_USED = 226; const HTTP_MULTIPLE_CHOICES = 300; const HTTP_MOVED_PERMANENTLY = 301; const HTTP_FOUND = 302; const HTTP_SEE_OTHER = 303; const HTTP_NOT_MODIFIED = 304; const HTTP_USE_PROXY = 305; const HTTP_UNUSED= 306; const HTTP_TEMPORARY_REDIRECT = 307; const HTTP_PERMANENT_REDIRECT = 308; const HTTP_BAD_REQUEST = 400; const HTTP_UNAUTHORIZED = 401; const HTTP_PAYMENT_REQUIRED = 402; const HTTP_FORBIDDEN = 403; const HTTP_NOT_FOUND = 404; const HTTP_METHOD_NOT_ALLOWED = 405; const HTTP_NOT_ACCEPTABLE = 406; const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; const HTTP_REQUEST_TIMEOUT = 408; const HTTP_CONFLICT = 409; const HTTP_GONE = 410; const HTTP_LENGTH_REQUIRED = 411; const HTTP_PRECONDITION_FAILED = 412; const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; const HTTP_REQUEST_URI_TOO_LONG = 414; const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; const HTTP_EXPECTATION_FAILED = 417; const HTTP_IM_A_TEAPOT = 418; const HTTP_MISDIRECTED_REQUEST = 421; const HTTP_UNPROCESSABLE_ENTITY = 422; const HTTP_LOCKED = 423; const HTTP_FAILED_DEPENDENCY = 424; const HTTP_UPGRADE_REQUIRED = 426; const HTTP_PRECONDITION_REQUIRED = 428; const HTTP_TOO_MANY_REQUESTS = 429; const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; const HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE = 444; const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; const HTTP_CLIENT_CLOSED_REQUEST = 499; const HTTP_INTERNAL_SERVER_ERROR = 500; const HTTP_NOT_IMPLEMENTED = 501; const HTTP_BAD_GATEWAY = 502; const HTTP_SERVICE_UNAVAILABLE = 503; const HTTP_GATEWAY_TIMEOUT = 504; const HTTP_VERSION_NOT_SUPPORTED = 505; const HTTP_VARIANT_ALSO_NEGOTIATES = 506; const HTTP_INSUFFICIENT_STORAGE = 507; const HTTP_LOOP_DETECTED = 508; const HTTP_NOT_EXTENDED = 510; const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; const HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR = 599; } Slim/Http/UploadedFile.php 0000666 00000025050 15165376503 0011456 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UploadedFileInterface; use RuntimeException; /** * Represents an uploaded file according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/UploadedFileInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php */ class UploadedFile implements UploadedFileInterface { /** * The client-provided full path to the file * * @note this is public to maintain BC with 3.1.0 and earlier. * * @var string */ public $file; /** * The client-provided file name. * * @var string */ protected $name; /** * The client-provided media type of the file. * * @var string */ protected $type; /** * The size of the file in bytes. * * @var int */ protected $size; /** * A valid PHP UPLOAD_ERR_xxx code for the file upload. * * @var int */ protected $error = UPLOAD_ERR_OK; /** * Indicates if the upload is from a SAPI environment. * * @var bool */ protected $sapi = false; /** * An optional StreamInterface wrapping the file resource. * * @var StreamInterface */ protected $stream; /** * Indicates if the uploaded file has already been moved. * * @var bool */ protected $moved = false; /** * Create a normalized tree of UploadedFile instances from the Environment. * * Returns a normalized tree of UploadedFile instances or null if none are provided. * * @param Environment $env The environment * * @return array|null */ public static function createFromEnvironment(Environment $env) { if (is_array($env['slim.files']) && $env->has('slim.files')) { return $env['slim.files']; } elseif (! empty($_FILES)) { return static::parseUploadedFiles($_FILES); } return []; } /** * Parse a non-normalized, i.e. $_FILES superglobal, tree of uploaded file data. * * Returns a normalized tree of UploadedFile instances. * * @param array $uploadedFiles The non-normalized tree of uploaded file data. * * @return array */ private static function parseUploadedFiles(array $uploadedFiles) { $parsed = []; foreach ($uploadedFiles as $field => $uploadedFile) { if (!isset($uploadedFile['error'])) { if (is_array($uploadedFile)) { $parsed[$field] = static::parseUploadedFiles($uploadedFile); } continue; } $parsed[$field] = []; if (!is_array($uploadedFile['error'])) { $parsed[$field] = new static( $uploadedFile['tmp_name'], isset($uploadedFile['name']) ? $uploadedFile['name'] : null, isset($uploadedFile['type']) ? $uploadedFile['type'] : null, isset($uploadedFile['size']) ? $uploadedFile['size'] : null, $uploadedFile['error'], true ); } else { $subArray = []; foreach ($uploadedFile['error'] as $fileIdx => $error) { // normalise subarray and re-parse to move the input's keyname up a level $subArray[$fileIdx]['name'] = $uploadedFile['name'][$fileIdx]; $subArray[$fileIdx]['type'] = $uploadedFile['type'][$fileIdx]; $subArray[$fileIdx]['tmp_name'] = $uploadedFile['tmp_name'][$fileIdx]; $subArray[$fileIdx]['error'] = $uploadedFile['error'][$fileIdx]; $subArray[$fileIdx]['size'] = $uploadedFile['size'][$fileIdx]; $parsed[$field] = static::parseUploadedFiles($subArray); } } } return $parsed; } /** * @param string $file The full path to the uploaded file provided by the client. * @param string|null $name The file name. * @param string|null $type The file media type. * @param int|null $size The file size in bytes. * @param int $error The UPLOAD_ERR_XXX code representing the status of the upload. * @param bool $sapi Indicates if the upload is in a SAPI environment. */ public function __construct($file, $name = null, $type = null, $size = null, $error = UPLOAD_ERR_OK, $sapi = false) { $this->file = $file; $this->name = $name; $this->type = $type; $this->size = $size; $this->error = $error; $this->sapi = $sapi; } /** * Retrieve a stream representing the uploaded file. * * This method MUST return a StreamInterface instance, representing the * uploaded file. The purpose of this method is to allow utilizing native PHP * stream functionality to manipulate the file upload, such as * stream_copy_to_stream() (though the result will need to be decorated in a * native PHP stream wrapper to work with such functions). * * If the moveTo() method has been called previously, this method MUST raise * an exception. * * @return StreamInterface * * @throws RuntimeException in cases when no stream is available or can be created. */ public function getStream() { if ($this->moved) { throw new RuntimeException(sprintf('Uploaded file %s has already been moved', $this->name)); } if ($this->stream === null) { $this->stream = new Stream(fopen($this->file, 'r')); } return $this->stream; } /** * Move the uploaded file to a new location. * * Use this method as an alternative to move_uploaded_file(). This method is * guaranteed to work in both SAPI and non-SAPI environments. * Implementations must determine which environment they are in, and use the * appropriate method (move_uploaded_file(), rename(), or a stream * operation) to perform the operation. * * $targetPath may be an absolute path, or a relative path. If it is a * relative path, resolution should be the same as used by PHP's rename() * function. * * The original file or stream MUST be removed on completion. * * If this method is called more than once, any subsequent calls MUST raise * an exception. * * When used in an SAPI environment where $_FILES is populated, when writing * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be * used to ensure permissions and upload status are verified correctly. * * If you wish to move to a stream, use getStream(), as SAPI operations * cannot guarantee writing to stream destinations. * * @see http://php.net/is_uploaded_file * @see http://php.net/move_uploaded_file * * @param string $targetPath Path to which to move the uploaded file. * * @throws InvalidArgumentException If the $path specified is invalid. * @throws RuntimeException On any error during the move operation or on the second subsequent call to the method. */ public function moveTo($targetPath) { if ($this->moved) { throw new RuntimeException('Uploaded file already moved'); } $targetIsStream = strpos($targetPath, '://') > 0; if (!$targetIsStream && !is_writable(dirname($targetPath))) { throw new InvalidArgumentException('Upload target path is not writable'); } if ($targetIsStream) { if (!copy($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } if (!unlink($this->file)) { throw new RuntimeException(sprintf('Error removing uploaded file %s', $this->name)); } } elseif ($this->sapi) { if (!is_uploaded_file($this->file)) { throw new RuntimeException(sprintf('%s is not a valid uploaded file', $this->file)); } if (!move_uploaded_file($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } } else { if (!rename($this->file, $targetPath)) { throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); } } $this->moved = true; } /** * Retrieve the error associated with the uploaded file. * * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants. * * If the file was uploaded successfully, this method MUST return * UPLOAD_ERR_OK. * * Implementations SHOULD return the value stored in the "error" key of * the file in the $_FILES array. * * @see http://php.net/manual/en/features.file-upload.errors.php * * @return int */ public function getError() { return $this->error; } /** * Retrieve the filename sent by the client. * * Do not trust the value returned by this method. A client could send * a malicious filename with the intention to corrupt or hack your * application. * * Implementations SHOULD return the value stored in the "name" key of * the file in the $_FILES array. * * @return string|null */ public function getClientFilename() { return $this->name; } /** * Retrieve the media type sent by the client. * * Do not trust the value returned by this method. A client could send * a malicious media type with the intention to corrupt or hack your * application. * * Implementations SHOULD return the value stored in the "type" key of * the file in the $_FILES array. * * @return string|null */ public function getClientMediaType() { return $this->type; } /** * Retrieve the file size. * * Implementations SHOULD return the value stored in the "size" key of * the file in the $_FILES array if available, as PHP calculates this based * on the actual size transmitted. * * @return int|null */ public function getSize() { return $this->size; } } Slim/Http/Response.php 0000666 00000042072 15165376503 0010722 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Interfaces\Http\HeadersInterface; /** * Response * * This class represents an HTTP response. It manages * the response status, headers, and body * according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/ResponseInterface.php */ class Response extends Message implements ResponseInterface { /** * Status code * * @var int */ protected $status = StatusCode::HTTP_OK; /** * Reason phrase * * @var string */ protected $reasonPhrase = ''; /** * Status codes and reason phrases * * @var array */ protected static $messages = [ //Informational 1xx StatusCode::HTTP_CONTINUE => 'Continue', StatusCode::HTTP_SWITCHING_PROTOCOLS => 'Switching Protocols', StatusCode::HTTP_PROCESSING => 'Processing', //Successful 2xx StatusCode::HTTP_OK => 'OK', StatusCode::HTTP_CREATED => 'Created', StatusCode::HTTP_ACCEPTED => 'Accepted', StatusCode::HTTP_NONAUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', StatusCode::HTTP_NO_CONTENT => 'No Content', StatusCode::HTTP_RESET_CONTENT => 'Reset Content', StatusCode::HTTP_PARTIAL_CONTENT => 'Partial Content', StatusCode::HTTP_MULTI_STATUS => 'Multi-Status', StatusCode::HTTP_ALREADY_REPORTED => 'Already Reported', StatusCode::HTTP_IM_USED => 'IM Used', //Redirection 3xx StatusCode::HTTP_MULTIPLE_CHOICES => 'Multiple Choices', StatusCode::HTTP_MOVED_PERMANENTLY => 'Moved Permanently', StatusCode::HTTP_FOUND => 'Found', StatusCode::HTTP_SEE_OTHER => 'See Other', StatusCode::HTTP_NOT_MODIFIED => 'Not Modified', StatusCode::HTTP_USE_PROXY => 'Use Proxy', StatusCode::HTTP_UNUSED => '(Unused)', StatusCode::HTTP_TEMPORARY_REDIRECT => 'Temporary Redirect', StatusCode::HTTP_PERMANENT_REDIRECT => 'Permanent Redirect', //Client Error 4xx StatusCode::HTTP_BAD_REQUEST => 'Bad Request', StatusCode::HTTP_UNAUTHORIZED => 'Unauthorized', StatusCode::HTTP_PAYMENT_REQUIRED => 'Payment Required', StatusCode::HTTP_FORBIDDEN => 'Forbidden', StatusCode::HTTP_NOT_FOUND => 'Not Found', StatusCode::HTTP_METHOD_NOT_ALLOWED => 'Method Not Allowed', StatusCode::HTTP_NOT_ACCEPTABLE => 'Not Acceptable', StatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', StatusCode::HTTP_REQUEST_TIMEOUT => 'Request Timeout', StatusCode::HTTP_CONFLICT => 'Conflict', StatusCode::HTTP_GONE => 'Gone', StatusCode::HTTP_LENGTH_REQUIRED => 'Length Required', StatusCode::HTTP_PRECONDITION_FAILED => 'Precondition Failed', StatusCode::HTTP_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', StatusCode::HTTP_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', StatusCode::HTTP_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', StatusCode::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', StatusCode::HTTP_EXPECTATION_FAILED => 'Expectation Failed', StatusCode::HTTP_IM_A_TEAPOT => 'I\'m a teapot', StatusCode::HTTP_MISDIRECTED_REQUEST => 'Misdirected Request', StatusCode::HTTP_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', StatusCode::HTTP_LOCKED => 'Locked', StatusCode::HTTP_FAILED_DEPENDENCY => 'Failed Dependency', StatusCode::HTTP_UPGRADE_REQUIRED => 'Upgrade Required', StatusCode::HTTP_PRECONDITION_REQUIRED => 'Precondition Required', StatusCode::HTTP_TOO_MANY_REQUESTS => 'Too Many Requests', StatusCode::HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', StatusCode::HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE => 'Connection Closed Without Response', StatusCode::HTTP_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', StatusCode::HTTP_CLIENT_CLOSED_REQUEST => 'Client Closed Request', //Server Error 5xx StatusCode::HTTP_INTERNAL_SERVER_ERROR => 'Internal Server Error', StatusCode::HTTP_NOT_IMPLEMENTED => 'Not Implemented', StatusCode::HTTP_BAD_GATEWAY => 'Bad Gateway', StatusCode::HTTP_SERVICE_UNAVAILABLE => 'Service Unavailable', StatusCode::HTTP_GATEWAY_TIMEOUT => 'Gateway Timeout', StatusCode::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', StatusCode::HTTP_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', StatusCode::HTTP_INSUFFICIENT_STORAGE => 'Insufficient Storage', StatusCode::HTTP_LOOP_DETECTED => 'Loop Detected', StatusCode::HTTP_NOT_EXTENDED => 'Not Extended', StatusCode::HTTP_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', StatusCode::HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR => 'Network Connect Timeout Error', ]; /** * EOL characters used for HTTP response. * * @var string */ const EOL = "\r\n"; /** * @param int $status The response status code. * @param HeadersInterface|null $headers The response headers. * @param StreamInterface|null $body The response body. */ public function __construct( $status = StatusCode::HTTP_OK, HeadersInterface $headers = null, StreamInterface $body = null ) { $this->status = $this->filterStatus($status); $this->headers = $headers ? $headers : new Headers(); $this->body = $body ? $body : new Body(fopen('php://temp', 'r+')); } /** * This method is applied to the cloned object * after PHP performs an initial shallow-copy. This * method completes a deep-copy by creating new objects * for the cloned object's internal reference pointers. */ public function __clone() { $this->headers = clone $this->headers; } /** * Gets the response status code. * * The status code is a 3-digit integer result code of the server's attempt * to understand and satisfy the request. * * @return int */ public function getStatusCode() { return $this->status; } /** * Return an instance with the specified status code and, optionally, reason phrase. * * If no reason phrase is specified, implementations MAY choose to default * to the RFC 7231 or IANA recommended reason phrase for the response's * status code. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated status and reason phrase. * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * * @param int $code The 3-digit integer result code to set. * @param string $reasonPhrase The reason phrase to use with the * provided status code; if none is provided, implementations MAY * use the defaults as suggested in the HTTP specification. * * @return static * * @throws InvalidArgumentException For invalid status code arguments. */ public function withStatus($code, $reasonPhrase = '') { $code = $this->filterStatus($code); if (!is_string($reasonPhrase) && !method_exists($reasonPhrase, '__toString')) { throw new InvalidArgumentException('ReasonPhrase must be a string'); } $clone = clone $this; $clone->status = $code; if ($reasonPhrase === '' && isset(static::$messages[$code])) { $reasonPhrase = static::$messages[$code]; } if ($reasonPhrase === '') { throw new InvalidArgumentException('ReasonPhrase must be supplied for this code'); } $clone->reasonPhrase = $reasonPhrase; return $clone; } /** * Filter HTTP status code. * * @param int $status HTTP status code. * * @return int * * @throws InvalidArgumentException If an invalid HTTP status code is provided. */ protected function filterStatus($status) { if (!is_integer($status) || $status<StatusCode::HTTP_CONTINUE || $status>StatusCode::HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR ) { throw new InvalidArgumentException('Invalid HTTP status code'); } return $status; } /** * Gets the response reason phrase associated with the status code. * * Because a reason phrase is not a required element in a response * status line, the reason phrase value MAY be null. Implementations MAY * choose to return the default RFC 7231 recommended reason phrase (or those * listed in the IANA HTTP Status Code Registry) for the response's * status code. * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * * @return string Reason phrase; must return an empty string if none present. */ public function getReasonPhrase() { if ($this->reasonPhrase) { return $this->reasonPhrase; } if (isset(static::$messages[$this->status])) { return static::$messages[$this->status]; } return ''; } /** * Return an instance with the provided value replacing the specified header. * * If a Location header is set and the status code is 200, then set the status * code to 302 to mimic what PHP does. See https://github.com/slimphp/Slim/issues/1730 * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * * @return static * * @throws InvalidArgumentException For invalid header names or values. */ public function withHeader($name, $value) { $clone = clone $this; $clone->headers->set($name, $value); if ($this->body instanceof NonBufferedBody) { header(sprintf('%s: %s', $name, $clone->getHeaderLine($name))); } if ($clone->getStatusCode() === StatusCode::HTTP_OK && strtolower($name) === 'location') { $clone = $clone->withStatus(StatusCode::HTTP_FOUND); } return $clone; } /** * Write data to the response body. * * Note: This method is not part of the PSR-7 standard. * * Proxies to the underlying stream and writes the provided data to it. * * @param string $data * * @return static */ public function write($data) { $this->getBody()->write($data); return $this; } /** * Redirect. * * Note: This method is not part of the PSR-7 standard. * * This method prepares the response object to return an HTTP Redirect * response to the client. * * @param string|UriInterface $url The redirect destination. * @param int|null $status The redirect HTTP status code. * * @return static */ public function withRedirect($url, $status = null) { $responseWithRedirect = $this->withHeader('Location', (string)$url); if (is_null($status) && $this->getStatusCode() === StatusCode::HTTP_OK) { $status = StatusCode::HTTP_FOUND; } if (!is_null($status)) { return $responseWithRedirect->withStatus($status); } return $responseWithRedirect; } /** * Json. * * Note: This method is not part of the PSR-7 standard. * * This method prepares the response object to return an HTTP Json * response to the client. * * @param mixed $data The data * @param int $status The HTTP status code. * @param int $encodingOptions Json encoding options * * @return static * * @throws RuntimeException */ public function withJson($data, $status = null, $encodingOptions = 0) { $response = $this->withBody(new Body(fopen('php://temp', 'r+'))); $response->body->write($json = json_encode($data, $encodingOptions)); // Ensure that the json encoding passed successfully if ($json === false) { throw new RuntimeException(json_last_error_msg(), json_last_error()); } $responseWithJson = $response->withHeader('Content-Type', 'application/json'); if (isset($status)) { return $responseWithJson->withStatus($status); } return $responseWithJson; } /** * Is this response empty? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isEmpty() { return in_array( $this->getStatusCode(), [StatusCode::HTTP_NO_CONTENT, StatusCode::HTTP_RESET_CONTENT, StatusCode::HTTP_NOT_MODIFIED] ); } /** * Is this response informational? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isInformational() { return $this->getStatusCode() >= StatusCode::HTTP_CONTINUE && $this->getStatusCode() < StatusCode::HTTP_OK; } /** * Is this response OK? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isOk() { return $this->getStatusCode() === StatusCode::HTTP_OK; } /** * Is this response successful? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isSuccessful() { return $this->getStatusCode() >= StatusCode::HTTP_OK && $this->getStatusCode() < StatusCode::HTTP_MULTIPLE_CHOICES; } /** * Is this response a redirect? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isRedirect() { return in_array( $this->getStatusCode(), [ StatusCode::HTTP_MOVED_PERMANENTLY, StatusCode::HTTP_FOUND, StatusCode::HTTP_SEE_OTHER, StatusCode::HTTP_TEMPORARY_REDIRECT, StatusCode::HTTP_PERMANENT_REDIRECT ] ); } /** * Is this response a redirection? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isRedirection() { return $this->getStatusCode() >= StatusCode::HTTP_MULTIPLE_CHOICES && $this->getStatusCode() < StatusCode::HTTP_BAD_REQUEST; } /** * Is this response forbidden? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isForbidden() { return $this->getStatusCode() === StatusCode::HTTP_FORBIDDEN; } /** * Is this response not Found? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isNotFound() { return $this->getStatusCode() === StatusCode::HTTP_NOT_FOUND; } /** * Is this a bad request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isBadRequest() { return $this->getStatusCode() === StatusCode::HTTP_BAD_REQUEST; } /** * Is this response a client error? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isClientError() { return $this->getStatusCode() >= StatusCode::HTTP_BAD_REQUEST && $this->getStatusCode() < StatusCode::HTTP_INTERNAL_SERVER_ERROR; } /** * Is this response a server error? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isServerError() { return $this->getStatusCode() >= StatusCode::HTTP_INTERNAL_SERVER_ERROR && $this->getStatusCode() < 600; } /** * Convert response to string. * * Note: This method is not part of the PSR-7 standard. * * @return string */ public function __toString() { $output = sprintf( 'HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReasonPhrase() ); $output .= Response::EOL; foreach ($this->getHeaders() as $name => $values) { $output .= sprintf('%s: %s', $name, $this->getHeaderLine($name)) . Response::EOL; } $output .= Response::EOL; $output .= (string)$this->getBody(); return $output; } } Slim/Http/RequestBody.php 0000666 00000000757 15165376503 0011376 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; /** * Provides a PSR-7 implementation of a reusable raw request body */ class RequestBody extends Body { public function __construct() { $stream = fopen('php://temp', 'w+'); stream_copy_to_stream(fopen('php://input', 'r'), $stream); rewind($stream); parent::__construct($stream); } } Slim/Http/Request.php 0000666 00000105321 15165376503 0010551 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Closure; use InvalidArgumentException; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\StreamInterface; use AmeliaPsr\Http\Message\UploadedFileInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Collection; use Slim\Exception\InvalidMethodException; use Slim\Interfaces\Http\HeadersInterface; /** * This class represents an HTTP request. * It manages the request method, URI, headers, cookies, and body according to the PSR-7 standard. * * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/RequestInterface.php * @link https://github.com/php-fig/http-message/blob/master/src/ServerRequestInterface.php */ class Request extends Message implements ServerRequestInterface { /** * The request method * * @var string */ protected $method; /** * The original request method (ignoring override) * * @var string */ protected $originalMethod; /** * The request URI object * * @var UriInterface */ protected $uri; /** * The request URI target (path + query string) * * @var string */ protected $requestTarget; /** * The request query string params * * @var array */ protected $queryParams; /** * The request cookies * * @var array */ protected $cookies; /** * The server environment variables at the time the request was created. * * @var array */ protected $serverParams; /** * The request attributes (route segment names and values) * * @var Collection */ protected $attributes; /** * The request body parsed (if possible) into a PHP array or object * * @var null|array|object */ protected $bodyParsed = false; /** * List of request body parsers (e.g., url-encoded, JSON, XML, multipart) * * @var callable[] */ protected $bodyParsers = []; /** * List of uploaded files * * @var UploadedFileInterface[] */ protected $uploadedFiles; /** * Valid request methods * * @var string[] * @deprecated */ protected $validMethods = [ 'CONNECT' => 1, 'DELETE' => 1, 'GET' => 1, 'HEAD' => 1, 'OPTIONS' => 1, 'PATCH' => 1, 'POST' => 1, 'PUT' => 1, 'TRACE' => 1, ]; /** * Create new HTTP request with data extracted from the application * Environment object * * @param Environment $environment The Slim application Environment * * @return static */ public static function createFromEnvironment(Environment $environment) { $method = $environment['REQUEST_METHOD']; $uri = Uri::createFromEnvironment($environment); $headers = Headers::createFromEnvironment($environment); $cookies = Cookies::parseHeader($headers->get('Cookie', [])); $serverParams = $environment->all(); $body = new RequestBody(); $uploadedFiles = UploadedFile::createFromEnvironment($environment); $request = new static($method, $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles); if ($method === 'POST' && in_array($request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data']) ) { // parsed body must be $_POST $request = $request->withParsedBody($_POST); } return $request; } /** * @param string $method The request method * @param UriInterface $uri The request URI object * @param HeadersInterface $headers The request headers collection * @param array $cookies The request cookies collection * @param array $serverParams The server environment variables * @param StreamInterface $body The request body object * @param array $uploadedFiles The request uploadedFiles collection * * @throws InvalidMethodException on invalid HTTP method */ public function __construct( $method, UriInterface $uri, HeadersInterface $headers, array $cookies, array $serverParams, StreamInterface $body, array $uploadedFiles = [] ) { try { $this->originalMethod = $this->filterMethod($method); } catch (InvalidMethodException $e) { $this->originalMethod = $method; } $this->uri = $uri; $this->headers = $headers; $this->cookies = $cookies; $this->serverParams = $serverParams; $this->attributes = new Collection(); $this->body = $body; $this->uploadedFiles = $uploadedFiles; if (isset($serverParams['SERVER_PROTOCOL'])) { $this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']); } if (!$this->headers->has('Host') && $this->uri->getHost() !== '') { $port = $this->uri->getPort() ? ":{$this->uri->getPort()}" : ''; $this->headers->set('Host', $this->uri->getHost() . $port); } $this->registerMediaTypeParser('application/json', function ($input) { $result = json_decode($input, true); if (!is_array($result)) { return null; } return $result; }); $this->registerMediaTypeParser('application/xml', function ($input) { $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); if ($result === false) { return null; } return $result; }); $this->registerMediaTypeParser('text/xml', function ($input) { $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); if ($result === false) { return null; } return $result; }); $this->registerMediaTypeParser('application/x-www-form-urlencoded', function ($input) { parse_str($input, $data); return $data; }); // if the request had an invalid method, we can throw it now if (isset($e) && $e instanceof InvalidMethodException) { throw $e; } } /** * This method is applied to the cloned object after PHP performs an initial shallow-copy. * This method completes a deep-copy by creating new objects for the cloned object's internal reference pointers. */ public function __clone() { $this->headers = clone $this->headers; $this->attributes = clone $this->attributes; $this->body = clone $this->body; } /** * Retrieves the HTTP method of the request. * * @return string */ public function getMethod() { if ($this->method === null) { $this->method = $this->originalMethod; $customMethod = $this->getHeaderLine('X-Http-Method-Override'); if ($customMethod) { $this->method = $this->filterMethod($customMethod); } elseif ($this->originalMethod === 'POST') { $overrideMethod = $this->filterMethod($this->getParsedBodyParam('_METHOD')); if ($overrideMethod !== null) { $this->method = $overrideMethod; } if ($this->getBody()->eof()) { $this->getBody()->rewind(); } } } return $this->method; } /** * Get the original HTTP method (ignore override). * * Note: This method is not part of the PSR-7 standard. * * @return string */ public function getOriginalMethod() { return $this->originalMethod; } /** * Return an instance with the provided HTTP method. * * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT * modify the given string. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request method. * * @param string $method Case-sensitive method. * * @return static * * @throws InvalidArgumentException for invalid HTTP methods. */ public function withMethod($method) { $method = $this->filterMethod($method); $clone = clone $this; $clone->originalMethod = $method; $clone->method = $method; return $clone; } /** * Validate the HTTP method * * @param null|string $method * * @return null|string * * @throws InvalidArgumentException on invalid HTTP method. */ protected function filterMethod($method) { if ($method === null) { return $method; } if (!is_string($method)) { throw new InvalidArgumentException(sprintf( 'Unsupported HTTP method; must be a string, received %s', (is_object($method) ? get_class($method) : gettype($method)) )); } $method = strtoupper($method); if (preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method) !== 1) { throw new InvalidMethodException($this, $method); } return $method; } /** * Does this request use a given method? * * Note: This method is not part of the PSR-7 standard. * * @param string $method HTTP method * * @return bool */ public function isMethod($method) { return $this->getMethod() === $method; } /** * Is this a GET request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isGet() { return $this->isMethod('GET'); } /** * Is this a POST request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPost() { return $this->isMethod('POST'); } /** * Is this a PUT request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPut() { return $this->isMethod('PUT'); } /** * Is this a PATCH request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isPatch() { return $this->isMethod('PATCH'); } /** * Is this a DELETE request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isDelete() { return $this->isMethod('DELETE'); } /** * Is this a HEAD request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isHead() { return $this->isMethod('HEAD'); } /** * Is this a OPTIONS request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isOptions() { return $this->isMethod('OPTIONS'); } /** * Is this an XHR request? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ public function isXhr() { return $this->getHeaderLine('X-Requested-With') === 'XMLHttpRequest'; } /** * Retrieves the message's request target. * * Retrieves the message's request-target either as it will appear (for * clients), as it appeared at request (for servers), or as it was * specified for the instance (see withRequestTarget()). * * In most cases, this will be the origin-form of the composed URI, * unless a value was provided to the concrete implementation (see * withRequestTarget() below). * * If no URI is available, and no request-target has been specifically * provided, this method MUST return the string "/". * * @return string */ public function getRequestTarget() { if ($this->requestTarget) { return $this->requestTarget; } if ($this->uri === null) { return '/'; } if ($this->uri instanceof Uri) { $basePath = $this->uri->getBasePath(); } else { $basePath = ''; } $path = $this->uri->getPath(); $path = $basePath . '/' . ltrim($path, '/'); $query = $this->uri->getQuery(); if ($query) { $path .= '?' . $query; } $this->requestTarget = $path; return $this->requestTarget; } /** * Return an instance with the specific request-target. * * If the request needs a non-origin-form request-target — e.g., for * specifying an absolute-form, authority-form, or asterisk-form — * this method may be used to create an instance with the specified * request-target, verbatim. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request target. * * @link http://tools.ietf.org/html/rfc7230#section-2.7 * (for the various request-target forms allowed in request messages) * * @param string $requestTarget * * @return static * * @throws InvalidArgumentException if the request target is invalid */ public function withRequestTarget($requestTarget) { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException( 'Invalid request target provided; must be a string and cannot contain whitespace' ); } $clone = clone $this; $clone->requestTarget = $requestTarget; return $clone; } /** * Retrieves the URI instance. * * This method MUST return a UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * * @return UriInterface */ public function getUri() { return $this->uri; } /** * Returns an instance with the provided URI. * * This method MUST update the Host header of the returned request by * default if the URI contains a host component. If the URI does not * contain a host component, any pre-existing Host header MUST be carried * over to the returned request. * * You can opt-in to preserving the original state of the Host header by * setting `$preserveHost` to `true`. When `$preserveHost` is set to * `true`, this method interacts with the Host header in the following ways: * * - If the the Host header is missing or empty, and the new URI contains * a host component, this method MUST update the Host header in the returned * request. * - If the Host header is missing or empty, and the new URI does not contain a * host component, this method MUST NOT update the Host header in the returned * request. * - If a Host header is present and non-empty, this method MUST NOT update * the Host header in the returned request. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * * @param UriInterface $uri New request URI to use. * @param bool $preserveHost Preserve the original state of the Host header. * * @return static */ public function withUri(UriInterface $uri, $preserveHost = false) { $clone = clone $this; $clone->uri = $uri; if (!$preserveHost) { if ($uri->getHost() !== '') { $clone->headers->set('Host', $uri->getHost()); } } else { if ($uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeaderLine('Host') === '')) { $clone->headers->set('Host', $uri->getHost()); } } return $clone; } /** * Get request content type. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getContentType() { $result = $this->getHeader('Content-Type'); return $result ? $result[0] : null; } /** * Get request media type, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getMediaType() { $contentType = $this->getContentType(); if ($contentType) { $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); return strtolower($contentTypeParts[0]); } return null; } /** * Get request media type params, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string[] */ public function getMediaTypeParams() { $contentType = $this->getContentType(); $contentTypeParams = []; if ($contentType) { $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); $contentTypePartsLength = count($contentTypeParts); for ($i = 1; $i < $contentTypePartsLength; $i++) { $paramParts = explode('=', $contentTypeParts[$i] ? $contentTypeParts[$i] : ""); $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1]; } } return $contentTypeParams; } /** * Get request content character set, if known. * * Note: This method is not part of the PSR-7 standard. * * @return string|null */ public function getContentCharset() { $mediaTypeParams = $this->getMediaTypeParams(); if (isset($mediaTypeParams['charset'])) { return $mediaTypeParams['charset']; } return null; } /** * Get request content length, if known. * * Note: This method is not part of the PSR-7 standard. * * @return int|null */ public function getContentLength() { $result = $this->headers->get('Content-Length'); return $result ? (int)$result[0] : null; } /** * Retrieve cookies. * * Retrieves cookies sent by the client to the server. * * The data MUST be compatible with the structure of the $_COOKIE superglobal. * * @return array */ public function getCookieParams() { return $this->cookies; } /** * Fetch cookie value from cookies sent by the client to the server. * * Note: This method is not part of the PSR-7 standard. * * @param string $key The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * * @return mixed */ public function getCookieParam($key, $default = null) { $cookies = $this->getCookieParams(); $result = $default; if (isset($cookies[$key])) { $result = $cookies[$key]; } return $result; } /** * Return an instance with the specified cookies. * * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST * be compatible with the structure of $_COOKIE. Typically, this data will * be injected at instantiation. * * This method MUST NOT update the related Cookie header of the request * instance, nor related values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated cookie values. * * @param array $cookies Array of key/value pairs representing cookies. * * @return static */ public function withCookieParams(array $cookies) { $clone = clone $this; $clone->cookies = $cookies; return $clone; } /** * Retrieve query string arguments. * * Retrieves the deserialized query string arguments, if any. * * Note: the query params might not be in sync with the URI or server * params. If you need to ensure you are only getting the original * values, you may need to parse the query string from `getUri()->getQuery()` * or from the `QUERY_STRING` server param. * * @return array */ public function getQueryParams() { if (is_array($this->queryParams)) { return $this->queryParams; } if ($this->uri === null) { return []; } parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data return $this->queryParams; } /** * Return an instance with the specified query string arguments. * * These values SHOULD remain immutable over the course of the incoming * request. They MAY be injected during instantiation, such as from PHP's * $_GET superglobal, or MAY be derived from some other value such as the * URI. In cases where the arguments are parsed from the URI, the data * MUST be compatible with what PHP's parse_str() would return for * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * * Setting query string arguments MUST NOT change the URI stored by the * request, nor the values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated query string arguments. * * @param array $query Array of query string arguments, typically from * $_GET. * * @return static */ public function withQueryParams(array $query) { $clone = clone $this; $clone->queryParams = $query; return $clone; } /** * Retrieve normalized file upload data. * * This method returns upload metadata in a normalized tree, with each leaf * an instance of AmeliaPsr\Http\Message\UploadedFileInterface. * * These values MAY be prepared from $_FILES or the message body during * instantiation, or MAY be injected via withUploadedFiles(). * * @return array */ public function getUploadedFiles() { return $this->uploadedFiles; } /** * Create a new instance with the specified uploaded files. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param array $uploadedFiles An array tree of UploadedFileInterface instances. * * @return static * * @throws InvalidArgumentException if an invalid structure is provided. */ public function withUploadedFiles(array $uploadedFiles) { $clone = clone $this; $clone->uploadedFiles = $uploadedFiles; return $clone; } /** * Retrieve server parameters. * * Retrieves data related to the incoming request environment, * typically derived from PHP's $_SERVER superglobal. The data IS NOT * REQUIRED to originate from $_SERVER. * * @return array */ public function getServerParams() { return $this->serverParams; } /** * Retrieve a server parameter. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getServerParam($key, $default = null) { $serverParams = $this->getServerParams(); return isset($serverParams[$key]) ? $serverParams[$key] : $default; } /** * Retrieve attributes derived from the request. * * The request "attributes" may be used to allow injection of any * parameters derived from the request: e.g., the results of path * match operations; the results of decrypting cookies; the results of * deserializing non-form-encoded message bodies; etc. Attributes * will be application and request specific, and CAN be mutable. * * @return array */ public function getAttributes() { return $this->attributes->all(); } /** * Retrieve a single derived request attribute. * * Retrieves a single derived request attribute as described in * getAttributes(). If the attribute has not been previously set, returns * the default value as provided. * * This method obviates the need for a hasAttribute() method, as it allows * specifying a default value to return if the attribute is not found. * * @see getAttributes() * * @param string $name The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * * @return mixed */ public function getAttribute($name, $default = null) { return $this->attributes->get($name, $default); } /** * Return an instance with the specified derived request attribute. * * This method allows setting a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated attribute. * * @see getAttributes() * * @param string $name The attribute name. * @param mixed $value The value of the attribute. * * @return static */ public function withAttribute($name, $value) { $clone = clone $this; $clone->attributes->set($name, $value); return $clone; } /** * Create a new instance with the specified derived request attributes. * * Note: This method is not part of the PSR-7 standard. * * This method allows setting all new derived request attributes as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * updated attributes. * * @param array $attributes New attributes * * @return static */ public function withAttributes(array $attributes) { $clone = clone $this; $clone->attributes = new Collection($attributes); return $clone; } /** * Return an instance that removes the specified derived request attribute. * * This method allows removing a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the attribute. * * @see getAttributes() * * @param string $name The attribute name. * * @return static */ public function withoutAttribute($name) { $clone = clone $this; $clone->attributes->remove($name); return $clone; } /** * Retrieve any parameters provided in the request body. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, this method MUST * return the contents of $_POST. * * Otherwise, this method may return any results of deserializing * the request body content; as parsing returns structured content, the * potential types MUST be arrays or objects only. A null value indicates * the absence of body content. * * @return null|array|object * * @throws RuntimeException if the request body media type parser returns an invalid value */ public function getParsedBody() { if ($this->bodyParsed !== false) { return $this->bodyParsed; } if (!$this->body) { return null; } $mediaType = $this->getMediaType(); // Check if this specific media type has a parser registered first if (!isset($this->bodyParsers[$mediaType])) { // If not, look for a media type with a structured syntax suffix (RFC 6839) $parts = explode('+', $mediaType ? $mediaType : ""); if (count($parts) >= 2) { $mediaType = 'application/' . $parts[count($parts)-1]; } } if (isset($this->bodyParsers[$mediaType])) { $body = (string)$this->getBody(); $parsed = $this->bodyParsers[$mediaType]($body); if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) { throw new RuntimeException( 'Request body media type parser return value must be an array, an object, or null' ); } $this->bodyParsed = $parsed; return $this->bodyParsed; } return null; } /** * Return an instance with the specified body parameters. * * These MAY be injected during instantiation. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, use this method * ONLY to inject the contents of $_POST. * * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of * deserializing the request body content. Deserialization/parsing returns * structured data, and, as such, this method ONLY accepts arrays or objects, * or a null value if nothing was available to parse. * * As an example, if content negotiation determines that the request data * is a JSON payload, this method could be used to create a request * instance with the deserialized parameters. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param null|array|object $data The deserialized body data. This will typically be in an array or object. * * @return static * * @throws InvalidArgumentException if an unsupported argument type is * provided. */ public function withParsedBody($data) { if (!is_null($data) && !is_object($data) && !is_array($data)) { throw new InvalidArgumentException('Parsed body value must be an array, an object, or null'); } $clone = clone $this; $clone->bodyParsed = $data; return $clone; } /** * Force Body to be parsed again. * * Note: This method is not part of the PSR-7 standard. * * @return $this */ public function reparseBody() { $this->bodyParsed = false; return $this; } /** * Register media type parser. * * Note: This method is not part of the PSR-7 standard. * * @param string $mediaType A HTTP media type (excluding content-type params). * @param callable $callable A callable that returns parsed contents for media type. */ public function registerMediaTypeParser($mediaType, callable $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this); } $this->bodyParsers[(string)$mediaType] = $callable; } /** * Fetch request parameter value from body or query string (in that order). * * Note: This method is not part of the PSR-7 standard. * * @param string $key The parameter key. * @param mixed $default The default value. * * @return mixed */ public function getParam($key, $default = null) { $postParams = $this->getParsedBody(); $getParams = $this->getQueryParams(); $result = $default; if (is_array($postParams) && isset($postParams[$key])) { $result = $postParams[$key]; } elseif (is_object($postParams) && property_exists($postParams, $key)) { $result = $postParams->$key; } elseif (isset($getParams[$key])) { $result = $getParams[$key]; } return $result; } /** * Fetch parameter value from request body. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getParsedBodyParam($key, $default = null) { $postParams = $this->getParsedBody(); $result = $default; if (is_array($postParams) && isset($postParams[$key])) { $result = $postParams[$key]; } elseif (is_object($postParams) && property_exists($postParams, $key)) { $result = $postParams->$key; } return $result; } /** * Fetch parameter value from query string. * * Note: This method is not part of the PSR-7 standard. * * @param string $key * @param mixed $default * * @return mixed */ public function getQueryParam($key, $default = null) { $getParams = $this->getQueryParams(); $result = $default; if (isset($getParams[$key])) { $result = $getParams[$key]; } return $result; } /** * Fetch associative array of body and query string parameters. * * Note: This method is not part of the PSR-7 standard. * * @param array|null $only list the keys to retrieve. * * @return array|null */ public function getParams(array $only = null) { $params = $this->getQueryParams(); $postParams = $this->getParsedBody(); if ($postParams) { $params = array_replace($params, (array)$postParams); } if ($only) { $onlyParams = []; foreach ($only as $key) { if (array_key_exists($key, $params)) { $onlyParams[$key] = $params[$key]; } } return $onlyParams; } return $params; } } Slim/Http/Environment.php 0000666 00000003624 15165376503 0011430 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Slim\Collection; use Slim\Interfaces\Http\EnvironmentInterface; /** * This class decouples the Slim application from the global PHP environment. * This is particularly useful for unit testing, but it also lets us create * custom sub-requests. */ class Environment extends Collection implements EnvironmentInterface { /** * {@inheritdoc} */ public static function mock(array $settings = []) { //Validates if default protocol is HTTPS to set default port 443 if ((isset($settings['HTTPS']) && $settings['HTTPS'] !== 'off') || ((isset($settings['REQUEST_SCHEME']) && $settings['REQUEST_SCHEME'] === 'https'))) { $defscheme = 'https'; $defport = 443; } else { $defscheme = 'http'; $defport = 80; } $data = array_merge([ 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_METHOD' => 'GET', 'REQUEST_SCHEME' => $defscheme, 'SCRIPT_NAME' => '', 'REQUEST_URI' => '', 'QUERY_STRING' => '', 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => $defport, 'HTTP_HOST' => 'localhost', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'HTTP_USER_AGENT' => 'Slim Framework', 'REMOTE_ADDR' => '127.0.0.1', 'REQUEST_TIME' => time(), 'REQUEST_TIME_FLOAT' => microtime(true), ], $settings); return new static($data); } } Slim/Http/Cookies.php 0000666 00000010515 15165376503 0010515 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use Slim\Interfaces\Http\CookiesInterface; class Cookies implements CookiesInterface { /** * Cookies from HTTP request * * @var array */ protected $requestCookies = []; /** * Cookies for HTTP response * * @var array */ protected $responseCookies = []; /** * Default cookie properties * * @var array */ protected $defaults = [ 'value' => '', 'domain' => null, 'hostonly' => null, 'path' => null, 'expires' => null, 'secure' => false, 'httponly' => false, 'samesite' => null ]; /** * @param array $cookies */ public function __construct(array $cookies = []) { $this->requestCookies = $cookies; } /** * Set default cookie properties * * @param array $settings */ public function setDefaults(array $settings) { $this->defaults = array_replace($this->defaults, $settings); } /** * {@inheritdoc} */ public function get($name, $default = null) { return isset($this->requestCookies[$name]) ? $this->requestCookies[$name] : $default; } /** * {@inheritdoc} */ public function set($name, $value) { if (!is_array($value)) { $value = ['value' => (string)$value]; } $this->responseCookies[$name] = array_replace($this->defaults, $value); } /** * {@inheritdoc} */ public function toHeaders() { $headers = []; foreach ($this->responseCookies as $name => $properties) { $headers[] = $this->toHeader($name, $properties); } return $headers; } /** * Convert to `Set-Cookie` header * * @param string $name Cookie name * @param array $properties Cookie properties * * @return string */ protected function toHeader($name, array $properties) { $result = urlencode($name) . '=' . urlencode($properties['value']); if (isset($properties['domain'])) { $result .= '; domain=' . $properties['domain']; } if (isset($properties['path'])) { $result .= '; path=' . $properties['path']; } if (isset($properties['expires'])) { if (is_string($properties['expires'])) { $timestamp = strtotime($properties['expires']); } else { $timestamp = (int)$properties['expires']; } if ($timestamp !== 0) { $result .= '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp); } } if (isset($properties['secure']) && $properties['secure']) { $result .= '; secure'; } if (isset($properties['hostonly']) && $properties['hostonly']) { $result .= '; HostOnly'; } if (isset($properties['httponly']) && $properties['httponly']) { $result .= '; HttpOnly'; } if (isset($properties['samesite']) && in_array(strtolower($properties['samesite']), ['lax', 'strict'], true)) { // While strtolower is needed for correct comparison, the RFC doesn't care about case $result .= '; SameSite=' . $properties['samesite']; } return $result; } /** * {@inheritdoc} */ public static function parseHeader($header) { if (is_array($header) === true) { $header = isset($header[0]) ? $header[0] : ''; } if (is_string($header) === false) { throw new InvalidArgumentException('Cannot parse Cookie data. Header value must be a string.'); } $header = rtrim($header, "\r\n"); $pieces = preg_split('@[;]\s*@', $header); $cookies = []; foreach ($pieces as $cookie) { $cookie = explode('=', $cookie, 2); if (count($cookie) === 2) { $key = urldecode($cookie[0]); $value = urldecode($cookie[1]); if (!isset($cookies[$key])) { $cookies[$key] = $value; } } } return $cookies; } } Slim/Http/Headers.php 0000666 00000012455 15165376503 0010501 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use Slim\Collection; use Slim\Interfaces\Http\HeadersInterface; /** * This class represents a collection of HTTP headers * that is used in both the HTTP request and response objects. * It also enables header name case-insensitivity when * getting or setting a header value. * * Each HTTP header can have multiple values. This class * stores values into an array for each header name. When * you request a header value, you receive an array of values * for that header. */ class Headers extends Collection implements HeadersInterface { /** * Special HTTP headers that do not have the "HTTP_" prefix * * @var array */ protected static $special = [ 'CONTENT_TYPE' => 1, 'CONTENT_LENGTH' => 1, 'PHP_AUTH_USER' => 1, 'PHP_AUTH_PW' => 1, 'PHP_AUTH_DIGEST' => 1, 'AUTH_TYPE' => 1, ]; /** * Create new headers collection with data extracted from the application Environment object * * @param Environment $environment The Slim application Environment * * @return self */ public static function createFromEnvironment(Environment $environment) { $data = []; $environment = self::determineAuthorization($environment); foreach ($environment as $key => $value) { $key = strtoupper($key); if (isset(static::$special[$key]) || strpos($key, 'HTTP_') === 0) { if ($key !== 'HTTP_CONTENT_LENGTH') { $data[$key] = $value; } } } return new static($data); } /** * If HTTP_AUTHORIZATION does not exist tries to get it from getallheaders() when available. * * @param Environment $environment The Slim application Environment * * @return Environment */ public static function determineAuthorization(Environment $environment) { $authorization = $environment->get('HTTP_AUTHORIZATION'); if (!empty($authorization) || !is_callable('getallheaders')) { return $environment; } $headers = getallheaders(); if (!is_array($headers)) { return $environment; } $headers = array_change_key_case($headers, CASE_LOWER); if (isset($headers['authorization'])) { $environment->set('HTTP_AUTHORIZATION', $headers['authorization']); } return $environment; } /** * Return array of HTTP header names and values. * This method returns the _original_ header name as specified by the end user. * * @return array */ public function all() { $all = parent::all(); $out = []; foreach ($all as $key => $props) { $out[$props['originalKey']] = $props['value']; } return $out; } /** * Set HTTP header value * * This method sets a header value. It replaces * any values that may already exist for the header name. * * @param string $key The case-insensitive header name * @param array|string $value The header value */ public function set($key, $value) { if (!is_array($value)) { $value = [$value]; } parent::set($this->normalizeKey($key), [ 'value' => $value, 'originalKey' => $key ]); } /** * Get HTTP header value * * @param string $key The case-insensitive header name * @param mixed $default The default value if key does not exist * * @return string[] */ public function get($key, $default = null) { if ($this->has($key)) { return parent::get($this->normalizeKey($key))['value']; } return $default; } /** * Get HTTP header key as originally specified * * @param string $key The case-insensitive header name * @param mixed $default The default value if key does not exist * * @return string */ public function getOriginalKey($key, $default = null) { if ($this->has($key)) { return parent::get($this->normalizeKey($key))['originalKey']; } return $default; } /** * {@inheritdoc} */ public function add($key, $value) { $oldValues = $this->get($key, []); $newValues = is_array($value) ? $value : [$value]; $this->set($key, array_merge($oldValues, array_values($newValues))); } /** * Does this collection have a given header? * * @param string $key The case-insensitive header name * * @return bool */ public function has($key) { return parent::has($this->normalizeKey($key)); } /** * Remove header from collection * * @param string $key The case-insensitive header name */ public function remove($key) { parent::remove($this->normalizeKey($key)); } /** * {@inheritdoc} */ public function normalizeKey($key) { $key = strtr(strtolower($key), '_', '-'); if (strpos($key, 'http-') === 0) { $key = substr($key, 5); } return $key; } } Slim/Http/NonBufferedBody.php 0000666 00000004053 15165376503 0012134 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use AmeliaPsr\Http\Message\StreamInterface; /** * Represents a non-readable stream that whenever it is written pushes * the data back to the browser immediately. */ class NonBufferedBody implements StreamInterface { /** * {@inheritdoc} */ public function __toString() { return ''; } /** * {@inheritdoc} */ public function close() { } /** * {@inheritdoc} */ public function detach() { return null; } /** * {@inheritdoc} */ public function getSize() { return null; } /** * {@inheritdoc} */ public function tell() { return 0; } /** * {@inheritdoc} */ public function eof() { return true; } /** * {@inheritdoc} */ public function isSeekable() { return false; } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) { } /** * {@inheritdoc} */ public function rewind() { } /** * {@inheritdoc} */ public function isWritable() { return true; } /** * {@inheritdoc} */ public function write($string) { $buffered = ''; while (0 < ob_get_level()) { $buffered = ob_get_clean() . $buffered; } echo $buffered . $string; flush(); return strlen($string) + strlen($buffered); } /** * {@inheritdoc} */ public function isReadable() { return false; } /** * {@inheritdoc} */ public function read($length) { return ''; } /** * {@inheritdoc} */ public function getContents() { return ''; } /** * {@inheritdoc} */ public function getMetadata($key = null) { return null; } } Slim/Http/Body.php 0000666 00000000302 15165376503 0010007 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; class Body extends Stream { } Slim/Http/Uri.php 0000666 00000063037 15165376503 0007667 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Http; use InvalidArgumentException; use AmeliaPsr\Http\Message\UriInterface; /** * Value object representing a URI. * * This interface is meant to represent URIs according to RFC 3986 and to * provide methods for most common operations. Additional functionality for * working with URIs can be provided on top of the interface or externally. * Its primary use is for HTTP requests, but may also be used in other * contexts. * * Instances of this interface are considered immutable; all methods that * might change state MUST be implemented such that they retain the internal * state of the current instance and return an instance that contains the * changed state. * * Typically the Host header will be also be present in the request message. * For server-side requests, the scheme will typically be discoverable in the * server parameters. * * @link http://tools.ietf.org/html/rfc3986 (the URI specification) */ class Uri implements UriInterface { /** * Uri scheme (without "://" suffix) * * @var string */ protected $scheme = ''; /** * Uri user * * @var string */ protected $user = ''; /** * Uri password * * @var string */ protected $password = ''; /** * Uri host * * @var string */ protected $host = ''; /** * Uri port number * * @var null|int */ protected $port; /** * Uri base path * * @var string */ protected $basePath = ''; /** * Uri path * * @var string */ protected $path = ''; /** * Uri query string (without "?" prefix) * * @var string */ protected $query = ''; /** * Uri fragment string (without "#" prefix) * * @var string */ protected $fragment = ''; /** * @param string $scheme Uri scheme. * @param string $host Uri host. * @param int $port Uri port number. * @param string $path Uri path. * @param string $query Uri query string. * @param string $fragment Uri fragment. * @param string $user Uri user. * @param string $password Uri password. */ public function __construct( $scheme, $host, $port = null, $path = '/', $query = '', $fragment = '', $user = '', $password = '' ) { $this->scheme = $this->filterScheme($scheme); $this->host = $host; $this->port = $this->filterPort($port); $this->path = ($path === null || !strlen($path)) ? '/' : $this->filterPath($path); $this->query = $this->filterQuery($query); $this->fragment = $this->filterQuery($fragment); $this->user = $user; $this->password = $password; } /** * Create new Uri from string. * * @param string $uri Complete Uri string (i.e., https://user:pass@host:443/path?query). * * @return self */ public static function createFromString($uri) { if (!is_string($uri) && !method_exists($uri, '__toString')) { throw new InvalidArgumentException('Uri must be a string'); } $parts = parse_url($uri); $scheme = isset($parts['scheme']) ? $parts['scheme'] : ''; $user = isset($parts['user']) ? $parts['user'] : ''; $pass = isset($parts['pass']) ? $parts['pass'] : ''; $host = isset($parts['host']) ? $parts['host'] : ''; $port = isset($parts['port']) ? $parts['port'] : null; $path = isset($parts['path']) ? $parts['path'] : ''; $query = isset($parts['query']) ? $parts['query'] : ''; $fragment = isset($parts['fragment']) ? $parts['fragment'] : ''; return new static($scheme, $host, $port, $path, $query, $fragment, $user, $pass); } /** * Create new Uri from environment. * * @param Environment $env * * @return self */ public static function createFromEnvironment(Environment $env) { // Scheme $isSecure = $env->get('HTTPS'); $scheme = (empty($isSecure) || $isSecure === 'off') ? 'http' : 'https'; // Authority: Username and password $username = $env->get('PHP_AUTH_USER', ''); $password = $env->get('PHP_AUTH_PW', ''); // Authority: Host and Port if ($env->has('HTTP_HOST')) { $host = $env->get('HTTP_HOST'); // set a port default $port = null; } else { $host = $env->get('SERVER_NAME'); // set a port default $port = (int)$env->get('SERVER_PORT', 80); } if (preg_match('/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/', $host, $matches)) { $host = $matches[1]; if (isset($matches[2])) { $port = (int) substr($matches[2], 1); } } else { $pos = strpos($host, ':'); if ($pos !== false) { $port = (int) substr($host, $pos + 1); $host = strstr($host, ':', true); } } // Path $requestScriptName = (string) parse_url($env->get('SCRIPT_NAME'), PHP_URL_PATH); $requestScriptDir = dirname($requestScriptName); // parse_url() requires a full URL. As we don't extract the domain name or scheme, // we use a stand-in. $requestUri = (string) parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_PATH); $basePath = ''; $virtualPath = $requestUri; if (stripos($requestUri, $requestScriptName) === 0) { $basePath = $requestScriptName; } elseif ($requestScriptDir !== '/' && stripos($requestUri, $requestScriptDir) === 0) { $basePath = $requestScriptDir; } if ($basePath) { $virtualPath = ltrim(substr($requestUri, strlen($basePath)), '/'); } // Query string $queryString = $env->get('QUERY_STRING', ''); if ($queryString === '') { $queryString = parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_QUERY); } // Fragment $fragment = ''; // Build Uri $uri = new static($scheme, $host, $port, $virtualPath, $queryString, $fragment, $username, $password); if ($basePath) { $uri = $uri->withBasePath($basePath); } return $uri; } /** * Retrieve the scheme component of the URI. * * If no scheme is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.1. * * The trailing ":" character is not part of the scheme and MUST NOT be * added. * * @see https://tools.ietf.org/html/rfc3986#section-3.1 * * @return string The URI scheme. */ public function getScheme() { return $this->scheme; } /** * Return an instance with the specified scheme. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified scheme. * * Implementations MUST support the schemes "http" and "https" case * insensitively, and MAY accommodate other schemes if required. * * An empty scheme is equivalent to removing the scheme. * * @param string $scheme The scheme to use with the new instance. * * @return self A new instance with the specified scheme. * * @throws InvalidArgumentException for invalid or unsupported schemes. */ public function withScheme($scheme) { $scheme = $this->filterScheme($scheme); $clone = clone $this; $clone->scheme = $scheme; return $clone; } /** * Filter Uri scheme. * * @param string $scheme Raw Uri scheme. * @return string * * @throws InvalidArgumentException If the Uri scheme is not a string. * @throws InvalidArgumentException If Uri scheme is not "", "https", or "http". */ protected function filterScheme($scheme) { static $valid = [ '' => true, 'https' => true, 'http' => true, ]; if (!is_string($scheme) && !method_exists($scheme, '__toString')) { throw new InvalidArgumentException('Uri scheme must be a string'); } $scheme = str_replace('://', '', strtolower((string)$scheme)); if (!isset($valid[$scheme])) { throw new InvalidArgumentException('Uri scheme must be one of: "", "https", "http"'); } return $scheme; } /** * Retrieve the authority component of the URI. * * If no authority information is present, this method MUST return an empty * string. * * The authority syntax of the URI is: * * <pre> * [user-info@]host[:port] * </pre> * * If the port component is not set or is the standard port for the current * scheme, it SHOULD NOT be included. * * @see https://tools.ietf.org/html/rfc3986#section-3.2 * * @return string The URI authority, in "[user-info@]host[:port]" format. */ public function getAuthority() { $userInfo = $this->getUserInfo(); $host = $this->getHost(); $port = $this->getPort(); return ($userInfo !== '' ? $userInfo . '@' : '') . $host . ($port !== null ? ':' . $port : ''); } /** * Retrieve the user information component of the URI. * * If no user information is present, this method MUST return an empty * string. * * If a user is present in the URI, this will return that value; * additionally, if the password is also present, it will be appended to the * user value, with a colon (":") separating the values. * * The trailing "@" character is not part of the user information and MUST * NOT be added. * * @return string The URI user information, in "username[:password]" format. */ public function getUserInfo() { return $this->user . ($this->password !== '' ? ':' . $this->password : ''); } /** * Return an instance with the specified user information. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified user information. * * Password is optional, but the user information MUST include the * user; an empty string for the user is equivalent to removing user * information. * * @param string $user The user name to use for authority. * @param null|string $password The password associated with $user. * * @return self A new instance with the specified user information. */ public function withUserInfo($user, $password = null) { $clone = clone $this; $clone->user = $this->filterUserInfo($user); if ('' !== $clone->user) { $clone->password = !in_array($password, [null, ''], true) ? $this->filterUserInfo($password) : ''; } else { $clone->password = ''; } return $clone; } /** * Filters the user info string. * * @param string $query The raw uri query string. * * @return string The percent-encoded query string. */ protected function filterUserInfo($query) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u', function ($match) { return rawurlencode($match[0]); }, $query ); } /** * Retrieve the host component of the URI. * * If no host is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.2.2. * * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 * * @return string The URI host. */ public function getHost() { return $this->host; } /** * Return an instance with the specified host. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified host. * * An empty host value is equivalent to removing the host. * * @param string $host The hostname to use with the new instance. * * @return self A new instance with the specified host. */ public function withHost($host) { $clone = clone $this; $clone->host = $host; return $clone; } /** * Retrieve the port component of the URI. * * If a port is present, and it is non-standard for the current scheme, * this method MUST return it as an integer. If the port is the standard port * used with the current scheme, this method SHOULD return null. * * If no port is present, and no scheme is present, this method MUST return * a null value. * * If no port is present, but a scheme is present, this method MAY return * the standard port for that scheme, but SHOULD return null. * * @return null|int The URI port. */ public function getPort() { return $this->port && !$this->hasStandardPort() ? $this->port : null; } /** * Return an instance with the specified port. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified port. * * Implementations MUST raise an exception for ports outside the * established TCP and UDP port ranges. * * A null value provided for the port is equivalent to removing the port * information. * * @param null|int $port The port to use with the new instance; a null value * removes the port information. * * @return self A new instance with the specified port. */ public function withPort($port) { $port = $this->filterPort($port); $clone = clone $this; $clone->port = $port; return $clone; } /** * Does this Uri use a standard port? * * @return bool */ protected function hasStandardPort() { return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); } /** * Filter Uri port. * * @param null|int $port The Uri port number. * @return null|int * * @throws InvalidArgumentException If the port is invalid. */ protected function filterPort($port) { if (is_null($port) || (is_integer($port) && ($port >= 1 && $port <= 65535))) { return $port; } throw new InvalidArgumentException('Uri port must be null or an integer between 1 and 65535 (inclusive)'); } /** * Retrieve the path component of the URI. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * Normally, the empty path "" and absolute path "/" are considered equal as * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically * do this normalization because in contexts with a trimmed base path, e.g. * the front controller, this difference becomes significant. It's the task * of the user to handle both "" and "/". * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.3. * * As an example, if the value should include a slash ("/") not intended as * delimiter between path segments, that value MUST be passed in encoded * form (e.g., "%2F") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.3 * * @return string The URI path. */ public function getPath() { return $this->path; } /** * Return an instance with the specified path. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified path. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all three syntaxes. * * If the path is intended to be domain-relative rather than path relative then * it must begin with a slash ("/"). Paths not starting with a slash ("/") * are assumed to be relative to some base path known to the application or * consumer. * * Users can provide both encoded and decoded path characters. * Implementations ensure the correct encoding as outlined in getPath(). * * @param string $path The path to use with the new instance. * * @return static A new instance with the specified path. * * @throws InvalidArgumentException For invalid paths. */ public function withPath($path) { if (!is_string($path)) { throw new InvalidArgumentException('Uri path must be a string'); } $clone = clone $this; $clone->path = $this->filterPath($path); // if the path is absolute, then clear basePath if (substr($path, 0, 1) == '/') { $clone->basePath = ''; } return $clone; } /** * Retrieve the base path segment of the URI. * * Note: This method is not part of the PSR-7 standard. * * This method MUST return a string; if no path is present it MUST return * an empty string. * * @return string The base path segment of the URI. */ public function getBasePath() { return $this->basePath; } /** * Set base path. * * Note: This method is not part of the PSR-7 standard. * * @param string $basePath * * @return static */ public function withBasePath($basePath) { if (!is_string($basePath)) { throw new InvalidArgumentException('Uri path must be a string'); } if (!empty($basePath)) { $basePath = '/' . trim($basePath, '/'); // <-- Trim on both sides } $clone = clone $this; if ($basePath !== '/') { $clone->basePath = $this->filterPath($basePath); } return $clone; } /** * Filter Uri path. * * Returns a RFC 3986 percent-encoded uri path. * * This method percent-encodes all reserved * characters in the provided path string. This method * will NOT double-encode characters that are already * percent-encoded. * * @param string $path The raw uri path. * * @return string * * @link http://www.faqs.org/rfcs/rfc3986.html */ protected function filterPath($path) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/', function ($match) { return rawurlencode($match[0]); }, $path ); } /** * Retrieve the query string of the URI. * * If no query string is present, this method MUST return an empty string. * * The leading "?" character is not part of the query and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.4. * * As an example, if a value in a key/value pair of the query string should * include an ampersand ("&") not intended as a delimiter between values, * that value MUST be passed in encoded form (e.g., "%26") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.4 * * @return string */ public function getQuery() { return $this->query; } /** * Return an instance with the specified query string. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified query string. * * Users can provide both encoded and decoded query characters. * Implementations ensure the correct encoding as outlined in getQuery(). * * An empty query string value is equivalent to removing the query string. * * @param string $query The query string to use with the new instance. * * @return self A new instance with the specified query string. * * @throws InvalidArgumentException For invalid query strings. */ public function withQuery($query) { if (!is_string($query) && !method_exists($query, '__toString')) { throw new InvalidArgumentException('Uri query must be a string'); } $query = ltrim((string)$query, '?'); $clone = clone $this; $clone->query = $this->filterQuery($query); return $clone; } /** * Filters the query string or fragment of a URI. * * @param string $query The raw uri query string. * * @return string The percent-encoded query string. */ protected function filterQuery($query) { return preg_replace_callback( '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/', function ($match) { return rawurlencode($match[0]); }, $query ); } /** * Retrieve the fragment component of the URI. * * If no fragment is present, this method MUST return an empty string. * * The leading "#" character is not part of the fragment and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.5. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.5 * * @return string The URI fragment. */ public function getFragment() { return $this->fragment; } /** * Return an instance with the specified URI fragment. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified URI fragment. * * Users can provide both encoded and decoded fragment characters. * Implementations ensure the correct encoding as outlined in getFragment(). * * An empty fragment value is equivalent to removing the fragment. * * @param string $fragment The fragment to use with the new instance. * * @return static A new instance with the specified fragment. */ public function withFragment($fragment) { if (!is_string($fragment) && !method_exists($fragment, '__toString')) { throw new InvalidArgumentException('Uri fragment must be a string'); } $fragment = ltrim((string)$fragment, '#'); $clone = clone $this; $clone->fragment = $this->filterQuery($fragment); return $clone; } /** * Return the string representation as a URI reference. * * Depending on which components of the URI are present, the resulting * string is either a full URI or relative reference according to RFC 3986, * Section 4.1. The method concatenates the various components of the URI, * using the appropriate delimiters: * * - If a scheme is present, it MUST be suffixed by ":". * - If an authority is present, it MUST be prefixed by "//". * - The path can be concatenated without delimiters. But there are two * cases where the path has to be adjusted to make the URI reference * valid as PHP does not allow to throw an exception in __toString(): * - If the path is rootless and an authority is present, the path MUST * be prefixed by "/". * - If the path is starting with more than one "/" and no authority is * present, the starting slashes MUST be reduced to one. * - If a query is present, it MUST be prefixed by "?". * - If a fragment is present, it MUST be prefixed by "#". * * @see http://tools.ietf.org/html/rfc3986#section-4.1 * * @return string */ public function __toString() { $scheme = $this->getScheme(); $authority = $this->getAuthority(); $basePath = $this->getBasePath(); $path = $this->getPath(); $query = $this->getQuery(); $fragment = $this->getFragment(); $path = $basePath . '/' . ltrim($path, '/'); return ($scheme !== '' ? $scheme . ':' : '') . ($authority !== '' ? '//' . $authority : '') . $path . ($query !== '' ? '?' . $query : '') . ($fragment !== '' ? '#' . $fragment : ''); } /** * Return the fully qualified base URL. * * Note that this method never includes a trailing / * * This method is not part of PSR-7. * * @return string */ public function getBaseUrl() { $scheme = $this->getScheme(); $authority = $this->getAuthority(); $basePath = $this->getBasePath(); if ($authority !== '' && substr($basePath, 0, 1) !== '/') { $basePath = $basePath . '/' . $basePath; } return ($scheme !== '' ? $scheme . ':' : '') . ($authority ? '//' . $authority : '') . rtrim($basePath, '/'); } } Slim/DeferredCallable.php 0000666 00000002213 15165376503 0011336 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use AmeliaPsr\Container\ContainerInterface; class DeferredCallable { use CallableResolverAwareTrait; /** * @var callable|string */ private $callable; /** * @var ContainerInterface */ private $container; /** * @param callable|string $callable * @param ContainerInterface $container */ public function __construct($callable, ContainerInterface $container = null) { $this->callable = $callable; $this->container = $container; } /** * @return callable|string */ public function getCallable() { return $this->callable; } /** * @return mixed */ public function __invoke() { $callable = $this->resolveCallable($this->callable); if ($callable instanceof Closure) { $callable = $callable->bindTo($this->container); } $args = func_get_args(); return call_user_func_array($callable, $args); } } Slim/DefaultServicesProvider.php 0000666 00000014410 15165376503 0013003 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Handlers\Error; use Slim\Handlers\NotAllowed; use Slim\Handlers\NotFound; use Slim\Handlers\PhpError; use Slim\Handlers\Strategies\RequestResponse; use Slim\Http\Environment; use Slim\Http\Headers; use Slim\Http\Request; use Slim\Http\Response; use Slim\Interfaces\CallableResolverInterface; use Slim\Interfaces\InvocationStrategyInterface; use Slim\Interfaces\RouterInterface; class DefaultServicesProvider { /** * Register Slim's default services. * * @param Container $container A DI container implementing ArrayAccess and psr/container. */ public function register($container) { if (!isset($container['environment'])) { /** * This service MUST return a shared instance of \Slim\Http\Environment. * * @return Environment */ $container['environment'] = function () { return new Environment($_SERVER); }; } if (!isset($container['request'])) { /** * PSR-7 Request object * * @param Container $container * * @return ServerRequestInterface */ $container['request'] = function ($container) { return Request::createFromEnvironment($container->get('environment')); }; } if (!isset($container['response'])) { /** * PSR-7 Response object * * @param Container $container * * @return ResponseInterface */ $container['response'] = function ($container) { $headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']); $response = new Response(200, $headers); return $response->withProtocolVersion($container->get('settings')['httpVersion']); }; } if (!isset($container['router'])) { /** * This service MUST return a shared instance of \Slim\Interfaces\RouterInterface. * * @param Container $container * * @return RouterInterface */ $container['router'] = function ($container) { $routerCacheFile = false; if (isset($container->get('settings')['routerCacheFile'])) { $routerCacheFile = $container->get('settings')['routerCacheFile']; } $router = (new Router)->setCacheFile($routerCacheFile); if (method_exists($router, 'setContainer')) { $router->setContainer($container); } return $router; }; } if (!isset($container['foundHandler'])) { /** * This service MUST return a SHARED instance InvocationStrategyInterface. * * @return InvocationStrategyInterface */ $container['foundHandler'] = function () { return new RequestResponse; }; } if (!isset($container['phpErrorHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * 3. Instance of Error * * The callable MUST return an instance of ResponseInterface. * * @param Container $container * * @return callable */ $container['phpErrorHandler'] = function ($container) { return new PhpError($container->get('settings')['displayErrorDetails']); }; } if (!isset($container['errorHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of \AmeliaPsr\Http\Message\ServerRequestInterface * 2. Instance of \AmeliaPsr\Http\Message\ResponseInterface * 3. Instance of \Exception * * The callable MUST return an instance of ResponseInterface. * * @param Container $container * * @return callable */ $container['errorHandler'] = function ($container) { return new Error( $container->get('settings')['displayErrorDetails'] ); }; } if (!isset($container['notFoundHandler'])) { /** * This service MUST return a callable that accepts two arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * * The callable MUST return an instance of ResponseInterface. * * @return callable */ $container['notFoundHandler'] = function () { return new NotFound; }; } if (!isset($container['notAllowedHandler'])) { /** * This service MUST return a callable that accepts three arguments: * * 1. Instance of ServerRequestInterface * 2. Instance of ResponseInterface * 3. Array of allowed HTTP methods * * The callable MUST return an instance of ResponseInterface. * * @return callable */ $container['notAllowedHandler'] = function () { return new NotAllowed; }; } if (!isset($container['callableResolver'])) { /** * Instance of CallableResolverInterface * * @param Container $container * * @return CallableResolverInterface */ $container['callableResolver'] = function ($container) { return new CallableResolver($container); }; } } } Slim/Router.php 0000666 00000027321 15165376503 0007465 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser; use FastRoute\RouteParser\Std as StdParser; use InvalidArgumentException; use AmeliaPsr\Container\ContainerInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use AmeliaPsr\Http\Message\UriInterface; use RuntimeException; use Slim\Interfaces\RouteInterface; use Slim\Interfaces\RouterInterface; /** * This class organizes Slim application route objects. It is responsible * for registering route objects, assigning names to route objects, * finding routes that match the current HTTP request, and creating * URLs for a named route. */ class Router implements RouterInterface { /** * Container Interface * * @var ContainerInterface */ protected $container; /** * Parser * * @var RouteParser */ protected $routeParser; /** * Base path used in pathFor() * * @var string */ protected $basePath = ''; /** * Path to fast route cache file. Set to false to disable route caching * * @var string|False */ protected $cacheFile = false; /** * Routes * * @var Route[] */ protected $routes = []; /** * Route counter incrementer * @var int */ protected $routeCounter = 0; /** * Route groups * * @var RouteGroup[] */ protected $routeGroups = []; /** * @var Dispatcher */ protected $dispatcher; /** * @param RouteParser $parser */ public function __construct(RouteParser $parser = null) { $this->routeParser = $parser ?: new StdParser; } /** * Set the base path used in pathFor() * * @param string $basePath * * @return static * @throws InvalidArgumentException */ public function setBasePath($basePath) { if (!is_string($basePath)) { throw new InvalidArgumentException('Router basePath must be a string'); } $this->basePath = $basePath; return $this; } /** * Get the base path used in pathFor() * * @return string */ public function getBasePath() { return $this->basePath; } /** * Set path to fast route cache file. If this is false then route caching is disabled. * * @param string|false $cacheFile * * @return static * * @throws InvalidArgumentException If cacheFile is not a string or not false * @throws RuntimeException If cacheFile directory is not writable */ public function setCacheFile($cacheFile) { if (!is_string($cacheFile) && $cacheFile !== false) { throw new InvalidArgumentException('Router cache file must be a string or false'); } if ($cacheFile && file_exists($cacheFile) && !is_readable($cacheFile)) { throw new RuntimeException( sprintf('Router cache file `%s` is not readable', $cacheFile) ); } if ($cacheFile && !file_exists($cacheFile) && !is_writable(dirname($cacheFile))) { throw new RuntimeException( sprintf('Router cache file directory `%s` is not writable', dirname($cacheFile)) ); } $this->cacheFile = $cacheFile; return $this; } /** * @param ContainerInterface $container */ public function setContainer(ContainerInterface $container) { $this->container = $container; } /** * {@inheritdoc} */ public function map($methods, $pattern, $handler) { if (!is_string($pattern)) { throw new InvalidArgumentException('Route pattern must be a string'); } // Prepend parent group pattern(s) if ($this->routeGroups) { $pattern = $this->processGroups() . $pattern; } // According to RFC methods are defined in uppercase (See RFC 7231) $methods = array_map("strtoupper", $methods); /** @var Route $route */ $route = $this->createRoute($methods, $pattern, $handler); $this->routes[$route->getIdentifier()] = $route; $this->routeCounter++; return $route; } /** * {@inheritdoc} */ public function dispatch(ServerRequestInterface $request) { $uri = '/' . ltrim($request->getUri()->getPath(), '/'); return $this->createDispatcher()->dispatch( $request->getMethod(), $uri ); } /** * Create a new Route object * * @param string[] $methods Array of HTTP methods * @param string $pattern The route pattern * @param callable $callable The route callable * * @return RouteInterface */ protected function createRoute($methods, $pattern, $callable) { $route = new Route($methods, $pattern, $callable, $this->routeGroups, $this->routeCounter); if (!empty($this->container)) { $route->setContainer($this->container); } return $route; } /** * @return Dispatcher */ protected function createDispatcher() { if ($this->dispatcher) { return $this->dispatcher; } $routeDefinitionCallback = function (RouteCollector $r) { foreach ($this->getRoutes() as $route) { $r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier()); } }; if ($this->cacheFile) { $this->dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [ 'routeParser' => $this->routeParser, 'cacheFile' => $this->cacheFile, ]); } else { $this->dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [ 'routeParser' => $this->routeParser, ]); } return $this->dispatcher; } /** * @param Dispatcher $dispatcher */ public function setDispatcher(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } /** * Get route objects * * @return Route[] */ public function getRoutes() { return $this->routes; } /** * {@inheritdoc} */ public function getNamedRoute($name) { foreach ($this->routes as $route) { if ($name == $route->getName()) { return $route; } } throw new RuntimeException('Named route does not exist for name: ' . $name); } /** * Remove named route * * @param string $name Route name * * @throws RuntimeException If named route does not exist */ public function removeNamedRoute($name) { $route = $this->getNamedRoute($name); // no exception, route exists, now remove by id unset($this->routes[$route->getIdentifier()]); } /** * Process route groups * * @return string A group pattern to prefix routes with */ protected function processGroups() { $pattern = ""; foreach ($this->routeGroups as $group) { $pattern .= $group->getPattern(); } return $pattern; } /** * {@inheritdoc} */ public function pushGroup($pattern, $callable) { $group = new RouteGroup($pattern, $callable); array_push($this->routeGroups, $group); return $group; } /** * {@inheritdoc} */ public function popGroup() { $group = array_pop($this->routeGroups); return $group instanceof RouteGroup ? $group : false; } /** * {@inheritdoc} */ public function lookupRoute($identifier) { if (!isset($this->routes[$identifier])) { throw new RuntimeException('Route not found, looks like your route cache is stale.'); } return $this->routes[$identifier]; } /** * {@inheritdoc} */ public function relativePathFor($name, array $data = [], array $queryParams = []) { $route = $this->getNamedRoute($name); $pattern = $route->getPattern(); $routeDatas = $this->routeParser->parse($pattern); // $routeDatas is an array of all possible routes that can be made. There is // one routedata for each optional parameter plus one for no optional parameters. // // The most specific is last, so we look for that first. $routeDatas = array_reverse($routeDatas); $segments = []; $segmentName = ''; foreach ($routeDatas as $routeData) { foreach ($routeData as $item) { if (is_string($item)) { // this segment is a static string $segments[] = $item; continue; } // This segment has a parameter: first element is the name if (!array_key_exists($item[0], $data)) { // we don't have a data element for this segment: cancel // testing this routeData item, so that we can try a less // specific routeData item. $segments = []; $segmentName = $item[0]; break; } $segments[] = $data[$item[0]]; } if (!empty($segments)) { // we found all the parameters for this route data, no need to check // less specific ones break; } } if (empty($segments)) { throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName); } $url = implode('', $segments); $hasQueryParams = array_filter($queryParams, function ($value) { return $value !== null; }) !== []; if ($hasQueryParams) { $url .= '?' . http_build_query($queryParams); } return $url; } /** * {@inheritdoc} */ public function pathFor($name, array $data = [], array $queryParams = []) { return $this->urlFor($name, $data, $queryParams); } /** * Build the path for a named route including the base path * * @param string $name Route name * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function urlFor($name, array $data = [], array $queryParams = []) { $url = $this->relativePathFor($name, $data, $queryParams); if ($this->basePath) { $url = $this->basePath . $url; } return $url; } /** * Get fully qualified URL for named route * * @param UriInterface $uri * @param string $routeName * @param array $data Named argument replacement data * @param array $queryParams Optional query string parameters * * @return string * * @throws RuntimeException If named route does not exist * @throws InvalidArgumentException If required data not provided */ public function fullUrlFor(UriInterface $uri, $routeName, array $data = [], array $queryParams = []) { $path = $this->urlFor($routeName, $data, $queryParams); $scheme = $uri->getScheme(); $authority = $uri->getAuthority(); $protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : ''); return $protocol . $path; } } Slim/Handlers/AbstractHandler.php 0000666 00000003031 15165376503 0012776 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ServerRequestInterface; abstract class AbstractHandler { /** * Known handled content types * * @var array */ protected $knownContentTypes = [ 'application/json', 'application/xml', 'text/xml', 'text/html', ]; /** * Determine which content type we know about is wanted using `Accept` header * * Note: This method is a bare-bones implementation designed specifically for * Slim's error handling requirements. Consider a fully-feature solution such * as willdurand/negotiation for any other situation. * * @param ServerRequestInterface $request * * @return string */ protected function determineContentType(ServerRequestInterface $request) { $acceptHeader = $request->getHeaderLine('Accept'); $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes); if (count($selectedContentTypes)) { return current($selectedContentTypes); } // handle +json and +xml specially if (preg_match('/\+(json|xml)/', $acceptHeader, $matches)) { $mediaType = 'application/' . $matches[1]; if (in_array($mediaType, $this->knownContentTypes)) { return $mediaType; } } return 'text/html'; } } Slim/Handlers/Strategies/RequestResponseArgs.php 0000666 00000002200 15165376503 0016030 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers\Strategies; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Interfaces\InvocationStrategyInterface; /** * Route callback strategy with route parameters as individual arguments. */ class RequestResponseArgs implements InvocationStrategyInterface { /** * Invoke a route callable with request, response and all route parameters * as individual arguments. * * @param array|callable $callable * @param ServerRequestInterface $request * @param ResponseInterface $response * @param array $routeArguments * * @return ResponseInterface */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ) { array_unshift($routeArguments, $request, $response); return call_user_func_array($callable, $routeArguments); } } Slim/Handlers/Strategies/RequestResponse.php 0000666 00000002312 15165376503 0015217 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers\Strategies; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Interfaces\InvocationStrategyInterface; /** * Default route callback strategy with route parameters as an array of arguments. */ class RequestResponse implements InvocationStrategyInterface { /** * Invoke a route callable with request, response, and all route parameters * as an array of arguments. * * @param array|callable $callable * @param ServerRequestInterface $request * @param ResponseInterface $response * @param array $routeArguments * * @return ResponseInterface */ public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments ) { foreach ($routeArguments as $k => $v) { $request = $request->withAttribute($k, $v); } return call_user_func($callable, $request, $response, $routeArguments); } } Slim/Handlers/Error.php 0000666 00000015534 15165376503 0011041 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use Exception; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use RuntimeException; use Slim\Http\Body; use UnexpectedValueException; class Error extends AbstractError { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param Exception $exception The caught Exception object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Exception $exception) { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonErrorMessage($exception); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlErrorMessage($exception); break; case 'text/html': $output = $this->renderHtmlErrorMessage($exception); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } $this->writeToErrorLog($exception); $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response ->withStatus(500) ->withHeader('Content-type', $contentType) ->withBody($body); } /** * Render HTML error page * * @param Exception $exception * * @return string */ protected function renderHtmlErrorMessage(Exception $exception) { $title = 'Slim Application Error'; if ($this->displayErrorDetails) { $html = '<p>The application could not run because of the following error:</p>'; $html .= '<h2>Details</h2>'; $html .= $this->renderHtmlException($exception); while ($exception = $exception->getPrevious()) { $html .= '<h2>Previous exception</h2>'; $html .= $this->renderHtmlExceptionOrError($exception); } } else { $html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>'; } $output = sprintf( "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," . "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" . "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>", $title, $title, $html ); return $output; } /** * Render exception as HTML. * * Provided for backwards compatibility; use renderHtmlExceptionOrError(). * * @param Exception $exception * * @return string */ protected function renderHtmlException(Exception $exception) { return $this->renderHtmlExceptionOrError($exception); } /** * Render exception or error as HTML. * * @param Exception|\Error $exception * * @return string * * @throws RuntimeException */ protected function renderHtmlExceptionOrError($exception) { if (!$exception instanceof Exception && !$exception instanceof \Error) { throw new RuntimeException("Unexpected type. Expected Exception or Error."); } $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception)); if (($code = $exception->getCode())) { $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); } if (($message = $exception->getMessage())) { $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message)); } if (($file = $exception->getFile())) { $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); } if (($line = $exception->getLine())) { $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); } if (($trace = $exception->getTraceAsString())) { $html .= '<h2>Trace</h2>'; $html .= sprintf('<pre>%s</pre>', htmlentities($trace)); } return $html; } /** * Render JSON error * * @param Exception $exception * * @return string */ protected function renderJsonErrorMessage(Exception $exception) { $error = [ 'message' => 'Slim Application Error', ]; if ($this->displayErrorDetails) { $error['exception'] = []; do { $error['exception'][] = [ 'type' => get_class($exception), 'code' => $exception->getCode(), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => explode("\n", $exception->getTraceAsString()), ]; } while ($exception = $exception->getPrevious()); } return json_encode($error, JSON_PRETTY_PRINT); } /** * Render XML error * * @param Exception $exception * * @return string */ protected function renderXmlErrorMessage(Exception $exception) { $xml = "<error>\n <message>Slim Application Error</message>\n"; if ($this->displayErrorDetails) { do { $xml .= " <exception>\n"; $xml .= " <type>" . get_class($exception) . "</type>\n"; $xml .= " <code>" . $exception->getCode() . "</code>\n"; $xml .= " <message>" . $this->createCdataSection($exception->getMessage()) . "</message>\n"; $xml .= " <file>" . $exception->getFile() . "</file>\n"; $xml .= " <line>" . $exception->getLine() . "</line>\n"; $xml .= " <trace>" . $this->createCdataSection($exception->getTraceAsString()) . "</trace>\n"; $xml .= " </exception>\n"; } while ($exception = $exception->getPrevious()); } $xml .= "</error>"; return $xml; } /** * Returns a CDATA section with the given content. * * @param string $content * * @return string */ private function createCdataSection($content) { return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content)); } } Slim/Handlers/NotAllowed.php 0000666 00000007466 15165376503 0012025 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use UnexpectedValueException; class NotAllowed extends AbstractHandler { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param string[] $methods Allowed HTTP methods * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $methods) { if ($request->getMethod() === 'OPTIONS') { $status = 200; $contentType = 'text/plain'; $output = $this->renderPlainOptionsMessage($methods); } else { $status = 405; $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonNotAllowedMessage($methods); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlNotAllowedMessage($methods); break; case 'text/html': $output = $this->renderHtmlNotAllowedMessage($methods); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } } $body = new Body(fopen('php://temp', 'r+')); $body->write($output); $allow = implode(', ', $methods); return $response ->withStatus($status) ->withHeader('Content-type', $contentType) ->withHeader('Allow', $allow) ->withBody($body); } /** * Render plain message for OPTIONS response * * @param string[] $methods * * @return string */ protected function renderPlainOptionsMessage($methods) { $allow = implode(', ', $methods); return 'Allowed methods: ' . $allow; } /** * Render JSON not allowed message * * @param string[] $methods * * @return string */ protected function renderJsonNotAllowedMessage($methods) { $allow = implode(', ', $methods); return '{"message":"Method not allowed. Must be one of: ' . $allow . '"}'; } /** * Render XML not allowed message * * @param string[] $methods * * @return string */ protected function renderXmlNotAllowedMessage($methods) { $allow = implode(', ', $methods); return "<root><message>Method not allowed. Must be one of: $allow</message></root>"; } /** * Render HTML not allowed message * * @param string[] $methods * * @return string */ protected function renderHtmlNotAllowedMessage($methods) { $allow = implode(', ', $methods); $output = <<<END <html> <head> <title>Method not allowed</title> <style> body{ margin:0; padding:30px; font:12px/1.5 Helvetica,Arial,Verdana,sans-serif; } h1{ margin:0; font-size:48px; font-weight:normal; line-height:48px; } </style> </head> <body> <h1>Method not allowed</h1> <p>Method not allowed. Must be one of: <strong>$allow</strong></p> </body> </html> END; return $output; } } Slim/Handlers/NotFound.php 0000666 00000007165 15165376503 0011505 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use UnexpectedValueException; class NotFound extends AbstractHandler { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response) { if ($request->getMethod() === 'OPTIONS') { $contentType = 'text/plain'; $output = $this->renderPlainNotFoundOutput(); } else { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonNotFoundOutput(); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlNotFoundOutput(); break; case 'text/html': $output = $this->renderHtmlNotFoundOutput($request); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } } $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response->withStatus(404) ->withHeader('Content-Type', $contentType) ->withBody($body); } /** * Render plain not found message * * @return string */ protected function renderPlainNotFoundOutput() { return 'Not found'; } /** * Return a response for application/json content not found * * @return string */ protected function renderJsonNotFoundOutput() { return '{"message":"Not found"}'; } /** * Return a response for xml content not found * * @return string */ protected function renderXmlNotFoundOutput() { return '<root><message>Not found</message></root>'; } /** * Return a response for text/html content not found * * @param ServerRequestInterface $request The most recent Request object * * @return string */ protected function renderHtmlNotFoundOutput(ServerRequestInterface $request) { $homeUrl = (string)($request->getUri()->withPath('')->withQuery('')->withFragment('')); return <<<END <html> <head> <title>Page Not Found</title> <style> body{ margin:0; padding:30px; font:12px/1.5 Helvetica,Arial,Verdana,sans-serif; } h1{ margin:0; font-size:48px; font-weight:normal; line-height:48px; } strong{ display:inline-block; width:65px; } </style> </head> <body> <h1>Page Not Found</h1> <p> The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below. </p> <a href='$homeUrl'>Visit the Home Page</a> </body> </html> END; } } Slim/Handlers/PhpError.php 0000666 00000014066 15165376503 0011510 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use AmeliaPsr\Http\Message\ResponseInterface; use AmeliaPsr\Http\Message\ServerRequestInterface; use Slim\Http\Body; use Throwable; use UnexpectedValueException; class PhpError extends AbstractError { /** * @param ServerRequestInterface $request The most recent Request object * @param ResponseInterface $response The most recent Response object * @param Throwable $error The caught Throwable object * * @return ResponseInterface * * @throws UnexpectedValueException */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Throwable $error) { $contentType = $this->determineContentType($request); switch ($contentType) { case 'application/json': $output = $this->renderJsonErrorMessage($error); break; case 'text/xml': case 'application/xml': $output = $this->renderXmlErrorMessage($error); break; case 'text/html': $output = $this->renderHtmlErrorMessage($error); break; default: throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); } $this->writeToErrorLog($error); $body = new Body(fopen('php://temp', 'r+')); $body->write($output); return $response ->withStatus(500) ->withHeader('Content-type', $contentType) ->withBody($body); } /** * Render HTML error page * * @param Throwable $error * * @return string */ protected function renderHtmlErrorMessage(Throwable $error) { $title = 'Slim Application Error'; if ($this->displayErrorDetails) { $html = '<p>The application could not run because of the following error:</p>'; $html .= '<h2>Details</h2>'; $html .= $this->renderHtmlError($error); while ($error = $error->getPrevious()) { $html .= '<h2>Previous error</h2>'; $html .= $this->renderHtmlError($error); } } else { $html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>'; } $output = sprintf( "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," . "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" . "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>", $title, $title, $html ); return $output; } /** * Render error as HTML. * * @param Throwable $error * * @return string */ protected function renderHtmlError(Throwable $error) { $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($error)); if (($code = $error->getCode())) { $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); } if (($message = $error->getMessage())) { $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message)); } if (($file = $error->getFile())) { $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); } if (($line = $error->getLine())) { $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); } if (($trace = $error->getTraceAsString())) { $html .= '<h2>Trace</h2>'; $html .= sprintf('<pre>%s</pre>', htmlentities($trace)); } return $html; } /** * Render JSON error * * @param Throwable $error * * @return string */ protected function renderJsonErrorMessage(Throwable $error) { $json = [ 'message' => 'Slim Application Error', ]; if ($this->displayErrorDetails) { $json['error'] = []; do { $json['error'][] = [ 'type' => get_class($error), 'code' => $error->getCode(), 'message' => $error->getMessage(), 'file' => $error->getFile(), 'line' => $error->getLine(), 'trace' => explode("\n", $error->getTraceAsString()), ]; } while ($error = $error->getPrevious()); } return json_encode($json, JSON_PRETTY_PRINT); } /** * Render XML error * * @param Throwable $error * * @return string */ protected function renderXmlErrorMessage(Throwable $error) { $xml = "<error>\n <message>Slim Application Error</message>\n"; if ($this->displayErrorDetails) { do { $xml .= " <error>\n"; $xml .= " <type>" . get_class($error) . "</type>\n"; $xml .= " <code>" . $error->getCode() . "</code>\n"; $xml .= " <message>" . $this->createCdataSection($error->getMessage()) . "</message>\n"; $xml .= " <file>" . $error->getFile() . "</file>\n"; $xml .= " <line>" . $error->getLine() . "</line>\n"; $xml .= " <trace>" . $this->createCdataSection($error->getTraceAsString()) . "</trace>\n"; $xml .= " </error>\n"; } while ($error = $error->getPrevious()); } $xml .= "</error>"; return $xml; } /** * Returns a CDATA section with the given content. * * @param string $content * * @return string */ private function createCdataSection($content) { return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content)); } } Slim/Handlers/AbstractError.php 0000666 00000004560 15165376503 0012522 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim\Handlers; use Exception; use Throwable; abstract class AbstractError extends AbstractHandler { /** * @var bool */ protected $displayErrorDetails; /** * @param bool $displayErrorDetails Set to true to display full details */ public function __construct($displayErrorDetails = false) { $this->displayErrorDetails = (bool) $displayErrorDetails; } /** * Write to the error log if displayErrorDetails is false * * @param Exception|Throwable $throwable * * @return void */ protected function writeToErrorLog($throwable) { if ($this->displayErrorDetails) { return; } $message = 'Slim Application Error:' . PHP_EOL; $message .= $this->renderThrowableAsText($throwable); while ($throwable = $throwable->getPrevious()) { $message .= PHP_EOL . 'Previous error:' . PHP_EOL; $message .= $this->renderThrowableAsText($throwable); } $message .= PHP_EOL . 'View in rendered output by enabling the "displayErrorDetails" setting.' . PHP_EOL; $this->logError($message); } /** * Render error as Text. * * @param Exception|Throwable $throwable * * @return string */ protected function renderThrowableAsText($throwable) { $text = sprintf('Type: %s' . PHP_EOL, get_class($throwable)); if ($code = $throwable->getCode()) { $text .= sprintf('Code: %s' . PHP_EOL, $code); } if ($message = $throwable->getMessage()) { $text .= sprintf('Message: %s' . PHP_EOL, htmlentities($message)); } if ($file = $throwable->getFile()) { $text .= sprintf('File: %s' . PHP_EOL, $file); } if ($line = $throwable->getLine()) { $text .= sprintf('Line: %s' . PHP_EOL, $line); } if ($trace = $throwable->getTraceAsString()) { $text .= sprintf('Trace: %s', $trace); } return $text; } /** * Wraps the error_log function so that this can be easily tested * * @param string $message */ protected function logError($message) { error_log($message); } } Slim/CallableResolverAwareTrait.php 0000666 00000002160 15165376503 0013404 0 ustar 00 <?php /** * Slim Framework (https://slimframework.com) * * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ namespace Slim; use Closure; use AmeliaPsr\Container\ContainerInterface; use RuntimeException; use Slim\Interfaces\CallableResolverInterface; /** * This is an internal class that enables resolution of 'class:method' strings * into a closure. This class is an implementation detail and is used only inside * of the Slim application. */ trait CallableResolverAwareTrait { /** * Resolve a string of the format 'class:method' into a closure that the * router can dispatch. * * @param callable|string $callable * * @return Closure * * @throws RuntimeException If the string cannot be resolved as a callable */ protected function resolveCallable($callable) { if (!$this->container instanceof ContainerInterface) { return $callable; } /** @var CallableResolverInterface $resolver */ $resolver = $this->container->get('callableResolver'); return $resolver->resolve($callable); } } composer.json 0000666 00000003164 15165376503 0007311 0 ustar 00 { "name": "slim/slim", "type": "library", "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", "keywords": ["framework","micro","api","router"], "homepage": "https://slimframework.com", "license": "MIT", "authors": [ { "name": "Josh Lockhart", "email": "hello@joshlockhart.com", "homepage": "https://joshlockhart.com" }, { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", "homepage": "http://silentworks.co.uk" }, { "name": "Rob Allen", "email": "rob@akrabat.com", "homepage": "http://akrabat.com" }, { "name": "Gabriel Manricks", "email": "gmanricks@me.com", "homepage": "http://gabrielmanricks.com" } ], "require": { "php": ">=5.5.0", "ext-json": "*", "ext-libxml": "*", "ext-simplexml": "*", "pimple/pimple": "^3.0", "psr/http-message": "^1.0", "nikic/fast-route": "^1.0", "psr/container": "^1.0" }, "require-dev": { "squizlabs/php_codesniffer": "^2.5", "phpunit/phpunit": "^4.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "autoload": { "psr-4": { "Slim\\": "Slim" } }, "autoload-dev": { "psr-4": { "Slim\\Tests\\": "tests" } }, "scripts": { "test": [ "phpunit", "phpcs" ] } }
| ver. 1.4 |
Github
|
.
| PHP 5.4.45 | Generation time: 0.01 |
proxy
|
phpinfo
|
Settings