diff --git a/- Copy.htaccess b/- Copy.htaccess new file mode 100644 index 0000000..0a1d946 --- /dev/null +++ b/- Copy.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ serviceapp.php [QSA,L] + \ No newline at end of file diff --git a/Middleware/CheckFrom.php b/Middleware/CheckFrom.php new file mode 100644 index 0000000..c79061e --- /dev/null +++ b/Middleware/CheckFrom.php @@ -0,0 +1,33 @@ +getHost(); + $currentRefererRequest = substr($currentRefererRequest, 7); //Senza http:// + $indexDoublePoint = strpos($currentRefererRequest, ':'); + $indexFirstSlash = strpos($currentRefererRequest, '/'); + $currentRefererRequest = substr($currentRefererRequest, 0, $indexDoublePoint > 0 && $indexDoublePoint < $indexFirstSlash ? $indexDoublePoint : $indexFirstSlash ); + if(!in_array($currentRefererRequest, $allowedHost)) + { + return $response->withStatus(500)->write('Generic error occurred'); + } + */ + $currentHostRequest = $app->request()->getHost(); + if(!in_array($currentHostRequest, $allowedHost)) + { + return $response->withStatus(403)->write('Request arrive from host not allowed'); + } + + return $next($request, $response); + } +} \ No newline at end of file diff --git a/MySqlClass - Copy.php b/MySqlClass - Copy.php new file mode 100644 index 0000000..537275a --- /dev/null +++ b/MySqlClass - Copy.php @@ -0,0 +1,89 @@ +attiva) + { + $this->connessione = mysql_connect($this->nomehost,$this->nomeuser,$this->password); + if ($this->connessione == FALSE) + die(mysql_error()); + mysql_select_db($this->mydb, $this->connessione) or die ("Errore nella selezione del database. Verificare i parametri nel file config.inc.php"); + $this->attiva = true; + } + else{ + return true; + } + + } + + public function executeQuery($queryStr) + { + $this->connetti(); + + if (!$res = mysql_query($queryStr, $this->connessione)) die(mysql_error()); + return true; + return false; + } + + public function insertRecord($queryStr) + { + $this->connetti(); + + if (!$res = mysql_query($queryStr, $this->connessione)) die(mysql_error()); + return mysql_insert_id(); + } + + public function queryToObject($queryStr) + { + $this->connetti(); + + $sth = mysql_query($queryStr, $this->connessione) or die(mysql_error()); + + $rows = array(); + while($r = mysql_fetch_assoc($sth)) { + array_push($rows,array_map('utf8_encode', $r)); + } + mysql_free_result($sth); + return $rows; + } + + // funzione per la chiusura della connessione + public function disconnetti() + { + if($this->attiva) + { + if(mysql_close($this->connessione)) + { + $this->attiva = false; + return true; + } + else + { + return false; + } + } + } + + public function __destruct() + { + $this->disconnetti(); + } + } +?> \ No newline at end of file diff --git a/SimpleImage.php b/SimpleImage.php new file mode 100644 index 0000000..bb471ec --- /dev/null +++ b/SimpleImage.php @@ -0,0 +1,104 @@ +image_type = $image_info[2]; + if ($this->image_type == IMAGETYPE_JPEG) { + $this->image = imagecreatefromjpeg($filename); + } elseif ($this->image_type == IMAGETYPE_GIF) { + $this->image = imagecreatefromgif($filename); + } elseif ($this->image_type == IMAGETYPE_PNG) { + $this->image = imagecreatefrompng($filename); + } + } + + function saveas($filename, $image_type = IMAGETYPE_JPEG, $compression = 75, $permissions = null) { + if ($image_type == IMAGETYPE_JPEG) { + imagejpeg($this->image, $filename, $compression); + } elseif ($image_type == IMAGETYPE_GIF) { + imagegif($this->image, $filename); + } elseif ($image_type == IMAGETYPE_PNG) { + imagepng($this->image, $filename); + } if ($permissions != null) { + chmod($filename, $permissions); + } + } + + function save($filename) { + if ($this->image_type == IMAGETYPE_JPEG) { + imagejpeg($this->image, $filename); + } elseif ($this->image_type == IMAGETYPE_GIF) { + imagegif($this->image, $filename); + } elseif ($this->image_type == IMAGETYPE_PNG) { + imagepng($this->image, $filename); + } + } + + function output($image_type = IMAGETYPE_JPEG) { + if ($image_type == IMAGETYPE_JPEG) { + imagejpeg($this->image); + } elseif ($image_type == IMAGETYPE_GIF) { + imagegif($this->image); + } elseif ($image_type == IMAGETYPE_PNG) { + imagepng($this->image); + } + } + + function getWidth() { + return imagesx($this->image); + } + + function getHeight() { + return imagesy($this->image); + } + + function resizeToHeight($height) { + $ratio = $height / $this->getHeight(); + $width = $this->getWidth() * $ratio; + $this->resize($width, $height); + } + + function resizeToWidth($width) { + $ratio = $width / $this->getWidth(); + $height = $this->getheight() * $ratio; + $this->resize($width, $height); + } + + function scale($scale) { + $width = $this->getWidth() * $scale / 100; + $height = $this->getheight() * $scale / 100; + $this->resize($width, $height); + } + + function resize($width, $height) { + $new_image = imagecreatetruecolor($width, $height); + if ($this->image_type == IMAGETYPE_GIF || $this->image_type == IMAGETYPE_PNG) { + $current_transparent = imagecolortransparent($this->image); + if ($current_transparent != -1) { + $transparent_color = imagecolorsforindex($this->image, $current_transparent); + $current_transparent = imagecolorallocate($new_image, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + imagefill($new_image, 0, 0, $current_transparent); + imagecolortransparent($new_image, $current_transparent); + } elseif ($this->image_type == IMAGETYPE_PNG) { + imagealphablending($new_image, false); + $color = imagecolorallocatealpha($new_image, 0, 0, 0, 127); + imagefill($new_image, 0, 0, $color); + imagesavealpha($new_image, true); + } + } + imagecopyresampled($new_image, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight()); + $this->image = $new_image; + } + + function close() + { + imagedestroy($this->image); + } +} + +?> \ No newline at end of file diff --git a/Slim/App.php b/Slim/App.php new file mode 100644 index 0000000..641270c --- /dev/null +++ b/Slim/App.php @@ -0,0 +1,555 @@ +container = $container; + } + + /** + * Enable access to the DI container by consumers of $app + * + * @return ContainerInterface + */ + public function getContainer() + { + return $this->container; + } + + /** + * Add middleware + * + * This method prepends new middleware to the app's middleware stack. + * + * @param mixed $callable The callback routine + * + * @return static + */ + public function add($callable) + { + $callable = $this->resolveCallable($callable); + if ($callable instanceof Closure) { + $callable = $callable->bindTo($this->container); + } + + return $this->addMiddleware($callable); + } + + /** + * Calling a non-existant method on App checks to see if there's an item + * in the container than is callable and if so, calls it. + * + * @param string $method + * @param array $args + * @return mixed + */ + 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); + } + } + } + + /******************************************************************************** + * Router proxy methods + *******************************************************************************/ + + /** + * Add GET route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\RouteInterface + */ + public function get($pattern, $callable) + { + return $this->map(['GET'], $pattern, $callable); + } + + /** + * Add POST route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\RouteInterface + */ + public function post($pattern, $callable) + { + return $this->map(['POST'], $pattern, $callable); + } + + /** + * Add PUT route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\RouteInterface + */ + public function put($pattern, $callable) + { + return $this->map(['PUT'], $pattern, $callable); + } + + /** + * Add PATCH route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\RouteInterface + */ + public function patch($pattern, $callable) + { + return $this->map(['PATCH'], $pattern, $callable); + } + + /** + * Add DELETE route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\RouteInterface + */ + public function delete($pattern, $callable) + { + return $this->map(['DELETE'], $pattern, $callable); + } + + /** + * Add OPTIONS route + * + * @param string $pattern The route URI pattern + * @param mixed $callable The route callback routine + * + * @return \Slim\Interfaces\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 mixed $callable The route callback routine + * + * @return \Slim\Interfaces\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 mixed $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; + } + + /** + * Route Groups + * + * 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 $callable + * + * @return RouteGroupInterface + */ + public function group($pattern, $callable) + { + /** @var RouteGroup $group */ + $group = $this->container->get('router')->pushGroup($pattern, $callable); + $group->setContainer($this->container); + $group($this); + $this->container->get('router')->popGroup(); + return $group; + } + + /******************************************************************************** + * Runner + *******************************************************************************/ + + /** + * 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 MethodNotAllowedException + * @throws NotFoundException + */ + public function run($silent = false) + { + $request = $this->container->get('request'); + $response = $this->container->get('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 (MethodNotAllowedException $e) { + if (!$this->container->has('notAllowedHandler')) { + throw $e; + } + /** @var callable $notAllowedHandler */ + $notAllowedHandler = $this->container->get('notAllowedHandler'); + $response = $notAllowedHandler($e->getRequest(), $e->getResponse(), $e->getAllowedMethods()); + } catch (NotFoundException $e) { + if (!$this->container->has('notFoundHandler')) { + throw $e; + } + /** @var callable $notFoundHandler */ + $notFoundHandler = $this->container->get('notFoundHandler'); + $response = $notFoundHandler($e->getRequest(), $e->getResponse()); + } catch (SlimException $e) { + $response = $e->getResponse(); + } catch (Exception $e) { + if (!$this->container->has('errorHandler')) { + throw $e; + } + /** @var callable $errorHandler */ + $errorHandler = $this->container->get('errorHandler'); + $response = $errorHandler($request, $response, $e); + } + + $response = $this->finalize($response); + + if (!$silent) { + $this->respond($response); + } + + return $response; + } + + /** + * Send the response the client + * + * @param ResponseInterface $response + */ + public function respond(ResponseInterface $response) + { + // Send response + if (!headers_sent()) { + // Status + header(sprintf( + 'HTTP/%s %s %s', + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() + )); + + // Headers + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header(sprintf('%s: %s', $name, $value), false); + } + } + } + + // Body + if (!$this->isEmptyResponse($response)) { + $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(); + } + $totalChunks = ceil($contentLength / $chunkSize); + $lastChunkSize = $contentLength % $chunkSize; + $currentChunk = 0; + while (!$body->eof() && $currentChunk < $totalChunks) { + if (++$currentChunk == $totalChunks && $lastChunkSize > 0) { + $chunkSize = $lastChunkSize; + } + echo $body->read($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 \Slim\Interfaces\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 + */ + 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 + */ + protected function finalize(ResponseInterface $response) + { + // stop PHP sending a Content-Type automatically + ini_set('default_mimetype', ''); + + if ($this->isEmptyResponse($response)) { + return $response->withoutHeader('Content-Type')->withoutHeader('Content-Length'); + } + + $size = $response->getBody()->getSize(); + if ($size !== null && !$response->hasHeader('Content-Length')) { + $response = $response->withHeader('Content-Length', (string) $size); + } + + 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]); + } +} diff --git a/Slim/CallableResolver.php b/Slim/CallableResolver.php new file mode 100644 index 0000000..4dc671e --- /dev/null +++ b/Slim/CallableResolver.php @@ -0,0 +1,87 @@ +container = $container; + } + + /** + * Resolve toResolve into a closure that 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 mixed $toResolve + * + * @return callable + * + * @throws RuntimeException if the callable does not exist + * @throws RuntimeException if the callable is not resolvable + */ + public function resolve($toResolve) + { + $resolved = $toResolve; + + if (!is_callable($toResolve) && is_string($toResolve)) { + // check for slim callable as "class:method" + $callablePattern = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!'; + if (preg_match($callablePattern, $toResolve, $matches)) { + $class = $matches[1]; + $method = $matches[2]; + + if ($this->container->has($class)) { + $resolved = [$this->container->get($class), $method]; + } else { + if (!class_exists($class)) { + throw new RuntimeException(sprintf('Callable %s does not exist', $class)); + } + $resolved = [new $class($this->container), $method]; + } + } else { + // check if string is something in the DIC that's callable or is a class name which + // has an __invoke() method + $class = $toResolve; + if ($this->container->has($class)) { + $resolved = $this->container->get($class); + } else { + if (!class_exists($class)) { + throw new RuntimeException(sprintf('Callable %s does not exist', $class)); + } + $resolved = new $class($this->container); + } + } + } + + if (!is_callable($resolved)) { + throw new RuntimeException(sprintf('%s is not resolvable', $toResolve)); + } + + return $resolved; + } +} diff --git a/Slim/CallableResolverAwareTrait.php b/Slim/CallableResolverAwareTrait.php new file mode 100644 index 0000000..e6bae51 --- /dev/null +++ b/Slim/CallableResolverAwareTrait.php @@ -0,0 +1,47 @@ +container instanceof ContainerInterface) { + return $callable; + } + + /** @var CallableResolverInterface $resolver */ + $resolver = $this->container->get('callableResolver'); + + return $resolver->resolve($callable); + } +} diff --git a/Slim/Collection.php b/Slim/Collection.php new file mode 100644 index 0000000..9540d46 --- /dev/null +++ b/Slim/Collection.php @@ -0,0 +1,204 @@ + $value) { + $this->set($key, $value); + } + } + + /******************************************************************************** + * Collection interface + *******************************************************************************/ + + /** + * Set collection item + * + * @param string $key The data key + * @param mixed $value The data value + */ + public function set($key, $value) + { + $this->data[$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) + { + return $this->has($key) ? $this->data[$key] : $default; + } + + /** + * Add item to collection + * + * @param array $items Key-value array of data to append to this collection + */ + public function replace(array $items) + { + foreach ($items as $key => $value) { + $this->set($key, $value); + } + } + + /** + * Get all items in collection + * + * @return array The collection's source data + */ + 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); + } + + /** + * Does this collection have a given key? + * + * @param string $key The data key + * + * @return bool + */ + public function has($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Remove item from collection + * + * @param string $key The data key + */ + public function remove($key) + { + unset($this->data[$key]); + } + + /** + * Remove all items from collection + */ + public function clear() + { + $this->data = []; + } + + /******************************************************************************** + * ArrayAccess interface + *******************************************************************************/ + + /** + * Does this collection have a given key? + * + * @param string $key The data key + * + * @return bool + */ + 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 + */ + public function offsetGet($key) + { + return $this->get($key); + } + + /** + * Set collection item + * + * @param string $key The data key + * @param mixed $value The data value + */ + public function offsetSet($key, $value) + { + $this->set($key, $value); + } + + /** + * Remove item from collection + * + * @param string $key The data key + */ + public function offsetUnset($key) + { + $this->remove($key); + } + + /** + * Get number of items in collection + * + * @return int + */ + public function count() + { + return count($this->data); + } + + /******************************************************************************** + * IteratorAggregate interface + *******************************************************************************/ + + /** + * Get collection iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->data); + } +} diff --git a/Slim/Container.php b/Slim/Container.php new file mode 100644 index 0000000..fb08a93 --- /dev/null +++ b/Slim/Container.php @@ -0,0 +1,296 @@ + '1.1', + 'responseChunkSize' => 4096, + 'outputBuffering' => 'append', + 'determineRouteBeforeAppMiddleware' => false, + 'displayErrorDetails' => false, + ]; + + /** + * Create new container + * + * @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 - that is, 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)); + }; + + if (!isset($this['environment'])) { + /** + * This service MUST return a shared instance + * of \Slim\Interfaces\Http\EnvironmentInterface. + * + * @return EnvironmentInterface + */ + $this['environment'] = function () { + return new Environment($_SERVER); + }; + } + + if (!isset($this['request'])) { + /** + * PSR-7 Request object + * + * @param Container $c + * + * @return ServerRequestInterface + */ + $this['request'] = function ($c) { + return Request::createFromEnvironment($c->get('environment')); + }; + } + + if (!isset($this['response'])) { + /** + * PSR-7 Response object + * + * @param Container $c + * + * @return ResponseInterface + */ + $this['response'] = function ($c) { + $headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']); + $response = new Response(200, $headers); + + return $response->withProtocolVersion($c->get('settings')['httpVersion']); + }; + } + + if (!isset($this['router'])) { + /** + * This service MUST return a SHARED instance + * of \Slim\Interfaces\RouterInterface. + * + * @return RouterInterface + */ + $this['router'] = function () { + return new Router; + }; + } + + if (!isset($this['foundHandler'])) { + /** + * This service MUST return a SHARED instance + * of \Slim\Interfaces\InvocationStrategyInterface. + * + * @return InvocationStrategyInterface + */ + $this['foundHandler'] = function () { + return new RequestResponse; + }; + } + + if (!isset($this['errorHandler'])) { + /** + * This service MUST return a callable + * that accepts three arguments: + * + * 1. Instance of \Psr\Http\Message\ServerRequestInterface + * 2. Instance of \Psr\Http\Message\ResponseInterface + * 3. Instance of \Exception + * + * The callable MUST return an instance of + * \Psr\Http\Message\ResponseInterface. + * + * @param Container $c + * + * @return callable + */ + $this['errorHandler'] = function ($c) { + return new Error($c->get('settings')['displayErrorDetails']); + }; + } + + if (!isset($this['notFoundHandler'])) { + /** + * This service MUST return a callable + * that accepts two arguments: + * + * 1. Instance of \Psr\Http\Message\ServerRequestInterface + * 2. Instance of \Psr\Http\Message\ResponseInterface + * + * The callable MUST return an instance of + * \Psr\Http\Message\ResponseInterface. + * + * @return callable + */ + $this['notFoundHandler'] = function () { + return new NotFound; + }; + } + + if (!isset($this['notAllowedHandler'])) { + /** + * This service MUST return a callable + * that accepts three arguments: + * + * 1. Instance of \Psr\Http\Message\ServerRequestInterface + * 2. Instance of \Psr\Http\Message\ResponseInterface + * 3. Array of allowed HTTP methods + * + * The callable MUST return an instance of + * \Psr\Http\Message\ResponseInterface. + * + * @return callable + */ + $this['notAllowedHandler'] = function () { + return new NotAllowed; + }; + } + + if (!isset($this['callableResolver'])) { + /** + * Instance of \Slim\Interfaces\CallableResolverInterface + * + * @param Container $c + * + * @return CallableResolverInterface + */ + $this['callableResolver'] = function ($c) { + return new CallableResolver($c); + }; + } + } + + /******************************************************************************** + * Methods to satisfy Interop\Container\ContainerInterface + *******************************************************************************/ + + /** + * Finds an entry of the container by its identifier and returns it. + * + * @param string $id Identifier of the entry to look for. + * + * @throws ContainerValueNotFoundException No entry was found for this identifier. + * @throws ContainerException Error while retrieving the entry. + * + * @return mixed Entry. + */ + public function get($id) + { + if (!$this->offsetExists($id)) { + throw new ContainerValueNotFoundException(sprintf('Identifier "%s" is not defined.', $id)); + } + return $this->offsetGet($id); + } + + /** + * 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); + } + + + /******************************************************************************** + * Magic methods for convenience + *******************************************************************************/ + + public function __get($name) + { + return $this->get($name); + } + + public function __isset($name) + { + return $this->has($name); + } +} diff --git a/Slim/Exception/ContainerValueNotFoundException.php b/Slim/Exception/ContainerValueNotFoundException.php new file mode 100644 index 0000000..9627164 --- /dev/null +++ b/Slim/Exception/ContainerValueNotFoundException.php @@ -0,0 +1,20 @@ +allowedMethods = $allowedMethods; + } + + /** + * Get allowed methods + * + * @return string[] + */ + public function getAllowedMethods() + { + return $this->allowedMethods; + } +} diff --git a/Slim/Exception/NotFoundException.php b/Slim/Exception/NotFoundException.php new file mode 100644 index 0000000..3739085 --- /dev/null +++ b/Slim/Exception/NotFoundException.php @@ -0,0 +1,8 @@ +request = $request; + $this->response = $response; + } + + /** + * Get request + * + * @return ServerRequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get response + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/Slim/Handlers/Error.php b/Slim/Handlers/Error.php new file mode 100644 index 0000000..f525391 --- /dev/null +++ b/Slim/Handlers/Error.php @@ -0,0 +1,239 @@ +displayErrorDetails = (bool)$displayErrorDetails; + } + + /** + * Invoke error handler + * + * @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 + */ + 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; + } + + $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 = '

The application could not run because of the following error:

'; + $html .= '

Details

'; + $html .= $this->renderHtmlException($exception); + + while ($exception = $exception->getPrevious()) { + $html .= '

Previous exception

'; + $html .= $this->renderHtmlException($exception); + } + } else { + $html = '

A website error has occurred. Sorry for the temporary inconvenience.

'; + } + + $output = sprintf( + "" . + "%s

%s

%s", + $title, + $title, + $html + ); + + return $output; + } + + /** + * Render exception as HTML. + * + * @param Exception $exception + * + * @return string + */ + protected function renderHtmlException(Exception $exception) + { + $html = sprintf('
Type: %s
', get_class($exception)); + + if (($code = $exception->getCode())) { + $html .= sprintf('
Code: %s
', $code); + } + + if (($message = $exception->getMessage())) { + $html .= sprintf('
Message: %s
', htmlentities($message)); + } + + if (($file = $exception->getFile())) { + $html .= sprintf('
File: %s
', $file); + } + + if (($line = $exception->getLine())) { + $html .= sprintf('
Line: %s
', $line); + } + + if (($trace = $exception->getTraceAsString())) { + $html .= '

Trace

'; + $html .= sprintf('
%s
', 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 = "\n Slim Application Error\n"; + if ($this->displayErrorDetails) { + do { + $xml .= " \n"; + $xml .= " " . get_class($exception) . "\n"; + $xml .= " " . $exception->getCode() . "\n"; + $xml .= " " . $this->createCdataSection($exception->getMessage()) . "\n"; + $xml .= " " . $exception->getFile() . "\n"; + $xml .= " " . $exception->getLine() . "\n"; + $xml .= " " . $this->createCdataSection($exception->getTraceAsString()) . "\n"; + $xml .= " \n"; + } while ($exception = $exception->getPrevious()); + } + $xml .= ""; + + return $xml; + } + + /** + * Returns a CDATA section with the given content. + * + * @param string $content + * @return string + */ + private function createCdataSection($content) + { + return sprintf('', str_replace(']]>', ']]]]>', $content)); + } + + /** + * Determine which content type we know about is wanted using Accept header + * + * @param ServerRequestInterface $request + * @return string + */ + private function determineContentType(ServerRequestInterface $request) + { + $acceptHeader = $request->getHeaderLine('Accept'); + $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes); + + if (count($selectedContentTypes)) { + return $selectedContentTypes[0]; + } + + return 'text/html'; + } +} diff --git a/Slim/Handlers/NotAllowed.php b/Slim/Handlers/NotAllowed.php new file mode 100644 index 0000000..5fa9ab3 --- /dev/null +++ b/Slim/Handlers/NotAllowed.php @@ -0,0 +1,173 @@ +getMethod() === 'OPTIONS') { + $status = 200; + $contentType = 'text/plain'; + $output = $this->renderPlainNotAllowedMessage($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; + } + } + + $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); + } + + /** + * Determine which content type we know about is wanted using Accept header + * + * @param ServerRequestInterface $request + * @return string + */ + private function determineContentType(ServerRequestInterface $request) + { + $acceptHeader = $request->getHeaderLine('Accept'); + $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes); + + if (count($selectedContentTypes)) { + return $selectedContentTypes[0]; + } + + return 'text/html'; + } + + /** + * Render PLAIN not allowed message + * + * @param array $methods + * @return string + */ + protected function renderPlainNotAllowedMessage($methods) + { + $allow = implode(', ', $methods); + + return 'Allowed methods: ' . $allow; + } + + /** + * Render JSON not allowed message + * + * @param array $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 array $methods + * @return string + */ + protected function renderXmlNotAllowedMessage($methods) + { + $allow = implode(', ', $methods); + + return "Method not allowed. Must be one of: $allow"; + } + + /** + * Render HTML not allowed message + * + * @param array $methods + * @return string + */ + protected function renderHtmlNotAllowedMessage($methods) + { + $allow = implode(', ', $methods); + $output = << + + Method not allowed + + + +

Method not allowed

+

Method not allowed. Must be one of: $allow

+ + +END; + + return $output; + } +} diff --git a/Slim/Handlers/NotFound.php b/Slim/Handlers/NotFound.php new file mode 100644 index 0000000..a66e1d5 --- /dev/null +++ b/Slim/Handlers/NotFound.php @@ -0,0 +1,157 @@ +determineContentType($request); + switch ($contentType) { + case 'application/json': + $output = $this->renderJsonNotFoundOutput($request, $response); + break; + + case 'text/xml': + case 'application/xml': + $output = $this->renderXmlNotFoundOutput($request, $response); + break; + + case 'text/html': + $output = $this->renderHtmlNotFoundOutput($request, $response); + } + + $body = new Body(fopen('php://temp', 'r+')); + $body->write($output); + + return $response->withStatus(404) + ->withHeader('Content-Type', $contentType) + ->withBody($body); + } + + /** + * Determine which content type we know about is wanted using Accept header + * + * @param ServerRequestInterface $request + * @return string + */ + private function determineContentType(ServerRequestInterface $request) + { + $acceptHeader = $request->getHeaderLine('Accept'); + $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes); + + if (count($selectedContentTypes)) { + return $selectedContentTypes[0]; + } + + return 'text/html'; + } + + /** + * Return a response for application/json content not found + * + * @param ServerRequestInterface $request The most recent Request object + * @param ResponseInterface $response The most recent Response object + * + * @return ResponseInterface + */ + protected function renderJsonNotFoundOutput(ServerRequestInterface $request, ResponseInterface $response) + { + return '{"message":"Not found"}'; + } + + /** + * Return a response for xml content not found + * + * @param ServerRequestInterface $request The most recent Request object + * @param ResponseInterface $response The most recent Response object + * + * @return ResponseInterface + */ + protected function renderXmlNotFoundOutput(ServerRequestInterface $request, ResponseInterface $response) + { + return 'Not found'; + } + + /** + * Return a response for text/html content not found + * + * @param ServerRequestInterface $request The most recent Request object + * @param ResponseInterface $response The most recent Response object + * + * @return ResponseInterface + */ + protected function renderHtmlNotFoundOutput(ServerRequestInterface $request, ResponseInterface $response) + { + $homeUrl = (string)($request->getUri()->withPath('')->withQuery('')->withFragment('')); + return << + + Page Not Found + + + +

Page Not Found

+

+ 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. +

+ Visit the Home Page + + +END; + } +} diff --git a/Slim/Handlers/Strategies/RequestResponse.php b/Slim/Handlers/Strategies/RequestResponse.php new file mode 100644 index 0000000..da6cf7f --- /dev/null +++ b/Slim/Handlers/Strategies/RequestResponse.php @@ -0,0 +1,43 @@ + $v) { + $request = $request->withAttribute($k, $v); + } + + return call_user_func($callable, $request, $response, $routeArguments); + } +} diff --git a/Slim/Handlers/Strategies/RequestResponseArgs.php b/Slim/Handlers/Strategies/RequestResponseArgs.php new file mode 100644 index 0000000..f4c41d4 --- /dev/null +++ b/Slim/Handlers/Strategies/RequestResponseArgs.php @@ -0,0 +1,42 @@ + 'HTTP/1.1', + 'REQUEST_METHOD' => 'GET', + 'SCRIPT_NAME' => '', + 'REQUEST_URI' => '', + 'QUERY_STRING' => '', + 'SERVER_NAME' => 'localhost', + 'SERVER_PORT' => 80, + '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), + ], $userData); + + return new static($data); + } +} diff --git a/Slim/Http/Message.php b/Slim/Http/Message.php new file mode 100644 index 0000000..501a263 --- /dev/null +++ b/Slim/Http/Message.php @@ -0,0 +1,295 @@ +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) + { + static $valid = [ + '1.0' => true, + '1.1' => true, + '2.0' => true, + ]; + if (!isset($valid[$version])) { + throw new InvalidArgumentException('Invalid HTTP version. Must be one of: 1.0, 1.1, 2.0'); + } + $clone = clone $this; + $clone->protocolVersion = $version; + + return $clone; + } + + /******************************************************************************* + * Headers + ******************************************************************************/ + + /** + * Retrieves all message header values. + * + * 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 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. + */ + public function getHeaders() + { + return $this->headers->all(); + } + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool 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. + */ + 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[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader($name) + { + return $this->headers->get($name, []); + } + + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns 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 A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty 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 + * @throws \InvalidArgumentException for invalid header names or values. + */ + 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 + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader($name, $value) + { + $clone = clone $this; + $clone->headers->add($name, $value); + + 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); + + return $clone; + } + + /******************************************************************************* + * Body + ******************************************************************************/ + + /** + * 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 + * @throws \InvalidArgumentException When the body is not valid. + */ + public function withBody(StreamInterface $body) + { + // TODO: Test for invalid body? + $clone = clone $this; + $clone->body = $body; + + return $clone; + } +} diff --git a/Slim/Http/RequestBody.php b/Slim/Http/RequestBody.php new file mode 100644 index 0000000..9b952ac --- /dev/null +++ b/Slim/Http/RequestBody.php @@ -0,0 +1,27 @@ + ['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; + + /** + * Create a new Stream. + * + * @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; + + 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) { + 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']) ? $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) { + 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) { + $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 = $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 on failure. + */ + 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 on failure. + */ + 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 unable to read or an error occurs while + * reading. + */ + public function getContents() + { + if (!$this->isReadable() || ($contents = stream_get_contents($this->stream)) === false) { + throw new RuntimeException('Could not get contents of stream'); + } + + return $contents; + } +} diff --git a/Slim/Http/UploadedFile.php b/Slim/Http/UploadedFile.php new file mode 100644 index 0000000..14dde22 --- /dev/null +++ b/Slim/Http/UploadedFile.php @@ -0,0 +1,317 @@ +has('slim.files')) { + return $env['slim.files']; + } elseif (isset($_FILES)) { + return static::parseUploadedFiles($_FILES); + } + + return []; + } + + /** + * Parse a non-normalized, i.e. $_FILES superglobal, tree of uploaded file data. + * + * @param array $uploadedFiles The non-normalized tree of uploaded file data. + * + * @return array A normalized tree of UploadedFile instances. + */ + 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['tmp_name']) ? $uploadedFile['name'] : null, + isset($uploadedFile['type']) ? $uploadedFile['type'] : null, + isset($uploadedFile['size']) ? $uploadedFile['size'] : null, + $uploadedFile['error'], + true + ); + } else { + foreach ($uploadedFile['error'] as $fileIdx => $error) { + $parsed[$field][] = new static( + $uploadedFile['tmp_name'][$fileIdx], + isset($uploadedFile['tmp_name']) ? $uploadedFile['name'][$fileIdx] : null, + isset($uploadedFile['type']) ? $uploadedFile['type'][$fileIdx] : null, + isset($uploadedFile['size']) ? $uploadedFile['size'][$fileIdx] : null, + $uploadedFile['error'][$fileIdx], + true + ); + } + } + } + + return $parsed; + } + + /** + * Construct a new UploadedFile instance. + * + * @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 Stream representation of the uploaded file. + * @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 %1s 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 or subsequent call to the method. + */ + public function moveTo($targetPath) + { + if ($this->moved) { + throw new RuntimeException('Uploaded file already moved'); + } + + if (!is_writable(dirname($targetPath))) { + throw new InvalidArgumentException('Upload target path is not writable'); + } + + $targetIsStream = strpos($targetPath, '://') > 0; + if ($targetIsStream) { + if (!copy($this->file, $targetPath)) { + throw new RuntimeException(sprintf('Error moving uploaded file %1s to %2s', $this->name, $targetPath)); + } + if (!unlink($this->file)) { + throw new RuntimeException(sprintf('Error removing uploaded file %1s', $this->name)); + } + } elseif ($this->sapi) { + if (!is_uploaded_file($this->file)) { + throw new RuntimeException(sprintf('%1s is not a valid uploaded file', $this->file)); + } + + if (!move_uploaded_file($this->file, $targetPath)) { + throw new RuntimeException(sprintf('Error moving uploaded file %1s to %2s', $this->name, $targetPath)); + } + } else { + if (!rename($this->file, $targetPath)) { + throw new RuntimeException(sprintf('Error moving uploaded file %1s to %2s', $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 One of PHP's UPLOAD_ERR_XXX constants. + */ + 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 The filename sent by the client or null if none + * was provided. + */ + 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 The media type sent by the client or null if none + * was provided. + */ + 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 The file size in bytes or null if unknown. + */ + public function getSize() + { + return $this->size; + } +} diff --git a/Slim/Http/Uri.php b/Slim/Http/Uri.php new file mode 100644 index 0000000..ac65019 --- /dev/null +++ b/Slim/Http/Uri.php @@ -0,0 +1,809 @@ +scheme = $this->filterScheme($scheme); + $this->host = $host; + $this->port = $this->filterPort($port); + $this->path = empty($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 + if ($env->has('HTTP_HOST')) { + $host = $env->get('HTTP_HOST'); + } else { + $host = $env->get('SERVER_NAME'); + } + + // Authority: Port + $port = (int)$env->get('SERVER_PORT', 80); + if (preg_match('/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/', $host, $matches)) { + $host = $matches[1]; + + if ($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 = parse_url($env->get('SCRIPT_NAME'), PHP_URL_PATH); + $requestScriptDir = dirname($requestScriptName); + $requestUri = parse_url($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', ''); + + // Fragment + $fragment = ''; + + // Build Uri + $uri = new static($scheme, $host, $port, $virtualPath, $queryString, $fragment, $username, $password); + if ($basePath) { + $uri = $uri->withBasePath($basePath); + } + + return $uri; + } + + /******************************************************************************** + * Scheme + *******************************************************************************/ + + /** + * 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; + } + + /******************************************************************************** + * Authority + *******************************************************************************/ + + /** + * 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: + * + *
+     * [user-info@]host[:port]
+     * 
+ * + * 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 = $user; + $clone->password = $password ? $password : ''; + + return $clone; + } + + /** + * 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. + * @throws \InvalidArgumentException for invalid hostnames. + */ + 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. + * @throws \InvalidArgumentException for invalid ports. + */ + 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)'); + } + + /******************************************************************************** + * Path + *******************************************************************************/ + + /** + * 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 self 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 self + */ + 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. + * + * 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 The RFC 3986 percent-encoded uri path. + * @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 + ); + } + + /******************************************************************************** + * Query + *******************************************************************************/ + + /** + * 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 The URI query 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 + ); + } + + /******************************************************************************** + * Fragment + *******************************************************************************/ + + /** + * 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 self 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; + } + + /******************************************************************************** + * Helpers + *******************************************************************************/ + + /** + * 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, '/'); + } +} diff --git a/Slim/Interfaces/CallableResolverInterface.php b/Slim/Interfaces/CallableResolverInterface.php new file mode 100644 index 0000000..aa8ee9d --- /dev/null +++ b/Slim/Interfaces/CallableResolverInterface.php @@ -0,0 +1,27 @@ +middlewareLock) { + throw new RuntimeException('Middleware can’t be added once the stack is dequeuing'); + } + + if (is_null($this->stack)) { + $this->seedMiddlewareStack(); + } + $next = $this->stack->top(); + $this->stack[] = function (ServerRequestInterface $req, ResponseInterface $res) use ($callable, $next) { + $result = call_user_func($callable, $req, $res, $next); + if ($result instanceof ResponseInterface === false) { + throw new UnexpectedValueException('Middleware must return instance of \Psr\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->stack)) { + throw new RuntimeException('MiddlewareStack can only be seeded once.'); + } + if ($kernel === null) { + $kernel = $this; + } + $this->stack = new SplStack; + $this->stack->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP); + $this->stack[] = $kernel; + } + + /** + * Call middleware stack + * + * @param ServerRequestInterface $req A request object + * @param ResponseInterface $res A response object + * + * @return ResponseInterface + */ + public function callMiddlewareStack(ServerRequestInterface $req, ResponseInterface $res) + { + if (is_null($this->stack)) { + $this->seedMiddlewareStack(); + } + /** @var callable $start */ + $start = $this->stack->top(); + $this->middlewareLock = true; + $resp = $start($req, $res); + $this->middlewareLock = false; + return $resp; + } +} diff --git a/Slim/Routable.php b/Slim/Routable.php new file mode 100644 index 0000000..7fccd37 --- /dev/null +++ b/Slim/Routable.php @@ -0,0 +1,102 @@ +middleware; + } + + /** + * Get the route pattern + * + * @return string + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Set container for use with resolveCallable + * + * @param ContainerInterface $container + * + * @return self + */ + public function setContainer(ContainerInterface $container) + { + $this->container = $container; + return $this; + } + + /** + * Prepend middleware to the middleware collection + * + * @param mixed $callable The callback routine + * + * @return static + */ + public function add($callable) + { + $callable = $this->resolveCallable($callable); + if ($callable instanceof Closure) { + $callable = $callable->bindTo($this->container); + } + + $this->middleware[] = $callable; + return $this; + } +} diff --git a/Slim/RouteGroup.php b/Slim/RouteGroup.php new file mode 100644 index 0000000..a6d22f4 --- /dev/null +++ b/Slim/RouteGroup.php @@ -0,0 +1,47 @@ +pattern = $pattern; + $this->callable = $callable; + } + + /** + * Invoke the group to register any Routable objects within it. + * + * @param App $app The App to bind the callable to. + */ + public function __invoke(App $app = null) + { + $callable = $this->resolveCallable($this->callable); + if ($callable instanceof Closure && $app !== null) { + $callable = $callable->bindTo($app); + } + + $callable(); + } +} diff --git a/_2.htaccess b/_2.htaccess new file mode 100644 index 0000000..5332f1b --- /dev/null +++ b/_2.htaccess @@ -0,0 +1,12 @@ + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ serviceapp.php [QSA,L] + + + +# Allow from app.gruppolapastamadre.it + + +#Header set Access-Control-Allow-Origin "app.gruppolapastamadre.it" +#Header set Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT" \ No newline at end of file diff --git a/image_backend.php b/image_backend.php new file mode 100644 index 0000000..605fd5f --- /dev/null +++ b/image_backend.php @@ -0,0 +1,82 @@ +put('/photos/publish/:imageID', function ($imageID) use ($app) { + $mysqlconnetion = new MysqlClass; + + $query = "update immagini set published = 1, published_date = NOW() where id = " . $imageID; + + $mysqlconnetion->insertRecord($query); + $mysqlconnetion->disconnetti(); +}); + +$app->post('/photos', function () use ($app) { + $idRicette = $app->request()->post('ricetta_id'); + $profileID = $app->request()->post('keyStore'); + $tmpFileName = $_FILES['image']["tmp_name"]; + $layer = ImageWorkshop::initFromPath($tmpFileName); + // istanza della classe + $mysqlconnetion = new MysqlClass; + //$mysqlconneti on->connetti(); + $query = "insert into immagini(id_ricette, type_format, from_profile_id, uploaded_date) " . + "values(" . $idRicette . ", '" . image_type_to_mime_type(exif_imagetype($tmpFileName)) . + "', '" . $profileID . "', NOW())"; + $newID = $mysqlconnetion->insertRecord($query); + $ext = image_type_to_extension(exif_imagetype($tmpFileName), FALSE); + + $folder = $dirRicetteDropBox . "/" . $idRicette; + $imageFileName = $newID . "_full_ricetta." . $ext; + + $dropBoxObj = new myDropBox(); + + resizeImage($dropBoxObj, $layer, 640, dirname($tmpFileName), $imageFileName, $folder); + + $imageMediumFileName = $newID . "_medium_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 320, dirname($tmpFileName), $imageMediumFileName, $folder); + + $thumbFileName = $newID . "_thumb_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 80, dirname($tmpFileName), $thumbFileName, $folder); + + $mysqlconnetion->disconnetti(); + + echo $newID; +}); + +$app->put('/photos/:imageID', function ($imageID) use ($app) { + $tmpFileName = $_FILES['image']["tmp_name"]; + $layer = ImageWorkshop::initFromPath($tmpFileName); +// istanza della classe + $mysqlconnetion = new MysqlClass; +//$mysqlconneti on->connetti(); + $query = "update immagini set (type_format = '" . image_type_to_mime_type(exif_imagetype($tmpFileName)) . "', " . + " where id=" . $imageID; + + $newID = $mysqlconnetion->insertRecord($query); + + $folder = $dirRicetteDropBox . "/" . $newID; + $imageFileName = $newID . "_full_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 640, dirname($tmpFileName), $imageFileName, $folder); + + $imageMediumFileName = $newID . "_medium_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 320, dirname($tmpFileName), $imageMediumFileName, $folder); + + $thumbFileName = $newID . "_thumb_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 80, dirname($tmpFileName), $thumbFileName, $folder); + + $mysqlconnetion->disconnetti(); + + return $newID; +}); diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties deleted file mode 100644 index e69de29..0000000 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties deleted file mode 100644 index 49ccdf8..0000000 --- a/nbproject/private/private.properties +++ /dev/null @@ -1,9 +0,0 @@ -browser.id=Chrome.INTEGRATED -copy.src.files=false -copy.src.on.open=false -copy.src.target= -hostname=localhost -port=8888 -router=mdbTester.php -run.as=INTERNAL -url=http://localhost:8888/ diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml deleted file mode 100644 index 284eeec..0000000 --- a/nbproject/private/private.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index 6df3cbb..0000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,7 +0,0 @@ -include.path=${php.global.include.path} -php.version=PHP_53 -source.encoding=UTF-8 -src.dir=. -tags.asp=false -tags.short=false -web.root=. diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index aa3be94..0000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - org.netbeans.modules.php.project - - - Service - - - diff --git a/pippo.php b/pippo.php new file mode 100644 index 0000000..266e499 --- /dev/null +++ b/pippo.php @@ -0,0 +1,19 @@ + + + diff --git a/tokens/access.token b/tokens/access.token new file mode 100644 index 0000000..ba753a0 --- /dev/null +++ b/tokens/access.token @@ -0,0 +1 @@ +a:2:{s:1:"t";s:16:"s1ti76srenx4b7aj";s:1:"s";s:15:"qshrdehad4dzz6n";} \ No newline at end of file