FeaturedImage::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 9
dl 0
loc 13
rs 10
c 0
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\Hyde;
8
use Stringable;
9
use Hyde\Facades\Config;
10
use Illuminate\Support\Str;
11
use Hyde\Support\BuildWarnings;
12
use Illuminate\Support\Facades\Http;
13
use Hyde\Foundation\Kernel\Hyperlinks;
14
use Hyde\Support\Filesystem\MediaFile;
15
use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\FeaturedImageSchema;
16
17
use function array_key_exists;
18
use function array_flip;
19
use function key;
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 $licenseName = null,
60
        protected readonly ?string $licenseUrl = null,
61
        protected readonly ?string $copyrightText = null,
62
        protected readonly ?string $caption = null
63
    ) {
64
        $this->type = Hyperlinks::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 Hyde\Foundation\Kernel\H...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...
65
        $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...
66
    }
67
68
    public function __toString(): string
69
    {
70
        return $this->getSource();
71
    }
72
73
    /**
74
     * Get the source of the image, must be usable within the src attribute of an image tag,
75
     * and is thus not necessarily the path to the source image on disk.
76
     *
77
     * @return string The image's url or path
78
     */
79
    public function getSource(): string
80
    {
81
        if ($this->type === self::TYPE_LOCAL) {
82
            // Return value is always resolvable from a compiled page in the _site directory.
83
            return (string) Hyde::asset($this->source);
0 ignored issues
show
Bug introduced by
The method asset() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

83
            return (string) Hyde::/** @scrutinizer ignore-call */ asset($this->source);
Loading history...
84
        }
85
86
        return $this->source;
87
    }
88
89
    protected function setSource(string $source): string
90
    {
91
        if ($this->type === self::TYPE_LOCAL) {
92
            // Normalize away any leading media path prefixes.
93
            return Str::after($source, Hyde::getMediaDirectory().'/');
0 ignored issues
show
Bug introduced by
The method getMediaDirectory() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

93
            return Str::after($source, Hyde::/** @scrutinizer ignore-call */ getMediaDirectory().'/');
Loading history...
94
        }
95
96
        return $source;
97
    }
98
99
    public function getContentLength(): int
100
    {
101
        if ($this->type === self::TYPE_LOCAL) {
102
            return $this->getContentLengthForLocalImage();
103
        }
104
105
        return $this->getContentLengthForRemoteImage();
106
    }
107
108
    /** @return self::TYPE_* */
109
    public function getType(): string
110
    {
111
        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...
112
    }
113
114
    /**
115
     * Used in resources/views/components/post/image.blade.php to add meta tags with itemprop attributes.
116
     *
117
     * @return array{text?: string|null, name?: string|null, url: string, contentUrl: string}
118
     */
119
    public function getMetadataArray(): array
120
    {
121
        $metadata = [];
122
123
        if ($this->hasAltText()) {
124
            $metadata['text'] = $this->getAltText();
125
        }
126
127
        if ($this->hasTitleText()) {
128
            $metadata['name'] = $this->getTitleText();
129
        }
130
131
        if ($this->hasCaption()) {
132
            $metadata['caption'] = $this->getCaption();
133
        }
134
135
        $metadata['url'] = $this->getSource();
136
        $metadata['contentUrl'] = $this->getSource();
137
138
        return $metadata;
139
    }
140
141
    public function getAltText(): ?string
142
    {
143
        return $this->altText ?? $this->caption;
144
    }
145
146
    public function getTitleText(): ?string
147
    {
148
        return $this->titleText;
149
    }
150
151
    public function getAuthorName(): ?string
152
    {
153
        return $this->authorName;
154
    }
155
156
    public function getAuthorUrl(): ?string
157
    {
158
        return $this->authorUrl;
159
    }
160
161
    public function getCopyrightText(): ?string
162
    {
163
        return $this->copyrightText;
164
    }
165
166
    public function getLicenseName(): ?string
167
    {
168
        return $this->licenseName;
169
    }
170
171
    public function getLicenseUrl(): ?string
172
    {
173
        return $this->licenseUrl;
174
    }
175
176
    public function getCaption(): ?string
177
    {
178
        return $this->caption;
179
    }
180
181
    public function hasAltText(): bool
182
    {
183
        return $this->has('altText');
184
    }
185
186
    public function hasTitleText(): bool
187
    {
188
        return $this->has('titleText');
189
    }
190
191
    public function hasAuthorName(): bool
192
    {
193
        return $this->has('authorName');
194
    }
195
196
    public function hasAuthorUrl(): bool
197
    {
198
        return $this->has('authorUrl');
199
    }
200
201
    public function hasCopyrightText(): bool
202
    {
203
        return $this->has('copyrightText');
204
    }
205
206
    public function hasLicenseName(): bool
207
    {
208
        return $this->has('licenseName');
209
    }
210
211
    public function hasLicenseUrl(): bool
212
    {
213
        return $this->has('licenseUrl');
214
    }
215
216
    public function hasCaption(): bool
217
    {
218
        return $this->has('caption');
219
    }
220
221
    protected function has(string $property): bool
222
    {
223
        return $this->$property !== null;
224
    }
225
226
    protected function getContentLengthForLocalImage(): int
227
    {
228
        return MediaFile::get($this->source)->getLength();
229
    }
230
231
    protected function getContentLengthForRemoteImage(): int
232
    {
233
        // API calls can be skipped in the config, or by setting the --no-api flag when running the build command.
234
        if (Config::getBool('hyde.api_calls', true)) {
235
            /** @var string[][] $headers */
236
            $headers = Http::withHeaders([
237
                'User-Agent' => Config::getString('hyde.http_user_agent', 'RSS Request Client'),
238
            ])->head($this->getSource())->headers();
239
240
            if (array_key_exists('Content-Length', $headers)) {
241
                return (int) key(array_flip($headers['Content-Length']));
242
            }
243
244
            BuildWarnings::report('The image "'.$this->getSource().'" has a content length of zero.');
245
        }
246
247
        return 0;
248
    }
249
}
250