Relearn PHP
PHP7 release也一段時間了,最近在開發常會看到相關的文章,最近抽個空好好來重新學習PHP.
Preface
"PHP: Hypertext Preprocessor",官方網站表明PHP很適合做web development,但是並不局限於此,亦可做Process Control,但是依據個人經驗,PHP5.3以下的版本做浮點數的運算不是很精準.
另外在2015年PHP7問世,效率更是翻倍,但是有著相容性問題,系統升級前需要做好測試.
環境建置
採用Virtualbox+Vagrant來架設環境,執行步驟如下:
安裝Virtualbox與Vagrant,這邊就不講解了,幾本上下一步到底
下載Vagrant box
1$ vagrant box add rasmus/php7dev 2$ vagrant init rasmus/php7dev 3# 確認box list 4$ vagrant box list
git clone
1git clone https://github.com/rlerdorf/php7dev.git 2cd php7dev
共享目錄設定,編輯php7dev.yaml檔案
1folders: 2- map: www 3 to: /var/www/default
啟動VM、進入VM、暫停VM、註銷VM
1# 啟動VM 2$ vagrnat up 3# 進入VM 4$ vagrant ssh 5# 暫停VM 6$ vagrant halt 7# 註銷VM 8$ vagrant destroy --force
建立檔案
1# 建立一個 index.php 檔案, 運行可以顯示 PHP 資訊 2$ echo "<?php echo phpinfo(); ?>" > /var/www/default/index.php
環境建立完成,再來可以透過Browser輸入http://192.168.7.7/
來進入站台.
Type
PHP是屬於弱型別的語言,也就是變數不用宣告就直接可以做assign,但是資料型態還是程式語言的重點基礎之一,還是要好好的認識一下.
Boolean:通常是一個常量
true
或false
,不區分大小寫.1$var1 = true; 2$var2 = False;
Integer:是一個有限整數((2^32)-1),集合中的某個數Z = {..., -2, -1, 0, 1, 2, ...}
1//自PHP5.4.0後可以採用十進制、十六進制、八進制、二進制 2$var = 1234; //十進制 3$var = -123; //負數 4$var = 0123; //八進制 5$var = 0x1A; //十六進制 6$var = 0b11111111; //二進制
Float:也就是小數,長度取決於平台. 另外不要相信浮點數結果精確到了最後一位,也永遠不要比較兩個浮點數是否相等,如果確實需要更高的精度,應該使用任意精度數學函數或者gmp函數.
1$var = 1.23456789;
String:每個字都是由一系列的character(字元)組成(字的編碼),每個字元相當於一個byte(2^8),也就是說PHP只支援到256-character,因此不支援Unicode(因為過長).
1$name = 'ting'; 2$var = 'my name is $name'; //全視為為字串 3$var = "my name is $name"; //對$name進行解析 4//Heredoc結構,自PHP5.3起,會對變數進行解析 5$var = <<<EOT 6my name is $name 7 EOT; 8//newcode結構,全視為字串,不對變數進行解析 9$var = <<<'EOT' 10my name is $name 11 EOT;
Array:是一組有序映射,官網說"it can be treated as an array, list (vector), hash table (an implementation of a map), dictionary, collection, stack, queue, and probably more.",由此可見他的功能很廣.
1$array = array( 2 "foo" => "bar", 3); 4// 自 PHP 5.4 起 5$array = [ 6 "foo" => "bar", 7];
Object:物件.
1class foo 2{ 3 function do_foo() 4 { 5 echo "Doing foo."; 6 } 7} 8$bar = new foo; 9$bar->do_foo();
Resource:資源,通常可能是連接資料庫、開啟文件.
NULL:指一個變數還未被assign,或著被釋放
1$var = NULL;
Callback
1error_reporting(E_ALL); 2function increment(&$var) 3{ 4 $var++; 5} 6$a = 0; 7call_user_func('increment', $a); 8echo $a."\n"; 9call_user_func_array('increment', array(&$a)); 10echo $a."\n";
Variables
變數的命名很重要,程式要寫的漂亮的重點之一就是變數的命名.
基本變數:PHP中宣告變數是使用符號*$*後面再跟上'^[a-zA-Z][_]?[\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*',且命名是有大小寫的區別.
1$var = 'Bob'; 2$bar = &$var; //$bar被assign變數$var,且記憶體位置相同
全域變數與區域變數
1$a = 1; 2function Test() 3{ 4 global $a; 5 $b = 2; 6 echo ($a+$b); 7} 8echo $b; //Undefined variable 9Test();
Static variable:靜態變數只會在函數中出現,當程序離開函式,其值不丟失.從下面範例可以發現,在$b被宣告時,都還是NULL的狀態,宣告後才會取回值.
1function Test() 2{ 3 var_dump($b); 4 static $b = 0; 5 var_dump($b); 6 echo (++$b); 7} 8Test();
Variable variables:變數的名稱也是變數,這樣的特性讓程式變得更彈性.
1$a = 'hello'; 2$hello = 'world'; 3$world = 'a'; 4echo $a.'<br/>'; 5echo $$a.'<br/>'; 6echo $$$a.'<br/>'; 7echo "$a ${$a} <br/>"; 8echo "$a $hello <br/>";
Constants
常量定義好後,就無法變更,且長量名稱區分大小寫.
1define("version","0.1");
2//magic constants
3function a(){
4 echo __function__;
5}
6function b(){
7 echo __function__;
8}
9echo a();
10echo b();
Operators
運算子有先後順序,個人習慣將運算子拆開,一來確保程式的穩定度,二來增加程式的可讀性
1$a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2
2$a = 1;
3$b = 2;
4$a = $b += 3; // $a = ($b += 3) -> $a = 5, $b = 5
算數運算子
1echo (5 + 3)."\n"; //prints 8 2echo (5 - 3)."\n"; //prints 2 3echo (5 * 3)."\n"; //prints 15 4echo (15 / 3)."\n"; //prints 5 5echo (5 % 3)."\n"; //prints 2 6echo (5 3)."\n"; //prints 125
位元運算子
1echo 0b0010 & 0b0010; //print 2 2echo 0b0010 | 0b0010; //print 2 3echo 0b0010 ^ 0b0011; //print 1 4echo ~0b0101; //print -6 5echo 0b0010 >> 1; //print 1 6echo 0b0010 << 1; //print 4
比較運算子
1var_dump(0 == "a"); //0 == 0 -> true 2var_dump("1" == "01"); //1 == 1 -> true 3var_dump("10" == "1e1"); //10 == 10 -> true 4var_dump(100 == "1e2"); //100 == 100 -> true 5var_dump("1e1"); //string '1e1'
錯誤控制運算子
1@include('./not_exist_file.php'); 2$x = @$a["name"];
Execution Operators:以前都用exec(),這個好方便
1$mkdir = `mkdir new floder`; 2$output = `ls -al`; 3echo "<pre>$output</pre>";
遞增/遞減運算子
1$a = 5; 2echo $a++."<br />"; //print 5 3echo ++$a."<br />"; //print 7 4echo $a--."<br />"; //print 7 5echo --$a."<br />"; //print 5
Logical Operators
1//"||" 比 "or" 的優先级高,"&&" 比 "and" 的優先级高 2$e = false || true; //bool(true),等同于:($e = (false || true)) 3$f = false or true; //bool(false),等同于:(($f = false) or true) 4$g = true && false; //bool(false),等同于:($g = (true && false)) 5$h = true and false; //bool(true),等同于:(($h = true) and false)
String Operators
1$a = "Hello "; 2$b = $a . "World!"; // now $b contains "Hello World!" 3$a = "Hello "; 4$a .= "World!"; // now $a contains "Hello World!"
Array Operators
1$a = array("a" => "apple", "b" => "banana"); 2$b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); 3$c = $a + $b; // Union of $a and $b 4var_dump($c); // print array(3) {["a"]=>"apple",["b"]=>"banana",["c"]=>"cherry"}
Type Operators
1class ParentClass{} 2class MyClass extends ParentClass{} 3$a = new MyClass; 4var_dump($a instanceof MyClass); //bool(true) 5var_dump($a instanceof ParentClass); //bool(true)
Control Structures
通常要呈現一個商業邏輯都會透過流程控制來表示,而每種商業邏輯呈現的方法百百種,所以要築構一支好的程式,對流程控制的熟悉度不可少.
if else
1if ($a > $b): 2 echo "a is bigger than b"; 3elseif ($a < $b): 4 echo "a is smaller than b"; 5else: 6 echo "a is equal b"; 7endif
while:循環語法之一,在遞迴中很好用
1while ($i <= 10): 2 echo $i++; 3endwhile
do-while:跟while很像,差異在於while在執行前做判斷,do-while在執行後做判斷.
1do { 2 echo $i; 3} while ($i <= 10);
for
1for (;;): 2 if ($i > 10): 3 break; 4 endif; 5 echo $i; 6 $i++; 7endfor;
foreach:用來解構array很好用
1foreach (array(1, 2, 3, 4) as $value): 2 $value = $value * 2; 3endforeach;
break
1$arr = array('one', 'two', 'three', 'four', 'stop', 'five'); 2while (list (, $val) = each($arr)) { 3 if ($val == 'stop') { 4 break; /* You could also write 'break 1;' here. */ 5 } 6 echo "$val<br />\n"; 7}
continue
1for ($i = 0; $i < 5; ++$i) { 2 if ($i == 2) 3 continue; 4 print "$i\n"; 5}
switch:在做Boolean值判斷的時候建議使用if else,若做值得判斷則可使用switch
1switch ($i) { 2 case 0: 3 echo "i equals 0"; 4 break; 5 case 1: 6 case 2: 7 echo "i equals to 1, 2"; 8 break; 9 default: 10 echo "i is not equal to 0, 1 or 2"; 11}
declare:Zend引擎每執行1條低級語句就去執行一次register_tick_function()注冊的函數
1declare(ticks=1); 2// A function called on each tick event 3function tick_handler() { 4 echo "tick_handler() called\n"; 5} 6register_tick_function('tick_handler'); 7$a = 1; 8if ($a > 0) { 9 $a += 2; 10 print($a); 11}
return:如果在全局範圍中調用,則當前腳本文件中止運行.
require/include/require_once/include_once
1require('somefile.php'); //若找不到檔案發出E_COMPILE_ERROR级别錯誤,並終止程序 2include('somefile.php'); //若找不到檔案發出E_WARNING级别錯誤,繼續程序 3require_once('somefile.php'); //和require一樣,但已經包含過的檔案則不會再包含 4include_once('somefile.php'); //和include一樣,但已經包含過的檔案則不會再包含
goto:只在PHP 5.3以上版本有效,跳轉至改文件的目標.目標位置只能位於同一個文件和作用域,也就是說無法跳出一個函數或類方法,也無法跳入到另一個函數.也無法跳入到任何循環或者switch結構中.可以跳出循環或者switch,通常的用法是用goto代替多層的break.
1//下面結果輸出Bar 2goto a; 3echo 'Foo'; 4a: 5echo 'Bar';
Function
這邊要注意部分預設函式是跟著extension加載的,例如mysql_connect()、imagecreatetruecolor()等,所以必須知道自己的環境是否有這些extension,可以用phpinfo()或get_loaded_extensions()來查詢. 函數名稱的命名很重要,好的命名可以讓程式可讀性提升,更好維護.
User-defined functions
1function sum($a, $b): float { 2 return $a + $b; 3}
Variable functions
1function foo() { 2 echo "In foo()<br />\n"; 3} 4$func = 'foo'; 5$func(); //This calls foo()
Anonymous functions
1echo preg_replace_callback('~-([a-z])~', function ($match) { 2 return strtoupper($match[1]); 3}, 'hello-world');
Classes and Objects
自從PHP5後的版本有著較好的效能與功能
class:每個類別都有自己的constants、variables與function,並且可使用$this來調用自己.
1class SimpleClass 2{ 3 // property declaration 4 public $var = 'a default value'; 5 // method declaration 6 public function displayVar() { 7 echo $this->var; 8 } 9}
Object Assignment:宣告物件的兩種方式.
1Class Object{ 2 public $foo="bar"; 3}; 4$objectVar = new Object(); 5$reference =& $objectVar; 6$assignment = $objectVar; 7$objectVar=null; 8var_dump($objectVar); //NULL 9var_dump($reference); //NULL,指向同樣的記憶體,所以$objectVar=null被註銷時,一併註銷 10var_dump($assignment); //object(Object),被clone
extends:一個class可以申明繼承另一個class的方法與屬性,不支援多個繼承.可以使用同名稱覆蓋父類別的方法,但是父類別使用final,則不能被覆蓋.被覆蓋的方法或屬性,可以透過parent::來訪問.
1class ExtendClass extends SimpleClass 2{ 3 // Redefine the parent method 4 function displayVar() 5 { 6 echo "Extending class\n"; 7 parent::displayVar(); 8 } 9}
Properties,類別的成員.
1class SimpleClass 2{ 3 public $var1 = (1+2); 4 public $var2 = myConstant; 5 public $var3 = array(true, false); 6}
Class Constants:在類別中不變的值.
1class MyClass 2{ 3 const constant = 'constant value'; 4 function showConstant() { 5 echo self::constant . "\n"; 6 } 7}
Autoloading Classes:可以不用透過include檔案來加載類別,可以透過__autoload()或spl_autoload_register() 函数自動註冊類別.
1spl_autoload_register(function ($class_name) { 2 require_once $class_name . '.php'; 3}); 4$obj = new MyClass1(); 5$obj2 = new MyClass2();
Constructors and Destructors,在創建時會先調用__construct,註銷或程序結束時會調用__destruct.
1class MyDestructableClass { 2 function __construct() { 3 print "In constructor\n"; 4 $this->name = "MyDestructableClass"; 5 } 6 function __destruct() { 7 print "Destroying " . $this->name . "\n"; 8 } 9} 10$obj = new MyDestructableClass(); 11unset($obj);
Visibility:包含public、protected、private.
1class MyClass 2{ 3 public $public = 'Public'; 4 protected $protected = 'Protected'; 5 private $private = 'Private'; 6 function printHello() 7 { 8 echo $this->public; 9 echo $this->protected; 10 echo $this->private; 11 } 12} 13$obj = new MyClass(); 14echo $obj->public; //public 15echo $obj->protected; //產生錯誤 16echo $obj->private; //產生錯誤 17$obj->printHello(); //印出Public、Protected和Private
Scope Resolution Operator(::):用於訪問類別的靜態成員、常量或函式.
1class MyClass { 2 const CONST_VALUE = 'A constant value'; 3} 4echo MyClass::CONST_VALUE;
Class Abstraction:繼承抽象類別的子類別,強制要求一定要有父類別定義的抽象函式的實作.這樣的好處是統一子類別中的實作格式.
1abstract class AbstractClass 2{ 3 //抽象方法僅需要定義需要的参数 4 abstract protected function prefixName($name); 5} 6class ConcreteClass extends AbstractClass 7{ 8 //子類別可以定義父類別抽象方法中不存在的可選参数 9 public function prefixName($name, $separator = ".") { 10 if ($name == "Pacman") { 11 $prefix = "Mr"; 12 } elseif ($name == "Pacwoman") { 13 $prefix = "Mrs"; 14 } else { 15 $prefix = ""; 16 } 17 return "{$prefix}{$separator} {$name}"; 18 } 19} 20$class = new ConcreteClass; 21echo $class->prefixName("Pacman"), "\n"; //Mr. Pacman 22echo $class->prefixName("Pacwoman"), "\n"; //Mrs. Pacwoman
Object Interfaces,透過implements導入多個介面接口,只能定義方法,但不能實作.
1interface iTemplate 2{ 3 public function setVariable($name, $var); 4 public function getHtml($template); 5} 6class Template implements iTemplate 7{ 8 private $vars = array(); 9 public function setVariable($name, $var) 10 { 11 $this->vars[$name] = $var; 12 } 13 public function getHtml($template) 14 { 15 foreach($this->vars as $name => $value) { 16 $template = str_replace('{' . $name . '}', $value, $template); 17 } 18 return $template; 19 } 20}
Class Abstraction(抽象類別)與Object Interfaces(介面)的差異性如下面表格.
類別(Class) 抽象類別(Abstract Class) 介面(Interface) 宣告屬性(attribute) O O X 常數(const) O O O 實例化(new class) O X X 抽象方法(abstract function) X O O 實作方法內容(functoin()) O O X 類別是否可繼承多個 X X O Trait:自PHP5.4.0起可使用.介於繼承與介面之間的功能,可以參考Trait.
1trait PrivateTrait 2{ 3 private privateFunc() 4 { 5 echo "This is private"; 6 } 7} 8class PrivateClass 9{ 10 use privateTrait; 11 // This is allowed. 12 public publicFunc() 13 { 14 $this->privateFunc(); 15 } 16}
Anonymous classes.PHP7開始支持匿名類別.匿名類別可以創建一次性的簡單對象.
Overloading:動態創建成員與方法.
1public void __set ( string $name , mixed $value ) 2public mixed __get ( string $name ) 3public bool __isset ( string $name ) 4public void __unset ( string $name ) 5 6class PropertyTest { 7 private $data = array(); 8 public $declared = 1; 9 private $hidden = 2; 10 public function __set($name, $value) 11 { 12 $this->data[$name] = $value; 13 } 14 public function __get($name) 15 { 16 if (array_key_exists($name, $this->data)) { 17 return $this->data[$name]; 18 } 19 $trace = debug_backtrace(); 20 trigger_error( 21 'Undefined property via __get(): ' . $name . 22 ' in ' . $trace[0]['file'] . 23 ' on line ' . $trace[0]['line'], 24 E_USER_NOTICE); 25 return null; 26 } 27 public function __isset($name) 28 { 29 return isset($this->data[$name]); 30 } 31 public function __unset($name) 32 { 33 unset($this->data[$name]); 34 } 35 public function getHidden() 36 { 37 return $this->hidden; 38 } 39} 40$obj = new PropertyTest; 41$obj->a = 1; 42echo $obj->a; //1 43var_dump(isset($obj->a)); //true 44unset($obj->a); 45var_dump(isset($obj->a)); //false
Magic Methods
- __sleep()
- __wakeup()
- __construct()
- __destruct()
- __call()
- __callStatic()
- __get()
- __set()
- __isset()
- __unset()
- __sleep()
- __wakeup()
- __toString()
- __invoke()
- __set_state()
- __clone()
- __debugInfo()