# 前言
這是我的 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
以下的 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();
?>
留言