Proc_Open:与外界沟通

There are many ways we can interact with other applications from PHP and share data; there’s web services, message queuing systems, sockets, temporary files, exec(), etc. Well, today I’d like to show you one approach in particular, proc_open(). The function spawns a new command but with open file pointers which can be used to send and receive data to achieve interprocess communication (IPC).

我们可以通过多种方式与PHP中的其他应用程序进行交互并共享数据。 那里有Web服务,消息队列系统,套接字,临时文件, exec()等。今天,我想向您展示一种特别的方法proc_open() 。 该函数产生一个新命令,但带有打开的文件指针,该文件指针可用于发送和接收数据以实现进程间通信(IPC)。

什么是烟斗? (What’s a Pipe?)

To understand how proc_open() and the process sends and receives data, you know know what a pipe is.

要了解proc_open()和进程如何发送和接收数据,您知道什么是管道。

The Unix Philosophy lead early developers to write many small programs each with very specific functionality. These programs shared plain text as a common format, and users could “chain them together” for greater functionality. The output from one command become the input for the next. The virtual channels that let the data flow between commands became known as pipes.

Unix Philosophy导致早期的开发人员编写许多具有特定功能的小程序。 这些程序以普通格式共享纯文本,用户可以将它们“链接在一起”以实现更大的功能。 一个命令的输出将成为下一个命令的输入。 使数据在命令之间流动的虚拟通道称为管道。

If you’ve ever worked in a Unix shell then there’s a good chance you’ve used pipes, perhaps even without realizing it. For example:

如果您曾经在Unix shell中工作过,那么很有可能已经使用过管道,甚至可能没有意识到它。 例如:

$ mysql -u dbuser -p test < mydata.sql

Here the mysql utility is invoked and a pipe is set up by the system through which the contents of the mydata.sql file is fed into mysql as if you were typing directly at it’s prompt from your keyboard.

在这里,将调用mysql实用程序并由系统设置管道,通过该管道将mydata.sql文件的内容馈送到mysql ,就好像您是直接从键盘上键入时一样。

There are two types of pipes: anonymous and named. An anonymous pipe is ad hoc, created only for as long as the process is running and is destroyed once it is no longer needed. The example with redirecting the file contents into mysql above uses an anonymous pipe. A named pipe on the other hand is given a name and can last indefinitely. It’s created using special commands and often appears as a special file in the filesystem.

管道有两种: 匿名管道和命名管道。 匿名管道是ad hoc ,仅在进程运行时才创建,并且在不再需要时被销毁。 上面的将文件内容重定向到mysql的示例使用匿名管道。 另一方面,命名管道具有名称,并且可以无限期使用。 它是使用特殊命令创建的,通常在文件系统中显示为特殊文件。

Regardless of the type, an important property of any pipe is that it’s a FIFO (first in, first out) structure. This means the data written into a pipe first by one process is the first data read out from the pipe by the other process.

无论哪种类型,任何管道的重要属性都是它是FIFO ( 先进先出 )结构。 这意味着由一个进程首先写入管道的数据是由另一个进程从管道中读取的第一数据。

引入proc_open() (Introducing proc_open())

The PHP function proc_open() executes a command, much like exec() does, but with the added ability to direct input and output streams through pipes. It accepts some optional arguments, but the mandatory arguments are:

PHP函数proc_open()执行命令,就像exec()一样,但是具有通过管道引导输入和输出流的附加功能。 它接受一些可选参数,但必需参数为:

  • a command to execute.

    要执行的命令。
  • an array that describes the pipes to be used.

    一个描述要使用的管道的数组。
  • an array reference that will later be populated with references to the pipes’ endpoints so you can send/receive data.

    数组引用,以后将使用对管道端点的引用来填充它,以便您可以发送/接收数据。

The optional arguments are used for tweaking the execution environment in which the command spawns. I won’t discuss it here, but you can find more information in the PHP manual.

可选参数用于调整命令产生的执行环境。 我不会在这里讨论它,但是您可以在PHP手册中找到更多信息

Aside from the command to execute, I would say the descriptor array that defines the pipes is the most important argument to pay attention to. The documentation explains it as an “indexed array where the key represents the descriptor number and the value represents how PHP will pass that descriptor to the child process,” but what exactly does that mean?

除了要执行的命令外,我还要说定义管道的描述符数组是要注意的最重要的参数。 文档将其解释为“索引数组,其中键代表描述符号,而值代表PHP如何将描述符传递给子进程”,但这到底是什么意思?

The three main data streams for a well-behaved unix process are STDIN (standard input), STDOUT (standard output), and STDERR (standard error). That is, there’s a stream for incoming data, one for outgoing data, and a second outgoing stream for informational messages. STDIN has traditionally been represented by the integer 0, STDOUT by 1, and STDERR by 2. So, the definition with key 0 will be used to set up the input stream, 1 for the output stream, and 2 for the error stream.

行为良好的unix进程的三个主要数据流是STDIN(标准输入),STDOUT(标准输出)和STDERR(标准错误)。 也就是说,有一个用于输入数据的流,一个用于输出数据的流,以及另一个用于信息消息的输出流。 传统上,STDIN用整数0表示,STDOUT用1表示,STDERR用2表示。因此,键0的定义将用于设置输入流,将1设置为输出流,将2设置为错误流。

These definitions themselves can take one of two forms, either an open file resource or an array that describes the nature of the pipe. For an anonymous pipe, the first element of the descriptive array is the string “pipe” and the second is “r”, “w”, or “a” depending if the pipe is to be read from, written to, or appended. For named pipes, the descriptive array holds the string “file”, the filename, and then “r”, “w”, or “a”.

这些定义本身可以采取以下两种形式之一:打开文件资源或描述管道性质的数组。 对于匿名管道,说明性数组的第一个元素是字符串“ pipe”,第二个元素是“ r”,“ w”或“ a”,具体取决于是否要从管道中读取,写入或附加管道。 对于命名管道,描述性数组包含字符串“ file”,文件名,然后是“ r”,“ w”或“ a”。

Once called, proc_open() fills the third parameter’s array reference to return the resources to the process. The elements in the reference can be treated as normal file descriptors and they work with file and stream functions like fwrite(), fread(), stream_get_contents(), etc.

一旦调用, proc_open()将填充第三个参数的数组引用,以将资源返回给进程。 引用中的元素可以视为普通文件描述符,它们可以使用文件和流函数,例如fwrite()fread()stream_get_contents()等。

When you’re done interacting with the external command, it’s important to clean up after yourself. You can close the pipes (with fclose()) and the process resource (with proc_close()).

与外部命令交互完成后,请务必自行清理。 您可以关闭管道(使用fclose() )和进程资源(使用proc_close() )。

Depending on how your target command/process behaves, you may need to close the STDIN connection before it begins its work (so it knows not to expect any more input). And, you should close the STDOUT and STDERR connections before closing the process, or it might hang while it waits for everything to be clean before shutting down.

根据目标命令/进程的行为,您可能需要在STDIN连接开始工作之前将其关闭(因此,它知道不会再有其他输入了)。 并且,您应该在关闭进程之前关闭STDOUT和STDERR连接,否则在等待所有清理之前,进程可能会挂起。

实际示例:转换Wiki标记 (A Practical Example: Converting Wiki Markup)

So far I’ve only talked about how things work, but I haven’t shown you an example using proc_open() to spawn and communicate with an external process yet. So, let’s see how easy it is to use.

到目前为止,我只讨论了事情的工作原理,但是还没有向您展示使用proc_open()生成并与外部进程进行通信的示例。 因此,让我们看看它的使用有多么容易。

Suppose we have a need to convert a chunk of text with wiki markup to HTML for display in a user’s browser. We’re using the Nyctergatis Markup Engine (NME) to perform the conversion, but since it’s a compiled C binary we need a way to fire up nme when its needed and a way to pass input and receive output.

假设我们需要将带有Wiki标记的大量文本转换为HTML,以便在用户的浏览器中显示。 我们正在使用Nyctergatis标记引擎 (NME)进行转换,但是由于它是已编译的C二进制文件,因此我们需要一种在需要时触发nme的方法,以及一种传递输入和接收输出的方法。

<?php
// descriptor array
$desc = array(
    0 => array('pipe', 'r'), // 0 is STDIN for process
    1 => array('pipe', 'w'), // 1 is STDOUT for process
    2 => array('file', '/tmp/error-output.txt', 'a') // 2 is STDERR for process
);

// command to invoke markup engine
$cmd = "nme --strictcreole --autourllink --body --xref";

// spawn the process
$p = proc_open($cmd, $desc, $pipes);

// send the wiki content as input to the markup engine 
// and then close the input pipe so the engine knows 
// not to expect more input and can start processing
fwrite($pipes[0], $content);
fclose($pipes[0]);

// read the output from the engine
$html = stream_get_contents($pipes[1]);

// all done! Clean up
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($p);

First the descriptor array is laid out, with 0 (STDIN) as an anonymous pipe that will be readable by the markup engine, 1 (STDOUT) as an anonymous pipe writable by the engine, and 2 (STDERR) redirecting any error messages to an error log file.

首先,布置描述符数组,其中0(STDIN)作为标记引擎可以读取的匿名管道,1(STDOUT)作为引擎可以写入的匿名管道,以及2(STDERR)将所有错误消息重定向到错误日志文件。

The “r” and “w” on the pipe definitions might seem counter intuitive at first, but keep in mind they are the channels that the engine will be using and so are configured from it’s perspective. We write to the read pipe because the engine will be reading the data from it. We read from the write pipe because the engine has written data to it.

管道定义上的“ r”和“ w”乍看起来似乎很不直观,但请记住,它们是引擎将使用的通道,因此从其角度进行配置。 我们写入读取管道,因为引擎将从中读取数据。 我们从写入管道读取数据,因为引擎已向其中写入数据。

结论 (Conclusion)

There are many ways to interact with external processes; some may be better than using proc_open() given how and what you need to work with, or proc_open() might just be what the doctor ordered for your situation. Of course you’ll implement what makes sense, but now you’ll know how to use this powerful function if you need to!

与外部流程进行交互的方法有很多。 鉴于您需要使用的方式和功能,有些方法可能比使用proc_open()更好,或者proc_open()可能正是医生根据您的情况而定的。 当然,您将实现有意义的方法,但是现在您将知道如何在需要时使用此强大功能!

I’ve placed someexample code on GitHub that simulates a bare-bones wiki using NME, just like in the example above. Feel free to clone it if you’re interested in playing and exploring further.

我已经在GitHub上放置了一些示例代码 ,就像上面的示例一样,该代码使用NME模拟了基本的Wiki。 如果您有兴趣玩和进一步探索,请随时克隆它。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/proc-open-communicate-with-the-outside-world/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值