php核心技術與最佳實踐知識點(上)
一.基礎
1.serialize:序列化一個類,只是保存了類的屬性,所以還需要反序列化unserialize的時候包含該類.
2.對於將array轉為object,這個轉換因為沒有具體的類,所以稱為了一個孤類:
<?php $arr = [1,2]; var_dump((object) $arr); 輸出 object(stdClass)#1 (2) { [0]=> int(1) [1]=> int(2) }
3.其他語言的多態是向上轉型,php的多態沒有轉型,只是調用了不同的派生類.
4.接口是一種契約,但php的接口並不具備約束的能力.
interface Engine { public function run(); } class Car implements Engine { public function run() { echo ‘run‘ . PHP_EOL; } public function fly() { echo ‘fly‘ . PHP_EOL; } } function check(Engine $e) { $e->fly(); } $car = new Car(); check($car);
我們在函數check中指明傳入的參數應該是一個符合Engine的接口約束,但實際上並沒有起到這個作用.php只關心傳入的對象是否實現了接口中定義的方法,並不關心接口的語義是否正確.
5.為了彌補php缺乏多重繼承的缺陷,自php5.4.0起,php引入了Trait,實現代碼復用.Trait可以被視為一種加強型的接口
trait HelloWorld { public function sayHello() { echo ‘Hello World!‘; } } class TheWorldIsNotEnough { use HelloWorld; } $o = new TheWorldIsNotEnough(); $o->sayHello();
6.反射API:ReflectionClass
7.tokenizer
tokenizer函數提供了一個內嵌在Zend引擎的"PHP tokenizer"的調用接口。使用這些函數,你可以寫出你自己的PHP源碼分析或者修改工具,而無需處理詞法分析級別上的語言規範。
示例代碼: $tokens = token_get_all(‘<?php echo 1; ?>‘); print_r($tokens);
8.php中的錯誤指腳本運行不正常,異常是業務流程不正常.php中的異常需要手動拋出,意義不是很大.
9.set_error_handler():自定義錯誤,如果自定義錯誤,則錯誤抑制符失效.
function handler($errorNo, $errorStr, $errorFile, $errorLine) { die($errorStr); } set_error_handler(‘handler‘, E_ALL); @$a[500];
自定義錯誤和異常想結合的一個例子:
function handler($errorNo, $errorStr, $errorFile, $errorLine) { throw new Exception($errorStr, $errorNo); } set_error_handler(‘handler‘, E_ALL); try { @$a[500]; } catch (Exception $e) { echo $e->getMessage(); }
10.捕獲致命錯誤fetal error:register_shutdown_function
function shutdown() { //echo error_get_last(); echo ‘ok‘; } register_shutdown_function(‘shutdown‘); a;
產生致命錯誤時,會回調shutdown,提供一些補救機會,但程序終止是必然的.
11.語法錯誤(parse error)處理:計入日誌,register_shutdown_function並不會對語法錯誤進行處理.
php.ini log_error=on error_log=/usr/log/php-error.log
12.觸發錯誤:trigger_error
<?php function test() { trigger_error(‘手動觸發錯誤‘); } test();
13.面向對象的一個基本例子:
// domain class Message { public function setMsg() { } public function getMsg() { } } // model class MessageModel { // 從數據庫讀 public function read() { } // 從數據庫寫 public function write(Message $data) { } // 分頁 public function page() { } } // 邏輯層 class LogicMessage { public function write(MessageModel $model, Message $data) { $model->write($data); } public function view(MessageModel $model) { return $model->read(); } } // 控制器 class ActionMessage { public function write(LogicMessage $logicMessage, MessageModel $model, Message $data) { $logicMessage->write($model, $data); } public function read(MessageModel $model) { return $model->read(); } } // 調用 $message = new Message(); $logic = new LogicMessage(); $model = new MessageModel(); $action = new ActionMessage(); $action->write($logic, $model, $message); $action->read($model);
14.ignore_user_abort(true); //用戶即便關閉網頁,程序在後臺執行不會中斷.
二.http協議
1.cookie
服務器發給客戶端使用:Set-Cookie報頭
客戶端發給服務器使用Cookie報頭
兩者區別是Cookie報頭的value可以有多個Cookie值,並且不需要指定domain;Set-Cookie只能有一個值,並且需要指明path和domain
2.ip在tcp層傳遞,因此PHP中的SERVER變量中的HTTP_CLIENT_IP或REMOTE_ADDR,兩者是難以偽造的.而HTTP_X_FORWARDED_FOR來自於與http請求中的X FORWARDED FOR報頭,而這個報頭是可以修改的.
三.cUrl
cUrl中的批處理,異步執行: $ch1 = curl_init(); $ch2 = curl_init(); curl_setopt($ch1, CURLOPT_URL, ‘http://www.baidu.com‘); curl_setopt($ch1, CURLOPT_HEADER, 0); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch2, CURLOPT_URL, ‘http://news.baidu.com‘); curl_setopt($ch2, CURLOPT_HEADER, 0); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); $mh = curl_multi_init(); curl_multi_add_handle($mh, $ch1); curl_multi_add_handle($mh, $ch2); // 執行批處理 do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active and $mrc == CURLM_OK) { if(curl_multi_select($mh) === -1){ usleep(100); } do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } // 獲取內容 $content1 = curl_multi_getcontent($ch1); $content2 = curl_multi_getcontent($ch2); // 移除句柄,否則造成死循環 curl_multi_remove_handle($mh, $ch1); curl_multi_remove_handle($mh, $ch2); curl_multi_close($mh); echo $content1; echo $content2;
四.cookie
1.setrawcookie和setcookie的區別是,前者不對值進行urlencode
2.php並沒有設置cookie,只是通知瀏覽器來設置cookie.cookie設置在當前頁不會生效,在下一個頁面才會生效.
3.cookie是http頭的一部分,因此在設置cookie前,不能有輸出.
4.每個域名下允許的cookie是有限制的,ie8是50個,firefox是150個
5.一個域名的每個cookie限制大小為4kb
五.session
1.php.ini中的Session設置中的session.save_path="N;MODE;/path"
N:表示目錄級數;如為2表示2級目錄存放,每一級有0-9和a-z共36個字符作為目錄名,2級則可以有36*36個目錄
MODE:表示目錄權限,默認為600
path:表示存放路徑
2.對於設置分級目錄存儲的session,php不會自動回收,需要自己實現回收機制
3.訪問量大的站點,默認的文件session並不適合,可以用數據庫或內存緩存
六.基本SQL優化的十個原則
1.避免在列上進行運算,這將使索引失效.
SELECT * FROM table WHERE YEAR(d) > 2017;
優化為
SELECT * FROM table WHERE d > ‘2017-01-01;
2.使用join時,小結果集驅動大結果集,同時把復雜的join查詢拆分為多個query.因為join多個表時,導致更多的鎖定和阻塞.
內聯結時MySQL自動用小結果集驅動大結果集.
左聯結和右聯結,則需要作出優化
3.避免like的模糊查詢
select * from user where username like ‘%u%‘;
優化為:
select * from user where username >= ‘u‘ AND username < ‘v‘;
或
select * from user where username like ‘u%‘;
4.列出僅需要的字段,這對速度不會有影響,主要考慮節省內存.
select * from user where username >= ‘u‘ AND username < ‘v‘;
優化為
select id from user where username >= ‘u‘ AND username < ‘v‘;
5.使用批量插入,節省交互
insert into table values(‘a‘, ‘b‘, ‘c‘);
insert into table values(‘a‘, ‘b‘, ‘c‘);
insert into table values(‘a‘, ‘b‘, ‘c‘);
優化為:
insert into table values(‘a‘, ‘b‘, ‘c‘),(‘a‘, ‘b‘, ‘c‘),(‘a‘, ‘b‘, ‘c‘);
6.limit基數比較大時使用between
select * from user order by id asc limit 1000000,10
優化為:
select * from user where id between 1000000 and 1000010 order by id asc
但是如果有斷行,該項將不適用 .
7.不用使用rand返回隨機數據.
8.避免使用NULL,MYSQL難以優化引用了可空列的查詢
9.不用使用count(id),而應使用count(*)
10.不要做無謂的排序操作,應盡可能在索引中完成排序.
七.MySQL
1.explain:保證type達到range級別,最好是ref級別,All為全表掃描,這是最壞的情況
2.簡單的確定讀寫比例(R/W):
show global status;
com_select為讀,com_update+com_insert為寫
如果R/W小於10:1,則認為是以寫為主的數據庫.
3.MyISAM:適當增加key_buffer_size;可以根據key_cache的命中率進行計算
show global status like ‘key_read%‘;
緩存未命中率:key_cache_miss_rate=Key_reads/Key_read_requests * 100%,如果大於1%就要適當增加key_buffer_size
還要註意table_cache的設置.如果該值過小,mysql就會反復打開,關閉frm文件.如果過大,又會造成cpu的浪費.一個合理的辦法是觀察opened_tables的值,如果持續增長,就要增加table_cache的值.
4.InnoDB:重點註意innodb_buffer_pool_size;
5.從表中刪除大量行時,可運行 optimize table tableName;
八.MySQL模擬消息隊列
0:流程:
用戶發布一篇文章(insert into article),觸發tigger_insert_feed,向feed表插入一條動態,觸發tigger_insert_queue,定時器event_push_broadcast每1分鐘從queue中提取數據到feed_broadcast.
1.新建user表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` char(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
2.新建friend表
CREATE TABLE `friend` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`fuid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
3.新建feed_broadcast
CREATE TABLE `feed_broadcast` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`title` char(255) DEFAULT NULL,
`is_read` tinyint(4) DEFAULT ‘0‘ COMMENT ‘是否已讀‘,
`time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
4.新建article表
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`title` char(255) DEFAULT NULL,
`time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
建立觸發器trigger_insert_feed
create trigger trigger_insert_feed after insert on article for each row
begin
insert into feed(uid,article_id,content,time) values(New.uid, New.id,‘發布了文章‘,New.time);
end
5.新建feed表
CREATE TABLE `feed` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`content` varchar(500) NOT NULL,
`time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
建立觸發器trigger_insert_queue
create trigger trigger_insert_queue after insert on feed for each row
begin
insert into queue(uid,feed_id,content,status,time) values(New.uid,New.id,‘發布了文章‘,0,New.time)
end
6.建立queue表:
CREATE TABLE `queue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`feed_id` int(11) NOT NULL,
`content` varchar(500) NOT NULL,
`status` tinyint(4) NOT NULL DEFAULT ‘0‘ COMMENT ‘0未未處理,1為處理‘,
`time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk
7.創建存儲過程push_broadcast:
CREATE DEFINER=`root`@`localhost` PROCEDURE `push_broadcast`()
BEGIN
insert into feed_broadcast(uid,title,time) select ef.fuid,tmp.content,tmp.time from (select * from queue where status=0 order by id desc limit 10) tmp,friend ef where tmp.uid=ef.uid;
update queue set status=1 where status=0 order by id desc limit 10;
END
8.創建一個定時事件event_push_broadcast:
CREATE DEFINER=`root`@`localhost` EVENT `event_push_broadcast` ON SCHEDULE EVERY 1 MINUTE STARTS ‘2017-11-12 17:53:58‘ ON COMPLETION NOT PRESERVE ENABLE DO call push_broadcast()
查看event_scheduler是否是開啟狀態
show global variables like ‘%sche%‘;
如果event_scheduler為off;
開啟:
set global event_scheduler=on;
9.至此可以插入數據做測試了
php核心技術與最佳實踐知識點(上)