Skip to content

Commit 3157080

Browse files
authored
[PHP Playground] Select WordPress version (#2472)
## Motivation for the change, related issues Adds a WordPress version selector to the PHP Playground <img width="2038" height="820" alt="CleanShot 2025-08-07 at 13 15 03@2x" src="https://github.com/user-attachments/assets/bac73159-2ef9-408b-93e1-3fc462a71664" /> ## Testing instructions Wait for the next deployment – this relies on a new `MinifiedWordPressVersionsList` export from the `@wp-playground/client` cc @juanmaguitar
1 parent 1d9cccc commit 3157080

File tree

1 file changed

+125
-48
lines changed

1 file changed

+125
-48
lines changed

packages/playground/website/public/php-playground.html

Lines changed: 125 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@
414414
<div class="controls">
415415
<h1>WordPress PHP Playground</h1>
416416
<div class="controls-right">
417+
<label for="wpVersion">WordPress</label>
418+
<select id="wpVersion">
419+
<option>Loading...</option>
420+
</select>
417421
<label for="phpVersion">PHP</label>
418422
<select id="phpVersion">
419423
<option value="8.4" selected>8.4</option>
@@ -478,7 +482,8 @@ <h2>💡 Tips</h2>
478482
<strong>🔗 Embed in your app:</strong> You can embed
479483
this playground in an iframe to provide interactive
480484
PHP code snippets. Provide your base64-encoded code
481-
snippet and your PHP version in the URL fragment:
485+
snippet, PHP version, and WordPress version in the
486+
URL fragment:
482487
<pre
483488
style="
484489
background: #f5f5f5;
@@ -512,7 +517,7 @@ <h2>💡 Tips</h2>
512517
</div>
513518

514519
<script>
515-
// Function to restore code and PHP version from URL fragment
520+
// Function to restore code, PHP version, and WordPress version from URL fragment
516521
function loadStateFromURL() {
517522
const fragment = window.location.hash.slice(1); // Remove #
518523
if (fragment) {
@@ -523,31 +528,33 @@ <h2>💡 Tips</h2>
523528
return {
524529
code: state.code || null,
525530
phpVersion: state.php || null,
531+
wpVersion: state.wp || null,
526532
};
527533
} catch (e) {
528534
// Try legacy format (just code)
529535
return {
530536
code: decoded,
531537
phpVersion: null,
538+
wpVersion: null,
532539
};
533540
}
534541
}
535542
}
536-
return { code: null, phpVersion: null };
543+
return { code: null, phpVersion: null, wpVersion: null };
537544
}
538545

539546
/**
540547
* URL Fragment State Storage
541548
*
542-
* This implementation stores and restores PHP code and PHP version from the URL fragment using base64 encoding.
549+
* This implementation stores and restores PHP code, PHP version, and WordPress version from the URL fragment using base64 encoding.
543550
* Features:
544551
* - UTF-8 safe encoding/decoding
545-
* - Stores both code and PHP version
552+
* - Stores code, PHP version, and WordPress version
546553
* - Automatic saving on code changes (debounced to 500ms)
547-
* - Automatic saving when PHP version changes
554+
* - Automatic saving when PHP or WordPress version changes
548555
* - Immediate saving when running code (Cmd+S)
549556
* - Browser navigation support (back/forward buttons)
550-
* - Shareable URLs with embedded code and PHP version
557+
* - Shareable URLs with embedded code and versions
551558
* - Backward compatibility with legacy code-only URLs
552559
*/
553560

@@ -589,25 +596,33 @@ <h2>💡 Tips</h2>
589596
}
590597
}
591598

592-
// Function to save code and PHP version to URL fragment
593-
function saveStateToURL(code, phpVersion) {
599+
// Function to save code, PHP version, and WordPress version to URL fragment
600+
function saveStateToURL(code, phpVersion, wpVersion) {
594601
const state = JSON.stringify({
595602
code: code,
596603
php: phpVersion,
604+
wp: wpVersion,
597605
});
598606
const encoded = encodeBase64UTF8(state);
599607
// Use replaceState to avoid adding to browser history
600608
window.history.replaceState(null, null, '#' + encoded);
601609
}
602610

603-
var versionSelect = document.getElementById('phpVersion');
611+
var phpVersionSelect = document.getElementById('phpVersion');
612+
var wpVersionSelect = document.getElementById('wpVersion');
604613

605-
function restorePHPVersionFromURL() {
614+
function restoreVersionsFromURL() {
606615
var defaultPhpVersion = '8.4';
607-
const { phpVersion: initialPhpVersion } = loadStateFromURL();
608-
versionSelect.value = initialPhpVersion || defaultPhpVersion;
616+
var defaultWpVersion = '6.8'; // Will be updated with actual latest version
617+
const {
618+
phpVersion: initialPhpVersion,
619+
wpVersion: initialWpVersion,
620+
} = loadStateFromURL();
621+
phpVersionSelect.value = initialPhpVersion || defaultPhpVersion;
622+
if (initialWpVersion && wpVersionSelect.options.length > 1) {
623+
wpVersionSelect.value = initialWpVersion;
624+
}
609625
}
610-
restorePHPVersionFromURL();
611626

612627
// Modal functionality
613628
const helpBtn = document.getElementById('helpBtn');
@@ -692,26 +707,32 @@ <h2>💡 Tips</h2>
692707
import {
693708
startPlaygroundWeb,
694709
SupportedPHPVersionsList,
695-
} from 'https://esm.sh/@wp-playground/client';
710+
} from 'https://playground.wordpress.net/client/index.js';
696711

697712
const editorEl = document.getElementById('editor');
698713
const phpCompartment = new Compartment();
699714

700715
const defaultPhpVersion = '8.4';
701-
const { phpVersion: initialPhpVersion } = loadStateFromURL();
716+
const {
717+
phpVersion: initialPhpVersion,
718+
wpVersion: initialWpVersion,
719+
} = loadStateFromURL();
702720
const startingPhpVersion = initialPhpVersion || defaultPhpVersion;
703-
// Populate PHP version select options from SupportedPHPVersionsList
704-
versionSelect.innerHTML = ''; // Clear existing options
721+
const startingWpVersion = initialWpVersion || 'latest';
705722

723+
// Populate PHP version select options from SupportedPHPVersionsList
724+
phpVersionSelect.innerHTML = ''; // Clear existing options
706725
SupportedPHPVersionsList.forEach((version) => {
707726
const option = document.createElement('option');
708727
option.value = version;
709728
option.textContent = version;
710-
versionSelect.appendChild(option);
729+
phpVersionSelect.appendChild(option);
711730
});
712731

713-
// Set the first version as selected by default
714-
restorePHPVersionFromURL();
732+
// WordPress versions will be populated after the client connects
733+
734+
// Set the versions from URL or defaults
735+
restoreVersionsFromURL();
715736

716737
const defaultCode = `<?php
717738
echo "Hello from PHP " . phpversion();
@@ -720,6 +741,8 @@ <h2>💡 Tips</h2>
720741
// WordPress is available if you need it!
721742
require '/wordpress/wp-load.php';
722743
744+
echo "WordPress " . wp_get_wp_version() . "<br>";
745+
723746
$html_processor = WP_HTML_Processor::create_fragment('<p><span>Hey!</span></p>');
724747
$html_processor->next_tag();
725748
var_dump($html_processor->get_tag());
@@ -761,12 +784,12 @@ <h2>💡 Tips</h2>
761784
EditorView.updateListener.of((update) => {
762785
if (update.docChanged) {
763786
const code = update.state.doc.toString();
764-
const phpVersion =
765-
document.getElementById('phpVersion').value;
787+
const phpVersion = phpVersionSelect.value;
788+
const wpVersion = wpVersionSelect.value;
766789
// Debounce URL updates to avoid excessive history updates
767790
clearTimeout(window.saveCodeTimeout);
768791
window.saveCodeTimeout = setTimeout(() => {
769-
saveStateToURL(code, phpVersion);
792+
saveStateToURL(code, phpVersion, wpVersion);
770793
}, 500);
771794
}
772795
}),
@@ -797,56 +820,95 @@ <h2>💡 Tips</h2>
797820
const runBtn = document.getElementById('runBtn');
798821
const previewIframe = document.getElementById('preview');
799822

800-
// Set initial PHP version from URL
823+
// Set initial versions from URL
801824
if (startingPhpVersion) {
802-
versionSelect.value = startingPhpVersion;
825+
phpVersionSelect.value = startingPhpVersion;
826+
}
827+
if (startingWpVersion && wpVersionSelect.options.length > 1) {
828+
wpVersionSelect.value = startingWpVersion;
803829
}
804830

805831
let client = null;
806-
let currentPhpVersion = versionSelect.value;
832+
let currentPhpVersion = phpVersionSelect.value;
833+
let currentWpVersion = wpVersionSelect.value;
807834

808835
// Detect platform for keyboard shortcut display
809836
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
810837
const shortcutText = isMac ? 'Cmd+S' : 'Ctrl+S';
811838
runBtn.textContent = `Run (${shortcutText})`;
812839

813840
// Save to URL when PHP version changes
814-
versionSelect.addEventListener('change', () => {
841+
phpVersionSelect.addEventListener('change', () => {
815842
const code = view.state.doc.toString();
816-
const phpVersion = versionSelect.value;
817-
saveStateToURL(code, phpVersion);
843+
const phpVersion = phpVersionSelect.value;
844+
const wpVersion = wpVersionSelect.value;
845+
saveStateToURL(code, phpVersion, wpVersion);
818846
});
819847

820-
async function bootPlayground(version) {
821-
// Point the iframe at the appropriate PHP version using the Query API
822-
previewIframe.src = `https://playground.wordpress.net/remote.html?php=${encodeURIComponent(
823-
version
824-
)}`;
848+
// Save to URL when WordPress version changes
849+
wpVersionSelect.addEventListener('change', () => {
850+
const code = view.state.doc.toString();
851+
const phpVersion = phpVersionSelect.value;
852+
const wpVersion = wpVersionSelect.value;
853+
saveStateToURL(code, phpVersion, wpVersion);
854+
});
855+
856+
async function bootPlayground(phpVersion, wpVersion) {
857+
// Point the iframe at the appropriate PHP and WordPress versions using the Query API
858+
previewIframe.src = `https://playground.wordpress.net/remote.html`;
825859
client = await startPlaygroundWeb({
826860
iframe: previewIframe,
827861
remoteUrl: previewIframe.src,
828862
blueprint: {
829863
preferredVersions: {
830-
wp: 'latest',
831-
php: version,
864+
wp: wpVersion,
865+
php: phpVersion,
832866
},
833867
},
834868
});
869+
870+
// Populate WordPress versions from the connected client
871+
try {
872+
const { all, latest } =
873+
await client.getMinifiedWordPressVersions();
874+
const keys = Object.keys(all);
875+
wpVersionSelect.innerHTML = '';
876+
for (const version of keys) {
877+
const option = document.createElement('option');
878+
option.value = version;
879+
option.textContent = version;
880+
wpVersionSelect.appendChild(option);
881+
}
882+
// Restore selection if still available; otherwise fall back to latest
883+
wpVersionSelect.value = keys.includes(startingWpVersion)
884+
? startingWpVersion
885+
: latest;
886+
} catch (e) {
887+
console.warn(
888+
'Failed to load WordPress versions list from client',
889+
e
890+
);
891+
}
835892
await client.isReady;
836893
await client.writeFile('/wordpress/code.php', startingCode);
837894
await client.goTo('/code.php'); // Blank document
838895
}
839896

840897
async function executeCode() {
841898
const code = view.state.doc.toString();
842-
const phpVersion = versionSelect.value;
899+
const phpVersion = phpVersionSelect.value;
900+
const wpVersion = wpVersionSelect.value;
843901

844-
// Save code and PHP version to URL fragment immediately when running
845-
saveStateToURL(code, phpVersion);
902+
// Save code, PHP version, and WordPress version to URL fragment immediately when running
903+
saveStateToURL(code, phpVersion, wpVersion);
846904

847-
if (phpVersion !== currentPhpVersion) {
905+
if (
906+
phpVersion !== currentPhpVersion ||
907+
wpVersion !== currentWpVersion
908+
) {
848909
currentPhpVersion = phpVersion;
849-
await bootPlayground(currentPhpVersion);
910+
currentWpVersion = wpVersion;
911+
await bootPlayground(currentPhpVersion, currentWpVersion);
850912
}
851913
await client.writeFile('/wordpress/code.php', code);
852914
const uuid = crypto.randomUUID();
@@ -857,8 +919,11 @@ <h2>💡 Tips</h2>
857919

858920
// Handle browser navigation (back/forward) to restore code from URL
859921
window.addEventListener('hashchange', () => {
860-
const { code: codeFromURL, phpVersion: phpVersionFromURL } =
861-
loadStateFromURL();
922+
const {
923+
code: codeFromURL,
924+
phpVersion: phpVersionFromURL,
925+
wpVersion: wpVersionFromURL,
926+
} = loadStateFromURL();
862927

863928
if (codeFromURL !== null) {
864929
const currentCode = view.state.doc.toString();
@@ -876,20 +941,32 @@ <h2>💡 Tips</h2>
876941

877942
if (
878943
phpVersionFromURL !== null &&
879-
phpVersionFromURL !== versionSelect.value
944+
phpVersionFromURL !== phpVersionSelect.value
880945
) {
881946
// Update PHP version selector
882-
versionSelect.value = phpVersionFromURL;
947+
phpVersionSelect.value = phpVersionFromURL;
948+
}
949+
950+
if (
951+
wpVersionFromURL !== null &&
952+
wpVersionFromURL !== wpVersionSelect.value
953+
) {
954+
// Update WordPress version selector
955+
wpVersionSelect.value = wpVersionFromURL;
883956
}
884957
});
885958

886959
// Save initial state to URL if not already present
887960
if (!window.location.hash) {
888-
saveStateToURL(startingCode, startingPhpVersion);
961+
saveStateToURL(
962+
startingCode,
963+
startingPhpVersion,
964+
startingWpVersion
965+
);
889966
}
890967

891968
// Initial boot
892-
bootPlayground(currentPhpVersion);
969+
bootPlayground(currentPhpVersion, currentWpVersion);
893970
</script>
894971
</body>
895972
</html>

0 commit comments

Comments
 (0)