在 PHP 中执行 javascript

IT技术 php javascript html
2021-01-16 02:51:50

我正在用 PHP 生成典型的 Web 2.0 HTML 页面:它包含大量<script>标记和 javascript 代码,它们将在加载事件后显着更改 DOM。

有没有办法直接从 PHP 获取最终的 HTML 代码,而无需使用任何浏览器打开页面?

例如,假设页面的 HTML 是(这只是一个示例):

<html>
<head>
<script>...the jquery library code...</script>
<script>$(document).ready(function() { $("body").append("<p>Hi!</p>");</script>
</head>
<body>
</body>
</html>

此 HTML 保存在$htmlPHP 变量中。现在,我想将该变量传递给某个将返回 $result = 的函数<html>....<body><p>Hi!</p></body></html>

这可能吗?

编辑:由于你们中的许多人对我的要求感到困惑,我将解释原因。不幸的是,用户面对的一切都是用 javascript 制作的,这使得搜索引擎无法抓取网站。所以我想向他们发送后期准备好的事件 HTML 代码。

6个回答

使用 PHP 评估 JavaScript 代码,请查看V8 JavaScript 引擎扩展,您可以将其编译为 PHP 二进制文件:

V8 是Google 的开源 JavaScript 实现

我能找到的最佳解决方案是在服务器上使用 HtmlUnit http://htmlunit.sourceforge.net/用 javascript 执行您的 html 并取回用户将在浏览器上看到的最终 html。

该库对 JavaScript 有很好的支持,并且是无头的,因此您应该能够在服务器上运行它。

您需要编写一个小型 Java 包装器,它可以通过命令行接受输入并将其传递给 HtmlUnit 进行处理,然后将结果返回给您。然后您可以从 PHP 调用这个包装器。

你有两个问题:

  1. 执行 JavaScript。
  2. 更新 DOM(执行 javascript 后的 html)。

要执行 javascript,您需要一个 javascript 引擎。目前有 3 种可供您使用:

  1. V8:由谷歌为 Chrome。PHP扩展。
  2. Rhino:由 Mozilla 用于 Firefox。仅在 JAVA 中可用。
  3. JavaScriptCore:由 Apple 用于 Safari。仅在 C 中可用。

一旦你有了一个 javascript 引擎,你就需要管理 DOM(文档对象模型)。这允许您将 HTML 解析为 DOM 节点、文本节点、元素等对象。最重要的是,您需要将 DOM 与 javascript 引擎同步并在 javascript 引擎中安装 DOM 库。虽然可能有多种方法可以做到这一点,但我更喜欢简单地将独立的 JavaScript DOM 包含/评估到引擎中,然后简单地将 HTML 传递给它。

  1. Env-JS JavaScript DOM 库。兼容原型/jQuery。
  2. jsdom用于NodeJS 的JavaScript DOM 库。

现在您拥有 JavaScript 引擎和 DOM 库,您现在可以毫无问题地评估大多数脚本。

最佳答案

NodeJS 是一个独立的可执行文件,具有 javascript 引擎和 DOM 操作合二为一。除此之外,您还可以将其用作 Web 服务器。也许这是您问题的更好解决方案,但是如果必须使用 PHP,请坚持上面提到的内容。

这个问题和how to execute javascript in javascript, or php in php 非常相似,答案是你可以eval它。如果 php 可以 eval javascript 并且 javascript 可以 eval php,我们就不会有这个讨论。

为了让 JavaScript 评估 PHP,它必须将 PHP 代码解析为表示脚本的结构。JavaScript 可以使用 JavaScript 对象表示法(不是 JSON 格式,而是实际表示)轻松完成此操作,并在功能上分解脚本。

这是 JavaScript 解释 PHP 的一个天真的例子(一个更诚实的例子不会那么做作,而是将 php 解析成它自己的类似 JSON 的表示或可能的字节码,然后在 php 的 javascript 模拟上解释这个类似 json 的表示或字节码虚拟机,但仍然如此):

(() => {
    'use strict';

    var phpSnippet = 'echo "Hi";';    

    var partialPHPEval = (phpCode) => {
        var regex = /echo[\s]["]([^"]*)["][;]/mg;
        var match = null;
        phpCode = phpCode.trim();

        if ((match = phpCode.match(regex))) {
            var code = (match[0].replace(regex, "(console.log('$1'))"));
            console.log('converted to "' + code + '"');

            eval(code);
        }        
    };

    partialPHPEval(phpSnippet);  
})();

问题是 PHP 不是 javascript,而且它的 eval 比 javascript 弱得多。

这会产生一个问题,即 php 可以轻松地发出将 javascript 标记为 PHP 的请求:JavaScript 可以轻松创建任何内容的“JSONified”版本(只要它不是本机的),因此您可以让 PHP 向 nodejs 服务器发送请求使用您要评估的脚本。

例如:(PHP代码)

include "some_file_defining_jsEval.php";

$wantedObject = function($a) {
    return $a;
};

$resultingObject = jsEval(
    '(function(a) {' .
    '    return a;' .
    '})'
);

echo $resultingObject("Hello, World!");

JavaScript 可以通过执行以下操作轻松将其评估为“函数对象”:

var functionObject = eval(
    '(function(a) {' +
    '    return a;' +
    '})'
);

console.log('your code is: ' + '(' + functionObject.toString() + ')');

如您所见,js 可以轻松地将其解析为一个对象,然后再解析为一个字符串,有一个小麻烦,即必须添加 '(' 和 ')' 以使其 eval() 不会导致错误“Uncaught SyntaxError : 意外的标记 (”。

无论如何,类似的事情可以在 PHP 中完成:

<?php

$functionObject = eval(
    'return function($a) {' .
    '    return $a;' .
    '};'
);

echo $functionObject("hi");
?>

知道了这一点,您必须让 JavaScript 将 JavaScript 函数对象转换为 PHP 函数对象,或者走简单的翻译路线。

问题在于 JavaScript(ES6) 比 PHP 更具表现力(5.6、7 可能更好,但如果没有 service pack 1 windows 7,它就无法运行,所以我无法在这台计算机上运行它)。这反过来意味着 JavaScript 有很多功能,而 PHP 没有,例如:

(function() {
    console.log("Hello World");
})();

不适用于 PHP 5.6,因为它不支持自执行函数。这意味着您需要做更多的工作才能将其翻译成:

call_user_func(function() {
    echo "hello, world!" . "\n";
});

还有一些问题是,PHP 并没有像 javascript 那样真正使用原型,因此很难对其进行翻译。

无论如何,最终 php 和 javascript 非常相似,以至于您基本上可以在另一个中使用一个例外。

例如:(PHP)

/* 就我所知,不能描述为函数,因为不是原型 */ class console { static function log($text) { echo $text . "\n"; } };

call_user_func(function() { $myScopeVariable = "嘿,这不是 JavaScript!"; console::log($myScopeVariable); });

例如 JavaScript:

/* javascript requires brackets because semicolons are not mandatory */
var almost_echo = (console.log.bind(console));

结论

您可以在 PHP 和 JavaScript 之间进行转换,但将 PHP 转换为 JavaScript 比将 JavaScript 转换为 PHP 容易得多,因为 JavaScript 本身更具表现力,而 PHP 必须创建类来表示许多 JavaScript 结构(有趣的是,php 可以预处理 php 以修复所有这些问题)。

幸运的是,PHP 现在可以原生理解 JSON,因此在 javascript 评估自身之后,javascript 可以读取结果结构(JavaScript 中的大多数内容是对象或函数),包括源代码,并将这些对象放入 JSON 编码形式。之后,您可以让 PHP 解析 JSON 以通过中性形式恢复代码)。

例如

php: jsEval('function(a){return a;}');
js: [{"type":"function", "name": "foo", "args":["a"], body: "return a"}]
php: oh, i get it, you mean 
function foo($a) { return $a; } 

本质上,可以说是通过“通用 LISP”进行通信。当然,这将是非常昂贵的并且不是原生的,但是演示一个示例很好。理想情况下,我们将拥有一个封装各种脚本的本机module,它可以轻松地将 ruby​​ 转换为 php,将 perl 转换为 python 到 javascript,然后将结果编译为 c 以实现它)。javascript 通过能够评估自己以及打印自己的代码来帮助接近这一点。如果所有语言都可以做这两件事,那么实现起来会容易得多,但遗憾的是 javascript 只是“几乎在那里”(没有 un-eval 函数,您可以轻松发明它,但还没有)

至于更新DOM。PHP 可以像 JavaScript 一样轻松地做到这一点。问题是 javascript 和 php 都不知道 DOM 是什么,只是在浏览器中,dom 很方便地被挂钩为“窗口”对象。你只是表现得好像窗口在那里一样,当 php 被评估为 javascript 时,它将再次获得对 DOM 的访问权限。然而,要使用 dom,代码必须是“面向回调的”,因为它在被评估之前不会得到 dom,但这还不错,你只是在评估完成之前什么都不做,然后执行dom 可用后立即执行整个操作。

代码如下所示:

(() => {

    var php_code = `
function ($window) {
    $window::document::getElementById('myDIV')->innerHTML = "Hello, World!";
};
    `;

    window.addEventListener('load', () => {
        (eval(php_code(window)))();
    });
})();

虽然正确的做法是让函数评估为一个Promise(Promise是通用的......只要你在所有语言中实现它们......)。之后,它就变成了处理本质上独立于语言的Promise/意图的问题(具体来说,意图是语言独立的,一旦意图被翻译,意图将需要依赖关系,这些依赖关系可能会或可能不会被提供来实际执行从头到尾的顺序)。

希望有一天我们会看到 JavaScript 可以评估 PHP 和 PHP 可以无缝评估 JavaScript 的未来,至少可以完成混淆圈,允许我们编写客户端 php 和服务器端 javascript(我们已经完成了一半!)

一些结束的想法

  1. php、perl、lisp 和其他 lambda 演算同义词需要它们自己的内置 JSON 变体。它基本上是 eval 和 uneval,但更简单,因为它没有处理更令人兴奋的数据结构,如函数(JavaScript 可以使用 toString 对它进行 uneval,Perl 可以使用 Data::Dumper 并将 Data::Dumper::Deparse 设置为1)。

  2. 每个 lambda 演算同义词语言(php,perl,lisp,...,其中语句(function(a){return function(b){return a + b;}})(2)(3)是有意义的(天真地甚至汇编可以通过堆栈挖掘来做到这一点,所以它有点像 lambda 演算同义词语言,并且也可以有自己的变体JSON) 应该能够将一串有效代码编码为一个通用的抽象表示,该表示可以编码为任何其他 lambda 演算同义词语言,也可以从其中解码。

    1. 函数式编程原则规定,每个动作都可以分解为执行动作的请求,一个“提交列表”的转换,它堆叠提交而不执行它们,然后将提交映射到全局环境的实际转换(无论是纯的,通过创建一个新环境并进行尾递归/将其放在队列中并使未来的操作与新环境一起工作;或者不纯,通过改变全局状态并继续前进;两者与适当的抽象机制等效)。这意味着您可以在 ajax 请求中发送一个 php 脚本,让服务器在虚拟提交对象上象征性地执行它,然后将它需要执行的操作列表返回给客户端,以使其看起来像是执行了 php)。

如果您在 PHP 中内置了一个 javascript 解释器(或者至少在服务器上可以调用以解释嵌入了 javascript 的 HTML),那将是可能的。已经有一些尝试(例如http://j4p5.sourceforge.net/index.php),但我会避开这些尝试重新思考你在做什么。根据您的特定需求,模板(即Smarty 之类的东西)可能能够解决您的部分问题(当然它不会解释 javascript)。