Completed
Push — develop ( f71bd8...4fb2c6 )
by Alejandro
17s queued 14s
created

ShortUrl::matchesCriteria()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 12.1908

Importance

Changes 0
Metric Value
cc 11
eloc 13
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 20
rs 7.3166
ccs 11
cts 14
cp 0.7856
crap 12.1908

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\Entity;
6
7
use Cake\Chronos\Chronos;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\Common\Collections\Collection;
10
use Laminas\Diactoros\Uri;
11
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
12
use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface;
13
use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver;
14
use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException;
15
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
16
17
use function array_reduce;
18
use function count;
19
use function Functional\contains;
20
use function Functional\invoke;
21
use function Shlinkio\Shlink\Core\generateRandomShortCode;
22
23
class ShortUrl extends AbstractEntity
24
{
25
    private string $longUrl;
26
    private string $shortCode;
27
    private Chronos $dateCreated;
28
    /** @var Collection|Visit[] */
29
    private Collection $visits;
30
    /** @var Collection|Tag[] */
31
    private Collection $tags;
32
    private ?Chronos $validSince = null;
33
    private ?Chronos $validUntil = null;
34
    private ?int $maxVisits = null;
35
    private ?Domain $domain;
36
    private bool $customSlugWasProvided;
37
38 56
    public function __construct(
39
        string $longUrl,
40
        ?ShortUrlMeta $meta = null,
41
        ?DomainResolverInterface $domainResolver = null
42
    ) {
43 56
        $meta = $meta ?? ShortUrlMeta::createEmpty();
44
45 56
        $this->longUrl = $longUrl;
46 56
        $this->dateCreated = Chronos::now();
47 56
        $this->visits = new ArrayCollection();
48 56
        $this->tags = new ArrayCollection();
49 56
        $this->validSince = $meta->getValidSince();
50 56
        $this->validUntil = $meta->getValidUntil();
51 56
        $this->maxVisits = $meta->getMaxVisits();
52 56
        $this->customSlugWasProvided = $meta->hasCustomSlug();
53 56
        $this->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode();
54 56
        $this->domain = ($domainResolver ?? new SimpleDomainResolver())->resolveDomain($meta->getDomain());
55
    }
56
57 22
    public function getLongUrl(): string
58
    {
59 22
        return $this->longUrl;
60
    }
61
62 28
    public function getShortCode(): string
63
    {
64 28
        return $this->shortCode;
65
    }
66
67 12
    public function getDateCreated(): Chronos
68
    {
69 12
        return $this->dateCreated;
70
    }
71
72
    /**
73
     * @return Collection|Tag[]
74
     */
75 22
    public function getTags(): Collection
76
    {
77 22
        return $this->tags;
78
    }
79
80
    /**
81
     * @param Collection|Tag[] $tags
82
     */
83 6
    public function setTags(Collection $tags): self
84
    {
85 6
        $this->tags = $tags;
86 6
        return $this;
87
    }
88
89 1
    public function updateMeta(ShortUrlMeta $shortCodeMeta): void
90
    {
91 1
        if ($shortCodeMeta->hasValidSince()) {
92 1
            $this->validSince = $shortCodeMeta->getValidSince();
93
        }
94 1
        if ($shortCodeMeta->hasValidUntil()) {
95 1
            $this->validUntil = $shortCodeMeta->getValidUntil();
96
        }
97 1
        if ($shortCodeMeta->hasMaxVisits()) {
98 1
            $this->maxVisits = $shortCodeMeta->getMaxVisits();
99
        }
100
    }
101
102
    /**
103
     * @throws ShortCodeCannotBeRegeneratedException
104
     */
105 4
    public function regenerateShortCode(): self
106
    {
107
        // In ShortUrls where a custom slug was provided, do nothing
108 4
        if ($this->customSlugWasProvided) {
109 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlWithCustomSlug();
110
        }
111
112
        // The short code can be regenerated only on ShortUrl which have not been persisted yet
113 3
        if ($this->id !== null) {
114 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlAlreadyPersisted();
115
        }
116
117 2
        $this->shortCode = generateRandomShortCode();
118 2
        return $this;
119
    }
120
121 13
    public function getValidSince(): ?Chronos
122
    {
123 13
        return $this->validSince;
124
    }
125
126 13
    public function getValidUntil(): ?Chronos
127
    {
128 13
        return $this->validUntil;
129
    }
130
131 16
    public function getVisitsCount(): int
132
    {
133 16
        return count($this->visits);
134
    }
135
136
    /**
137
     * @param Collection|Visit[] $visits
138
     * @internal
139
     */
140 4
    public function setVisits(Collection $visits): self
141
    {
142 4
        $this->visits = $visits;
143 4
        return $this;
144
    }
145
146 13
    public function getMaxVisits(): ?int
147
    {
148 13
        return $this->maxVisits;
149
    }
150
151 5
    public function isEnabled(): bool
152
    {
153 5
        $maxVisitsReached = $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;
154 5
        if ($maxVisitsReached) {
155 2
            return false;
156
        }
157
158 3
        $now = Chronos::now();
159 3
        $beforeValidSince = $this->validSince !== null && $this->validSince->gt($now);
160 3
        if ($beforeValidSince) {
161 1
            return false;
162
        }
163
164 2
        $afterValidUntil = $this->validUntil !== null && $this->validUntil->lt($now);
165 2
        if ($afterValidUntil) {
166 1
            return false;
167
        }
168
169 1
        return true;
170
    }
171
172 14
    public function toString(array $domainConfig): string
173
    {
174 14
        return (string) (new Uri())->withPath($this->shortCode)
175 14
                                   ->withScheme($domainConfig['schema'] ?? 'http')
176 14
                                   ->withHost($this->resolveDomain($domainConfig['hostname'] ?? ''));
177
    }
178
179 15
    private function resolveDomain(string $fallback = ''): string
180
    {
181 15
        if ($this->domain === null) {
182 14
            return $fallback;
183
        }
184
185 1
        return $this->domain->getAuthority();
186
    }
187
188 9
    public function matchesCriteria(ShortUrlMeta $meta, array $tags): bool
189
    {
190 9
        if ($meta->hasMaxVisits() && $meta->getMaxVisits() !== $this->maxVisits) {
191 1
            return false;
192
        }
193 9
        if ($meta->hasDomain() && $meta->getDomain() !== $this->resolveDomain()) {
194
            return false;
195
        }
196 9
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq($this->validSince)) {
0 ignored issues
show
Bug introduced by
It seems like $this->validSince can also be of type null; however, parameter $dt of Cake\Chronos\Chronos::eq() does only seem to accept Cake\Chronos\ChronosInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

196
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq(/** @scrutinizer ignore-type */ $this->validSince)) {
Loading history...
197
            return false;
198
        }
199 9
        if ($meta->hasValidUntil() && ! $meta->getValidUntil()->eq($this->validUntil)) {
200
            return false;
201
        }
202
203 9
        $shortUrlTags = invoke($this->getTags(), '__toString');
204 9
        return count($shortUrlTags) === count($tags) && array_reduce(
205 9
            $tags,
206 9
            fn (bool $hasAllTags, string $tag) => $hasAllTags && contains($shortUrlTags, $tag),
207 9
            true,
208
        );
209
    }
210
}
211