Merge pull request #296 from acelaya/feature/fix-lowercase

Feature/fix lowercase
This commit is contained in:
Alejandro Celaya 2018-12-01 21:47:40 +01:00 committed by GitHub
commit 84f6080a38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 39 deletions

View file

@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
#### Fixed
* [#278](https://github.com/shlinkio/shlink/pull/278) Added missing `X-Api-Key` header to the list of valid cross domain headers.
* [#295](https://github.com/shlinkio/shlink/pull/295) Fixed custom slugs so that they are case sensitive and do not try to lowercase provided values.
## 1.14.1 - 2018-11-17

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use Cocur\Slugify\Slugify;
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
return [
'slugify_options' => [
'lowercase' => false,
],
'dependencies' => [
'factories' => [
Slugify::class => ConfigAbstractFactory::class,
],
],
ConfigAbstractFactory::class => [
Slugify::class => ['config.slugify_options'],
],
];

View file

@ -1,13 +1,12 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core;
use Cocur\Slugify\Slugify;
use Doctrine\Common\Cache\Cache;
use Shlinkio\Shlink\Common\Service\PreviewGenerator;
use Shlinkio\Shlink\Core\Action;
use Shlinkio\Shlink\Core\Middleware;
use Shlinkio\Shlink\Core\Options;
use Shlinkio\Shlink\Core\Response\NotFoundHandler;
use Shlinkio\Shlink\Core\Service;
use Zend\Expressive\Router\RouterInterface;
use Zend\Expressive\Template\TemplateRendererInterface;
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
@ -16,12 +15,13 @@ return [
'dependencies' => [
'factories' => [
NotFoundHandler::class => ConfigAbstractFactory::class,
Options\AppOptions::class => ConfigAbstractFactory::class,
Options\DeleteShortUrlsOptions::class => ConfigAbstractFactory::class,
Options\NotFoundShortUrlOptions::class => ConfigAbstractFactory::class,
NotFoundHandler::class => ConfigAbstractFactory::class,
Options\UrlShortenerOptions::class => ConfigAbstractFactory::class,
// Services
Service\UrlShortener::class => ConfigAbstractFactory::class,
Service\VisitsTracker::class => ConfigAbstractFactory::class,
Service\ShortUrlService::class => ConfigAbstractFactory::class,
@ -29,11 +29,11 @@ return [
Service\Tag\TagService::class => ConfigAbstractFactory::class,
Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
// Middleware
Action\RedirectAction::class => ConfigAbstractFactory::class,
Action\PixelAction::class => ConfigAbstractFactory::class,
Action\QrCodeAction::class => ConfigAbstractFactory::class,
Action\PreviewAction::class => ConfigAbstractFactory::class,
Middleware\QrCodeCacheMiddleware::class => ConfigAbstractFactory::class,
],
],
@ -44,21 +44,15 @@ return [
Options\AppOptions::class => ['config.app_options'],
Options\DeleteShortUrlsOptions::class => ['config.delete_short_urls'],
Options\NotFoundShortUrlOptions::class => ['config.url_shortener.not_found_short_url'],
Options\UrlShortenerOptions::class => ['config.url_shortener'],
// Services
Service\UrlShortener::class => [
'httpClient',
'em',
'config.url_shortener.validate_url',
'config.url_shortener.shortcode_chars',
],
Service\UrlShortener::class => ['httpClient', 'em', Options\UrlShortenerOptions::class, Slugify::class],
Service\VisitsTracker::class => ['em'],
Service\ShortUrlService::class => ['em'],
Service\VisitService::class => ['em'],
Service\Tag\TagService::class => ['em'],
Service\ShortUrl\DeleteShortUrlService::class => ['em', Options\DeleteShortUrlsOptions::class],
// Middleware
Action\RedirectAction::class => [
Service\UrlShortener::class,
Service\VisitsTracker::class,
@ -74,6 +68,7 @@ return [
],
Action\QrCodeAction::class => [RouterInterface::class, Service\UrlShortener::class, 'Logger_Shlink'],
Action\PreviewAction::class => [PreviewGenerator::class, Service\UrlShortener::class, 'Logger_Shlink'],
Middleware\QrCodeCacheMiddleware::class => [Cache::class],
],

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Options;
use Zend\Stdlib\AbstractOptions;
class UrlShortenerOptions extends AbstractOptions
{
public const DEFAULT_CHARS = '123456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';
// phpcs:disable
protected $__strictMode__ = false;
// phpcs:enable
private $shortcodeChars = self::DEFAULT_CHARS;
private $validateUrl = true;
public function getChars(): string
{
return $this->shortcodeChars;
}
protected function setShortcodeChars(string $shortcodeChars): self
{
$this->shortcodeChars = empty($shortcodeChars) ? self::DEFAULT_CHARS : $shortcodeChars;
return $this;
}
public function isUrlValidationEnabled(): bool
{
return $this->validateUrl;
}
protected function setValidateUrl($validateUrl): self
{
$this->validateUrl = (bool) $validateUrl;
return $this;
}
}

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service;
use Cake\Chronos\Chronos;
use Cocur\Slugify\Slugify;
use Cocur\Slugify\SlugifyInterface;
use Doctrine\ORM\EntityManagerInterface;
use GuzzleHttp\ClientInterface;
@ -17,6 +16,7 @@ use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Exception\RuntimeException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Throwable;
@ -29,32 +29,29 @@ class UrlShortener implements UrlShortenerInterface
{
use TagManagerTrait;
public const DEFAULT_CHARS = '123456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';
/** @deprecated */
public const DEFAULT_CHARS = UrlShortenerOptions::DEFAULT_CHARS;
private const ID_INCREMENT = 200000;
/** @var ClientInterface */
private $httpClient;
/** @var EntityManagerInterface */
private $em;
/** @var string */
private $chars;
/** @var SlugifyInterface */
private $slugger;
/** @var bool */
private $urlValidationEnabled;
/** @var UrlShortenerOptions */
private $options;
public function __construct(
ClientInterface $httpClient,
EntityManagerInterface $em,
$urlValidationEnabled,
$chars = self::DEFAULT_CHARS,
SlugifyInterface $slugger = null
UrlShortenerOptions $options,
SlugifyInterface $slugger
) {
$this->httpClient = $httpClient;
$this->em = $em;
$this->urlValidationEnabled = (bool) $urlValidationEnabled;
$this->chars = empty($chars) ? self::DEFAULT_CHARS : $chars;
$this->slugger = $slugger ?: new Slugify();
$this->options = $options;
$this->slugger = $slugger;
}
/**
@ -71,7 +68,7 @@ class UrlShortener implements UrlShortenerInterface
?int $maxVisits = null
): ShortUrl {
// If the URL validation is enabled, check that the URL actually exists
if ($this->urlValidationEnabled) {
if ($this->options->isUrlValidationEnabled()) {
$this->checkUrlExists($url);
}
$customSlug = $this->processCustomSlug($customSlug);
@ -121,16 +118,18 @@ class UrlShortener implements UrlShortenerInterface
private function convertAutoincrementIdToShortCode(float $id): string
{
$id += self::ID_INCREMENT; // Increment the Id so that the generated shortcode is not too short
$length = strlen($this->chars);
$chars = $this->options->getChars();
$length = strlen($chars);
$code = '';
while ($id > 0) {
// Determine the value of the next higher character in the short code and prepend it
$code = $this->chars[(int) fmod($id, $length)] . $code;
$code = $chars[(int) fmod($id, $length)] . $code;
$id = floor($id / $length);
}
return $this->chars[(int) $id] . $code;
return $chars[(int) $id] . $code;
}
private function processCustomSlug(?string $customSlug): ?string
@ -157,9 +156,11 @@ class UrlShortener implements UrlShortenerInterface
*/
public function shortCodeToUrl(string $shortCode): ShortUrl
{
$chars = $this->options->getChars();
// Validate short code format
if (! preg_match('|[' . $this->chars . ']+|', $shortCode)) {
throw InvalidShortCodeException::fromCharset($shortCode, $this->chars);
if (! preg_match('|[' . $chars . ']+|', $shortCode)) {
throw InvalidShortCodeException::fromCharset($shortCode, $chars);
}
/** @var ShortUrlRepository $shortUrlRepo */

View file

@ -17,6 +17,7 @@ use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\UrlShortener;
use Zend\Diactoros\Uri;
@ -57,16 +58,12 @@ class UrlShortenerTest extends TestCase
$this->setUrlShortener(false);
}
/**
* @param bool $urlValidationEnabled
*/
public function setUrlShortener($urlValidationEnabled)
public function setUrlShortener(bool $urlValidationEnabled)
{
$this->urlShortener = new UrlShortener(
$this->httpClient->reveal(),
$this->em->reveal(),
$urlValidationEnabled,
UrlShortener::DEFAULT_CHARS,
new UrlShortenerOptions(['validate_url' => $urlValidationEnabled]),
$this->slugger->reveal()
);
}