xref: /dokuwiki/inc/Feed/FeedParserFile.php (revision 7930587300104cf1bc6f40d322f168238a9fdfdc)
1<?php
2
3namespace dokuwiki\Feed;
4
5use dokuwiki\HTTP\DokuHTTPClient;
6use SimplePie\File;
7
8/**
9 * Fetch an URL using our own HTTPClient
10 *
11 * Replaces SimplePie's own File class.
12 */
13class FeedParserFile extends File
14{
15    /** @var DokuHTTPClient */
16    protected $http;
17
18    /** @var string the requested URL */
19    protected $requestUrl;
20    /** @var int the HTTP status code of the response */
21    protected $responseStatus;
22    /** @var array<string, string[]> response headers in SimplePie's representation */
23    protected $responseHeaders = [];
24    /** @var string the response body */
25    protected $responseBody = '';
26
27    /** @noinspection PhpMissingParentConstructorInspection */
28
29    /**
30     * Fetches the given URL through DokuHTTPClient
31     *
32     * SimplePie creates this object through its registry and reads the response via the
33     * get_*() methods below, so the fetch has to happen here in the constructor.
34     *
35     * @inheritdoc
36     */
37    public function __construct($url)
38    {
39        $this->http = $this->initHTTPClient();
40        $this->success = $this->http->sendRequest($url);
41
42        $this->requestUrl = $url;
43        $this->responseStatus = (int)$this->http->status;
44        $this->responseBody = (string)$this->http->resp_body;
45        $this->responseHeaders = $this->normalizeHeaders($this->http->resp_headers);
46        // DokuHTTPClient uses an empty string for "no error", but SimplePie's FileClient
47        // treats any non-null error combined with a zero status code as a failed request
48        $this->error = $this->http->error ?: null;
49    }
50
51    /**
52     * Creates the HTTP client used to fetch the feed
53     *
54     * Separated out so tests can inject a client with a canned response
55     *
56     * @return DokuHTTPClient
57     */
58    protected function initHTTPClient()
59    {
60        return new DokuHTTPClient();
61    }
62
63    /**
64     * Converts DokuHTTPClient's "name => value" headers into SimplePie's
65     * "name => [values]" representation
66     *
67     * @param array<string, string> $headers
68     * @return array<string, string[]>
69     */
70    protected function normalizeHeaders($headers)
71    {
72        return array_map(static function ($value) {
73            return array_map('trim', explode(',', (string)$value));
74        }, $headers);
75    }
76
77    // the following methods implement SimplePie's Response interface and have to keep its naming
78    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
79
80    /** @inheritdoc */
81    public function get_final_requested_uri(): string
82    {
83        return (string)$this->requestUrl;
84    }
85
86    /** @inheritdoc */
87    public function get_permanent_uri(): string
88    {
89        return (string)$this->requestUrl;
90    }
91
92    /** @inheritdoc */
93    public function get_status_code(): int
94    {
95        return $this->responseStatus;
96    }
97
98    /** @inheritdoc */
99    public function get_headers(): array
100    {
101        return $this->responseHeaders;
102    }
103
104    /** @inheritdoc */
105    public function has_header(string $name): bool
106    {
107        return isset($this->responseHeaders[strtolower($name)]);
108    }
109
110    /** @inheritdoc */
111    public function get_header(string $name): array
112    {
113        return $this->responseHeaders[strtolower($name)] ?? [];
114    }
115
116    /** @inheritdoc */
117    public function get_header_line(string $name): string
118    {
119        return implode(', ', $this->get_header($name));
120    }
121
122    /** @inheritdoc */
123    public function get_body_content(): string
124    {
125        return $this->responseBody;
126    }
127
128    // phpcs:enable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
129}
130