分类 IT技术 下的文章

江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(三)

本节思路
由于微信小程序的网络请求都必须走HTTPS协议,于是打算用nginx反向代理go的websocket服务,这样在go的服务端,不用处理https,提高了性能。

根据之前的文章:江西小程序-给swoole的websocket server加上ssl
我们已经得到了一个pem文件和一个key文件

配置nginx

vi /etc/nginx/conf.d/yourdomain.conf

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
upstream gowebsocket {
    server 127.0.0.1:8929;
}
server {
    listen       443;
    server_name  yourdomain;

    ssl on;
    ssl_certificate /usr/local/ca/xxxxxxxxxxxxx.pem;
    ssl_certificate_key /usr/local/ca/xxxxxxxxxxxxxx.key;

    #charset koi8-r;
    access_log  /var/log/nginx/log/yourdomain.access.log  main;

    location / {
        proxy_pass http://gowebsocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

重启nginx

江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(二)

本节思路
1、redis的go客户端安装
2、基于redis的set集合,实现房间的概念,一个房间对应一个set集合,集合内保存该房间内用户的唯一标识
我们给每个用户生成了唯一标识uuid(后期接入微信小程序,则可以使用微信用户openid代替),于是set集合大致如下:

房间1
用户A-uuid
用户B-uuid
......
房间2
用户C-uuid
用户D-uuid
......
......

3、用户的uuid,又对应着go服务端里面的一个map

ActiveClients = make(map[string]ClientConn)

该map以用户的uuid为key,在线用户的websocket链接为value
于是在发送消息时,取到redis里某房间内所有的uuid,就可以得到对应的websocket链接,实现房间内的广播
如果限制房间内只有2个用户,则实现了一对一私聊

安装redis的go客户端

go get -u github.com/go-redis/redis

服务器端go代码:

package main
import (
    "golang.org/x/net/websocket"
    "fmt"
    "log"
    "net/http"
    "github.com/go-redis/redis"
    "encoding/json" 
)

var (  
    JSON          = websocket.JSON              // codec for JSON  
    Message       = websocket.Message           // codec for string, []byte  
    ActiveClients = make(map[string]ClientConn) // map containing clients  
    User          = make(map[string]string)
)  

type ClientConn struct {
    websocket *websocket.Conn  
}

type UserMsg struct {
    Room string
    Cmd string
    User string
    Content string
    Uuid string
}

func echoHandler(ws *websocket.Conn) {
    var err error  
    var userMsg UserMsg
    
    for {  

        var data []byte
        if err = websocket.Message.Receive(ws, &data); err != nil {  
            fmt.Println("can't receive")  
            break  
        }

        err = json.Unmarshal(data, &userMsg)  
        fmt.Println(userMsg)

        go wsHandler(ws,userMsg)

    }  

}

func wsHandler(ws *websocket.Conn,userMsg UserMsg) {
    sockCli := ClientConn{ws}
    var err error


    redisClient := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    //登录
    if userMsg.Cmd == "login" {
        fmt.Println("login") 
        //用户列表新增当前用户
        ActiveClients[userMsg.Uuid] = sockCli
        //保存到redis房间set集合内
        redisClient.SAdd(userMsg.Room,userMsg.Uuid)
    //退出
    } else if userMsg.Cmd == "logout" {
        fmt.Println("logout") 

        delete(ActiveClients,userMsg.Uuid)

        redisClient.SRem(userMsg.Room,userMsg.Uuid)

    //发消息
    } else {

        //从redis取房间内的所有用户uuid
        roomSlice := redisClient.SMembers(userMsg.Room)
        //用户uuid保存到一个go切片online 
        online := roomSlice.Val()

        //循环给房间内用户发送消息
        if len(online) != 0 {
            for _, na := range online {  
                if na != "" {  
                    //ActiveClients[na].websocket就是用户对应的websocket链接
                    if err = websocket.Message.Send(ActiveClients[na].websocket, userMsg.User+"说:"+userMsg.Content); err != nil {  
                        log.Println("Could not send message to ", userMsg.User, err.Error())  
                    }  
                }  
            }
        }


    }
}

func main() {
    http.Handle("/echo", websocket.Handler(echoHandler))
    http.Handle("/", http.FileServer(http.Dir(".")))

    err := http.ListenAndServe(":8929", nil)

    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}

为了生成唯一uuid,选择房间号和用户名,客户端代码由php实现(chat.php):

<?php
$room = $_GET["room"];
$username = $_GET["username"];
$uuid = md5(time().$username);
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Sample of websocket with golang</title>
    <script src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    <script>
      $(function() {
        var userAgent = navigator.userAgent;

        var ws = new WebSocket("ws://xxx.xx.xxx.xx:8929/echo");
        ws.onopen = function (evt) {
          //连接成功
          console.log("Connected to WebSocket server.");

          //发送登录信息
          msg = new Object();
          msg.Room = '<?php echo $room;?>';
          msg.Cmd = 'login';
          msg.User = '<?php echo $username;?>';
          msg.Uuid = '<?php echo $uuid;?>';
          ws.send(JSON.stringify(msg));
        };

        ws.onclose = function (evt) {
          
          console.log("Close WebSocket server.");

          //发送退出信息
          msg = new Object();
          msg.Room = '<?php echo $room;?>';
          msg.Cmd = 'logout';
          msg.User = '<?php echo $username;?>';
          msg.Uuid = '<?php echo $uuid;?>';
          ws.send(JSON.stringify(msg));
        };

        ws.onmessage = function(e) {
          $('<li>').text(event.data).appendTo($ul);
        };
        var $ul = $('#msg-list');
        $('#sendBtn').click(function(){

          //发送信息
          msg = new Object();
          msg.Room = '<?php echo $room;?>';
          msg.Cmd = 'send';
          msg.User = '<?php echo $username;?>';
          msg.Uuid = '<?php echo $uuid;?>';
          msg.Content = $('#name').val();
          ws.send(JSON.stringify(msg));

        });
      });
    </script>
</head>
<body>
<input id="name" type="text"/>
<input type="button" id="sendBtn" value="send"/>
<ul id="msg-list"></ul>
</body>
</html>

两个不同浏览器下,分别访问http://yourdomain/chat.php?room=1&username=keyunq1
http://yourdomain/chat.php?room=1&username=keyunq2
则可实现一对一私聊。
不完善之处:对于redis里房间的set集合,程序没有实现删除元素功能,如某用户的websocket链接关闭,set集合里未删除该用户的uuid,则发消息时,继续从set集合里取数据循环,则会发送数据到一个不存在的websocket,产生错误,此处待完善。

江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(一)

本文环境基于centOS6.5

安装go1.8

下载go安装包,解压到/usr/local/,

cd /usr/local/src
wget http://golangtc.com/static/go/1.8/go1.8.linux-amd64.tar.gz
tar -zxvf go1.8.linux-amd64.tar.gz
mv go ../

设置环境变量,GOROOT和GOPATH

GOROOT就是go的安装路径

GOPATH
go install/go get和 go的工具等会用到GOPATH环境变量.
GOPATH是作为编译后二进制的存放目的地和import包时的搜索路径 (其实也是你的工作目录, 你可以在src下创建你自己的go源文件, 然后开始工作)。
GOPATH之下主要包含三个目录: bin、pkg、src
bin目录主要存放可执行文件; pkg目录存放编译好的库文件, 主要是*.a文件; src目录下主要存放go的源文件
不要把GOPATH设置成go的安装路径,

GOPATH可以是一个目录列表, go get下载的第三方库, 一般都会下载到列表的第一个目录里面
需要把GOPATH中的可执行目录也配置到环境变量中, 否则你自行下载的第三方go工具就无法使用了, 操作如下:
export $PATH:$GOPATH/bin

在/var/下新建go-project作为工作目录,

综合以上,做如下操作:

vi /etc/profile

GOROOT=/usr/local/go

export GOROOT

PATH=$PATH:$GOROOT/bin

GOPATH=/var/go-project

PATH=$PATH:$GOPATH/bin

export PATH

source /etc/profile
go version
go version go1.8 linux/amd64

测试是否安装成功

新建一个hello.go文件

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

通过 go 工具运行它

go run hello.go
hello, world

看到了“hello, world”信息,那么Go已被正确安装。

下载官方websocket包

使用的golang官方的net包下面的websocket,地址:
https://github.com/golang/net

go get github.com/golang/net

其中有一些注意事项:

Golang 官方的子仓库,应该使用 import "golang.org/x/net/websocket" 这种方式导入,通过 go
get 方式下载。如果使用 git clone,clone下来的目录,也应该使用
src/golang.org/x/net/websocket 这样的目录,所以做法就特别蛋疼

要先

go get github.com/golang/net/websocket

然后

mv github.com/golang/net/websocket golang.org/x/net/websocket

然后import的时候用 golang.org/x/net/websocket

特别的蛋疼,难道是不想人用github上面的?因为上面的不是稳定版本

于是,将/var/go-project/src/github.com/golang/net重命名成了/var/go-project/src/golang.org/x/net

然后,go install golang.org/x/net/websocket

简单实现websocket广播的server端代码(server.go):

package main
import (
    "golang.org/x/net/websocket"
    "fmt"
    "log"
    "net/http"
)

var (  
    JSON          = websocket.JSON              // codec for JSON  
    Message       = websocket.Message           // codec for string, []byte  
    ActiveClients = make(map[ClientConn]string) // map containing clients  
    User          = make(map[string]string)  
)  

type ClientConn struct {  
    websocket *websocket.Conn  
    clientIP string  
}

func echoHandler(ws *websocket.Conn) {
    var err error  
    for {  
        var reply string  
        if err = websocket.Message.Receive(ws, &reply); err != nil {  
            fmt.Println("can't receive")  
            break  
        }  

        client := ws.Request().RemoteAddr  
        fmt.Println("Client connected:", client) 

        sockCli := ClientConn{ws, client}  
        ActiveClients[sockCli] = client 
        fmt.Println("Number of clients connected:", len(ActiveClients))

        if ActiveClients[sockCli] != "" {  
            for cs, na := range ActiveClients {  
                if na != "" {  
                    if err = websocket.Message.Send(cs.websocket, reply); err != nil {  
                        log.Println("Could not send message to ", cs.clientIP, err.Error())  
                    }  
                }  
            }  
        }
    }  

}

func main() {
    http.Handle("/echo", websocket.Handler(echoHandler))
    http.Handle("/", http.FileServer(http.Dir(".")))

    err := http.ListenAndServe(":8080", nil)

    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}

运行服务端代码

go run server.go

客户端html代码,置于nginx下,通过域名访问,打开两个浏览器,可以实现简单聊天功能

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Sample of websocket with golang</title>
    <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>

    <script>
      $(function() {
        var ws = new WebSocket("ws://xxx.xx.xxx.xx:8080/echo");
        ws.onmessage = function(e) {
          $('<li>').text(event.data).appendTo($ul);
        };
        var $ul = $('#msg-list');
        $('#sendBtn').click(function(){
          var data = $('#name').val();
          ws.send(data);
        });
      });
    </script>
</head>
<body>
<input id="name" type="text"/>
<input type="button" id="sendBtn" value="send"/>
<ul id="msg-list"></ul>
</body>
</html>

江西小程序-微信小程序中远程调用接口及使用md5加密

背景:使用微信小程序调用https接口,md5加密当前时间戳,然后再md5(时间戳的md5+约定好的key)生成token,来实现简单校验。服务端接口,判断时间戳是否在有效时间内,然后生成token对比传过来的token。

微信小程序使用的开发语言是js,网上找到js实现md5加密的代码,融合进小程序内使用

在utils目录下新建md5.js文件

function safeAdd (x, y) {
  var lsw = (x & 0xFFFF) + (y & 0xFFFF)
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
  return (msw << 16) | (lsw & 0xFFFF)
}

/*
* Bitwise rotate a 32-bit number to the left.
*/
function bitRotateLeft (num, cnt) {
  return (num << cnt) | (num >>> (32 - cnt))
}

/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5cmn (q, a, b, x, s, t) {
  return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
}
function md5ff (a, b, c, d, x, s, t) {
  return md5cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function md5gg (a, b, c, d, x, s, t) {
  return md5cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function md5hh (a, b, c, d, x, s, t) {
  return md5cmn(b ^ c ^ d, a, b, x, s, t)
}
function md5ii (a, b, c, d, x, s, t) {
  return md5cmn(c ^ (b | (~d)), a, b, x, s, t)
}

/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binlMD5 (x, len) {
  /* append padding */
  x[len >> 5] |= 0x80 << (len % 32)
  x[(((len + 64) >>> 9) << 4) + 14] = len

  var i
  var olda
  var oldb
  var oldc
  var oldd
  var a = 1732584193
  var b = -271733879
  var c = -1732584194
  var d = 271733878

  for (i = 0; i < x.length; i += 16) {
    olda = a
    oldb = b
    oldc = c
    oldd = d

    a = md5ff(a, b, c, d, x[i], 7, -680876936)
    d = md5ff(d, a, b, c, x[i + 1], 12, -389564586)
    c = md5ff(c, d, a, b, x[i + 2], 17, 606105819)
    b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330)
    a = md5ff(a, b, c, d, x[i + 4], 7, -176418897)
    d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426)
    c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341)
    b = md5ff(b, c, d, a, x[i + 7], 22, -45705983)
    a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416)
    d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417)
    c = md5ff(c, d, a, b, x[i + 10], 17, -42063)
    b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162)
    a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682)
    d = md5ff(d, a, b, c, x[i + 13], 12, -40341101)
    c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290)
    b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329)

    a = md5gg(a, b, c, d, x[i + 1], 5, -165796510)
    d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632)
    c = md5gg(c, d, a, b, x[i + 11], 14, 643717713)
    b = md5gg(b, c, d, a, x[i], 20, -373897302)
    a = md5gg(a, b, c, d, x[i + 5], 5, -701558691)
    d = md5gg(d, a, b, c, x[i + 10], 9, 38016083)
    c = md5gg(c, d, a, b, x[i + 15], 14, -660478335)
    b = md5gg(b, c, d, a, x[i + 4], 20, -405537848)
    a = md5gg(a, b, c, d, x[i + 9], 5, 568446438)
    d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690)
    c = md5gg(c, d, a, b, x[i + 3], 14, -187363961)
    b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501)
    a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467)
    d = md5gg(d, a, b, c, x[i + 2], 9, -51403784)
    c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473)
    b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734)

    a = md5hh(a, b, c, d, x[i + 5], 4, -378558)
    d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463)
    c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562)
    b = md5hh(b, c, d, a, x[i + 14], 23, -35309556)
    a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060)
    d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353)
    c = md5hh(c, d, a, b, x[i + 7], 16, -155497632)
    b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640)
    a = md5hh(a, b, c, d, x[i + 13], 4, 681279174)
    d = md5hh(d, a, b, c, x[i], 11, -358537222)
    c = md5hh(c, d, a, b, x[i + 3], 16, -722521979)
    b = md5hh(b, c, d, a, x[i + 6], 23, 76029189)
    a = md5hh(a, b, c, d, x[i + 9], 4, -640364487)
    d = md5hh(d, a, b, c, x[i + 12], 11, -421815835)
    c = md5hh(c, d, a, b, x[i + 15], 16, 530742520)
    b = md5hh(b, c, d, a, x[i + 2], 23, -995338651)

    a = md5ii(a, b, c, d, x[i], 6, -198630844)
    d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415)
    c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905)
    b = md5ii(b, c, d, a, x[i + 5], 21, -57434055)
    a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571)
    d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606)
    c = md5ii(c, d, a, b, x[i + 10], 15, -1051523)
    b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799)
    a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359)
    d = md5ii(d, a, b, c, x[i + 15], 10, -30611744)
    c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380)
    b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649)
    a = md5ii(a, b, c, d, x[i + 4], 6, -145523070)
    d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379)
    c = md5ii(c, d, a, b, x[i + 2], 15, 718787259)
    b = md5ii(b, c, d, a, x[i + 9], 21, -343485551)

    a = safeAdd(a, olda)
    b = safeAdd(b, oldb)
    c = safeAdd(c, oldc)
    d = safeAdd(d, oldd)
  }
  return [a, b, c, d]
}

/*
* Convert an array of little-endian words to a string
*/
function binl2rstr (input) {
  var i
  var output = ''
  var length32 = input.length * 32
  for (i = 0; i < length32; i += 8) {
    output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF)
  }
  return output
}

/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl (input) {
  var i
  var output = []
  output[(input.length >> 2) - 1] = undefined
  for (i = 0; i < output.length; i += 1) {
    output[i] = 0
  }
  var length8 = input.length * 8
  for (i = 0; i < length8; i += 8) {
    output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32)
  }
  return output
}

/*
* Calculate the MD5 of a raw string
*/
function rstrMD5 (s) {
  return binl2rstr(binlMD5(rstr2binl(s), s.length * 8))
}

/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstrHMACMD5 (key, data) {
  var i
  var bkey = rstr2binl(key)
  var ipad = []
  var opad = []
  var hash
  ipad[15] = opad[15] = undefined
  if (bkey.length > 16) {
    bkey = binlMD5(bkey, key.length * 8)
  }
  for (i = 0; i < 16; i += 1) {
    ipad[i] = bkey[i] ^ 0x36363636
    opad[i] = bkey[i] ^ 0x5C5C5C5C
  }
  hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8)
  return binl2rstr(binlMD5(opad.concat(hash), 512 + 128))
}

/*
* Convert a raw string to a hex string
*/
function rstr2hex (input) {
  var hexTab = '0123456789abcdef'
  var output = ''
  var x
  var i
  for (i = 0; i < input.length; i += 1) {
    x = input.charCodeAt(i)
    output += hexTab.charAt((x >>> 4) & 0x0F) +
    hexTab.charAt(x & 0x0F)
  }
  return output
}

/*
* Encode a string as utf-8
*/
function str2rstrUTF8 (input) {
  return unescape(encodeURIComponent(input))
}

/*
* Take string arguments and return either raw or hex encoded strings
*/
function rawMD5 (s) {
  return rstrMD5(str2rstrUTF8(s))
}
function hexMD5 (s) {
  return rstr2hex(rawMD5(s))
}
function rawHMACMD5 (k, d) {
  return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d))
}
function hexHMACMD5 (k, d) {
  return rstr2hex(rawHMACMD5(k, d))
}

function md5 (string, key, raw) {
  if (!key) {
    if (!raw) {
      return hexMD5(string)
    }
    return rawMD5(string)
  }
  if (!raw) {
    return hexHMACMD5(key, string)
  }
  return rawHMACMD5(key, string)
}

module.exports.md5 = md5

根据微信小程序文档

模块化 我们可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者
exports 才能对外暴露接口。

最后通过 module.exports.md5 = md5 将方法暴露出来,然后通过以下方式调用md5方法

var utilMd5 = require("../../utils/md5.js");
var d=new Date();
var callTime = parseInt(d.getTime()/1000);
console.log(utilMd5.md5(callTime));

完整的调用api接口示例如下:
如pages/index/index.js

var utilMd5 = require("../../utils/md5.js")

var app = getApp()
Page({
  data: {
    matchList: {},
    userInfo: {}
  },
  onLoad: function () {
    var d=new Date()
    var callTime = parseInt(d.getTime()/1000)
    var token = utilMd5.md5(utilMd5.md5(callTime)+"key")
    wx.request({
      url: 'https://api.xxxxx.com/xxxxxx',
      data: {},
      header: {
          'content-type': 'application/x-www-form-urlencoded',
          'callTime':callTime,
          'token':token
      },
      success: function(res) {
        console.log(res.data)
      },
      complete: function(res) {
        console.log(res.message)
      }
    })
  }
})

PHP 标准规范-PSR学习

PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。

PHP FIG,FIG 是 Framework Interoperability Group(框架可互用性小组)的缩写,由几位开源框架的开发者成立于 2009 年,从那开始也选取了很多其他成员进来(包括但不限于 Laravel, Joomla, Drupal, Composer, Phalcon, Slim, Symfony, Zend Framework 等),虽然不是「官方」组织,但也代表了大部分的 PHP 社区。

项目的目的在于:通过框架作者或者框架的代表之间讨论,以最低程度的限制,制定一个协作标准,各个框架遵循统一的编码规范,避免各家自行发展的风格阻碍了 PHP 的发展,解决这个程序设计师由来已久的困扰。

目前已表决通过了 6 套标准,已经得到大部分 PHP 框架的支持和认可。

PSR-1 基础编码规范

本篇规范制定了代码基本元素的相关标准,以确保共享的PHP代码间具有较高程度的技术互通性。

PSR-1详情介绍

PSR-2 编码风格规范

本篇规范是 [PSR-1][] 基本代码规范的继承与扩展。

本规范希望通过制定一系列规范化PHP代码的规则,以减少在浏览不同作者的代码时,因代码风格的不同而造成不便。

当多名程序员在多个项目中合作时,就需要一个共同的编码规范, 而本文中的风格规范源自于多个不同项目代码风格的共同特性, 因此,本规范的价值在于我们都遵循这个编码风格,而不是在于它本身。

PSR-2详情介绍

PSR-3 日志接口规范

本文制定了日志类库的通用接口规范。

本规范的主要目的,是为了让日志类库以简单通用的方式,通过接收一个 Psr\Log\LoggerInterface 对象,来记录日志信息。 框架以及CMS内容管理系统如有需要,可以 对此接口进行扩展,但需遵循本规范, 这才能保证在使用第三方的类库文件时,日志接口仍能正常对接。

PSR-3详情介绍

PSR-4 自动加载规范

本 PSR 是关于由文件路径 自动载入 对应类的相关规范, 本规范是可互操作的,可以作为任一自动载入规范的补充,其中包括 PSR-0,此外, 本 PSR 还包括自动载入的类对应的文件存放路径规范。

PSR-4详情介绍

PSR-6 缓存接口规范

介绍#

缓存是提升应用性能的常用手段,为框架中最通用的功能,每个框架也都推出专属的、功能多 样的缓存库。这些差别使得开发人员不得不学习多种系统,而很多可能是他们并不需要的功能。 此外,缓存库的开发者同样面临着一个窘境,是只支持有限数量的几个框架还是创建一堆庞 大的适配器类。

一个通用的缓存系统接口可以解决掉这些问题。库和框架的开发人员能够知道缓存系统会按照他们所 预期的方式工作,缓存系统的开发人员只需要实现单一的接口,而不用去开发各种各样的适配器。

目标#

本 PSR 的目标是:创建一套通用的接口规范,能够让开发人员整合到现有框架和系统,而不需要去 开发框架专属的适配器类。

PSR-6详情介绍

PSR-7 HTTP 消息接口规范

此文档描述了 RFC 7230 和 RFC 7231 HTTP 消息传递的接口,还有 RFC 3986 里对 HTTP 消息的 URIs 使用。

HTTP 消息是 Web 技术发展的基础。浏览器或 HTTP 客户端如 curl 生成发送 HTTP 请求消息到 Web 服务器,Web 服务器响应 HTTP 请求。服务端的代码接受 HTTP 请求消息后返回 HTTP 响应消息。

通常 HTTP 消息对于终端用户来说是不可见的,但是作为 Web 开发者,我们需要知道 HTTP 机制,如何发起、构建、取用还有操纵 HTTP 消息,知道这些原理,以助我们刚好的完成开发任务,无论这个任务是发起一个 HTTP 请求,或者处理传入的请求。

PSR-7详情介绍