• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

timber / timber / 20695674007

04 Jan 2026 04:14PM UTC coverage: 89.681% (+1.5%) from 88.211%
20695674007

push

travis-ci

nlemoine
test: Fix ancestors post tests

4615 of 5146 relevant lines covered (89.68%)

63.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.85
/src/Twig.php
1
<?php
2

3
namespace Timber;
4

5
use DateInterval;
6
use DateTime;
7
use DateTimeInterface;
8
use Exception;
9
use Timber\Factory\PostFactory;
10
use Timber\Factory\TermFactory;
11
use Twig\DeprecatedCallableInfo;
12
use Twig\Environment;
13
use Twig\Extension\CoreExtension;
14
use Twig\Extension\EscaperExtension;
15
use Twig\Runtime\EscaperRuntime;
16
use Twig\TwigFilter;
17
use Twig\TwigFunction;
18

19
/**
20
 * Class Twig
21
 */
22
class Twig
23
{
24
    public static $dir_name;
25

26
    /**
27
     * @codeCoverageIgnore
28
     */
29
    public static function init(): void
30
    {
31
        $self = new self();
32

33
        \add_filter('timber/twig', [$self, 'add_timber_functions']);
34
        \add_filter('timber/twig', [$self, 'add_timber_filters']);
35
        \add_filter('timber/twig', [$self, 'add_timber_escaper_filters']);
36
        \add_filter('timber/twig', [$self, 'add_timber_escapers']);
37
        \add_filter('timber/loader/twig', [$self, 'set_defaults']);
38
    }
39

40
    /**
41
     * Get Timber default functions
42
     *
43
     * @return array Default Timber functions
44
     */
45
    public function get_timber_functions()
413✔
46
    {
47
        $post_factory = new PostFactory();
413✔
48
        $termFactory = new TermFactory();
413✔
49

50
        $functions = [
413✔
51
            'action' => [
413✔
52
                'callable' => function ($action_name, ...$args) {
413✔
53
                    \do_action_ref_array($action_name, $args);
1✔
54
                },
413✔
55
            ],
413✔
56
            'function' => [
413✔
57
                'callable' => [$this, 'exec_function'],
413✔
58
            ],
413✔
59
            'fn' => [
413✔
60
                'callable' => [$this, 'exec_function'],
413✔
61
            ],
413✔
62
            'get_post' => [
413✔
63
                'callable' => [Timber::class, 'get_post'],
413✔
64
            ],
413✔
65
            'get_image' => [
413✔
66
                'callable' => [Timber::class, 'get_image'],
413✔
67
            ],
413✔
68
            'get_external_image' => [
413✔
69
                'callable' => [Timber::class, 'get_external_image'],
413✔
70
            ],
413✔
71
            'get_attachment' => [
413✔
72
                'callable' => [Timber::class, 'get_attachment'],
413✔
73
            ],
413✔
74
            'get_posts' => [
413✔
75
                'callable' => [Timber::class, 'get_posts'],
413✔
76
            ],
413✔
77
            'get_attachment_by' => [
413✔
78
                'callable' => [Timber::class, 'get_attachment_by'],
413✔
79
            ],
413✔
80
            'get_term' => [
413✔
81
                'callable' => [Timber::class, 'get_term'],
413✔
82
            ],
413✔
83
            'get_terms' => [
413✔
84
                'callable' => [Timber::class, 'get_terms'],
413✔
85
            ],
413✔
86
            'get_user' => [
413✔
87
                'callable' => [Timber::class, 'get_user'],
413✔
88
            ],
413✔
89
            'get_users' => [
413✔
90
                'callable' => [Timber::class, 'get_users'],
413✔
91
            ],
413✔
92
            'get_comment' => [
413✔
93
                'callable' => [Timber::class, 'get_comment'],
413✔
94
            ],
413✔
95
            'get_comments' => [
413✔
96
                'callable' => [Timber::class, 'get_comments'],
413✔
97
            ],
413✔
98
            'Post' => [
413✔
99
                'callable' => function ($post_id) use ($post_factory) {
413✔
100
                    Helper::deprecated('{{ Post() }}', '{{ get_post() }} or {{ get_posts() }}', '2.0.0');
2✔
101
                    return $post_factory->from($post_id);
2✔
102
                },
413✔
103
                'options' => [
413✔
104
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
105
                        package: 'timber/timber',
413✔
106
                        version: '2.0.0',
413✔
107
                        altName: '{{ get_post() }} or {{ get_posts() }}',
413✔
108
                        altPackage: null,
413✔
109
                        altVersion: null
413✔
110
                    ),
413✔
111
                ],
413✔
112
            ],
413✔
113
            'TimberPost' => [
413✔
114
                'callable' => function ($post_id) use ($post_factory) {
413✔
115
                    Helper::deprecated('{{ TimberPost() }}', '{{ get_post() }} or {{ get_posts() }}', '2.0.0');
2✔
116
                    return $post_factory->from($post_id);
2✔
117
                },
413✔
118
                'options' => [
413✔
119
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
120
                        package: 'timber/timber',
413✔
121
                        version: '2.0.0',
413✔
122
                        altName: '{{ get_post() }} or {{ get_posts() }}',
413✔
123
                        altPackage: null,
413✔
124
                        altVersion: null
413✔
125
                    ),
413✔
126
                ],
413✔
127
            ],
413✔
128
            'Image' => [
413✔
129
                'callable' => function ($post_id) use ($post_factory) {
413✔
130
                    Helper::deprecated('{{ Image() }}', '{{ get_image() }}, {{ get_post() }}, {{ get_posts() }}, {{ get_attachment() }} or {{ get_attachment_by() }}', '2.0.0');
2✔
131
                    return $post_factory->from($post_id);
2✔
132
                },
413✔
133
                'options' => [
413✔
134
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
135
                        package: 'timber/timber',
413✔
136
                        version: '2.0.0',
413✔
137
                        altName: '{{ get_image() }}',
413✔
138
                        altPackage: null,
413✔
139
                        altVersion: null
413✔
140
                    ),
413✔
141
                ],
413✔
142
            ],
413✔
143
            'TimberImage' => [
413✔
144
                'callable' => function ($post_id) use ($post_factory) {
413✔
145
                    Helper::deprecated('{{ TimberImage() }}', '{{ get_image() }}, {{ get_post() }}, {{ get_posts() }}, {{ get_attachment() }} or {{ get_attachment_by() }}', '2.0.0');
2✔
146
                    return $post_factory->from($post_id);
2✔
147
                },
413✔
148
                'options' => [
413✔
149
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
150
                        package: 'timber/timber',
413✔
151
                        version: '2.0.0',
413✔
152
                        altName: '{{ get_image() }}',
413✔
153
                        altPackage: null,
413✔
154
                        altVersion: null
413✔
155
                    ),
413✔
156
                ],
413✔
157
            ],
413✔
158
            'Term' => [
413✔
159
                'callable' => function ($term_id) use ($termFactory) {
413✔
160
                    Helper::deprecated('{{ Term() }}', '{{ get_term() }} or {{ get_terms() }}', '2.0.0');
2✔
161
                    return $termFactory->from($term_id);
2✔
162
                },
413✔
163
                'options' => [
413✔
164
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
165
                        package: 'timber/timber',
413✔
166
                        version: '2.0.0',
413✔
167
                        altName: '{{ get_term() }}',
413✔
168
                        altPackage: null,
413✔
169
                        altVersion: null
413✔
170
                    ),
413✔
171
                ],
413✔
172
            ],
413✔
173
            'TimberTerm' => [
413✔
174
                'callable' => function ($term_id) use ($termFactory) {
413✔
175
                    Helper::deprecated('{{ TimberTerm() }}', '{{ get_term() }} or {{ get_terms() }}', '2.0.0');
2✔
176
                    return $termFactory->from($term_id);
2✔
177
                },
413✔
178
                'options' => [
413✔
179
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
180
                        package: 'timber/timber',
413✔
181
                        version: '2.0.0',
413✔
182
                        altName: '{{ get_term() }} or {{ get_terms() }}',
413✔
183
                        altPackage: null,
413✔
184
                        altVersion: null
413✔
185
                    ),
413✔
186
                ],
413✔
187
            ],
413✔
188
            'User' => [
413✔
189
                'callable' => function ($user_id) {
413✔
190
                    Helper::deprecated('{{ User() }}', '{{ get_user() }} or {{ get_users() }}', '2.0.0');
2✔
191
                    return Timber::get_user($user_id);
2✔
192
                },
413✔
193
                'options' => [
413✔
194
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
195
                        package: 'timber/timber',
413✔
196
                        version: '2.0.0',
413✔
197
                        altName: '{{ get_user() }} or {{ get_users() }}',
413✔
198
                        altPackage: null,
413✔
199
                        altVersion: null
413✔
200
                    ),
413✔
201
                ],
413✔
202
            ],
413✔
203
            'TimberUser' => [
413✔
204
                'callable' => function ($user_id) {
413✔
205
                    Helper::deprecated('{{ TimberUser() }}', '{{ get_user() }} or {{ get_users() }}', '2.0.0');
2✔
206
                    return Timber::get_user($user_id);
2✔
207
                },
413✔
208
                'options' => [
413✔
209
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
210
                        package: 'timber/timber',
413✔
211
                        version: '2.0.0',
413✔
212
                        altName: '{{ get_user() }} or {{ get_users() }}',
413✔
213
                        altPackage: null,
413✔
214
                        altVersion: null
413✔
215
                    ),
413✔
216
                ],
413✔
217
            ],
413✔
218
            'shortcode' => [
413✔
219
                'callable' => 'do_shortcode',
413✔
220
            ],
413✔
221
            'bloginfo' => [
413✔
222
                'callable' => 'bloginfo',
413✔
223
            ],
413✔
224

225
            // Translation functions.
226
            '__' => [
413✔
227
                'callable' => '__',
413✔
228
            ],
413✔
229
            'translate' => [
413✔
230
                'callable' => 'translate',
413✔
231
            ],
413✔
232
            '_e' => [
413✔
233
                'callable' => '_e',
413✔
234
            ],
413✔
235
            '_n' => [
413✔
236
                'callable' => '_n',
413✔
237
            ],
413✔
238
            '_x' => [
413✔
239
                'callable' => '_x',
413✔
240
            ],
413✔
241
            '_ex' => [
413✔
242
                'callable' => '_ex',
413✔
243
            ],
413✔
244
            '_nx' => [
413✔
245
                'callable' => '_nx',
413✔
246
            ],
413✔
247
            '_n_noop' => [
413✔
248
                'callable' => '_n_noop',
413✔
249
            ],
413✔
250
            '_nx_noop' => [
413✔
251
                'callable' => '_nx_noop',
413✔
252
            ],
413✔
253
            'translate_nooped_plural' => [
413✔
254
                'callable' => 'translate_nooped_plural',
413✔
255
            ],
413✔
256
        ];
413✔
257

258
        /**
259
         * Filters the functions that are added to Twig.
260
         *
261
         * The `$functions` array is an associative array with the filter name as a key and an
262
         * arguments array as the value. In the arguments array, you pass the function to call with
263
         * a `callable` entry.
264
         *
265
         * This is an alternative filter that you can use instead of adding your function in the
266
         * `timber/twig` filter.
267
         *
268
         * @api
269
         * @since 2.0.0
270
         * @example
271
         * ```php
272
         * add_filter( 'timber/twig/functions', function( $functions ) {
273
         *     // Add your own function.
274
         *     $functions['url_to_domain'] = [
275
         *         'callable' => 'url_to_domain',
276
         *     ];
277
         *
278
         *     // Replace a function.
279
         *     $functions['get_image'] = [
280
         *         'callable' => 'custom_image_get',
281
         *     ];
282
         *
283
         *     // Remove a function.
284
         *     unset( $functions['bloginfo'] );
285
         *
286
         *     return $functions;
287
         * } );
288
         * ```
289
         *
290
         * @param array $functions
291
         */
292
        $functions = \apply_filters('timber/twig/functions', $functions);
413✔
293

294
        return $functions;
413✔
295
    }
296

297
    /**
298
     * Adds Timber-specific functions to Twig.
299
     *
300
     * @param Environment $twig The Twig Environment.
301
     *
302
     * @return Environment
303
     */
304
    public function add_timber_functions($twig)
413✔
305
    {
306
        foreach ($this->get_timber_functions() as $name => $function) {
413✔
307
            $twig->addFunction(
413✔
308
                new TwigFunction(
413✔
309
                    $name,
413✔
310
                    $function['callable'],
413✔
311
                    $function['options'] ?? []
413✔
312
                )
413✔
313
            );
413✔
314
        }
315

316
        return $twig;
413✔
317
    }
318

319
    /**
320
     * Get Timber default filters
321
     *
322
     * @return array Default Timber filters
323
     */
324
    public function get_timber_filters()
413✔
325
    {
326
        $filters = [
413✔
327
            /* image filters */
328
            'resize' => [
413✔
329
                'callable' => [ImageHelper::class, 'resize'],
413✔
330
            ],
413✔
331
            'retina' => [
413✔
332
                'callable' => [ImageHelper::class, 'retina_resize'],
413✔
333
            ],
413✔
334
            'letterbox' => [
413✔
335
                'callable' => [ImageHelper::class, 'letterbox'],
413✔
336
            ],
413✔
337
            'tojpg' => [
413✔
338
                'callable' => [ImageHelper::class, 'img_to_jpg'],
413✔
339
            ],
413✔
340
            'towebp' => [
413✔
341
                'callable' => [ImageHelper::class, 'img_to_webp'],
413✔
342
            ],
413✔
343

344
            // Debugging filters.
345
            'get_class' => [
413✔
346
                'callable' => function ($obj) {
413✔
347
                    Helper::deprecated('{{ my_object | get_class }}', "{{ function('get_class', my_object) }}", '2.0.0');
1✔
348
                    return $obj::class;
1✔
349
                },
413✔
350
                'options' => [
413✔
351
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
352
                        package: 'timber/timber',
413✔
353
                        version: '2.0.0',
413✔
354
                        altName: "{{ function('get_class', my_object) }}",
413✔
355
                        altPackage: null,
413✔
356
                        altVersion: null
413✔
357
                    ),
413✔
358
                ],
413✔
359
            ],
413✔
360
            'print_r' => [
413✔
361
                'callable' => function ($arr) {
413✔
362
                    Helper::deprecated('{{ my_object | print_r }}', '{{ dump(my_object) }}', '2.0.0');
×
363
                    return \print_r($arr, true);
×
364
                },
413✔
365
                'options' => [
413✔
366
                    'deprecation_info' => new DeprecatedCallableInfo(
413✔
367
                        package: 'timber/timber',
413✔
368
                        version: '2.0.0',
413✔
369
                        altName: '{{ dump(my_object) }}',
413✔
370
                        altPackage: null,
413✔
371
                        altVersion: null
413✔
372
                    ),
413✔
373
                ],
413✔
374
            ],
413✔
375

376
            // Other filters.
377
            'stripshortcodes' => [
413✔
378
                'callable' => 'strip_shortcodes',
413✔
379
            ],
413✔
380
            'array' => [
413✔
381
                'callable' => [$this, 'to_array'],
413✔
382
            ],
413✔
383
            'excerpt' => [
413✔
384
                'callable' => 'wp_trim_words',
413✔
385
            ],
413✔
386
            'excerpt_chars' => [
413✔
387
                'callable' => [TextHelper::class, 'trim_characters'],
413✔
388
            ],
413✔
389
            'function' => [
413✔
390
                'callable' => [$this, 'exec_function'],
413✔
391
            ],
413✔
392
            'pretags' => [
413✔
393
                'callable' => [$this, 'twig_pretags'],
413✔
394
            ],
413✔
395
            'sanitize' => [
413✔
396
                'callable' => 'sanitize_title',
413✔
397
            ],
413✔
398
            'shortcodes' => [
413✔
399
                'callable' => 'do_shortcode',
413✔
400
            ],
413✔
401
            'wpautop' => [
413✔
402
                'callable' => 'wpautop',
413✔
403
            ],
413✔
404
            'list' => [
413✔
405
                'callable' => [$this, 'add_list_separators'],
413✔
406
            ],
413✔
407
            'pluck' => [
413✔
408
                'callable' => [Helper::class, 'pluck'],
413✔
409
            ],
413✔
410
            'wp_list_filter' => [
413✔
411
                'callable' => [Helper::class, 'wp_list_filter'],
413✔
412
            ],
413✔
413

414
            'relative' => [
413✔
415
                'callable' => fn ($link) => URLHelper::get_rel_url($link, true),
413✔
416
            ],
413✔
417

418
            /**
419
             * Date and Time filters.
420
             */
421
            'date' => [
413✔
422
                'callable' => [$this, 'twig_date_format_filter'],
413✔
423
                'options' => [
413✔
424
                    'needs_environment' => true,
413✔
425
                ],
413✔
426
            ],
413✔
427
            'time_ago' => [
413✔
428
                'callable' => [DateTimeHelper::class, 'time_ago'],
413✔
429
            ],
413✔
430
            'truncate' => [
413✔
431
                'callable' => fn ($text, $len) => TextHelper::trim_words($text, $len),
413✔
432
            ],
413✔
433

434
            // Numbers filters
435
            'size_format' => [
413✔
436
                'callable' => 'size_format',
413✔
437
            ],
413✔
438

439
            // Actions and filters.
440
            'apply_filters' => [
413✔
441
                'callable' => function () {
413✔
442
                    $args = \func_get_args();
1✔
443
                    $tag = \current(\array_splice($args, 1, 1));
1✔
444

445
                    return \apply_filters_ref_array($tag, $args);
1✔
446
                },
413✔
447
            ],
413✔
448
        ];
413✔
449

450
        /**
451
         * Filters the filters that are added to Twig.
452
         *
453
         * The `$filters` array is an associative array with the filter name as a key and an
454
         * arguments array as the value. In the arguments array, you pass the function to call with
455
         * a `callable` entry.
456
         *
457
         * This is an alternative filter that you can use instead of adding your filter in the
458
         * `timber/twig` filter.
459
         *
460
         * @api
461
         * @since 2.0.0
462
         * @example
463
         * ```php
464
         * add_filter( 'timber/twig/default_filters', function( $filters ) {
465
         *     // Add your own filter.
466
         *     $filters['price'] = [
467
         *         'callable' => 'format_price',
468
         *     ];
469
         *
470
         *     // Replace a filter.
471
         *     $filters['list'] = [
472
         *         'callable' => 'custom_list_filter',
473
         *     ];
474
         *
475
         *     // Remove a filter.
476
         *     unset( $filters['list'] );
477
         *
478
         *     return $filters;
479
         * } );
480
         * ```
481
         *
482
         * @param array $filters
483
         */
484
        $filters = \apply_filters('timber/twig/filters', $filters);
413✔
485

486
        return $filters;
413✔
487
    }
488

489
    /**
490
     * Get Timber default filters
491
     *
492
     * @return array Default Timber filters
493
     */
494
    public function get_timber_escaper_filters()
413✔
495
    {
496
        $escaper_filters = [
413✔
497
            'esc_url' => [
413✔
498
                'callable' => 'esc_url',
413✔
499
            ],
413✔
500
            'wp_kses' => [
413✔
501
                'callable' => 'wp_kses',
413✔
502
            ],
413✔
503
            'wp_kses_post' => [
413✔
504
                'callable' => 'wp_kses_post',
413✔
505
            ],
413✔
506
            'esc_attr' => [
413✔
507
                'callable' => 'esc_attr',
413✔
508
            ],
413✔
509
            'esc_html' => [
413✔
510
                'callable' => 'esc_html',
413✔
511
            ],
413✔
512
            'esc_js' => [
413✔
513
                'callable' => 'esc_js',
413✔
514
            ],
413✔
515
        ];
413✔
516

517
        /**
518
         * Filters the escaping filters that are added to Twig.
519
         *
520
         * The `$escaper_filters` array is an associative array with the filter name as a key and an
521
         * arguments array as the value. In the arguments array, you pass the function to call with
522
         * a `callable` entry.
523
         *
524
         *
525
         * @api
526
         * @since 2.1.0
527
         * @example
528
         * ```php
529
         * add_filter( 'timber/twig/escapers', function( $escaper_filters ) {
530
         *     // Add your own filter.
531
         *     $filters['esc_xml'] = [
532
         *         'callable' => 'esc_xml',
533
         *          'options' => [
534
         *             'is_safe' => ['html'],
535
         *          ],
536
         *     ];
537
         *
538
         *     // Remove a filter.
539
         *     unset( $filters['esc_js'] );
540
         *
541
         *     return $filters;
542
         * } );
543
         * ```
544
         *
545
         * @param array $escaper_filters
546
         */
547
        $escaper_filters = \apply_filters('timber/twig/escapers', $escaper_filters);
413✔
548

549
        return $escaper_filters;
413✔
550
    }
551

552
    /**
553
     * Adds filters to Twig.
554
     *
555
     * @param Environment $twig The Twig Environment.
556
     *
557
     * @return Environment
558
     */
559
    public function add_timber_filters($twig)
413✔
560
    {
561
        foreach ($this->get_timber_filters() as $name => $function) {
413✔
562
            $twig->addFilter(
413✔
563
                new TwigFilter(
413✔
564
                    $name,
413✔
565
                    $function['callable'],
413✔
566
                    $function['options'] ?? []
413✔
567
                )
413✔
568
            );
413✔
569
        }
570

571
        return $twig;
413✔
572
    }
573

574
    public function add_timber_escaper_filters($twig)
413✔
575
    {
576
        foreach ($this->get_timber_escaper_filters() as $name => $function) {
413✔
577
            $twig->addFilter(
413✔
578
                new TwigFilter(
413✔
579
                    $name,
413✔
580
                    $function['callable'],
413✔
581
                    $function['options'] ?? [
413✔
582
                        'is_safe' => ['html'],
413✔
583
                    ]
584
                )
413✔
585
            );
413✔
586
        }
587

588
        return $twig;
413✔
589
    }
590

591
    /**
592
     * Adds escapers.
593
     *
594
     * @param Environment $twig The Twig Environment.
595
     * @return Environment
596
     */
597
    public function add_timber_escapers($twig)
413✔
598
    {
599
        $esc_url = fn (Environment $env, $string) => \esc_url($string);
413✔
600

601
        $wp_kses_post = fn (Environment $env, $string) => \wp_kses_post($string);
413✔
602

603
        $esc_html = fn (Environment $env, $string) => \esc_html($string);
413✔
604

605
        $esc_js = fn (Environment $env, $string) => \esc_js($string);
413✔
606

607
        if (\class_exists(EscaperRuntime::class)) {
413✔
608
            $escaper_extension = $twig->getRuntime(EscaperRuntime::class);
413✔
609
            $escaper_extension->setEscaper('esc_url', '\esc_url');
413✔
610
            $escaper_extension->setEscaper('wp_kses_post', '\wp_kses_post');
413✔
611
            $escaper_extension->setEscaper('esc_html', '\esc_html');
413✔
612
            $escaper_extension->setEscaper('esc_js', '\esc_js');
413✔
613
        } elseif ($twig->hasExtension(EscaperExtension::class)) {
×
614
            $escaper_extension = $twig->getExtension(EscaperExtension::class);
×
615
            $escaper_extension->setEscaper('esc_url', $esc_url);
×
616
            $escaper_extension->setEscaper('wp_kses_post', $wp_kses_post);
×
617
            $escaper_extension->setEscaper('esc_html', $esc_html);
×
618
            $escaper_extension->setEscaper('esc_js', $esc_js);
×
619
        }
620

621
        return $twig;
413✔
622
    }
623

624
    /**
625
     * Overwrite Twig defaults.
626
     *
627
     * Makes Twig compatible with how WordPress handles dates, timezones, numbers and perhaps other items in
628
     * the future
629
     *
630
     * @since 2.0.0
631
     *
632
     * @throws \Twig\Error\RuntimeError
633
     * @param Environment $twig Twig Environment.
634
     *
635
     * @return Environment
636
     */
637
    public function set_defaults(Environment $twig)
413✔
638
    {
639
        $twig->getExtension(CoreExtension::class)->setDateFormat(\get_option('date_format'), '%d days');
413✔
640
        $twig->getExtension(CoreExtension::class)->setTimezone(\wp_timezone_string());
413✔
641

642
        /** @see https://developer.wordpress.org/reference/functions/number_format_i18n/ */
643
        global $wp_locale;
644
        if (isset($wp_locale)) {
413✔
645
            $twig->getExtension(CoreExtension::class)->setNumberFormat(0, $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep']);
413✔
646
        }
647

648
        return $twig;
413✔
649
    }
650

651
    /**
652
     * Converts a date to the given format.
653
     *
654
     * @internal
655
     * @since 2.0.0
656
     * @see  twig_date_format_filter()
657
     * @link https://twig.symfony.com/doc/2.x/filters/date.html
658
     *
659
     * @throws Exception
660
     *
661
     * @param Environment         $env      Twig Environment.
662
     * @param null|string|int|DateTime $date     A date.
663
     * @param null|string               $format   Optional. PHP date format. Will return the
664
     *                                            current date as a DateTimeImmutable object by
665
     *                                            default.
666
     * @param null                      $timezone Optional. The target timezone. Use `null` to use
667
     *                                            the default or
668
     *                                            `false` to leave the timezone unchanged.
669
     *
670
     * @return false|string A formatted date.
671
     */
672
    public function twig_date_format_filter(Environment $env, $date = null, $format = null, $timezone = null)
68✔
673
    {
674
        // Support for DateInterval.
675
        if ($date instanceof DateInterval) {
68✔
676
            if (null === $format) {
5✔
677
                $format = $env->getExtension(CoreExtension::class)->getDateFormat()[1];
2✔
678
            }
679

680
            return $date->format($format);
5✔
681
        }
682

683
        if (null === $date || 'now' === $date) {
63✔
684
            return DateTimeHelper::wp_date($format, null);
2✔
685
        }
686

687
        /**
688
         * If a string is given and it’s not a timestamp (e.g. "2010-01-28T15:00:00+04:00", try creating a DateTime
689
         * object and read the timezone from that string.
690
         */
691
        if (\is_string($date) && !\ctype_digit($date)) {
61✔
692
            $date_obj = \date_create($date);
15✔
693

694
            if ($date_obj) {
15✔
695
                $date = $date_obj;
15✔
696
            }
697
        }
698

699
        /**
700
         * Check for `false` parameter in |date filter in Twig
701
         *
702
         * @link https://twig.symfony.com/doc/2.x/filters/date.html#timezone
703
         */
704
        if (false === $timezone && $date instanceof DateTimeInterface) {
61✔
705
            $timezone = $date->getTimezone();
7✔
706
        }
707

708
        return DateTimeHelper::wp_date($format, $date, $timezone);
61✔
709
    }
710

711
    /**
712
     *
713
     *
714
     * @return array
715
     */
716
    public function to_array(mixed $arr)
2✔
717
    {
718
        if (\is_array($arr)) {
2✔
719
            return $arr;
1✔
720
        }
721
        $arr = [$arr];
1✔
722
        return $arr;
1✔
723
    }
724

725
    /**
726
     *
727
     *
728
     * @param string  $function_name
729
     * @return mixed
730
     */
731
    public function exec_function($function_name)
8✔
732
    {
733
        $args = \func_get_args();
8✔
734
        \array_shift($args);
8✔
735
        if (\is_string($function_name)) {
8✔
736
            $function_name = \trim($function_name);
8✔
737
        }
738
        return \call_user_func_array($function_name, ($args));
8✔
739
    }
740

741
    /**
742
     *
743
     *
744
     * @param string  $content
745
     * @return string
746
     */
747
    public function twig_pretags($content)
1✔
748
    {
749
        return \preg_replace_callback('|<pre.*>(.*)</pre|isU', [&$this, 'convert_pre_entities'], $content);
1✔
750
    }
751

752
    /**
753
     *
754
     *
755
     * @param array   $matches
756
     * @return string
757
     */
758
    public function convert_pre_entities($matches)
1✔
759
    {
760
        return \str_replace($matches[1], \htmlentities((string) $matches[1]), (string) $matches[0]);
1✔
761
    }
762

763
    /**
764
     * Formats a date.
765
     *
766
     * @deprecated 2.0.0
767
     *
768
     * @param null|string|false    $format Optional. PHP date format. Will use the `date_format`
769
     *                                     option as a default.
770
     * @param string|int|DateTime $date   A date.
771
     *
772
     * @return string
773
     */
774
    public function intl_date($date, $format = null)
×
775
    {
776
        Helper::deprecated('intl_date', 'DateTimeHelper::wp_date', '2.0.0');
×
777

778
        return DateTimeHelper::wp_date($format, $date);
×
779
    }
780

781
    /**
782
     *
783
     * @deprecated 2.0.0
784
     *
785
     * Returns the difference between two times in a human readable format.
786
     *
787
     * Differentiates between past and future dates.
788
     *
789
     * @see \human_time_diff()
790
     *
791
     * @param int|string $from          Base date as a timestamp or a date string.
792
     * @param int|string $to            Optional. Date to calculate difference to as a timestamp or
793
     *                                  a date string. Default to current time.
794
     * @param string     $format_past   Optional. String to use for past dates. To be used with
795
     *                                  `sprintf()`. Default `%s ago`.
796
     * @param string     $format_future Optional. String to use for future dates. To be used with
797
     *                                  `sprintf()`. Default `%s from now`.
798
     *
799
     * @return string
800
     */
801
    public static function time_ago($from, $to = null, $format_past = null, $format_future = null)
×
802
    {
803
        Helper::deprecated('time_ago', 'DateTimeHelper::time_ago', '2.0.0');
×
804

805
        return DateTimeHelper::time_ago($from, $to, $format_past, $format_future);
×
806
    }
807

808
    /**
809
     * @param array $arr
810
     * @param string $first_delimiter
811
     * @param string $second_delimiter
812
     * @return string
813
     */
814
    public function add_list_separators($arr, $first_delimiter = ',', $second_delimiter = ' and')
2✔
815
    {
816
        $length = \count($arr);
2✔
817
        $list = '';
2✔
818
        foreach ($arr as $index => $item) {
2✔
819
            if ($index < $length - 2) {
2✔
820
                $delimiter = $first_delimiter . ' ';
2✔
821
            } elseif ($index == $length - 2) {
2✔
822
                $delimiter = $second_delimiter . ' ';
2✔
823
            } else {
824
                $delimiter = '';
2✔
825
            }
826
            $list = $list . $item . $delimiter;
2✔
827
        }
828
        return $list;
2✔
829
    }
830
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc