一.基礎
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或env變量中的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 id from user where username >= 'u' AND username < 'v';
5.使用批量插入,節省互動
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` (
`uid` int(11) DEFAULT NULL,
`fuid` int(11) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
3.建立feed_broadcast
CREATE TABLE `feed_broadcast` (
`title` char(255) DEFAULT NULL,
`is_read` tinyint(4) DEFAULT '0' COMMENT '是否已讀',
`time` int(11) DEFAULT NULL,
4.建立article表
CREATE TABLE `article` (
建立觸發器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` (
`uid` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`content` varchar(500) NOT NULL,
`time` int(11) NOT NULL,
建立觸發器trigger_insert_queue
create trigger trigger_insert_queue after insert on feed for each row
insert into queue(uid,feed_id,content,status,time) values(New.uid,New.id,'釋出了文章',0,New.time)
6.建立queue表:
CREATE TABLE `queue` (
`feed_id` int(11) NOT NULL,
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0未未處理,1為處理',
) 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%';