# php學習筆記 https://kejyuntw.gitbooks.io/php-learning-notes/content/class/class.html https://www.cntofu.com/book/107/PHP%20Composer%E2%80%94%E2%80%94%E8%87%AA%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%8E%9F%E7%90%86.md 10大好用 https://iter01.com/211503.html 個人總結抽象繼承是放在底層作規範的 可看這篇的下 eat那個 https://stackoverflow.com/questions/1814821/interface-or-an-abstract-class-which-one-to-use(建議全部看) 他是可以傳參數進去 而class是實際去做動作 然後動作一樣就用可以繼承他,這樣比較不靈活,所以抽象class在最底層像自己寫的filter interface就只是規範 介面抽象之類的介紹 必看 https://ithelp.ithome.com.tw/articles/10115273?sc=pt# ## Lambda https://www.youtube.com/watch?v=cFpBK2EBrMY&ab_channel=Laracasts ## 一次多個namespace import ![](https://i.imgur.com/33sKbIN.png) ## 時間 12換成24 https://stackoverflow.com/questions/8742191/how-do-you-convert-between-12-hour-time-and-24-hour-time-in-php ## php 7.4 https://stitcher.io/blog/shorthand-comparisons-in-php ## yield https://ithelp.ithome.com.tw/articles/10214084 ## static 重點靜態是第一次 就給記憶體位置 可是其他都是new 去申請一個 所以重複會用到的 可以用 還有static變數 也因為止申請一次記憶體 所以會保留到下一次 所以如果function 結果要保留 可以在裡面定義static, 當成gloabl的用法 ![](https://i.imgur.com/2Kkk1hk.png) ## range loop https://blog.johnsonlu.org/phprange/ ## zip https://blog.johnsonlu.org/use-zip-archive-with-php/ ## 跌帶器 去require文件 ![](https://i.imgur.com/JWwxL8u.png) ## http_build_query ![](https://i.imgur.com/Az1SYex.png) ## static **var** 只初始化一次 ![](https://i.imgur.com/13Rcvrm.png) ![](https://i.imgur.com/ZPjYeON.png) PHP 開發者很少會去使用 static 關鍵字,因為平常會用到 static 的場合其實也不多。這裡我再做一次 static 使用時機的重點整理: 需要記住上一次函式執行的結果。 某些可以保留執行結果的遞迴函式。 不希望因為物件個體不同,進而被影響的類別屬性。 類別的 Singleton 模式。 **funtion** 可以看TDO用的 他只有單一function 去new 一個更號效能 第二使用時機 後期靜態綁定 ex model trait的 每個用的人都是不同 存在繼承關系的時候 self調用的方法和屬性始終表示當前類的方法和屬性 static調用的方法和屬性為當前執行的類的方法和屬性 parent調用的方法和屬性為父類的方法和屬性 ## self vs static self指的new是實際編寫關鍵字的同一類。 static,在 PHP 5.3 的後期靜態綁定中,指的是層次結構中您調用該方法的任何類。 當您self用來指代類成員時,您指的是在其中使用關鍵字的類。在這種情況下,您的Foo類定義了一個名為 的受保護靜態屬性$bar。當你self在Foo類中使用來引用屬性時,你引用的是同一個類。 因此,如果您嘗試self::$bar在班級的其他地方使用,Foo但您有一個Bar具有不同屬性值的班級,它將使用Foo::$bar而不是Bar::$bar,這可能不是您想要的: ``` class Foo { protected static $bar = 1234; } class Bar extends Foo { protected static $bar = 4321; } ``` 當您通過調用方法時static,您正在調用一個稱為後期靜態綁定的功能(在 PHP 5.3 中引入)。 在上述場景中,使用self將導致Foo::$bar(1234)。並且 usingstatic將導致Bar::$bar(4321) 因為 with ,解釋器會在運行時static考慮類內的重新聲明。Bar ``` // self var_dump(Foo::$bar); // (int) 1234 // static var_dump(Bar::$bar); // (int) 4321 ``` 您通常對方法甚至類本身使用後期靜態綁定,而不是屬性,因為您不經常在子類中重新聲明屬性;static可以在以下相關問題中找到使用關鍵字調用後期綁定構造函數的示例: New self vs. new static 但是,這也不排除使用staticwith 屬性。 ## 後期靜態綁定 您肯定需要閱讀PHP 手冊中的Late Static Bindings。不過,我會盡量給你一個快速的總結。 基本上,歸結為self關鍵字不遵循相同的繼承規則這一事實。 self始終解析為使用它的類。這意味著如果您在父類中創建一個方法並從子類中調用它,self則不會像您預期的那樣引用子類。 後期靜態綁定引入了static關鍵字的新用途,它解決了這個特殊的缺點。當你使用static時,它代表你第一次使用它的類,即。它“綁定”到運行時類。 這是它背後的兩個基本概念。播放時的方式self和操作可能很微妙,因此我強烈建議您學習手冊頁示例,而不是更詳細地介紹parent。一旦您了解了每個關鍵字的基礎知識,這些示例對於了解您將獲得什麼樣的結果是非常必要的。staticstatic ## 匿名函數 https://ithelp.ithome.com.tw/articles/10263512 ``` <?php // 使用回調執行正則表達式搜索和替換 echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); // 將所選的變成大寫,從index: 1開始過濾掉原先"-" }, 'hello-world'); // 输出 helloWorld ?> ``` 閉包函數也可以作為變量的值來使用。 PHP 會自動把此種表達式轉換成內置類 Closure 的對象實例。把一個 closure 對象賦值給一個變量的方式與普通變量賦值的語法是一樣的,最後也要加上分號 ; ### 2. 匿名函數變量賦值示例 ``` <?php $sayHi = function($name) { echo "Hello $name"; }; // 記得加上分號 $sayHi('joe'); $sayHi('cherry'); ?> ``` ### 箭頭函數 箭頭函數是更簡潔的匿名函數,都是屬於Closure類(閉包)的實現 箭頭函數的語法為 fn (argument_list) => expr。 箭頭函數支持與 匿名函數 相同的功能,只是其父作用域的變量總是自動的。 當表達式中使用的變量是在父作用域中定義的,它將被隱式地按值捕獲。 在下面的例子中,函數 $fn1 和 $fn2 的行為是一樣的 ### 箭頭函數自動捕捉變量的值,即使在嵌套的情況下 通常在A程序執行中,尚未結束前又開始執行B程序,B程序結束後,繼續執行A程序,就稱為嵌套。 ``` <?php $z = 1; $fn = fn($x) => fn($y) => $x * $y + $z; // 輸出 21 var_export($fn(5)(4)); ?> ``` ## 靜態方法使用時機 ![](https://i.imgur.com/f3R3Ycv.png) 裡面不能用this 因為他是靜態的 基本上不會牽扯到屬性的 不會改變 在這類裡面不會變的 不管new幾次都不變的就叫靜態 注意靜態不要太多 會先佔空間 ## isset, empty, is_null empty() 的話,0 "0" "" 都會判定為空的。 另外,isset 只有 null 會是 false,其他情況都會是 true。 ## final防止複寫 ![](https://i.imgur.com/weWH9xr.png) ## trait衝突 重複function名稱 ![](https://i.imgur.com/BUFaauc.png) ## 用於檢測 SOLID 違規的靜態分析工具:Larastan、PHP Insights、PHP Metrics ## 特殊符號 https://stackoverflow.com/questions/3737139/reference-what-does-this-symbol-mean-in-php ## 跳脫字元 \ \$A 這樣$就不會以為是變數 ## && 左到右 所以如果他->的話可以先判斷他 用&& ``` if (isset($request) && $request->has(['sort', 'dir'])) { return parent::applyOrderByQuery($query, $request); } return $query->orderBy( 'sort', 'asc' )->orderBy( 'updated_at', 'asc' ); ``` ## PHP Switch 是兩個等於 switch 是 loose comparison,也就是==,而非===。 所以到第一個 case 時,$value == null為真時就會進去;而在 PHP,null、0、''、[]跟null都為真,跟empty()有點像。 要三個等於要用match ## PHP 可變函數及回傳值 https://ithelp.ithome.com.tw/articles/10263383 ``` 返回一個引用 從函數返回一個引用,必須在函數聲明和指派返回值給一個變量時都使用引用運算符 & <?php function &returns_reference() { return $someref; } $newref =& returns_reference(); ?> ``` 可變函數 PHP 支持可變函數的概念。就是說如果一個變量名後有圓括號,PHP 就會先去尋找這個變數名稱的函數執行。可變函數可以用來實現包括回調函數,以及函數表在內的一些用途。 來看看範例 ``` 1. 可變函數示例 <?php function foo() { echo "In foo()<br />\n"; } function bar($arg = '') { echo "In bar(); argument was '$arg'.<br />\n"; } // 使用名為 echo 的函數 function echoit($string) { echo $string; } $func = 'foo'; $func(); // This calls foo() $func = 'bar'; $func('test'); // This calls bar() $func = 'echoit'; $func('test'); // This calls echoit() ?> ``` ``` 2. 可變方法範例 <?php class Foo { function Variable() { $name = 'Bar'; $this->$name(); // This calls the Bar() method } function Bar() { echo "This is Bar"; } } $foo = new Foo(); $funcname = "Variable"; $foo->$funcname(); // This calls $foo->Variable() ?> ``` ## 匿名函数 匿名函數(Anonymous functions),也稱作閉包函數(closures),它允許臨時創建一個沒有指定名稱的函數。最經常用作回調函數 callable參數的值。當然,也有其它應用的情況。 1. 匿名函數範例 ``` <?php // 使用回調執行正則表達式搜索和替換 echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); // 將所選的變成大寫,從index: 1開始過濾掉原先"-" }, 'hello-world'); // 输出 helloWorld ?> ``` 閉包函數也可以作為變量的值來使用。 PHP 會自動把此種表達式轉換成內置類 Closure 的對象實例。把一個 closure 對象賦值給一個變量的方式與普通變量賦值的語法是一樣的,最後也要加上分號 ; ## call_user_func_array ``` <?php function foobar($arg, $arg2) { echo __FUNCTION__, " got $arg and $arg2\n"; } class foo { function bar($arg, $arg2) { echo __METHOD__, " got $arg and $arg2\n"; } } // Call the foobar() function with 2 arguments call_user_func_array("foobar", array("one", "two")); // Call the $foo->bar() method with 2 arguments $foo = new foo; call_user_func_array(array($foo, "bar"), array("three", "four")); ?> ``` ## 多載 https://ithelp.ithome.com.tw/articles/10132318 ## PDO 連接資料庫 https://www.bilibili.com/video/BV1Nz411b7R4?from=search&seid=9553083562719463986&spm_id_from=333.337.0.0 ## namespace https://www.bilibili.com/video/BV1fk4y1d7V9?from=search&seid=9553083562719463986&spm_id_from=333.337.0.0 ## 地雷王 clouse https://ithelp.ithome.com.tw/articles/10219530 如果希望 Closure 中的變更影響外部變數,可以利用 & 將匿名函式中的變更影響原本的外部變數 ``` $number = 10; $c = function () use (&$number) { return ++$n; }; $c(); // 11 $number; // 11 ``` ## if else 多個用switch取代 第二 但這樣也太長所以可以用key value的方式 ![](https://i.imgur.com/35bic8g.png) 這樣變成 ![](https://i.imgur.com/nwxbBEl.png) 用key去找 如果是php8 可以用最新的方法去 這種方法不能用function 因為array會先建立 ## 解構 $a = [1,2] [$a,$c] =$a 這個不好用 因為要一個對一個 空的要有空 關聯數組 ![](https://i.imgur.com/f0coTgi.png) 這樣比較好用 還可以重新命名 這個不像上面一下吃 順序 所以你數組有三個 你可以只取一個 參數解構 ![](https://i.imgur.com/nBKkVwq.png) 引數解構 ![](https://i.imgur.com/hWeCzyQ.png) ## 等於特別用法 .= 等号后面的值以字符串形式合并到$var中。 例如 $var = '123' $var .= '456' print $var 是123456 ## public protected private 在繼承中 public 和 protected 可進行重定義,但 private 而不能 可利用function 去抓定義的 但如果 是function 和 const private去利用function呼叫 也不行喔 ``` /** * Define MyClass2 */ class MyClass2 extends MyClass { // 為公開的函數 function Foo2() { $this->MyPublic(); $this->MyProtected(); $this->MyPrivate(); // 會發生錯誤 } } ``` public太汙染了 protected 要繼承的再給 自己要用給private 不確定就用protected 確定就private 記住 protected只能繼承的 所以blade要 ->抓屬性不能 那怎辦 **可以用function抓被保護的屬性** present都這樣做 ``` pulbic functio a() { returen $this->$a } ``` ## match 比switch更嚴格 可以試看看 沒有default會出錯 https://www.youtube.com/watch?v=-gF0qMl1MFc ## early returen https://www.youtube.com/watch?v=0vJ_SkuBgbo ## (必看 有地雷) : returen 宣告 : 後面接上 returen的限定 但 注意 如果相似的 ex 要returen int 但你是'111' 一樣會幫你 隱性的轉 很爛 注意 弱語言 所以returen view這個 會view blade這樣 但她會算是物件 所以用:string就能隱性的轉 很猛 很雷 ``` public function presentPrizeTypeBadge(): string { $class = $this->lookUpTable($this->getPrizeType(), $this->prizeTypeBadgeMapping); $prizeTypeBadge = view('twjoinadmin::adminlte3.components.badge', [ 'class' => $class, 'value' => $this->presentPrizeType(), ]); return $prizeTypeBadge; } ``` ## _DIR_ https://www.phptutorial.net/php-tutorial/php-__dir__/ __DIR__當文件用作包含文件時,PHP返回文件的目錄或包含文件的目錄。 __DIR__在包含文件時使用。 配合file_get_content ![](https://i.imgur.com/44ViSgI.png) laravel中使用 ## php 花括號 PHP分析双引号中的数据是否含有变量(并解析它的值),当用双引号时,{}用来界定变量的界限。 https://www.jianshu.com/p/03a7c203a5e9 https://www.cnblogs.com/xiaochaohuashengmi/archive/2011/12/08/2281155.html ## Throwable 接口 这是 PHP 7 引进的一个值得期待的新特性,将极大增强 PHP 错误处理能力。PHP 5 的 try ... catch ... finally 无法处理传统错误,如果需要,你通常会考虑用 set_error_handler() 来 Hack 一下。但是仍有很多错误类型是 set_error_handler() 捕捉不到的。PHP 7引入 Throwable 接口,错误及异常都实现了 Throwable,无法直接实现 Throwable,但可以扩展 \Exception 和 \Error 类。可以用 Throwable 捕捉异常跟错误。\Exception 是所有PHP及用户异常的基类;\Error 是所有内部PHP错误的基类。 ``` $name = "Tony"; try { $name = $name->method(); } catch (\Error $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } try { $name = $name->method(); } catch (\Throwable $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } try { intdiv(5, 0); } catch (\DivisionByZeroError $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } ``` ## 錯誤捕捉 一次捕捉多种类型的异常 / 错误 PHP 7.1 新添加了捕获多种异常/错误类型的语法——通过竖杠“|”来实现。 ``` try { throw new LengthException("LengthException"); // throw new DivisionByZeroError("DivisionByZeroError"); // throw new Exception("Exception"); } catch (\DivisionByZeroError | \LengthException $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } catch (\Exception $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } finally { // ... } ``` ## 通过 define() 定义常量数组 实例如下: ``` <?php define('ANIMALS', [ 'dog', 'cat', 'bird' ]); echo ANIMALS[1]; // 输出 "cat" ?> ``` ## namespace https://www.bilibili.com/video/BV1fk4y1d7V9?from=search&seid=12003072008765508472&spm_id_from=333.337.0.0 ## 雙引號中 \\ 顯示反斜線( \ ) " {變數}" 雙引號編譯 ## stdclass https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/207949/ php 基本array 用 json_encode()和json_decode() 轉換過來php也是stdclas laravel collection也是 可以用 [] 跟 -> ## php object vs array php中 object跟array一樣 []定義 但裡面不一樣 看文章就懂了 https://jiepeng.me/2017/04/15/how-to-create-php-object 但php 預設是stdClass 這雖然是object 但跟 一般object不一樣 **數組** 有大量的array_*函數可以在數組上工作,其中大多數都非常快。 默認情況下,它們按值傳遞(複製) 輕量級/簡單(更改僅影響局部變量,較少考慮) 通常用於構建一次數據(不會改變的數據) 所有數據都是公開的 略少資源密集型 **對象** 可以使用方法來保持數據更嚴格。(IE。檢查字段是否符合格式) 子類化(減少代碼重複) 默認情況下,它們通過引用傳遞 數據的變化可以有連鎖效應(__get,__set,等) 通常用於更易變的數據 可以通過函數和受保護/私有變量保護外部數據 對象的函數類型提示更靈活(不同類的類型提示不同) ### 在 PHP 使用不同方式定義 Object 的差異 ``` $name = [ 'first' => 'Peng', 'last' => 'Jie', ]; echo json_encode($name); // {"first":"Peng","last":"Jie"} echo $name['first']; // Peng ``` ``` $name = (object) [ 'first' => 'Peng', 'last' => 'Jie', ]; echo json_encode($name); // {"first":"Peng","last":"Jie"} echo $name['first']; // Fatal error: Cannot use object of type stdClass as array... ``` 這裡可以看到錯誤的訊息:不能使用類型 stdClass 的 object 作為物件。 所以我認為php 預設的stdClass能用 . [] 跟js一樣 但如果特別變成純的object就只能用-> 別的資料轉過來感覺會遇到 ### PHP 7 中的匿名類(不容易用到 可先無視) PHP 7 通過添加對動態匿名類的支持,在噴出對象“數據容器”時避免使用通用且笨拙的stdClass,進一步改進了 OOP 。 例如,以下示例是在現有類中即時創建的 ``` // Anonymous class $containerObject = new class() { public $one; public $two; }; $containerObject->one = 1; $containerObject->two = 2; $containerObject->three = 3; foreach ($containerObject as $property_name => $value) { echo $property_name .' '. $value . PHP_EOL; } ``` ## 命名空間運算子 有namespace 就直接use 如果沒有定義命名空間,即為全局空間,相當於根空間,通過「\」來表示。 ## PHP面向对象三大特性:封装、继承、多态 https://segmentfault.com/a/1190000008802881 抽象类和接口的区别,不在于编程实现,而在于程序设计模式的不同。 一般来讲,抽象用于不同的事物,而接口用于事物的行为。 如:水生生物是鲸鱼的抽象概念,但是水生生物并不是鲸鱼的行为,吃东西才是鲸鱼的行为。 * 对接口的使用方式是通过关键字implements来实现的,而对于抽象类的操作是使用类继承的关键字exotends实现的,使用时要特别注意。 * * 接口没有数据成员,但是抽象类有数据成员,抽象类可以实现数据的封装。 * * 接口没有构造函数,抽象类可以有构造函数。 * * 接口中的方法都是public类型,而抽象类中的方法可以使用private、protected或public来修饰。 * * 一个类可以同时实现多个接口,但是只能实现一个抽象类。 **相同点**:(注意抽象方法没有方法体,而且方法结束使用 ; 号,非抽象方法必须有方法体即{},可以不写具体内容)函数体内不能写任何东西,连两个大括号都不能写!!!如:function getName();这样就行了 ### 封装/Encapsulation 对事物的封装是指,将事物进行抽象后,提供抽象概念的实现的具体方法。 听起来很拗口,还是举鲸鱼的例子。 对于鲸鱼来说,需要吃东西这个行为,吃本身是一个抽象的概念,因为具体到怎么吃,是咀嚼和消化的过程,甚至如何咀嚼和消化也是不可见的。对外部而言,可见的只是吃这一个接口,如何吃、怎么吃,是被封装在了鲸鱼的实现中。 甚至可以说,消化系统,被封装到了鲸鱼这个对象中,对外部不可见,仅仅鲸鱼自己可见。 和别的程序设计语言一样,PHP也只是三种封装概念:Private,Protected,Public。 ### 继承/Inheritance 类和类之间应该是低耦合的。 继承通常是继承自抽象类,而不是具体类。 通常直接继承抽象类的具体类只有一层,在抽象类中用protected来限定。 **私有/Private** 私有的概念是,仅仅对象内部可见,外部不可见 **保护/Protected** 保护的概念是,仅仅是自身类和继承类可见,这个关键字的用途主要是防止滥用类的派生,另外三方库编写的时候会用到,防止误用 **继承权限**:private、protected、public三者权限控制不能小于父类,可以扩大父类权限 ### 多态 多态是指在面向对象中能够根据使用类的上下文来重新定义或改变类的性质和行为。 唯独这个多态,php体现的十分模糊。原因是php是弱类型语言。 ## 多形 https://imyoungyang.gitbooks.io/php7-study-group-notes/content/Chapter2/php-abstract.html ## 联合类型 rfc 考虑到 PHP 动态语言类型的特性,现在很多情况下,联合类型都是很有用的。联合类型是两个或者多个类型的集合,表示可以使用其中任何一个类型。 **public function foo(Foo|Bar $input): int|float;** 请注意,联合类型中不包含 void,因为 void 表示的含义是 “根本没有返回值”。 另外,可以使用 |null 或者现有的 ? 表示法来表示包含 nullable 的联合体 : ``` public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void; ``` ## stdclass 轉array (js跟 php 溝通) PHP和JS通訊通常都用json,但用 json 傳過來的陣列並不是標準的array,而是 stdClass 型別。那麼我們可以參考下面的幾個方法進行轉換。 https://stackoverflow.com/questions/6815520/cannot-use-object-of-type-stdclass-as-array https://www.huaweicloud.com/articles/5305ad4858cb3c150080362ef7f3ac57.html 基本上 ## 类型属性 2.0 类型的声明,类型提示,以及指定确定类型的变量传递给函数或类的方法。其中类型提示是在 PHP5 的时候有的一个功能,PHP 7.2 的时候添加了 object 的数据类型。而 PHP7.4 更是增加了主类属性声明,看下面的例子: ``` class User { public int $id; public string $name; } ``` 除了 void 和 callable 外,所有的类型都支持 ``` public int $scalarType; protected ClassName $classType; private ?ClassName $nullableClassType; ``` ## 太空船操作符(组合比较符) 太空船操作符用于比较两个表达式。当 $a 小于、等于或者大于 $b 时他分别返回 -1、0 或 1。 ``` <?php # 整数 echo 1 <=> 2; // -1 echo 1 <=> 1; // 0 echo 2 <=> 1; // 1 # 浮点数 echo 1.5 <=> 2.5; // -1 echo 1.5 <=> 1.5; // 0 echo 2.5 <=> 1.5; // 1 # 整数 echo "a" <=> "b"; // -1 echo "a" <=> "a"; // 0 echo "b" <=> "a"; // 1 ``` ## 展開運算子 https://stackoverflow.com/questions/40663687/cannot-unpack-array-with-string-keys Spread 運算符應該比 array_merge 擁有更好的性能。這不僅僅是 Spread 運算符是一個語法結構,而 array_merge 是一個方法。還是在編譯時,優化了高效率的常量數組 問題是splat 運算符(數組解包運算符或...)不適用於關聯數組。例子: ``` $array = [1, 2, 3]; $assoc = ["one" => 1, "two" => 2, "three" => 3]; var_dump(...$array); //Works var_dump(...$assoc); //Doesn't work ``` 您需要強制對數組進行數字索引以對其進行解包。您可以使用array_values執行此操作: ``` $values = array_values($q->fetch()); ModelController::execute(...$values); ``` 數組值將確保所有值都有一個連續的數字鍵。 更新 從 PHP 8 開始,將支持命名參數,這將使兩種情況都能正常工作。提議的 RFC 中記錄了一個示例,用於命名參數,該示例表示以下代碼應該從 PHP 8 開始工作 ``` $params = ['start_index' => 0, 'num' => 100, 'value' => 50]; array_fill(...$params); ``` ![](https://i.imgur.com/ylygatF.jpg) array裡面包物件 ex: ``` $a = [ new car(); new car(); ] ``` 但這樣 function會寫 function(array $a) 不好看董 可以用 function(Car ...$car) ``` <?php class Cart { private array $items; /** * 我們透過 ... 語法,代表參數可以輸入多筆類別是 Product 的物件 * @param Product[] $products */ public function __construct(Product ...$products) { $this->items = $products; } } ``` 上面建構子改良後有兩種用法,方法一是我覺得實務上最常用的方式 ``` $products = [ new Product("手機", 2, 100, "支"), new Product("衣服", 3, 50, "件") ]; $cart = new Cart(...$products); ``` interface最好用 用在ide提示 看下面文章 http://jsnwork.kiiuo.com/archives/3181/php-%E5%9C%A8%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8%E5%B1%95%E9%96%8B%E9%81%8B%E7%AE%97%E5%AD%90%E7%9A%84%E9%A1%9E%E5%9E%8B%E6%8F%90%E7%A4%BA%E6%9B%BF%E6%8F%9B%E9%99%A3%E5%88%97%E6%8F%90%E7%A4%BA/ ## 7.4 NULL 合併運算符 由於日常使用中存在大量同時使用三元表達式和 isset () 的情況, 我們添加了 null 合併運算符 (??) 這個語法糖。如果變量存在且值不為 NULL, 它就會返回自身的值,否則返回它的第二個操作數。 $username = $_GET['user'] ?? ‘nobody'; 這段代碼的作用非常簡單:它獲取請求參數並設置默認值(如果它不存在)。但是在 RFC 這個例子中,如果我們有更長的變量名稱呢? `$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';` 長遠來看,這段代碼可能難以維護。因此,旨在幫助開發人員編寫更直觀的代碼,這個 RFC 建議引入 null 合併等於運算符 (null_coalesce_equal_operator)??=,所以我們可以敲下面這段代碼來替代上面的這段代碼: ` $this->request->data['comments']['user_id'] ??= ‘value’;` 如果左側參數的值為 null,則使用右側參數的值。 **注意,雖然 coalesce 運算符 ?? 是一個比較運算符,但 ??= 它是賦值運算符。** ## PHP 7.1 新特性 ? 可为空(Nullable)类型 参数以及返回值的类型现在可以通过在类型前加上一个问号使之允许为空。当启用这个特性时,传入的参数或者函数返回的结果要么是给定的类型,要么是 null。 ``` <?php function testReturn(): ?string { return 'elePhPant'; } var_dump(testReturn()); function testReturn1(): ?string { return null; } var_dump(testReturn1()); function test(?string $name) { var_dump($name); } test('elePHPant'); test(null); test(); ### 输出 string(9) "elePhPant" NULL string(9) "elePHPant" NULL PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed Copy ``` ### null vs ? ?string 與 string = null 現在,你可能在想: 等等等等等等。如何?string不同於string $funFact = null? 嗯,好問題!因為如果我說string $funFact = null,那確實允許傳遞空值。實際上,這兩種語法幾乎相同。不同之處在於,當您將參數默認為 null 時,我可以setFunFact()不帶任何參數進行調用:該參數是可選的。 但是對於可**為空的?string語法,參數仍然是必需的**……它只是null一個有效值。這?string更好......除非你真的 希望參數是可選的。 順便說一下,可空值?適用於任何類型,比如類。我們將在接下來的行動中看到它! void 函数 一个新的返回值类型 void 被引入。返回值声明为 void 类型的方法要么干脆省去 return 语句,要么使用一个空的 return 语句。对于 void 函数来说,**NULL 不是一个合法的返回值** ## 魔術方法 三、 __call(),在对象中调用一个不可访问方法时调用。 该方法有两个参数,第一个参数 $function_name 会自动接收不存在的方法名,第二个 $arguments 则以数组的方式接收不存在方法的多个参数。 1、 __call() 方法的格式: ``` function __call(string $function_name, array $arguments) { // 方法体 } ``` 2、 __call() 方法的作用: 为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。 该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。 请参考如下代码: ``` <?php class Person { function say() { echo "Hello, world!<br>"; } /** * 声明此方法用来处理调用对象中不存在的方法 */ function __call($funName, $arguments) { echo "你所调用的函数:" . $funName . "(参数:" ; // 输出调用不存在的方法名 print_r($arguments); // 输出调用不存在的方法时的参数列表 echo ")不存在!<br>\n"; // 结束换行 } } $Person = new Person(); $Person->run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法 $Person->eat("小明", "苹果"); $Person->say(); ``` 运行结果: ``` 你所调用的函数:run(参数:Array ( [0] => teacher ) )不存在! 你所调用的函数:eat(参数:Array ( [0] => 小明 [1] => 苹果 ) )不存在! Hello, world! ``` 四、 __callStatic(),用静态方式中调用一个不可访问方法时调用 此方法与上面所说的 __call() 功能除了 __callStatic() 是未静态方法准备的之外,其它都是一样的。 ``` ?php class Person { function say() { echo "Hello, world!<br>"; } /** * 声明此方法用来处理调用对象中不存在的方法 */ public static function __callStatic($funName, $arguments) { echo "你所调用的静态方法:" . $funName . "(参数:" ; // 输出调用不存在的方法名 print_r($arguments); // 输出调用不存在的方法时的参数列表 echo ")不存在!<br>\n"; // 结束换行 } } ``` ``` $Person = new Person(); $Person::run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法 $Person::eat("小明", "苹果"); $Person->say(); 运行结果如下: 你所调用的静态方法:run(参数:Array ( [0] => teacher ) )不存在! 你所调用的静态方法:eat(参数:Array ( [0] => 小明 [1] => 苹果 ) )不存在! Hello, world! ``` **get set前提預告** ``` <?php 2 class Person 3 { 4 // 下面是人的成员属性, 都是封装的私有成员 5 private $name; //人的名子 6 private $sex; //人的性别 7 private $age; //人的年龄 8 9 //__get()方法用来获取私有属性 10 function __get($property_name) 11 { 12 echo "在直接获取私有属性值的时候,自动调用了这个__get()方法<br />"; 13 if (isset($this->$property_name)) 14 { 15 return ($this->$property_name); 16 } 17 else 18 { 19 return NULL; 20 } 21 } 22 23 //__set()方法用来设置私有属性 24 function __set($property_name, $value) 25 { 26 echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值<br />"; 27 $this->$property_name = $value; 28 } 29 } 30 31 $p1 = new Person(); 32 33 // 直接为私有属性赋值的操作, 会自动调用__set()方法进行赋值 34 $p1->name = "张三"; 35 $p1->sex = "男"; 36 $p1->age = 20; 37 38 // 直接获取私有属性的值, 会自动调用__get()方法,返回成员属性的值 39 echo "姓名:" . $p1->name . "<br />"; 40 echo "性别:" . $p1->sex . "<br />"; 41 echo "年龄:" . $p1->age . "<br />"; 42 ?> ``` 五、 __get(),获得一个类的成员变量时调用 在 php 面向对象编程中,类的成员属性被设定为 private 后,如果我们试图在外面调用它则会出现“不能访问某个私有属性”的错误。那么为了解决这个问题,我们可以使用魔术方法 __get()。 魔术方法__get()的作用 在程序运行过程中,通过它可以在对象的外部获取私有成员属性的值。 我们通过下面的 __get() 的实例来更进一步的连接它吧: ``` <?php class Person { private $name; private $age; function __construct($name="", $age=1) { $this->name = $name; $this->age = $age; } /** * 在类中添加__get()方法,在直接获取属性值时自动调用一次,以属性名作为参数传入并处理 * @param $propertyName * * @return int */ public function __get($propertyName) { if ($propertyName == "age") { if ($this->age > 30) { return $this->age - 10; } else { return $this->$propertyName; } } else { return $this->$propertyName; } } } $Person = new Person("小明", 60); // 通过Person类实例化的对象,并通过构造方法为属性赋初值 echo "姓名:" . $Person->name . "<br>"; // 直接访问私有属性name,自动调用了__get()方法可以间接获取 echo "年龄:" . $Person->age . "<br>"; // 自动调用了__get()方法,根据对象本身的情况会返回不同的值 运行结果: ``` ``` 姓名:小明 年龄:50 ``` 六、 __set(),设置一个类的成员变量时调用 __set() 的作用: __set( $property, $value )` 方法用来设置私有属性, 给一个未定义的属性赋值时,此方法会被触发,传递的参数是被设置的属性名和值。 请看下面的演示代码: ``` <?php class Person { private $name; private $age; public function __construct($name="", $age=25) { $this->name = $name; $this->age = $age; } /** * 声明魔术方法需要两个参数,真接为私有属性赋值时自动调用,并可以屏蔽一些非法赋值 * @param $property * @param $value */ public function __set($property, $value) { if ($property=="age") { if ($value > 150 || $value < 0) { return; } } $this->$property = $value; } /** * 在类中声明说话的方法,将所有的私有属性说出 */ public function say(){ echo "我叫".$this->name.",今年".$this->age."岁了"; } } $Person=new Person("小明", 25); //注意,初始值将被下面所改变 //自动调用了__set()函数,将属性名name传给第一个参数,将属性值”李四”传给第二个参数 $Person->name = "小红"; //赋值成功。如果没有__set(),则出错。 //自动调用了__set()函数,将属性名age传给第一个参数,将属性值26传给第二个参数 $Person->age = 16; //赋值成功 $Person->age = 160; //160是一个非法值,赋值失效 $Person->say(); //输出:我叫小红,今年16岁了 ``` 运行结果: `我叫小红,今年16岁了` **call** https://devindeving.blogspot.com/2018/12/php-call.html ### 什麼時候要使用get或set? https://devindeving.blogspot.com/2020/12/php-magic-method-get-set-and-laravel.html 1. 類別的是動態產生的,難以事前宣告,例如前面提到的Laravel的Eloquent Model。 2. 需要透過一些特殊的方式來存取property,可藉由__get、__set的來一致性地處理。 ``` function __get($eky) { return $this->doSomethingBeforeGet($this->$key); } function __set($eky, $value) { return $this->$key = $this->doSomethingBeforeSet($value); } ``` 3. property傳遞,一個主類別可以注入許多小類別,當想取得的property不屬於主類別時,可以藉由__get將請求傳遞到小類別,並取得小類別的的property。 ``` function __get($eky) { return $this->dataStoreManager->get($key); } function __set($eky, $value) {fs return $this->dataStoreManager->set($eky, $value); } ``` ## 反射 https://www.kancloud.cn/shaoguan/phpstudy/384102 面向对象编程中对象被赋予了自省的能力,而这个自省的过程就是反射。 反射,直观理解就是根据到达地找到出发地和来源。比如,一个光秃秃的对象,我们可以仅仅通过这个对象就能知道它所属的类、拥有哪些方法。 反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。 在平常开发中,用到反射的地方不多:一个是对对象进行调试,另一个是获取类的信息。在MVC和插件开发中,使用反射很常见 ## ?? vs | 這是我自己看的沒文章 小心閱讀 | 是給預設 所以undefind會給後面 ?? 是用 if(isset($x) (也就是 if (!is_null($x)),判斷是否不為 null 總結undefind的用 | ??是判斷有沒有null 實際遇到的情況 ``` list($name, $prize_type, $activeity_status,) = $request->validated() | null; ``` 這解構才對 如果把|改成??會出錯 原因我猜是因為 裡面的質有些沒有所以解構不出來會是undefind 但用|就可以解決了 ## ::class https://christoph-rumpel.com/2016/03/using-the-class-keyword-in-PHP-and-Laravel **use the class keyword** PHP 5.5 起, class 關鍵字用於類名解析 ``` <?php class User { } echo User::class; // returns User <?php namespace App\Models; class User { } echo User::class; // returns App\Models\User ``` ![](https://i.imgur.com/T5C0gL2.png) ## interface和implements (個人覺得 interface感覺是协议)(因為裡面都抽象方法) ### 觀念釐清區 php是單繼承,所以如果要多繼承就無能為力了 ,所以php引入介面技術 如果一個抽象類裡面的所有方法都是抽象方法,且沒有宣告變數,而且介面裡面所有的成員都是 public 許可權的,那麼這種特殊的抽象類就叫 介面 。 介面使用關鍵字 interface 來定義,並使用關鍵字 implements 來實現介面中的方法,且必須完全實現。 ### 介面 介面:一種成員屬性全部為抽象或常量的特殊抽象類。 規則: 1.類中全部為抽象方法。 2.抽象方法錢不用加abstract。 3.介面抽象方法屬性為public。 4.成員屬性必須為常量。 具體化他的都要跑涵式 ``` interface action{ public function run(); } class animal implements action{ function run(){ } } ``` 如果class裡面沒run()會出錯,對了當你父實現方法,子繼承會一樣有方法,就不用特別在意,只要實現就存在了 **不想全部都實現在怎辦(介面有兩個涵式,但我只想實現一個怎辦)** 實作一個虛擬類別 ``` abstract class animal implements action{ function run(){ $this->fast(); } } ``` 實際另用方法 ``` interface action{ public function run(); public function fast(); } abstract class animal implements action{ function run(){ $this->fast(); } } class dog extends animal{ function fast(){ echo 'very fast'; } } $dog = new dog; $dog->run(); ``` ### 介面的應用與規範 介面引用區別於類繼承關鍵字 extends ,繼承只能只是單一性,而介面可以使用關鍵字 implements 多個引用並用逗號分開 ### interface https://mark-lin.com/posts/20181030/ 同一個interface的 要實體化,嵌套的情況,使用container(記得這種情況要bind) ## 抽象類別(Abstract Class)抽象類別 當父層的方法會互相用來用去就可以用 因為一般class要內部互相呼叫 要new 才能用$this指向 不可實作抽象方法 不可以實例化(instance) 可以想成是 多個靜態方法跟變數可以繼承,不然你靜態方法只能一個一個寫上去。 **抽象類別(Abstract Class)使用時機** 當「多個類別(Class)」之間有共同的方法(function)或屬性(attribute)時,可以將這些共用的地方寫成「抽象類別(Abstract Class)」,讓其他的「子類別(Class)」去繼承 **介面(Interface)使用時機** 當「多個類別(Class)」之間有共同的方法(function),但方法實做的方式有差異,可以將這些共用「方法」寫成「介面(Interface)」,讓其他的「子類別(Class)」去實做這個介面 ## Interface, Abstract Class, 與 Concrete Class 的不同。 http://ot-note.logdown.com/posts/208733/interface-and-abstract-class-different-from-the-concrete-class https://ithelp.ithome.com.tw/articles/10115273?sc=pt 我們都知道要訂 Pizza,就會要知道 Pizza 店的電話,然後打給他。等到送達後,再開門見到穿著制服的員工,向他收下 Pizza。 然而每個 Pizza 店都會照著這個協定走: 1. &1提供電話 2. &2讓你打電話訂餐 3. &3送貨員送 Pizza 到你家 這個 協定 就是 `Interface`。 ``` struct PizzaShop { virtual string phoneNumber() = 0; virtual bool call() = 0; virtual void deliverPizza() = 0; }; ``` 當你想要吃 **PizzaHut** 的 **Pizza** 時,你大概知道打電話的流程是什麼,也預期送貨員會穿著紅色的制服,交給你 Pizza。 但是知道這個還不夠,你需要"實際的店家"的幫你作,幫你送,還要知道實際的電話才打去。 大部分的訂 **Pizza** 行為都知道,但是還有些尚未確定的資訊,需要真正存在的分店來提供,的這種 概念,就是一種 **Abstract Class**。 你甚至不能只靠 **PizzaHut** 來取得 **Pizza**,你需要下面所提到的 **PizzaHut** 分店,才能獲取實際熱騰騰的 **Pizza**。 ``` class PizzaHut : public PizzaShop {public: virtual string phoneNumber() = 0; virtual bool call() { Phone phone; if(!phone.dial(phoneNumber())) return false; return phone.speak("PizzaHut 嗎?你好,我要點..."); } virtual void deliverPizza() { PizzaHutEmployee employee; employee.knockDoor(); employee.deliverThePizza(); } }; ``` 真正存在的分店,會真的做出一個 **Pizza** ,並且給你真正的電話號碼。它是一間存在的 **Pizza** 店,你可以在龍江路上找到這家店,你收到的 **Pizza** 是從台北龍江路上這家店做出來的。 這種實際存在,又處理大部分實作細節的 實體,就叫做 **Concrete Class**。 ``` class PizzaHutBranchNearMyHome : PizzaHut {public: virtual phoneNumber(){ return "02-2516-2256"; // 龍江店 } }; ``` 一般設計上不會常用到 **Concrete Class** ,而都用 **Interface** 或 **Abstract Class**。 就像是你會跟住在外縣市的大學同學說:【幹,我好想吃 Pizza 哦。】而不會說:【幹,我好想吃 PizzaHut 龍江分店 的 Pizza 哦。】一樣。 ## self static $this 的差別 self代表 get_class() static 代表 get_called_class get_class獲得當前的域名 get_called_class() 獲得當前主調類的類名 self是參照到目前的class,$this是參照到目前的object ( 已經被宣告的實體上 ) ### self self是指向類本身,也就是self是不指向任何已經產生實體的物件,一般self使用來指向類中的靜態變數。假如我們使用類裡面靜態(一般用關鍵字static)的成員,我們也必須使用self來調用。還要注意使用self來調用靜態變數必須使用 :: (域運算子號) ## 延遲綁定 (new static)(new self) https://learnku.com/articles/31929 實際用到的在原生model all會掉用到這function ``` /** * Begin querying the model. * * @return \Illuminate\Database\Eloquent\Builder */ public static function query() { return (new static)->newQuery(); } ``` ## parent:: 一般我們使用parent來調用父類的構造函數 調用父親的涵式 ex 表單驗證的 ``` public function rules(): array { return parent::rules() + [ 'title' => 'sometimes|nullable|string', 'category_id' => 'sometimes|nullable|exists:f_a_q_categories,id', ]; } ``` 用範圍解析運算子存取類別時,仍然受到public、protected、private可視性原則的限制 ## trait | interface | abstract 比較 https://yosia.net/article/434 ## php 一樣會有傳址的問題 但foreach他是會複製一份 跟JS不一樣 ## PHP的語言特性 : 型別 / Type Juggling / Type Hint https://ithelp.ithome.com.tw/articles/10135041 ## query string `/use-qa?faqId={$faq->uuid}&faqCategoryId={$faq->category->uuid}` 用花括號,不然直接在外面寫雖然變數有編譯,但如果寫太長,最好花括號,因為太長有些會被判斷成string ## $decorator PHP 允许把变量 $decorator 作为类,并调用其方法 ## 依賴注入 (只有OOP才會遇到) 通过构造函数,把一个外部的 实例 "注入" 进 User 实例内部,而不是在 User 实例内部创建 实例,就是 依赖注入。 Constructor 注入 最适合必须的依赖关系 ## 依賴注入 (di) 依賴注入只在面向對象的世界中有意義 **大多数时候,Dependency Injection 并不需要 Container** **只有当你需要管理一大堆具有很多依赖关系的不同对象时,Container 才会非常有用(例如框架中)。** http://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.htmlhttp://fabien.potencier.org/what-is-dependency-injection.html 看上面文章 主要認為 (SessionStorage 原本的) (USER 被注入的) 我們不是SessionStorage在User類中創建對象,而是通過將其作為構造函數參數傳遞來SessionStorage將User對象注入對像中 (但你使用User時要先創建SeessionStroage) ``` $storage = new SessionStorage('SESSION_ID'); $user = new User($storage); ``` (但在laravel裡面好像不用,我猜是因為contanier註冊了) 好處,這樣不用NEW可以隨時替換,雖然一開始也能用new 然後傳參數去改,但還是會使用到new 這樣會使用同一個class,如果更改呢? 使用原因 **關注點分離** ## Trait (原本是laravel 被引入php) https://clouding.city/php/traits/ https://www.youtube.com/watch?v=6mN0Oj6xR30 laravel中softdeleted就是用trait 像 interface 可以形容一個 class 可以做什麼 像 class 可以提供一個模組化的實作 像是複製貼上的概念 不會改變原先繼承的架構 使用 trait 用 use 關鍵字 (class 內使用) 一個檔案一個 trait ,讓程式碼簡單容易除錯 不相關聯,硬繼承會很怪用 trait,另外trait重複命名用instdnceof 相關的用繼承,不相關的用trait **在Traits中可以定義abstract方法,使用Traits的類別,則必須實作這些方法** Traits 已被添加到 PHP 中,原因很简单s:PHP 不支持多重继承。简而言之,一个类不能一次性扩展至多个类。当你需要在其他类也使用的两个不同类中声明的功能时,这变得很费力,结果是你必须重复执行代码才能完成工作,而不会纠缠自己。 引入 Traits,它能使我们声明一种包含多个可复用方法的类。更好的是,它们的方法可以直接注入到你使用的任何类中,并且你可以在同一类中使用多个 Trait。让我们看一个简单的 Hello World 示例。 ``` trait SayHello { private function hello() { return "Hello "; } private function world() { return "World"; } } trait Talk { private function speak() { echo $this->hello() . $this->world(); } } class HelloWorld { use SayHello; use Talk; public function __construct() { $this->speak(); } } $message = new HelloWorld(); // returns "Hello World"; ``` trait的重要性质。 1. (1)优先级:当前类的方法会覆盖 trait 中的方法,而 trait中的方 法会覆盖基类的方法。 1. (2)多个 trait组合:通过逗号分隔,通过use关键字列出多个 trait。 1. (3)冲突的解决:如果两个 trait 都插入了一个同名的方法,若没 有明确解决冲突将会产生一个致命错误。 为了解决多个 trait 在同一个1. 类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中 的哪一个。 同时,可以通过as 操作符将其中一个冲突的方法以另一个 名称来引入。 1. (4)修改方法的访问控制:使用 as 语法可以用来调整方法的访问 控制。 1. (5)trait 的抽象方法:在trait中可以使用抽象成员,使得类中必须 实现这个抽象方法。 1. (6)trait 的静态成员:在trait中可以用静态方法和静态变量。 1. (7)trait的属性定义:在trait中同样可以定义属性。 ## yield ``` function a($items) { foreach ($items as $item) { yield $item + 1; } } ``` yield 关键字从生成器函数返回数据。生成器函数实际上是编写 Iterator 的更紧凑和有效的方式。它允许您定义一个函数,该函数将在您遍历该函数时计算并返回值。 因此,问题中的函数与以下内容的函数几乎相同: ``` function b($items) { $result = []; foreach ($items as $item) { $result[] = $item + 1; } return $result; } ``` 只有一个区别,a() 返回一个 generator,而 b() 只是一个简单的 数组。而且两者都可以被迭代。 函数的生成器版本未分配完整的数组,因此对内存的需求较少。生成器可用于解决内存限制。由于生成器仅按需计算其 yielded 值,因此它们用于代替计算成本昂贵或无法一次性计算的序列很有用。 **generrator** yield 会将当前一个值传递给 foreach,换句话说,foreach 每一次迭代过程都会从 yield 处取一个值,直到整个遍历过程不再存在 yield 为止的时候 ``` foreach (xrange(0, 10, 2) as $key => $value) { printf("%d %d\n", $key, $value); } ``` 簡單來說上面的a要跑foreach,因為yield會return ## PHP的多載 可以讓需要動態產生大量不同屬性或方法的物件時,處理更簡單。例如要開發ORM,使用物件對應到不同資料表的列時,我們實際上並不需要根據不同資料表的不同欄位來定義不同的類別,只要利用PHP的多載...然後在__get()、__call()等方法裡面檢查查詢的結果,並根據傳入的參數判斷要回傳的資料,操作起來感覺就好像不同的類別實例 ## namespace https://ithelp.ithome.com.tw/articles/10134247 一樣用 use 然後可以幫他取別名 ``` use namespace1\sub1 as ns1; $a = new ns1\a; $b = new ns1\b(); ``` ## 魔術方法 類別實例化時會呼叫__construct()方法,存取屬性時如果屬性不存在會嘗試呼叫__get()/__set(),呼叫類別方法如果方法不存在時會嘗試呼叫__call() ## php的箭頭函式 在 PHP 7.4 裡可用 以 fn 關鍵字開頭 只能包含 一個 表示式, 即返回表示式 return 關鍵字可忽略 閉包的地方 可以用這個取代 比較快 重點重點 閉包 要外面變數 要use 但這個不用 由於語言結構,匿名函數(閉包)可以使用 use 繼承父作用域中定義的變量,如下所示: $factor = 10; $calc = function($num) use($factor){ return $num * $factor; }; 但是在 PHP 7.4 中,父級作用域的值是通過隱式捕獲的(隱式按值的作用域進行綁定)。所以我們可以用一行來完成一下這個函數 $factor = 10; $calc = fn($num) => $num * $factor; ``` public function newQuery() { return $this->model ->newQuery() ->whereHas('category', fn ($query) => $query->where('type', FAQ::TYPE_MEMBER)); } ``` ## PHP 自动加载功能 https://www.cntofu.com/book/107/PHP%20Composer%E2%80%94%E2%80%94%E8%87%AA%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%8E%9F%E7%90%86.md PHP 开发过程中,如果希望从外部引入一个 Class ,通常会使用 include 和 require 如果一个 PHP 文件需要使用很多其它类,那么就需要很多的 require/include 语句,这样有可能会 **造成遗漏** 或者 **包含进不必要的类文件** PHP5 为这个问题提供了一个解决方案,这就是 **类的自动加载(autoload)机制**。autoload机制 可以使得 PHP 程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为 **Lazy loading (延迟加载)** 总结起来,自动加载功能带来了几处优点: 使用类之前无需 include / require 使用类的时候才会 include / require 文件,实现了 lazy loading ,避免了 include / require 多余文件。 无需考虑引入 类的实际磁盘地址 ,实现了逻辑和实体文件的分离。 **PHP 自动加载函数 autoload()** 通常 PHP5 在使用一个类时,如果发现这个类没有加载,就会自动运行 __autoload() 函数,这个函数是我们在程序中自定义的,在这个函数中我们可以加载需要使用的类。下面是个简单的示例: ``` <?php function __autoload($classname) { require_once ($classname . ".class.php"); } ``` 在我们这个简单的例子中,我们直接将类名加上扩展名 **.class.php** 构成了类文件名,然后使用 require_once 将其加载。 从这个例子中,我们可以看出 __autoload 至少要做三件事情: * 根据类名确定类文件名; * 确定类文件所在的磁盘路径 (**在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下**); * 将类从磁盘文件中加载到系统中。 第三步最简单,只需要使用 **include / require** 即可。要实现第一步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。 当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在 __autoload() 函数中,将类名与实际的磁盘文件对应起来,就可以实现 lazy loading 的效果 。从这里我们也可以看出 __autoload() 函数的实现中最重要的是类名与实际的磁盘文件映射规则的实现。 問題 __autoload() 是全局函数只能定义一次 ,不够灵活,所以所有的类名与文件名对应的逻辑规则都要在一个函数里面实现,造成这个函数的臃肿。那么如何来解决这个问题呢?答案就是使用一个 __autoload调用堆栈 ,不同的映射关系写到不同的 __autoload函数 中去,然后统一注册统一管理,这个就是PHP5引入的 SPL Autoload 。 **看很多吧,只要你有compsoer都會幫你做,所以當知識吧** 只要有命名空間跟 再用 use就好了 ## Composer自动加载文件 首先,我们先大致了解一下Composer自动加载所用到的源文件。 1. autoload_real.php: 自动加载功能的引导类。 任务是composer加载类的初始化(顶级命名空间与文件路径映射初始化)和注册(spl_autoload_register())。 1. ClassLoader.php: composer加载类。 composer自动加载功能的核心类。 1. autoload_static.php: 顶级命名空间初始化类, 用于给核心类初始化顶级命名空间。 1. autoload_classmap.php: 自动加载的最简单形式, 有完整的命名空间和文件目录的映射; 1. autoload_files.php: 用于加载全局函数的文件, 存放各个全局函数所在的文件路径名; 1. autoload_namespaces.php: 符合PSR0标准的自动加载文件, 存放着顶级命名空间与文件的映射; 1. autoload_psr4.php: 符合PSR4标准的自动加载文件, 存放着顶级命名空间与文件的映射; ## 反射 為IOC 容器也是要通過反射來實現的.從網上抄了一段來解釋反射是什麼意思 “反射它指在PHP運行狀態中,擴展分析PHP程序,導出或提取出關於類、方法、屬性、參數等的詳細信息,包括註釋。這種動態獲取的信息以及動態調用對象的方法的功能稱為反射API。反射是操縱面向對象範型中元模型的API,其功能十分強大,可幫助我們構建複雜,可擴展的應用。其用途如:自動加載插件,自動生成文檔,甚至可用來擴充PHP語言” ## $$$ 在 PHP 中是什么意思? 主题: PHP 让我们尝试 $$$: ``` $real_variable = 'test'; $name = 'real_variable'; // variable variable for real variable $name_of_name = 'name'; // variable variable for variable variable echo $name_of_name . '<br />'; echo $$name_of_name . '<br />'; echo $$$name_of_name . '<br />'; ``` 这是输出: ``` name real_variable test ``` **變數加變數** ## csv fopen練立一個 fputcsv 推進去 fclouse 關閉 ![](https://i.imgur.com/q6QkmX7.png) 比較特別的是php:://output 可以不用站存 直接response就好 ## zip ![](https://i.imgur.com/2ynqi4O.png) 注意 open後面兩個zip的const都必備 ![](https://i.imgur.com/FGRTfUy.png) ## 浮水印 ![](https://i.imgur.com/XxBShO9.png) https://www.twblogs.net/a/5b7cc1182b71770a43dc6c49 ###### tags: `PHP`