全部這些外部資源都不能徹底相信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