Skip to content

Fix: Make PHP and JavaScript identifiers safe when cloning themes (#777) - #874

Open
thetwopct wants to merge 1 commit into
WordPress:trunkfrom
thetwopct:safer-identifiers-when-cloning-themes
Open

thetwopct wants to merge 1 commit into
WordPress:trunkfrom
thetwopct:safer-identifiers-when-cloning-themes

Conversation

@thetwopct

Copy link
Copy Markdown

Summary

Cloning a theme currently applies the same text replacement rules to human-readable theme names, slugs, PHP identifiers, and JavaScript identifiers. These values have different syntax requirements.

For example, cloning Ollie with the name My Theme could produce invalid PHP such as:

namespace My Theme;

Using the slug directly is also unsafe because hyphens, spaces, symbols, and leading numbers are not valid in PHP or JavaScript identifiers.

This PR makes namespace replacement context-aware for PHP and JavaScript while preserving the existing replacement behaviour for other text content.

Contributing this as just ran a workshop on Create Block Theme, and the errors experienced by users because of this issue was confusing and off-putting for something that should be a smooth easy experience.

What changed

PHP identifiers

PHP files are now processed using token_get_all() so identifiers can be handled separately from comments and string values.

Generated PHP identifiers use an underscore-separated form:

Context Original Cloned as My Theme
Namespace Ollie My_Theme
Class Ollie_Setup My_Theme_Setup
Function ollie_setup my_theme_setup
Variable $ollie_setting $my_theme_setting
Text domain ollie my-theme
Display name Ollie My Theme

Names beginning with a number receive a theme_ prefix so that the resulting PHP identifier remains valid.

For example, 2024 Clone produces:

namespace Theme_2024_Clone;

function theme_2024_clone_setup() {
}

JavaScript identifiers

JavaScript files now distinguish identifiers from quoted strings and comments.

JavaScript identifiers use the appropriate casing:

  • myTheme for variables and other camel-case identifiers.
  • MyTheme for globals and other capitalized identifiers.
  • my-theme for text domains, CSS classes, script handles, and other string values.
  • My Theme for display-name strings.

For example:

const extendable = window.ExtendableAnimations;
window.extendableOpenAnimationModal = () => true;

becomes:

const myTheme = window.MyThemeAnimations;
window.myThemeOpenAnimationModal = () => true;

Embedded identifier prefixes, such as custom event names, are also updated without inserting invalid hyphens:

'extendableAnimationSettingsChanged'

becomes:

'myThemeAnimationSettingsChanged'

Localized JavaScript globals

Object names passed to wp_localize_script() are JavaScript identifiers even though they appear inside PHP string literals.

These names are now converted using the same JavaScript identifier rules as the corresponding JavaScript files:

wp_localize_script(
	'extendable-animations',
	'ExtendableAnimations',
	array()
);

becomes:

wp_localize_script(
	'my-theme-animations',
	'MyThemeAnimations',
	array()
);

This keeps the PHP registration and JavaScript global references aligned.

Clone and ZIP paths

The file extension is now passed into the replacement logic in both cloning paths:

  • Cloning a theme to a directory.
  • Creating a cloned theme ZIP archive.

This allows PHP and JavaScript content to receive the appropriate identifier handling while retaining the existing generic replacement behaviour for CSS, SCSS, HTML, and text files.

Why

I am contributing this after running a Create Block Theme workshop where participants encountered these errors. The failures were confusing and off-putting in a workflow that should otherwise be a smooth and straightforward experience.

Popular themes such as Ollie use PHP namespaces and prefixed identifiers. A literal replacement using the entered theme name can therefore generate invalid PHP and make the cloned theme impossible to activate.

Other themes use theme-prefixed JavaScript variables, globals, events, and localized data. Replacing those prefixes with a hyphenated slug can similarly generate invalid or mismatched JavaScript.

Handling PHP and JavaScript identifiers according to their respective syntax rules makes more existing themes safely clonable without restricting the theme name users can enter.

Scope

This is intentionally a focused improvement and does not fully resolve #777.

Issue #777 covers replacement problems across every eligible file type and a wider range of theme structures and naming conventions. This PR adds syntax-aware handling for the two executable languages where invalid identifiers can immediately break a cloned theme:

  • PHP
  • JavaScript

The existing generic replacement remains in place for other supported text files. Further language-specific or structural replacement cases can continue to be addressed separately under #777.

For that reason, this PR uses Addresses #777 rather than closing the issue, and hopefully this stop gap solution is enough to improve Create Block Theme until or if a larger fix is warranted (as per @t-hamano)

Testing

Automated coverage has been added for:

  • PHP namespaces, classes, functions, variables, qualified names, and string references.
  • JavaScript variables, globals, custom event names, text domains, and CSS-class strings.
  • JavaScript identifiers derived from multi-word source slugs.
  • JavaScript global names registered through wp_localize_script().
  • Theme names containing spaces.
  • Theme names containing symbols.
  • Theme names beginning with numbers.

The following checks pass locally:

  • npm run test:unit
  • npm run test:unit:php:base
  • npm run test:unit:php:multisite-api
  • npm run lint:php
  • npm run lint:js
  • npm run lint:css
  • npm run lint:md-docs
  • npm run lint:pkg-json
  • npm run build

Additional real-theme validation:

  • Cloned Ollie using My Theme and verified that the generated PHP identifiers and namespaces are valid.
  • Syntax-checked the transformed Ollie PHP files.
  • Validated JavaScript identifiers and localized globals using a theme with prefixed JavaScript, including Extendable.

Manual testing instructions

  1. Install and activate the Ollie theme.
  2. Open Appearance -> Create Block Theme.
  3. Choose Clone Theme.
  4. Enter My Theme as the theme name.
  5. Clone and activate the generated theme.
  6. Confirm that activation does not produce a PHP parse error or critical error.
  7. Inspect the generated PHP and confirm that:
    • Namespaces use My_Theme.
    • Functions and variables use my_theme.
    • Text domains and other slug values use my-theme.
    • Human-readable references use My Theme.
  8. Repeat using a name beginning with a number, such as 2024 Clone, and confirm that generated identifiers receive a valid theme prefix.

Screenshots

Not applicable. This change affects generated source code rather than the cloning interface.

Addresses #777.

@thetwopct
thetwopct force-pushed the safer-identifiers-when-cloning-themes branch 2 times, most recently from 16b40cb to 0e6b352 Compare September 30, 2026 05:22
@thetwopct
thetwopct force-pushed the safer-identifiers-when-cloning-themes branch from 0e6b352 to f1f3460 Compare October 2, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improved namespace replacement logic when duplicating theme

1 participant