Hướng dẫn dùng node replacewith trong PHP

Nội dung

  • 1 Cài đặt Composer
  • 2 Cài đặt DNode
  • 3 Sử dụng DNode
    • 3.1 Gọi hàm php từ node.js
    • 3.2 DNode gọi node module khác

Để kết nối giữa nodejs và php, chúng ta sử dụng thư viện DNode ý tưởng giao tiếp giữa nodejs và một ngôn ngữ tạo client không được hỗ trợ mặc định đó là cài đặt một thư viện nhận dạng chung trên node.js server và php client. Khi php phát tín hiệu vào node.js sử dụng thư viện này, lúc này trên server sẽ nhận được dữ liệu truyền đến từ php thông qua thư viện đó.

Cài đặt Composer

Composer là thư viện cho phép bạn cài đặt các thư viện ngoài và giúp chúng ta include vào ứng dụng web một cách dễ dàng, với những thư viện yêu cầu sử dụng phụ thuộc vào thư viện khác nữa, đây là cách mới để bạn cài thư viện bên thứ 3 cung cấp vào website.
Xem chi tiết hướng dẫn cài đặt Composer cho PHP Project.

Cài đặt DNode

Trước tiên, bạn download thư viện dnode-php.
DNode là giao thức cho php, là module DNode sử dụng trong node.js. Một Lời gọi thủ tục từ xa thiết kế cho ngôn ngữ PHP.

DNode cũng là một module được phân phối trên node.js, cho phép php scripts tương tác với server node.js và ngược lại từ node gọi file thực thi lệnh PHP. Xem chi tiết tại blog http://bergie.iki.fi/blog/dnode-make_php_and_node-js_talk_to_each_other/

Giải nén file zip sau khi tải về xong, chép thư mục chứa thư viện DNode src/DNode vào thư mục “src” trong dự án.
Tiếp đến, mở composer.json tìm trong thư mục giải nén và thêm nội dung các trường “require”, “autoload” vào composer.json gốc của folder dự án web.

"require": {
        "php": ">=5.3.0",
        "evenement/evenement": "~1.0",
        "react/socket": "0.3.*"
    },
    "autoload": {
        "psr-0": {
            "DNode": "src"
        }
    }

Ok, tiến hành cài đặt dnode-php sử dụng công cụ composer. Gõ lệnh sau để cập nhật thêm thư viện DNode vào autoload.

$ php composer.phar install


Chúng ta đang chọn cách cài đặt độc lập (standalone) cho thư viện trong trường hợp bằng cách nào đó bạn đã có các files nguồn của thư viện. Tuy nhiên thư viện này đã có trên getcomposer.org, bạn có thể cài DNode bằng cách khai báo vào package dependencies.
"require": {
        "dnode/dnode": "0.2.0",
    },

Tìm phiên bản dnode thích hợp trên https://packagist.org, phiên bản mới nhất hiện tại là 0.2.0
Tuy nhiên, ở manifest composer.json trên còn khai báo thêm 2 thư viện cần được cài đặt nữa là evenement/evenement, react/socket, trước đó bạn đã chạy lệnh cài đặt php composer.phar install cho các thư viện khai báo một lần trong composer.json thì file composer.lock đã tạo ra và bạn không thể sử dụng lệnh này nữa để cài thêm thư viện khác. Nếu chạy lệnh install lần sau bạn sẽ thấy thông báo này.

Lệnh này chỉ sử dụng để cài mới trong lần đầu, lần cài thư viện tiếp theo bạn thay lệnh install bằng update.

php composer.phar update

Lệnh trên có tác dụng cập nhật lại composer.lock hoặc nếu không thích thì tạo mới file composer.lock bằng lệnh cài đặt ban đầu. Để làm điều này bạn xóa đi file composer.json cũ và tạo file mới với lệnh quen thuộc:

php composer.phar install

Kết quả các thư viện vừa thêm còn lại đã được cài đặt.

Sử dụng DNode

Chèn file vendor/autoload.php vào trong code để bắt đầu sử dụng thư viện. Ví dụ đơn giản mình sẽ tạo file demo.php lưu tại thư mục gốc của ứng dụng nằm chung với thư mục vendor và Thêm đoạn code sau.

require 'vendor/autoload.php';

Bạn có thể sử dụng ngay lớp DNode có địa chỉ namespace DNode/DNode bằng cách tạo đối tượng instance cho nó. DNode có nhiều classes được sử dụng để kết nối tới Nodejs server tạo bởi DNode

// Connect to DNode server running in port 7070 and call 
$loop = new React\EventLoop\StreamSelectLoop();	//instance
//create instance
$client = new DNode\DNode($loop);

Chúng ta cần một DNode server, trên môi trường nodejs các bạn sẽ cài đặt thêm module dnode, chúng ta sử dụng module này để tạo server mới mà không phải tạo server từ module nodejs http. Bởi vì module dnode không chạy trên trình duyệt nó chỉ dùng để kết nối giữa các ngôn ngữ lập trình khác nhau ngoài ngôn ngữ chính javascript và node.js vd: PHP, java

Cài đặt module dnode cho ứng dụng nodejs của bạn, nếu chưa có với lệnh cmd sau:

npm install dnode

Chú ý: Phiên bản dnode hiện yêu cầu máy tính của bạn phải có python >=2.5.0 và < 3.0.0 , nếu chưa cài python hoặc python khác phiên bản này thì cài thêm vào máy tính. Ví dụ, python 3.4.1 khi cài module dnode có báo lỗi như sau:

Bạn có thể cài nhiều bản python trên một máy tính. Mặc định python được cài vào ổ c:


Chỉ định phiên bản python trong lệnh cài cho các nodejs module có sử dụng python, ví dụ dnode.
npm install dnode [email protected]">=2.5.0 <3.0.0"

Nếu không được thì thiết lập lại đường dẫn trỏ vào phiên bản python25 trong biến môi trường windows. VD Nếu bạn cài 2 phiên bản python version 2.5 và 2.6 biến PATH có thể khai báo cả 2 bản này.

PATH=c:\python25\2.5;c:\python26

Xóa đi một bản, chỉ để lại python25 để dùng cho việc cài dnode. Trong quá trình cài đặt lại dnode với python25 mình lại gặp lỗi module gyp. Nghĩa là “dnode” cần yêu cầu module “gyp”.

Tiến hành cài gyp có phạm vi global.

npm -g install node-gyp

Cài gyp thành công.


Tôi cài lại dnode trên cmd, git bash và cygwin vẫn thấy lỗi.
———hướng giải quyết:
*khi gõ lệnh: node-gyp rebuild tại thư mục dự án tạo file build/config.gypi , mở lên thấy có dòng:
"python": "c:\\python27\\python.exe",

cài thêm python27 và chuyển sử dụng python27 trong biến môi trường windows, gỡ dnode cũ sử dụng lệnh

npm uninstall dnode

và cài lại dnode bằng lệnh install.

npm install dnode

Ok đã thành công!

Trước đó, mình đã cài bản Microsoft Visual C++ 2010 Redistributable Package (x86) không biết có phải do cái này, cũng có thể chỉ cần cài python27 là xong.
Nếu bạn vẫn chưa biết nguyên nhân lỗi vì sao thì tham khảo thêm hướng dẫn cài node-gyp tại đây: https://github.com/TooTallNate/node-gyp#installation

——————————————-
Tạo dnode server.

var dnode=require('dnode');
var server = dnode({
	......
}).listen(7070);

Mỗi serer sử dụng cổng không được trùng nhau, do chạy chung địa chỉ IP. Hoặc nếu có 2 server trùng port thì code server nodejs phải lưu ở 2 hosting có IP khác nhau. Điều này dễ hiểu.
Xây dựng các hàm vào đối tượng json truyền vào hàm dnode ở trên. Những hàm này được gọi từ xa bởi các client khác nhau trong bài tập này mình hướng dẫn về PHP.

Ok, chúng ta lấy ví dụ gửi mail trong node.js với hỗ trợ của module nodemailer. Trước đây, chúng ta gửi email bằng SMTP riêng với yahoo, gmail sử dụng thư viện nổi tiếng phpmailer, điển hình thư viện này áp dụng cho Gửi email trong wordpress tới người dùng cho khách hàng mua hàng trên website các bạn cần cài đặt plugin SMTP để mới có thể gửi đi được, tuy nhiên thời gian load trang khá lâu vì phải gồng thêm quá trình sử lý của phpmailer.

Nếu muốn nhanh hơn, chỉ có cách sử lý dữ liệu ở server khác website chỉ có nhiệm vụ truyền nội dung email vào server, và mọi việc còn lại do server thực thi.
Tạo hàm gửi email trong server.

/**
 * send email
*/
function sendmail(to,subject,body,cb){
	var nodemailer = require("nodemailer");
	//kết nối vào gmail
	var smtpTransport = nodemailer.createTransport("SMTP",{
	   service: "Gmail",
	   auth: {
		   user: "",
		   pass: "xxxxxxxxx"
	   }
	});
	//gửi mail
	smtpTransport.sendMail({
	   from: "Hoangweb <>", // sender address
	   to: to, // comma separated list of receivers. ie: "Quach Hoang <>"
	   subject: subject, // Subject line
	   html: body // plaintext body
	}, function(error, response){
	   if(error){
		   console.log(error);
		   //callback error
		   if(typeof cb=='function') cb();
	   }else{
		   console.log("Message sent: " + response.message);
	   }
	});
}

Tiếp theo, chúng ta thêm hàm vào dnode. Các hàm chứa trong dnode sẽ được gọi từ xa.

var server = dnode({
	//send mail
	mail:function(to,subject,body,cb){
		sendmail(to,subject,body,cb);
	}
});
server.listen(7070);

Thử nghiệm node.js trên máy tính, chúng ta có địa chỉ server dnode ở trên là localhost:7070
Kết nối vào server này trên php.

$client->connect('localhost',7070, function($remote, $connection)  {
	...
});

Khi kết nối thành công, hàm callback sẽ kích hoạt, bạn có 2 tham số $remote, $connection. Sử dụng $remote để thực hiện lời gọi hàm và bạn có thể các phương thức liên quan đến kết nối vd đóng kết nối: $connection->end().

Nhưng sau lệnh gọi hàm tới dnode server từ php mà chúng ta không hoàn tất bằng cách ngắt kết nối thì sẽ bị lỗi này

Fatal error: Maximum execution time of 30 seconds exceeded ..vendor\react\event-loop\React\EventLoop\StreamSelectLoop.php on line 141

Mặc dù server không bị lỗi, vì lỗi là do phía trang php.

$client->connect('localhost',7070, function($remote, $connection)  {
	$remote->mail('','Tư vấn thiết kế web','Chào hoangweb, mình muốn làm lại website...');
});

Và lưu ý thêm trường hợp sau khi gọi hàm của dnode server từ php mà tại server có gọi lại hàm php từ server thì bạn cũng sẽ cần phải ngắt kết nối để kết thúc lời gọi hàm, không thì code php gặp lỗi timeout load vô hạn (trình duyệt đứng đực luôn). Gọi hàm của dnode trên nodejs chúng ta sử dụng $remote.

$client->connect('localhost',7070, function($remote, $connection)  {
    // Remote is a proxy object that provides us all methods
    $remote->zing(33, function($n) use ($connection) {
        echo "test 1: n = {$n}\n";
			
        // Once we have the result we can close the connection
        $connection->end();
    });
});

Hàm trên server dnode.

var server1 = dnode({
    zing: function (n, cb) { cb(n * 100) },	//dùng callback để gọi lại hàm php

});
server1.listen(7070);

Kết quả:

Khi bạn sử dụng nhiều lời gọi hàm vào server, hãy kiểm tra chắc chắn nếu gọi hàm lại quay trở lại php thì đảm bảo kết nối từ php đến server vẫn được duy trì minh chứng là bạn không được gọi hàm đóng kết nối $connection->end(). Ví dụ sau server sẽ báo lỗi ECONNRESET.

$client->connect('localhost',3700, function($remote, $connection) use ($to,$sub,$msg) {
     $remote->zing(33, function($n) use ($connection) {
        echo "test 1: n = {$n}\n";
			
        // Once we have the result we can close the connection
        $connection->end();
    });
	$connection->end();
});


Vì sau khi kết nối thành công vào server, bạn đã thoát kết nối ngay lập tức, dnode server không thể thực hiện gọi hàm quay trở lại php được nữa.
Hoặc trước khi ngắt kết nối bạn có gọi hàm khác nhưng hàm callback php của hàm zing vẫn còn được gọi từ dnode server. Vì lý do thực hiện kết nối giữa client và server mà đã không còn kết nối giữa chúng đó là nguyên nhân phát sinh lỗi ECONNRESET từ nơi gọi hàm, trong trường hợp này là dnode server.
$client->connect('localhost',3700, function($remote, $connection) use ($to,$sub,$msg) {
     $remote->zing(33, function($n) use ($connection) {
        echo "test 1: n = {$n}\n";
			
        // Once we have the result we can close the connection
        $connection->end();
    });
       $remote->sendMsgtoAll('hello dnode !');
	$connection->end();
});

Giải pháp: Như vậy bạn không được đóng kết nối khi đang trờ hoàn tất gọi hàm php từ server. Xóa lệnh ngắt sau khi gọi hàm sendMsgtoAll.

$client->connect('localhost',3700, function($remote, $connection) use ($to,$sub,$msg) {
     $remote->zing(33, function($n) use ($connection) {
        echo "test 1: n = {$n}\n";
			
        // Once we have the result we can close the connection
        $connection->end();
    });
       $remote->sendMsgtoAll('hello dnode !');
});

Tuy nhiên, các bạn cũng nên lưu tâm và nhớ điều này nhé, phải đóng kết nối sau khi các hàm đã gọi hoàn tất như mình nói ở trên nếu không có lệnh ngắt kết nối cuối cùng code php sẽ hiển thị lỗi: Fatal error: Maximum execution time of 30 seconds exceeded ..vendor\react\event-loop\React\EventLoop\StreamSelectLoop.php on line 141

Bình thường gọi một chiều từ php gọi hàm tới hàm dnode từ xa.

$client->connect('localhost',3700, function($remote, $connection) use ($to,$sub,$msg) {
	$remote->mail('','Tư vấn thiết kế web','Chào hoangweb, mình muốn làm lại website...');
        $connection->end();
});

Gọi hàm php từ node.js

Truyền tham số là nội dung hàm php trực tiếp, trong khi gọi hàm từ xa tới dnode server . Tham số hàm đặc biệt này sẽ đồng bộ với tham số trong khai báo của hàm chạy trên server. Để gọi lại hàm đến php, chỉ bằng cách gọi tham số hàm theo cách sử dụng gọi hàm của ngôn ngữ server là javascript. Ví dụ ở trên, chúng ta thấy biến tham số cb của hàm zing là biến hàm dạng reference và được gọi hàm với tham số như bình thường.

var server1 = dnode({
      zing: function (n, cb) { cb(n * 100) }
});

Từ php bạn truyền trực tiếp nội dung hàm vào tham số của hàm khai báo trong dnode server.

$client->connect('localhost',7070, function($remote, $connection)  {
    // Remote is a proxy object that provides us all methods
    $remote->zing(33, function($n) use ($connection) {
        echo "test 1: n = {$n}\n";
			
        // Once we have the result we can close the connection
        $connection->end();
    });
});

DNode gọi node module khác

Một khi mọi client kết nối vào code server node.js, thì bạn có thể sử dụng các module khác đang chạy trong server. Mình có bài tập thế này, sử dụng php để gửi thông báo cho tất cả những người truy cập vào website. Điều này rất hữu ích khi bạn không muốn sử dụng javascript với socket.io cho ứng dụng web thông thường để làm thực hiện công việc đó.
Thay vào đó tôi sẽ gọi hàm gửi tin nhắn của module socket.io trong server sử dụng php client.
Trước tiên để hoàn thành ứng dụng demo này bạn có thể cần đọc bài viết Tạo ứng dụng socket.io chạy trên website với heroku trước khi tiếp tục.

Chuẩn bị code server index.js:

var http=require('http');
var server=http.createServer(function(req,res){
	res.writeHead(200, {"Content-Type": "text/html"});	
   res.write('noi dung ');	//vi?t vào web
   res.end();	//k?t thúc vi?t
});
//socket
var io=require('socket.io').listen(server.listen(3700));   //dùng cho socket.io javascipt client
//connection event
io.sockets.on('connection',function(socket){
	console.log('new user: '+socket.id);
	socket.on('chat message',function(msg){
		io.sockets.emit('chat message',msg);
		console.log('send message "'+msg+'" from '+socket.id);
	});
	socket.on('disconnect',function(d){
        io.sockets.emit('_disconnect',{message:'user '+socket.id+' disconnected.'});
		console.log('user '+socket.id+' disconnected');
    });
});
console.log('server listen port 3700');

var dnode = require('dnode');

//các hàm du?c g?i t? php
var server1 = dnode({
    zing: function (n, cb) { cb(n * 100) },	//dùng callback d? g?i l?i hàm php
	//send mail
	mail:function(to,subject,body,cb){
		sendmail(to,subject,body,cb);
	},
	sendMsgtoAll:function(msg){
		io.sockets.emit('chat message',msg);
	}
});
server1.listen(7070);
console.log('DNOde server running on port 7070');

Code php tạo client kết nối tới hàm từ xa trên server DNode.
Tạo file index.php với nội dung sau:

connect('localhost',7070, function($remote, $connection) /*use ($to,$sub,$msg)*/ {
    // Remote is a proxy object that provides us all methods
	$remote->sendMsgtoAll('Thông báo dịch vụ thiết kế web giá rẻ tại hoangweb.com');
	$connection->end();
	echo 'Đã gửi tin nhắn thành công !';
});
$loop->run();
}
catch(Exception $e){
	echo $e->getMessage();
}
?>

Chúng ta gửi nội dung cho các trình duyệt kết nối đến server module http có port 3700 đã tạo ở trên. Tạo file index.html để hiển thị tin nhắn.



	Tieu de





Các files đã chuẩn bị xong, giờ khởi động lại file server index.js

Mở 2 trang mô phỏng website của bạn trên trình duyệt chrome và firefox để theo dõi tin nhắn.

Tiếp theo, Tiến hành gửi tin nhắn cho 2 trình duyệt client ở trên có kết nối vào server 3700 bằng cách chạy file index.php


Ngay sau khi chạy file trên trình duyệt, bạn quay trở lại 2 tab đang mở index.html một chuỗi tin nhắn thời gian thực được hiện thị ra bạn không phải nạp lại trang mới thấy. Thời gian thực muôn năm!

.

Trong đoạn code index.html trên, mình còn thêm tính năng gửi tin nhắn trên form, cho hình thức tương tự cách gửi tin nhắn với php như trình bày trên. Nhờ hàm socket.io giúp truyền tin nhắn cho tất cả mọi user kết nối vào nodejs là io.sockets.emit.

Tác giả: hoangweb.com - tư vấn thiết kế web miễn phí.

Để nhận được bài viết mới vui lòng đăng ký kênh kiến thức WordPress từ A-Z ở Form bên dưới. Bạn cũng có thể nhận được sự trợ giúp trên Twitter và Facebook