PHP之MVC项目实战(三)
本文主要包括以下內容
標準錯誤錯誤處理
PHP在語法層面上發生的錯誤
兩個過程:
觸發階段(發生一個錯誤)
處理階段(如何處理該錯誤)
觸發階段
系統觸發,php自己觸發
典型的都是由php的核心在執行或者編譯php代碼時,發現的錯誤,并觸發該錯誤!用戶觸發,自定義錯誤
但是可以通過用戶的php代碼,手動觸發一個錯誤!
利用函數 trigger_error();觸發一個用戶自定義的錯誤!
錯誤處理階段
有三種典型的錯誤處理方法
第一:報告錯誤信息
將錯誤信息獲取后,輸出到瀏覽器。
因此,典型的一個錯誤信息應包含:
級別,消息,發生的文件,行號:
想要管理錯誤報告,需要基于錯誤的級別進行管理:
php允許管理:
1,是否報告。
2,報告哪些級別的錯誤。
利用php的配置:
display_erorrs:是否顯示錯誤信息,布爾。
error_reporting:報告的級別!
需要利用位運算,將所有需要報告的級別都設置上!
典型的級別是:
顯示所有的錯誤:
也可以通過腳本代碼進行以上配置
ini_set('error_reporting', E_ALL | E_STRICT); ini_set('display_errors', 1);第二:錯誤日志
將錯誤的信息寫到日志文件中!
也是通過兩個配置完成處理?
log_errors:是否開啟錯誤日志
error_log: 錯誤文件位置
可以同歸ini_set進行配置!
error_reporting:針對 錯誤日志同樣有效!
上面的兩種都是php內置的錯誤方式!
第三:自定義的錯誤處理
當觸發錯誤時,php自身不處理,交由用戶腳本代碼進行處理!
用戶要提供一個方法(函數)來處理發生的錯誤:定義函數!
告知php,一旦發生錯誤,由用戶定義的函數處理!
定義一個處理函數:
設置成錯誤處理器:
有參數,四個參數:
級別,消息,文件,行號:
用戶定義的處理器一但設置,則系統的(報告,日志)就不可以使用了!
但是通過使 用戶的錯誤處理器返回 false的形式!。此時錯誤處理繼續交由系統的處理器來處理。
級別管理
每個標準錯誤都有一個級別:
在php中,是采用位運算的形式,管理各個標準的錯誤級別!
可以在php代碼中通過,php的預定義常量,看到,設置該級別:
例如:
系統觸發錯誤典型級別:E_NOTICE, E_WARNING,E_ERROR
用戶觸發錯誤的典型級別:E_USER_NOTICE, E_USER_WARNING, E_USER_ERROR
一個典型的是E_ALL,表示所有的錯誤級別:
操作
ini_set(‘error_reporting’, 2047);
開啟所有級別的錯誤:2047 二進制后,所有的位都為1!
生產環境與開發環境典型錯誤配置:
生產:開啟所有的錯誤級別,不顯示錯誤信息在頁面上,而記錄在日志內!
開發:開啟所有級別,顯示在頁面上。關閉錯誤日志!
項目中增加一個配置項,表示當前的環境模型:
dev,pro
增加一個配置項:
app/config/app.config.php
然后在framework中初始化
private static function initErrorHandler() {if('dev' == $GLOBALS['config']['app']['run_mode']) {ini_set('error_reporting', E_ALL | E_STRICT);ini_set('display_errors', 1);ini_set('log_errors', 0);} elseif ('pro' == $GLOBALS['config']['app']['run_mode']) {ini_set('display_errors', 0);ini_set('error_log', APP_DIR . 'error.log');ini_set('log_errors', 1);}}致命錯誤的處理
會終止腳本運行!
但是如果是用戶定義了錯誤處理器,并且觸發的是用戶的error級別,是可以恢復的(繼續執行)
但是,系統觸發的致命錯誤,是不能被自定義的處理器所處理的!
致命錯誤都會終止腳本執行么?
不是,用戶腳本觸發的致命錯誤在,用戶自定義了錯誤處理器時,可以恢復,腳本繼續運行!
http操作
PHP模擬GET請求
<?php$host_ip = '127.0.0.1'; $port = '80'; if (!$link = fsockopen($host_ip, $port)) {//連接失敗die('連接失敗'); } //var_dump($link);//構建get請求字符串數據 $request_str = 'GET /test.php HTTP/1.1' . "\r\n";//請求行 //請求頭 $request_str .= 'Host: shop.100.com' . "\r\n";//請求主機 $request_str .= 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0' . "\r\n";//請求代理標識,模擬的firefox //請求頭以空行結束 $request_str .= "\r\n";//空行 //沒有請求主體 //發請求 $result = fwrite($link, $request_str); //var_dump($result);//等待接收響應數據 echo '<pre>'; echo fread($link, 500); //while (!feof($link)) { // echo fgets($link); //}模擬POST請求
<?php$host_ip = '127.0.0.1'; $port = '80'; if (!$link = fsockopen($host_ip, $port)) {//連接失敗die('連接失敗'); }//post數據 $admin_name = 'admin'; $admin_pass = '1234abcd'; $post_data = 'username='.$admin_name . '&password='.$admin_pass; //username=admin&password=1234abcd//構建post請求字符串數據 $request_str = 'POST /index.php?p=back&c=Admin&a=signin HTTP/1.1' . "\r\n";//請求行 //請求頭 $request_str .= 'Host: shop.100.com' . "\r\n";//請求主機 $request_str .= 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0' . "\r\n";//請求代理標識,模擬的firefox //post數據相關的請求頭 $request_str .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";//post主體數據類型 $request_str .= 'Content-Length: ' . strlen($post_data) . "\r\n"; //請求頭以空行結束 $request_str .= "\r\n";//空行 //請求主體部分 $request_str .= $post_data;//不需要\r\n//發送! fwrite($link, $request_str);//獲得相應數據 echo '<pre>'; //echo fread($link, 8000); while (!feof($link)) {echo fgets($link); }http下載
下載,將服務器端的數據,保存到瀏覽器端!
http下載,就是將原本應該在瀏覽器上打開的數據數據,讓瀏覽器保存起來即可!
我們服務器需要工作:
1,將需要下載的內容輸出到瀏覽器!
2,告知瀏覽器,接收到的響應數據應該保存起來!、
利用一個響應頭,Content-disposition: 告知瀏覽器內容的處理方法:
Content-disposition: attachment 將響應數據作為附件來對待!
http下載,只是下載的響應主體!
<?php //test.php得到文件的標識,id //利用id,獲得文件信息。 $img_file = 'E:\php1016\apache\htdocs\shop\app\upload\2013111909\goods_528abce32871b.png';header('Content-Disposition: attachment; filename=' . basename($img_file)); $finfo = finfo_open(FILEINFO_MIME);//獲得一個可以得到文件的MIME信息的資源 $mime = finfo_file($finfo, $img_file);//利用這個資源獲得文件的信息 header('Content-Type: ' . $mime);readfile($img_file);curl:client URL
php的專門用于模擬請求的擴展!
(curl,獨立工具,php支持curl庫而已)
使用之前開啟擴展
<?php$curl = curl_init(); //var_dump($curl);curl_setopt($curl, CURLOPT_URL, 'http://shop.100.com/index.php?p=back&c=Admin&a=signin'); curl_setopt($curl, CURLOPT_POST, true);//false curl_setopt($curl, CURLOPT_POSTFIELDS, array('username'=>'admin', 'password'=>'1234abcd')); curl_setopt($curl, CURLOPT_HEADER, true); //curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_COOKIEJAR, 'e:/php1016/apache/htdocs/test/curl_cookie.txt'); echo '<pre>'; curl_exec($curl);curl_close($curl);控制緩存(瀏覽器的緩存
控制的瀏覽器端的緩存
典型的響應頭,Expires
告知瀏覽器,當前你收到的響應數據應該的有效期到什么時候!
header()函數完成響應頭的設置:
注意,此時的時間應該是一個格林威治平時
Tue, 19 Nov 2013 07:27:14 GMT
因此,Expires也是GMT時間:
gmdate();與date功能一致,將一個時間戳格式化成一個時間!date格式成本地時間(帶時區的)。而gmdate格式化成一個GMT時間!
<?phpheader('Expires: ' . gmdate('D, d M Y H:i:s', time()-1) . ' GMT'); header('Cache-Control: no-store no-cache must-revalidate'); echo gmdate('Y-m-d H:i:s'); echo '<br>'; echo gmdate('D, d M Y H:i:s') . ' GMT'; echo '<br>'; echo date('Y-m-d H:i:s'); echo '<hr>'; ?><a href="121.php">self</a>防止盜鏈
利用 請求時攜帶的請求頭:
referer:表示當前請求的來源!
只要在請求圖片的響應,判斷一下當前的請求是否是來源本站即可!
得到來源。判斷來源!
可以在Apache上,或者 php上都可以!
對于圖片的請求都是用php生成!
通過判斷$_SERVER[‘HTTP_REFERER’] 是否是當前網站!
PDO
另一種php操作mysql(或其他數據庫)方法
PHP Data Object
語法上 是 oop,對象編程(類,方法,屬性,對象)
PDO 是一個 數據庫抽象層,PDO可以完成對大多數主流數據的操作
pdo操作具體的數據庫,至少要:
開啟PDO,同時需要開啟相應的驅動!
PDO已經內置,mysql的驅動沒有內置!
簡單使用
/** * @abstract PDO study: PDO操作MySQL增刪查改類 * @date 2014/06/30 * @author Silov[bluebird237@gmail.com] */class mysqlPdoClass{private $db;private $db_name = 'study';private $db_serv;private $db_user = 'root';private $db_pass = 'root';private $db_host = 'localhost';//構造函數以及連接數據庫public function __construct( $username = 'root' , $password = 'root' , $connect = 'false'){$this->db_serv = "mysql:dbname=".$this->db_name.";host=".$this->db_host.";charset=utf-8";$this->db_user = $username;$this->db_pass = $password;}//管理數據庫,析構函數public function __destruct(){$this->db = null;}//執行SQL語句,返回值為sql執行結果public function run_query($sql){$this->db = new PDO($this->db_serv, $this->db_user, $this->db_pass);$this->db->query("set names utf8"); //設置PHP+MySQL連接的編碼格式為UTF8$row = $this->db->query($sql);$row->setFetchMode(PDO::FETCH_ASSOC); //設置查詢結果顯示為鍵值對數組模式return $row;}//執行SQL語句,返回值為sql影響行數public function run_exec($sql){$this->db = new PDO($this->db_serv, $this->db_user, $this->db_pass);$this->db->query("set names utf8"); //設置PHP+MySQL連接的編碼格式為UTF8return $this->db->exec($sql);}/*** @abstract 插入操作* @param $table:表名; $data:插入數據鍵值對數組; $return:是否返回值,為true時,返回插入字段的id; $debug:是否測試,true時返回sql語句,不執行* @return 返回值:插入結果,boolean*/public function data_insert($table, $data, $return = true,$debug=false){if(!$table) {return false;}$fields = array();$values = array();foreach ($data as $field => $value){$fields[] = '`'.$field.'`';$values[] = "'".addslashes($value)."'";}if(empty($fields) || empty($values)) {return false;}$sql = 'INSERT INTO `'.$table.'` ('.join(',',$fields).') VALUES ('.join(',',$values).')';if($debug){return $sql;}$query = $this->run_exec($sql);return $return ? $this->db->lastInsertId() : $query;}/*** @abstract 更新操作* @param $table:表名; $condition:更新查詢條件; $data:更新數據鍵值對數組; $limit:更新數據條數上限* $debug:測試時給true,則不執行update,直接返回完整的sql語句* @return 返回值:插入結果,boolean*/public function data_update($table, $condition, $data, $limit = 1,$debug=false) {if(!$table) {return false;}$set = array();foreach ($data as $field => $value) {$set[] = '`'.$field.'`='."'".addslashes($value)."'";}if(empty($set)) {return false;}$sql = 'UPDATE `'.$table.'` SET '.join(',',$set).' WHERE '.$condition.' '.($limit ? 'LIMIT '.$limit : '');if($debug){return $sql;}return $this->run_exec($sql);}/*** @abstract 查詢單個字段值* @param $sql:sql語句* @return 返回值:string*/public function getOne($sql){$row = $this->run_query($sql);$data = $row->fetch();$data = array_shift($data);return $data;}/*** @abstract 查詢單條記錄,多個字段* @param $sql:sql語句* @return 返回值:鍵值對數組*/public function getRow($sql){$row = $this->run_query($sql);$data = $row->fetch();return $data;}/*** @abstract 查詢多條記錄* @param $sql:sql語句* @return 返回值:以鍵值對數組為元素的數組*/public function getRows($sql){$row = $this->run_query($sql);$data = $row->fetchAll();return $data;} }預處理的SQL的執行方式
如果需要重復地執行結構相同的SQL。此時結構相同的SQL意味著SQL的編譯結果是類似的。除掉數據部分,在做重復的工作!
應該如何解決?
將SQL中結構相同的先提取,編譯。
將不一樣的地方,獨立的處理。
最后執行將數綁定了的結果可以
錯誤的處理
mysql的擴展,錯誤的處理方式不會主動報錯,稱之為 靜默模式。
pdo的錯誤的處理:默認的也是 靜默模式!
此時需要使用 錯誤函數獲得錯誤信息:
pdo?>errorCode();//獲得錯誤代碼pdo->errorInfo();//錯誤信息集合
pdo還支持其他的錯誤模式:
需要通過修改PDO對象的錯誤模式屬性進行修改:
pdo?>setAttribute()方法可以完成屬性的設置pdo->setAttribute(屬性名,屬性值);
靜默模式:Silent mode
默認的
警告模式:Warning Mode
一旦發生錯誤,則觸發一個警告級別的錯誤!
異常模式:Exception mode
異常,類似于標準錯誤,是屬于php在處理oop語法時,新的一種錯誤的處理模式!
分成三個階段進行處理:
拋出,監聽,捕獲!
使用如下語法實現:
拋出:throw
監聽:try
捕獲:catch
拋出的異常,其實就是將所有的錯誤信息封裝到一個異常對象中!
異常:語法上就是一個 內置的Exception類(其擴展類也算)的對象!
典型的語法如下:
<?php$dsn = 'mysql:dbname=itcast_shop;host=127.0.0.1;port=3306'; $username = 'root'; $password = '123456'; $pdo = new PDO($dsn, $username, $password); //設置錯誤模式: //$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); //$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);echo '<pre>'; try {$sql = 'show database';if (!$pdo->query($sql)) {echo $pdo->errorCode();echo '<br>';var_dump($pdo->errorInfo());} } catch (PDOException $e) {echo $e->getMessage(); }文件操作
<? class FSC{// 函數名: getfilesource // 功能: 得到指定文件的內容 // 參數: $file 目標文件 // test passed function getfilesource($file){if($fp=fopen($file,'r')){$filesource=fread($fp,filesize($file));fclose($fp);return $filesource;}elsereturn false; } // 函數名: writefile // 功能: 創建新文件,并寫入內容,如果指定文件名已存在,那將直接覆蓋 // 參數: $file -- 新文件名 // $source 文件內容 //test passed function writefile($file,$source){if($fp=fopen($file,'w')){$filesource=fwrite($fp,$source);fclose($fp);return $filesource;}elsereturn false; } // 函數名: movefile // 功能: 移動文件 // 參數: $file -- 待移動的文件名 // $destfile -- 目標文件名 // $overwrite 如果目標文件存在,是否覆蓋.默認是覆蓋. // $bak 是否保留原文件 默認是不保留即刪除原文件 // test passed function movefile($file,$destfile,$overwrite=1,$bak=0){if(file_exists($destfile)){if($overwrite)unlink($destfile);elsereturn false;}if($cf=copy($file,$destfile)){if(!$bak)return(unlink($file));}return($cf); }// 函數名: movedir // 功能: 這是下一涵數move的附助函數,功能就是移動目錄 function movedir($dir,$destdir,$overwrite=1,$bak=0){@set_time_limit(600);if(!file_exists($destdir))FSC::notfate_any_mkdir($destdir);if(file_exists($dir)&&(is_dir($dir))){if(substr($dir,-1)!='/')$dir.='/';if(file_exists($destdir)&&(is_dir($destdir))){if(substr($destdir,-1)!='/')$destdir.='/';$h=opendir($dir);while($file=readdir($h)){if($file=='.'||$file=='..'){continue;$file="";}if(is_dir($dir.$file)){if(!file_exists($destdir.$file))FSC::notfate_mkdir($destdir.$file);elsechmod($destdir.$file,0777);FSC::movedir($dir.$file,$destdir.$file,$overwrite,$bak);FSC::delforder($dir.$file);}else{if(file_exists($destdir.$file)){if($overwrite)unlink($destdir.$file);else{continue;$file="";}}if(copy($dir.$file,$destdir.$file))if(!$bak)if(file_exists($dir.$file)&&is_file($dir.$file))@unlink($dir.$file);}}}elsereturn false;}elsereturn false; } // 函數名: move // 功能: 移動文件或目錄 // 參數: $file -- 源文件/目錄 // $path -- 目標路徑 // $overwrite -- 如是目標路徑中已存在該文件時,是否覆蓋移動 // -- 默認值是1, 即覆蓋 // $bak -- 是否保留備份(原文件/目錄) function move($file,$path,$overwrite=1,$bak=0){if(file_exists($file)){if(is_dir($file)){if(substr($file,-1)=='/')$dirname=basename(substr($file,0,strlen($file)-1));else $dirname=basename($file);if(substr($path,-1)!='/')$path.='/';if($file!='.'||$file!='..'||$file!='../'||$file!='./')$path.=$dirname;FSC::movedir($file,$path,$overwrite,$bak);if(!$bak)FSC::delforder($file);}else{if(file_exists($path)){if(is_dir($path))chmod($path,0777);else {if($overwrite)@unlink($path);elsereturn false;}}elseFSC::notfate_any_mkdir($path);if(substr($path,-1)!='/')$path.='/';FSC::movefile($file,$path.basename($file),$overwrite,$bak);}}elsereturn false; } // 函數名: delforder // 功能: 刪除目錄,不管該目錄下是否有文件或子目錄,全部刪除哦,小心別刪錯了哦! // 參數: $file -- 源文件/目錄 //test passed function delforder($file) {chmod($file,0777);if (is_dir($file)) {$handle = opendir($file);while($filename = readdir($handle)) {if ($filename != "." && $filename != ".."){FSC::delforder($file."/".$filename);}}closedir($handle);return(rmdir($file));}else {unlink($file);} } // 函數名: notfate_mkdir // 功能: 創建新目錄,這是來自php.net的一段代碼.彌補mkdir的不足. // 參數: $dir -- 目錄名function notfate_mkdir($dir,$mode=0777){$u=umask(0);$r=mkdir($dir,$mode);umask($u);return $r; } // 函數名: notfate_any_mkdir // 功能: 創建新目錄,與上面的notfate_mkdir有點不同,因為它多了一個any,即可以創建多級目錄 // 如:notfate_any_mkdir("abc/abc/abc/abc/abc") // 參數: $dirs -- 目錄名function notfate_any_mkdir($dirs,$mode=0777) {if(!strrpos($dirs,'/')){return(FSC::notfate_mkdir($dirs,$mode));}else{$forder=explode('/',$dirs);$f='';for($n=0;$n<count($forder);$n++){if($forder[$n]=='') continue;$f.=((($n==0)&&($forder[$n]<>''))?(''):('/')).$forder[$n];if(file_exists($f)){chmod($f,0777);continue;}else{if(FSC::notfate_mkdir($f,$mode)) continue;elsereturn false;}}return true;} } } ?>總結
以上是生活随笔為你收集整理的PHP之MVC项目实战(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链表简单实现(增删查改)
- 下一篇: 7.Mysql数据库表引擎与字符集