Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace Symfony\Component\Serializer\Normalizer; Chris@14: Chris@14: use Symfony\Component\Serializer\Exception\InvalidArgumentException; Chris@14: use Symfony\Component\Serializer\Exception\UnexpectedValueException; Chris@14: Chris@14: /** Chris@14: * Normalizes an instance of {@see \DateInterval} to an interval string. Chris@14: * Denormalizes an interval string to an instance of {@see \DateInterval}. Chris@14: * Chris@14: * @author Jérôme Parmentier Chris@14: */ Chris@14: class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface Chris@14: { Chris@14: const FORMAT_KEY = 'dateinterval_format'; Chris@14: Chris@14: private $format; Chris@14: Chris@14: /** Chris@14: * @param string $format Chris@14: */ Chris@14: public function __construct($format = 'P%yY%mM%dDT%hH%iM%sS') Chris@14: { Chris@14: $this->format = $format; Chris@14: } Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: * Chris@14: * @throws InvalidArgumentException Chris@14: */ Chris@17: public function normalize($object, $format = null, array $context = []) Chris@14: { Chris@14: if (!$object instanceof \DateInterval) { Chris@14: throw new InvalidArgumentException('The object must be an instance of "\DateInterval".'); Chris@14: } Chris@14: Chris@14: $dateIntervalFormat = isset($context[self::FORMAT_KEY]) ? $context[self::FORMAT_KEY] : $this->format; Chris@14: Chris@14: return $object->format($dateIntervalFormat); Chris@14: } Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: */ Chris@14: public function supportsNormalization($data, $format = null) Chris@14: { Chris@14: return $data instanceof \DateInterval; Chris@14: } Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: * Chris@14: * @throws InvalidArgumentException Chris@14: * @throws UnexpectedValueException Chris@14: */ Chris@17: public function denormalize($data, $class, $format = null, array $context = []) Chris@14: { Chris@17: if (!\is_string($data)) { Chris@17: throw new InvalidArgumentException(sprintf('Data expected to be a string, %s given.', \gettype($data))); Chris@14: } Chris@14: Chris@14: if (!$this->isISO8601($data)) { Chris@14: throw new UnexpectedValueException('Expected a valid ISO 8601 interval string.'); Chris@14: } Chris@14: Chris@14: $dateIntervalFormat = isset($context[self::FORMAT_KEY]) ? $context[self::FORMAT_KEY] : $this->format; Chris@14: Chris@14: $valuePattern = '/^'.preg_replace('/%([yYmMdDhHiIsSwW])(\w)/', '(?P<$1>\d+)$2', $dateIntervalFormat).'$/'; Chris@14: if (!preg_match($valuePattern, $data)) { Chris@14: throw new UnexpectedValueException(sprintf('Value "%s" contains intervals not accepted by format "%s".', $data, $dateIntervalFormat)); Chris@14: } Chris@14: Chris@14: try { Chris@14: return new \DateInterval($data); Chris@14: } catch (\Exception $e) { Chris@14: throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e); Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: */ Chris@14: public function supportsDenormalization($data, $type, $format = null) Chris@14: { Chris@14: return \DateInterval::class === $type; Chris@14: } Chris@14: Chris@14: private function isISO8601($string) Chris@14: { Chris@14: return preg_match('/^P(?=\w*(?:\d|%\w))(?:\d+Y|%[yY]Y)?(?:\d+M|%[mM]M)?(?:(?:\d+D|%[dD]D)|(?:\d+W|%[wW]W))?(?:T(?:\d+H|[hH]H)?(?:\d+M|[iI]M)?(?:\d+S|[sS]S)?)?$/', $string); Chris@14: } Chris@14: }