Issues (46)

src/HandlerStack.php (2 issues)

Labels
Severity
1
<?php
2
/**
3
 * Copyright (c) 2017–2019 Ryan Parman <http://ryanparman.com>.
4
 * Copyright (c) 2017–2019 Contributors.
5
 *
6
 * http://opensource.org/licenses/Apache2.0
7
 */
8
9
declare(strict_types=1);
10
11
namespace SimplePie;
12
13
use DOMXPath;
14
use SimplePie\Enum\FeedType;
15
use SimplePie\Exception\MiddlewareException;
16
use SimplePie\Middleware\Html\HtmlInterface;
17
use SimplePie\Middleware\Json\JsonInterface;
18
use SimplePie\Middleware\Xml\XmlInterface;
19
use SimplePie\Mixin as Tr;
20
use SimplePie\Util\Ns;
21
use Skyzyx\UtilityPack\Types;
0 ignored issues
show
The type Skyzyx\UtilityPack\Types was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use stdClass;
23
24
/**
25
 * `SimplePie\HandlerStack` is a middleware stack system which is modeled after
26
 * [Guzzle's middleware handler stack system](http://docs.guzzlephp.org/en/latest/handlers-and-middleware.html),
27
 * but is designed specifically for SimplePie's use-cases.
28
 *
29
 * Its primary job is to (a) allow the registration and priority of middleware,
30
 * and (b) provide the interface for SimplePie NG to trigger middleware.
31
 */
32
class HandlerStack implements HandlerStackInterface
33
{
34
    use Tr\LoggerTrait;
0 ignored issues
show
The type SimplePie\Mixin\LoggerTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
35
36
    /**
37
     * The middleware stack, grouped by feed type.
38
     *
39
     * @var array
40
     */
41
    protected $stack;
42
43
    /**
44
     * Constructs a new instance of this class.
45
     */
46 564
    public function __construct()
47
    {
48 564
        $this->stack = [
49
            'html' => [],
50
            'json' => [],
51
            'xml'  => [],
52
        ];
53 564
    }
54
55
    /**
56
     * {@inheritdoc}
57
     *
58
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
59
     */
60 564
    public function append(
61
        callable $middleware,
62
        ?string $name = null,
63
        ?string $overrideType = null
64
    ): HandlerStackInterface {
65
        // @phpcs:enable
66
67 564
        $this->validateMiddleware(
68 564
            $middleware,
69
            $name,
70
            $overrideType,
71
            static function (&$arr) use ($middleware, $name): void {
72 563
                $arr[] = [$middleware, $name];
73 564
            }
74
        );
75
76 563
        return $this;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     *
82
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
83
     */
84 3
    public function appendClosure(
85
        string $overrideType,
86
        callable $middleware,
87
        ?string $name = null
88
    ): HandlerStackInterface {
89
        // @phpcs:enable
90
91 3
        return $this->append($middleware, $name, $overrideType);
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     *
97
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
98
     */
99 1
    public function prepend(
100
        callable $middleware,
101
        ?string $name = null,
102
        ?string $overrideType = null
103
    ): HandlerStackInterface {
104
        // @phpcs:enable
105
106 1
        $this->validateMiddleware(
107 1
            $middleware,
108
            $name,
109
            $overrideType,
110
            static function (&$arr) use ($middleware, $name): void {
111 1
                \array_unshift($arr, [$middleware, $name]);
112 1
            }
113
        );
114
115 1
        return $this;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     *
121
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
122
     */
123 1
    public function prependClosure(
124
        string $overrideType,
125
        callable $middleware,
126
        ?string $name = null
127
    ): HandlerStackInterface {
128
        // @phpcs:enable
129
130 1
        return $this->prepend($middleware, $name, $overrideType);
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 560
    public function invoke(string $feedType, stdClass $feedRoot, ?string $namespaceAlias, DOMXPath $xpath): void
137
    {
138 560
        if (isset($this->stack[$feedType])) {
139 560
            foreach ($this->stack[$feedType] as $tuple) {
140 560
                $middleware = $tuple[0];
141 560
                $middleware->setLogger($this->getLogger());
142 560
                $middleware($feedRoot, $namespaceAlias ?? '', $xpath);
143
            }
144
        } else {
145
            $allowedTypes = FeedType::introspectKeys();
146
            \array_shift($allowedTypes);
147
148
            throw new MiddlewareException(\sprintf(
149
                'Could not determine which handler stack to invoke. Stack `%s` was requested. [Allowed: %s]',
150
                $feedType,
151
                \implode(', ', \array_map(
152
                    static function ($type) {
153
                        return \sprintf('FeedType::%s', $type);
154
                    },
155
                    $allowedTypes
156
                ))
157
            ));
158
        }
159 560
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 560
    public function registerNamespaces(Ns $ns): void
165
    {
166 560
        foreach ($this->stack[FeedType::XML] as $tuple) {
167 560
            $middleware = $tuple[0];
168 560
            $ns->addAliases($middleware->getSupportedNamespaces());
169
        }
170 560
    }
171
172
    /**
173
     * Returns information about the HandlerStack that is useful for debugging.
174
     */
175 2
    public function debugStack(): array
176
    {
177
        $fn = static function ($mw) {
178 2
            return \sprintf(
179 2
                '[<%s: resource %s>, %s]',
180 2
                Types::getClassOrType($mw[0]),
181 2
                \md5(\spl_object_hash($mw[0])),
182 2
                isset($mw[1]) ? \sprintf('"%s"', $mw[1]) : 'null'
183
            );
184 2
        };
185
186
        return [
187 2
            'html' => \array_map($fn, $this->stack['html']),
188 2
            'json' => \array_map($fn, $this->stack['json']),
189 2
            'xml'  => \array_map($fn, $this->stack['xml']),
190
        ];
191
    }
192
193
    /**
194
     * Validates the middleware and applies it to the right stack.
195
     *
196
     * @param callable    $middleware   The middleware to add to the stack.
197
     * @param string|null $name         A name for the middleware. Can be used with `pushBefore()` and `pushAfter()`.
198
     * @param string|null $overrideType Override our best guess for which stack to apply the middleware to. By default
199
     *                                  the appropriate stack will be determined by which
200
     *                                  `SimplePie\Middleware\*\*Interface` the middleware extends from. If the
201
     *                                  middleware is a closure, this parameter is required. If the appropriate stack
202
     *                                  cannot be determined, a `SimplePie\Exception\MiddlewareException` exception
203
     *                                  will be thrown.
204
     * @param callable    $fn           A callable which receives the stack by-reference as a parameter, and chooses
205
     *                                  how to add the middleware to that stack.
206
     *
207
     * @throws MiddlewareException
208
     *
209
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
210
     */
211 564
    protected function validateMiddleware(
212
        callable $middleware,
213
        ?string $name,
214
        ?string $overrideType,
215
        callable $fn
216
    ): void {
217
        // @phpcs:enable
218
219 564
        if (FeedType::ALL === $overrideType) {
220 2
            $fn($this->stack['html']);
221 2
            $fn($this->stack['json']);
222 2
            $fn($this->stack['xml']);
223 564
        } elseif (FeedType::JSON === $overrideType || $middleware instanceof JsonInterface) {
224 2
            $fn($this->stack['json']);
225 564
        } elseif (FeedType::XML === $overrideType || $middleware instanceof XmlInterface) {
226 563
            $fn($this->stack['xml']);
227 3
        } elseif (FeedType::HTML === $overrideType || $middleware instanceof HtmlInterface) {
228 2
            $fn($this->stack['html']);
229
        } else {
230 1
            throw new MiddlewareException($this->exceptionMessage($middleware, $name));
231
        }
232 563
    }
233
234
    /**
235
     * Log that the registration of the middleware occurred.
236
     *
237
     * @param callable    $middleware The middleware to add to the stack.
238
     * @param string|null $name       A name for the middleware. Can be used with `pushBefore()` and `pushAfter()`.
239
     */
240
    protected function logRegistration(callable $middleware, ?string $name = null): void
241
    {
242
        $this->logger->info(\sprintf(
243
            'Registered `%s` as middleware%s.',
244
            Types::getClassOrType($middleware),
245
            (null !== $name ? \sprintf(' with the name `%s`', $name) : '')
246
        ));
247
    }
248
249
    /**
250
     * Generate the most appropriate error message based on the parameters that were passed.
251
     *
252
     * @param callable    $middleware The middleware to add to the stack.
253
     * @param string|null $name       A name for the middleware. Can be used with `pushBefore()` and `pushAfter()`.
254
     */
255 1
    protected function exceptionMessage(callable $middleware, ?string $name = null): string
256
    {
257 1
        return \sprintf(
258 1
            'The middleware `%s`%s could not be assigned to a feed type.',
259 1
            Types::getClassOrType($middleware),
260 1
            (null !== $name ? \sprintf(' with the name `%s`', $name) : '')
261
        );
262
    }
263
}
264