xref: /dokuwiki/vendor/simplepie/simplepie/src/Registry.php (revision 8e88a29b81301f78509349ab1152bb09c229123e)
1<?php
2
3// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
4// SPDX-License-Identifier: BSD-3-Clause
5
6declare(strict_types=1);
7
8namespace SimplePie;
9
10use InvalidArgumentException;
11use SimplePie\Content\Type\Sniffer;
12use SimplePie\Parse\Date;
13use SimplePie\XML\Declaration\Parser as DeclarationParser;
14
15/**
16 * Handles creating objects and calling methods
17 *
18 * Access this via {@see \SimplePie\SimplePie::get_registry()}
19 */
20class Registry
21{
22    /**
23     * Default class mapping
24     *
25     * Overriding classes *must* subclass these.
26     *
27     * @var array<class-string, class-string>
28     */
29    protected $default = [
30        Cache::class => Cache::class,
31        Locator::class => Locator::class,
32        Parser::class => Parser::class,
33        File::class => File::class,
34        Sanitize::class => Sanitize::class,
35        Item::class => Item::class,
36        Author::class => Author::class,
37        Category::class => Category::class,
38        Enclosure::class => Enclosure::class,
39        Caption::class => Caption::class,
40        Copyright::class => Copyright::class,
41        Credit::class => Credit::class,
42        Rating::class => Rating::class,
43        Restriction::class => Restriction::class,
44        Sniffer::class => Sniffer::class,
45        Source::class => Source::class,
46        Misc::class => Misc::class,
47        DeclarationParser::class => DeclarationParser::class,
48        Date::class => Date::class,
49    ];
50
51    /**
52     * Class mapping
53     *
54     * @see register()
55     * @var array<string, class-string>
56     */
57    protected $classes = [];
58
59    /**
60     * Legacy classes
61     *
62     * @see register()
63     * @var array<class-string>
64     */
65    protected $legacy = [];
66
67    /**
68     * Legacy types
69     *
70     * @see register()
71     * @var array<string, class-string>
72     */
73    private $legacyTypes = [
74        'Cache' => Cache::class,
75        'Locator' => Locator::class,
76        'Parser' => Parser::class,
77        'File' => File::class,
78        'Sanitize' => Sanitize::class,
79        'Item' => Item::class,
80        'Author' => Author::class,
81        'Category' => Category::class,
82        'Enclosure' => Enclosure::class,
83        'Caption' => Caption::class,
84        'Copyright' => Copyright::class,
85        'Credit' => Credit::class,
86        'Rating' => Rating::class,
87        'Restriction' => Restriction::class,
88        'Content_Type_Sniffer' => Sniffer::class,
89        'Source' => Source::class,
90        'Misc' => Misc::class,
91        'XML_Declaration_Parser' => DeclarationParser::class,
92        'Parse_Date' => Date::class,
93    ];
94
95    /**
96     * Constructor
97     *
98     * No-op
99     */
100    public function __construct()
101    {
102    }
103
104    /**
105     * Register a class
106     *
107     * @param string $type See {@see $default} for names
108     * @param class-string $class Class name, must subclass the corresponding default
109     * @param bool $legacy Whether to enable legacy support for this class
110     * @return bool Successfulness
111     */
112    public function register(string $type, $class, bool $legacy = false)
113    {
114        if (array_key_exists($type, $this->legacyTypes)) {
115            // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
116
117            $type = $this->legacyTypes[$type];
118        }
119
120        if (!array_key_exists($type, $this->default)) {
121            return false;
122        }
123
124        if (!class_exists($class)) {
125            return false;
126        }
127
128        /** @var string */
129        $base_class = $this->default[$type];
130
131        if (!is_subclass_of($class, $base_class)) {
132            return false;
133        }
134
135        $this->classes[$type] = $class;
136
137        if ($legacy) {
138            $this->legacy[] = $class;
139        }
140
141        return true;
142    }
143
144    /**
145     * Get the class registered for a type
146     *
147     * Where possible, use {@see create()} or {@see call()} instead
148     *
149     * @template T
150     * @param class-string<T> $type
151     * @return class-string<T>|null
152     */
153    public function get_class($type)
154    {
155        if (array_key_exists($type, $this->legacyTypes)) {
156            // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
157
158            $type = $this->legacyTypes[$type];
159        }
160
161        if (!array_key_exists($type, $this->default)) {
162            return null;
163        }
164
165        // For PHPStan: values in $default should be subtypes of keys.
166        /** @var class-string<T> */
167        $class = $this->default[$type];
168
169        if (array_key_exists($type, $this->classes)) {
170            // For PHPStan: values in $classes should be subtypes of keys.
171            /** @var class-string<T> */
172            $class = $this->classes[$type];
173        }
174
175        return $class;
176    }
177
178    /**
179     * Create a new instance of a given type
180     *
181     * @template T class-string $type
182     * @param class-string<T> $type
183     * @param array<mixed> $parameters Parameters to pass to the constructor
184     * @return T Instance of class
185     */
186    public function &create($type, array $parameters = [])
187    {
188        $class = $this->get_class($type);
189        if ($class === null) {
190            throw new InvalidArgumentException(sprintf(
191                '%s(): Argument #1 ($type) "%s" not found in class list.',
192                __METHOD__,
193                $type
194            ), 1);
195        }
196
197        if (!method_exists($class, '__construct')) {
198            $instance = new $class();
199        } else {
200            $reflector = new \ReflectionClass($class);
201            // For PHPStan: $class is T.
202            /** @var T */
203            $instance = $reflector->newInstanceArgs($parameters);
204        }
205
206        if ($instance instanceof RegistryAware) {
207            $instance->set_registry($this);
208        } elseif (method_exists($instance, 'set_registry')) {
209            trigger_error(sprintf('Using the method "set_registry()" without implementing "%s" is deprecated since SimplePie 1.8.0, implement "%s" in "%s".', RegistryAware::class, RegistryAware::class, $class), \E_USER_DEPRECATED);
210            $instance->set_registry($this);
211        }
212
213        return $instance;
214    }
215
216    /**
217     * Call a static method for a type
218     *
219     * @param class-string $type
220     * @param string $method
221     * @param array<mixed> $parameters
222     * @return mixed
223     */
224    public function &call($type, string $method, array $parameters = [])
225    {
226        $class = $this->get_class($type);
227        if ($class === null) {
228            throw new InvalidArgumentException(sprintf(
229                '%s(): Argument #1 ($type) "%s" not found in class list.',
230                __METHOD__,
231                $type
232            ), 1);
233        }
234
235        if (in_array($class, $this->legacy)) {
236            switch ($type) {
237                case Cache::class:
238                    // For backwards compatibility with old non-static
239                    // Cache::create() methods in PHP < 8.0.
240                    // No longer supported as of PHP 8.0.
241                    if ($method === 'get_handler') {
242                        // Fixing this PHPStan error breaks CacheTest::testDirectOverrideLegacy()
243                        /** @phpstan-ignore argument.type */
244                        $result = @call_user_func_array([$class, 'create'], $parameters);
245                        return $result;
246                    }
247                    break;
248            }
249        }
250
251        $callable = [$class, $method];
252        assert(is_callable($callable), 'For PHPstan');
253        $result = call_user_func_array($callable, $parameters);
254        return $result;
255    }
256}
257
258class_alias('SimplePie\Registry', 'SimplePie_Registry');
259