# 前言
這是我的 PHP 學習筆記, 學什麼記什麼!
# 安裝 extension
自從 2018 年的 4 月, Homebrew 已經不再是 PHP 在 macOS 上的套件管理器, 所以所有的 PHP Extension 之後都應該使用 pecl
來安裝
# 第一步 - 刪除可能衝突的套件
brew rm php php@5.6 php@7.0 php@7.1 |
# 第二步 - 更新 Xcode command line 工具, 以及取得 build 套件
確定已經安裝 Xcode command line 工具
xcode-select --install
到 AppStore 去更新 Xcode 以及套件
安裝 homebrew building 工具
brew install pkg-config
# 第三步 - 安裝 ImageMagick
brew install imagemagick |
為什麼上面說了 brew 已不支援安裝 extension, 而這邊又使用 brew 來安裝呢? ImageMagick 是一個開源套件, 用 C 寫的, 跟 PHP 沒關係。
而下面安裝的 Imagick 才是 PHP 的 extension, 算是 PHP 跟 ImageMagick 之間的 binding
# 第四步 - 使用 Homebrew 安裝 PHP
brew install php --build-from-source |
--build-from-source
會安裝 PHP-FPM
# 判別 Apache based PHP 或 Homebrew based PHP
type php |
/usr/local/...anything.../php
表示你執行的為 homebrew based 的 PHP/usr/bin/php
表示你執行的為 Apache based PHP
# 第五步 - 安裝 Imagick
pecl install imagick |
# 記得重啟你的 Web server
- 使用 Apache 記得重啟 Apache
- 使用 Nginx 記得重啟 Nginx
- 若是使用 valet, macOS 開發, 記得重啟 valet
# 疑難雜症篇
# 環境為 macOS, 使用 pecl 安裝 extension 時遇到以下的錯誤
Warning: mkdir(): File exists in System.php on line 294 |
看起來該是無法在該位置建立資料夾, 那就手動建立一個吧
首先, 取得
pecl
的 extension 資料夾位址, 並複製pecl config-get ext_dir|pbcopy
建立該資料夾
mkdir -p copiedValueFromLastCommand
這樣一來, 應該就解決了
# PHP 無法找到模組, 我的心慌慌
先找出 pecl 將模組放在哪
pecl config-get ext_dir|pbcopy
打開目前被使用的 php.ini
vim "$(php-config --ini-path)/php.ini"
打開設定檔
; Directory in which the loadable extensions (modules) reside.
; http://php.net/extension-dir加入上面得到的模組位置
extension_dir = "/usr/local/lib/php/pecl/XXXXXX"
上面的做法適用於 homebrew 安裝的 PHP
# 優化
# opcache
; 是否啟用 opcache, 設為 0 時為不啟用 |
# PHP.ini
; 設定單個 PHP process 可以使用的系統記憶體最大值, 默認 128M, 若是單主機單 PHP process |
# 參考文章
stackoverflow
Make your Laravel App Fly with PHP OPcache
PHP 及 Laravel 上線優化
PHP-Late Static Bindings
CSDN lamp_yang_3533 的部落格
程式狂想筆記
# Questions and Answers
以下的 PHP example 中, person2 的 output 是? 如何不 reference 同一個 amount object?
- Example:
<?php
class Person
{
private $name;
private $age;
private $id;
public $account;
public function __construct(string $name, int $age, Account $account)
{
$this->name = $name;
$this->age = $age;
$this->account = $account;
}
public function setId(int $id)
{
$this->id = $id;
}
public function __clone()
{
$this->id = 0;
}
}
$person = new Person("bob", 44, new Account(200));
$person->setId(343);
$person2 = clone $person;
// give $person some money
$person->account->balance += 10;
// $person2 sees the credit too
print $person2->account->balance; - Answer:
210function __clone()
{
$this->id = 0;
$this->account = clone $this->account;
}
以下的 Laravel example code 的意思是?
- Example:
<?php
class Person
{
private $writer;
public function __construct(PersonWriter $writer)
{
$this->writer = $writer;
}
public function __call(string $method, array $args)
{
if (method_exists($this->writer, $method)) {
return $this->writer->$method($this);
}
}
}
$person = new Person(new PersonWriter());
$person->writeName(); - Answer:
使用__call
delegation, 直接使用 PersonWriter 中的 method
PHP 中, __callStatic($method, $arg_array)
method 觸發的時機是?
當嘗試呼叫一個 undefined static method
PHP 中, __call($method, $arg_array)
method 觸發的時機是?
當嘗試呼叫一個 undefined non-static method
PHP 中, __isset($property)
method 觸發的時機是?
當嘗試使用 isset() 在一個 undefined property 時
PHP 中, __unset($property)
method 觸發的時機是?
當嘗試使用 unset() 在一個 undefined property 時
PHP 中, __set($property, $value)
method 觸發的時機是?
當嘗試 assign value 到一個 undefined property 時
PHP 中, __get($property)
method 觸發的時機是?
當嘗試存取未定義的 property 時
以下的 PHP example 的意思是?
- Example:
<?php
class IllegalCheckout extends Checkout
{
final public function totalize()
{
// change bill calculation
}
}
// PHP Fatal error: Cannot override final method popp\ch04\batch14\Checkout::totalize() ... - Answer:
final method 無法被覆寫
以下的 PHP example 的意思是?
- Example:
<?php
final class Checkout
{
// ...
} - Answer:
final class 無法被繼承
以下的 PHP example 的意思是?
- Example:
class UtilityService extends Service
{
use PriceUtilities {
PriceUtilities::calculateTax as private;
}
} - Answer:
使用 trait 時, 可使用 as operator 來改變該 method 在 UtilityService 中的 access level
即一般 use 該 trait 可能為 public, 但在 UtilityService 中為 private
以下的 PHP example 的意思是?
- Example:
trait PriceUtilities
{
function calculateTax(float $price): float
{
// better design.. we know getTaxRate() is implemented
return (($this->getTaxRate() / 100) * $price);
}
abstract function getTaxRate(): float;
// other utilities
} - Answer:
trait 中可使用 abstruct method
以下的 PHP example 的意思是?
- Example:
class UtilityService extends Service
{
use PriceUtilities, TaxTools {
TaxTools::calculateTax insteadof PriceUtilities;
PriceUtilities::calculateTax as basicTax;
}
}
$u = new UtilityService();
print $u->calculateTax(100) . "\n";
print $u->basicTax(100) . "\n"; - Answer:
當兩個 trait 有相同的 method 時, 可使用 insteadof 來指定要使用哪一個 method
然後使用 as 來命名另外一個同名 method, 這樣兩個都可存取ㄕ
以下的 PHP example 的意思是?
- Example:
class UtilityService extends Service
{
use PriceUtilities, TaxTools {
TaxTools::calculateTax insteadof PriceUtilities;
}
}
// listing 04.30
$u = new UtilityService();
print $u->calculateTax(100) . "\n"; - Answer:
當兩個 trait 有相同的 method 時, 可使用 insteadof 來指定要使用哪一個 method
以下的 PHP example 的結果是?
- Example:
$a = true ? 0 : true ? 1 : 2;
- Answer:
// (true ? 0 : true) ? 1 : 2 = 2
以下的 PHP example 的結果是?
- Example:
$a = 3 * 3 % 5;
- Answer:
4
以下的 PHP example 中, 輸出分別是?
- Example:
<?php
$a=1;
$b=1
$c=++$a;
$d=$b++;
echo $c;
echo $d; - Answer:
$c=2, 取++後的值, $d=1, 取++前的值
PHP 中, 以下的常量分別代表的意思是?
- Example:
__LINE__
__FILE__
__DIR__
__FUNCTION__
__CLASS__
__TRAIT__
__METHOD__
__NAMESPACE__
- Answer:
__LINE__
: 文件中的當前行號__FILE__
: 文件完整路徑及文件名, 若被包含在文件中, 則返回被包含的文件名__DIR__
: 文件所在的目錄。如果用在被包括文件中,則返回被包括的文件所在的目錄。它等同於 dirname(FILE)。除非是根目錄,否則目錄中名不包括末尾的斜杠。__FUNCTION__
: 當前函數的名稱。匿名函數則為 {closure}__CLASS__
: 當前 class 的名稱。class 名稱包括其被聲明的作用區域(例如 Foo\Bar)。注意自 PHP 6.4 起 CLASS 對 trait 也起作用。當用在 trait 方法中時,CLASS 是調用 trait method 的 class 的名字。__TRAIT__
: Trait 的名字。 Trait 名包括其被聲明的作用區域(例如 Foo\Bar)__METHOD__
: class 的 method 名稱__NAMESPACE__
: 當前 namespace 的名稱
以下位於 PHP.ini 的 configuration example 的意思是?
- Example:
apc.ttl=0
- Answer:
讓 apc cache 永不過期, 預設是 3600s (1 小時)
以下的 Laravel example code 的意思是?
- Example:
<?php
class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
// output
int(5)
bool(true) - Answer:
當嘗試以 function 的方式來呼叫一個 object 時, 就會觸發 invoke()
PHP 套件中, mbstring 是什麼?
multi bytes string, PHP 預設是使用 ASCII 編碼, 若要支援 UNICODE, 需要安裝 mbstring 來處理 multi bytes string (多個 bytes 代表一個 character)
以下的 example command 的意思是?
- Example:
ps aux | grep php-fpm
- Answer:
確認 php-fpm 是否有在運行中
以下的 PHP example code 的意思是?
- Example:
<?php
var_dump(25/7);
var_dump((int) (25/7));
var_dump(round(25/7)); - Answer:
<?php
var_dump(25/7); // float(3.5714285714286)
var_dump((int) (25/7)); // int(3)
var_dump(round(25/7)); // float(4)
?>
以下的 PHP example code 的意思是?
- Example:
<?php
$a = 1234;
$a = 0123;
$a = 0x1A;
$a = 0b11111111;
$a = 1_234_567;
?> - Answer:
<?php
$a = 1234; // 十进制数
$a = 0123; // 八进制数 (等于十进制 83)
$a = 0x1A; // 十六进制数 (等于十进制 26)
$a = 0b11111111; // 二进制数字 (等于十进制 255)
$a = 1_234_567; // 整型数值 (PHP 7.4.0 以后)
?>
PHP 當中, float 跟 double 一樣嗎
一樣
PHP-FPM 中, 如果要從 LISTEN PORT 改成 LISTEN SOCKET, 該修改哪個檔案?
pool 的 config 檔
以下的 PHP terminal command 的意思是?
- Example:
php -S IP:Port -t Directory
- Answer:
使用 PHP 內建 Web server
以下的 PHP example code 的輸出為?
- Example:
<?php
class A {
public static function get_self() {
return new self();
}
public static function get_static() {
return new static();
}
}
class B extends A {}
echo get_class(B::get_self()); // ?
echo get_class(B::get_static()); // ?
echo get_class(A::get_self()); // ?
echo get_class(A::get_static()); // ? - Answer:
<?php
echo get_class(B::get_self()); // A
echo get_class(B::get_static()); // B
echo get_class(A::get_self()); // A
echo get_class(A::get_static()); // A
// 總結, self 表示 method 所在的 class, static 表示呼叫的 class 本身
SplStack 是什麼?
Stand PHP Library 已實作完成的 stack structure class
SplLinkedList 是什麼?
Stand PHP Library 已實作完成的 linked list structure class
以下的 PECL terminal command 的意思是?
- Example:
pecl config-get php_ini
- Answer:
使用 pecl 取得 php.ini 位置, 當然, 你的 PHP 必須是要使用 PECL 安裝的
以下的 Brew terminal command 的意思是?
- Example:
brew info php
- Answer:
可取得 php.ini 位置, 當然, PHP 需要是使用 brew 安裝
以下的 PHP terminal command 的意思是?
- Example:
php-config --ini-path
- Answer:
可取得 php.ini 位置, PHP 版本為 7.4.9
以下的 PECL terminal command 的意思是?
- Example:
pecl config-get ext_dir
- Answer:
取得 PECL 模組安裝的位置
以下的 PHP terminal command 的意思是?
- Example:
php -m
- Answer:
查詢目前已載入哪些模組
Brew 安裝的 PHP 是如何使用 PECL 安裝的 module?
當使用 brew 安裝 PHP 時, 其 php.ini 文件內 extention_dir
參數可以指定 extension 資料夾路徑, 因為是使用 PECL 安裝的, 在 Mac 上通常路徑是 /usr/local/lib/php/pecl/<日期版本號>
也就是說, 如果你用 brew 重新安裝了 PHP, 那新安裝的 PHP 的 extension_dir 會指向新的日期版本, 所以務必要更新 extension_dir 的指向, 最好是使用 PECL uninstall packageName
, 然後在重新安裝 extension pecl install packageName
, 最後再將舊的 extension_dir 移除 rm -rf oldExtensionDir
什麼是 Zend Engine?
為一虛擬機, PHP 的核心
什麼是 Opcode?
Zend Engine 解析 PHP code 之後得到的執行碼, 可直接被 Zend 虛擬機執行
PECL 全寫是?
PHP Extension Community Library
PEAR 全寫是?
PHO Extension and Application Repository
以下的 PHP example code 回傳的值是?
- Example:
<?php
function test()
{
static $var;
$var += 1;
echo $var . PHP_EOL;
}
function testtest()
{
static $var;
$var += 1;
echo $var . PHP_EOL;
}
// 以下的輸出是?
test();
test();
test();
testtest();
testtest();
testtest();
?> - Answer:
<?php
// static variable 在不同的 function scope 內會累加
test(); // 1
test(); // 2
test(); // 3
testtest(); // 1
testtest(); // 2
testtest(); // 3
以下的 PHP example code 回傳的值是?
- Example:
<?php
class A
{
static $var;
public static function test()
{
self::$var += 1;
echo self::$var . PHP_EOL;
}
public static function testtest()
{
self::$var += 1;
echo self::$var . PHP_EOL;
}
}
A::test(); // 輸出是?
A::test(); // 輸出是?
A::test(); // 輸出是?
A::testtest(); // 輸出是?
A::testtest(); // 輸出是?
A::testtest(); // 輸出是?
?> - Answer:
<?php
class A
{
static $var;
public static function test()
{
self::$var += 1;
echo self::$var . PHP_EOL;
}
public static function testtest()
{
self::$var += 1;
echo self::$var . PHP_EOL;
}
}
A::test(); // 1
A::test(); // 2
A::test(); // 3
A::testtest(); // 4
A::testtest(); // 5
A::testtest(); // 6
?>
以下的 PHP example code 回傳的值是?
- Example:
<?php
class A {
protected $name = 'A';
static $alias = 'a';
const HASH = 'md5';
public function dd() {
echo $this->name; echo '--';
echo static::$alias; echo '--';
echo static::HASH; echo '--';
echo self::$alias; echo '--';
echo self::HASH; echo '--';
var_dump(new self); echo '--';
var_dump($this); echo '--';
var_dump(new static); echo '<br>';
}
}
class B extends A {
protected $name = 'B';
static $alias = 'b';
const HASH = 'sha1';
}
(new A)->dd();
(new B)->dd(); - Answer:
<?php
// $this->name = A, $this 代表當前 instance, 而當前 instance 為
// class A 的 instance, 故為 class A 中的 protected variable
// static::$alias = a, static 為 forwarding call, static 的 forwarding call
// 下面會詳述。 呼叫的 instance 的 class 為 class A, 所以取 class A 中定義的 static variable
// static::HASH = md5, 原理同上
// self::$alias = a, self 表示當前 method 歸屬的 class, 此處為 class A,
// 因此使用 class A 中定義的 static variable
// self::HASH = md5, 原理同上
// new self = class A, 如同上面提到的, 當前 method 所在的 class 為 class A
// $this = class A, 原理同上, $this 為 class A 的 instance, 所以是 class A
// new static = 同上, 原始被呼叫的 class 為 class A, 所以為 class A
(new A)->dd();
// 輸出為: A--a--md5--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" }
// --object(A)#1 (1) { ["name":protected]=> string(1) "A" }
// --object(A)#2 (1) { ["name":protected]=> string(1) "A" }
// $this->name = B, $this 代表當前 instance, 也就是 class B
// static::$alias = b, static 為 forwarding call, static 的 forwarding call 下面會詳述。
// 呼叫的 instance 的 class 為 class B, 所以取 class B 中定義的 static variable
// static::HASH = sha1, 原理同上
// self::$alias = a, self 表示當前 method 歸屬的 class, 因 class B 使用的 test() method 是
// 繼承自 class A 的, 實際上 self 為 class A, 所以使用定義在 class A 的 alias
// self::HASH = md5, 原理同上
// new self = class A, 如同上面提到的, 當前 method 定義於 class A
// $this = class B, 原理同上, $this 為 class B 的 instance, 所以是 class B
// new static = 同上, 原始被呼叫的 class 為 class B, 所以為 class B
(new B)->dd();
// 輸出為: B--b--sha1--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" }
// --object(B)#1 (1) { ["name":protected]=> string(1) "B" }
// --object(B)#2 (1) { ["name":protected]=> string(1) "B" }
以下的 PHP example code 回傳的值是?
- Example:
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test(); // 輸出是?
?> - Answer:
<?php
// 輸出為 A
// 因為 B 本身沒有 test(), 所以調用 A 的 test(), 而 test() 中的
// self 代表 test() 歸屬的 class, 即 A, 所以會調用 A 的 who()
以下的 PHP example code 回傳的值是?
- Example:
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test(); // 輸出是?
?> - Answer:
<?php
// 輸出為 B
// static 代表著上一個調用 non-forwarding call 的 class, 即 B
// 下面會針對 forwarding call 以及 non-forwarding call 詳述
以下的 PHP example code 回傳的值是?
- Example:
<?php
class A {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
?> - Answer:
<?php
class A {
public static function foo() {
// who() 為 echo called class name, 因此 static 代表什麼, 決定著輸出
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
// 上面有提到 A:: 算 non-forwarding call, 所以假如後面有呼叫 static:: 的話, static 就會代表 A
A::foo();
// 上面有提到, parent:: 算 forwarding call, 而上一個 non-forwarding call
// 為 C::test(), 因此如果後面有使用 static::, static 表示 C
parent::foo();
// 同上
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
// A C C
// A::foo() 輸出 A, 如上所述, A:: 為 non-forwarding call,
// 因此 static::who() 這邊的 static 代表 A
// parent::foo() 輸出 C, 如上所述, parent:: 為 forwarding call,
// 因此 foo() 中的 static 還是代表上一個 non-forwarding call, 即 C
// self::foo() 輸出為 C, 同上
?>
PHP 中, forwarding call 與 non-forwarding call 分別是?
forwarding call:
- parent::
- self::
- static::
- forward_static_call()
non-forwarding call:
A::test()
PHP 中, self 跟 static 的差異是?
- self 代表當前 method 所歸屬的 class
- static 代表上一個 non-forwarding 調用的 class
PHP 中, 在 non-static context 環境中, static 的調用順序是?
它指向的 class 中找尋 private method ==> public method ==> 調用 static 的 method 所在的 class 中尋找 private method ==> public method, 如果存在就調用, 並停止找尋。
PHP 中, 在 non-static context 環境中, $this 的調用順序是?
調用 $this 的 method 所在的 class 中尋找 private method ==> 它指向的 instance 的 class 中尋找 private method ==> 然後是 public method ==> 調用 $this 的 method 所在的 class 中尋找 public method, 只要找到就調用, 並停止找尋
以下的 PHP example code 的輸出是?
- Example
<?php
class A {
private function foo() {
echo __class__ . PHP_EOL;
}
public function test() {
$this->foo();
static::foo();
}
}
class B extends A {
}
class C extends A {
private function foo() {
}
}
$b = new B(); // 輸出是?
$b->test(); // 輸出是?
$c = new C(); // 輸出是?
$c->test(); // 輸出是?
?> - Answer:
<?php
class A {
private function foo() {
echo __class__ . PHP_EOL;
}
public function test() {
$this->foo();
static::foo();
}
}
class B extends A {
// foo() 是從 A copy 過來 B 的, 因此它的 scope 依然是 A
}
class C extends A {
private function foo() {
// 這個 foo() 覆蓋了 class A 的 foo(), 所以新的 scope 為 C
}
}
$b = new B();
// $this->foo() 輸出為 A, 如上所述, class B 本身沒有 test(), 所以會調用 class A 的
// 而 $this 會先到 "調用 $this->foo()" 的 method, 即 test(), 所在的 class, 即 class A,
// 中尋找 private method, 你可以試試在 class B 中新增一個一模一樣的 private method foo(),
// 結果還是會先調用 class A 的 foo()
// static::foo() 輸出為 A, 同上, 但 class B 並沒有 foo(), 所以調用了 class A 的 foo()
// 可以試試在 class B 新增一個 private method foo(), 這樣會報錯, 因為 static 會先從實際調用
// class, 即 class B, 尋找 private method, 這樣就變成從 class A 去呼叫 class B 的 private
// method, private method 只可從所屬的 class 中才可調用, 當然, 如果改成 public method,
// 那輸出就會變成 B
$b->test();
$c = new C();
// $this->foo() 輸出是 A, 這點同上, 不加贅述
// static::foo() 會 fail, 因為 static 會到實際調用的 class 中尋找 private method, 而
// class C 中確實定義了 private method foo(), 這樣就變成了從 class A 的 scope 去調用
// class C 的 private method, 所以錯了
// 可以試試把 class C 中的 foo() 改成 public, 且輸入跟 class A foo() 一樣的內容
// 這樣輸出就會變成 C 了
$c->test();
?>
留言