Dependency & supply-chain security
When to use this skill
Use this skill whenever code consumes third-party code or assets:
- Adding a Composer package, or vendoring a library copy into
vendor/ or assets/lib/.
- Enqueueing a script or stylesheet whose
src is a full https:// CDN URL.
- Loading PHP at runtime from the network, including "remote updater" patterns.
- Preparing the distributable zip and deciding what ships inside
vendor/.
- Merging a snippet taken from Stack Overflow, a blog post, or an AI answer.
Do not reach for this skill for flaws in first-party code: use the
output-escaping skill for XSS in your own markup, the
capability-permission-checks skill for authorization, and the
cron-background-job-security skill for cron callbacks.
A plugin is only as secure as the oldest library inside it. A large share of
WordPress plugin CVEs are vulnerable bundled dependencies or compromised
third-party code loaded at runtime, not bugs in the plugin's own logic.
Related: see the http-api-ssrf-prevention skill for validating URLs you fetch
data from, the filesystem-security skill for confining local includes, and
the secure-plugin-development skill for overall plugin hardening.
Core principles (and why they matter)
- Prefer core over bundling. WordPress registers jQuery, Underscore,
Backbone, media libraries, and the block-editor React build under documented
handles.
wp_enqueue_script() the core handle instead of shipping your own
copy: bundled duplicates drift out of date and conflict with other plugins
and themes that use the core version. Core patches its bundled libraries;
your vendored copy of the same library gets no patches.
- Vet before adding. Confirm the project shipped a release in the last
~12 months, scan its CVE history, and check the license is compatible with
GPL where required.
composer audit (Composer 2.4+) checks installed
packages against the Packagist advisory database; the
roave/security-advisories dev dependency refuses installs of
known-vulnerable versions outright.
- Lock and pin. Commit
composer.lock and build with composer install,
never composer update. Avoid minimum-stability: dev and unbounded
constraints like dev-main: they make builds unreproducible and
unauditable, so a compromise upstream is indistinguishable from your own
release.
- Ship a minimal artifact. Distributable zips must not contain dev
dependencies, tests, CI configs, or demo data.
.gitattributes
export-ignore lines keep them out of git archive / composer archive
builds, so attackers cannot read your tests to map untested code paths.
- Never load code from the network at runtime.
eval(), string
assert(), create_function() (removed in PHP 8), the preg /e modifier
(removed in PHP 7), or include/require of a fetched file, including
wp_remote_get() bodies and phar:// deserialization tricks, is RCE by
design. Updates ship through an update channel; fetched payloads are not an
update channel.
- Integrity-check front-end assets you do not host. Self-host when
possible. If a CDN is required, pin an exact version URL and add Subresource
Integrity (
integrity + crossorigin attributes) via the
script_loader_tag / style_loader_tag filters. Never enqueue a URL whose
content can change, such as latest.js.
- Keep update latency low for what you ship. Do not ship code that blocks
the auto-updates users rely on (
auto_update_plugin, auto_update_theme,
WP_AUTO_UPDATE_CORE, AUTOMATIC_UPDATER_DISABLED). Guard your own PHP and
WordPress minimums with version_compare() and release patched builds fast
when a bundled dependency announces a CVE.
- Treat pasted snippets as untrusted dependencies. Rewrite them against
current WordPress APIs and check for removed functions (
mysql_*,
create_function()) before merging. A snippet copied from 2011 carries the
threat model of 2011.
Step-by-step implementation
Before vendoring, check whether WordPress core already registers the asset.
The wp_register_script() documentation lists the default handles
(jquery, jquery-ui-*, underscore, backbone, wp-api, and more).
Enqueue the handle; do not print your own copy of the same file.
Vet each new dependency before the first composer require:
- Last tagged release within roughly the last 12 months.
- No open high-severity advisories: run
composer audit after adding.
- License compatible with the plugin's distribution (GPL-compatible for
WordPress.org hosting).
- For non-Composer front-end libraries, check advisories with OWASP
Dependency-Check or the vendor's security page.
Configure Composer for reproducible builds and commit the lock file:
{
"name": "acme/my-plugin",
"minimum-stability": "stable",
"require": {
"php": ">=7.4"
},
"require-dev": {
"roave/security-advisories": "dev-latest"
},
"config": {
"sort-packages": true
}
}
Build with composer install --no-dev --optimize-autoloader. Only
composer update intentionally bumps the lock file, and the diff gets
reviewed like any other code change.
Keep tests, CI configs, and demo data out of the shipped zip via
.gitattributes:
/.github export-ignore
/tests export-ignore
/demo export-ignore
/phpcs.xml.dist export-ignore
/phpunit.xml.dist export-ignore
Enforce your minimums at load time with version_compare() and refuse to
run on stacks below them (see the reference file for the guard).
For CDN assets: self-host if possible. Otherwise pin an exact version URL,
store an integrity hash per asset, and add integrity + crossorigin
through the script_loader_tag / style_loader_tag filters (full example
in the reference file).
Ship no auto-update blockers. Never distribute
add_filter( 'auto_update_plugin', '__return_false' ); or a
define( 'AUTOMATIC_UPDATER_DISABLED', true ); inside plugin code. If you
must influence updates, filter only your own plugin:
// Keep this plugin on the auto-update train; never silence the whole site.
add_filter( 'auto_update_plugin', 'my_plugin_auto_update_self', 10, 2 );
function my_plugin_auto_update_self( $update, $item ) {
if ( isset( $item->slug ) && 'my-plugin' === $item->slug ) {
return true;
}
return $update;
}
Grep the codebase before every release for runtime code loading:
eval(, assert(, create_function(, preg_replace( with /e,
include/require fed by wp_remote_get(), and phar:// stream usage.
Rewrite pasted snippets against current APIs before merging. Anything
calling mysql_* or create_function() was written for PHP that no longer
runs your code.
Supporting references
| Reference |
Load when |
| Dependency & supply-chain cheatsheet |
Choosing the applicable WordPress API or control for dependency and supply-chain security. |
| Dependency & supply-chain checklist |
Before final verification of the dependency & supply-chain controls. |
| Secure dependency management |
Implementing version-gated dependency loading, core asset reuse, pinned CDN assets, and confined local includes. |
Common AI mistakes / anti-patterns
Mistake 1 - Vendoring a stale copy of a library core already ships
// ❌ Insecure: bundles an old PHPMailer 5.x copy "because it worked".
// Old 5.x releases carry known remote-command-execution CVEs, and this copy
// never receives patches. Core's copy does.
require_once __DIR__ . '/vendor/phpmailer/phpmailer/PHPMailerAutoload.php';
// ✅ Secure: wp_mail() uses the PHPMailer version core ships and patches.
wp_mail( $to, $subject, $message, $headers );
Mistake 2 - CDN script with no version pin and no integrity
// ❌ Insecure: "chart.js" can change content at any time; the CDN operator
// (or anyone compromising it) controls the code that runs on every site.
wp_enqueue_script( 'my-charts', 'https://cdn.example.com/chart.js', array(), null );
// ✅ Secure: exact version URL + Subresource Integrity, or self-host the file.
wp_enqueue_script(
'my-charts',
'https://cdn.jsdelivr.net/npm/chart.js@4.1.2/dist/chart.umd.min.js',
array(),
'4.1.2', // Matches the version pinned in the URL path.
true // Load in the footer.
);
// integrity/crossorigin attributes are added in the script_loader_tag filter.
Mistake 3 - Runtime updater that fetches and executes code
// ❌ Insecure: RCE by design, and the plugin bricks itself when the host dies.
// $response = wp_remote_get( 'https://example.com/updater.php' );
// $body = wp_remote_retrieve_body( $response );
// eval( $body ); // executes whatever the remote server returns
// include $tmp_path; // same result if $tmp_path holds the fetched payload
// ✅ Secure: updates arrive through the WordPress.org update channel, or a
// signed self-hosted channel. Metadata travels over the network; code does not.
// Plugins on WordPress.org need no custom updater code at all.
Mistake 4 - Committing vendor/ with dev dependencies and CI configs
// ❌ Insecure: the distributable zip ships phpunit, phpcs configs, and test
// fixtures. Attackers read tests to map code paths nothing verifies.
// Built with: composer update (whatever was newest that Tuesday).
// ✅ Secure: export-ignore keeps dev trees out of archives; builds are pinned.
// .gitattributes:
// /tests export-ignore
// /.github export-ignore
// Build command:
// composer install --no-dev --optimize-autoloader
Mistake 5 - minimum-stability: dev plus unbounded constraints
// ❌ Insecure: every build resolves differently; you cannot audit what ships.
{
"minimum-stability": "dev",
"require": { "monolog/monolog": "dev-main" }
}
// ✅ Secure: stable channel, bounded constraints, committed lock file.
{
"minimum-stability": "stable",
"require": { "monolog/monolog": "^3.0" }
}
Mistake 6 - Shipping removed dynamic-code constructs
// ❌ Insecure: fatals on PHP 8 (create_function removed) and on PHP 7 (/e
// modifier removed). Both execute strings as code, which is the point.
// $double = create_function( '$a', 'return $a * 2;' );
// $bold = preg_replace( '/<b>(.*?)<\/b>/e', 'strtoupper("$1")', $html );
// ✅ Secure: closures and callbacks; no string-as-code anywhere.
$double = function ( $a ) {
return $a * 2;
};
$bold = preg_replace_callback(
'/<b>(.*?)<\/b>/',
function ( $m ) {
return strtoupper( $m[1] );
},
$html
);
Correct code examples
A complete reference module covering core-handle-first enqueuing, pinned CDN
assets with Subresource Integrity via the script_loader_tag filter, a
version_compare() minimum-version gate, and a guarded local include confined
to the plugin directory is in
references/secure-dependency-management.php.
A before-adding / before-shipping checklist is in
references/checklist.md, and a
situation-to-practice lookup table is in
references/cheatsheet.md.
Checklist
Official references
1---2name: dependency-supply-chain-security3description: Use when a plugin or theme bundles a third-party PHP or JavaScript library, enqueues an asset from a CDN, fetches or executes code at runtime, manages dependencies with Composer, or prepares the distributable zip. Covers core-handle-first enqueuing, dependency vetting with composer audit, lockfile pinning, export-ignore artifact hygiene, Subresource Integrity for CDN assets via script_loader_tag, and refusal of eval() and remote include patterns. Prevents supply-chain compromise through stale, unvetted, or remotely loaded third-party code.4license: MIT5---67# Dependency & supply-chain security89## When to use this skill1011Use this skill whenever code consumes third-party code or assets:1213- Adding a Composer package, or vendoring a library copy into `vendor/` or `assets/lib/`.14- Enqueueing a script or stylesheet whose `src` is a full `https://` CDN URL.15- Loading PHP at runtime from the network, including "remote updater" patterns.16- Preparing the distributable zip and deciding what ships inside `vendor/`.17- Merging a snippet taken from Stack Overflow, a blog post, or an AI answer.1819Do not reach for this skill for flaws in first-party code: use the20`output-escaping` skill for XSS in your own markup, the21`capability-permission-checks` skill for authorization, and the22`cron-background-job-security` skill for cron callbacks.2324A plugin is only as secure as the oldest library inside it. A large share of25WordPress plugin CVEs are vulnerable bundled dependencies or compromised26third-party code loaded at runtime, not bugs in the plugin's own logic.2728Related: see the `http-api-ssrf-prevention` skill for validating URLs you fetch29data from, the `filesystem-security` skill for confining local includes, and30the `secure-plugin-development` skill for overall plugin hardening.3132## Core principles (and why they matter)33341. **Prefer core over bundling.** WordPress registers jQuery, Underscore,35 Backbone, media libraries, and the block-editor React build under documented36 handles. `wp_enqueue_script()` the core handle instead of shipping your own37 copy: bundled duplicates drift out of date and conflict with other plugins38 and themes that use the core version. Core patches its bundled libraries;39 your vendored copy of the same library gets no patches.402. **Vet before adding.** Confirm the project shipped a release in the last41 ~12 months, scan its CVE history, and check the license is compatible with42 GPL where required. `composer audit` (Composer 2.4+) checks installed43 packages against the Packagist advisory database; the44 `roave/security-advisories` dev dependency refuses installs of45 known-vulnerable versions outright.463. **Lock and pin.** Commit `composer.lock` and build with `composer install`,47 never `composer update`. Avoid `minimum-stability: dev` and unbounded48 constraints like `dev-main`: they make builds unreproducible and49 unauditable, so a compromise upstream is indistinguishable from your own50 release.514. **Ship a minimal artifact.** Distributable zips must not contain dev52 dependencies, tests, CI configs, or demo data. `.gitattributes`53 `export-ignore` lines keep them out of `git archive` / `composer archive`54 builds, so attackers cannot read your tests to map untested code paths.555. **Never load code from the network at runtime.** `eval()`, string56 `assert()`, `create_function()` (removed in PHP 8), the preg `/e` modifier57 (removed in PHP 7), or `include`/`require` of a fetched file, including58 `wp_remote_get()` bodies and `phar://` deserialization tricks, is RCE by59 design. Updates ship through an update channel; fetched payloads are not an60 update channel.616. **Integrity-check front-end assets you do not host.** Self-host when62 possible. If a CDN is required, pin an exact version URL and add Subresource63 Integrity (`integrity` + `crossorigin` attributes) via the64 `script_loader_tag` / `style_loader_tag` filters. Never enqueue a URL whose65 content can change, such as `latest.js`.667. **Keep update latency low for what you ship.** Do not ship code that blocks67 the auto-updates users rely on (`auto_update_plugin`, `auto_update_theme`,68 `WP_AUTO_UPDATE_CORE`, `AUTOMATIC_UPDATER_DISABLED`). Guard your own PHP and69 WordPress minimums with `version_compare()` and release patched builds fast70 when a bundled dependency announces a CVE.718. **Treat pasted snippets as untrusted dependencies.** Rewrite them against72 current WordPress APIs and check for removed functions (`mysql_*`,73 `create_function()`) before merging. A snippet copied from 2011 carries the74 threat model of 2011.7576## Step-by-step implementation77781. Before vendoring, check whether WordPress core already registers the asset.79 The `wp_register_script()` documentation lists the default handles80 (`jquery`, `jquery-ui-*`, `underscore`, `backbone`, `wp-api`, and more).81 Enqueue the handle; do not print your own copy of the same file.822. Vet each new dependency before the first `composer require`:83 - Last tagged release within roughly the last 12 months.84 - No open high-severity advisories: run `composer audit` after adding.85 - License compatible with the plugin's distribution (GPL-compatible for86 WordPress.org hosting).87 - For non-Composer front-end libraries, check advisories with OWASP88 Dependency-Check or the vendor's security page.893. Configure Composer for reproducible builds and commit the lock file:9091 ```json92 {93 "name": "acme/my-plugin",94 "minimum-stability": "stable",95 "require": {96 "php": ">=7.4"97 },98 "require-dev": {99 "roave/security-advisories": "dev-latest"100 },101 "config": {102 "sort-packages": true103 }104 }105 ```106107 Build with `composer install --no-dev --optimize-autoloader`. Only108 `composer update` intentionally bumps the lock file, and the diff gets109 reviewed like any other code change.1104. Keep tests, CI configs, and demo data out of the shipped zip via111 `.gitattributes`:112113 ```114 /.github export-ignore115 /tests export-ignore116 /demo export-ignore117 /phpcs.xml.dist export-ignore118 /phpunit.xml.dist export-ignore119 ```1201215. Enforce your minimums at load time with `version_compare()` and refuse to122 run on stacks below them (see the reference file for the guard).1236. For CDN assets: self-host if possible. Otherwise pin an exact version URL,124 store an integrity hash per asset, and add `integrity` + `crossorigin`125 through the `script_loader_tag` / `style_loader_tag` filters (full example126 in the reference file).1277. Ship no auto-update blockers. Never distribute128 `add_filter( 'auto_update_plugin', '__return_false' );` or a129 `define( 'AUTOMATIC_UPDATER_DISABLED', true );` inside plugin code. If you130 must influence updates, filter only your own plugin:131132 ```php133 // Keep this plugin on the auto-update train; never silence the whole site.134 add_filter( 'auto_update_plugin', 'my_plugin_auto_update_self', 10, 2 );135 function my_plugin_auto_update_self( $update, $item ) {136 if ( isset( $item->slug ) && 'my-plugin' === $item->slug ) {137 return true;138 }139 return $update;140 }141 ```1421438. Grep the codebase before every release for runtime code loading:144 `eval(`, `assert(`, `create_function(`, `preg_replace(` with `/e`,145 `include`/`require` fed by `wp_remote_get()`, and `phar://` stream usage.1469. Rewrite pasted snippets against current APIs before merging. Anything147 calling `mysql_*` or `create_function()` was written for PHP that no longer148 runs your code.149150### Supporting references151152| Reference | Load when |153| --- | --- |154| [Dependency & supply-chain cheatsheet](references/cheatsheet.md) | Choosing the applicable WordPress API or control for dependency and supply-chain security. |155| [Dependency & supply-chain checklist](references/checklist.md) | Before final verification of the dependency & supply-chain controls. |156| [Secure dependency management](references/secure-dependency-management.php) | Implementing version-gated dependency loading, core asset reuse, pinned CDN assets, and confined local includes. |157158## Common AI mistakes / anti-patterns159160### Mistake 1 - Vendoring a stale copy of a library core already ships161162```php163// ❌ Insecure: bundles an old PHPMailer 5.x copy "because it worked".164// Old 5.x releases carry known remote-command-execution CVEs, and this copy165// never receives patches. Core's copy does.166require_once __DIR__ . '/vendor/phpmailer/phpmailer/PHPMailerAutoload.php';167```168169```php170// ✅ Secure: wp_mail() uses the PHPMailer version core ships and patches.171wp_mail( $to, $subject, $message, $headers );172```173174### Mistake 2 - CDN script with no version pin and no integrity175176```php177// ❌ Insecure: "chart.js" can change content at any time; the CDN operator178// (or anyone compromising it) controls the code that runs on every site.179wp_enqueue_script( 'my-charts', 'https://cdn.example.com/chart.js', array(), null );180```181182```php183// ✅ Secure: exact version URL + Subresource Integrity, or self-host the file.184wp_enqueue_script(185 'my-charts',186 'https://cdn.jsdelivr.net/npm/chart.js@4.1.2/dist/chart.umd.min.js',187 array(),188 '4.1.2', // Matches the version pinned in the URL path.189 true // Load in the footer.190);191// integrity/crossorigin attributes are added in the script_loader_tag filter.192```193194### Mistake 3 - Runtime updater that fetches and executes code195196```php197// ❌ Insecure: RCE by design, and the plugin bricks itself when the host dies.198// $response = wp_remote_get( 'https://example.com/updater.php' );199// $body = wp_remote_retrieve_body( $response );200// eval( $body ); // executes whatever the remote server returns201// include $tmp_path; // same result if $tmp_path holds the fetched payload202```203204```php205// ✅ Secure: updates arrive through the WordPress.org update channel, or a206// signed self-hosted channel. Metadata travels over the network; code does not.207// Plugins on WordPress.org need no custom updater code at all.208```209210### Mistake 4 - Committing `vendor/` with dev dependencies and CI configs211212```php213// ❌ Insecure: the distributable zip ships phpunit, phpcs configs, and test214// fixtures. Attackers read tests to map code paths nothing verifies.215// Built with: composer update (whatever was newest that Tuesday).216```217218```php219// ✅ Secure: export-ignore keeps dev trees out of archives; builds are pinned.220// .gitattributes:221// /tests export-ignore222// /.github export-ignore223// Build command:224// composer install --no-dev --optimize-autoloader225```226227### Mistake 5 - `minimum-stability: dev` plus unbounded constraints228229```json230// ❌ Insecure: every build resolves differently; you cannot audit what ships.231{232 "minimum-stability": "dev",233 "require": { "monolog/monolog": "dev-main" }234}235```236237```json238// ✅ Secure: stable channel, bounded constraints, committed lock file.239{240 "minimum-stability": "stable",241 "require": { "monolog/monolog": "^3.0" }242}243```244245### Mistake 6 - Shipping removed dynamic-code constructs246247```php248// ❌ Insecure: fatals on PHP 8 (create_function removed) and on PHP 7 (/e249// modifier removed). Both execute strings as code, which is the point.250// $double = create_function( '$a', 'return $a * 2;' );251// $bold = preg_replace( '/<b>(.*?)<\/b>/e', 'strtoupper("$1")', $html );252```253254```php255// ✅ Secure: closures and callbacks; no string-as-code anywhere.256$double = function ( $a ) {257 return $a * 2;258};259$bold = preg_replace_callback(260 '/<b>(.*?)<\/b>/',261 function ( $m ) {262 return strtoupper( $m[1] );263 },264 $html265);266```267268## Correct code examples269270A complete reference module covering core-handle-first enqueuing, pinned CDN271assets with Subresource Integrity via the `script_loader_tag` filter, a272`version_compare()` minimum-version gate, and a guarded local include confined273to the plugin directory is in274[`references/secure-dependency-management.php`](references/secure-dependency-management.php).275276A before-adding / before-shipping checklist is in277[`references/checklist.md`](references/checklist.md), and a278situation-to-practice lookup table is in279[`references/cheatsheet.md`](references/cheatsheet.md).280281## Checklist282283- [ ] Core handles are used for assets WordPress registers (`jquery`, `underscore`, editor builds).284- [ ] Every bundled dependency is maintained (release within ~12 months) and audited (`composer audit`).285- [ ] `roave/security-advisories` is in `require-dev` for Composer projects.286- [ ] `composer.lock` is committed; builds run `composer install --no-dev`.287- [ ] `minimum-stability` is `stable`; no unbounded `dev-*` constraints.288- [ ] `.gitattributes` `export-ignore` keeps tests, CI configs, and demo data out of the zip.289- [ ] No `eval()`, string `assert()`, `create_function()`, preg `/e`, or remote `include`/`require` anywhere in shipped code.290- [ ] CDN assets are self-hosted, or pinned to an exact version with `integrity` + `crossorigin` attributes.291- [ ] No enqueued URL resolves to changeable content such as `latest.js`.292- [ ] Shipped code contains no `auto_update_plugin` / `auto_update_theme` / `AUTOMATIC_UPDATER_DISABLED` blockers.293- [ ] PHP and WordPress minimums are enforced with `version_compare()` before the plugin loads.294- [ ] Pasted snippets were rewritten against current APIs before merging.295296## Official references297298- [`wp_enqueue_script()`](https://developer.wordpress.org/reference/functions/wp_enqueue_script/)299- [`wp_enqueue_style()`](https://developer.wordpress.org/reference/functions/wp_enqueue_style/)300- [`wp_register_script()`](https://developer.wordpress.org/reference/functions/wp_register_script/) (default handle list)301- [`wp_script_add_data()`](https://developer.wordpress.org/reference/functions/wp_script_add_data/)302- [`script_loader_tag` filter](https://developer.wordpress.org/reference/hooks/script_loader_tag/)303- [`style_loader_tag` filter](https://developer.wordpress.org/reference/hooks/style_loader_tag/)304- [`WP_HTML_Tag_Processor` class](https://developer.wordpress.org/reference/classes/wp_html_tag_processor/)305- [`wp_remote_get()`](https://developer.wordpress.org/reference/functions/wp_remote_get/)306- [`wp_is_auto_update_enabled_for_type()`](https://developer.wordpress.org/reference/functions/wp_is_auto_update_enabled_for_type/)307- [`auto_update_{$type}` filter](https://developer.wordpress.org/reference/hooks/auto_update_type/) (`auto_update_plugin`, `auto_update_theme`)308- [`AUTOMATIC_UPDATER_DISABLED` constant](https://developer.wordpress.org/advanced-administration/wordpress/wp-config/#disable-wordpress-auto-updates)309- [`WP_AUTO_UPDATE_CORE` constant](https://developer.wordpress.org/advanced-administration/wordpress/wp-config/#disable-wordpress-core-updates)310- [`validate_file()`](https://developer.wordpress.org/reference/functions/validate_file/)311- [Composer `audit` command](https://getcomposer.org/doc/03-cli.md#audit)312- [Composer `composer.lock`](https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control)313- [Composer `minimum-stability`](https://getcomposer.org/doc/04-schema.md#minimum-stability)314- [Roave SecurityAdvisories](https://github.com/Roave/SecurityAdvisories)315- [MDN: Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)316- [OWASP Dependency-Check](https://owasp.org/www-project-dependency-check/)317- [OWASP Third-Party JavaScript Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html)318- [`version_compare()`](https://www.php.net/manual/en/function.version-compare.php)319- [`create_function()` (removed)](https://www.php.net/manual/en/function.create-function.php)320- [PCRE pattern modifiers (`/e` removed)](https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php)321- [`export-ignore` in `.gitattributes`](https://git-scm.com/docs/gitattributes)