- Submit RequestReport
- Submit GetReportRequestList
- If report is ready, you should receive a report id in the GeneratedReportId tag
- Submit a GetReport using GeneratedReportId
- A csv report is returned.
2016年11月3日 星期四
Amazon Marketplace service api workflow
2016年10月29日 星期六
Handle async functions in loop
How to loop over arrays/objects with async calls
https://stackoverflow.com/questions/4288759/asynchronous-for-cycle-in-javascript/4288992#4288992
https://stackoverflow.com/questions/4288759/asynchronous-for-cycle-in-javascript/4288992#4288992
2016年8月14日 星期日
PHP7 spaceship operator (組合比較符)
Spaceship operator <=> (組合比較符)
spaceship operator 是用來比較兩個值$a是大於, 等於或小於$b. 當
- $a 大於 $b -> 回傳 1
- $a 等於 $b -> 回傳 0
- $a 小於 $b -> 回傳 -1
2016年8月11日 星期四
PHP7 Null 合併運算符
PHP AES encrypt
Php Aes encrypt 筆記
公司剛好有需求, 要Server端加密資料跟手機交換資料.也就是
- Server端能解密手機端加密出來的資料
- 手機端要能解密Server端加密出來的資料
- 規格是以ECB 128bits 的方式來加密
接下來就是開始試著先能把自己加密出來的再解回去
encrypt.php
<?php
//產生加完時要用到的KEY
$rand_key = random_bits(16);
$string = '1234567890,A,b,c';
echo 'string to encrypted '.$string;
echo "<br>";
$encrypted_str = aes_encrypt($string);
echo 'encrypted string : '.$encrypted_str;
echo "<br>";
echo 'decrypted string : '.aes_decrypt($encrypted_str);
function aes_encrypt($str){
global $rand_key;
$key = $rand_key;
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
//也可以用base64_encode來編碼, 只要注意你用什麼編就要用什麼反編
return bin2hex($encrypted);
}
function aes_decrypt($str){
global $rand_key;
$key = $rand_key;
//先前用16進制編碼的就用16進制反編回去
$str = hex2bin($str);
$str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
return $str;
}
function random_bits($l = 8) {
return substr(md5(uniqid(mt_rand(), true)), 0, $l);
}
?>
實際狀況可能會有需要自己補0, 所以當加/解密的結果不同時, 可能要注意一下是不是補0的問題
2016年8月10日 星期三
HTML5 Server-Sent Event
什麼是Server-Sent Event?
因為有個功能更強大的Websocket Api的存在, 很多人應該都沒聽過Server-Sent Event. 其實它跟 Long polling 是很類似的. 主要的差別在於 Server-Sent Event 的事件是由browser處理的, client 端只需要listen for messages.
Server-Sent Event 比較 Websocket
Websocket雖然功能強大, 但有些情況, 用Server-Sent Event會比較符合效益.例如:
- 像是資訊交換是非雙向, 單純只是client端在接收資料, 而client不需要回傳資料.
- 資源不足. 例如: Server 是虛擬主機, 沒有root的權限或是不能執行特定程式來架起Websocket server.
Server-Sent Event 屬性/方法
屬性/方法 | 說明 |
---|---|
url | EventSource 中指定的 URL |
readystate | EventSource 的狀態 |
CONNECTING | 0 : 代表試著與SERVER連線 |
OPEN | 1 : 連線成功 |
CLOSE | 2 : 關閉連線 |
onopen | 連線成功時會呼叫的事件 |
onmessage | 接收到資料的時候會呼叫的事件 |
error | 發生錯誤時會呼叫的事件, 發生錯誤後不會重試連線 |
close() | 中斷與SERVER的連線, 中斷後不會再重試連線 |
Server-Sent Even 範例
client.html
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="body">
<div class="container">
<header>
<button onclick="stop();">stop</button>
</header>
<main>
<div id="result"></div>
<div id="ce"></div>
</main>
</div>
</div>
<script>
var source = new EventSource("serverside.php");
source.onmessage = function(event) {
console.log('new message, msg type', typeof(event.data));
document.getElementById("result").innerHTML = event.data + "<br>";
};
function stop(){
source.close();
}
</script>
</body>
</html>
serverside.php
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$time = date('r');
echo "data: The server time is: {$time}\n\n";
?>
格式說明:
- serverside 一定要把header定義為 Content-Type: text/event-stream
- 要推送的資料前面一定要加 data:
- 結尾一定要多斷一行
自定義事件
Server-Sent Event可以同時推送不同的資料. 假設, 同一個頁面要更新的資料有好友線上狀態跟新通知, 我們可以在serverside 定義出兩個事件
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "event: myEvent_1\n";
echo "data: my Custom event msg {$time}\n";
echo 'data:' . json_encode(array("username"=> "John123", "status"=> "online")) . "\n\n";
echo "event: myEvent_2\n";
echo "data: my Custom event msg {$time}\n";
echo 'data:' . json_encode(array("title"=> "notification", "msg"=> "You've got mail")) . "\n\n";
?>
然後在client.html用addlistener來監聽這兩個事件
<script>
source.addEventListener('myEvent_1', function(event){
var obj = JSON.parse(event.data.split('\n')[1]);
document.getElementById("ev1").innerHTML += obj.username+" : "+obj.status+"<br>";
}, false)
source.addEventListener('myEvent_2', function(event){
var obj = JSON.parse(event.data.split('\n')[1]);
document.getElementById("ev2").innerHTML += obj.title +" : "+obj.msg+"<br>";
}, false)
</script>
2016年6月6日 星期一
Postgresql 很棒的 NOTIFY 功能
Postgresql 有個在做即時互動很好用的功能叫Notify.
根據Documentation表示, Notify 可以在table 有insert/update/delete 的時候推出一個通知.
那麼來做個小測試吧~
首先, 我們用 pgadmin 的 query 視窗先建立一個測試用的table
最後, 建立一個 trigger 來觸發事件
DB 的部分處理好了之後, 我們來寫一段小程式試試看
然後就在該目錄下cmd 執行 php notify.php, 在還沒有新增任何資料的時候只會看到

然後我們來新增一筆資料吧!
insert into test_table (test_col_1) values ('row1')
這時, 如果有成功的話應該會看到通知被推送出來

Notify 不止可以推送通知, 還可以在通知裡把有變動的資料以JSON格式一起推送. 只不過, 沒記錯的話POSTGRESQL, 要9.1版之後才有內建的JSON函式可以用
根據Documentation表示, Notify 可以在table 有insert/update/delete 的時候推出一個通知.
那麼來做個小測試吧~
首先, 我們用 pgadmin 的 query 視窗先建立一個測試用的table
CREATE TABLE test_table
然後, 建立一個 trigger 觸發後要執行的 function(id serial NOT NULL,test_col_1 character varying)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE OR REPLACE FUNCTION notify_event() RETURNS TRIGGER AS $$ | |
BEGIN | |
// Execute pg_notify(channel, notification) | |
PERFORM pg_notify('table_changed', 'new row added'::text); | |
// Result is ignored since this is an AFTER trigger | |
RETURN NULL; | |
END; | |
$$ LANGUAGE plpgsql; | |
最後, 建立一個 trigger 來觸發事件
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Trigger: table_changed on test_table | |
CREATE TRIGGER table_changed | |
AFTER INSERT | |
ON test_table | |
FOR EACH ROW | |
EXECUTE PROCEDURE notify_event(); |
DB 的部分處理好了之後, 我們來寫一段小程式試試看
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
include '../conn.php'; | |
set_time_limit(0); | |
ob_end_clean(); | |
//開始監聽之前要先向DB下一個 LISTEN table_changed 的 query | |
pg_query($conn, 'LISTEN table_changed;'); | |
while(true){ | |
$notify = pg_get_notify($conn); | |
if (!$notify) { | |
echo json_encode(array('result'=>false, 'data'=>'No messages')).PHP_EOL; | |
ob_flush(); | |
flush(); | |
sleep(1); | |
} else { | |
echo json_encode(array('result'=>true, 'process_id'=>$pid , 'pid' => pg_get_pid($conn), 'data' => $notify)).PHP_EOL; | |
} | |
} |
然後就在該目錄下cmd 執行 php notify.php, 在還沒有新增任何資料的時候只會看到

然後我們來新增一筆資料吧!
insert into test_table (test_col_1) values ('row1')
這時, 如果有成功的話應該會看到通知被推送出來

Notify 不止可以推送通知, 還可以在通知裡把有變動的資料以JSON格式一起推送. 只不過, 沒記錯的話POSTGRESQL, 要9.1版之後才有內建的JSON函式可以用
2016年6月5日 星期日
在XAMPP(WINDOWS)下安裝pthread
安裝前請先用phpinfo檢查3件事:
- 自己的PHP 版本
- Complier 的版本
- 64 or 32 bits
- Thread Safety 是 enabled
如下圖
然後到這裡找相對應的檔案下載回來.
找到相對應的版本下載回來後, 把檔案解壓縮.
把裡面2個檔案
- php_pthreads.dll
- pthreadVC2.dll
找出來, 然後把 php_pthreads.dll copy 到
- /xampp/php/ext 資料夾底下
再來把 pthreadVC2.dll copy 到
- /xampp/php
- /xampp/apache/bin
- windows/system32
這3個資料夾下 再重啟xampp就可以了
2016年5月31日 星期二
如何解決 Jquery.map() 回傳 'prevObject' 的問題
今天在處理一個table的時候出現一個問題,
我用jquery.map()去loop through 所有已勾選的checkbox並吧該勾選的資料放入一個array做為回傳的資料.
結果在看console時發現array 變成
[x,x,x,x,x, prevObject: jQuery.fn.jQuery.init[7], context: ]
根據我在這裡找到的答案是:
當你用jquery.map()的時候, 它回傳的是一個jquery collection而不只是迴圈內你所形成的array. 所以你需要在後面加一個toArray()來存取你要的array.
例.
我用jquery.map()去loop through 所有已勾選的checkbox並吧該勾選的資料放入一個array做為回傳的資料.
結果在看console時發現array 變成
[x,x,x,x,x, prevObject: jQuery.fn.jQuery.init[7], context: ]
根據我在這裡找到的答案是:
當你用jquery.map()的時候, 它回傳的是一個jquery collection而不只是迴圈內你所形成的array. 所以你需要在後面加一個toArray()來存取你要的array.
例.
$(selector).map(function(i,v){
//Do something....
}).toArray();
就可以只取陣列, 而不取多餘的資料了2016年3月25日 星期五
Node JS + Express JS + Passport JS 做一個登入系統 & 多個帳號綁定的範例
教學出處
安裝所需要的模組
- Express
- mongoose
- passport
- passport-facebook
- connect-flash
- bycrypt-nodejs
- morgan
- body-parser
- cookie-parser
- express-session
以上都用npm安裝, 沒接觸過的請自行GOOGLE
建立檔案的結構
- app
------ routes.js //處理路由
-models //所有DB的操作都放在這裡
------ user.js
- config //設定檔都放這
------ auth.js //處理串接社群API的設定(ClientId, Secret, callbackUrl...etc)
------ database.js //設定DB連線的參數
------ passport.js //設定passport的認證策略
- views
------ index.ejs //起始畫面, 選擇登入的方式
------ login.ejs //登入畫面
------ signup.ejs //註冊畫面
------ profile.ejs //登入後顯示個人檔案
- server.js //沒什麼好解釋的, 就是SERVER
Server.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// server.js | |
// 把所需的模組載入 | |
var express = require('express'); | |
var app = express(); | |
var port = process.env.PORT || 8080; | |
var mongoose = require('mongoose'); | |
var passport = require('passport'); | |
var flash = require('connect-flash'); | |
var morgan = require('morgan'); | |
var cookieParser = require('cookie-parser'); | |
var bodyParser = require('body-parser'); | |
var session = require('express-session'); | |
// 設定 =============================================================== | |
var configDB = require('./config/database.js'); | |
mongoose.connect(configDB.url); | |
require('./config/passport')(passport); | |
// 套入 express 的應用程式 | |
app.use(morgan('dev')); // 把每個請求都顯示在 console | |
app.use(cookieParser()); // 認證需要用到 | |
app.use(bodyParser()); // 讀取 html 表格的資料(POST...etc) | |
app.set('view engine', 'ejs'); // 設定 ejs 為套用模版的引擎 | |
// 啟用 passport | |
app.use(session({ secret: 'blablablablablablablablablablabla' })); // session 的加密密鑰 | |
app.use(passport.initialize()); | |
app.use(passport.session()); // 紀錄 session | |
app.use(flash()); // 回饋訊息處理 | |
// 路由 ====================================================================== | |
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport | |
// 啟動server ====================================================================== | |
app.listen(port); | |
console.log('Listening on port ' + port); |
DB設定 config/database.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
依照自己的環境填入URL | |
*/ | |
module.exports = { | |
//協定是mongodb, ip: 你主機的IP位置, port:預設是27017, userDb是資料表名稱, 隨你取 | |
url: 'mongodb://localhost:27017/userDb' | |
} |
路由 app/routes.js
我們用以下的路由來試範
- 首頁 : /
- 登入頁 : /login
- 註冊頁 : /signup
- 處理登入 (POST)
- 處理註冊 (POST)
- 個人檔案: /profile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/routes.js | |
module.exports = function(app, passport) { | |
// 首頁 =============================== | |
app.get('/', function(req, res) { | |
res.render('index.ejs'); // 載入 index.ejs file | |
}); | |
// 登入頁 | |
app.get('/login', function(req, res) { | |
res.render('login.ejs', { message: req.flash('loginMessage') }); | |
}); | |
// 處理登入 | |
app.post('/login', passport.authenticate('local-login', { | |
successRedirect : '/profile', // 成功則導入profile | |
failureRedirect : '/login', // 失敗則返回登入頁 | |
failureFlash : true // 允許 flash 訊息 | |
})); | |
// FACEBOOK 登入路由 ===================== | |
app.get('/auth/facebook', passport.authenticate('facebook')); | |
// 處理登入後的callback url | |
app.get('/auth/facebook/callback', | |
passport.authenticate('facebook', { | |
successRedirect : '/profile', | |
failureRedirect : '/' | |
})); | |
// 登出用 | |
app.get('/logout', function(req, res) { | |
req.logout(); | |
res.redirect('/'); | |
}); | |
// 帳號綁定/聯結其他社群帳號 ============= | |
// 綁定本地帳戶 -------------------------------- | |
app.get('/connect/local', isLoggedIn, function(req, res) { | |
res.render('connect-local.ejs', { message: req.flash('loginMessage') }); | |
}); | |
app.post('/connect/local', isLoggedIn, passport.authenticate('local-connect', { | |
successRedirect : '/profile', // redirect to the secure profile section | |
failureRedirect : '/connect/local', // redirect back to the signup page if there is an error | |
failureFlash : true // allow flash messages | |
})); | |
// facebook 綁定------------------------------- | |
app.get('/connect/facebook', passport.authorize('facebook', { scope : 'email' })); | |
app.get('/connect/facebook/callback', | |
passport.authorize('facebook', { | |
successRedirect : '/profile', | |
failureRedirect : '/' | |
})); | |
// 註冊表單 | |
app.get('/signup', function(req, res) { | |
res.render('signup.ejs', { message: req.flash('signupMessage') }); | |
}); | |
// 處理註冊 | |
app.post('/signup', passport.authenticate('local-signup', { | |
successRedirect : '/profile', // redirect to the secure profile section | |
failureRedirect : '/signup', // redirect back to the signup page if there is an error | |
failureFlash : true // allow flash messages | |
})); | |
// PROFILE ===================== | |
// 需要權限才能造訪的頁面我們就用 isLoggedIn function 來處理 | |
app.get('/profile', isLoggedIn, function(req, res) { | |
res.render('profile.ejs', { | |
user : req.user | |
}); | |
}); | |
// 帳號解除綁定 ============================================================= | |
// 社群帳號, 只移除token以方便日後要重新綁定 | |
// 本地帳號則會移除email & password | |
// 本地帳號 ----------------------------------- | |
app.get('/unlink/local', function(req, res) { | |
var user = req.user; | |
user.local.email = undefined; | |
user.local.password = undefined; | |
user.save(function(err) { | |
res.redirect('/profile'); | |
}); | |
}); | |
// facebook ------------------------------- | |
app.get('/unlink/facebook', function(req, res) { | |
var user = req.user; | |
user.facebook.token = undefined; | |
user.save(function(err) { | |
res.redirect('/profile'); | |
}); | |
}); | |
// 登出 ============================== | |
app.get('/logout', function(req, res) { | |
req.logout(); | |
res.redirect('/'); | |
}); | |
}; | |
// 處理權限 | |
function isLoggedIn(req, res, next) { | |
if (req.isAuthenticated()) | |
return next(); | |
res.redirect('/'); | |
} |
User model
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/models/user.js | |
// 載入需要的東西 | |
var mongoose = require('mongoose'); | |
var bcrypt = require('bcrypt-nodejs'); | |
// 定義欄位 | |
var userSchema = mongoose.Schema({ | |
local : { | |
email : String, | |
password : String, | |
}, | |
facebook : { | |
id : String, | |
token : String, | |
email : String, | |
name : String | |
} | |
}); | |
// methods ====================== | |
// 加密 | |
userSchema.methods.generateHash = function(password) { | |
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); | |
}; | |
// 核對密碼 | |
userSchema.methods.validPassword = function(password) { | |
return bcrypt.compareSync(password, this.local.password); | |
}; | |
module.exports = mongoose.model('User', userSchema); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/models/user.js | |
// 載入需要的東西 | |
var mongoose = require('mongoose'); | |
var bcrypt = require('bcrypt-nodejs'); | |
// 定義欄位 | |
var userSchema = mongoose.Schema({ | |
local : { | |
email : String, | |
password : String, | |
}, | |
facebook : { | |
id : String, | |
token : String, | |
email : String, | |
name : String | |
} | |
}); | |
// methods ====================== | |
// 加密 | |
userSchema.methods.generateHash = function(password) { | |
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); | |
}; | |
// 核對密碼 | |
userSchema.methods.validPassword = function(password) { | |
return bcrypt.compareSync(password, this.local.password); | |
}; | |
module.exports = mongoose.model('User', userSchema); |
Passport.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// config/passport.js | |
var LocalStrategy = require('passport-local').Strategy; | |
var FacebookStrategy = require('passport-facebook').Strategy; | |
var User = require('../models/user'); | |
var configAuth = require('./auth'); | |
module.exports = function(passport) { | |
// passport session setup ================================================== | |
// required for persistent login sessions | |
// passport needs ability to serialize and unserialize users out of session | |
// used to serialize the user for the session | |
passport.serializeUser(function(user, done) { | |
done(null, user.id); | |
}); | |
// used to deserialize the user | |
passport.deserializeUser(function(id, done) { | |
User.findById(id, function(err, user) { | |
done(err, user); | |
}); | |
}); | |
// FACEBOOK ================================================================ | |
passport.use(new FacebookStrategy({ | |
// pull in our app id and secret from our auth.js file | |
clientID : configAuth.facebookAuth.clientID, | |
clientSecret : configAuth.facebookAuth.clientSecret, | |
callbackURL : configAuth.facebookAuth.callbackURL, | |
passReqToCallback : true, | |
profileFields :['id', 'name', 'emails'] | |
}, | |
// 處理 facebook 回傳的 token & 個人資料 | |
function(req, token, refreshToken, profile, done) { | |
process.nextTick(function() { | |
if(!req.user){ | |
// 在 DB 找 facebook id | |
User.findOne({ 'facebook.id' : profile.id }, function(err, user) { | |
if (err) | |
return done(err); | |
if (user) { | |
if (!user.facebook.token) { | |
user.facebook.token = token; | |
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; | |
user.facebook.email = profile.emails[0].value; | |
user.save(function(err) { | |
if (err) | |
throw err; | |
return done(null, user); | |
}); | |
} | |
return done(null, user); // user found, return that user | |
} else { | |
// 如果沒有該USER則新增 | |
var newUser = new User(); | |
// set all of the facebook information in our user model | |
newUser.facebook.id = profile.id; | |
newUser.facebook.token = token; | |
newUser.facebook.name = profile.name.familyName + ' ' + profile.name.givenName; | |
newUser.facebook.email = profile.emails[0].value; | |
newUser.save(function(err) { | |
if (err) | |
throw err; | |
return done(null, newUser); | |
}); | |
} | |
}); | |
}else{ | |
// user already exists and is logged in, we have to link accounts | |
var user = req.user; // pull the user out of the session | |
// update the current users facebook credentials | |
user.facebook.id = profile.id; | |
user.facebook.token = token; | |
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; | |
user.facebook.email = profile.emails[0].value; | |
// save the user | |
user.save(function(err) { | |
if (err) | |
throw err; | |
return done(null, user); | |
}); | |
} | |
}); | |
})); | |
// LOCAL 註冊 ============================================================ | |
passport.use('local-signup', new LocalStrategy({ | |
usernameField : 'email', | |
passwordField : 'password', | |
passReqToCallback : true | |
}, | |
function(req, email, password, done) { | |
process.nextTick(function() { | |
User.findOne({'local.email' : email}, function(err, user) { | |
if (err) | |
return done(err); | |
if (user) { | |
return done(null, false, req.flash('signupMessage', 'That email is already taken.')); | |
} else { | |
var newUser = new User(); | |
newUser.local.email = email; | |
newUser.local.password = newUser.generateHash(password); | |
newUser.save(function(err) { | |
if (err) | |
throw err; | |
return done(null, newUser); | |
}); | |
} | |
}); | |
}); | |
})); | |
// LOCAL 帳號綁定 ============================================================ | |
passport.use('local-connect', new LocalStrategy({ | |
usernameField : 'email', | |
passwordField : 'password', | |
passReqToCallback : true | |
}, | |
function(req, email, password, done) { | |
process.nextTick(function() { | |
User.findOne({$or :[{'local.email' : email}, {'facebook.email' : email}]}, function(err, user) { | |
if (err) | |
return done(err); | |
if (user && user.local.email == email) { | |
return done(null, false, req.flash('signupMessage', 'That email is already taken.')); | |
} else { | |
var connect_user; | |
if(user.facebook.email == email){ | |
connect_user = user; | |
}else{ | |
connect_user = new User(); | |
} | |
connect_user.local.email = email; | |
connect_user.local.password = connect_user.generateHash(password); | |
connect_user.save(function(err) { | |
if (err) throw err; | |
return done(null, connect_user); | |
}); | |
} | |
}); | |
}); | |
})); | |
// ========================================================================= | |
// LOCAL LOGIN ============================================================= | |
// ========================================================================= | |
// we are using named strategies since we have one for login and one for signup | |
// by default, if there was no name, it would just be called 'local' | |
passport.use('local-login', new LocalStrategy({ | |
// by default, local strategy uses username and password, we will override with email | |
usernameField : 'email', | |
passwordField : 'password', | |
passReqToCallback : true // allows us to pass back the entire request to the callback | |
}, | |
function(req, email, password, done) { // callback with email and password from our form | |
// find a user whose email is the same as the forms email | |
// we are checking to see if the user trying to login already exists | |
User.findOne({ 'local.email' : email}, function(err, user) { | |
// if there are any errors, return the error before anything else | |
if (err) | |
return done(err); | |
// if no user is found, return the message | |
if (!user) | |
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash | |
// if the user is found but the password is wrong | |
if (!user.validPassword(password)) | |
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata | |
// all is well, return successful user | |
return done(null, user); | |
}); | |
})); | |
}; |
2016年3月3日 星期四
PHP Ratchet 的第一個範例 Hello World
從上一篇我們已經把 php Ratchet 安裝好了. 現在要來試試教學上的第一個範例 Hello World.
重新產生composer.json檔
下一篇要來講講怎麼用Ratchet 做一個push server
- 在安裝目錄(以我的例子是D:\xampp\htdocs\ws)底下建立一個 src資料夾, 在src裡再建立一個MyApp資料夾(名字沒限制, 這裡以MyApp示範),
然後在MyApp裡建立一個管理訊息的應用程式的檔案Chat.php, 這個程式會聆聽4個事件:
- onOpen - 有新連線的時候會呼叫這個function
- onMessage - 有新訊息的事件
- onClose - 連線關閉的事件
- onError - 連線有錯誤的事件
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters<?php namespace MyApp; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; class Chat implements MessageComponentInterface { protected $clients; public function __construct() { $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { // Store the new connection to send messages to later $this->clients->attach($conn); echo "New connection! ({$conn->resourceId})\n"; } public function onMessage(ConnectionInterface $from, $msg) { $numRecv = count($this->clients) - 1; echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n" , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's'); foreach ($this->clients as $client) { if ($from !== $client) { // The sender is not the receiver, send to each client connected $client->send($msg); } } } public function onClose(ConnectionInterface $conn) { // The connection is closed, remove it, as we can no longer send it messages $this->clients->detach($conn); echo "Connection {$conn->resourceId} has disconnected\n"; } public function onError(ConnectionInterface $conn, \Exception $e) { echo "An error has occurred: {$e->getMessage()}\n"; $conn->close(); } }
- 再來, 我們要建立一個處理訊息往來的server, 在安裝目錄底下建立一個bin資料夾然後再建立一個chat-server.php的檔案
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
<?php use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; use MyApp\Chat; require dirname(__DIR__) . '/vendor/autoload.php'; $server = IoServer::factory( new HttpServer( new WsServer( new Chat() ) ), 8080 ); $server->run(); - 建好chat-server.php之後, 我們在該目錄下用cmd執行
php chat-server.php
來啟用"server". - 啟用後, 我們來看看是否有成功執行:
- 我們在根目錄(xampp是htdocs)建立一個test.html然後把底下的code貼上去
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
<!DOCTYPE html> <html> <head> <title>Ratchet Test</title> <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script> <script> var messages = []; // connect to the socket server var chat_conn = new WebSocket('ws://localhost:8080'); chat_conn.onopen = function(e) { console.log('Connected to server:', chat_conn); } chat_conn.onerror = function(e) { console.log('Error: Could not connect to server.'); } chat_conn.onclose = function(e) { console.log('Connection closed'); } // handle new message received from the socket server chat_conn.onmessage = function(e) { // message is data property of event object var message = JSON.parse(e.data); console.log('message', message); // add to message list var li = '<li>' + message.text + '</li>'; $('.message-list').append(li); } // attach onSubmit handler to the form $(function() { $('.message-form').on('submit', function(e) { // prevent form submission which causes page reload e.preventDefault(); // get the input var input = $(this).find('input'); // get message text from the input var message = { type: 'message', text: input.val() }; // clear the input input.val(''); // send message to server chat_conn.send(JSON.stringify(message)); }); }); </script> </head> <body> <h1>Chat App Using Ratchet</h1> <h2>Messages</h2> <ul class="message-list"></ul> <form class="message-form"> <input type="text" size="40" placeholder="Type your message here" /> <button>Post it!</button> </form> </body> </html> - 然後打開2個瀏覽器各自輸入
localhost/test.html
127.0.0.1/test.html
-
如果沒問題, 輸入訊息後就會看到下面的畫面
{
"autoload": {
"psr-0": {
"MyApp": "src"
}
},
"require": {
"cboden/ratchet": "^0.3.5"
}
}
如果不是, 請依照上面修改, 然後cmd 輸入 composer dumpautoload重新產生composer.json檔
在 XAMPP 上安裝 php Ratchet (Windows OS)
這幾天研究了一下用php Ratchet來建立一個websocket server,
發現他的官網給的教學不是很詳細, 在此寫一篇我在安裝流程.
注2: 請確定你有設定環境變數讓 Windows cmd 命令列可以執行 php 指令,這裡有教學
發現他的官網給的教學不是很詳細, 在此寫一篇我在安裝流程.
- 首先, 要安裝COMPOSER. 對沒用過的人會比較陌生, 不過其實不難, 因為它有WINDOWS版的安裝程式, 下載後點兩下執行就可以了.
- 安裝完後可以開始安裝Ratchet, 用COMPOSER安裝需要幾個步驟:
- 在windows底下, 執行CMD.EXE進入command line模式, 把目錄換到你要安裝的位置(以我的例子是D:\xampp\htdocs\ws)然後輸入
php composer.phar require cboden/ratchet 注1, 注2
- 執行完後, 就要開始正式安裝了, 一樣在cmd裡輸入
php composer.phar install
- 等程式跑完就安裝好了Ratchet, 下一篇我會寫實際使用Ratchet做一個hello world 的範例
- 在windows底下, 執行CMD.EXE進入command line模式, 把目錄換到你要安裝的位置(以我的例子是D:\xampp\htdocs\ws)然後輸入
注2: 請確定你有設定環境變數讓 Windows cmd 命令列可以執行 php 指令,這裡有教學
訂閱:
文章 (Atom)