/** * Load transformed images from the cache * * @param EventInterface $event The current event */ public function loadFromCache(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); // Generate the full file path to the response $path = $this->getCacheFilePath($request); if (is_file($path)) { $data = @unserialize(file_get_contents($path)); // Make sure the data from the cache is valid if (is_array($data) && isset($data['image']) && isset($data['headers']) && $data['image'] instanceof Image && $data['headers'] instanceof ResponseHeaderBag) { // Mark as cache hit $data['headers']->set('X-Imbo-TransformationCache', 'Hit'); $data['image']->hasBeenTransformed(false); // Replace all headers and set the image model $response->headers = $data['headers']; $response->setModel($data['image']); // Stop other listeners on this event $event->stopPropagation(); return; } else { // Invalid data in the cache, delete the file unlink($path); } } // Mark as cache miss $response->headers->set('X-Imbo-TransformationCache', 'Miss'); }
/** * Get data from the cache * * @param EventInterface $event The event instance */ public function loadFromCache(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $cacheKey = $this->getCacheKey($request->getPublicKey(), $request->getImageIdentifier()); $result = $this->cache->get($cacheKey); if (is_array($result) && isset($result['lastModified']) && $result['lastModified'] instanceof DateTime && isset($result['metadata'])) { $model = new Model\Metadata(); $model->setData($result['metadata']); $response->setModel($model)->setLastModified($result['lastModified']); $response->headers->set('X-Imbo-MetadataCache', 'Hit'); // Stop propagation of listeners for this event $event->stopPropagation(); return; } else { if ($result) { // Invalid result stored in the cache. Delete $this->cache->delete($cacheKey); } } $response->headers->set('X-Imbo-MetadataCache', 'Miss'); }
/** * Choose an image variation based on the transformations and the original size of the image * * @param EventInterface $event The current event */ public function chooseVariation(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $publicKey = $request->getPublicKey(); $imageIdentifier = $request->getImageIdentifier(); // Fetch the original width / height of the image to use for ratio calculations $image = $response->getModel(); $imageWidth = $image->getWidth(); $imageHeight = $image->getHeight(); // Fetch the transformations from the request and find the max width used in the set $transformations = $request->getTransformations(); if (!$transformations) { // No transformations in the request return; } $maxWidth = $this->getMaxWidth($imageWidth, $imageHeight, $transformations); if (!$maxWidth) { // No need to use a variation based on the set of transformations return; } // Fetch the index of the transformation that decided the max width, and the width itself list($transformationIndex, $maxWidth) = each($maxWidth); if ($maxWidth >= $imageWidth) { // The width is the same or above the original, use the original return; } // WE HAVE A WINNER! Find the best variation. The width of the variation is the first // available one above the $maxWidth value $variation = $this->database->getBestMatch($publicKey, $imageIdentifier, $maxWidth); if (!$variation) { // Could not find any :( return; } // Now that we have a variation we can use we need to adjust some of the transformation // parameters. $event->getManager()->trigger('image.transformations.adjust', ['transformationIndex' => $transformationIndex, 'ratio' => $imageWidth / $variation['width']]); // Fetch the image variation blob from the storage adapter $imageBlob = $this->storage->getImageVariation($publicKey, $imageIdentifier, $variation['width']); if (!$imageBlob) { // The image blob does not exist in the storage, which it should. Trigger an error and // return trigger_error('Image variation storage is not in sync with the image variation database', E_USER_WARNING); return; } // Set some data that the storage operations listener usually sets, since that will be // skipped since we have an image variation $lastModified = $event->getStorage()->getLastModified($publicKey, $imageIdentifier); $response->setLastModified($lastModified); // Update the model $model = $response->getModel(); $model->setBlob($imageBlob)->setWidth($variation['width'])->setHeight($variation['height']); // Set a HTTP header that informs the user agent on which image variation that was used in // the transformations $response->headers->set('X-Imbo-ImageVariation', $variation['width'] . 'x' . $variation['height']); // Stop the propagation of this event $event->stopPropagation(); $event->getManager()->trigger('image.loaded'); }
/** * Handle the OPTIONS requests * * @param EventInterface $event The event instance */ public function options(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $origin = $request->headers->get('Origin', '*'); // This is an OPTIONS request, send 204 since no more content will follow $response->setStatusCode(204); // Vary on Origin to prevent caching allowed/disallowed requests $event->getResponse()->setVary('Origin', false); // Fall back if the passed origin is not allowed if (!$this->originIsAllowed($origin)) { return; } $resource = (string) $request->getRoute(); $allowedMethods = array('OPTIONS'); if (isset($this->params['allowedMethods'][$resource])) { $allowedMethods = array_merge($allowedMethods, $this->params['allowedMethods'][$resource]); } $allowedHeaders = array('Content-Type', 'Accept'); $requestHeaders = $request->headers->get('Access-Control-Request-Headers', ''); $requestHeaders = array_map('trim', explode(',', $requestHeaders)); foreach ($requestHeaders as $header) { if (strpos($header, 'x-imbo') === 0) { $allowedHeaders[] = implode('-', array_map('ucfirst', explode('-', $header))); } } $response->headers->add(array('Access-Control-Allow-Origin' => $origin, 'Access-Control-Allow-Methods' => implode(', ', $allowedMethods), 'Access-Control-Allow-Headers' => implode(', ', $allowedHeaders), 'Access-Control-Max-Age' => (int) $this->params['maxAge'])); // Since this is an OPTIONS-request, there is no need for further parsing $event->stopPropagation(); }