Completed
Pull Request — develop (#645)
by Alejandro
05:15
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 12
    public function getDomain(): ?Domain
73
    {
74 12
        return $this->domain;
75
    }
76
77
    /**
78
     * @return Collection|Tag[]
79
     */
80 22
    public function getTags(): Collection
81
    {
82 22
        return $this->tags;
83
    }
84
85
    /**
86
     * @param Collection|Tag[] $tags
87
     */
88 6
    public function setTags(Collection $tags): self
89
    {
90 6
        $this->tags = $tags;
91 6
        return $this;
92
    }
93
94 1
    public function updateMeta(ShortUrlMeta $shortCodeMeta): void
95
    {
96 1
        if ($shortCodeMeta->hasValidSince()) {
97 1
            $this->validSince = $shortCodeMeta->getValidSince();
98
        }
99 1
        if ($shortCodeMeta->hasValidUntil()) {
100 1
            $this->validUntil = $shortCodeMeta->getValidUntil();
101
        }
102 1
        if ($shortCodeMeta->hasMaxVisits()) {
103 1
            $this->maxVisits = $shortCodeMeta->getMaxVisits();
104
        }
105
    }
106
107
    /**
108
     * @throws ShortCodeCannotBeRegeneratedException
109
     */
110 4
    public function regenerateShortCode(): self
111
    {
112
        // In ShortUrls where a custom slug was provided, do nothing
113 4
        if ($this->customSlugWasProvided) {
114 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlWithCustomSlug();
115
        }
116
117
        // The short code can be regenerated only on ShortUrl which have not been persisted yet
118 3
        if ($this->id !== null) {
119 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlAlreadyPersisted();
120
        }
121
122 2
        $this->shortCode = generateRandomShortCode();
123 2
        return $this;
124
    }
125
126 13
    public function getValidSince(): ?Chronos
127
    {
128 13
        return $this->validSince;
129
    }
130
131 13
    public function getValidUntil(): ?Chronos
132
    {
133 13
        return $this->validUntil;
134
    }
135
136 16
    public function getVisitsCount(): int
137
    {
138 16
        return count($this->visits);
139
    }
140
141
    /**
142
     * @param Collection|Visit[] $visits
143
     * @internal
144
     */
145 4
    public function setVisits(Collection $visits): self
146
    {
147 4
        $this->visits = $visits;
148 4
        return $this;
149
    }
150
151 13
    public function getMaxVisits(): ?int
152
    {
153 13
        return $this->maxVisits;
154
    }
155
156 5
    public function isEnabled(): bool
157
    {
158 5
        $maxVisitsReached = $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;
159 5
        if ($maxVisitsReached) {
160 2
            return false;
161
        }
162
163 3
        $now = Chronos::now();
164 3
        $beforeValidSince = $this->validSince !== null && $this->validSince->gt($now);
165 3
        if ($beforeValidSince) {
166 1
            return false;
167
        }
168
169 2
        $afterValidUntil = $this->validUntil !== null && $this->validUntil->lt($now);
170 2
        if ($afterValidUntil) {
171 1
            return false;
172
        }
173
174 1
        return true;
175
    }
176
177 14
    public function toString(array $domainConfig): string
178
    {
179 14
        return (string) (new Uri())->withPath($this->shortCode)
180 14
                                   ->withScheme($domainConfig['schema'] ?? 'http')
181 14
                                   ->withHost($this->resolveDomain($domainConfig['hostname'] ?? ''));
182
    }
183
184 15
    private function resolveDomain(string $fallback = ''): string
185
    {
186 15
        if ($this->domain === null) {
187 14
            return $fallback;
188
        }
189
190 1
        return $this->domain->getAuthority();
191
    }
192
193 9
    public function matchesCriteria(ShortUrlMeta $meta, array $tags): bool
194
    {
195 9
        if ($meta->hasMaxVisits() && $meta->getMaxVisits() !== $this->maxVisits) {
196 1
            return false;
197
        }
198 9
        if ($meta->hasDomain() && $meta->getDomain() !== $this->resolveDomain()) {
199
            return false;
200
        }
201 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

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