Tag: PHP

判断客户端是否使用代理服务器及其匿名级别

要判断客户端是否使用代理服务器,可以从客户端所发送的环境变量信息来判断。
具体来说,就是看HTTP_VIA字段,如果这个字段设置了,说明客户端使用了代理服务器。
匿名级别可以参考下表来判断。
给出一个应用例子,可以挂上代理试试效果: http://ip.mixsec.org/
一、没有使用代理服务器的情况:
REMOTE_ADDR = 您的 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示

二、使用透明代理服务器的情况:Transparent Proxies

REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP (补充:这个字段由代理服务器填充,有时会填充网关信息等)
HTTP_X_FORWARDED_FOR = 您的真实 IP
这类代理服务器还是将您的信息转发给您的访问对象,无法达到隐藏真实身份的目的。

三、使用普通匿名代理服务器的情况:Anonymous Proxies
REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP (补充:这个字段由代理服务器填充,有时会填充网关信息等)
HTTP_X_FORWARDED_FOR = 代理服务器 IP
隐藏了您的真实IP,但是向访问对象透露了您是使用代理服务器访问他们的。

四、使用欺骗性代理服务器的情况:Distorting Proxies
REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP (补充:这个字段由代理服务器填充,有时会填充网关信息等)
HTTP_X_FORWARDED_FOR = 随机的 IP
告诉了访问对象您使用了代理服务器,但编造了一个虚假的随机IP代替您的真实IP欺骗它。

五、使用高匿名代理服务器的情况:High Anonymity Proxies
REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示
完全用代理服务器的信息替代了您的所有信息,就象您就是完全使用那台代理服务器直接访问对象。

除此之外,可以通过proxy judges总 结其他一些可供参考的判定信息,一遍于在实践中加以利用。

最后写一个php例子,仅供大家参考:
if(!empty($_SERVER['HTTP_VIA'])) //使用了代理
{
if(!isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
//Anonymous Proxies 普通匿名代理服务器
//代理IP地址为 $_SERVER['REMOTE_ADDR']
}
else
{
//Transparent Proxies 透明代理服务器
//代理IP地址为 $_SERVER['REMOTE_ADDR']
//真实ip地址为 $_SERVER['HTTP_X_FORWARDED_FOR']
}
}
else //没有代理或者是高匿名代理
{
//真实ip地址为 $_SERVER['REMOTE_ADDR']
}


php效率高写法

1、用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说echo是语言结构,不是真正的函数,故把函数加上了双引号)。
2、如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。
3、$row[’id’] 的速度是$row[id]的7倍。
4、echo 比 print 快,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2。
5、在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替。
6、注销那些不用的变量尤其是大数组,以便释放内存。
7、尽量避免使用__get,__set,__autoload。
8、require_once()代价昂贵。
9、include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少。
10、如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。
11、函数代替正则表达式完成相同功能。
12、str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。
13、如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行代码接受数组作为查询和替换的参数。
14、使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。
15、用@屏蔽错误消息的做法非常低效,极其低效。
16、打开apache的mod_deflate模块,可以提高网页的浏览速度。
17、数据库连接当使用完毕时应关掉,不要用长连接。
18、错误消息代价昂贵。
19、在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。
20、递增一个全局变量要比递增一个局部变量慢2倍。
21、递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。
22、递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。
23、仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP大概会检查看是否存在全局变量。
24、方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添? ……


php5 中的this,self,parent详解

我们来明白上面三个关键字: this,self,parent,从字面上比较好理解,是指这,自己,父亲,呵呵,比较好玩了,我们先建立几个概念,这三个关键字分别是用在什么地方 呢?我们初步解释一下,this是指向当前对象的指针(我们姑且用C里面的指针来看吧),self是指向当前类的指针,parent是指向父类的指针。

这么说还不能很了解,那我们就根据实际的例子结合来讲讲。

(1)$this 伪变量提供了一种用于对象引用自己的属性和方法的机制。在对象外部,可以使用句柄来访问它的元素(在本例子中是 $en)。在对象内部,则无此句柄,所以必须求助于 $this。如果觉得 $this 有些迷惑,则在代码中遇到它时,试着在头脑中用当前实例 替换它。
class UserName{
//定义属性
private $name;
//定义构造函数
function __construct( $name ){
$this->name = $name; //这里已经使用了this指针
}
//析构函数
function __destruct(){}
//打印用户名成员函数
function printName(){
print( $this->name ); //又使用了this指针
}
}
//实例化对象
$nameObject = new UserName( “heiyeluren” );
//执行打印
$nameObject->printName(); //输出: heiyeluren
//第二次实例化对象
$nameObject = new UserName( “PHP” );
//执行打印
$nameObject->printName(); //输出:PHP
?>
我们看,上面的类使用了this指针,那么当时this是指向谁呢?其实this是在实例化的时候来确定指向谁,比如第一次实例化对象的时候 (行),那么当时this就是指向$nameObject对象,那么执行行的打印的时候就把print( $this->name ),那么当然就输出了”heiyeluren”。第二个实例的时候,print( $this->name )变成了print( $nameObject->name ),于是就输出了”PHP”。所以说,this就是指向当前对象实例的指针,不指向任何其他对象或类。

(2)self
首先我们要明确一点,self是指向类本身,也就是self是不指向任何已经实例化的对象,一般self使用来指向类中的静态变量。
class Counter{
//定义属性,包括一个静态变量
private static $firstCount = ;
private $lastCount;
//构造函数
function __construct(){
$this->lastCount = ++selft::$firstCount; //使用self来调用静态变量,使用self调用必须使用::(域运算符号)
}
//打印最次数值
function printLastCount(){
print( $this->lastCount );
}
}
//实例化对象
$countObject = new Counter();
$countObject->printLastCount(); //输出
?>
我们在第二行定义了一个静态变量$firstCount,并且初始值为,那么在行的时候调用了这个值得,使用的是 self来调用,并且中间使用”::”来连接,就是我们所谓的域运算符,那么这时候我们调用的就是类自己定义的静态变量$frestCount,我们的静 态变量与下面对象的实例无关,它只是跟类有关,那么我调用类本身的的,那么我们就无法使用this来引用,可以使用self来引用,因为self是指向类 本身,与任何对象实例无关。换句话说,假如我们的类里面静态的成员,我们也必须使用self来调用。

(3)parent

我们知道parent是指向父类的指针,一般我们使用parent来调用父类的构造函数。 CODE:[复制到剪切板]

//基类
class Animal
{
//基类的属性
public $name; //名字

//基类的构造函数
public function __construct( $name )
{
$this->name = $name;
}
}

//派生类
class Person extends Animal //Person类继承了Animal类
{
public $personSex; //性别
public $personAge; //年龄

//继承类的构造函数
function __construct( $personSex, $personAge )
{
parent::__construct( “heiyeluren” ); //使用parent调用了父类的构造函数
$this->personSex = $personSex;
$this->personAge = $personAge;
}

function printPerson()
{
print( $this->name. ” is ” .$this->personSex. “,this year ” .$this->personAge );
}
}

//实例化Person对象
$personObject = new Person( “male”, “”);

//执行打印
$personObject->printPerson(); //输出:heiyeluren is male,this year

?> 我 们注意这么几个细节:成员属性都是public的,特别是父类的,是为了供继承类通过this来访问。我们注意关键的地方,第 行:parent::__construct( “heiyeluren” ),这时候我们就使用parent来调用父类的构造函数进行对父类的初始化,因为父类的成员都是public的,于是我们就能够在继承类中直接使用 this来调用。

总结:
this是指向对象实例的一个指针,self是对类本身的一个引用,parent是对父类的引用。


从魔兽看PHP设计模式

前段时间看到有人用魔兽来解释设计模式,感觉很有意思,于是我把它改了改,又添加了些设计模式内容,今天发出来。有些地方借鉴了前人的内容,没有注明,请前人不要见怪啊。
这里用大家感兴趣的魔兽3来讨论PHP的几种常见的设计模式:单件模式、策略模式、工厂模式、观察者模式。今天就讲这四个吧,以后继续。

这些设计模式,都是针对面向对象来说的,所以都用PHP5,另外在这里我想说的是PHP4从2008年8月8日(我记得是和北京奥运会同一天,没查证,呵呵)的时候官方就发了最后一个PHP4的补丁,这意味这PHP4的时代已经终结,所以,我建议大家现在就别理PHP4吧,就以PHP5来说吧。

一、单件模式:
问题的提出:
某些应用程序资源是独占的,因为有且只有一个此类型的资源。例如,通过数据库句柄到数据库的连接是独占的。您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销,在获取单个页面的过程中更是如此。

问题的解决:
那么下面我们就开始玩魔兽吧。首先双击war3.exe,这时候就开始运行魔兽了。我们用代码来实现吧。。
class War3
{

public function __construct()

{

echo "War3 is Running.","
“;

}
}
$war = new War3();

运行!很好,输出
War3 is Running.
我们已经可以开始游戏了,但是,如果我在代码末尾再加入
$war2 = new War3();
$war3 = new War3();
会怎么样呢?我们试试,输出结果:
War3 is Running.
War3 is Running.
War3 is Running.
完了,如果不小心双击了两次就开了3个魔兽,那如果再双击几次,那电脑肯定爆掉。。。我们还是来想想解决方法吧。。。
既然我们不能这么随意的就把这个类实例化了,那么我们就把构造函数改成私有方法。
class War3
{

private function __construct()

{

echo “War3 is Running.”,”
“;

}
}
可是私有变量外部是无法访问的,这样以来,我们就连一个都打不开了啊。别急,我们再给他加一个不用通过实例化,外部也能访问的函数,那就是静态函数,
class War3
{

private function __construct()

{

echo “War3 is Running.”,”
“;

}

public static function runWar()

{

}
}

通过这个静态的方法runWar()我们来控制类War3的实例化,那么还缺上一个标识,我们再创建一个标识,通过这个标识来表示我们的类是否已经实例化,如果实例化,直接返回句柄就行了。
把类修改成
class War3
{

protected static $_instance = null;

private function __construct()

{

echo “War3 is Running.”,”
“;

}

public static function runWar()

{

if (null === self::$_instance) {

self::$_instance = new self();

}

return self::$_instance;

}
}
当然,我们运行魔兽时的实例化也要换种方法,就通过
$war = War3::runWar();
就能开始玩魔兽了,好了,下面把完整的代码附上来:
class War3
{

protected static $_instance = null;

private function __construct()

{

echo "War3 is Running.","
“;

}

public static function runWar()

{

if (null === self::$_instance) {

self::$_instance = new self();

}

return self::$_instance;

}
}

$war = War3::runWar();
$war2 = War3::runWar();
$war3 = War3::runWar();

运行一下,结果是:
War3 is Running.
太好了,我双击了这么多次,也就只运行了一个魔兽,现在随便你怎么打开,机子都不会爆掉了。
这就是传说中的单价模式,主要用于一些很占资源的而且实例仅有一个实例就够用的东西,比如,zend framework中的Zend_Controller_Front前端控制器,就是采用单价模式来设计的,大家有兴趣的话可以看看那个。

二、策略模式:
问题的提出:
在此模式中,算法是从复杂类提取的,因而可以方便地替换。例如,如果要更改搜索引擎中排列页的方法,则策略模式是一个不错的选择。思考一下搜索引擎的几个部分 ?? 一部分遍历页面,一部分对每页排列,另一部分基于排列的结果排序。在复杂的示例中,这些部分都在同一个类中。通过使用策略模式,您可将排列部分放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。
问题的解决:
呵呵,不讲那么复杂,刚才魔兽好不容易打开了,我们还是玩魔兽好了。
下面我们选battle,哇好多种族啊,有人族(Human),兽族(ORC),暗夜精灵族(Nighy Elf),不死族(Undead)。我选精灵族(Nighy Elf),再选一个精灵族和两个兽族(ORC),一个兽族和我是一家的,另一个精灵族和兽族是另一家的。
每一个玩家在进入游戏后都会得到一些资源,如一个大厅,五个小精灵(苦工)和一个矿山。这些可以称为是初始化的一些东西,这里我们就可以用到策略模式来封装这些初始化。

进入正题,首先我们来构建一个玩家类:
class player
{

//玩家名字

protected $_name;

//种族

protected $_race;

//队伍

protected $army;

//建筑

protected $building;

//人口

protected $population;

//黄金

protected $gold;

//木材

protected $wood;

//构造函数,设定所属种族

public function __construct($race)

{

$this->race = $race;

}

//__get()方法用来获取保护属性

private function __get($property_name)

{

if(isset($this->$property_name)) {

return($this->$property_name);

}

else {

return(NULL);

}

}

//__set()方法用来设置保护属性

private function__set($property_name,$value)

{

$this->$property_name=$value;

}
}

接着,我们再建一个玩家初始化的接口,

interface initialPlayer
{

//制造初始化的部队

public function giveArmy($player);

//制造初始化的建筑

public function giveBuilding($player);

//初始化资源

public function giveSource($player);
}

好了,到这里我们就该对这个接口来实现了,为了方便,我只选了两个种族,就只写这两个种族的初始化了:
首先是精灵族:
class NighyElfInitial implements initialPlayer
{

//制造初始化的部队

public function giveArmy($player)

{

//五个小精灵

for($i=0; $i<=5;$i++)

{

$creator = new CreatArms();//这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了

$player->army[] = $creator->Creat(’Wisp’,’./Arms/’);

}

}

//制造初始化的建筑

public function giveBuilding($player)

{

$creator = new CreatBuildings();

//一个基地

$player->building[] = $creator->Creat(’TownHall’,’./Buildings/’);

//一个矿场

$player->building[] = $creator->Creat(’Mine’,’./Buildings/’);

}

//初始化人口上限

public function giveSource($player)

{

$player->population= 10;

$player->gold= 1000;

$player->wood= 100;

}
}

接下来是兽族:
class ORCInitial implements initialPlayer
{

//制造初始化的部队

public function giveArmy($player)

{

//五个苦工

for($i=0; $i<=5;$i++)

{

$creator = new CreatArms();//这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了

$player->army[] = $creator->Creat(’Peon’,’./Arms/’);

}

}

//制造初始化的建筑

public function giveBuilding($player)

{

$creator = new CreatBuildings();

//一个基地

$player->building[] = $creator->Creat(’TownHall’,’./Buildings/’);

//一个矿场

$player->building[] = $creator->Creat(’Mine’,’./Buildings/’);

}

//初始化人口上限

public function giveSource($player)

{

$player->population= 10;

$player->gold= 1000;

$player->wood= 100;

}
}

好了,到这里初始化代码就写好了,现在还差一个控制这些初始化得类,也就是封装他们:
class initialController {
//构造函数,参数为玩家的数组
public function __construct($playerArray)
{

foreach ($playerArray as $player)

{

switch ($player->race)

{

case ‘NighyElf’:

$initialController = new NighyElfInitial();break;

case ‘ORC’:

$initialController = new ORCInitial();break;

}

$initialController->giveArmy($player);

$initialController->giveBuilding($player);

$initialController->giveSupply($player);

}

}
}

最后就是简单这么一调用,就OK:
//有两个精灵族两个兽族
$playerArray = array(new player('NighyElf'), new player('NighyElf'), new player('ORC'), new player('ORC'));
//进行初始化工作
$initialController = new initialController($playerArray);

这就是策略模式,他将不同情况下的算法封装在一起。Zend framework中的Zend_Application_Resource就是用策略模式来设计的。

三、工厂模式:
问题的提出:
最初在设计模式一书中,许多设计模式都鼓励使用松散耦合。要理解这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分 ?? 您曾认为完全不相关的部分中也有可能出现级联破坏。
该问题在于紧密耦合。系统某个部分中的函数和类严重依赖于系统的其他部分中函数和类的行为和结构。您需要一组模式,使这些类能够相互通信,但不希望将它们紧密绑定在一起,以避免出现联锁。
在大型系统中,许多代码依赖于少数几个关键类。需要更改这些类时,可能会出现困难。例如,假设您有一个从文件读取的 User 类。您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。这时候,使用工厂模式会很方便。
工厂模式是一种类,它具有为您创建对象的某些方法。您可以使用工厂类创建对象,而不直接使用 new。这样,如果您想要更改所创建的对象类型,只需更改该工厂即可。使用该工厂的所有代码会自动更改。
问题的解决:
呵呵,估计有些phper没看懂吧,没关系,那是我从其他地方抄的,我们下面还是通过魔兽来进行吧。这一部分,我看都已经有前人写好了,我就基本上照抄了,请前人不要见怪啊。呵呵。
前面选了暗夜精灵族(Nighy Elf),和兽族(ORC),因为小精灵(Wisp)能建造建筑,还能自爆。所以根据这个我们下面先写个小精灵(Wisp)的类。
class Wisp
{

private $mHealthPoint = 120;//这是小精灵的血量

private $mArmor = 0;//这是小精灵的护甲

//小精灵能建造建筑

public function Build()

{

echo '精灵建造建筑咯。
‘;

}

//每个小精灵被造出来时还会占用一个人口

public function __construct()

{

echo ‘你已经建造了一个小精灵。
‘;

//这里是增加已有人口的代码

}

//每个小精灵死亡会减少你占用的人口

public function __destruct()

{

//这里是减少已有人口的代码

}
}
把这些代码放在Arms/ Wisp.php中。
啊,还有还有,还有苦工(Peon)的类
class Peon
{

private $mHealthPoint = 250;//这是苦工的血量

private $mArmor = 0;//这是苦工的护甲

//苦工能建造建筑

public function Build()

{

echo '苦工建造建筑咯。
‘;

}

//每个苦工被造出来时还会占用一个人口

public function __construct()

{

echo ‘你已经建造了一个苦工。
‘;

//这里是增加已有人口的代码

}

//每个苦工死亡会减少你占用的人口

public function __destruct()

{

//这里是减少已有人口的代码

}
}
把这些代码放在Arms/ Peon.php中。

等等,这样岂不是很复杂,魔兽里面还有那么多的兵种,另外都还有两个种族,每次创建一个兵就要new一个,要是记不住这个兵的类名,岂不是new不了?而且如果一个兵是一个类,放在一个文件里,那是不是一开始就要把所有的几十上百个文件都include一次啊,那效率可想而知啊。
嘿嘿,当然是有解决办法的啊,我们再写一个类把这些类都封装起来,这个把兵种都封装起来的类我们称之为工厂类,他可以像生产产品一样,来创建兵,帮我们对其实例化。下面我们就来看这个类怎么实现吧。
class CreatArms
{

public function __construct(){}

public function Creat($arms,$path = '')

{

include $path.$arms.'.php'; //包含要这个类的文件

return new $arms; //返回你创建的兵种对象的句柄

}
}

这样,即使在兵种多样的情况下,我们仍然可以很方便地实例化:
$creator = new CreatArms();
$w1 = $creator->Creat(’兵种名’,'前缀或路径’);
例如创建小精灵:
$creator = new CreatArms();//不管创建啥,我都只要使用这个类
$w1 = $creator->Creat(‘Wisp’,’./Arms/’);//创建一个小精灵
$w1->Build();//让小精灵造建筑

这就是传说中的工厂模式,通过工厂模式,对于如论坛那种有很多种用户的,特别是为了以后扩展比较方便的,采用工厂模式,是个很好的解决方法。在zend framework中的Zend_Form、Zend_Filter、Zend_Validate就是用工厂模式来构架的。

四、观察者模式:
问题的提出:
观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。

问题的解决:
呵呵,上面还是抄的,看不懂没关系,我们今天重点是玩魔兽。
已经造了很长时间的兵了,现在可以出去带兵打仗了,如果我去打电脑的兽族,那么电脑与那个兽族同盟的精灵族就会过来帮忙。那么如何让他知道自己的同盟受攻击了呢。现在我们就来讨论这个问题。
首先我们写一下结盟的抽象类:
abstract class abstractAlly
{

//放置观察者的集合,这里以简单的数组来直观演示

protected $oberserverCollection;

//增加观察者的方法,参数为观察者(也是玩家)

public function addOberserver($oberserver)

{

$this->oberserverCollection[] = new oberserver($oberserver);

}

//将被攻击的电脑的名字通知各个观察者

public function notify($beAttackedPlayerName)

{

//把观察者的集合循环

foreach ($this->oberserverCollection as $oberserver)

{

//调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者

if($oberserver->name != $beAttackedPlayerName)

$oberserver->help($beAttackedPlayerName);

}

}

abstract public function beAttacked($beAttackedPlayer);
}

下面我们就写具体的结盟类:

class Ally extends abstractAlly
{

//构造函数,将所有电脑玩家的名称的数组作为参数

public function __construct($allPlayer)

{

//把所有电脑玩家的数组循环

foreach ($allPlayer as $player)

{

//增加观察者,参数为各个电脑玩家的名称

$this->addOberserver($player);

}

}

//将被攻击的电脑的名字通知各个观察者

public function beAttacked($beAttackedPlayerName)

{

//调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者

$this->notify($beAttackedPlayerName);

}
}

接着在二、策略模式中我们定义的player类中加入一个help方法
public help($beAttackedPlayerName)
{

//这里简单的输出,谁去救谁,最后加一个换行,便于显示

echo $this->name.” help “.$beAttackedPlayerName.”
“;
}

这样就行了。最后就是仿真了。
//先设置敌方电脑
$allComputePlayer = array('NighyElf2', 'ORC2');
//新建电脑结盟
$Ally = new Ally($allComputePlayer);
//假设我进攻了电脑的兽族
$Ally->beAttacked(’ORC2′);

这样结盟的另一家就能接到通知,去救援。观察者模式主要就是用在这种情况下。可以将某个状态变化立即通知到相关的对象,相关的对象就可以采用相应的策略。例如,zend framework中的Zend_Message就是用的观察者模式。
原文出处:http://www.phpchina.com/html/41/n-34841.html


php数组排序学习

array_multisort学习示例

$array_to_sort = array();
$array_to_sort['TestCase1'] = array('name'=>'Test1','value'=>'218');
$array_to_sort['TestCase2'] = array('name'=>'Test2','value'=>'10');
$array_to_sort['TestCase3'] = array('name'=>'Test3','value'=>'64');
$sort_arr = array();
foreach($array_to_sort AS $uniqid => $row){
foreach($row AS $key=>$value){
$sort_arr[$key][$uniqid] = $value;
}
}
print 'Before sorting:

';
    print_r($array_to_sort);
    print '

';
array_multisort($sort_arr[$sort['field']], SORT_ASC, $array_to_sort);
print 'After sorting:

';
    print_r($array_to_sort);
    print '

';

?>


php安全编程分析

规则 1:绝不要信任外部数据或输入

?
规则 2:禁用那些使安全性难以实施的 PHP 设置

规则 3:如果不能理解它,就不能保护它

<?php
$pid = tiny_mce_marker

GET['pid'];
if (strlen($pid)){
if (!ereg(”^[0-9]+$”,$pid) && strlen($pid) > 5){
//do something appropriate, like maybe logging \
them out or sending them back to home page
}
}else{
//empty $pid, so send them back to the home page
}
//we create an object of a fictional class Page, which is now
//even more protected from evil user input
$obj = new Page;
$content = $obj->fetchPage($pid);
//and now we have a bunch of PHP that displays the page
//……
//……
?>
防止 SQL 注入攻击
防止用户操纵 变量
缓冲区溢出攻击
缓冲区溢出攻击 试图使 PHP 应用程序中(或者更精确地说,在 Apache 或底层操作系统中)的内存分配缓冲区发生溢出。请记住,您可能是使用 PHP 这样的高级语言来编写 Web 应用程序,但是最终还是要调用 C(在 Apache 的情况下)。与大多数低级语言一样,C 对于内存分配有严格的规则。
缓冲区溢出攻击向缓冲区发送大量数据,使部分数据溢出到相邻的内存缓冲区,从而破坏缓冲区或者重写逻辑。这样就能够造成拒绝服务、破坏数据或者在远程服务器上执行恶意代码。
注意,缓冲区溢出攻击并不限于长的数字串或字母串。也可能会看到长的十六进制字符串(往往看起来像 \xA3 或 \xFF)。记住,任何缓冲区溢出攻击的目的都是淹没特定的缓冲区,并将恶意代码或指令放到下一个缓冲区中,从而破坏数据或执行恶意代码。对付十六进制缓冲区溢出最简单的方法也是不允许输入超过特定的长度。
如果您处理的是允许在数据库中输入较长条目的表单文本区,那么无法在客户端轻松地限制数据的长度。在数据到达 PHP 之后,可以使用正则表达式清除任何像十六进制的字符串。
$clean = preg_replace(”![][xX]([A-Fa-f0-9]{1,3})!”, “”,$input);
从安全的角度来看,对公共用户输入使用 strip_tags() 是必要的。如果表单在受保护区域(比如内容管理系统)中,而且您相信用户会正确地执行他们的任务(比如为 Web 站点创建 HTML 内容),那么使用 strip_tags() 可能是不必要的,会影响工作效率。
还有一个问题:如果要接受用户输入,比如对贴子的评论或来客登记项,并需要将这个输入向其他用户显示,那么一定要将响应放在 PHP 的 htmlspecialchars() 函数中。这个函数将与符号、< 和 > 符号转换为 HTML 实体。例如,与符号(&)变成 &。这样的话,即使恶意内容躲开了前端 strip_tags() 的处理,也会在后端被 htmlspecialchars() 处理掉。
浏览器内的数据操纵
远程表单提交
首先可能考虑检查

tiny_mce_marker

SERVER['HTTP_REFERER'],从而判断请求是否来自自己的服务器,这种方法可以挡住大多数恶意用户,但是挡不住最高明的黑客。这些人足够聪明,能够篡改头部中的引用者信息,使表单的远程副本看起来像是从您的服务器提交的。
处理远程表单提交更好的方式是,根据一个惟一的字符串或时间戳生成一个令牌,并将这个令牌放在会话变量和表单中。提交表单之后,检查两个令牌是否匹配。如果不匹配,就知道有人试图从表单的远程副本发送数据。
要创建随机的令牌,可以使用 PHP 内置的 md5()、uniqid() 和 rand() 函数

结束语

总结问题:
使用 mysql_real_escape_string() 防止 SQL 注入问题。
使用正则表达式和 strlen() 来确保 GET 数据未被篡改。
使用正则表达式和 strlen() 来确保用户提交的数据不会使内存缓冲区溢出。
使用 strip_tags() 和 htmlspecialchars() 防止用户提交可能有害的 HTML 标记。
避免系统被 Tamper Data 这样的工具突破。
使用惟一的令牌防止用户向服务器远程提交表单


翻页区间切割算法(PHP实现)

翻页区间切割算法(PHP实现)在Web应用程序中,我们经常会见到同一类数据经过视图层的渲染,以”分页显示”和”翻页链接”的形式展现在客户端。下图是Google搜索结果页中的”翻页链接”。??

?

?

根据上图不难看出,”翻页区间切割(或称划分)”是把已知总页数划分为N段(n1, n2, …),每段容纳M(m1, m2,…)个页链接,每个页链接对应一页,由数字编号。这里,我们讨论的”切割”是一个动态过程,而这个动态过程可以建立在静态”划分”的基础上。

在遭遇典型的大数据量和有限的屏幕空间时,翻页区间作为精确的导航系统是必不可少的,它应具备某些固定的属性和智能行为来辅助和引导用户浏览。我们应先针对屏幕显示空间的大小做划分,即给定翻页区间的长度$displaySize(图中Google搜索结果页区间长度是20)。然后我们便可以根据当前显示页$currentPage来动态地切割出它所在的区间范围(图中Google当前显示页是18),须做到让$currentPage处于区间起止页号所包含的范围内。同时,还要确保每次切割是在合理的范围内,不会出现页号溢出的现象,这就需要做一些后续的判断来达到智能化。

现代Web应用程序要求快速响应用户的请求,开发者往往要在用户体验和性能之间做出平衡。翻页区间切割算法首要考虑性能,因为我们主要剖析的是算法本身(貌似是废话 )。下面我们就结合PHP实现的代码来看看,如何做到切割的精确和性能最佳化。

代码清单 翻页区间切割算法
/**
* 获得页区间页号
*
* @param int $currentPage 当前页号
* @param int $totalPages 总页数
* @param int $displaySize 区间容量,默认显示10页
* @return array 返回由区间页号组成的数组
*/
function getPageRange($currentPage, $totalPages, $displaySize = 10) {
if ($totalPages <= 0 || $displaySize <= 0) {
return array();
} elseif ($displaySize > $totalPages) {
$startPage = 1;
$endPage = $totalPages;
} else {
if ($currentPage % $displaySize === 0) {
$startPage = $currentPage – $displaySize + 1;
} else {
while (($currentPage % $displaySize)) {
–$currentPage;
}
$startPage = $currentPage + 1;
}
if ($startPage <= 0) {
$startPage = 1;
}
$endPage = $startPage + $displaySize – 1;
if ($endPage > $totalPages) {
$endPage = $totalPages;
$startPage = $endPage – $displaySize + 1;
}
}
return range($startPage, $endPage);
}函数getPageRange接受三个参数,当前页$currentPage,总页数$totalPages,和翻页区间长度$displaySize,默认是10。根据这三个参数,函数getPageRange会生成一个适当的包含了$currentPage的翻页区间。首先,我们需要排除非法的参数值,对于总页数或区间长度小于零的情况,须加以检查。

然后,我们考察静态划分的思路。如前所述,给定翻页区间长度后,便可用总页数除以长度,得到区间个数。与此同时,我们可分析得知并不是所有区间都含有相同的页数,在极端情况下,还会出现总页数小于给定的翻页区间长度,那么划分或切割的结果将永远只有一个区间。幸运的是,这不会给我们的核心算法带来什么干扰,但我们仍须重视代码的健壮性。所以,我们先考虑极端情况,在运用算法解决核心问题之前,先迅速捕捉只有一页的区间。

接下来,我们便可以看看区间的固有属性了。每个动态切割的区间,都有一个起始页和一个尾页;由于区间彼此存在先后顺序,所以在经过静态划分后,我们始终会得到第一个(首)区间和最后一个(尾)区间,若首尾区间重合,则说明总页数小于给定的翻页区间长度。无论如何,算法需要解决的关键问题是如何找到区间的起始页和尾页,一旦确定这两个元素,便可以使用PHP内置的range函数生成区间内的全部页码。

算法利用当前页$currentPage和翻页区间长度$displaySize做比较,来判断当前页在区间内所处的位置,进而推导出区间起始页和尾页与当前页的偏移量。为了做到完美无缺,我们还要考虑边界溢出的问题,这也非常简单,只需判断起始页和尾页是否介于1和总页数之间即可。

至此,对于代码的分析已经完成。我们来看一下算法的时间效率,通俗地说,算法中的基本操作是求模,算法的执行时间取决于$currentPage与$displaySize的差值,差值越大,则求模次数越多、执行时间越长,呈线性结构。而实际的执行结果则是在瞬间完成的。

下面,我们来结合上面的Google搜索结果页,利用getPageRange函数生成一个翻页区间,输入所需参数:
print_r(implode(’,', getPageRange(18, 27, 20)));得到的结果是:
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20大家会发现这和Google搜索结果页显示的完全不一样。对,因为Google的翻页区间把当前页强制设置在中间位置上了!嗯,不要灰心,我们仍可以利用getPageRange函数得到与之相匹配的结果,只需把问题再分解一下:
print_r(implode(’,', array_merge(getPageRange(17, 17, 10), getPageRange(27, 27, 10))));得到的结果是:
8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27


PHP魔法函数应用详解

PHP魔法函数应用详解

???????? PHP提供两个方便我们引用数据的魔法引用函数magic_quotes_gpc和magic_quotes_runtime,这两个函数如果在 php.ini设置为ON的时候,就会为我们引用的数据碰到单引号’和双引号”以及反斜线 \ 是自动加上反斜线,帮我们自动转译符号,确保数据操作的正确运行,可是我们在php不同的版本或者不同的服务器配置下,有的 magic_quotes_gpc和magic_quotes_runtime设置为on,有的又是off,所以我们写的程序必须符合on和off两种情况。那么magic_quotes_gpc和magic_quotes_runtime两个函数有什么区别呢?看下面的说明:

magic_quotes_gpc

作用范围是:WEB客户服务端;
作用时间:请求开始是,例如当脚本运行时.

magic_quotes_runtime

作用范围:从文件中读取的数据或执行exec()的结果或是从SQL查询中得到的;
作用时间:每次当脚本访问运行状态中产生的数据.
所以 magic_quotes_gpc的设定值将会影响通过Get/Post/Cookies获得的数据;magic_quotes_runtime的设定值将会影响从文件中读取的数据或从数据库查询得到的数据。
例子说明:
<form action=”" method=”post” >STR:<input type=”text” name=”str”><input type=”submit”></form><?php/* 我们在表单里填写: ‘”\ 这些符号,如果magic_quotes_gpc没有开启,那么他们不会被反斜杠转义 */echo ‘现在通过POST传递过来的值是:’ ,

tiny_mce_marker

POST['str'], ‘<br />’;if (get_magic_quotes_gpc()) { // 检查magic_quotes_gpc是否打开,如果没有打开,用addslashes进行转义 $str =

tiny_mce_marker

POST['str'];} else { $str = addslashes(

tiny_mce_marker

POST['str']);}echo ‘这里是转义过后的:’ ,$str, ‘<hr />’;$sql = “INSERT INTO lastnames (lastname) VALUES (’$str’)”;//=====================================================================================//—–magic_quotes_gpc只会转义: 通过Get/Post/Cookies获得的数据//—–magic_quotes_runtime会转义:从文件中读取的数据或执行exec()的结果或是从SQL查询中得到的//=====================================================================================$data = implode(file(’try.php’)); // 我们在里面依然写’”\这几个字符,用来测试echo ‘这里是try.php的数据,’;if (get_magic_quotes_runtime()) { $data = $data; echo ‘被系统自带转义的’ .$data;} else { echo ‘被addslashes转义了的’ .$data = addslashes($data);}$sql = “INSERT INTO lastnames (lastname) VALUES (’$data’)”;echo ‘<br />SQL语句为:<br />’ ,$sql;//—入库都转义了,但是多余了反斜杠,我们要读出来是原来的数据时候使用stripslashes()去掉反斜杠//—stripslashes()和addslashes()作用相反?>
最关键的区别是就是上面提到的2点:他们针对的处理对象不同
magic_quotes_gpc的设定值将会影响通过Get/Post/Cookies获得的数据
magic_quotes_runtime的设定值将会影响从文件中读取的数据或从数据库查询得到的数据
在这里顺便在提几个想关联的函数:
set_magic_quotes_runtime():
设置magic_quotes_runtime值. 0=关闭.1=打开.默认状态是关闭的.可以通过 echo phpinfo(); 查看magic_quotes_runtime
get_magic_quotes_gpc():
查看magic_quotes_gpc值.0=关闭.1=打开.
get_magic_quotes_runtime():
查看magic_quotes_runtime值。0=关闭.1=打开.
注意的是没有 set_magic_quotes_gpc()这个函数,就是不能在程序里面设置magic_quotes_gpc的值。


Copyright © 1996-2010 V酷吧. All rights reserved.
iDream theme by Templates Next | Powered by WordPress