全部这些外部资源都不能彻底相信php
$_GEThtml
$_POSTmysql
$_REQUEST算法
$_COOKIEsql
$argv数据库
php://stdinapi
php://input安全
file_get_contents()网络
远程数据库app
远程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类来管理日期和时间
$datetime = new DateTime();
使用DateInterval
类来构造长度固定的时间段。配合上一个类中的方法使用。
时区的处理选择。
$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; }
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']; }
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_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://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