Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

NodeJS WEB Server Running PHP

3.00/5 (2 votes)
10 Jan 2019CPOL5 min read 20K   33  
How to use PHP scripts on NodeJS

Image 1

Introduction

This article comes from an attempt to be acquainted on NodeJS[1] and how a server built with JavaScript and NodeJS can call PHP scripts.

I am aware that there are many frameworks to speed up the development of applications, such as, to name one, Express.js and also a NodeJS extension for managing PHP based site (like WordPress sites), but it is precisely this abundance of products that is an indication of the easiness with which we can develop a pure NodeJS HTTP server.

NodeJS

There are two main features of NodeJS: modularity and the non-blocking asynchronously programming[2]. Modularity is based on require(JavaScript_File) function that reads and executes the contents of JavaScript_File, returning an object; NodeJS has many native modules and, of course, one can add their own modules.

The non-blocking characteristic is based on that NodeJS objects have, normally, the method addListener (or his synonym on) to manage events; in the fragment below, the event data is fired when the request object (that is the request made to the web server) has data; the same object reacts at the event end of data:

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

Create and Start the WEB Server

We can create and start a web server with few instructions:

JavaScript
// 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) {
...

In these lines of the above code, I highlight the two main features of NodeJS:

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

    This statement arose because the createServer method on module http returns a server whose method listen returns the object server itself, i.e., this is a compact writing of:

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

    This is the function that activates when a request arrives from the browser.

The script containing the web server is executed in the window console:

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

(The first line simply makes the PHP interpreter accessible.).

Managing the Requests

As previously mentioned, the server instance of HTTP object has the on method that allows to intercept an event, in particular, the request event that indicates the reception of a request from the browser, request that is managed by a function with two objects: request and response.
When the event end of request object occurs, there are three steps to meet the request:

  • to discover the type of the requested resource (HTML file, image, JavaScript, style sheet, PHP script, etc.)
  • to recover the data arrived, if any
  • to send the answer

Which is the Resource?

The module url is used to obtain an object that contains the URI components as properties:

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

Those necessary for the demo in question are pathname and query, i.e., the file requested and the possible parameters (the path property contains both):

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

Which are the Data?

The query property of Url object contains the possible parameters present in the URI, but if the request method is POST, as when submitting a form, the data are retrieved by the event data of the request object.

JavaScript
...
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);
	}
...
In the above fragment, after having acquired the possible POST data, an array of input data is created both from URI both from POSTed data (if any). The first ones are contained in the property query of the url object:
var parms = Url['query'];.
For the second ones, in case of submission of a form, the data have a structure similar to this on the right and these can be extracted using the regular expression[3]:
/name=\"(.+)\"\r\n\r\n(.*)\r\n/gm
where the bold parts identify the name and the value of the fields on form:
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-- 

Sending the Answer

The answer is sent when the event end of the request object is fired:

JavaScript
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);
		}
	}
	);		
});
...

The above function deals on a default request, if the pathname is root, i.e., /; if the resource called is not present generates an error message.

For the resources found, it is necessary to determine its Content-type in order to send an appropriate header and finally send the resource itself (for PHP scripts, see the paragraph below).

Dealing with PHP Resources

If the resource requested is a PHP script, the program calls a PHP interpreter with switch -r followed by PHP instructions; this can be simply as follows:

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

if the script has no parameters.

In case of this demo, before the instruction that includes the PHP script, the form fields must be inserted in the PHP associative array $_REQUEST so after the -r switch, there is something similar to:

JavaScript
$_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';

The script fragment below shows how to start the PHP interpreter and how to retrieve the answer on the stdout when the data event succeeded:

JavaScript
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)
});

Using the Demo

Decompress the demo file in a folder of your choice. You must change in the first line set path=C:\Sviluppo\php7;\%path% with the path to your PHP interpreter; at this point, you are ready to start typing in the browser http://127.0.0.1:1337/.

The demo uses some of my programs that you can download with the documentation from my site:

  • Crossing field in PDO table, there is also a CodeProject article
  • JavaScript Form Generator, there is also a CodeProject article

Conclusion

This article is a first approach for how to create a web server and it does not cover all the possibilities, in particular, how to manage the upload of files (which could be the topic of a next article).

Note

  1. ^ For an introduction on NodeJS, see the CodeProject article All About Core NodeJS — Part 1.
    How Node.Js Single Thread mechanism Work ? Understanding Event Loop in NodeJs
  2. ^ The effect of the non-blocking asynchronously is that the sequence of resources required by the browser may be different from those provided: as we can see from the console login, a JavaScript script formgen.js is requested before the CSS file styles.css bat it is served after because the size of the first is larger than the second.
    JavaScript
    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. ^A great site to try and increase knowledge is: Online regex tester and debugger.

History

  • 8th January, 2019: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)