[php]通过单例模式看self,static,$this的区别

单例模式比较简单,下面是一种写法:

    public static function getInstance() {
        static $i = null;  //注:声明时不能是函数或者计算
        (is_null($i)) && ($i = new static());
        return $i;
    }

这里想说new static() 和 self()的区别
官方资料这样写:
self refers to the same class in which the new keyword is actually written.
static, in PHP 5.3’s late static bindings, refers to whatever class in the hierarchy you called the method on.
self是当前类,而static取决你调用方法的层次
例子如下:

class A {
    public static function className(){
        echo __CLASS__;
    }

    public static function test(){
        self::className();
    }
}

class B extends A{
    public static function className(){
        echo __CLASS__;
    }
}

B::test();  //A
class A {
    public static function className(){
        echo __CLASS__;
    }

    public static function test(){
        static::className();
    }
}

class B extends A{
    public static function className(){
        echo __CLASS__;
    }
}
B::test(); //B

对于$this 和self
this是指向当前对象的指针,self是指向当前类的指针
也有网友这样总结:
Use $this to refer to the current object. Use self to refer to the current class. In other words, use $this->member for non-static members, use self::$member for static members.
只要注意在静态函数或者静态变量上的使用,就能应对一般的问题

[php]Yii框架下的curl component

php的curl用法网上介绍的很多了,这里整理一下自己的写法

<?php

class EApi extends CComponent {

    const ERROR_INVALID_TOKEN   = 20001;
    const ERROR_MISSING_PARAM   = 20002;
    const ERROR_INVALID_PARAM   = 20003;
    const ERROR_EMPTY_RESPONSE  = 20004;

    const CACHE_DURATION = 3600;

    public $accessToken;
    public $connectTimeout = 1;
    public $timeout = 5;

    protected $_currentUrlId;
    protected $_urls;
    protected $_ch;

    public function init() {
        //非必须
        $ua = "api/0.1"; defined("APP_MODE") && $ua .= "-".APP_MODE;

        $opt = array(
            CURLOPT_RETURNTRANSFER  => TRUE,         // return web page
            CURLOPT_HEADER          => FALSE,        // don't return headers
            CURLOPT_FOLLOWLOCATION  => FALSE,        // follow redirects
            CURLOPT_ENCODING        => "",           // handle all encodings
            CURLOPT_USERAGENT       => $ua,
            CURLOPT_AUTOREFERER     => TRUE,         // set referer on redirect
            CURLOPT_CONNECTTIMEOUT  => $this->connectTimeout,   // timeout on connect
            CURLOPT_TIMEOUT         => $this->timeout,          // timeout on response
            CURLOPT_SSL_VERIFYHOST  => 0,            // don't verify ssl
            CURLOPT_SSL_VERIFYPEER  => FALSE,        //
            CURLOPT_VERBOSE         => FALSE,        //
        );

        $this->_ch = curl_init();
        curl_setopt_array($this->_ch, $opt);
    }

    public function getApiUrl() {
        if (empty($this->_urls)) {
            return null;
        }
        if ($this->_currentUrlId === null) {
            $this->_currentUrlId = array_rand($this->_urls);
        }
        return $this->_urls[$this->_currentUrlId];
    }

    public function clearApiUrl() {
        unset($this->_urls[$this->_currentUrlId]);
        unset($this->_currentUrlId);
    }

    public function setUrls($urls) {
        $this->_urls = array();
        foreach ($urls as $url) {
            if ("http" != parse_url($url, PHP_URL_SCHEME)) {
                Yii::warning("lianjia-feed server is wrong", $url, "application.linajia-feed");
                continue;
            }
            $this->_urls[] = $url;
        }
    }

    public function query($name, array $params=array(), $post = array()) {

        $urlPrefix = $this->getApiUrl();
        if (empty($urlPrefix)) {
            Yii::warning("no valid lianjia-feed server url exists", null, "application.lianjia-feed");
            return false;
        }
        $query = http_build_query($params);
        $url = $urlPrefix . $name . (strpos($urlPrefix . $name, "?") ? "&" : "?") . $query;
        
        $cacheKey = sprintf("%s:%s?%s", "key", $name, $query);
        $ret = Yii::app()->cache->get($cacheKey);
        if ($ret) {
            return $ret;
        }
//         var_dump($url);
//         die();
        $ch = $this->_ch;
        curl_setopt($ch, CURLOPT_URL, $url);
        if($post) {
            $post = http_build_query($post);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
        }
        $raw = curl_exec($ch);
        $errno = curl_errno($ch);

        if ($errno == CURLE_COULDNT_CONNECT) {
            Yii::warning("Couldn't connect server.", $this->_urls[$this->_currentUrlId]);
            unset($this->_urls[$this->_currentUrlId]);
            $this->_currentUrlId = null;
            return $this->query($name, $params);
        }

        if ($errno != CURLE_OK) {
            $errstr = curl_error($ch);
            Yii::fatal("HTTP Error: cURL[{$errno}] {$errstr}", null, "application.lianjia-feed");
            return false;
        }

        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        Yii::info(sprintf("HTTP recieved %.1fKB in %.1fms ",
            curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD)/1024,
            curl_getinfo($ch, CURLINFO_TOTAL_TIME)*1000), null, "application.lianjia-feed");

        if ($code != 200) {
            Yii::fatal("HTTP Error: HTTP[{$code}]", null, "application.lianjia-feed");
            Yii::debug("HTTP Error Response:", $raw, "application.lianjia-feed");
            return false;
        }

        if (empty($raw)) {
            Yii::fatal("HTTP Error: Empty response", null, "application.lianjia-feed");
            return false;
        }

        $res = json_decode($raw, true);       
        
        Yii::app()->cache->set($cacheKey, $res["data"], self::CACHE_DURATION);
        return $res["data"];
    }
    
    //接口地址及其参数
    public function getApi($get, $post) {
        //第二个,第三个为get及其post参数
        return $this->query("feed/getNotifyNum", array("get" => $get), array("post" => $post));
    }

另外在config中添加

        'api' => array(
            'class' => 'ext.EApi',
            'urls'  => array(
                'http://172.0.0.1:8090/',  //IP地址
            ),     
        ),

使用的时候直接调用

Yii::app()->api->getApi($get, $post);

[php]打印字符a-z的问题

php打印字符a~z,先看个例子

        for($i = 'a'; $i <= 'z'; $i ++) {
            echo ($i)." " ;
        }

好像没什么问题,打印结果却是:
a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba bb …. (省略)yy yz

经过好心人提醒,原来php是用26进制,类似于十进制的a=1,b=2….z=25那么aa=26。在做$i++时候符合这个规则。
而做比较时候,’aa’和’z’比较时,还是按照字符串比较的规则,首字母大的则大,所以’aa’还是小于’z’.最后就是出现以上结果。

下面附上几二种简单的方式:

for($i=ord('a'); $i<ord('z'); $i++) {
    echo chr($i);
}
echo implode(',', range('a', 'z'));

[php]生成验证码API

网站很多时候需要用到验证码,PHP生成验证码也很方便,这里用到了php的GD库,GD库,是php处理图形的扩展库,GD库提供了一系列用来处理图片的API,使用GD库可以处理图片,或者生成图片。

生成验证码的代码如下

    public function actionGetVerifyCode() {
        $width = 80;
        $height = 36;
        $length = 4;
        $codelen = 4;
        //生成验证码
        $charset = 'abcdefghkmnprstuvwxyzABCDEFGHKMNPRSTUVWXYZ23456789';    //随机因子
        $code = substr(str_shuffle($charset), 0, $codelen);
        //绘制图片
        $width = ($length * 10 + 10) > $width ? $length * 10 + 10 : $width;

        $img = imagecreatetruecolor($width, $height);
        $r = array(225, 255, 255, 223);
        $g = array(225, 236, 237, 255);
        $b = array(225, 236, 166, 125);
        $key = mt_rand(0, 3);

        $backColor = imagecolorallocate($img, $r[$key], $g[$key], $b[$key]);    //背景色(随机)
        $borderColor = imagecolorallocate($img, 100, 100, 100);                    //边框色
        imagefilledrectangle($img, 0, 0, $width - 1, $height - 1, $backColor);
        imagerectangle($img, 0, 0, $width - 1, $height - 1, $borderColor);
        $stringColor = imagecolorallocate($img, mt_rand(0, 200), mt_rand(0, 120), mt_rand(0, 120));
        //生成干扰素
        for ($i = 0; $i < 5; $i++) {
            imagearc($img, mt_rand(-10, $width), mt_rand(-10, $height), mt_rand(30, 300), mt_rand(20, 200), 55, 44, $stringColor);
        }
        for ($i = 0; $i < 25; $i++) {
            imagesetpixel($img, mt_rand(0, $width), mt_rand(0, $height), $stringColor);
        }
        for ($i = 0; $i < $length; $i++) {
            imagestring($img, 10, $i * 20 + 5, mt_rand(1, 8), $code{$i}, $stringColor);
        }
        //输出图片
        header("Content-type: image/png");
        setcookie("validReg", md5(strtolower($code)), time() + 600, '/');
        imagepng($img);
        imagedestroy($img);
        //返回验证码
    

使用验证码时,只要取cookie中的validReg即可

        $cookieCode = isset($_COOKIE['validReg']) ? $_COOKIE['validReg'] : "";
        $verifyCode = isset($_POST['verifyCode']) ? $_POST['verifyCode'] : "";
        if ($cookieCode != md5(strtolower($verifyCode))) {
            $this->_processJson(array('errno' => -1, 'error' => '验证码错误'));
            return;
        }

PHP常用的魔术方法

常用的魔术方法有:__Tostring () __Call() __autoLoad() __ clone() __GET() __SET() __isset() __unset()

1.__Tostring() 用于定义输出对象引用时调用 常用于打印一些对象的信息 必须有返回值
eg:有一个persion类
Persion per =new persion()
Echo per; //直接调用会出错
我们可以在类的定义中添加__tostring()方法
Function __Tostring()
{
$str=this->$name.this->age;
Return $str;
}
2.__clone()对象的复制
引用赋值
$per1=$per2; 而这在内存中只有一块地址
而$per1=clone $per2 这时有两块内存地址

3.__call()方法 当调用类实例中不存在的函数时自动执行
如果试图调用类中不存在的函数,会出现语法错误,为了能够友好的提示
我们可以在类中声明Call()方法;
Function __call($funName,$argu)
{
Echo “名为”.$funName.”参数为”.printf($argh).”的函数不存在”,
}
4.__autoLoad 自动加载使用的类文件 该函数是在引用的页面添加
我们都使用过这样情况,在页面中需要调用其他php文件,我们需要使用include方法
但是如果有几十个页面需要引用,未免太过繁琐,我们可以在该页面中使用autoload方法
Function __autoload($className)
{
Include $className.”.php”;
}
这样凡是引用到其他类的地方,都会自动引用该类文件 前提类文件的名称必须是 类名.php
5.__GET() 访问类中私有属性
如果类中的属性设置为私有属性,在类的实例中是无法访问的,但怎样才能访问呢?
我们就可以使用__GET()
Eg :
类中有
Class person
{
Private $name;
Private $age;
}
实例化 person per=new person()
Per->$name; //这样是取不到值的
但是如果我们在类中增加__GET方法
Function __GET($proName)
{
Return this->$proName;
}
我们再次调用Per->$name 就可以访问了
这样做有人会提出疑问了,这样可以直接访问私有变量,和声明成公有的有什么区别呢?
如果声明成公有的,我们可以任意读取,如果是私有,如果我们增加了get方法,那么每次调用私有属性都会调用GET方法的内容,这样我们就可以在get方法中增加一些逻辑处理。
6.__SET()设置类中的私有属性
原理同上,我们可以再类中添加__SET()函数,每当通过调用类实例给私有属性赋值,都会执行__SET函数 ,函数原型:
Function __SET($proName,$value)
{
This->$proName=$value;
}
既然是方法赋值,我们就可以做一些逻辑处理
7.__isset() 判断类中私有属性或方法是否存在时自动调用
首先我们先介绍一下isset 方法,该方法用于判定属性和方法是否存在,但是我们无法通过类类实例判断类中的某个私有属性是否存在
如果我们使用isset(per->$name);//返回值是false,但是$name属性的确存在,怎么解决呢?
解决方法:
1.将$name定义为私有属性
2.
在类定义中添加
Function __isset($proName)
{
Return isset(this->$proName);//再类内部是可以访问私有属性的
}
这样的话我们再次调用isset($name);返回值就为true了;
8.__unset()清除类中私有变量时自动调用
与之结合的是unset() unset方法可以删除属性,当我们需要删除类中属性的时候,如果是公有属性我们可以直接
删除,但是如果是私有我们只通过该方法就无法实现了
怎样实现呢我们可以使用__unset()方法实现该功能我们需要在类中添加
Function __unset($proName)
{
Unset(this->$proName);
}
现在我们再调用unset($name);就可以删除person类中的私有属性$name了

Redis,memcache,apc,apcu,yac介绍和比较

一 Redis和memcache
关于Redis和memcache的比较已经很多了,这里随意摘抄一篇文章:
http://www.open-open.com/lib/view/open1409643182369.html
大概有一下结论:
应该说Memcached和Redis都能很好的满足解决我们的问题,它们性能都很高,总的来说,可以把Redis理解为是对Memcached的拓展,是更加重量级的实现,提供了更多更强大的功能。具体来说:

1.性能上:
性能上都很出色,具体到细节,由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比
Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。

2.内存空间和数据量大小:
MemCached可以修改最大内存,采用LRU算法。Redis增加了VM的特性,突破了物理内存的限制。

3.操作便利上:
MemCached数据结构单一,仅用来缓存数据,而Redis支持更加丰富的数据类型,也可以在服务器端直接对数据进行丰富的操作,这样可以减少网络IO次数和数据体积。

4.可靠性上:
MemCached不支持数据持久化,断电或重启后数据消失,但其稳定性是有保证的。Redis支持数据持久化和数据恢复,允许单点故障,但是同时也会付出性能的代价。

5.应用场景:
Memcached:动态系统中减轻数据库负载,提升性能;做缓存,适合多读少写,大数据量的情况(如人人网大量查询用户信息、好友信息、文章信息等)。
Redis:适用于对读写效率要求都很高,数据处理业务复杂和对安全性要求较高的系统(如新浪微博的计数和微博发布部分系统,对数据安全性、读写要求都很高)。

需要慎重考虑的部分

1.Memcached单个key-value大小有限,一个value最大只支持1MB,而Redis最大支持512MB
2.Memcached只是个内存缓存,对可靠性无要求;而Redis更倾向于内存数据库,因此对对可靠性方面要求比较高
3.从本质上讲,Memcached只是一个单一key-value内存Cache;而Redis则是一个数据结构内存数据库,支持五种数据类型,因此Redis除单纯缓存作用外,还可以处理一些简单的逻辑运算,Redis不仅可以缓存,而且还可以作为数据库用
4.新版本(3.0)的Redis是指集群分布式,也就是说集群本身均衡客户端请求,各个节点可以交流,可拓展行、可维护性更强大。

Redis是数据的持久化,也是一种nosql,它可以把需要的数据提前按照key-value存储好,这样用户取数据时,可以直接从key中取出一系列的数据,避免频繁的查表。而memcache可以把用户频繁取出来的key cache住,这样连redis都不用去取了。

二 apc和memcache

由于PHP的特性,每次执行完页面之后,所有运行中的对象都会被释放,所以APC和Memcached就可以用来在脚本、进程之间共享、缓存数据。

APC是PHP的一个扩展,会加载在PHP的进程中,除了可以将PHP代码解释成OPCode保存在内存中之外,还能在PHP的进程之间使用共享内存(系统内核的数据结构)来保存数据,而且完全透明

而Memcached是一个外部的服务,要通过tcp或udp的网络协议来共享/缓存数据

Memcached好处是可以在多台机器之间共享、缓存数据,或者是与其他非php应用共享数据,但由于使用网络协议进行交互,而且在交互过程中需要对php对象进行序列化、反序列化等,延迟较直接集成在PHP进程中的APC大很多

Facebook同时使用了APC和Memcache作了两层缓存

APC更多时候用作Opcode Cache, 而MemCache是用作CotentCache. APC用作Content Cache的时候, 它是单机Cache. 而MemCache可以实现多机共享. 所以类似一些Session共享问题, 就只能用类似MemCache的缓存.
另外因为设计问题, 如果你使用APC缓存易变的内容, 可能会造成缓存内容不同步.

三 apc和apcu
原来的APC:Alternative PHP Cache,包含了System Cache Entries 、Per-Directory Entries、User Cache Entries三大块。
而现在的APCu:APCu – APC User Cache,只包含一个User Cache Entries。
所以是个精简版

四 yac
关于yac直接看鸟哥的博客:
http://www.laruence.com/2013/03/18/2846.html
具体yac的描述如下:
yac is a shared memory user data cache for PHP. it can be used to replace APC or local memcached.

yac is lockless, that means, it is very fast, but there could be a chance you will get a wrong data(depends on how many key slots are allocated and how many keys are stored), so you’d better make sure that your product is not very sensitive to that.
为PHP提供共享用户数据,可以替代APC或者本地memcached,它的特性是无锁,非常快,但是可能有数据错误,所以推荐使用大键值内存。

[算法]leetcode-02:计算字符串中最大无重复子字符串长度

leetcode中有这样一道算法题:
Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for “abcabcbb” is “abc”, which the length is 3. For “bbbbb” the longest substring is “b”, with the length of 1.
大意是求字符串中的最大无重复字符串,因为leetcode并不支持php,所有这里用C++给出算法,代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> map;
		int len = s.length();
		int start = 0;
		int max   = 0;
		int cur   = 0;
		for(int i=0; i<len; i++) {
		
			char ch = s[i];
			if(map.find(ch) != map.end()) {

				start = start > (map[ch] + 1) ? start : (map[ch] + 1);
			}
			cur = i - start + 1;
			map[ch] = i;
			max = cur > max ? cur : max;

		}
        //cout<<max<<endl;
		return max;
    }
};

背景:
这里用到了C++中的 unordered_map,其中的map.find()函数是查找这个map中是否保存了这个值。结果可以用auto类型保存。
例如:auto res = map.find(‘a’);而res->first 是其中的key, res->second 是value.
如果在php中,随意的一个array类型都可以实现如上效果,通过isset($str[‘a’])也可以判断是否保存了此变量。
算法:
代码比较短,通过一次遍历O(n),查找字符串中这个值是否在map中,如果没在,存入这个map中,计算长度。如果在map中,start位置变为该map值和start的较大者,防止(“abba”)这种情况。然后计算长度。
总结:
改算法题并不是很难,主要考察是否会应用map这种数据结构类型。当然在php代码中,一个array可以代替同样的效果。

CSRF攻击与防御

一.CSRF是什么?

  CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

二.CSRF可以做什么?

  你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

三.CSRF漏洞现状

  CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI……而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。

四.CSRF的原理

  下图简单阐述了CSRF攻击的思想:

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1.登录受信任网站A,并在本地生成Cookie。

  2.在不登出A的情况下,访问危险网站B。

  看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

  1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

  2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了……)

  3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

  理解上面的3种攻击模式,其实可以看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!

五.CSRF的防御

  我总结了一下看到的资料,CSRF的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

  1.服务端进行CSRF防御

  服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。

  (1).Cookie Hashing(所有表单都包含同一个伪随机值):

  这可能是最简单的解决方案了,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了:>

  
  在表单里增加Hash值,以认证这确实是用户发送的请求。

  <?php
    $hash = md5($_COOKIE['cookie']);
  ?>
  <form method=”POST” action=”transfer.php”>
    <input type=”text” name=”toBankId”>
    <input type=”text” name=”money”>
    <input type=”hidden” name=”hash” value=”<?=$hash;?>”>
    <input type=”submit” name=”submit” value=”Submit”>
  </form>

  然后在服务器端进行Hash值验证

      <?php
        if(isset($_POST['check'])) {
             $hash = md5($_COOKIE['cookie']);
             if($_POST['check'] == $hash) {
                  doJob();
             } else {
        //...
             }
        } else {
      //...
        }
      ?>

  这个方法个人觉得已经可以杜绝99%的CSRF攻击了,那还有1%呢….由于用户的Cookie很容易由于网站的XSS漏洞而被盗取,这就另外的1%。一般的攻击者看到有需要算Hash值,基本都会放弃了,某些除外,所以如果需要100%的杜绝,这个不是最好的方法。
  (2).验证码

  这个方案的思路是:每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,厄….这个方案可以完全解决CSRF,但个人觉得在易用性方面似乎不是太好,还有听闻是验证码图片的使用涉及了一个被称为MHTML的Bug,可能在某些版本的微软IE中受影响。

  (3).One-Time Tokens(不同的表单包含一个不同的伪随机值)

  在实现One-Time Tokens时,需要注意一点:就是“并行会话的兼容”。如果用户在一个站点上同时打开了两个不同的表单,CSRF保护措施不应该影响到他对任何表单的提交。考虑一下如果每次表单被装入时站点生成一个伪随机值来覆盖以前的伪随机值将会发生什么情况:用户只能成功地提交他最后打开的表单,因为所有其他的表单都含有非法的伪随机值。必须小心操作以确保CSRF保护措施不会影响选项卡式的浏览或者利用多个浏览器窗口浏览一个站点。

  以下我的实现:

  1).先是令牌生成函数(gen_token()):

     <?php
     function gen_token() {
     //这里我是贪方便,实际上单使用Rand()得出的随机数作为令牌,也是不安全的。
    //这个可以参考我写的Findbugs笔记中的《Random object created and used only once》
          $token = md5(uniqid(rand(), true));
          return $token;
     }

  2).然后是Session令牌生成函数(gen_stoken()):

     <?php
       function gen_stoken() {
      $pToken = "";
      if($_SESSION[STOKEN_NAME]  == $pToken){
        //没有值,赋新值
        $_SESSION[STOKEN_NAME] = gen_token();
      }    
      else{
        //继续使用旧的值
      }
       }
     ?>

  3).WEB表单生成隐藏输入域的函数:  

     <?php
       function gen_input() {
            gen_stoken();
            echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
                 value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
       }
     ?>

  4).WEB表单结构:

     <?php
          session_start();
          include(”functions.php”);
     ?>
     <form method=”POST” action=”transfer.php”>
          <input type=”text” name=”toBankId”>
          <input type=”text” name=”money”>
          <? gen_input(); ?>
          <input type=”submit” name=”submit” value=”Submit”>
     </FORM>

  5).服务端核对令牌:

  这个很简单,这里就不再啰嗦了。
6)验证http refere
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank. test,然后通过点击页面上的按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)。而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求。

phpunit的安装与使用

phpunit是php的单元测试框架,安装起来十分方便。
一 安装phpunit
下载phpunit然后移动到bin目录下即可
wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit
之后就可以通过phpunit执行测试脚本了。
二 使用phpunit
一个简单的例子,比较结果是否一致

<?php
 
class ArrayTest extends PHPUnit_Framework_TestCase
{
    public function testNewArrayIsEmpty()
    {
        // 创建数组fixture。
        $fixture = array();
 
        // 断言数组fixture的尺寸是0。
        $this->assertEquals(0, sizeof($fixture));
    }
}
?>

同时可以用dataProvider 提供参数,其中provider函数必须是静态工有函数

class FeedTest extends UnitTestBase {

    /**
     *@dataProvider provider
     */
    public function testArray($a, $b) {

        $this->assertEquals($a, $b);


    }
    
    public static function provider() {
    
        return array(
                array(0, 0),
                array(1, 1),
                array(1, 0),
                array(1, 1),
                array(2, 2),
    
        );
    }   
}

git多分支自动补全

1,下载安装一个git的脚本

git clone https://github.com/markgandolfo/git-bash-completion.git 
cp git-bash-completion/git-completion.bash ~/.git-completion.bash 
source ~/.git-completion.bash

2,修改~/.bashrc
增加以下内容
source ~/.git-completion.bash
3,最后
source ~/.bashrc
之后通过tab键就可以实现分支的自动补全了