在PHP中执行JavaScript
我使用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保存在$html
PHPvariables中。 现在,我想把这个variables传递给一个函数,它会返回$ result = <html>....<body><p>Hi!</p></body></html>
。
这可能吗?
编辑 :因为你们很多人都困惑我的要求,我会解释原因。 不幸的是,用户所面对的所有东西都是用javascript制作的,这使得网站无法被search引擎抓取。 所以我想给他们发送准备好事件的HTML代码。
要使用PHP评估JavaScript代码 ,请查看可以编译到PHP二进制文件中的V8 JavaScript引擎扩展 :
V8是Google的开源JavaScript实现 。
我能find的最好的解决scheme是在服务器上使用HtmlUnit http://htmlunit.sourceforge.net/来执行你的html和javascript,并取回用户在浏览器上看到的最终html。;
该库对JavaScript有良好的支持,并且是无头的,所以你应该能够在服务器上运行它。
您需要编写一个小的Java包装器,它可以通过命令行接受input,并将其传递到HtmlUnit进行处理,然后将结果返回给您。 然后你可以从PHP调用这个包装器。
如果我理解你是正确的,你想在PHP中执行一个JavaScript函数… JavaScript在浏览器(客户端)中执行,PHP是服务器端的,所以除非你在PHP中编写JavaScriptparsing器,将无法工作。
为什么一个JSparsing器在服务器上根本就没有任何意义(我想不出它应该这样做)或者是可能的,另一个问题是…… JS会在一个DOM上工作,存在于服务器上以及函数被调用是无用的(想想“window.close()”会在服务器上做什么?)。
所以要简短的回答:否:)
你有两个问题:
- 执行JavaScript。
- 更新DOM(执行javascript后的HTML)。
要执行JavaScript,您将需要一个JavaScript引擎。 目前有3种供您使用:
- V8 :Google for Chrome。 PHP扩展。
- 犀牛 :由Mozilla为Firefox。 只有在JAVA中可用。
- JavaScriptCore :由苹果为Safari。 只有在C.
一旦你有一个JavaScript引擎,你将需要pipe理DOM(文档对象模型)。 这允许你parsingHTML到像DOM节点,文本节点,元素等对象。最重要的是,你将需要同步你的DOM与JavaScript引擎,并在您的JavaScript引擎中安装DOM库。 虽然可能有不同的方式来做到这一点,我更喜欢简单地包括/评估一个独立的JavaScript DOM到引擎,并简单地通过HTML。
- Env-JS JavaScript DOM库。 兼容原型/ jQuery。
- 用于NodeJS的jsdom JavaScript DOM库。
现在您已经拥有一个JavaScript引擎和DOM库,现在您可以毫无问题地评估大多数脚本。
最佳答案
NodeJS作为一个独立的可执行文件,它有一个JavaScript引擎以及DOM操作。除此之外,您还可以将其用作Web服务器。 也许这是一个更好的解决scheme,但是如果PHP是必须的,坚持上面提到的问题。
这将是可能的,如果你有一个JavaScript解释器内置到PHP(或至less在服务器上的东西,你可以调用解释embeddedJavaScript的HTML)。 已经有一些尝试(例如http://j4p5.sourceforge.net/index.php ),但我会避开这些尝试重新考虑你在做什么。 根据你的具体需求,模板(也就是像Smarty这样的东西)可能能够解决你的问题(当然,它不会解释JavaScript)。
这个问题是非常相似的如何在JavaScript中执行JavaScript,或PHP中的PHP,答案是,你可以评估它。 如果PHP可以评估JavaScript和JavaScript可以评价PHP,我们不会有这个讨论。
为了JavaScript评价PHP,必须将PHP代码parsing为代表脚本的结构。 JavaScript可以很容易地用JavaScript对象表示法(不是JSON格式,但是实际表示forms)来做到这一点,并在function上分解脚本。
下面是一个JavaScript解释PHP的简单例子(一个更诚实的例子不会这么做,但是把phpparsing成它自己的类似于JSON的表示或者可能的字节码,然后解释这个类似于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可以很容易地创build任何东西的“JSON化”版本(只要它不是原生的),所以你可以让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可以很容易地把它parsing成一个对象,然后回到一个string中,但是为了使eval()不会引起错误,必须添加'('和')'。“Uncaught SyntaxError : 意外的标记 (”。
无论如何,类似的事情可以在PHP中完成:
<?php $functionObject = eval( 'return function($a) {' . ' return $a;' . '};' ); echo $functionObject("hi"); ?>
了解了这一点,您必须将JavaScript函数对象转换为一个PHP函数对象,或者只是简单的翻译。
问题在于,JavaScript(ES6)比PHP(5.6,7可能更好,但没有Service Pack 1的Windows 7,所以我不能在这台计算机上运行它不起作用)performance力的事实。 这反过来意味着JavaScript有许多function,PHP没有,例如:
(function() { console.log("Hello World"); })();
不支持PHP 5.6,因为它不支持自动执行function。 这意味着你需要做更多的工作来把它翻译成:
call_user_func(function() { echo "hello, world!" . "\n"; });
还有一个问题是,PHP并没有像JavaScript那样真正使用原型,所以很难做翻译。
无论如何,最终PHP和JavaScript是非常相似的,以至于你可以基本上使用一个在另一个例外。
例如:(PHP)
/ *不能描述为一个函数据我所知,因为不是原型* /类控制台{静态函数日志($文本){回声$文本。 “\ 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在本地更具有performance力,而PHP必须创build类来表示许多JavaScript结构(足够有趣,PHP可以预处理php来修复所有这些问题)。
幸运的是,PHP现在可以自然地理解JSON,所以在javascript自己评估之后,javascript可以读取生成的结构(JavaScript中的大部分内容是对象或函数),包括源代码,并将这些对象转换为JSON编码的forms。 之后,您可以让PHPparsingJSON以通过中性表单恢复代码)。
例如
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; }
从本质上讲,可以说是通过“Common LISP”进行通信。 当然,这将是非常昂贵的,而不是本地的,但是展示一个例子是很好的。 理想情况下,我们将有一个本地模块封装所有types的脚本,可以很容易地翻译ruby到PHP到Perl到python到JavaScript,然后编译结果为C的哎呀)。 JavaScript可以通过打印自己的代码来评估自己,并帮助接近这一点。 如果所有的语言都能完成这两件事情,那么完成起来会容易得多,但是令人遗憾的是,JavaScript只是“几乎在那里”(没有非评估函数,你可以很容易地创造它,但它还没有)
至于更新DOM。 PHP可以像JavaScript那样简单地完成它。 问题是javascript和php都不知道DOM是什么,只是在浏览器中,dom被方便地挂钩为“窗口”对象。 你只需要像在那里的窗口一样,当php被评估为javascript时,它将再次访问DOM。 但是,为了使用dom,代码必须是“callback导向的”,因为在评估之前它不会得到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,至less要完成让我们编写客户端PHP和服务器端JavaScript的混淆圈(我们已经到了一半了!
一些结局的想法
-
php,perl,lisp和其他lambda微积分同义词需要自己构build的JSON变体。 它基本上是eval和不确定的,但更简单,因为它没有照顾更多的令人兴奋的数据结构,如函数(JavaScript可以使用toString有些不同,而Perl可以使用Data :: Dumper将Data :: Dumper :: Deparse设置为1)。
-
每一个lambda演算的同义词语言(php,perl,lisp,…,where语句
(function(a){return function(b){return a + b;}})(2)(3)
程序集可以用堆栈挖掘来做到这一点,所以它有点类似于lambda演算的同义词语言,也可以有它自己的JSON变体)应该能够将一个有效的代码串编码成一个可以被编码的通用抽象表示,从任何其他的lambda演算同义词语言解码。- 函数式编程原则规定每一个动作都可以分解成一个请求来执行动作,一个“提交列表”的转换,这个提交列表是堆栈提交而不执行的,然后将提交映射到全球环境的实际转换(纯粹的创造一个新的环境,做尾部recursion/放在队列中,让未来的行动与新的环境一起工作;或者通过改变全局状态并继续前进;二者与适当的抽象机制是等价的)。 这意味着你可以在ajax请求中发送一个php脚本,让服务器在伪提交对象上象征性地执行它,然后返回给客户端一个它需要做的动作列表,使它看起来像执行php)。
我怀疑浏览器的JavaScript通常有一些好的通用服务器端运行时,特别是在PHP中。 对于复杂的客户端脚本,不存在“最终DOM状态”这样的事情。 想象一下,有些DOM更新方法是用setTimeout
来安排的。 你想等吗? 如果它以相同的方式重新计划一些其他更新(例如只显示页面上的当前时间),那么您要等多久? 而如果页面做一些AJAX数据下载? 你想要做实际的服务器请求,模拟cookies等? 我认为这太复杂,不能很好地实施。 那么,也许谷歌在他们的抓取工具中有这样的东西,但它是专门为他们的特殊需求。
在构buildnetworking爬虫程序时,我遇到了同样的问题。 您可以通过使用PhantomJS等无头浏览器来获取最终的DOM。 您基本上告诉PhantomJS为您加载数据并转储您想要的数据。
如果你使用PHP,有一个包装, php-phantomjs来完成这项工作。
有新的服务器运行Javascript服务器端,并能够操纵DOM,但它与PHP无关。
<?php function visible() { echo '<script type="text/javascript"> var o = document.getElementById("overlay"); o.style.visibility = "visible"; </script>'; } ?>
这是我经常在php中运行javascript的好方法
<?php function visible() { return ('<script type="text/javascript">var o = document.getElementById("cf"); document.write(o.innerHTML); </script>'); } echo visible(); ?>