代码之家  ›  专栏  ›  技术社区  ›  Andreas Bonini

在PHP中执行javascript

  •  15
  • Andreas Bonini  · 技术社区  · 15 年前

    我正在用PHP生成典型的Web2.0HTML页面:它包含很多 <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保存在 $html PHP变量。现在,我想把这个变量传递给一个函数,它将返回$result= <html>....<body><p>Hi!</p></body></html> .

    这有可能吗?

    编辑 因为你们中的许多人对我的要求感到困惑,我会解释原因。不幸的是,用户所面对的一切都是用javascript编写的,这使得搜索引擎无法绘制网站。所以我想把准备好发布的事件HTML代码发送给他们。

    10 回复  |  直到 6 年前
        1
  •  22
  •   SteAp    12 年前

    使用PHP评估JavaScript代码 ,看看 V8 JavaScript engine extension ,可以编译为php二进制文件:

    V8是 Google's open source JavaScript implementation .

        2
  •  9
  •   Sijin    15 年前

    我能找到的最佳解决方案是使用HTMLUnit http://htmlunit.sourceforge.net/ 在服务器上用JavaScript执行HTML,并返回用户在浏览器上看到的最终HTML。

    这个库对javascript有很好的支持,并且是无头的,所以您应该能够在服务器上运行它。

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

        3
  •  5
  •   mAsT3RpEE    11 年前

    您有两个问题:

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

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

    1. V8 :谷歌为Chrome。PHP扩展。
    2. Rhino :由Mozilla for Firefox提供。仅在Java中可用。
    3. JavaScriptCore :苹果公司出游。仅在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,请遵循上面提到的内容。

        4
  •  4
  •   Dmytro    7 年前

    这个问题非常类似于如何在javascript中执行javascript,或者在php中执行php,答案是您可以对其进行评估。如果php可以评估javascript,而javascript可以评估php,那么我们将不进行此讨论。

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

    下面是一个简单的解释php的javascript示例(一个更诚实的示例不是那么做作,而是将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:unexpected token(”)。

    无论如何,在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)

    /*据我所知,无法将其描述为函数,因为它不是原型函数*/ 类控制台{ 静态函数日志($text){ 回声$文本。\n; } };

    调用用户func(function()。{ $myscopevariable=“嘿,这不是javascript!”; console::日志($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; } 
    

    从本质上讲,就是通过“共同的口齿不清”进行交流。当然,这将是非常昂贵的,而不是本地的,但可以演示一个例子。理想情况下,我们会有一个本机模块封装所有类型的脚本,可以轻松地将ruby转换为php、perl、python和javascript,然后将结果编译为c以供检查)。Javascript通过自己的评估和自己的代码的打印来帮助接近这一点。如果所有的语言都能做到这两件事,那就更容易实现了,但遗憾的是,javascript“几乎就在那里”(没有un-eval函数,你可以很容易地发明它,但还没有实现)。

    关于更新dom。PHP可以像JavaScript一样轻松地完成它。问题是,javascript和php都不知道dom是什么,只是在浏览器中,dom被方便地连接为“window”对象。您只需将窗口视为存在,当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)))();
        });
    })();
    

    虽然正确的做法是对承诺进行功能评估(承诺是普遍的…一旦你用所有语言实现它们…)。在这之后,它就变成了一个处理承诺/意图的问题,这些承诺/意图基本上是独立于语言的(具体来说,意图是独立于语言的,一旦意图被翻译,意图将需要依赖性,这些依赖性可能会被提供,也可能不会被提供来实际执行从开始到结束的顺序)。

    希望有一天我们能看到一个未来,在这个未来,javascript可以对php进行评估,而php可以无缝地对javascript进行评估,至少可以完成这个混乱的循环,让我们可以编写客户端php和服务器端javascript(我们已经完成了一半!)

    一些结束的想法

    1. PHP、Perl、Lisp和其他lambda微积分同义词需要它们自己内置的JSON变体。它基本上是eval和unaveral,但更简单,因为它不考虑更令人兴奋的数据结构,如函数(javascript可以稍微不均匀地使用toString,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)。
        5
  •  3
  •   wimvds    15 年前

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

        6
  •  2
  •   Select0r    15 年前

    如果我理解的对,你想用PHP执行一个javascript函数…javascript是在浏览器(客户端)中执行的,php是服务器端的,所以除非您用php编写一个javascript解析器,否则它将不起作用。

    为什么服务器上的JS解析器完全有意义(我想不出它应该有什么原因)或者一开始是可能的,这是另一个问题…JS可以在服务器上不存在的DOM上工作,也可以调用无用的函数(想想“window.close()”会/应该在服务器上做什么!?)

    所以简而言之:不。:)

        7
  •  1
  •   Hyder B.    8 年前

    我在构建一个网络爬虫时遇到了同样的问题。您可以使用无头浏览器(如 PhantomJS . 您基本上告诉Phantomjs为您加载数据并转储您想要的数据。

    如果您使用的是PHP,那么有一个包装器, php-phantomjs 做这项工作。

        8
  •  0
  •   SergGr    15 年前

    我怀疑对于浏览器javascript和PHP来说,有一些很好的通用服务器端运行时。对于复杂的客户机脚本,没有“最终的DOM状态”这样的东西。假设某个DOM更新方法是用 setTimeout . 你想等它吗?如果它以同样的方式重新安排其他更新(例如只在页面的某个位置显示当前时间),您将等待多长时间?如果页面下载了一些Ajax数据呢?是否要执行实际的服务器请求、模拟cookie等?我认为这太复杂了,不能很好地实现。好吧,也许谷歌在他们的爬虫程序中有类似的东西,但它是专门为他们的特殊需求而设计的吗?

        9
  •  -1
  •   jef2904    15 年前

    有一些新的服务器运行JavaScript服务器端,能够操作DOM,但与PHP无关。

    http://jaxer.org/

        10
  •  -1
  •   Barry    6 年前
    function visible() {
        echo '<script type="text/javascript">
            var o = document.getElementById("overlay");
            o.style.visibility = "visible"; 
        </script>';
    }
    

    这是我经常在PHP中运行javascript的一种好方法