php-wasm
Version:
Run PHP right in the browser or anywhere else JS can run.
195 lines (153 loc) • 4.26 kB
JavaScript
import { PhpBase } from './PhpBase.mjs';
import PhpBinary from './php-web.mjs';
export class PhpWeb extends PhpBase
{
constructor(args = {})
{
super(PhpBinary, args);
}
run(phpCode)
{
return this.binary.then(php => {
const sync = !php.persist
? Promise.resolve()
: new Promise(accept => php.FS.syncfs(true, err => {
if(err) console.warn(err);
accept();
}));
const run = sync.then(() => super.run(phpCode));
if(!php.persist)
{
return run;
}
return run.then(() => new Promise(accept => php.FS.syncfs(false, err => {
if(err) console.warn(err);
accept(run);
})));
})
.finally(() => this.flush());
}
exec(phpCode)
{
return this.binary.then(php => {
const sync = new Promise(accept => php.FS.syncfs(true, err => {
if(err) console.warn(err);
accept();
}));
const run = sync.then(() =>super.exec(phpCode));
return run.then(() => new Promise(accept => php.FS.syncfs(false, err => {
if(err) console.warn(err);
accept(run);
})));
})
.finally(() => this.flush());
}
}
const runPhpScriptTag = element => {
const tags = {stdin: null, stdout: null, stderr: null};
if(element.hasAttribute('data-stdout'))
{
tags.stdout = document.querySelector(element.getAttribute('data-stdout'));
}
if(element.hasAttribute('data-stderr'))
{
tags.stderr = document.querySelector(element.getAttribute('data-stderr'));
}
if(element.hasAttribute('data-stdin'))
{
tags.stdin = document.querySelector(element.getAttribute('data-stdin'));
}
let stdout = '';
let stderr = '';
let ran = false;
let getCode = Promise.resolve(element.innerText);
if(element.hasAttribute('src'))
{
getCode = fetch(element.getAttribute('src')).then(response => response.text());
}
let getInput = Promise.resolve('');
if(tags.stdin)
{
getInput = Promise.resolve(tags.stdin.innerText);
if(tags.stdin.hasAttribute('src'))
{
getInput = fetch(tags.stdin.getAttribute('src')).then(response => response.text());
}
}
const getAll = Promise.all([getCode, getInput]);
getAll.then(([code, input,]) => {
const php = new PhpWeb;
php.inputString(input);
const outListener = event => {
stdout += event.detail;
if(ran && tags.stdout)
{
tags.stdout.innerHTML = stdout;
}
};
const errListener = event => {
stderr += event.detail;
if(ran && tags.stderr)
{
tags.stderr.innerHTML = stderr;
}
};
php.addEventListener('output', outListener);
php.addEventListener('error', errListener);
php.addEventListener('ready', () => {
php.run(code)
.then(exitCode => console.log(exitCode))
.catch(error => console.warn(error))
.finally(() => {
ran = true;
php.flush();
tags.stdout && (tags.stdout.innerHTML = stdout);
tags.stderr && (tags.stderr.innerHTML = stderr);
});
});
// const observer = new MutationObserver((mutations, observer) => {
// for(const mutation of mutations)
// {
// for(const addedNode of mutation.addedNodes)
// {
// console.log(addedNode);
// // php.inputString(addedNode);
// // php.run(code)
// // .then(exitCode => console.log(exitCode))
// // .catch(error => console.warn(error))
// // .finally(() => {
// // php.flush();
// // tags.stdout && (tags.stdout.innerHTML = stdout);
// // tags.stderr && (tags.stderr.innerHTML = stderr);
// // php.removeEventListener('output', outListener);
// // php.removeEventListener('error', errListener);
// // });
// }
// }
// });
// observer.observe(element, {childList: true, subtree: true});
});
}
const phpSelector = 'script[type="text/php"]';
export const runPhpTags = (doc) => {
const phpNodes = doc.querySelectorAll(phpSelector);
for(const phpNode of phpNodes)
{
const code = phpNode.innerText.trim();
runPhpScriptTag(phpNode);
}
const observer = new MutationObserver((mutations, observer) => {
for(const mutation of mutations)
{
for(const addedNode of mutation.addedNodes)
{
if(!addedNode.matches || !addedNode.matches(phpSelector))
{
continue;
}
runPhpScriptTag(addedNode);
}
}
});
observer.observe(document.body.parentElement, {childList: true, subtree: true});
}