整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          如何使用PHP+WebHook自動同步Gitee、Github倉庫的代碼到服務器?

          文以Gitee作為演示,Github實現步驟是一樣的,區別在于二者WebHook推送的數據有所區別。

          WebHook

          1.WebHook是什么

          WebHook 功能是幫助用戶 push 代碼后,自動回調一個您設定的 http 地址。 這是一個通用的解決方案,用戶可以自己根據不同的需求,來編寫自己的相關程序。

          實現過程

          1.處理過程

          1. 遠程倉庫接收到Push推送,觸發WebHook向接口推送更新的消息;
          2. 服務器接口接收到推送的更新消息,提取倉庫地址、分支名、倉庫名(倉庫名用于同步多個倉庫時進行區分);
          3. 接口將提取的更新信息,投遞到后臺的異步任務。
          4. 異步任務自動執行Git命令,同步指定倉庫的代碼到對應的目錄。

          2.功能組成

          服務器后臺運行一個PHP cli 腳本,腳本運行時啟動一個SSH客戶端,然后再運行一個Redis客戶端,Redis保持訂閱,接收來自接口的Redis消息發布。

          每當Redis接收到發布的推送消息,觸發回調,通過SSH客戶端執行Git命令。

          PHP SSH 客戶端

          https://nicen.cn/2430.html

          實際代碼

          1.HTTP接口

          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("正在處理本次更新...");
          

          2.PHP Cli腳本

          使用前請先按照https://nicen.cn/2430.html,安裝PHP拓展,您需要準備:

          1. Redis的IP和端口
          2. SSH賬號和密碼
          3. 存放所有倉庫的根目錄
          <?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";
              }
          });
          

          3.運行腳本

          打開上方Cli腳本所在目錄,運行如下命令:

          # 前臺運行
          php 文件名.php  
          
          # 后臺臺運行
          nohup php 文件名.php  &

          每次腳本運行的日志,會自動寫入到當前目錄的nohup.out文件,作為日志方便觀察同步結果 ;

          運行之后,每次倉庫有更新,腳本都會自動同步這一次的更新;本文只是簡單的實現,您完全可以通過這個案例實現更復雜的功能。

          erry Chrismas!

          # 1.7 基本運算并賦值

          寫程序時,我了省時間,省空間。我們對一個變量運行后,不再新建變量存儲運算結果,直接賦值給該變量自身,這樣很容易操作。特別是早期寫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自動轉換為字符串,并與右側字符串拼接起來。這里面,有一個數據類型轉換。

          # 1.8 邏輯判斷操作符

          一切程序都要進行邏輯判斷,無論是面向過程變成,擬或面向對象編程,條件判斷散落在程序的角角落落,像空氣一樣,如影隨形。

          如果,想要把程序內的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的方式來發送郵件。

          2 程序

          2.1 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()
                       
          

          2.2 python程序使用幫助

          輸入以下命令,可以輸出這個程序的使用幫助

          # python mail.py --help

          2.3 php程序

          這個程序主要是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';
              }
          }
          
          

          2.3 使用樣例

          壓縮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();
          
          ?>
          

          2.4 程序源碼下載

          http://download.csdn.net/detail/clevercode/8711809

          版權聲明:

          1)原創作品,出自"CleverCode的博客",轉載時請務必注明以下原創地址,否則追究版權法律責任。

          2)原創地址:http://blog.csdn.net/clevercode/article/details/45815453(轉載務必注明該地址)。

          3)歡迎大家關注我博客更多的精彩內容:http://blog.csdn.net/CleverCode。


          主站蜘蛛池模板: 麻豆亚洲av熟女国产一区二| 一区二区三区精品| 日韩一区二区三区电影在线观看| 日本一区二区三区不卡在线视频| 国产亚洲无线码一区二区| 中日韩精品无码一区二区三区| 国模吧无码一区二区三区| 怡红院AV一区二区三区| 人妻体内射精一区二区三区| 一区二区三区在线观看中文字幕| 人妻AV一区二区三区精品| 国产成人久久精品麻豆一区| 无码一区二区三区在线| 国产精品视频一区二区三区经 | 无码精品人妻一区二区三区免费 | 亚洲AⅤ无码一区二区三区在线| 国产香蕉一区二区精品视频| 久久青草精品一区二区三区| jazzjazz国产精品一区二区| 国产激情一区二区三区在线观看| 国产一区二区成人| 精品亚洲AV无码一区二区三区| 伊人久久精品无码av一区| 国产三级一区二区三区 | 激情爆乳一区二区三区| 色国产在线视频一区| 另类国产精品一区二区| 国产大秀视频在线一区二区 | 中文字幕一区二区三区在线不卡| 国产丝袜无码一区二区视频| 日韩免费视频一区| 国产激情一区二区三区四区| 色天使亚洲综合一区二区| 精品国产一区二区三区香蕉事| 人妻少妇精品视频三区二区一区| 亚洲综合av一区二区三区不卡| 国产电影一区二区| 毛片一区二区三区无码| 成人在线观看一区| 韩国资源视频一区二区三区| 国产福利一区二区|