Passed
Push — master ( f2ac3c...abba28 )
by Caen
03:46 queued 12s
created

FeaturedImage::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
nc 2
nop 8
dl 0
loc 12
rs 10
c 4
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Framework\Features\Blogging\Models;
6
7
use Hyde\Facades\Config;
8
use Hyde\Framework\Exceptions\FileNotFoundException;
9
use Hyde\Hyde;
10
use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\FeaturedImageSchema;
11
use Illuminate\Support\Facades\Http;
12
use Illuminate\Support\Str;
13
use Stringable;
14
use function array_flip;
15
use function array_key_exists;
16
use function file_exists;
17
use function filesize;
18
use function key;
19
use function sprintf;
20
21
/**
22
 * Object representation of a blog post's featured image.
23
 *
24
 * While the object can of course be used for any other page type,
25
 * it is named "FeaturedImage" as it's only usage within Hyde
26
 * is for the featured image of a Markdown blog post.
27
 *
28
 * @see \Hyde\Framework\Factories\FeaturedImageFactory
29
 */
30
class FeaturedImage implements Stringable, FeaturedImageSchema
31
{
32
    /**
33
     * A featured image object, for a file stored locally.
34
     *
35
     * The internal data structure forces the image source to reference a file in the _media directory,
36
     * and thus that is what is required for the input. However, when outputting data, the data will
37
     * be used for the _site/media directory, so it will provide data relative to the site root.
38
     *
39
     * The source information is stored in $this->source, which is a file in the _media directory.
40
     */
41
    protected final const TYPE_LOCAL = 'local';
42
43
    /**
44
     * A featured image object, for a file stored remotely.
45
     */
46
    protected final const TYPE_REMOTE = 'remote';
47
48
    /** @var self::TYPE_* */
49
    protected readonly string $type;
50
51
    protected readonly string $source;
52
53
    public function __construct(
54
        string $source,
55
        protected readonly ?string $altText = null,
56
        protected readonly ?string $titleText = null,
57
        protected readonly ?string $authorName = null,
58
        protected readonly ?string $authorUrl = null,
59
        protected readonly ?string $copyrightText = null,
60
        protected readonly ?string $licenseName = null,
61
        protected readonly ?string $licenseUrl = null
62
    ) {
63
        $this->type = $this->isRemote($source) ? self::TYPE_REMOTE : self::TYPE_LOCAL;
0 ignored issues
show
Bug introduced by
The property type is declared read-only in Hyde\Framework\Features\...ng\Models\FeaturedImage.
Loading history...
Documentation Bug introduced by
It seems like $this->isRemote($source)...MOTE : self::TYPE_LOCAL of type string is incompatible with the declared type Hyde\Framework\Features\...ng\Models\FeaturedImage of property $type.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
64
        $this->source = $this->setSource($source);
0 ignored issues
show
Bug introduced by
The property source is declared read-only in Hyde\Framework\Features\...ng\Models\FeaturedImage.
Loading history...
65
    }
66
67
    public function __toString(): string
68
    {
69
        return $this->getSource();
70
    }
71
72
    /**
73
     * Get the source of the image, must be usable within the src attribute of an image tag,
74
     * and is thus not necessarily the path to the source image on disk.
75
     *
76
     * @return string The image's url or path
77
     */
78
    public function getSource(): string
79
    {
80
        if ($this->type === self::TYPE_LOCAL) {
81
            // Return value is always resolvable from a compiled page in the _site directory.
82
            return Hyde::mediaLink($this->source);
83
        }
84
85
        return $this->source;
86
    }
87
88
    protected function setSource(string $source): string
89
    {
90
        if ($this->type === self::TYPE_LOCAL) {
91
            // Normalize away any leading media path prefixes.
92
            return Str::after($source, Hyde::getMediaDirectory().'/');
93
        }
94
95
        return $source;
96
    }
97
98
    public function getContentLength(): int
99
    {
100
        if ($this->type === self::TYPE_LOCAL) {
101
            return $this->getContentLengthForLocalImage();
102
        }
103
104
        return $this->getContentLengthForRemoteImage();
105
    }
106
107
    /** @return self::TYPE_* */
108
    public function getType(): string
109
    {
110
        return $this->type;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->type returns the type string which is incompatible with the documented return type Hyde\Framework\Features\...ng\Models\FeaturedImage.
Loading history...
111
    }
112
113
    /**
114
     * Used in resources/views/components/post/image.blade.php to add meta tags with itemprop attributes.
115
     *
116
     * @return array{text?: string|null, name?: string|null, url: string, contentUrl: string}
117
     */
118
    public function getMetadataArray(): array
119
    {
120
        $metadata = [];
121
122
        if ($this->hasAltText()) {
123
            $metadata['text'] = $this->getAltText();
124
        }
125
126
        if ($this->hasTitleText()) {
127
            $metadata['name'] = $this->getTitleText();
128
        }
129
130
        $metadata['url'] = $this->getSource();
131
        $metadata['contentUrl'] = $this->getSource();
132
133
        return $metadata;
134
    }
135
136
    public function getAltText(): ?string
137
    {
138
        return $this->altText;
139
    }
140
141
    public function getTitleText(): ?string
142
    {
143
        return $this->titleText;
144
    }
145
146
    public function getAuthorName(): ?string
147
    {
148
        return $this->authorName;
149
    }
150
151
    public function getAuthorUrl(): ?string
152
    {
153
        return $this->authorUrl;
154
    }
155
156
    public function getCopyrightText(): ?string
157
    {
158
        return $this->copyrightText;
159
    }
160
161
    public function getLicenseName(): ?string
162
    {
163
        return $this->licenseName;
164
    }
165
166
    public function getLicenseUrl(): ?string
167
    {
168
        return $this->licenseUrl;
169
    }
170
171
    public function hasAltText(): bool
172
    {
173
        return $this->has('altText');
174
    }
175
176
    public function hasTitleText(): bool
177
    {
178
        return $this->has('titleText');
179
    }
180
181
    public function hasAuthorName(): bool
182
    {
183
        return $this->has('authorName');
184
    }
185
186
    public function hasAuthorUrl(): bool
187
    {
188
        return $this->has('authorUrl');
189
    }
190
191
    public function hasCopyrightText(): bool
192
    {
193
        return $this->has('copyrightText');
194
    }
195
196
    public function hasLicenseName(): bool
197
    {
198
        return $this->has('licenseName');
199
    }
200
201
    public function hasLicenseUrl(): bool
202
    {
203
        return $this->has('licenseUrl');
204
    }
205
206
    protected function has(string $property): bool
207
    {
208
        return $this->$property !== null;
209
    }
210
211
    protected function getContentLengthForLocalImage(): int
212
    {
213
        $storagePath = Hyde::mediaPath($this->source);
214
215
        if (! file_exists($storagePath)) {
216
            throw new FileNotFoundException(sprintf('Image at %s does not exist', Hyde::pathToRelative($storagePath)));
217
        }
218
219
        return filesize($storagePath);
220
    }
221
222
    protected function getContentLengthForRemoteImage(): int
223
    {
224
        $headers = Http::withHeaders([
225
            'User-Agent' => Config::getString('hyde.http_user_agent', 'RSS Request Client'),
226
        ])->head($this->getSource())->headers();
227
228
        if (array_key_exists('Content-Length', $headers)) {
229
            return (int) key(array_flip($headers['Content-Length']));
230
        }
231
232
        // Here we could throw an exception if we want to be strict about this, or add a warning.
233
234
        return 0;
235
    }
236
237
    public static function isRemote(string $source): bool
238
    {
239
        return str_starts_with($source, 'http') || str_starts_with($source, '//');
240
    }
241
}
242