mirror of
https://github.com/bdnugget/php-wasm.git
synced 2026-11-07 09:07:28 +00:00
559 lines
12 KiB
JavaScript
559 lines
12 KiB
JavaScript
"use strict";
|
|
|
|
import { PhpWebDrupal as PHP } from 'php-wasm/PhpWebDrupal';
|
|
|
|
window.PHP = PHP;
|
|
const php = new PHP;
|
|
|
|
let session_id = '';
|
|
|
|
const serviceWorker = navigator.serviceWorker;
|
|
|
|
if(serviceWorker)
|
|
{
|
|
serviceWorker.register(`${location.pathname}DrupalWorker.js`);
|
|
// .then(result => console.log('Result, ', result))
|
|
// .catch(error => console.log('Error, ', error));
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const input = document.querySelector('.input textarea');
|
|
const stdout = document.querySelector('.stdout > * > div.scroller');
|
|
const stderr = document.querySelector('.stderr > * > div.scroller');
|
|
const stdret = document.querySelector('.stdret > * > div.scroller');
|
|
const run = document.querySelector('[data-run]');
|
|
const token = document.querySelector('[data-tokenize]');
|
|
const status = document.querySelector('[data-status]');
|
|
const load = document.querySelector('[data-load-demo]')
|
|
const demo = document.querySelector('[data-select-demo]')
|
|
const editor = ace.edit(input);
|
|
const ret = document.querySelector('#ret');
|
|
|
|
const stdoutFrame = document.querySelector('.stdout > * > iframe');
|
|
const stderrFrame = document.querySelector('.stderr > * > iframe');
|
|
const stdretFrame = document.querySelector('.stdret > * > iframe');
|
|
const openFile = document.getElementById('openFile');
|
|
const exitBox = document.querySelector('#exit')
|
|
const exitLabel = exitBox.querySelector('span');
|
|
const persistBox = document.getElementById('persist');
|
|
const singleBox = document.getElementById('singleExpression');
|
|
const autorun = document.querySelector('#autorun');
|
|
|
|
const renderAs = Array.from(document.querySelectorAll('[name=render-as]'));
|
|
|
|
openFile.addEventListener('input', event =>{
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = (event) => {
|
|
editor.setValue(event.target.result);
|
|
};
|
|
|
|
reader.readAsText(event.target.files[0]);
|
|
|
|
});
|
|
|
|
const runCode = () => {
|
|
|
|
exitLabel.innerText = '_';
|
|
|
|
status.innerText = 'Executing...';
|
|
|
|
stdoutFrame.srcdoc = ' ';
|
|
stderrFrame.srcdoc = ' ';
|
|
stdretFrame.srcdoc = ' ';
|
|
|
|
while(stdout.firstChild)
|
|
{
|
|
stdout.firstChild.remove();
|
|
}
|
|
|
|
while(stderr.firstChild)
|
|
{
|
|
stderr.firstChild.remove();
|
|
}
|
|
|
|
while(stdret.firstChild)
|
|
{
|
|
stdret.firstChild.remove();
|
|
}
|
|
|
|
let code = editor.session.getValue();
|
|
|
|
if(code.length < 1024 * 2)
|
|
{
|
|
query.set('autorun', autorun.checked ? 1 : 0);
|
|
query.set('persist', persistBox.checked ? 1 : 0);
|
|
query.set('single-expression', singleBox.checked ? 1 : 0);
|
|
query.set('code', encodeURIComponent(code));
|
|
history.replaceState({}, document.title, "?" + query.toString());
|
|
}
|
|
|
|
const func = singleBox.checked
|
|
? 'exec'
|
|
: 'run';
|
|
|
|
if(singleBox.checked)
|
|
{
|
|
code = code.replace(/^\s*<\?php/, '');
|
|
code = code.replace(/\?>\s*/, '');
|
|
}
|
|
|
|
php[func](code).then(ret=>{
|
|
|
|
status.innerText = 'php-wasm ready!';
|
|
|
|
const content = String(ret);
|
|
|
|
stdret.innerText = content;
|
|
stdretFrame.srcdoc = content;
|
|
|
|
exitLabel.innerText = '_';
|
|
|
|
if(!singleBox.checked)
|
|
{
|
|
setTimeout(() => exitLabel.innerText = ret, 100);
|
|
}
|
|
}).finally(() => {
|
|
if(!persistBox.checked)
|
|
{
|
|
php.refresh();
|
|
}
|
|
});
|
|
};
|
|
|
|
load.addEventListener('click', event => {
|
|
if(!demo.value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
let scriptPath = '/php-wasm/scripts';
|
|
|
|
if(window.location.hostname === 'localhost')
|
|
{
|
|
scriptPath = '/scripts';
|
|
}
|
|
|
|
fetch(`${scriptPath}/${demo.value}`)
|
|
.then(r => r.text())
|
|
.then(php => {
|
|
|
|
const firstLine = String(php.split(/\n/).shift());
|
|
const settings = JSON.parse(firstLine.split('//').pop());
|
|
|
|
if('autorun' in settings)
|
|
{
|
|
autorun.checked = !!settings.autorun;
|
|
}
|
|
|
|
if('single-expression' in settings)
|
|
{
|
|
singleBox.checked = !!settings['single-expression'];
|
|
}
|
|
|
|
if('persist' in settings)
|
|
{
|
|
persistBox.checked = !!settings.persist;
|
|
}
|
|
|
|
if('render-as' in settings)
|
|
{
|
|
if(settings['render-as'] === 'text')
|
|
{
|
|
renderAs[0].checked = true;
|
|
|
|
renderAs[0].dispatchEvent(new Event('change'));
|
|
|
|
query.set('render-as', 'text');
|
|
}
|
|
else if(settings['render-as'] === 'html')
|
|
{
|
|
renderAs[1].checked = true;
|
|
|
|
renderAs[1].dispatchEvent(new Event('change'));
|
|
|
|
query.set('render-as', 'html');
|
|
}
|
|
}
|
|
|
|
persistBox.dispatchEvent(new Event('change'));
|
|
singleBox.dispatchEvent(new Event('input'));
|
|
autorun.dispatchEvent(new Event('change'));
|
|
|
|
history.replaceState({}, document.title, "?" + query.toString());
|
|
|
|
editor.getSession().setValue(php);
|
|
|
|
setTimeout(() => runCode(), 100);
|
|
});
|
|
});
|
|
|
|
const query = new URLSearchParams(location.search);
|
|
|
|
editor.setTheme('ace/theme/monokai');
|
|
editor.session.setMode("ace/mode/php");
|
|
|
|
status.innerText = 'php-wasm loading...';
|
|
|
|
const navigate = ({path, method, _GET, _POST}) => {
|
|
|
|
// console.trace({path, method, _GET, _POST});
|
|
|
|
exitLabel.innerText = '_';
|
|
|
|
status.innerText = 'Executing...';
|
|
|
|
stdoutFrame.srcdoc = ' ';
|
|
stderrFrame.srcdoc = ' ';
|
|
stdretFrame.srcdoc = ' ';
|
|
|
|
while(stdout.firstChild)
|
|
{
|
|
stdout.firstChild.remove();
|
|
}
|
|
|
|
while(stderr.firstChild)
|
|
{
|
|
stderr.firstChild.remove();
|
|
}
|
|
|
|
while(stdret.firstChild)
|
|
{
|
|
stdret.firstChild.remove();
|
|
}
|
|
|
|
const code = `<?php
|
|
ini_set('session.save_path', '/home/web_user');
|
|
|
|
$stdErr = fopen('php://stderr', 'w');
|
|
$errors = [];
|
|
|
|
fwrite($stdErr, isset($_SESSION) && json_encode(['session' => $_SESSION]) . "\n");
|
|
|
|
register_shutdown_function(function() use($stdErr){
|
|
fwrite($stdErr, json_encode(['session_id' => session_id()]) . "\n");
|
|
fwrite($stdErr, json_encode(['headers'=>headers_list()]) . "\n");
|
|
fwrite($stdErr, json_encode(['errors' => error_get_last()]) . "\n");
|
|
fwrite($stdErr, json_encode(['session' => $_SESSION]) . "\n");
|
|
});
|
|
|
|
set_error_handler(function(...$args) use($stdErr, &$errors){
|
|
fwrite($stdErr, json_encode($args, JSON_PRETTY_PRINT) . "\n" );
|
|
});
|
|
|
|
$request = (object) json_decode(
|
|
'${ JSON.stringify({path, method, _GET, _POST}) }'
|
|
, JSON_OBJECT_AS_ARRAY
|
|
);
|
|
|
|
parse_str(substr($request->_GET, 1), $_GET);
|
|
|
|
$_POST = $request->_POST;
|
|
|
|
$origin = 'http://localhost:3333';
|
|
$docroot = '/preload/drupal-7.59';
|
|
$script = 'index.php';
|
|
|
|
$path = $request->path;
|
|
$path = preg_replace('/^\\/php-wasm/', '', $path);
|
|
|
|
$_SERVER['SERVER_SOFTWARE'] = ${JSON.stringify(navigator.userAgent)};
|
|
$_SERVER['REQUEST_URI'] = $path;
|
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
|
$_SERVER['SERVER_NAME'] = $origin;
|
|
$_SERVER['SERVER_PORT'] = 3333;
|
|
$_SERVER['REQUEST_METHOD'] = $request->method;
|
|
$_SERVER['SCRIPT_FILENAME'] = $docroot . '/' . $script;
|
|
$_SERVER['SCRIPT_NAME'] = $docroot . '/' . $script;
|
|
$_SERVER['PHP_SELF'] = $docroot . '/' . $script;
|
|
$_SERVER['DOCUMENT_ROOT'] = '/';
|
|
$_SERVER['HTTPS'] = '';
|
|
|
|
chdir($docroot);
|
|
|
|
define('DRUPAL_ROOT', getcwd());
|
|
|
|
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
|
|
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
|
|
|
|
$uid = 1;
|
|
$user = user_load($uid);
|
|
$account = array('uid' => $user->uid);
|
|
user_login_submit(array(), $account);
|
|
|
|
$itemPath = $path;
|
|
$itemPath = preg_replace('/^\\/preload/', '', $itemPath);
|
|
$itemPath = preg_replace('/^\\/drupal-7.59/', '', $itemPath);
|
|
$itemPath = preg_replace('/^\\//', '', $itemPath);
|
|
|
|
if($itemPath && (substr($itemPath, 0, 4) !== 'node' || substr($itemPath, -4) === 'edit'))
|
|
{
|
|
$router_item = menu_get_item($itemPath);
|
|
$router_item['access_callback'] = true;
|
|
$router_item['access'] = true;
|
|
|
|
if ($router_item['include_file']) {
|
|
require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
|
|
}
|
|
|
|
$page_callback_result = call_user_func_array(
|
|
$router_item['page_callback']
|
|
, is_string($router_item['page_arguments'])
|
|
? unserialize($router_item['page_arguments'])
|
|
: $router_item['page_arguments']
|
|
);
|
|
|
|
drupal_deliver_page($page_callback_result);
|
|
}
|
|
else
|
|
{
|
|
menu_execute_active_handler();
|
|
}`;
|
|
|
|
php.run(code).then(exitCode => {
|
|
exitLabel.innerText = exitCode;
|
|
status.innerText = 'php-wasm ready!';
|
|
}).finally(
|
|
() => {
|
|
if(!persistBox.checked)
|
|
{
|
|
php.refresh();
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
php.addEventListener('ready', event => {
|
|
|
|
if(serviceWorker)
|
|
{
|
|
serviceWorker.addEventListener('message', event => navigate(event.data));
|
|
}
|
|
|
|
status.innerText = 'php-wasm ready!';
|
|
|
|
run.removeAttribute('disabled');
|
|
|
|
token && token.addEventListener('click', () => {
|
|
|
|
const url = '/drupal-7.59/install.php';
|
|
const options = {method: 'GET'};
|
|
|
|
fetch(url, options).then(r=> r.text()).then(r => {
|
|
console.log('Done');
|
|
});
|
|
|
|
});
|
|
|
|
run.addEventListener('click', runCode);
|
|
|
|
if(query.get('autorun'))
|
|
{
|
|
runCode();
|
|
}
|
|
});
|
|
|
|
const outputBuffer = [];
|
|
|
|
php.addEventListener('output', (event) => {
|
|
const row = document.createElement('div');
|
|
const content = event.detail.join('');
|
|
|
|
outputBuffer.push(content);
|
|
|
|
setTimeout(()=>{
|
|
let chunk = outputBuffer.join('');
|
|
|
|
if(!outputBuffer || !chunk)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(location.hostname.match(/github.io$/))
|
|
{
|
|
chunk = chunk.replace(/\/preload/g, '/php-wasm/preload');
|
|
}
|
|
|
|
const node = document.createTextNode(chunk);
|
|
|
|
stdout.append(node);
|
|
stdoutFrame.srcdoc += chunk;
|
|
|
|
while(outputBuffer.pop()){};
|
|
}, 500);
|
|
|
|
});
|
|
|
|
const errorBuffer = [];
|
|
|
|
php.addEventListener('error', (event) => {
|
|
const content = event.detail.join(" ");
|
|
try{
|
|
const headers = JSON.parse(content);
|
|
|
|
if(headers.session_id)
|
|
{
|
|
session_id = headers.session_id;
|
|
}
|
|
|
|
if(headers.headers)
|
|
{
|
|
for(const header of headers.headers)
|
|
{
|
|
const splitAt = header.indexOf(':')
|
|
const [name, value] = [
|
|
header.substring(0,splitAt)
|
|
, header.substring(splitAt + 2)
|
|
];
|
|
|
|
if(name === 'Location')
|
|
{
|
|
console.log(value);
|
|
|
|
const redirectUrl = new URL(value);
|
|
|
|
setTimeout(()=>navigate({
|
|
method: 'GET'
|
|
, path: redirectUrl.pathname
|
|
, _GET:''
|
|
, _POST:{}
|
|
}), 2000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(error)
|
|
{
|
|
}
|
|
|
|
for(const line of content)
|
|
{
|
|
errorBuffer.push(line);
|
|
|
|
setTimeout(()=>{
|
|
const chunk = errorBuffer.join('');
|
|
|
|
if(!errorBuffer || !chunk)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const node = document.createTextNode(chunk);
|
|
|
|
stderr.append(node);
|
|
stderrFrame.srcdoc += chunk;
|
|
|
|
while(errorBuffer.pop()){};
|
|
}, 500);
|
|
}
|
|
|
|
});
|
|
|
|
ret.style.display = 'none';
|
|
|
|
singleBox.addEventListener('input', event=>{
|
|
if(event.target.checked)
|
|
{
|
|
exitBox.style.display = 'none';
|
|
ret.style.display = 'flex';
|
|
}
|
|
else
|
|
{
|
|
exitBox.style.display = 'flex';
|
|
ret.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
exitLabel.innerText = '_';
|
|
|
|
if(query.has('code'))
|
|
{
|
|
editor.setValue(decodeURIComponent(query.get('code')));
|
|
}
|
|
|
|
if(query.has('render-as'))
|
|
{
|
|
document.querySelector(`[name=render-as][value=${query.get('render-as')}]`).checked = true;
|
|
}
|
|
|
|
autorun.checked = Number(query.get('autorun'));
|
|
persistBox.checked = Number(query.get('persist'));
|
|
singleBox.checked = Number(query.get('single-expression'));
|
|
|
|
if(singleBox.checked)
|
|
{
|
|
exitBox.style.display = 'none';
|
|
ret.style.display = 'flex';
|
|
}
|
|
else
|
|
{
|
|
exitBox.style.display = 'flex';
|
|
ret.style.display = 'none';
|
|
}
|
|
|
|
setTimeout(() => editor.selection.moveCursorFileStart(), 150);
|
|
|
|
renderAs.map(radio => {
|
|
|
|
if(query.get('render-as') === 'html')
|
|
{
|
|
|
|
stdout.style.display = 'none';
|
|
stdoutFrame.style.display = 'flex';
|
|
|
|
stderr.style.display = 'none';
|
|
stderrFrame.style.display = 'flex';
|
|
|
|
stdret.style.display = 'none';
|
|
stdretFrame.style.display = 'flex';
|
|
}
|
|
else
|
|
{
|
|
stdout.style.display = 'flex';
|
|
stdoutFrame.style.display = 'none';
|
|
|
|
stderr.style.display = 'flex';
|
|
stderrFrame.style.display = 'none';
|
|
|
|
stdret.style.display = 'flex';
|
|
stdretFrame.style.display = 'none';
|
|
}
|
|
|
|
radio.addEventListener('change', event => {
|
|
|
|
const type = event.target.value;
|
|
|
|
query.set('render-as', type);
|
|
history.replaceState({}, document.title, "?" + query.toString());
|
|
|
|
if(type === 'html')
|
|
{
|
|
|
|
stdout.style.display = 'none';
|
|
stdoutFrame.style.display = 'flex';
|
|
|
|
stderr.style.display = 'none';
|
|
stderrFrame.style.display = 'flex';
|
|
|
|
stdret.style.display = 'none';
|
|
stdretFrame.style.display = 'flex';
|
|
}
|
|
else
|
|
{
|
|
stdout.style.display = 'flex';
|
|
stdoutFrame.style.display = 'none';
|
|
|
|
stderr.style.display = 'flex';
|
|
stderrFrame.style.display = 'none';
|
|
|
|
stdret.style.display = 'flex';
|
|
stdretFrame.style.display = 'none';
|
|
}
|
|
|
|
});
|
|
});
|
|
|
|
});
|
|
|