From 93c2c1298a09f626c5e9849fc8fc21db6e5db4b5 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 17 Apr 2016 11:29:23 +0200 Subject: [PATCH] Implemented short code generation based on a provided long URL --- composer.json | 3 +- src/Entity/ShortUrl.php | 4 +- src/Exception/ExceptionInterface.php | 6 ++ src/Exception/InvalidUrlException.php | 11 +++ src/Exception/RuntimeException.php | 6 ++ src/Service/UrlShortener.php | 131 ++++++++++++++++++++++++++ src/Service/UrlShortenerInterface.php | 23 +++++ 7 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/Exception/ExceptionInterface.php create mode 100644 src/Exception/InvalidUrlException.php create mode 100644 src/Exception/RuntimeException.php create mode 100644 src/Service/UrlShortener.php create mode 100644 src/Service/UrlShortenerInterface.php diff --git a/composer.json b/composer.json index 270da115..96fc1489 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "zendframework/zend-expressive-aurarouter": "^1.0", "zendframework/zend-servicemanager": "^3.0", "zendframework/zend-expressive-twigrenderer": "^1.0", - "doctrine/orm": "^2.5" + "doctrine/orm": "^2.5", + "guzzlehttp/guzzle": "^6.2" }, "require-dev": { "phpunit/phpunit": "^4.8", diff --git a/src/Entity/ShortUrl.php b/src/Entity/ShortUrl.php index f6091b2b..cdf7c983 100644 --- a/src/Entity/ShortUrl.php +++ b/src/Entity/ShortUrl.php @@ -41,7 +41,9 @@ class ShortUrl extends AbstractEntity */ public function __construct() { + $this->dateCreated = new \DateTime(); $this->visits = new ArrayCollection(); + $this->shortCode = ''; } /** @@ -58,7 +60,7 @@ class ShortUrl extends AbstractEntity */ public function setOriginalUrl($originalUrl) { - $this->originalUrl = $originalUrl; + $this->originalUrl = (string) $originalUrl; return $this; } diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php new file mode 100644 index 00000000..d6bdc788 --- /dev/null +++ b/src/Exception/ExceptionInterface.php @@ -0,0 +1,6 @@ +getCode() : -1; + return new static(sprintf('Provided URL "%s" is not an exisitng and valid URL', $url), $code, $previous); + } +} diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php new file mode 100644 index 00000000..32e61356 --- /dev/null +++ b/src/Exception/RuntimeException.php @@ -0,0 +1,6 @@ +httpClient = $httpClient; + $this->em = $em; + $this->chars = $chars; + } + + /** + * @param UriInterface $url + * @return string + * @throws InvalidUrlException + * @throws RuntimeException + */ + public function urlToShortCode(UriInterface $url) + { + // If the url already exists in the database, just return its short code + $shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([ + 'originalUrl' => $url + ]); + if (isset($shortUrl)) { + return $shortUrl->getShortCode(); + } + + // Check that the URL exists + $this->checkUrlExists($url); + + // Transactionally insert the short url, then generate the short code and finally update the short code + try { + $this->em->beginTransaction(); + + // First, create the short URL with an empty short code + $shortUrl = new ShortUrl(); + $shortUrl->setOriginalUrl($url); + $this->em->persist($shortUrl); + $this->em->flush(); + + // Generate the short code and persist it + $shortCode = $this->convertAutoincrementIdToShortCode($shortUrl->getId()); + $shortUrl->setShortCode($shortCode); + $this->em->flush(); + + $this->em->commit(); + return $shortCode; + } catch (ORMException $e) { + if ($this->em->getConnection()->isTransactionActive()) { + $this->em->rollback(); + $this->em->close(); + } + + throw new RuntimeException('An error occured while persisting the short URL', -1, $e); + } + } + + /** + * Tries to perform a GET request to provided url, returning true on success and false on failure + * + * @param UriInterface $url + * @return bool + */ + protected function checkUrlExists(UriInterface $url) + { + try { + $this->httpClient->request('GET', $url); + } catch (GuzzleException $e) { + throw InvalidUrlException::fromUrl($url, $e); + } + } + + /** + * Generates the unique shortcode for an autoincrement ID + * + * @param int $id + * @return string + */ + protected function convertAutoincrementIdToShortCode($id) + { + $id = intval($id); + $length = strlen($this->chars); + $code = ''; + + while ($id > $length - 1) { + // Determine the value of the next higher character in the short code and prepend it + $code = $this->chars[fmod($id, $length)] . $code; + $id = floor($id / $length); + } + + return $this->chars[$id] . $code; + } + + /** + * @param string $shortCode + * @return string + */ + public function shortCodeToUrl($shortCode) + { + // Validate short code format + + } +} diff --git a/src/Service/UrlShortenerInterface.php b/src/Service/UrlShortenerInterface.php new file mode 100644 index 00000000..6c211c10 --- /dev/null +++ b/src/Service/UrlShortenerInterface.php @@ -0,0 +1,23 @@ +