如何在PHP中运行Ruby/Python脚本并传递参数?
我需要把HTML转换成相应的Markdown格式文本。
因为我在用PHP编程,有些人推荐了Markdownify来完成这个任务,但不幸的是,这个代码没有更新,实际上它是不能用的。在sourceforge.net/projects/markdownify上有个“注意:不再支持 - 你想维护这个项目吗?联系我!Markdownify是一个用PHP写的HTML到Markdown的转换器。可以把它看作是html2text.php的继任者,因为它设计更好,性能更强,处理边缘情况更少。”
根据我所了解,我只有两个不错的选择:
Python:Aaron Swartz的html2text.py
Ruby:Singpolyma的html2markdown.rb,基于Nokogiri
所以,从PHP中,我需要传递HTML代码,调用Ruby/Python脚本,然后接收输出。
(顺便说一下,有人这里问了一个类似的问题(“如何从PHP调用Ruby脚本?”),但对我来说没有实用的信息)。
根据Tin Man的建议,我得到了这个:
PHP代码:
$t='<p><b>Hello</b><i>world!</i></p>';
$scaped=preg_quote($t,"/");
$program='python html2md.py';
//exec($program.' '.$scaped,$n); print_r($n); exit; //Works!!!
$input=$t;
$descriptorspec=array(
array('pipe','r'),//stdin is a pipe that the child will read from
array('pipe','w'),//stdout is a pipe that the child will write to
array('file','./error-output.txt','a')//stderr is a file to write to
);
$process=proc_open($program,$descriptorspec,$pipes);
if(is_resource($process)){
fwrite($pipes[0],$input);
fclose($pipes[0]);
$r=stream_get_contents($pipes[1]);
fclose($pipes[1]);
$return_value=proc_close($process);
echo "command returned $return_value\n";
print_r($pipes);
print_r($r);
}
Python代码:
#! /usr/bin/env python
import html2text
import sys
print html2text.html2text(sys.argv[1])
#print "Hi!" #works!!!
用上面的代码,我得到了这个:
命令返回1 数组 ( [0] => 资源ID #17 1 => 资源ID #18 )
而“error-output.txt”文件显示:
追踪(最近的调用在最前面): 文件“html2md.py”,第5行,在 print html2text.html2text(sys.argv1) IndexError: 列表索引超出范围
有什么想法吗???
Ruby代码(仍在分析中)
#!/usr/bin/env ruby
require_relative 'html2markdown'
puts HTML2Markdown.new("<h1>#{ ARGF.read }</h1>").to_s
只是记录一下,我之前尝试使用PHP最简单的“exec()”,但在处理一些HTML语言中非常常见的特殊字符时遇到了一些问题。
PHP代码:
echo exec('./hi.rb');
echo exec('./hi.py');
Ruby代码:
#!/usr/bin/ruby
puts "Hello World!"
Python代码:
#!usr/bin/python
import sys
print sys.argv[1]
这两者都能正常工作。但当字符串稍微复杂一点时:
$h='<p><b>Hello</b><i>world!</i></p>';
echo exec("python hi.py $h");
它完全不工作。
这是因为HTML字符串需要对特殊字符进行转义。我使用了这个:
$t='<p><b>Hello</b><i>world!</i></p>';
$scaped=preg_quote($t,"/");
现在它像我说的那样工作了这里。
我正在运行: Fedora 14 ruby 1.8.7 Python 2.7 perl 5.12.2 PHP 5.3.4 nginx 0.8.53
5 个回答
在Ruby代码中使用一个变量,然后从PHP代码中把这个变量作为参数传递给Ruby脚本。接着,让Ruby脚本把处理后的结果输出到标准输出(stdout),这样PHP就可以读取到这些结果。
在Python中,可以让PHP把变量作为命令行参数传递过来,然后在Python里通过sys.argv
获取这些参数(这是一种存储传递给Python的命令行参数的列表),接着让Python打印输出,最后PHP再把这个输出显示出来。举个例子:
#!usr/bin/python
import sys
print "Hello ", sys.argv[1] # 2nd element, since the first is the script name
PHP:
<?php
echo exec('python script.py Rafe');
?>
在Ruby中,过程基本上也是一样的。
可以让PHP通过proc_open
来打开Ruby或Python脚本,并把HTML数据通过标准输入(STDIN)传给这个脚本。Ruby或Python脚本会读取这些数据并处理,然后通过标准输出(STDOUT)把结果返回给PHP脚本,最后退出。这种方法在Perl、Ruby或Python中很常见,因为它可以直接访问错误输出(STDERR),如果出现问题也不需要临时文件,不过相对来说稍微复杂一些。
另外一种方法是先把数据从PHP写入一个临时文件,然后使用system
、exec
或者类似的函数来调用Ruby或Python脚本,打开并处理这个文件,最后通过它们的标准输出打印结果。
编辑:
可以查看@Jonke的回答,了解“Ruby中STDIN的最佳实践”,里面有简单的例子说明如何用Ruby读取标准输入并写入标准输出。还有“如何在Python中从stdin读取”也有一些不错的示例。
下面是一个简单的例子,展示如何调用一个Ruby脚本,通过PHP的标准输入管道传递一个字符串,并读取Ruby脚本的标准输出:
把这个保存为“test.php”:
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "./error-output.txt", "a") // stderr is a file to write to
);
$process = proc_open('ruby ./test.rb', $descriptorspec, $pipes);
if (is_resource($process)) {
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
fwrite($pipes[0], 'hello world');
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
?>
把这个保存为“test.rb”:
#!/usr/bin/env ruby
puts "<b>#{ ARGF.read }</b>"
运行PHP脚本会得到:
Greg:Desktop greg$ php test.php
<b>hello world</b>
command returned 0
这个PHP脚本打开了Ruby解释器,然后运行Ruby脚本。PHP会把“hello world”发送给它。Ruby把接收到的文本用粗体标签包裹起来,然后输出,PHP再把这个结果捕获并输出。整个过程没有临时文件,也没有在命令行上传递参数,如果需要的话可以传递很多数据,而且速度也很快。你也可以用Python或Perl代替Ruby。
编辑:
如果你有:
HTML2Markdown.new('<h1>HTMLcode</h1>').to_s
作为示例代码,那么你可以开始用以下代码开发Ruby解决方案:
#!/usr/bin/env ruby
require_relative 'html2markdown'
puts HTML2Markdown.new("<h1>#{ ARGF.read }</h1>").to_s
假设你已经下载了HTML2Markdown代码,并且它在当前目录下,同时你正在运行Ruby 1.9.2。