Relearn PHP

PHP7 release也一段時間了,最近在開發常會看到相關的文章,最近抽個空好好來重新學習PHP.

Preface

"PHP: Hypertext Preprocessor",官方網站表明PHP很適合做web development,但是並不局限於此,亦可做Process Control,但是依據個人經驗,PHP5.3以下的版本做浮點數的運算不是很精準.

另外在2015年PHP7問世,效率更是翻倍,但是有著相容性問題,系統升級前需要做好測試.

環境建置

採用Virtualbox+Vagrant來架設環境,執行步驟如下:

  1. 安裝Virtualbox與Vagrant,這邊就不講解了,幾本上下一步到底

  2. 下載Vagrant box

    1$ vagrant box add rasmus/php7dev
    2$ vagrant init rasmus/php7dev
    3# 確認box list
    4$ vagrant box list
    
  3. git clone

    1git clone https://github.com/rlerdorf/php7dev.git
    2cd php7dev
    
  4. 共享目錄設定,編輯php7dev.yaml檔案

    1folders:
    2- map: www
    3  to: /var/www/default
    
  5. 啟動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
    
  6. 建立檔案

    1# 建立一個 index.php 檔案, 運行可以顯示 PHP 資訊
    2$ echo "<?php echo phpinfo(); ?>" > /var/www/default/index.php
    

環境建立完成,再來可以透過Browser輸入http://192.168.7.7/來進入站台.


Type

PHP是屬於弱型別的語言,也就是變數不用宣告就直接可以做assign,但是資料型態還是程式語言的重點基礎之一,還是要好好的認識一下.

  • Boolean:通常是一個常量truefalse,不區分大小寫.

    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

變數的命名很重要,程式要寫的漂亮的重點之一就是變數的命名.

  1. 基本變數:PHP中宣告變數是使用符號*$*後面再跟上'^[a-zA-Z][_]?[\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*',且命名是有大小寫的區別.

    1$var = 'Bob';
    2$bar = &$var; //$bar被assign變數$var,且記憶體位置相同
    
  2. 全域變數與區域變數

    1$a = 1;
    2function Test()
    3{
    4    global $a;
    5    $b = 2;
    6    echo ($a+$b);
    7}
    8echo $b; //Undefined variable
    9Test();
    
  3. Static variable:靜態變數只會在函數中出現,當程序離開函式,其值不丟失.從下面範例可以發現,在$b被宣告時,都還是NULL的狀態,宣告後才會取回值.

    1function Test()
    2{
    3    var_dump($b);
    4    static  $b = 0;
    5    var_dump($b);
    6    echo (++$b);
    7}
    8Test();
    
  4. 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
  1. 算數運算子

    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
    
  2. 位元運算子

    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
    
  3. 比較運算子

    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'
    
  4. 錯誤控制運算子

    1@include('./not_exist_file.php');
    2$x = @$a["name"];
    
  5. Execution Operators:以前都用exec(),這個好方便

    1$mkdir = `mkdir new floder`;
    2$output = `ls -al`;
    3echo "<pre>$output</pre>";
    
  6. 遞增/遞減運算子

    1$a = 5;
    2echo $a++."<br />"; //print 5
    3echo ++$a."<br />"; //print 7
    4echo $a--."<br />"; //print 7
    5echo --$a."<br />"; //print 5
    
  7. 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)
    
  8. 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!"
    
  9. 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"}
    
  10. 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

通常要呈現一個商業邏輯都會透過流程控制來表示,而每種商業邏輯呈現的方法百百種,所以要築構一支好的程式,對流程控制的熟悉度不可少.

  1. 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
    
  2. while:循環語法之一,在遞迴中很好用

    1while ($i <= 10):
    2    echo $i++;
    3endwhile
    
  3. do-while:跟while很像,差異在於while在執行前做判斷,do-while在執行後做判斷.

    1do {
    2   echo $i;
    3} while ($i <= 10);
    
  4. for

    1for (;;):
    2    if ($i > 10):
    3        break;
    4    endif;
    5    echo $i;
    6    $i++;
    7endfor;
    
  5. foreach:用來解構array很好用

    1foreach (array(1, 2, 3, 4) as $value):
    2    $value = $value * 2;
    3endforeach;
    
  6. 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}
    
  7. continue

    1for ($i = 0; $i < 5; ++$i) {
    2    if ($i == 2)
    3        continue;
    4    print "$i\n";
    5}
    
  8. 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}
    
  9. 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}
    
  10. return:如果在全局範圍中調用,則當前腳本文件中止運行.

  11. 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一樣,但已經包含過的檔案則不會再包含
    
  12. 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()來查詢. 函數名稱的命名很重要,好的命名可以讓程式可讀性提升,更好維護.

  1. User-defined functions

    1function sum($a, $b): float {
    2    return $a + $b;
    3}
    
  2. Variable functions

    1function foo() {
    2    echo "In foo()<br />\n";
    3}
    4$func = 'foo';
    5$func(); //This calls foo()
    
  3. Anonymous functions

    1echo preg_replace_callback('~-([a-z])~', function ($match) {
    2    return strtoupper($match[1]);
    3}, 'hello-world');
    

Classes and Objects

自從PHP5後的版本有著較好的效能與功能

  1. 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}
    
  2. 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
    
  3. 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}
    
  4. Properties,類別的成員.

    1class SimpleClass
    2{
    3   public $var1 = (1+2);
    4   public $var2 = myConstant;
    5   public $var3 = array(true, false);
    6}
    
  5. Class Constants:在類別中不變的值.

    1class MyClass
    2{
    3    const constant = 'constant value';
    4    function showConstant() {
    5        echo  self::constant . "\n";
    6    }
    7}
    
  6. 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();
    
  7. 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);
    
  8. 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
    
  9. Scope Resolution Operator(::):用於訪問類別的靜態成員、常量或函式.

    1class MyClass {
    2    const CONST_VALUE = 'A constant value';
    3}
    4echo MyClass::CONST_VALUE;
    
  10. 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
    
  11. 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}
    
  12. Class Abstraction(抽象類別)與Object Interfaces(介面)的差異性如下面表格.

    類別(Class)抽象類別(Abstract Class)介面(Interface)
    宣告屬性(attribute)OOX
    常數(const)OOO
    實例化(new class)OXX
    抽象方法(abstract function)XOO
    實作方法內容(functoin())OOX
    類別是否可繼承多個XXO
  13. 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}
    
  14. Anonymous classes.PHP7開始支持匿名類別.匿名類別可以創建一次性的簡單對象.

  15. 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
    
  16. Magic Methods

    • __sleep()
    • __wakeup()
    • __construct()
    • __destruct()
    • __call()
    • __callStatic()
    • __get()
    • __set()
    • __isset()
    • __unset()
    • __sleep()
    • __wakeup()
    • __toString()
    • __invoke()
    • __set_state()
    • __clone()
    • __debugInfo()
comments powered by Disqus