Skip to content

Zeroplex 生活隨筆

軟體開發和生活瑣事

小 縮小字型大小。 中 重設字型大小。 大 放大字型大小。

標籤: PHP

PHP 變數初始化 (?)

Posted on 2017 年 11 月 15 日2021 年 3 月 12 日 By 日落 在〈PHP 變數初始化 (?)〉中尚無留言

看到有 Java 工程師在學 PHP 遇到變數初始化的問題,覺得有趣,做個筆記順便複習 PHP 的特性。

Java 是強型別 (strong type) 的程式語言,所有的變數在使用之前都必須給資料型態以及初始值;PHP 則是弱型別,就算不給值、不給資料型態也沒差,interpreter 會在 runtime 的時候自動做型別轉換在繼續執行程式。我想 PHP 這一特性就會讓很多 Java 工程師決得很莫名其妙吧,沒定義的變數到底要怎麼用?執行的時候真的不會出事嗎? (其實就是會 XD)

先來看正常的寫法:

$x = 'hello';
var_dump($x);  // "hello"

如果變數沒有給值會發生什麼事?

var_dump($x);  // NULL

PHP 把沒有初始化的變數帶入 NULL 來使用,連錯誤訊息都沒噴耶?其實有,只是要調整一下 error reporting 的 level:

error_reporting(E_ALL);

var_dump($x);

// Notice: Undefined variable: x
// NULL


如果拿一個完全沒有被初始化的變數做運算,interpreter 到底會怎麼轉型:

var_dump('123' . $x);  // "123"

$x[] = '123';
var_dump($x);  // array([0] => "123")

好像很強耶,居然會自動轉陣列!那換個方式來玩玩看:

$list = [1, 2, 3];

array_merge($list, $x);

// PHP Warning:  array_merge(): Argument #2 is not an array

其實還是會遇到 interpreter 沒辦法幫你作主的時候。

以 PHP 這類的弱型別寫法,比較保險的作法就是去檢查要被使用的變數到底是否有初始化:

if (isset($x)) {
    var_dump($x);
} else {
    echo 'undefine';
}

// "undefine"

所以使用 PHP 寫程式時變數到底是否需要初始化?其實可以不用,但是若要保險起見 (防禦性程式設計),要用到的變數建議都要初始化、或是做檢查。StackOverflow 上也有提到:
PHP does not require it, but it is a good practice to always initialize your variables.

Tags:PHP

PHP curl 的一些特性

Posted on 2017 年 10 月 9 日2021 年 3 月 12 日 By 日落 在〈PHP curl 的一些特性〉中尚無留言

curl 在 PHP 中是以 extension 形式存在,所以只能透過 resource reference 去操作 curl 行為,沒辦法透過 debug 工具摸清楚 curl 在背景到底做了哪些事情。

手癢用 memory_get_usage() 看了一下 curl 在 init、exec 以及 close 這幾個狀態的記憶體使用量,來猜測 curl 到底怎麼運作。

echo memory_get_usage() . "n"   // 236136;

$ch = curl_init();
var_dump($ch);                   // resource(4) of type (curl)
echo memory_get_usage() . "n";  // 237472

在 init_curl() 以後,會先在記憶體中 allocate 並放一些資料,所以吃掉大概 1 KB 左右的記憶體,再將 curl 的 reference 傳出來給 $ch。

接下來這邊讓 curl 去 http://assets.blog.zeroplex.tw/2017-07/php-bench-v2.html 抓一個大小 128 KB 的網頁,並看一下記憶體使用量:

echo memory_get_usage() . "n"  // 235744;

$ch = curl_init();
echo memory_get_usage() . "n";  // 237072

curl_setopt($ch, CURLOPT_URL, "http://assets.blog.zeroplex.tw/2017-07/php-bench-v2.html");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_exec($ch);
echo memory_get_usage() . "n";  // 371784

curl_close($ch);
echo memory_get_usage() . "n"  // 235928;

var_dump($ch);                  // resource(4) of type (Unknown)

由整個記憶體使用量,可以看出在 curl_exec() 之後,會把傳回來的資料除存在記憶體中,除了網頁 128KB 的內容以外,可能還包括一些與 HTTP 連線有關的資料,所以記憶體使用量增加了 130 KB (371784 – 237072 = 134712) 左右。這些資料存在於記憶體中,直到呼叫了 curl_close() 才會釋放掉。

比較有趣的是 PHP 官方文件只有提到 curl_close() 會關閉 session,但是沒有提到用來儲存 resource 的 $ch reference 會如何。上面可以看到 curl_close() 以後,其實 resource refence 還存在,只是 resource 已經被 released,所以 type 顯示為 unknown。

由此可知,沒有 unset($ch) 還拿原來的 resource reference 亂用的話應該會大爆炸 XD

最後來看一下,如果沒有做 curl_close() 就一直做 curl_exe() 抓多個網頁,到底會不會有 memory leak 問題發生:

for ($i = 0; $i < 10; $i++) {
    curl_exec($ch);
    echo memory_get_usage() . "n";
}

執行結果如下:

369424
369976
372568
370856
372568
372568
372568
372568
370856
372568

所以看起來每次執行 curl_exec() 之前,curl 會自動將上一次的 response 清掉在重新做 request,所以記憶體不會無止境的增加。換而言之,如果今天有很複雜的 curl_setopt() 要處理,可以不用一直 reset curl 也不會有問題,setopt 只要做一次就可以了,可以省下一些時間。

原本想要測試一下 keep alive 的效果如何,但是沒有較為穩定的環境來測試,所以這邊就先跳過了。但大家可以參考 StackOverflow 上面的這篇「Persistent/keepalive HTTP with the PHP Curl library」來設定 keep alive。

Tags:PHP, 資訊學習

使用 datastax/php-driver 對 Cassandra 做 query 需要留意的地方

Posted on 2017 年 9 月 12 日2021 年 3 月 12 日 By 日落 在〈使用 datastax/php-driver 對 Cassandra 做 query 需要留意的地方〉中尚無留言

最近在研究如何把 log 塞到 Cassandra 中,使用的 Datastax 的 php-driver 輕鬆很多,但還是有一些需要留意的地方,免得莫名其妙鬼打牆。

這邊先假設我要記錄一個使用者上傳檔案的記錄,包含 ID、檔案名稱、檔案大小以及上傳日期,建立了一個 table:

CREATE TABLE hermes_log.file (
    id uuid,
    name text,
    size decimal,
    create_time timestamp,
    PRIMARY KEY (id, name, size, create_time)
)

這個時候使用 PHP 塞資料進入,直接建立 CQL 且不使用 prepare/binding,可以成功執行 insertion:

<?php

$cluster = Cassandra::cluster()->build();
$session = $cluster->connect();
$cql = "
    insert into hermes_log.file
    (
        id,
        name,
        size,
        create_time
    ) values (
        uuid(),
        'gavatar.jpg',
        123.4,
        '2017-01-01 10:30:45.678'
    )
";

$session->execute($cql);

cqlsh 看一下資料格式:

cqlsh:zero_test> select name, size, create_time from file;

 name        | size  | create_time
-------------+-------+---------------------------------
 gavatar.jpg | 123.4 | 2017-01-01 02:30:45.678000+0000

(1 rows)

除了日期會自動轉為 UTC+00:00 以外其他都沒什麼太大的問題。


接下來把 PHP 改為 prepare/bind 格式:

$cql = "
    insert into hermes_log.file
    (
        id,
        name,
        size,
        create_time
    ) values (
        uuid(),
        ?,
        ?,
        ?
    )
";

$session->execute($cql, [
    'gavatar.jpg',
    123.4,
    '2017-01-01 10:30:45.678'
]);

執行的時候你會看到錯誤訊:「Uncaught CassandraExceptionInvalidQueryException: Invalid amount of bind variables in your.php」,意思是找不到可以 binding 的資料。仔細去翻文件才會注意到 binding 的資料,不是直接放進 array 就好,而是要放在 key「arguments」底下,所以至卻的寫法應該是:

$session->execute($cql, [
    'arguments' => [
        'gavatar.jpg',
        123.4,
        '2017-01-01 10:30:45.678'
    ],
]);

再次執行後,錯誤訊息不一樣了「Uncaught CassandraExceptionInvalidQueryException: Expected 8 or 0 byte long for date (23) ….」,看起來 create_time 這邊沒辦法使用字串來代表新增日期。

如果你有注意到 Cassandra 是使用 Java 實作的,應該不難理解 PHP 和 Java 的資料型態差異很大,因此 DataStax 的 php-driver library 為了讓資料傳遞時不會發生問題,寫了不少 adapter 來解決資料型態的相容問題,像是 CassandraTimestamp、CassandraDecimal 等,這些類別可以在文件中的 Cassandra namespace 中找到。

所以按照規格改好以後,應該會是下面這樣:


$createTime = microtime(true);
list($sec, $ms) = explode('.', $createTime);
$sec = intval($sec);
$ms = intval(($createTime - $sec) * 1000000); // 毫秒取六位數

$session->execute($cql, [
    'arguments' => [
        'gavatar.jpg',
        new CassandraDecimal(strval(123.4)),
        new CassandraTimestamp($sec, $ms),
    ],
]);
Tags:Cassandra, PHP, 資訊學習

PHP strict typing performance

Posted on 2017 年 8 月 5 日2021 年 3 月 12 日 By 日落 在〈PHP strict typing performance〉中尚無留言

聽說 PHP 使用 strict type hint 會讓速度變慢,於是來做了個簡單的測試,先來個沒有 type hint 的:

<?php

function repeat($str)
{
    return $str;
}

$start = microtime(true);

for ($i = 0; $i < 100000000; $i++) {
    repeat('test');
}

$end = microtime(true);

var_dump($end - $start);

使用 PHP 7.1.8 執行,大概會花費 3.7 秒左右:

$ php main.php
float(3.713329076767)

接下來改成 strict type 的寫法:

<?php

declare(strict_types=1);

function repeat(string $str): string
{
    return $str;
}

$start = microtime(true);

for ($i = 0; $i < 100000000; $i++) {
    repeat('test');
}

$end = microtime(true);

var_dump($end - $start);

執行時間大概變成 5.3 秒左右:

$ php main.php 
float(5.3128638267517)

的確慢了不少,不過先別著急,不要因為慢了一些就打算放棄 strict type hint。

使用 PHP 5.6 去執行第一個測試 (沒有 strict type) 就大約需要 19 秒:

$ php5.6 main.php
float(19.477375030518)

其實把 PHP 版本升級上 7.x 才是正確的吧 XD

Tags:PHP

Apache / Nginx 與 PHP 各種搭配方式的效能測試

Posted on 2017 年 8 月 2 日2021 年 3 月 12 日 By 日落 在〈Apache / Nginx 與 PHP 各種搭配方式的效能測試〉中尚無留言

以 PHP 為主的 HTTP server 有很多種架設方式,最傳統的大概就是 Apache + mod_php 來執行 PHP scripts。隨著技術一直在翻新,後來也有了 Nginx + php-fpm 的搭配模式出現,甚至連 PHP 在 5.4.0 都推出了 built-in server。

因公司需要,就順手測試了以下幾種搭配:
  • Apache mpm-prefork + mod_php
  • Apache mpm-worker using mod_proxy + php-fpm
  • Apache mpm-worker using mod_proxy + php-fpm (by unix socket)
  • Nginx + php-fpm
  • Nginx + php-fpm (using unix socket)
  • PHP build-in server
在開始看壓力測試結果之前,先來說一下測試環境:
  • 所有的 HTTP server 除了以上搭配方式之外,其他都使用預設值 (像是不開啟 gzip 壓縮等)
  • 另外壓力測試的 PHP script 內容也很簡單,僅有「phpinfo();」一行
  • 1 core CPU
  • 4 GB RAM
  • 使用 ab (Apache Bench) 做測試
  • ab 中每個 concurrency 固定會發出 1000 個 HTTP request,如 concurrency = 1 時僅有 1000 個 request,concurrency = 100 時,則送出 1000 x 100 個 request。
  • 每次 ab 跑完一次壓力測試,均重新啟動 HTTP server 與 PHP server,以防止使用到 cache。
先簡單做個結論,詳細的壓力測試結果再請大家自行過目:
  • concurrency = 1 時,沒有壓力測試會出現的情況,不過可以把 response time 和伺服器的 CPU、記憶體使用量當作一個基準值作為參考。
  • Apache mpm-prefork 搭配 mod_php 的記憶體使用量最大,畢竟要 fork 一個 Apache instance 出來就要把 Apache 的 module 也 initialize 出來。小型伺服器不推薦這個方案。
  • php-fpm 即使設定「pm.max_children = 50」,也可以輕鬆負荷超過 50 個 concurrency。比較微妙的是從 htop 看不出 php-fpm 有使用 thread 來同時處理 request。
  • php-fpm listen on Unix socket 的效率比 listen TCP (127.0.0.1:9000) 的效率還要高一些,但是在高負荷的情況下會變得很不穩定,猜測和 OS 的 socket 實作、設定有關連。
  • PHP built-in server 遇到在高負載的情況下表現的不錯,唯獨 log 給的資訊太少,不方便做分析。但也可考慮透過 Nginx proxy 給 PHP built-in server,讓 Nginx 來處理 log。
最後做個廣告,Hackmd 真不錯用,筆記寫完最後還以轉換成 HTML 下載,有夠方便的啦!
Tags:Apache, Linux, Nginx, PHP, 資訊學習

文章分頁

上一頁 1 ... 14 15 16 ... 27 下一頁

其他

關於我  (About me)

  文章 RSS Feed

  留言 RSS Feed

Apache AWS Bash C/C++ Docker FreeBSD GCP Git Google Java JavaScript Laravel Linux Microsoft MSSQL MySQL Nginx PHP PHPUnit PostgreSQL Python Qt Ubuntu Unix Vim Web Windows XD 作業系統 分享 好站推薦 專題 小提琴 攝影 新奇搞笑 新聞 旅遊 生活雜記 程式設計 網路架站 網頁設計 資訊學習 資訊安全 遊戲 音樂


創用 CC 授權條款
本著作係採用創用 CC 姓名標示-相同方式分享 4.0 國際 授權條款授權.