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

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