PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-assets/src/class-assets.php +131 -42 12.0.3 → 16.3 View file →
@@ -7,8 +7,9 @@
7 7
8 8 namespace Automattic\Jetpack;
9 9
10 10 use Automattic\Jetpack\Assets\Semver;
11 +use Automattic\Jetpack\Assets\Shared_Stores_Assets;
11 12 use Automattic\Jetpack\Constants as Jetpack_Constants;
12 13 use InvalidArgumentException;
13 14
14 15 /**
@@ -31,13 +32,36 @@
31 32
32 33 /**
33 34 * The registered textdomain mappings.
34 35 *
35 - * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver, string path_prefix ) )`.
36 + * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver ) )`.
36 37 */
37 38 private static $domain_map = array();
38 39
39 40 /**
41 + * The registered package paths, by textdomain.
42 + *
43 + * Separate from `$domain_map` because the two answer different questions:
44 + * the map says which domain a package's strings are translated under, while
45 + * this says where the package's files live — the prefix WordPress hashes to
46 + * name a JS translation file. A package whose textdomain is already its
47 + * plugin's has nothing to alias but still needs the path.
48 + *
49 + * Note the entries are keyed by domain, not by script: `downloadI18n()`
50 + * prepends a domain's prefix to every bundle path looked up under it. That
51 + * is only ever one package's path, so a plugin must not both bundle a
52 + * package whose textdomain is the plugin's own and load its own
53 + * `wp-jp-i18n-loader` bundles under that same domain — the package's prefix
54 + * would be applied to the plugin's catalogs too, and they would all 404. No
55 + * plugin does both today. One that needs to should give the package a
56 + * distinct textdomain, the way `jetpack-backup-pkg` and
57 + * `jetpack-videopress-pkg` do.
58 + *
59 + * @var array `array( domain => array( string semver, string path_prefix ) )`.
60 + */
61 + private static $domain_paths = array();
62 +
63 + /**
40 64 * Constructor.
41 65 *
42 66 * Static-only class, so nothing here.
43 67 */
@@ -53,9 +77,8 @@
53 77 */
54 78 public static function instance() {
55 79 if ( ! isset( self::$instance ) ) {
56 80 self::$instance = new Assets();
57 - self::$instance->init_hooks();
58 81 }
59 82
60 83 return self::$instance;
61 84 }
@@ -60,24 +83,18 @@
60 83 return self::$instance;
61 84 }
62 85
63 86 /**
64 - * Initalize the hooks as needed.
65 - */
66 - private function init_hooks() {
67 - /*
68 - * Load some scripts asynchronously.
69 - */
70 - add_filter( 'script_loader_tag', array( $this, 'script_add_async' ), 10, 2 );
71 - }
72 -
73 - /**
74 87 * A public method for adding the async script.
75 88 *
89 + * @deprecated Since 2.1.0, the `strategy` feature should be used instead, with the "defer" setting.
90 + *
76 91 * @param string $script_handle Script handle.
77 92 */
78 - public function add_async_script( $script_handle ) {
79 - $this->defer_script_handles[] = $script_handle;
93 + public static function add_async_script( $script_handle ) {
94 + _deprecated_function( __METHOD__, '2.1.0' );
95 +
96 + wp_script_add_data( $script_handle, 'strategy', 'defer' );
80 97 }
81 98
82 99 /**
83 100 * Add an async attribute to scripts that can be loaded deferred.
@@ -82,12 +99,15 @@
82 99 /**
83 100 * Add an async attribute to scripts that can be loaded deferred.
84 101 * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
85 102 *
103 + * @deprecated Since 2.1.0, the `strategy` feature should be used instead.
104 + *
86 105 * @param string $tag The <script> tag for the enqueued script.
87 106 * @param string $handle The script's registered handle.
88 107 */
89 108 public function script_add_async( $tag, $handle ) {
109 + _deprecated_function( __METHOD__, '2.1.0' );
90 110 if ( empty( $this->defer_script_handles ) ) {
91 111 return $tag;
92 112 }
93 113
@@ -101,8 +121,10 @@
101 121
102 122 /**
103 123 * A helper function that lets you enqueue scripts in an async fashion.
104 124 *
125 + * @deprecated Since 2.1.0 - use the strategy feature instead.
126 + *
105 127 * @param string $handle Name of the script. Should be unique.
106 128 * @param string $min_path Minimized script path.
107 129 * @param string $non_min_path Full Script path.
108 130 * @param array $deps Array of script dependencies.
@@ -109,11 +131,11 @@
109 131 * @param bool $ver The script version.
110 132 * @param bool $in_footer Should the script be included in the footer.
111 133 */
112 134 public static function enqueue_async_script( $handle, $min_path, $non_min_path, $deps = array(), $ver = false, $in_footer = true ) {
113 - $assets_instance = self::instance();
114 - $assets_instance->add_async_script( $handle );
135 + _deprecated_function( __METHOD__, '2.1.0' );
115 136 wp_enqueue_script( $handle, self::get_file_url_for_environment( $min_path, $non_min_path ), $deps, $ver, $in_footer );
137 + wp_script_add_data( $handle, 'strategy', 'defer' );
116 138 }
117 139
118 140 // endregion .
119 141
@@ -258,13 +280,13 @@
258 280
259 281 $ret = '';
260 282 $ret .= isset( $parts['scheme'] ) ? $parts['scheme'] . '://' : '';
261 283 if ( isset( $parts['user'] ) || isset( $parts['pass'] ) ) {
262 - $ret .= isset( $parts['user'] ) ? $parts['user'] : '';
284 + $ret .= $parts['user'] ?? '';
263 285 $ret .= isset( $parts['pass'] ) ? ':' . $parts['pass'] : '';
264 286 $ret .= '@';
265 287 }
266 - $ret .= isset( $parts['host'] ) ? $parts['host'] : '';
288 + $ret .= $parts['host'] ?? '';
267 289 $ret .= isset( $parts['port'] ) ? ':' . $parts['port'] : '';
268 290
269 291 $pp = explode( '/', $parts['path'] );
270 292 if ( '' === $pp[0] ) {
@@ -280,9 +302,9 @@
280 302 } else {
281 303 ++$i;
282 304 }
283 305 }
284 - $ret .= join( '/', $pp );
306 + $ret .= implode( '/', $pp );
285 307
286 308 $ret .= isset( $parts['query'] ) ? '?' . $parts['query'] : '';
287 309 $ret .= isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
288 310
@@ -305,14 +327,15 @@
305 327 *
306 328 * This wrapper handles all of that.
307 329 *
308 330 * @since 1.12.0
331 + * @since 2.1.0 Add a new `strategy` option to leverage WP >= 6.3 script strategy feature. The `async` option is deprecated.
309 332 * @param string $handle Name of the script. Should be unique across both scripts and styles.
310 333 * @param string $path Minimized script path.
311 334 * @param string $relative_to File that `$path` is relative to. Pass `__FILE__`.
312 335 * @param array $options Additional options:
313 336 * - `asset_path`: (string|null) `.asset.php` to load. Default is to base it on `$path`.
314 - * - `async`: (bool) Set true to register the script as async, like `Assets::enqueue_async_script()`
337 + * - `async`: (bool) Set true to register the script as deferred, like `Assets::enqueue_async_script()`. Deprecated in favor of `strategy`.
315 338 * - `css_dependencies`: (string[]) Additional style dependencies to queue.
316 339 * - `css_path`: (string|null) `.css` to load. Default is to base it on `$path`.
317 340 * - `dependencies`: (string[]) Additional script dependencies to queue.
318 341 * - `enqueue`: (bool) Set true to enqueue the script immediately.
@@ -319,10 +342,12 @@
319 342 * - `in_footer`: (bool) Set true to register script for the footer.
320 343 * - `media`: (string) Media for the css file. Default 'all'.
321 344 * - `minify`: (bool|null) Set true to pass `minify=true` in the query string, or `null` to suppress the normal `minify=false`.
322 345 * - `nonmin_path`: (string) Non-minified script path.
346 + * - `strategy`: (string) Specify a script strategy to use, eg. `defer` or `async`. Default is `""`.
323 347 * - `textdomain`: (string) Text domain for the script. Required if the script depends on wp-i18n.
324 348 * - `version`: (string) Override the version from the `asset_path` file.
349 + * @phan-param array{asset_path?:?string,async?:bool,css_dependencies?:string[],css_path?:?string,dependencies?:string[],enqueue?:bool,in_footer?:bool,media?:string,minify?:?bool,nonmin_path?:string,strategy?:string,textdomain?:string,version?:string} $options
325 350 * @throws \InvalidArgumentException If arguments are invalid.
326 351 */
327 352 public static function register_script( $handle, $path, $relative_to, array $options = array() ) {
328 353 if ( substr( $path, -3 ) !== '.js' ) {
@@ -328,8 +353,12 @@
328 353 if ( substr( $path, -3 ) !== '.js' ) {
329 354 throw new \InvalidArgumentException( '$path must end in ".js"' );
330 355 }
331 356
357 + if ( isset( $options['async'] ) ) {
358 + _deprecated_argument( __METHOD__, '2.1.0', 'The `async` option is deprecated in favor of `strategy`' );
359 + }
360 +
332 361 $dir = dirname( $relative_to );
333 362 $base = substr( $path, 0, -3 );
334 363 $options += array(
335 364 'asset_path' => "$base.asset.php",
@@ -340,12 +369,14 @@
340 369 'enqueue' => false,
341 370 'in_footer' => false,
342 371 'media' => 'all',
343 372 'minify' => false,
373 + 'strategy' => '',
344 374 'textdomain' => null,
345 375 );
376 + '@phan-var array{asset_path:?string,async:bool,css_dependencies:string[],css_path:?string,dependencies:string[],enqueue:bool,in_footer:bool,media:string,minify:?bool,nonmin_path?:string,strategy:string,textdomain:string,version?:string} $options'; // Phan gets confused by the array addition.
346 377
347 - if ( $options['css_path'] && substr( $options['css_path'], -4 ) !== '.css' ) {
378 + if ( is_string( $options['css_path'] ) && $options['css_path'] !== '' && substr( $options['css_path'], -4 ) !== '.css' ) {
348 379 throw new \InvalidArgumentException( '$options[\'css_path\'] must end in ".css"' );
349 380 }
350 381
351 382 if ( isset( $options['nonmin_path'] ) ) {
@@ -369,17 +400,28 @@
369 400 }
370 401 ),
371 402 $options['css_dependencies']
372 403 );
373 - $ver = isset( $options['version'] ) ? $options['version'] : $asset['version'];
404 + $ver = $options['version'] ?? $asset['version'];
374 405 } else {
375 - $ver = isset( $options['version'] ) ? $options['version'] : filemtime( "$dir/$path" );
406 + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
407 + $ver = $options['version'] ?? @filemtime( "$dir/$path" );
376 408 }
377 409
378 - wp_register_script( $handle, $url, $options['dependencies'], $ver, $options['in_footer'] );
379 - if ( $options['async'] ) {
380 - self::instance()->add_async_script( $handle );
410 + if ( $options['async'] && '' === $options['strategy'] ) { // Handle the deprecated `async` option
411 + $options['strategy'] = 'defer';
381 412 }
413 + wp_register_script(
414 + $handle,
415 + $url,
416 + $options['dependencies'],
417 + $ver,
418 + array(
419 + 'in_footer' => $options['in_footer'],
420 + 'strategy' => $options['strategy'],
421 + )
422 + );
423 +
382 424 if ( $options['textdomain'] ) {
383 425 // phpcs:ignore Jetpack.Functions.I18n.DomainNotLiteral
384 426 wp_set_script_translations( $handle, $options['textdomain'] );
385 427 } elseif ( in_array( 'wp-i18n', $options['dependencies'], true ) ) {
@@ -390,9 +432,9 @@
390 432 ''
391 433 );
392 434 }
393 435
394 - if ( $options['css_path'] && file_exists( "$dir/{$options['css_path']}" ) ) {
436 + if ( is_string( $options['css_path'] ) && $options['css_path'] !== '' && file_exists( "$dir/{$options['css_path']}" ) ) {
395 437 $csspath = $options['css_path'];
396 438 if ( is_rtl() ) {
397 439 $rtlcsspath = substr( $csspath, 0, -4 ) . '.rtl.css';
398 440 if ( file_exists( "$dir/$rtlcsspath" ) ) {
@@ -428,8 +470,21 @@
428 470 }
429 471 }
430 472
431 473 /**
474 + * Re-hook the bootstraps an older copy's `actions.php` did not know about. See JETPACK-2649.
475 + *
476 + * Static callables only: `add_action()` dedupes those, but closures and object callables would
477 + * register twice. Callers must run before `wp_loaded`.
478 + *
479 + * @access private
480 + * @since 5.0.5
481 + */
482 + public static function ensure_package_bootstrap() {
483 + Shared_Stores_Assets::configure();
484 + }
485 +
486 + /**
432 487 * 'wp_default_scripts' action handler.
433 488 *
434 489 * This registers the `wp-jp-i18n-loader` script for use by Webpack bundles built with
435 490 * `@automattic/i18n-loader-webpack-plugin`.
@@ -448,8 +503,9 @@
448 503 $lang_dir = Jetpack_Constants::get_constant( 'WP_LANG_DIR' );
449 504 $content_dir = Jetpack_Constants::get_constant( 'WP_CONTENT_DIR' );
450 505 $abspath = Jetpack_Constants::get_constant( 'ABSPATH' );
451 506
507 + // Note: str_starts_with() is not used here, as wp-includes/compat.php may not be loaded at this point.
452 508 if ( strpos( $lang_dir, $content_dir ) === 0 ) {
453 509 $data['baseUrl'] = content_url( substr( trailingslashit( $lang_dir ), strlen( trailingslashit( $content_dir ) ) ) );
454 510 } elseif ( strpos( $lang_dir, $abspath ) === 0 ) {
455 511 $data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) );
@@ -454,10 +510,12 @@
454 510 } elseif ( strpos( $lang_dir, $abspath ) === 0 ) {
455 511 $data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) );
456 512 }
457 513
458 - foreach ( self::$domain_map as $from => list( $to, $type, , $path ) ) {
514 + foreach ( self::$domain_map as $from => list( $to, $type ) ) {
459 515 $data['domainMap'][ $from ] = ( 'core' === $type ? '' : "{$type}/" ) . $to;
516 + }
517 + foreach ( self::$domain_paths as $from => list( , $path ) ) {
460 518 if ( '' !== $path ) {
461 519 $data['domainPaths'][ $from ] = trailingslashit( $path );
462 520 }
463 521 }
@@ -490,9 +548,16 @@
490 548 );
491 549 }
492 550 $url = self::normalize_path( plugins_url( $path, __FILE__ ) );
493 551 $url = add_query_arg( 'minify', 'true', $url );
494 - $wp_scripts->add( 'wp-jp-i18n-loader', $url, $asset['dependencies'], $asset['version'] );
552 +
553 + $handle = 'wp-jp-i18n-loader';
554 +
555 + $wp_scripts->add( $handle, $url, $asset['dependencies'], $asset['version'] );
556 +
557 + // Ensure the script is loaded in the footer and deferred.
558 + $wp_scripts->add_data( $handle, 'group', 1 );
559 +
495 560 if ( ! is_array( $data ) ||
496 561 ! isset( $data['baseUrl'] ) || ! ( is_string( $data['baseUrl'] ) || false === $data['baseUrl'] ) ||
497 562 ! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
498 563 ! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] ) ||
@@ -497,19 +562,19 @@
497 562 ! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
498 563 ! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] ) ||
499 564 ! isset( $data['domainPaths'] ) || ! is_array( $data['domainPaths'] )
500 565 ) {
501 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );' );
566 + $wp_scripts->add_inline_script( $handle, 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );' );
502 567 } elseif ( ! $data['baseUrl'] ) {
503 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );' );
568 + $wp_scripts->add_inline_script( $handle, 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );' );
504 569 } else {
505 570 $data['domainMap'] = (object) $data['domainMap']; // Ensure it becomes a json object.
506 571 $data['domainPaths'] = (object) $data['domainPaths']; // Ensure it becomes a json object.
507 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'wp.jpI18nLoader.state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';' );
572 + $wp_scripts->add_inline_script( $handle, 'wp.jpI18nLoader.state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';' );
508 573 }
509 574
510 575 // Deprecated state module: Depend on wp-i18n to ensure global `wp` exists and because anything needing this will need that too.
511 - $wp_scripts->add( 'wp-jp-i18n-state', null, array( 'wp-deprecated', 'wp-jp-i18n-loader' ) );
576 + $wp_scripts->add( 'wp-jp-i18n-state', false, array( 'wp-deprecated', $handle ) );
512 577 $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.deprecated( "wp-jp-i18n-state", { alternative: "wp-jp-i18n-loader" } );' );
513 578 $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.jpI18nState = wp.jpI18nLoader.state;' );
514 579 }
515 580
@@ -558,13 +623,30 @@
558 623 ''
559 624 );
560 625 }
561 626
627 + // Where the package lives is needed for JS translation files whether or
628 + // not its domain is aliased, so it is recorded before the self-alias
629 + // check below.
630 + if (
631 + empty( self::$domain_paths[ $from ] ) ||
632 + Semver::compare( $ver, self::$domain_paths[ $from ][0] ) > 0
633 + ) {
634 + self::$domain_paths[ $from ] = array( $ver, $path );
635 + }
636 +
637 + // A self-alias would make filter_gettext() re-translate into the same
638 + // domain, recursing infinitely on any untranslated string (a package
639 + // textdomain can collide with its containing plugin's slug).
640 + if ( $from === $to ) {
641 + return;
642 + }
643 +
562 644 if ( empty( self::$domain_map[ $from ] ) ) {
563 645 self::init_domain_map_hooks( $from, array() === self::$domain_map );
564 - self::$domain_map[ $from ] = array( $to, $totype, $ver, $path );
646 + self::$domain_map[ $from ] = array( $to, $totype, $ver );
565 647 } elseif ( Semver::compare( $ver, self::$domain_map[ $from ][2] ) > 0 ) {
566 - self::$domain_map[ $from ] = array( $to, $totype, $ver, $path );
648 + self::$domain_map[ $from ] = array( $to, $totype, $ver );
567 649 }
568 650 }
569 651
570 652 /**
@@ -574,8 +656,11 @@
574 656 * with the following properties:
575 657 * - 'domain': String, `$to`
576 658 * - 'type': String, `$totype`
577 659 * - 'packages': Array, mapping `$from` to `array( 'path' => $path, 'ver' => $ver )` (or to the string `$ver` for back compat).
660 + * - 'paths': Array, same shape, for packages whose textdomain is already
661 + * `$to`. Those must not be aliased — that would recurse — but their
662 + * paths are still needed to locate their JavaScript translations.
578 663 *
579 664 * @since 1.15.0
580 665 * @param string $file Mapping file.
581 666 */
@@ -589,8 +674,13 @@
589 674 );
590 675 }
591 676 self::alias_textdomain( $from, $data['domain'], $data['type'], $fromdata['ver'], $fromdata['path'] );
592 677 }
678 + // Aliasing a domain to itself is a no-op that `alias_textdomain()`
679 + // declines, leaving just the path registration these entries are for.
680 + foreach ( $data['paths'] ?? array() as $from => $fromdata ) {
681 + self::alias_textdomain( $from, $from, $data['type'], $fromdata['ver'], $fromdata['path'] );
682 + }
593 683 }
594 684
595 685 /**
596 686 * Register the hooks for textdomain aliasing.
@@ -633,9 +723,9 @@
633 723 * @return string Translated text.
634 724 */
635 725 public static function filter_gettext( $translation, $text, $domain ) {
636 726 if ( $translation === $text ) {
637 - // phpcs:ignore WordPress.WP.I18n
727 + // phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
638 728 $newtext = __( $text, self::$domain_map[ $domain ][0] );
639 729 if ( $newtext !== $text ) {
640 730 return $newtext;
641 731 }
@@ -649,15 +739,15 @@
649 739 * @since 1.15.0
650 740 * @param string $translation Translated text.
651 741 * @param string $single The text to be used if the number is singular.
652 742 * @param string $plural The text to be used if the number is plural.
653 - * @param string $number The number to compare against to use either the singular or plural form.
743 + * @param int $number The number to compare against to use either the singular or plural form.
654 744 * @param string $domain Text domain.
655 745 * @return string Translated text.
656 746 */
657 747 public static function filter_ngettext( $translation, $single, $plural, $number, $domain ) {
658 748 if ( $translation === $single || $translation === $plural ) {
659 - // phpcs:ignore WordPress.WP.I18n
749 + // phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
660 750 $translation = _n( $single, $plural, $number, self::$domain_map[ $domain ][0] );
661 751 }
662 752 return $translation;
663 753 }
@@ -673,9 +763,9 @@
673 763 * @return string Translated text.
674 764 */
675 765 public static function filter_gettext_with_context( $translation, $text, $context, $domain ) {
676 766 if ( $translation === $text ) {
677 - // phpcs:ignore WordPress.WP.I18n
767 + // phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
678 768 $translation = _x( $text, $context, self::$domain_map[ $domain ][0] );
679 769 }
680 770 return $translation;
681 771 }
@@ -686,9 +776,9 @@
686 776 * @since 1.15.0
687 777 * @param string $translation Translated text.
688 778 * @param string $single The text to be used if the number is singular.
689 779 * @param string $plural The text to be used if the number is plural.
690 - * @param string $number The number to compare against to use either the singular or plural form.
780 + * @param int $number The number to compare against to use either the singular or plural form.
691 781 * @param string $context Context information for the translators.
692 782 * @param string $domain Text domain.
693 783 * @return string Translated text.
694 784 */
@@ -693,9 +783,9 @@
693 783 * @return string Translated text.
694 784 */
695 785 public static function filter_ngettext_with_context( $translation, $single, $plural, $number, $context, $domain ) {
696 786 if ( $translation === $single || $translation === $plural ) {
697 - // phpcs:ignore WordPress.WP.I18n
787 + // phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
698 788 $translation = _nx( $single, $plural, $number, $context, self::$domain_map[ $domain ][0] );
699 789 }
700 790 return $translation;
701 791 }
@@ -729,9 +819,8 @@
729 819 return $file;
730 820 }
731 821
732 822 // endregion .
733 -
734 823 }
735 824
736 825 // Enable section folding in vim:
737 826 // vim: foldmarker=//\ region,//\ endregion foldmethod=marker