Passed
Push — master ( 2fb99f...a26130 )
by Caen
03:18 queued 12s
created

RssFeedService   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 52
c 0
b 0
f 0
dl 0
loc 129
rs 10
wmc 21

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getXML() 0 3 1
A __construct() 0 11 3
A addInitialChannelItems() 0 12 1
A addItem() 0 9 1
A generate() 0 8 2
A generateFeed() 0 3 1
A getDefaultOutputFilename() 0 3 1
A getLink() 0 6 1
A addAdditionalChannelData() 0 5 1
A getDescription() 0 6 1
A getTitle() 0 4 1
A xmlEscape() 0 3 1
A addAdditionalItemData() 0 19 6
1
<?php
2
3
/** @noinspection PhpComposerExtensionStubsInspection */
4
/** @noinspection XmlUnusedNamespaceDeclaration */
5
6
namespace Hyde\Framework\Services;
7
8
use Hyde\Framework\Hyde;
9
use Hyde\Framework\Models\Pages\MarkdownPost;
10
use SimpleXMLElement;
11
12
/**
13
 * @see \Hyde\Framework\Testing\Feature\Services\RssFeedServiceTest
14
 * @see https://validator.w3.org/feed/docs/rss2.html
15
 * @phpstan-consistent-constructor
16
 */
17
class RssFeedService
18
{
19
    public SimpleXMLElement $feed;
20
21
    public function __construct()
22
    {
23
        if (! extension_loaded('simplexml') || config('testing.mock_disabled_extensions', false) === true) {
24
            throw new \Exception('The ext-simplexml extension is not installed, but is required to generate RSS feeds.');
25
        }
26
27
        $this->feed = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>
28
            <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" />');
29
        $this->feed->addChild('channel');
30
31
        $this->addInitialChannelItems();
32
    }
33
34
    /**
35
     * @throws \Exception
36
     */
37
    public function generate(): static
38
    {
39
        /** @var MarkdownPost $post */
40
        foreach (MarkdownPost::getLatestPosts() as $post) {
41
            $this->addItem($post);
42
        }
43
44
        return $this;
45
    }
46
47
    public function getXML(): string|bool
48
    {
49
        return $this->feed->asXML();
50
    }
51
52
    protected function addItem(MarkdownPost $post): void
53
    {
54
        $item = $this->feed->channel->addChild('item');
55
        $item->addChild('title', $post->title);
56
        $item->addChild('link', $post->getCanonicalLink());
0 ignored issues
show
Deprecated Code introduced by
The function Hyde\Framework\Models\Pa...ost::getCanonicalLink() has been deprecated: v0.58.x-beta (may be moved to BlogPostSchema) ( Ignorable by Annotation )

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

56
        $item->addChild('link', /** @scrutinizer ignore-deprecated */ $post->getCanonicalLink());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
57
        $item->addChild('guid', $post->getCanonicalLink());
0 ignored issues
show
Deprecated Code introduced by
The function Hyde\Framework\Models\Pa...ost::getCanonicalLink() has been deprecated: v0.58.x-beta (may be moved to BlogPostSchema) ( Ignorable by Annotation )

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

57
        $item->addChild('guid', /** @scrutinizer ignore-deprecated */ $post->getCanonicalLink());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
58
        $item->addChild('description', $post->getPostDescription());
0 ignored issues
show
Deprecated Code introduced by
The function Hyde\Framework\Models\Pa...t::getPostDescription() has been deprecated: v0.58.x-beta (pull description instead) ( Ignorable by Annotation )

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

58
        $item->addChild('description', /** @scrutinizer ignore-deprecated */ $post->getPostDescription());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
59
60
        $this->addAdditionalItemData($item, $post);
61
    }
62
63
    protected function addAdditionalItemData(SimpleXMLElement $item, MarkdownPost $post): void
64
    {
65
        if (isset($post->date)) {
66
            $item->addChild('pubDate', $post->date->dateTimeObject->format(DATE_RSS));
67
        }
68
69
        if (isset($post->author)) {
70
            $item->addChild('dc:creator', $post->author->getName(), 'http://purl.org/dc/elements/1.1/');
0 ignored issues
show
Bug introduced by
The method getName() does not exist on null. ( Ignorable by Annotation )

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

70
            $item->addChild('dc:creator', $post->author->/** @scrutinizer ignore-call */ getName(), 'http://purl.org/dc/elements/1.1/');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
71
        }
72
73
        if (isset($post->category)) {
74
            $item->addChild('category', $post->category);
75
        }
76
77
        if (isset($post->image)) {
78
            $image = $item->addChild('enclosure');
79
            $image->addAttribute('url', Hyde::image($post->image, true));
80
            $image->addAttribute('type', str_ends_with($post->image->getSource(), '.png') ? 'image/png' : 'image/jpeg');
0 ignored issues
show
Bug introduced by
The method getSource() does not exist on null. ( Ignorable by Annotation )

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

80
            $image->addAttribute('type', str_ends_with($post->image->/** @scrutinizer ignore-call */ getSource(), '.png') ? 'image/png' : 'image/jpeg');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
81
            $image->addAttribute('length', $post->image->getContentLength());
82
        }
83
    }
84
85
    protected function addInitialChannelItems(): void
86
    {
87
        $this->feed->channel->addChild('title', static::getTitle());
88
        $this->feed->channel->addChild('link', static::getLink());
89
        $this->feed->channel->addChild('description', static::getDescription());
90
91
        $atomLink = $this->feed->channel->addChild('atom:link', namespace: 'http://www.w3.org/2005/Atom');
92
        $atomLink->addAttribute('href', static::getLink().'/'.static::getDefaultOutputFilename());
93
        $atomLink->addAttribute('rel', 'self');
94
        $atomLink->addAttribute('type', 'application/rss+xml');
95
96
        $this->addAdditionalChannelData();
97
    }
98
99
    protected function addAdditionalChannelData(): void
100
    {
101
        $this->feed->channel->addChild('language', config('site.language', 'en'));
102
        $this->feed->channel->addChild('generator', 'HydePHP '.Hyde::version());
103
        $this->feed->channel->addChild('lastBuildDate', date(DATE_RSS));
104
    }
105
106
    protected static function xmlEscape(string $string): string
107
    {
108
        return htmlspecialchars($string, ENT_XML1 | ENT_COMPAT, 'UTF-8');
109
    }
110
111
    public static function getDescription(): string
112
    {
113
        return static::xmlEscape(
114
            config(
115
                'hyde.rss_description',
116
                static::getTitle().' RSS Feed'
117
            )
118
        );
119
    }
120
121
    public static function getTitle(): string
122
    {
123
        return static::xmlEscape(
124
            config('site.name', 'HydePHP')
125
        );
126
    }
127
128
    public static function getLink(): string
129
    {
130
        return static::xmlEscape(
131
            rtrim(
132
                config('site.url') ?? 'http://localhost',
133
                '/'
134
            )
135
        );
136
    }
137
138
    public static function getDefaultOutputFilename(): string
139
    {
140
        return config('hyde.rss_filename', 'feed.xml');
141
    }
142
143
    public static function generateFeed(): string
144
    {
145
        return (new static)->generate()->getXML();
0 ignored issues
show
Bug Best Practice introduced by
The expression return new static()->generate()->getXML() could return the type true which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
146
    }
147
}
148