文以Gitee作為演示,Github實現步驟是一樣的,區別在于二者WebHook推送的數據有所區別。
WebHook 功能是幫助用戶 push 代碼后,自動回調一個您設定的 http 地址。 這是一個通用的解決方案,用戶可以自己根據不同的需求,來編寫自己的相關程序。
服務器后臺運行一個PHP cli 腳本,腳本運行時啟動一個SSH客戶端,然后再運行一個Redis客戶端,Redis保持訂閱,接收來自接口的Redis消息發布。
每當Redis接收到發布的推送消息,觸發回調,通過SSH客戶端執行Git命令。
PHP SSH 客戶端
https://nicen.cn/2430.html
HTTP接口接收來自遠程倉庫的更新推送,然后把數據進行處理后推送給異步任務。
<?php
/* 獲取推送的數據 */
$json = file_get_contents("php://input");
$data = json_decode($json, true);
/* 判斷推送是否來自指定的用戶 */
if ($data['user_name'] != "friend-nicen") {
exit("非法請求");
}
/* 處理數據 */
$need = [
"clone" => $data['repository']["clone_url"], //遠程倉庫地址
"path" => $data['repository']["path"], //遠程倉庫名
"branch" => str_replace("refs/heads/", '', $data['ref']) //分支
];
/*通過Redis訂閱,進行任務投遞*/
getRedis()->publish("RECV_GIT", json_encode($need));
exit("正在處理本次更新...");
使用前請先按照https://nicen.cn/2430.html,安裝PHP拓展,您需要準備:
<?php
include_once 'vendor/autoload.php';
/* 引入SSH客戶端 */
use phpseclib3\Net\SSH2;
/*
* 創建redis
* */
function getRedis():Redis
{
$redis = new Redis();
$redis->connect("IP","端口");
$redis->setOption(3, -1);
return $redis;
}
/*鏈接ssh*/
$ssh = new SSH2('localhost', 22);
/*如果登錄失敗*/
if (!$ssh->login('root', '您的SSH密碼')) {
return;
}
/*
* SSH讀取消息
* */
$read = function () use ($ssh) {
/*拼接*/
$raw = "";
/*讀取空白次數*/
$blank = 0;
/*
* 循環讀取
* */
while (true) {
$msg = $ssh->read('username@username:~$');
if (!empty($msg)) {
$raw .= $msg;
} else {
/*
* 空白10次,終止,返回
* */
if ($blank > 10) {
break;
} else {
$blank++;
}
}
}
return $raw;
};
/*讀取間隔時間*/
$ssh->setTimeout(0.1);
$redis = getRedis(); //創建redis
/*
* 打開存放倉庫的目錄
* */
$root = "/home/repos/"; //存放倉庫的目錄
$ssh->write("cd " . $root . "\n");
echo $read(); //讀取初始化的消息
/*
* Redis訂閱等待消息
* */
$redis->subscribe(["RECV_GIT"], function ($redis, $chan, $msg) use ($ssh, $read, $root) {
echo "收到消息:" . $msg . "\n"; //收到消息
/*
* 判斷消息內容是否包含分支信息
* */
if (strpos($msg, "branch") === false) return;
try {
$data = json_decode($msg, true); //處理結果
/*
* 判斷倉庫是否存在
* */
$path = $root . $data['path']; //本地倉庫路徑
/*
* 倉庫目錄是否存在
* */
if (!file_exists($path)) {
/*創建*/
mkdir($path);
/*打開倉庫目錄*/
$ssh->write("cd " . $path . "\n");
/*初始化倉庫*/
$ssh->write("git init\n");
echo $read(); //讀取后續的消息
/*添加遠程倉庫*/
$ssh->write("git remote add origin " . $data['clone'] . "\n");
echo $read(); //讀取后續的消息
} else {
/*打開倉庫目錄*/
$ssh->write("cd " . $path . "\n");
}
/*拉取分支*/
$ssh->write("git pull origin " . $data['branch'] . ":" . $data['branch'] . " \n");
echo $read(); //讀取后續的消息
} catch (\Throwable $e) {
echo $e->getMessage() . "\n";
}
});
打開上方Cli腳本所在目錄,運行如下命令:
# 前臺運行
php 文件名.php
# 后臺臺運行
nohup php 文件名.php &
每次腳本運行的日志,會自動寫入到當前目錄的nohup.out文件,作為日志方便觀察同步結果 ;
運行之后,每次倉庫有更新,腳本都會自動同步這一次的更新;本文只是簡單的實現,您完全可以通過這個案例實現更復雜的功能。
erry Chrismas!
寫程序時,我了省時間,省空間。我們對一個變量運行后,不再新建變量存儲運算結果,直接賦值給該變量自身,這樣很容易操作。特別是早期寫PHP代碼,在PHP代碼中混入HTML語法,多行拼接的情況下,就會使用類似如下的方式:
上面代碼取自某國產開源商城代碼。為了拼接查詢語句,$where在不同的case內,拼接不同的查詢條件。
下面我們使用代碼實例,演示一下操作結果:
代碼如下:
/**
* ------------------------------------------------
* 下面是運算和賦值操作
* ------------------------------------------------
*/
$a = 1.542e-4;
$b = pow(2,5);
echo "a = " . $a . ", b = " . $b, "<br/>";
$b += 0.54;
echo "b = " . $b, "<br/>";
$b -= 7.5;
echo "b = " . $b, "<br/>";
$b *= mt_rand(1,10);
echo "b = " . $b, "<br/>";
$b /= 5;
echo "b = " . $b, "<br/>";
$b %= 3;
echo "b = " . $b, "<br/>";
// 自動轉換為字符串
$b .= " - toString";
echo "b = " . $b, "<br/>";
在瀏覽器內訪問 www.array.com/chapter1.5.php 得到如下結果:
注意,最后的 .= 操作符,是字符串連接。PHP將$b自動轉換為字符串,并與右側字符串拼接起來。這里面,有一個數據類型轉換。
一切程序都要進行邏輯判斷,無論是面向過程變成,擬或面向對象編程,條件判斷散落在程序的角角落落,像空氣一樣,如影隨形。
如果,想要把程序內的if...else...語句完全清除,幾乎要使用更為高級的設計模式,以及系統重構。
初學者,本著思路走,先寫一寫if...else...的判斷語句吧。
下面我們演示一下PHP中邏輯操作符。
代碼如下:
/**
* ------------------------------------------------
* 下面是邏輯操作符
* ------------------------------------------------
*/
$a = 65;
$b = 47;
if ($a == $b) echo "a等于b", "<br/>";
if ($a != $b) echo "a不等于b", "<br/>";
if ($a === $b) echo "a全等于b", "<br/>";
if ($a !== $b) echo "a不全等于b", "<br/>";
if ($a <> $b) echo "a不等于b", "<br/>";
if ($a > $b) echo "a大于b", "<br/>";
if ($a < $b) echo "a小于b", "<br/>";
if ($a >= $b) echo "a大于等于b", "<br/>";
if ($a <= $b) echo "a小于等于b", "<br/>";
// 還有一個重量級的,太空船操作符
// 左邊 > 右邊,返回 1
// 左邊 = 右邊,返回 0
// 左邊 < 右邊,返回 -1
switch ($a <=> $b) {
case 1:
echo "a大于b", "<br/>";
break;
case 0:
echo "a等于b", "<br/>";
break;
case -1:
echo "a小于b", "<br/>";
break;
}
大于,小于,等于,不等于,基本很多編程語言相同,沒什么說的。
上面演示的是兩個數據類型相同的整數值的比較,如果不同類型的數據進行比較,PHP會按照約定規則進行數據類型轉換。可參考官網章節。
特別說明的是“太空船操作符”,可以返回孰大孰小,或者等于。上面實例,使用了switch...case...選擇分支結果,進行結果展現。讀者可不必著急,下面一小節,就要將這個了。
同樣地,在瀏覽器訪問 www.array.com/chapter1.5.php ,可得以下結果:
讀者可以根據上面的表達式,依次核對,那個邏輯條件執行了,那個沒執行。
在PHP中發送郵件,通常都是封裝一個php的smtp郵件類來發送郵件。但是PHP底層的socket編程相對于python來說效率是非常低的。CleverCode同時寫過用python寫的爬蟲抓取網頁,和用php寫的爬蟲抓取網頁。發現雖然用了php的curl抓取網頁,但是涉及到超時,多線程同時抓取等等。不得不說python在網絡編程的效率要比PHP好的多。
PHP在發送郵件時候,自己寫的smtp類,發送的效率和速度都比較低。特別是并發發送大量帶有附件報表的郵件的時候。php的效率很低。建議可以使用php調用python的方式來發送郵件。
php的程序和python的文件必須是相同的編碼。如都是gbk編號,或者同時utf-8編碼,否則容易出現亂碼。python發送郵件主要使用了email模塊。這里python文件和php文件都是gbk編碼,發送的郵件標題內容與正文內容也是gbk編碼。
#!/usr/bin/python
# -*- coding:gbk -*-
"""
郵件發送類
"""
# mail.py
#
# Copyright (c) 2014 by http://blog.csdn.net/CleverCode
#
# modification history:
# --------------------
# 2014/8/15, by CleverCode, Create
import threading
import time
import random
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email import Utils, Encoders
import mimetypes
import sys
import smtplib
import socket
import getopt
import os
class SendMail:
def __init__(self,smtpServer,username,password):
"""
smtpServer:smtp服務器,
username:登錄名,
password:登錄密碼
"""
self.smtpServer = smtpServer
self.username = username
self.password = password
def genMsgInfo(self,fromAddress,toAddress,subject,content,fileList,\
subtype = 'plain',charset = 'gb2312'):
"""
組合消息發送包
fromAddress:發件人,
toAddress:收件人,
subject:標題,
content:正文,
fileList:附件,
subtype:plain或者html
charset:編碼
"""
msg = MIMEMultipart()
msg['From'] = fromAddress
msg['To'] = toAddress
msg['Date'] = Utils.formatdate(localtime=1)
msg['Message-ID'] = Utils.make_msgid()
#標題
if subject:
msg['Subject'] = subject
#內容
if content:
body = MIMEText(content,subtype,charset)
msg.attach(body)
#附件
if fileList:
listArr = fileList.split(',')
for item in listArr:
#文件是否存在
if os.path.isfile(item) == False:
continue
att = MIMEText(open(item).read(), 'base64', 'gb2312')
att["Content-Type"] = 'application/octet-stream'
#這里的filename郵件中顯示什么名字
filename = os.path.basename(item)
att["Content-Disposition"] = 'attachment; filename=' + filename
msg.attach(att)
return msg.as_string()
def send(self,fromAddress,toAddress,subject = None,content = None,fileList = None,\
subtype = 'plain',charset = 'gb2312'):
"""
郵件發送函數
fromAddress:發件人,
toAddress:收件人,
subject:標題
content:正文
fileList:附件列表
subtype:plain或者html
charset:編碼
"""
try:
server = smtplib.SMTP(self.smtpServer)
#登錄
try:
server.login(self.username,self.password)
except smtplib.SMTPException,e:
return "ERROR:Authentication failed:",e
#發送郵件
server.sendmail(fromAddress,toAddress.split(',') \
,self.genMsgInfo(fromAddress,toAddress,subject,content,fileList,subtype,charset))
#退出
server.quit()
except (socket.gaierror,socket.error,socket.herror,smtplib.SMTPException),e:
return "ERROR:Your mail send failed!",e
return 'OK'
def usage():
"""
使用幫助
"""
print """Useage:%s [-h] -s <smtpServer> -u <username> -p <password> -f <fromAddress> -t <toAddress> [-S <subject> -c
<content> -F <fileList>]
Mandatory arguments to long options are mandatory for short options too.
-s, --smtpServer= smpt.xxx.com.
-u, --username= Login SMTP server username.
-p, --password= Login SMTP server password.
-f, --fromAddress= Sets the name of the "from" person (i.e., the envelope sender of the mail).
-t, --toAddress= Addressee's address. -t "test@test.com,test1@test.com".
-S, --subject= Mail subject.
-c, --content= Mail message.-c "content, ......."
-F, --fileList= Attachment file name.
-h, --help Help documen.
""" %sys.argv[0]
def start():
"""
"""
try:
options,args = getopt.getopt(sys.argv[1:],"hs:u:p:f:t:S:c:F:","--help --smtpServer= --username= --password= --fromAddress= --toAddress= --subject= --content= --fileList=",)
except getopt.GetoptError:
usage()
sys.exit(2)
return
smtpServer = None
username = None
password = None
fromAddress = None
toAddress = None
subject = None
content = None
fileList = None
#獲取參數
for name,value in options:
if name in ("-h","--help"):
usage()
return
if name in ("-s","--smtpServer"):
smtpServer = value
if name in ("-u","--username"):
username = value
if name in ("-p","--password"):
password = value
if name in ("-f","--fromAddress"):
fromAddress = value
if name in ("-t","--toAddress"):
toAddress = value
if name in ("-S","--subject"):
subject = value
if name in ("-c","--content"):
content = value
if name in ("-F","--fileList"):
fileList = value
if smtpServer == None or username == None or password == None:
print 'smtpServer or username or password can not be empty!'
sys.exit(3)
mail = SendMail(smtpServer,username,password)
ret = mail.send(fromAddress,toAddress,subject,content,fileList)
if ret != 'OK':
print ret
sys.exit(4)
print 'OK'
return 'OK'
if __name__ == '__main__':
start()
輸入以下命令,可以輸出這個程序的使用幫助
# python mail.py --help
這個程序主要是php拼接命令字符串,調用python程序。注意:用程序發送郵件,需要到郵件服務商,開通stmp服務功能。如qq就需要開通smtp功能后,才能用程序發送郵件。開通如下圖。
php調用程序如下:
<?php
/**
* SendMail.php
*
* 發送郵件類
*
* Copyright (c) 2015 by http://blog.csdn.net/CleverCode
*
* modification history:
* --------------------
* 2015/5/18, by CleverCode, Create
*
*/
class SendMail{
/**
* 發送郵件方法
*
* @param string $fromAddress 發件人,'clevercode@qq.com' 或者修改發件人名 'CleverCode<clevercode@qq.com>'
* @param string $toAddress 收件人,多個收件人逗號分隔,'test1@qq.com,test2@qq.com,test3@qq.com....', 或者 'test1<test1@qq.com>,test2<test2@qq.com>,....'
* @param string $subject 標題
* @param string $content 正文
* @param string $fileList 附件,附件必須是絕對路徑,多個附件逗號分隔。'/data/test1.txt,/data/test2.tar.gz,...'
* @return string 成功返回'OK',失敗返回錯誤信息
*/
public static function send($fromAddress, $toAddress, $subject = NULL, $content = NULL, $fileList = NULL){
if (strlen($fromAddress) < 1 || strlen($toAddress) < 1) {
return '$fromAddress or $toAddress can not be empty!';
}
// smtp服務器
$smtpServer = 'smtp.qq.com';
// 登錄用戶
$username = 'clevercode@qq.com';
// 登錄密碼
$password = '123456';
// 拼接命令字符串,實際是調用了/home/CleverCode/mail.py
$cmd = "LANG=C && /usr/bin/python /home/CleverCode/mail.py";
$cmd .= " -s '$smtpServer'";
$cmd .= " -u '$username'";
$cmd .= " -p '$password'";
$cmd .= " -f '$fromAddress'";
$cmd .= " -t '$toAddress'";
if (isset($subject) && $subject != NULL) {
$cmd .= " -S '$subject'";
}
if (isset($content) && $content != NULL) {
$cmd .= " -c '$content'";
}
if (isset($fileList) && $fileList != NULL) {
$cmd .= " -F '$fileList'";
}
// 執行命令
exec($cmd, $out, $status);
if ($status == 0) {
return 'OK';
} else {
return "Error,Send Mail,$fromAddress,$toAddress,$subject,$content,$fileList ";
}
return 'OK';
}
}
壓縮excel成附件,發送郵件。
<?php
/**
* test.php
*
* 壓縮excel成附件,發送郵件
*
* Copyright (c) 2015 http://blog.csdn.net/CleverCode
*
* modification history:
* --------------------
* 2015/5/14, by CleverCode, Create
*
*/
include_once ('SendMail.php');
/*
* 客戶端類
* 讓客戶端和業務邏輯盡可能的分離,降低頁面邏輯和業務邏輯算法的耦合,
* 使業務邏輯的算法更具有可移植性
*/
class Client{
public function main(){
// 發送者
$fromAddress = 'CleverCode<clevercode@qq.com>';
// 接收者
$toAddress = 'all@qq.com';
// 標題
$subject = '這里是標題!';
// 正文
$content = "您好:\r\n";
$content .= " 這里是正文\r\n ";
// excel路徑
$filePath = dirname(__FILE__) . '/excel';
$sdate = date('Y-m-d');
$PreName = 'CleverCode_' . $sdate;
// 文件名
$fileName = $filePath . '/' . $PreName . '.xls';
// 壓縮excel文件
$cmd = "cd $filePath && zip $PreName.zip $PreName.xls";
exec($cmd, $out, $status);
$fileList = $filePath . '/' . $PreName . '.zip';
// 發送郵件(附件為壓縮后的文件)
$ret = SendMail::send($fromAddress, $toAddress, $subject, $content, $fileList);
if ($ret != 'OK') {
return $ret;
}
return 'OK';
}
}
/**
* 程序入口
*/
function start(){
$client = new Client();
$client->main();
}
start();
?>
http://download.csdn.net/detail/clevercode/8711809
版權聲明:
1)原創作品,出自"CleverCode的博客",轉載時請務必注明以下原創地址,否則追究版權法律責任。
2)原創地址:http://blog.csdn.net/clevercode/article/details/45815453(轉載務必注明該地址)。
3)歡迎大家關注我博客更多的精彩內容:http://blog.csdn.net/CleverCode。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。