Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 212
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2/**
3 * ServiceWiring.php
4 *
5 * This file returns the array loaded by the CodexServices class
6 * for use through `Wikimedia\CodexServices::getInstance()`. Each service is associated with a key (service name),
7 * and its value is a closure that returns an instance of the service. This setup allows for
8 * dependency injection, ensuring services are instantiated only when needed.
9 *
10 * Reminder:
11 *  - ServiceWiring is NOT a cache for arbitrary singletons.
12 *
13 * Services include various UI component builders, renderers, and utilities such as the
14 * Mustache template engine and Localization for localization. These services are integral to
15 * the Codex design system for generating reusable, standardized components.
16 *
17 * @category Infrastructure
18 * @package  Codex\Infrastructure
19 * @since    0.1.0
20 * @author   Doğu Abaris <abaris@null.net>
21 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
22 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
23 */
24
25use Krinkle\Intuition\Intuition;
26use MediaWiki\Context\RequestContext;
27use Wikimedia\Codex\Adapter\WebRequestAdapter;
28use Wikimedia\Codex\Builder\AccordionBuilder;
29use Wikimedia\Codex\Builder\ButtonBuilder;
30use Wikimedia\Codex\Builder\CardBuilder;
31use Wikimedia\Codex\Builder\CheckboxBuilder;
32use Wikimedia\Codex\Builder\FieldBuilder;
33use Wikimedia\Codex\Builder\HtmlSnippetBuilder;
34use Wikimedia\Codex\Builder\InfoChipBuilder;
35use Wikimedia\Codex\Builder\LabelBuilder;
36use Wikimedia\Codex\Builder\MessageBuilder;
37use Wikimedia\Codex\Builder\OptionBuilder;
38use Wikimedia\Codex\Builder\PagerBuilder;
39use Wikimedia\Codex\Builder\ProgressBarBuilder;
40use Wikimedia\Codex\Builder\RadioBuilder;
41use Wikimedia\Codex\Builder\SelectBuilder;
42use Wikimedia\Codex\Builder\TabBuilder;
43use Wikimedia\Codex\Builder\TableBuilder;
44use Wikimedia\Codex\Builder\TabsBuilder;
45use Wikimedia\Codex\Builder\TextAreaBuilder;
46use Wikimedia\Codex\Builder\TextInputBuilder;
47use Wikimedia\Codex\Builder\ThumbnailBuilder;
48use Wikimedia\Codex\Builder\ToggleSwitchBuilder;
49use Wikimedia\Codex\Contract\ILocalizer;
50use Wikimedia\Codex\Localization\IntuitionLocalization;
51use Wikimedia\Codex\Localization\MediaWikiLocalization;
52use Wikimedia\Codex\Renderer\AccordionRenderer;
53use Wikimedia\Codex\Renderer\ButtonRenderer;
54use Wikimedia\Codex\Renderer\CardRenderer;
55use Wikimedia\Codex\Renderer\CheckboxRenderer;
56use Wikimedia\Codex\Renderer\FieldRenderer;
57use Wikimedia\Codex\Renderer\InfoChipRenderer;
58use Wikimedia\Codex\Renderer\LabelRenderer;
59use Wikimedia\Codex\Renderer\MessageRenderer;
60use Wikimedia\Codex\Renderer\PagerRenderer;
61use Wikimedia\Codex\Renderer\ProgressBarRenderer;
62use Wikimedia\Codex\Renderer\RadioRenderer;
63use Wikimedia\Codex\Renderer\SelectRenderer;
64use Wikimedia\Codex\Renderer\TableRenderer;
65use Wikimedia\Codex\Renderer\TabsRenderer;
66use Wikimedia\Codex\Renderer\TemplateRenderer;
67use Wikimedia\Codex\Renderer\TextAreaRenderer;
68use Wikimedia\Codex\Renderer\TextInputRenderer;
69use Wikimedia\Codex\Renderer\ThumbnailRenderer;
70use Wikimedia\Codex\Renderer\ToggleSwitchRenderer;
71use Wikimedia\Codex\Utility\Sanitizer;
72use Wikimedia\Codex\Utility\SimpleWebRequest;
73use Wikimedia\Codex\Utility\WebRequestCallbacks;
74
75/** @phpcs-require-sorted-array */
76return [
77
78    'AccordionBuilder' => static function ( $container ) {
79        return new AccordionBuilder( $container->getService( 'AccordionRenderer' ) );
80    },
81
82    'AccordionRenderer' => static function ( $container ) {
83        return new AccordionRenderer(
84            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
85        );
86    },
87
88    'ButtonBuilder' => static function ( $container ) {
89        return new ButtonBuilder( $container->getService( 'ButtonRenderer' ) );
90    },
91
92    'ButtonRenderer' => static function ( $container ) {
93        return new ButtonRenderer(
94            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
95        );
96    },
97
98    'CardBuilder' => static function ( $container ) {
99        return new CardBuilder( $container->getService( 'CardRenderer' ) );
100    },
101
102    'CardRenderer' => static function ( $container ) {
103        return new CardRenderer(
104            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
105        );
106    },
107
108    'CheckboxBuilder' => static function ( $container ) {
109        return new CheckboxBuilder( $container->getService( 'CheckboxRenderer' ) );
110    },
111
112    'CheckboxRenderer' => static function ( $container ) {
113        return new CheckboxRenderer(
114            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' )
115        );
116    },
117
118    'FieldBuilder' => static function ( $container ) {
119        return new FieldBuilder( $container->getService( 'FieldRenderer' ) );
120    },
121
122    'FieldRenderer' => static function ( $container ) {
123        return new FieldRenderer(
124            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
125        );
126    },
127
128    'HTMLPurifier' => static function ( $container ) {
129        return new HTMLPurifier( $container->getService( 'HTMLPurifierConfig' ) );
130    },
131
132    'HTMLPurifierConfig' => static function () {
133        return HTMLPurifier_Config::createDefault();
134    },
135
136    'HtmlSnippetBuilder' => static function ( $container ) {
137        return new HtmlSnippetBuilder( $container->getService( 'Sanitizer' ) );
138    },
139
140    'InfoChipBuilder' => static function ( $container ) {
141        return new InfoChipBuilder( $container->getService( 'InfoChipRenderer' ) );
142    },
143
144    'InfoChipRenderer' => static function ( $container ) {
145        return new InfoChipRenderer(
146            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
147        );
148    },
149
150    'LabelBuilder' => static function ( $container ) {
151        return new LabelBuilder( $container->getService( 'LabelRenderer' ) );
152    },
153
154    'LabelRenderer' => static function ( $container ) {
155        return new LabelRenderer(
156            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' )
157        );
158    },
159
160    'Localization' => static function (): ILocalizer {
161        if ( defined( 'MW_INSTALL_PATH' ) ) {
162            $messageLocalizer = RequestContext::getMain();
163            return new MediaWikiLocalization( $messageLocalizer );
164        } else {
165            $intuition = new Intuition( 'codex' );
166            $intuition->registerDomain( 'codex', __DIR__ . '/../../i18n' );
167            return new IntuitionLocalization( $intuition );
168        }
169    },
170
171    'MessageBuilder' => static function ( $container ) {
172        return new MessageBuilder( $container->getService( 'MessageRenderer' ) );
173    },
174
175    'MessageRenderer' => static function ( $container ) {
176        return new MessageRenderer(
177            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
178        );
179    },
180
181    'OptionBuilder' => static function () {
182        return new OptionBuilder();
183    },
184
185    'PagerBuilder' => static function ( $container ) {
186        return new PagerBuilder( $container->getService( 'PagerRenderer' ) );
187    },
188
189    'PagerRenderer' => static function ( $container ) {
190        return new PagerRenderer(
191            $container->getService( 'Sanitizer' ),
192            $container->getService( 'TemplateRenderer' ),
193            $container->getService( 'Localization' )
194        );
195    },
196
197    'ProgressBarBuilder' => static function ( $container ) {
198        return new ProgressBarBuilder( $container->getService( 'ProgressBarRenderer' ) );
199    },
200
201    'ProgressBarRenderer' => static function ( $container ) {
202        return new ProgressBarRenderer(
203            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
204        );
205    },
206
207    'RadioBuilder' => static function ( $container ) {
208        return new RadioBuilder( $container->getService( 'RadioRenderer' ) );
209    },
210
211    'RadioRenderer' => static function ( $container ) {
212        return new RadioRenderer(
213            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' )
214        );
215    },
216
217    'Sanitizer' => static function ( $container ) {
218        return new Sanitizer( $container->getService( 'HTMLPurifier' ) );
219    },
220
221    'SelectBuilder' => static function ( $container ) {
222        return new SelectBuilder( $container->getService( 'SelectRenderer' ) );
223    },
224
225    'SelectRenderer' => static function ( $container ) {
226        return new SelectRenderer(
227            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
228        );
229    },
230
231    'SimpleWebRequest' => static function () {
232        //phpcs:ignore
233        return new SimpleWebRequest( $_GET );
234    },
235
236    'TabBuilder' => static function () {
237        return new TabBuilder();
238    },
239
240    'TableBuilder' => static function ( $container ) {
241        return new TableBuilder( $container->getService( 'TableRenderer' ) );
242    },
243
244    'TableRenderer' => static function ( $container ) {
245        return new TableRenderer( $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ) );
246    },
247
248    'TabsBuilder' => static function ( $container ) {
249        return new TabsBuilder( $container->getService( 'TabsRenderer' ) );
250    },
251
252    'TabsRenderer' => static function ( $container ) {
253        return new TabsRenderer( $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ) );
254    },
255
256    'TemplateRenderer' => static function ( $container ) {
257        $templatePath = __DIR__ . '/../../resources/templates';
258
259        $localization = $container->getService( 'Localization' );
260
261        $mustacheEngine = new Mustache_Engine( [
262            'loader' => new Mustache_Loader_FilesystemLoader( $templatePath ),
263            // Disable escaping in Mustache. We use custom PHP escaping instead.
264            'escape' => static fn ( $x ) => $x,
265            'helpers' => [
266                // TODO: Consider moving the following helper functions to a separate helper class.
267                // i18n helper - for localization
268                'i18n' => static function ( $text, $render ) use ( $localization ) {
269                    $renderedText = trim( $render( $text ) );
270                    // Split by '|' to separate the key and parameters.
271                    // XXX This assumes that the expanded content of parameters does not contain pipes.
272                    $parts = explode( '|', $renderedText );
273                    // The first part is the message key, the rest are parameters
274                    $key = trim( array_shift( $parts ) );
275                    $params = array_map( 'trim', $parts );
276
277                    return htmlspecialchars(
278                        $localization->msg( $key, [ 'variables' => $params ] ),
279                        ENT_QUOTES,
280                        'UTF-8'
281                    );
282                },
283                'renderClasses' => static function ( $attributes, $render ) {
284                    $renderedAttributes = $render( $attributes );
285
286                    // Use a regular expression to match the 'class' attribute and capture its value
287                    if ( preg_match( '/class="([^"]*)"/', $renderedAttributes, $matches ) ) {
288                        // If a 'class' attribute is found, prepend a space and return the class string
289                        return ' ' . $matches[1];
290                    }
291
292                    // If no 'class' attribute is found, return an empty string
293                    return '';
294                },
295                'renderAttributes' => static function ( $attributes, $render ) {
296                    $renderedAttributes = $render( $attributes );
297
298                    // Remove the 'class' attribute from the rendered attributes using a regular expression
299                    // This ensures that only non-class attributes are returned
300                    $attribs = trim( preg_replace( '/\s*class="[^"]*"/', '', $renderedAttributes ) );
301
302                    // If there are remaining attributes after removing 'class', prepend a space and return them
303                    // Otherwise, return an empty string
304                    return $attribs !== '' ? ' ' . $attribs : '';
305                },
306            ],
307        ] );
308
309        return new TemplateRenderer( $mustacheEngine );
310    },
311
312    'TextAreaBuilder' => static function ( $container ) {
313        return new TextAreaBuilder( $container->getService( 'TextAreaRenderer' ) );
314    },
315
316    'TextAreaRenderer' => static function ( $container ) {
317        return new TextAreaRenderer(
318            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' )
319        );
320    },
321
322    'TextInputBuilder' => static function ( $container ) {
323        return new TextInputBuilder( $container->getService( 'TextInputRenderer' ) );
324    },
325
326    'TextInputRenderer' => static function ( $container ) {
327        return new TextInputRenderer(
328            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
329        );
330    },
331
332    'ThumbnailBuilder' => static function ( $container ) {
333        return new ThumbnailBuilder( $container->getService( 'ThumbnailRenderer' ) );
334    },
335
336    'ThumbnailRenderer' => static function ( $container ) {
337        return new ThumbnailRenderer(
338            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
339        );
340    },
341
342    'ToggleSwitchBuilder' => static function ( $container ) {
343        return new ToggleSwitchBuilder( $container->getService( 'ToggleSwitchRenderer' ) );
344    },
345
346    'ToggleSwitchRenderer' => static function ( $container ) {
347        return new ToggleSwitchRenderer(
348            $container->getService( 'Sanitizer' ), $container->getService( 'TemplateRenderer' ),
349        );
350    },
351
352    'WebRequestAdapter' => static function ( $container ) {
353        return new WebRequestAdapter( $container->getService( 'SimpleWebRequest' ) );
354    },
355
356    'WebRequestCallbacks' => static function ( $container ) {
357        return new WebRequestCallbacks( $container->getService( 'WebRequestAdapter' ) );
358    },
359];