1. 程式人生 > >php核心技術與最佳實踐知識點(上)

php核心技術與最佳實踐知識點(上)

mode roc url mys 修改 class null nbsp (上)

一.基礎

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核心技術與最佳實踐知識點(上)