|
1 | 1 | <?php |
2 | 2 |
|
| 3 | +declare(strict_types=1); |
| 4 | + |
3 | 5 | namespace Bnomei; |
4 | 6 |
|
5 | | -class Feed |
| 7 | +final class Feed |
6 | 8 | { |
7 | | - private static $indexname = null; |
8 | | - private static $cache = null; |
9 | | - private static function cache(): \Kirby\Cache\Cache |
| 9 | + /** |
| 10 | + * @var array |
| 11 | + */ |
| 12 | + private $options; |
| 13 | + |
| 14 | + /* |
| 15 | + * @var string |
| 16 | + */ |
| 17 | + private $string; |
| 18 | + |
| 19 | + public function __construct(?\Kirby\Cms\Pages $pages = null, array $options = []) |
10 | 20 | { |
11 | | - if (!static::$cache) { |
12 | | - static::$cache = kirby()->cache('bnomei.feed'); |
13 | | - } |
14 | | - return static::$cache; |
| 21 | + $this->options = $this->optionsFromDefault($pages, $options); |
15 | 22 | } |
16 | 23 |
|
17 | | - public static function flush() |
| 24 | + /** |
| 25 | + * @return array |
| 26 | + */ |
| 27 | + public function getOptions(): array |
18 | 28 | { |
19 | | - return static::cache()->flush(); |
| 29 | + return $this->options; |
20 | 30 | } |
21 | 31 |
|
22 | | - public static function isJson($string) |
| 32 | + /** |
| 33 | + * @return string |
| 34 | + */ |
| 35 | + public function getString(): string |
23 | 36 | { |
24 | | - json_decode($string); |
25 | | - return (json_last_error() == JSON_ERROR_NONE); |
| 37 | + return $this->string; |
26 | 38 | } |
27 | 39 |
|
28 | | - public static function isXml($content) |
| 40 | + /** |
| 41 | + * @param null $force |
| 42 | + * @return Feed |
| 43 | + * @throws \Kirby\Exception\InvalidArgumentException |
| 44 | + */ |
| 45 | + public function stringFromSnippet($force = null): Feed |
29 | 46 | { |
30 | | - $content = trim($content); |
31 | | - if (empty($content)) { |
32 | | - return false; |
| 47 | + $force = $force ? $force : (option('debug') && option('bnomei.feed.debugforce')); |
| 48 | + $key = $this->modifiedHashFromKeys(); |
| 49 | + |
| 50 | + $string = null; |
| 51 | + if (! $force) { |
| 52 | + $string = kirby()->cache('bnomei.feed')->get($key); |
33 | 53 | } |
34 | | - if (stripos($content, '<!DOCTYPE html>') !== false) { |
35 | | - return false; |
| 54 | + if ($string) { |
| 55 | + $this->string = $string; |
| 56 | + return $this; |
36 | 57 | } |
37 | | - libxml_use_internal_errors(true); |
38 | | - simplexml_load_string($content); |
39 | | - $errors = libxml_get_errors(); |
40 | | - libxml_clear_errors(); |
41 | | - return empty($errors); |
| 58 | + |
| 59 | + $string = snippet( |
| 60 | + \Kirby\Toolkit\A::get($this->options, 'snippet'), |
| 61 | + $this->options, |
| 62 | + true |
| 63 | + ); |
| 64 | + |
| 65 | + kirby()->cache('bnomei.feed')->set( |
| 66 | + $key, |
| 67 | + $string, |
| 68 | + intval(option('bnomei.feed.expires')) |
| 69 | + ); |
| 70 | + |
| 71 | + $this->string = $string; |
| 72 | + return $this; |
42 | 73 | } |
43 | 74 |
|
44 | | - public static function feed($pages, $options = [], $force = null) |
| 75 | + /** |
| 76 | + * @return string |
| 77 | + * @throws \Kirby\Exception\DuplicateException |
| 78 | + */ |
| 79 | + private function modifiedHashFromKeys(): string |
45 | 80 | { |
46 | | - if ($force == null && option('debug') && option('bnomei.feed.debugforce')) { |
47 | | - $force = true; |
48 | | - } |
49 | | - $key = []; |
50 | | - foreach ($pages as $p) { |
51 | | - $key[] = $p->modified(); |
| 81 | + $keys = [ |
| 82 | + kirby()->language() ? kirby()->language()->code() : '', |
| 83 | + str_replace('.', '', kirby()->plugin('bnomei/feed')->version()[0]), |
| 84 | + \Kirby\Toolkit\A::get($this->options, 'snippet'), |
| 85 | + ]; |
| 86 | + $pages = \Kirby\Toolkit\A::get($this->options, 'items'); |
| 87 | + foreach ($pages as $page) { |
| 88 | + $keys[] = $page->modified(); |
52 | 89 | } |
53 | | - $l = kirby()->language() ? kirby()->language()->code() : ''; |
54 | | - $key = md5($l . '_' . \implode(',', $key)); |
55 | | - $response = $force ? null : static::cache()->get($key); |
56 | | - if (!$response) { |
57 | | - $snippet = \Kirby\Toolkit\A::get($options, 'snippet', 'feed/rss'); |
58 | | - $response = snippet($snippet, static::data($pages, $options), true); |
59 | | - static::cache()->set( |
60 | | - $key, |
61 | | - $response, |
62 | | - option('bnomei.feed.expires') |
63 | | - ); |
64 | | - } |
65 | | - return $response; |
| 90 | + return sha1(implode(',', $keys)); |
66 | 91 | } |
67 | 92 |
|
68 | | - public static function data($pages, $options = []) |
| 93 | + /** |
| 94 | + * @param \Kirby\Cms\Pages|null $pages |
| 95 | + * @param array $options |
| 96 | + * @return array |
| 97 | + */ |
| 98 | + public function optionsFromDefault(?\Kirby\Cms\Pages $pages = null, $options = []): array |
69 | 99 | { |
70 | | - $defaults = array( |
71 | | - 'url' => site()->url(), |
72 | | - 'feedurl' => site()->url() . '/feed/', |
73 | | - 'title' => 'Feed', |
| 100 | + $defaults = [ |
| 101 | + 'url' => site()->url(), |
| 102 | + 'feedurl' => site()->url() . '/feed/', |
| 103 | + 'title' => 'Feed', |
74 | 104 | 'description' => '', |
75 | | - 'link' => site()->url(), |
76 | | - 'urlfield' => 'url', |
77 | | - 'datefield' => 'date', |
78 | | - 'textfield' => 'text', |
79 | | - 'modified' => time(), |
80 | | - ); |
| 105 | + 'link' => site()->url(), |
| 106 | + 'urlfield' => 'url', |
| 107 | + 'datefield' => 'date', |
| 108 | + 'textfield' => 'text', |
| 109 | + 'modified' => time(), |
| 110 | + 'snippet' => 'feed/rss', |
| 111 | + 'mime' => null, |
| 112 | + 'sort' => true, |
| 113 | + ]; |
81 | 114 | $options = array_merge($defaults, $options); |
82 | 115 |
|
83 | | - $items = $pages->sortBy($options['datefield'], 'desc'); |
| 116 | + $items = $pages ?? null; |
| 117 | + if ($items && $options['sort'] === true) { |
| 118 | + $items = $items->sortBy($options['datefield'], 'desc'); |
| 119 | + } |
84 | 120 | $options['items'] = $items; |
| 121 | + $options['link'] = url($options['link']); |
85 | 122 |
|
86 | | - $options['link'] = url($options['link']); |
87 | | - |
88 | | - if ($options['datefield'] == 'modified') { |
| 123 | + if ($items && $options['datefield'] === 'modified') { |
89 | 124 | $options['modified'] = $items->first()->modified('r', 'date'); |
| 125 | + } elseif ($items) { |
| 126 | + $datefieldName = $options['datefield']; |
| 127 | + $options['modified'] = date('r', $items->first()->{$datefieldName}()->toTimestamp()); |
90 | 128 | } else { |
91 | | - $f = $options['datefield']; |
92 | | - $options['modified'] = date('r', $items->first()->{$f}()->toTimestamp()); |
| 129 | + $options['modified'] = site()->homePage()->modified(); |
93 | 130 | } |
94 | 131 |
|
95 | 132 | return $options; |
96 | 133 | } |
| 134 | + |
| 135 | + /** |
| 136 | + * @return \Kirby\Http\Response |
| 137 | + */ |
| 138 | + public function response(): \Kirby\Http\Response |
| 139 | + { |
| 140 | + $mime = \Kirby\Toolkit\A::get($this->options, 'mime'); |
| 141 | + $snippet = \Kirby\Toolkit\A::get($this->options, 'snippet'); |
| 142 | + |
| 143 | + if ($mime && in_array($mime, array_values(\Kirby\Toolkit\Mime::types()))) { |
| 144 | + return new \Kirby\Http\Response($this->string, $mime); |
| 145 | + } elseif ($snippet === 'feed/json' || \Bnomei\Feed::isJson($this->string)) { |
| 146 | + return new \Kirby\Http\Response($this->string, 'application/json'); |
| 147 | + } elseif ($snippet === 'feed/rss' || \Bnomei\Feed::isXml($this->string)) { |
| 148 | + return new \Kirby\Http\Response($this->string, 'application/rss+xml'); |
| 149 | + } |
| 150 | + return new \Kirby\Http\Response('Error: Feed Response', null, 500); |
| 151 | + } |
| 152 | + |
| 153 | + /** |
| 154 | + * @param \Kirby\Cms\Pages $pages |
| 155 | + * @param array $options |
| 156 | + * @param null $force |
| 157 | + * @return \Kirby\Http\Response |
| 158 | + * @throws \Kirby\Exception\InvalidArgumentException |
| 159 | + */ |
| 160 | + public static function feed(\Kirby\Cms\Pages $pages, array $options = [], $force = null): \Kirby\Http\Response |
| 161 | + { |
| 162 | + $feed = new self($pages, $options); |
| 163 | + return $feed->stringFromSnippet($force)->response(); |
| 164 | + } |
| 165 | + |
| 166 | + /** |
| 167 | + * @param $string |
| 168 | + * @return bool |
| 169 | + */ |
| 170 | + public static function isJson($string): bool |
| 171 | + { |
| 172 | + json_decode($string); |
| 173 | + $lastError = json_last_error(); |
| 174 | + return $lastError === JSON_ERROR_NONE; |
| 175 | + } |
| 176 | + |
| 177 | + /** |
| 178 | + * @param $content |
| 179 | + * @return bool |
| 180 | + */ |
| 181 | + public static function isXml($content): bool |
| 182 | + { |
| 183 | + if (! $content) { |
| 184 | + return false; |
| 185 | + } |
| 186 | + if (is_string($content) && strlen(trim($content)) === 0) { |
| 187 | + return false; |
| 188 | + } |
| 189 | + if (stripos($content, '<!DOCTYPE html>') !== false) { |
| 190 | + return false; |
| 191 | + } |
| 192 | + libxml_use_internal_errors(true); |
| 193 | + simplexml_load_string(trim($content)); |
| 194 | + $errors = libxml_get_errors(); |
| 195 | + libxml_clear_errors(); |
| 196 | + return count($errors) === 0; |
| 197 | + } |
97 | 198 | } |
0 commit comments