Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Serializer\Normalizer; Chris@0: Chris@0: use Symfony\Component\HttpFoundation\File\File; Chris@0: use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; Chris@0: use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface; Chris@0: use Symfony\Component\Serializer\Exception\InvalidArgumentException; Chris@14: use Symfony\Component\Serializer\Exception\NotNormalizableValueException; Chris@0: Chris@0: /** Chris@0: * Normalizes an {@see \SplFileInfo} object to a data URI. Chris@0: * Denormalizes a data URI to a {@see \SplFileObject} object. Chris@0: * Chris@0: * @author Kévin Dunglas Chris@0: */ Chris@0: class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface Chris@0: { Chris@17: private static $supportedTypes = [ Chris@14: \SplFileInfo::class => true, Chris@14: \SplFileObject::class => true, Chris@14: File::class => true, Chris@17: ]; Chris@14: Chris@0: /** Chris@0: * @var MimeTypeGuesserInterface Chris@0: */ Chris@0: private $mimeTypeGuesser; Chris@0: Chris@0: public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null) Chris@0: { Chris@0: if (null === $mimeTypeGuesser && class_exists('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser')) { Chris@0: $mimeTypeGuesser = MimeTypeGuesser::getInstance(); Chris@0: } Chris@0: Chris@0: $this->mimeTypeGuesser = $mimeTypeGuesser; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@17: public function normalize($object, $format = null, array $context = []) Chris@0: { Chris@0: if (!$object instanceof \SplFileInfo) { Chris@0: throw new InvalidArgumentException('The object must be an instance of "\SplFileInfo".'); Chris@0: } Chris@0: Chris@0: $mimeType = $this->getMimeType($object); Chris@0: $splFileObject = $this->extractSplFileObject($object); Chris@0: Chris@0: $data = ''; Chris@0: Chris@0: $splFileObject->rewind(); Chris@0: while (!$splFileObject->eof()) { Chris@0: $data .= $splFileObject->fgets(); Chris@0: } Chris@0: Chris@0: if ('text' === explode('/', $mimeType, 2)[0]) { Chris@0: return sprintf('data:%s,%s', $mimeType, rawurlencode($data)); Chris@0: } Chris@0: Chris@0: return sprintf('data:%s;base64,%s', $mimeType, base64_encode($data)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function supportsNormalization($data, $format = null) Chris@0: { Chris@0: return $data instanceof \SplFileInfo; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * Regex adapted from Brian Grinstead code. Chris@0: * Chris@0: * @see https://gist.github.com/bgrins/6194623 Chris@14: * Chris@14: * @throws InvalidArgumentException Chris@14: * @throws NotNormalizableValueException Chris@0: */ Chris@17: public function denormalize($data, $class, $format = null, array $context = []) Chris@0: { Chris@0: if (!preg_match('/^data:([a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}\/[a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}(;[a-z0-9\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\\\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i', $data)) { Chris@14: throw new NotNormalizableValueException('The provided "data:" URI is not valid.'); Chris@0: } Chris@0: Chris@0: try { Chris@0: switch ($class) { Chris@0: case 'Symfony\Component\HttpFoundation\File\File': Chris@0: return new File($data, false); Chris@0: Chris@0: case 'SplFileObject': Chris@0: case 'SplFileInfo': Chris@0: return new \SplFileObject($data); Chris@0: } Chris@0: } catch (\RuntimeException $exception) { Chris@14: throw new NotNormalizableValueException($exception->getMessage(), $exception->getCode(), $exception); Chris@0: } Chris@0: Chris@0: throw new InvalidArgumentException(sprintf('The class parameter "%s" is not supported. It must be one of "SplFileInfo", "SplFileObject" or "Symfony\Component\HttpFoundation\File\File".', $class)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function supportsDenormalization($data, $type, $format = null) Chris@0: { Chris@14: return isset(self::$supportedTypes[$type]); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the mime type of the object. Defaults to application/octet-stream. Chris@0: * Chris@0: * @param \SplFileInfo $object Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: private function getMimeType(\SplFileInfo $object) Chris@0: { Chris@0: if ($object instanceof File) { Chris@0: return $object->getMimeType(); Chris@0: } Chris@0: Chris@0: if ($this->mimeTypeGuesser && $mimeType = $this->mimeTypeGuesser->guess($object->getPathname())) { Chris@0: return $mimeType; Chris@0: } Chris@0: Chris@0: return 'application/octet-stream'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the \SplFileObject instance associated with the given \SplFileInfo instance. Chris@0: * Chris@0: * @param \SplFileInfo $object Chris@0: * Chris@0: * @return \SplFileObject Chris@0: */ Chris@0: private function extractSplFileObject(\SplFileInfo $object) Chris@0: { Chris@0: if ($object instanceof \SplFileObject) { Chris@0: return $object; Chris@0: } Chris@0: Chris@0: return $object->openFile(); Chris@0: } Chris@0: }