PHP|开发必知的良好实践
过滤、验证、转义
所有这些外部资源都不能完全相信
$_GET
$_POST
$_REQUEST
$_COOKIE
$argv
php://stdin
php://input
file_get_contents()
远程数据库
远程API
客户端的数据
过滤
使用htmlentities()
过滤HTML,将特殊字符转换为HTML实体,转义输出,第二个参数使用ENT_QUOTES
。
使用PDO预处理语句
过滤SQL注入.
使用filter_var()
& filter_input()
函数来过滤和验证不同类型的输入。
Eg: email, number, char, 特殊字符
密码
最安全的哈希算法:bcrypt
使用:
password_hash()
password_get_info()
password_needs_rehash()
password_verify()
函数来生成密码。
日期,时间,时区
使用PHP5.2.0引入的DateTime
& DateInterval
& DateTimeZone
类来处理时间。
设置默认时区
在php.ini中,设置
date.timezone = 'country/city'
使用date_default_timezone_set()
函数来设置默认时区。
DateTime Class
使用DateTime类来管理日期和时间
$datetime = new DateTime();
使用DateInterval
类来构造长度固定的时间段。配合上一个类中的方法使用。
DateTimeZone Class
时区的处理选择。
$timezone = new DateTimeZone('Asia/Shanghai');
$datetime = new DateTime('2016-05-20', $timezone);
$datetime->setTimezone(new DateTimeZone('Asia/Hong_Kong'));
有时,我们需要迭代处理一段时间内反复出现的一系列日期和时间,重复在日程表中记事就是个好例子。DatePeriod
类可以解决这种问题
DatePeriod
实例就是迭代器,每次迭代会产出一个DateTime
实例。
<?php
$start = new DateTime();
$interval = new DateInterval('P2W');
$period = new DatePeriod($start, $interval, 3);
foreach ($period as $nextDateTime) {
echo $nextDateTime->format('Y-m-d H:i:s'), PHP_EOL;
}
DB
PDO扩展
http://php.net/manual/zh/pdo.drivers.php
<?php
try {
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=books;port=3306;charset=utf8',
'USERNAME',
'PASSWORD'
);
} catch (PDOException $e) {
echo "Fail";
exit;
}
预处理语句
为了防止SQL注入,使用PDO的预处理语句。
预处理语句时PDOStatement的实例。可以通过PDO实例的prepare()
方法获取与处理语句对象。
$sql = "select id from users where email = :email";
$statement = $pdo->prepare($sql);
$email = filter_input(INPUT_GET, 'email');
$statement->bindValue(':email', $email);
http://php.net/manual/pdo.constants.php
继续:
$statement->execute();
while (($result = $statement->fetch(PDO::FETCH_ASSOC)) !== false) {
echo $result['email'];
}
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $result) {
echo $result['email'];
}
PDO的事务
try {
$pdo = new PDO();
}catch(PDOExcetion $e){
//
}
$stmtSubtract = $pdo->prepare('
UPDATE accounts
SET amount = amount - :amount
WHERE name = :name
');
$stmtAdd = $pdo->prepare('
UPDATE accounts
SET amount = amount + :amount
WHERE name = :name
');
$pdo->beginTransaction();
$fromAccount = 'Checking';
$withdrawal = 50;
$stmtSubtract->bindParam(':name', $fromAccount);
$stmtSubtract->bindParam(':amount', $withDrawal, PDO::PARAM_INT);
$stmtSubtract->execute();
$toAccount = 'Savings';
$deposit = 50;
$stmtSubtract->bindParam(':name', $$toAccount);
$stmtSubtract->bindParam(':amount', $deposit, PDO::PARAM_INT);
$stmtSubtract->execute();
$pdo->commit();
多字节字符串
PHP假设字符串中的每个自负都是八位字符,占一个字节的内存。然而考虑到国家化的时候,一个字符就不只占用一个字节了。
为了避免处理多字节字符串出错,可以安装mbstring
扩展。替换PHP原生的函数。
关于字符编码
一定要知道数据的字符编码
使用UTF8存储数据
使用UTF8输出数据
在php.ini
中设置,告诉PHP使用UTF8:
default_charset = "UTF-8";
很多PHP函数都使用这个默认的字符集:
htmlentities()
html_entity_decode()
htmlspecialchars()
以及mbstring中的扩展函数
流
流在PHP4.3.0中引入,作用是使用统一的方式处理文件,网络和数据压缩等共用同一套函数和用法的操作。简单而言,流是具有流式行为的资源对象。因此,流可以线性读写,或许还能使用
fseek()
函数定位到流中的任何位置。
流的作用实际上是在出发地和目的地之间传输数据。出发地和目的地可以是
文件,命令行进程,网络连接,zip, TAR压缩, 临时内存,标准输入输出,或者是通过php流封装协议实现的资源(http://php.net/manual/wrappers.php)
流封装协议
http://php.net/manual/wrappers.php
流封装协议的作用是使用通用的接口封装读写文件系统的差异。
每个流都有一个协议和目标
<scheme>://<target>
file:// 流封装协议
file_get_contents
fopen
fwrite
fclose
<?php
// 隐式
$handle = fopen('/etc/hosts', 'rb');
while (feof($handle) !== true) {
echo fgets($handle);
}
fclose($handle);
// 显式
$handle = fopen('file:///etc/hosts', 'rb');
while (feof($handle) !== true) {
//
}
fclose($handle);
php://流封装协议
php://stdin
php://stdout
php://memory
php://temp
自己编写流封装协议
PHP提供了一个示例streamWrapper
类,编写自定义的流封装协议。
流上下文
有些PHP流能接受一系列可选的参数,这些参数叫流上下文,用于定制流的行为。
使用
stream_context_create()
函数创建。
<?php
$requestBody = '{"username": "josh"}';
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "",
'content' => $requestBody,
],
]);
$response = file_get_contents('http://x/xapi', false, $context);
流过滤器
把过滤器附加到现有的流上,使用
stream_filter_append()
<?php
$handle = fopen('file', 'rb');
stream_filter_append($handle, 'string.toupper');
while (feof($handle) !== true) {
echo fgets($handle);
}
fclose($handle);
还可以使用php://filter
流协议把过滤器附加到流上。
$handle = fopen('php://filter/read=string.toupper/resource=data.txt', 'rb');
while () {
}
filter/read=<filter_name>/resource=<scheme>://<target>
我们还可以使用php_user_filter
类来自定义流过滤器
http://php.net/manual/en/class.php-user-filter.php
class DirtyWordsFilter extends php_user_filter
{
public function filter($in, $out, &$consumed, $closing)
{
$words = ['grime', 'dirt', 'grease'];
$wordData = [];
foreach ($words as $word) {
$replacement = array_fill(0, mb_strlen($word), '*');
$wordData[$word] = implode('', $replacement);
}
$bad = array_keys($wordData);
$good = array_values($wordData);
while ($bucket = stream_bucket_make_writeable($in)) {
$bucket->data = str_replace($bad, $good, $bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
然后,我们使用stream_filter_register()
函数注册这个自定义的流过滤器
stream_filter_register('dirty_words_filter', 'DirtyWordsFilter');
异常处理示例
try {
//
} catch (PDOException $e) {
} catch (Exception $e) {
// 捕获除了PDOException之外的所有异常
} finally {
// 最终执行
}
异常处理程序
PHP允许我们注册一个全局异常处理程序,捕获所有未被捕获的异常。
set_exception_handler(function (Exception $e) {
//
});
在某些情况下,代码执行完毕后,还需要还原成前一个异常处理程序。
restore_exception_handler()
<?php
// register
set_exception_handler(function (Exception $e) {
// Process exception
});
// Coding
// restore
restore_exception_handler();
错误
PHP由于某种原因导致无法运行,通常会触发错误。
我们可以使用
trigger_error()
自己触发错误。
我们可以使用
error_reporting()
或者php.ini
中使用error_reporting
指令,告诉PHP如何处理错误。
一定要遵守下述四个原则
一定要让PHP报告错误
在开发环境中显示错误
在生产环境中不能显示错误
都要记录错误
php.ini中:
//Dev
display_startup_errors = On
display_errors = On
error_reporting = -1
log_errors = On
//Prod
display_startup_errors = Off
display_errors = Off
error_reporting = E_ALL & ~E_NOTICE
log_errors = On
错误处理的函数
set_error_handler(function ($error, $errstr, $errfile, $errline) {
// Process error
})
我们也可以把PHP的错误转换为异常
只能转换满足php.ini中error_reporting
指令设置的错误
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
if (!(error_reporting() & $errno)) {
return
}
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
});
参考
Modern PHP
PHP|开发必知的良好实践的更多相关文章
- .NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上下文环境检查(实战)
Microsoft.NET 解决方案,项目开发必知必会. 从这篇文章开始我将分享一系列我认为在实际工作中很有必要的一些.NET项目开发的核心技术点,所以我称为必知必会.尽管这一系列是使用.NET/C# ...
- 打造高质量Android应用:Android开发必知的50个诀窍
打造高质量Android应用:Android开发必知的50个诀窍
- Git开发必知必会
比如说你现在准备写一个自己的视频资源网站,在创业初期,你的项目暂时还是测试阶段,没有用户的时候,你可能只有一个人在开发,你每天都以写的内容和时间作为文件名的命名,这样其实是可以满足你对版本控制的基本需 ...
- Web开发必知的八种隔离级别
ACID性质是数据库理论中的奠基石,它定义了一个理论上可靠数据库所必须具备的四个性质:原子性,一致性,隔离性和持久性.虽然这四个性质都很重要,但是隔离性最为灵活.大部分数据库都提供了一些可供选择的隔离 ...
- Android开发必知--几种不同对话框的实现
在开发过程中,与用户交互式免不了会用到对话框以实现更好的用户体验,所以掌握几种对话框的实现方法还是非常有必要的.在看具体实例之前先对AlertDialog做一个简单介绍.AlertDialog是功能最 ...
- Android开发必知--使用View.setId的正确姿势
这两天在写一个柱状图的自定义控件,用的直接继承ViewGroup的方式实现的,我们都知道,这是自定义控件里面最简单的一种了,有时间写个总结分享一下.这里我想说的重点是,在写这个自定义控件的时候遇到了个 ...
- Android开发必知--自定义Toast提示
开发过Android的童鞋都会遇到一个问题,就是在打印Toast提示时,如果短时间内触发多个提示,就会造成Toast不停的重复出现,直到被触发的Toast全部显示完为止.这虽然不是什么大毛病,但在用户 ...
- mysql开发必知必会
mysql的数据库的数据库,即存储mysql数据库的底层目录,是在/var/lib/mysql目录下(Linux,win在目录下的data中). 我们新创建的数据库db1就是在/var/lib/mys ...
- Python测试开发必知必会-PEP
互联网发展了许多年,不仅颠覆了很多行业,还让很多职位有了更多的用武之地.产品发布迭代速度不断加快,让测试开发这个岗位简直火得不要不要的. Python语言,作为一种更接近人来自然语言的开发语言,以简洁 ...
随机推荐
- 云计算分布式大数据Hadoop实战高手之路第七讲Hadoop图文训练课程:通过HDFS的心跳来测试replication具体的工作机制和流程
这一讲主要深入使用HDFS命令行工具操作Hadoop分布式集群,主要是通过实验的配置hdfs-site.xml文件的心跳来测试replication具体的工作和流程. 通过HDFS的心跳来测试repl ...
- 使用gulp、yeoman、bower建站
前端建站工具 标签 : 工具 *** 脚手架:yeoman 用途 快速搭建新项目 为项目增加新部分 创建模块或者包 引导新服务 ... 开始 安装yo和generator npm i -g yo np ...
- 关于python requests包新版本设置代理的问题
在更新了requests包之后,发现我电脑上的charles工具无法再成功抓取到数据包.百度了半年都没有找到原因. 然后 我使用了 google 查到了 charles的最新的文档发现.需要设置代理, ...
- 第二百三十一天 how can I 坚持
哎,蛋疼的一天,一点破问题搞了一下午,还没搞利索. 他们要组织出去玩,我没有参加啊,随便找了个借口. 博客园的字体怎么变小了呢,看着好难受啊,昨天传照片传的? 睡觉.外边下着雨呢,喜欢下雨的夏天还有下 ...
- ArcGIS Desktop10.2与CityEngine2012兼容问题
要培训ArcGIS Desktop和Esri CityEngine2012.在一台机器上装好Desktop10.2之后,在注册Esri CityEngine2012时报出了"7019:Inv ...
- C++11用于元编程的类别属性
[C++11用于元编程的类别属性] 许多算法能作用在不同的数据类别; C++ 模板支持泛型,这使得代码能更紧凑和有用.然而,算法经常会需要目前作用的数据类别的信息.这种信息可以通过类别属性 (type ...
- c#动态加载dll文件
1.在写一个记录日志到文件中的类库(生成dll文件copy到一个目录中去,然后在主函数的appconfig中去配置. using System; using System.Collections.Ge ...
- Codeforces 712 D. Memory and Scores (DP+滚动数组+前缀和优化)
题目链接:http://codeforces.com/contest/712/problem/D A初始有一个分数a,B初始有一个分数b,有t轮比赛,每次比赛都可以取[-k, k]之间的数,问你最后A ...
- UI进阶 科大讯飞(2) 语音合成(文字转换成语音)
科大讯飞开放平台.SDK下载.添加静态库.初始化见UI进阶 科大讯飞(1) 语音听写(语音转换成文字) 实现语音合成 功能实现步骤: 导入头文件 创建文字识别对象 指定文字识别后的回调代理对象 开启文 ...
- Odoo Qweb报表css丢失问题
有时候我们恢复过来的数据库在打印原来系统的Qweb报表的时候会发现所有的样式都丢失了,只打印内容出来. 这时候我们可以进入Setting/ Technical / Paramters / System ...