Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exploration: Execute PHP received as a message #1625

Closed
wants to merge 1 commit into from

Conversation

bgrgicak
Copy link
Collaborator

This PR is a exploration and shouldn't be merged as-is

Motivation for the change, related issues

In some cases, it's hard to load Playground using the JS API. For example when building Browser extensions with manifest v3.

To work around this it's possible to load the Playground website in an iframe.

<iframe src="playground.wordpress.net/?mode=seamless" ></iframe>

But this doesn't allow you to interact with Playground.

This PR adds support for sending PHP requests to the Playground iframe and receiving PHP responses.

Implementation details

The Playground website now listens for a php-request message and replies to that message with a php-response message.

Example:

// Listen for PHP responses
window.addEventListener('message', (event) => {
	if(event.data.type === 'php-response') {
		console.log('Message received', event.data.response);
	}
});

// Send PHP requests
const frame = document.getElementById('iframe-id');
frame.contentWindow.postMessage({ type: 'php-request', code: `<?php echo 'Hey Playground!';` }, '*');

Testing Instructions (or ideally a Blueprint)

  • checkout this branch locally
  • after Playground loads paste this code into the browser console
window.addEventListener('message', (event) => {
	if(event.data.type === 'php-response') {
		console.log('Message received', event.data.response);
	}
});
window.postMessage({ type: 'php-request', code: `<?php echo 'test';` }, '*');
  • You should see a test response in the console

@bgrgicak bgrgicak added [Aspect] Browser [Type] Exploration An exploration that may or may not result in mergable code [Aspect] Website Do not merge labels Jul 18, 2024
@bgrgicak
Copy link
Collaborator Author

@WordPress/playground-maintainers What do you think about us allowing running PHP from messages?

@akirk and I worked on a way to allow the Try WordPress extension to load Playground in an iframe without the JS API, but for the extension to still be able to execute PHP requests.

Alternatively, we could take a similar approach to WordPress/playground-tools#298, but this seemed complicated as it requires build tooling.

@bgrgicak bgrgicak requested a review from a team July 18, 2024 10:23
Comment on lines +161 to +165
window.parent.postMessage(
{
type: 'php-response',
response: response?.text,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful if the response included the request object, or some other way to match the response back up with the request. Since the request could be large, maybe we could denote a certain key that will be included in the reply?

Copy link
Collaborator Author

@bgrgicak bgrgicak Jul 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do something like this.

Suggested change
window.parent.postMessage(
{
type: 'php-response',
response: response?.text,
},
window.parent.postMessage(
{
type: 'php-response',
response: response?.text,
requestId: event.data.id
},

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good, we just need to omit this there is no id in the request.

*/
function addPhpRequestListener(playground: PlaygroundClient) {
window.addEventListener('message', async (event) => {
if (event.data.type === 'php-request') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we model this after the Blueprint API and call it runPHP, and ultimately, allow any/most blueprint steps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would model it based on the Playground API Client, because we started with the goal of allowing people do JS API interactions when the API isn't available.

If some features are missing, we could expand both.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's where we started. But thinking from my usecase, it would be very useful to be able to simply install a plugin. Is it feasible to add a command to execute a blueprint step or a big deal?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runBlueprintSteps(
	compileBlueprint(event.data.blueprint),
	playground
);

This code would run a blueprint.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that it wouldn't return an output.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely that's ok. For just most activities like creating a post, I don't really need to check the return code anyway.

@adamziel
Copy link
Collaborator

adamziel commented Jul 23, 2024

This PR, as it stands:

  • Doesn't tell the parent window whether Playground is loaded and ready to receive messages
  • Doesn't tell the parent window if Playground boot failed
  • Won't correctly serialize the PHP response object (Uint8Array, prototype methods)
  • Requires the parent window to implement a function like const response = await sendMessage() that will call postMessage(), monitor the incoming messages, correlate the sent message ID with the received one, and preferably use a timeout in case the response isn't coming

These, of course, can be solved by writing a few functions and handing them to the developer. We'd likely bundle them up as a package, host it somewhere, and make it easy to import – that's exactly what the JS API client is.

I understand the original JS API problem @akirk reported was solved by loading the API client this way:

import { startPlaygroundWeb, phpVars, setPluginProxyURL } from 'https://playground.wordpress.net/client/index.js';

I'd rather close this PR and focus on any remaining issues discouraging the developers from using the JS API.

@bgrgicak
Copy link
Collaborator Author

I'd rather close this PR and focus on any remaining issues discouraging the developers from using the JS API.

I agree. This feels like a complex workaround rather than a feature. Plus it would be another API to maintain.

@akirk
Copy link
Member

akirk commented Jul 23, 2024

@adamziel unfortunately this does not work, I think you might be mixing this up with another issue from Translate Live.

I've logged #1641 to detail the problem. I'd be happy to use the Playground Client API but it doesn't load WordPress in the iframe. It does work is to load https://playground.wordpress.net as the iframe src and then communicate it with postMessage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Aspect] Browser [Aspect] Website Do not merge [Type] Exploration An exploration that may or may not result in mergable code
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants