65.9K
CodeProject 正在变化。 阅读更多。
Home

NodeJS Web 服务器运行 PHP

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (2投票s)

2019年1月8日

CPOL

5分钟阅读

viewsIcon

20722

downloadIcon

62

如何在 NodeJS 上使用 PHP 脚本。

引言

本文源于一次尝试了解 NodeJS[1] 以及如何使用 JavaScript 和 NodeJS 构建的服务器调用 PHP 脚本。

我知道有很多框架可以加快应用程序的开发速度,例如,仅举一例,Express.js 以及一个用于管理 PHP 网站(如 WordPress 网站)的 NodeJS 扩展,但正是由于这些产品的丰富性,才表明了我们可以轻松开发纯 NodeJS HTTP 服务器。

NodeJS

NodeJS 的两个主要特点是:模块化和非阻塞异步编程[2]。模块化基于 require(JavaScript_File) 函数,该函数读取并执行 JavaScript_File 的内容,返回一个对象;NodeJS 有许多原生模块,当然,您也可以添加自己的模块。

非阻塞特性基于 NodeJS 对象通常具有 addListener (或其同义词 on) 方法来管理事件;在下面的片段中,当 request 对象(即对 Web 服务器的请求)具有数据时,会触发 data 事件;该对象本身会响应数据结束的 end 事件。

var inData = '';	//	for POST data recovery (possibly)
request.on('data', function(chunk) {
	inData += chunk.toString();
});
request.on('end', function() {
...

创建并启动 WEB 服务器

我们可以用几条指令创建并启动一个 Web 服务器。

// server definition and activation
var port = 1337;
var server = require('http').createServer().listen(port);
console.log('Server running at 127.0.0.1:' + port);
// end server definition and activation
server.on('request', function(req, resp) {
...

在上面代码的这些行中,我强调了 NodeJS 的两个主要特性。

  • var server = require('http').createServer().listen(port);

    之所以这样写,是因为 http 模块的 createServer 方法返回一个 server,该服务器的 listen 方法返回 server 对象本身,即,这是紧凑的写法:

    var server = require('http').createServer();
    server.listen(port);
  • server.on('request', function(request, response) {...

    这是当浏览器发起请求时激活的函数。

包含 Web 服务器的脚本在窗口控制台中执行。

set path=C:\Sviluppo\php7;\%path% C:\Users\User>node 
         C:\www\condorinformatique\Nodejs\tryNodeJS.js

(第一行只是使 PHP 解释器可访问。)

管理请求

如前所述,HTTP 对象的 server 实例具有 on 方法,该方法允许拦截一个事件,特别是 request 事件,该事件指示接收到来自浏览器的请求,该请求由一个具有两个对象的函数管理:requestresponse
request 对象的 end 事件发生时,需要进行三个步骤来响应请求:

  • 发现请求资源的类型(HTML 文件、图像、JavaScript、样式表、PHP 脚本等)
  • 检索到达的数据(如果有)
  • 发送响应

资源是什么?

url 模块用于获取一个包含 URI 组件作为属性的对象。

var Url = require('url').parse(request.url,true)

本次演示需要的属性是 pathnamequery,即请求的文件和可能的参数(path 属性同时包含这两者)。

  query: { Authors: '' },
  pathname: '/getData.php',
  path: '/getData.php?Authors',
  ...

数据是什么?

Url 对象的 query 属性包含 URI 中可能存在的参数,但如果 request 方法是 POST(例如提交表单时),则数据通过 request 对象的 data 事件检索。

...
var Regexp = /name=\"(.+)\"\r\n\r\n(.*)\r\n/gm;		// for extract form data
...
	var inData = '';	                  // for POST data recovery (possibly)
	request.on('data', function(chunk) {
		inData += chunk.toString();
	});
 ...
	var parms = Url['query'];	          // data from GET or URI
	// extract fields
	match = Regexp.exec(inData);	      // data from form (POST method)
	while (match != null) {		          // matched text: match[0]; 
                                          // capturing group n: match[n]
		parms[match[1]] = match[2];
		match = Regexp.exec(inData);
	}
...
在上面的片段中,在获取了可能的 POST 数据后,会创建一个包含来自 URI 和(如果有)POSTed 数据的输入数据数组。前者包含在 url 对象的 query 属性中。
var parms = Url['query'];.
对于后者,在提交表单的情况下,数据结构与右侧类似,可以使用正则表达式[3] 进行提取。
/name=\"(.+)\"\r\n\r\n(.*)\r\n/gm
其中加粗部分标识了表单字段的名称和值。
parms[match[1]] = match[2];.
-----------------------------7e217f121030a
Content-Disposition: form-data; name="fg_Store"

fg_frm
-----------------------------7e217f121030a
Content-Disposition: form-data; name="Author_Group"

Social Aphorisms
-----------------------------7e217f121030a
Content-Disposition: form-data; name="Author"

Claudie Baudry
-----------------------------7e217f121030a-- 

发送响应

request 对象的 end 事件触发时,发送响应。

request.on('end', function() {
	Url = require('url').parse(request.url,true)		
	if (pathName == "/") pathName = "\\index.html";	// first call
	fs.access(__basedir + pathName,(err) => {		// check if file exists)
		if (err == null) {
			var type = path.extname(pathName).substring(1).toLowerCase();
			var cType = "text/html; charset=utf-8"
			if (typeof types[type] != "undefined") cType = types[type];
			if (type == "php") {
...
			} else {
				fs.readFile(__basedir + pathName, (err,data) => {
					if (err) throw err;
					response.writeHead(200, {'Content-Type': cType});
					response.end(data,'binary');
				});
			}				
		} else {
			response.writeHead(404, {'Content-Type': 'text/html; charset=utf-8'});
			response.end("<img src='images/condor.gif'>" + err.message);
			console.log(err.message);
		}
	}
	);		
});
...

上述函数处理默认请求,如果 pathname 是根目录,即 /;如果调用的资源不存在,则生成错误消息。

对于找到的资源,需要确定其 Content-type 以发送适当的标头,最后发送资源本身(对于 PHP 脚本,请参阅下面的段落)。

处理 PHP 资源

如果请求的资源是 PHP 脚本,程序会调用 PHP 解释器,并带有开关 -r,后跟 PHP 指令;这可以很简单,如下所示:

var spawnPHP = spawn('php',["-r","include 'phpscript'"]);

如果脚本没有参数。

在本次演示的情况下,在包含 PHP 脚本的指令之前,必须将表单字段插入到 PHP 关联数组 $_REQUEST 中,因此在 -r 开关之后,会是类似以下的内容:

$_REQUEST['Author'] = 'Nicolas de Condorcet';
$_REQUEST['Author_Group'] = 'Social Aphorisms';
$_REQUEST['fg_Store'] = 'fg_frm';
$_REQUEST['fg_Button'] = 'Author';
$_REQUEST['R'] = '0.7828608712500384';
include 'C:\www\condorinformatique\nodejs/getData.php';

下面的 script 片段显示了如何启动 PHP 解释器以及当 data 事件成功时如何从 stdout 检索响应。

	var requests = "include '" + __basedir + pathName + "';";
	// prepare fields for PHP
	for(q in parms) requests = "\$_REQUEST['" + q +"'] = '" + 
                                parms[q] + "';" + requests;
	var spawnPHP = spawn('php',["-r",requests]);
	spawnPHP.stdout.on('data',function (data) {
		response.writeHead(200, {'Content-Type': cType});
		response.end(data);
		console.log(getTime() + " Sent resource: " + pathName)
	});

使用演示

将演示文件解压到您选择的文件夹中。您必须更改第一行中的 set path=C:\Sviluppo\php7;\%path% 为您的 PHP 解释器的路径;此时,您就可以在浏览器中输入 http://127.0.0.1:1337/ 开始访问了。

演示使用了一些我的程序,您可以从我的 网站下载,包括文档。

  • 在 PDO 表的字段交叉处,还有一篇 CodeProject 文章
  • JavaScript 表单生成器,还有一篇 CodeProject 文章

结论

本文是关于如何创建 Web 服务器的初步介绍,并未涵盖所有可能性,特别是文件上传的管理(这可能是下一篇文章的主题)。

注意

  1. ^ 有关 NodeJS 的介绍,请参阅 CodeProject 文章 关于核心 NodeJS 的一切 — 第一部分
    Node.Js 单线程机制如何工作?理解 NodeJs 中的事件循环
  2. ^ 非阻塞异步的效果是浏览器请求的资源顺序可能与提供的资源顺序不同:正如我们在控制台日志中看到的,一个 JavaScript 脚本 formgen.js 在 CSS 文件 styles.css 之前被请求,但它是在之后提供的,因为前者比后者更大。
    C:\www\condorinformatique\nodejs>node tryNodeJS.js
    16:28:45 Server running at 127.0.0.1:1337
    16:29:07 Request resource: \index.html
    16:29:07 Sent resource: \index.html
    16:29:07 Request resource: /js/formgen.js
    16:29:07 Request resource: /styles.css
    16:29:07 Sent resource: /js/formgen.js
    16:29:07 Sent resource: /styles.css
    16:29:07 Request resource: /images/condor.gif
    16:29:07 Sent resource: /images/condor.gif
    16:29:07 Request resource: /getData.php
    16:29:07 Request resource: /CONDOR.WAV
    16:29:08 Sent resource: /CONDOR.WAV
    16:29:08 Request resource: /favicon.ico
    16:29:08 Sent resource: /favicon.ico
    16:29:08 Sent resource: /getData.php
  3. ^ 一个很棒的尝试和增加知识的网站是:在线正则表达式测试器和调试器

历史

  • 2019 年 1 月 8 日:初稿
© . All rights reserved.