Tag: php

Best practice to generate random token in PHP

Posted by – January 10, 2017

在 Stackoverflow 看到一篇超級實用的文:在 PHP 中產生隨機字串的最佳實踐。

產生隨機字串這個看似不起眼的議題實作起來還挺麻煩的,曾經為了如何產生字串跟同事討論了許久。

$length = 16;
$token = bin2hex(openssl_random_pseudo_bytes($length));
# => e9bf18672b051619a3479ecbe1cb7d08

唯一的參數 $length 可以替換成任意整數,產生的字串長度就是 $length *2。

同場加映,Google Chrome 的亂數演算法也有改過

How to Flatten a Multidimensional Array in PHP?

Posted by – January 9, 2017

最近碰到的小需求:如何將多維陣列攤平成一維陣列?....

本來以為 PHP 那巨大的 Lib 會有一個神奇的 function 完成這任務,結果翻了一遍 php.net 的文件,沒有找到這神奇的功能。不過還是有另一個神妙的 function 可以拿來用,那就是 array_walk_recursive,這個 function 會有遞迴的方式走完整個 array,然後看使用者後續想幹嘛...

以攤平一維陣列為例,以下是只保留 value 的作法...

function flatten(array $array) {
  $return = array();
  array_walk_recursive($array, function($a) use (&$return) {
    $return[] = $a; 
  });
  return $return;
}

如此一來就可以將多維陣列轉為一維。若是要保留 key 與 value,要注意 key 名稱是否有重複 (isset)。

強制 fputcsv 所有欄位都加上雙引號

Posted by – August 2, 2016

公司跟一家新廠商展開合作,其中交換電文是用 CSV 格式。但對方要求所有的欄位都要用 " 雙引號包起來。看起來就像下圖這樣。

Screen Shot 2016-08-02 at 2.58.00 PM

但是很不巧的,PHP 內建產生 CSV 的指令 fputcsv 沒有提供強制的選項。做出來的檔案如下圖。

Screen Shot 2016-08-02 at 3.03.33 PM山不轉只好路轉...

在丟進 fputcsv 前就先用雙引號包一包。

<?php

function forceQuote($value) {
    return "\"$value\"";
}

$file = @fopen($file_path, "w");

foreach ($data['content'] as $line) {
    fputcsv(
        $file,                          // 輸出檔名
        array_map('forceQuote', $line), // 先包雙引號
        ',',                            // 資料用逗號分隔
        chr(0)                          // 跳脫預設的 enclosure 字元
    );
}

fclose($file);

比較特別是 fputcsv 原本的 enclosure 字元預設就是雙引號,無法替換成空白,只好用 chr(0) 來跳脫,不然每一筆資料會變成 """aaa""",三個引號…

這樣就可以交差了。

Connection could not be established with host

Posted by – July 19, 2016

最近重灌了工作用的筆電,把開發環境的 PHP 升級到 5.6.10,然後就各種狀況.....

其中一個問題是從本機連不上公司 mail server,所以寄信功能就掛掉了。Laravel log 的訊息如下

local.ERROR: 500 - Connection could not be established with host xxx.com.tw [ #0] @ /
exception 'Swift_TransportException' with message 'Connection could not be established with host xxx.com.tw [ #0]' in laravel/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php:270

原來是因為從 PHP 5.6 開始,變更了 SSL 認證的預設值,verify_peer 與 verify_peer_name 這兩項安全檢查都被改為預設,然後很多 SSL verification 就過不了。雖然提高安全性是好事,但開發環境卻造成一些困擾。

以上述的 swiftmailer 套件為例,它的 SSL 連線是用 stream_socket_client 實現的,其參數設置是透過 stream_context_create 傳入,所以設定方式如下:

$contextOptions = [
    'ssl' => [
        'verify_peer' => false,
        'verify_peer_name' => false
    ]
];
$context = stream_context_create($contextOptions);
stream_socket_client(
    $host.':'.$port, 
    $errno, 
    $errstr, 
    $timeout, 
    STREAM_CLIENT_CONNECT, 
    $context
);

如此一來就可以關閉檢查,終於可以連上 mail server 了。收工。

Laravel: failed to open stream: Too many open files

Posted by – April 24, 2015

Office Worker with Mountain of Paperwork公司的 Team 開發的產品也越來越龐大了,API 的部份依賴 Laravel 內建整合的 PHPUnit 來進行自動化測試。

這回從 git repo 上拉了新的 code 後,照慣例跑一下 phpunit,然後就掛了...

PHP Warning: failed to open stream: Too many open files

好像沒見過這樣的訊息。Google 一下才知道是踩到了 Mac OS 檔案開啟上限的地雷。

$ ulimit -a # 這個指令可以看系統限制
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 256 # 這行
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 709
virtual memory          (kbytes, -v) unlimited

ulimit -n 1024 臨時改為 1024 就可以正常跑完測試了。一時好奇借了同事的電腦看看,都是 Macbook Air (with Yosemite),其他人的 open files 值是 2560 耶,為什麼我的只有 256 ??? 不解。

How to override the path of PHP to use the MAMP path?

Posted by – August 4, 2014

最近在學習 Laravel Framework 的使用,現在 PHP 許多 Library、Framework 都改用了 composer.phar 這個套件管理功能。

其中許多管理功能需要在 command line 輸入指令,但是 Mac 其實也有內建 PHP,卻缺了許多常用的套件。

與其去補安裝套件,不如改用 MAMP 的 PHP 還比較省事。

在 ~/.bash_profile 加入以下指令:

export PATH="/Applications/MAMP/bin/php/php5.5.3/bin:$PATH"

這樣使用 PHP 指令就會自動選用 MAMP 下的版本和套件,其中紅字請改為您使用的版號。

PHPExcel: Delete sheets

Posted by – June 6, 2014

PHPExcel 是 PHP 所有第三方套件裡,功能相當強大的一款,主要用途是用來讀寫 Excel (.xls)、Excel 2007 (.xlsx)、Libre/OpenOffice Calc (.ods)、CSV 等等各種試算表文件。

儘管是個強大的套件,個人倒是覺得這套件的語法相當複雜、不夠直覺,文件也不是非常容易閱讀,往往還是去找 stack overflow 的範例更快。

最近在幫客戶開發報表的功能,匯出 Excel 試算表是常見的作法。寫著寫著,突然想到每次產生只有一個 sheet 的試算表,都會被自動再加一張空白的 sheet ... 就像下圖這般。

excel_with_empty_sheet

然後就.... 很想刪掉它 XD

在 stackoverflow 沒看到討論,倒是官方論壇有討論到,用法難得的直覺 (?)。

$objPHPExcel->removeSheetByIndex(1);

參數是第幾個 sheet,從 0 開始,所以要刪除 [Worksheet 1],就代入參數 1 醬。

Detect Browser Language in PHP

Posted by – January 28, 2014

最近某個已經上線的專案新增了製作多國語系介面的需求,流程上打算用 PHP 來偵測使用者瀏覽器的語言設定。

Firefox Language Preference More

Force SSL/https using mod_rewrite or PHP

Posted by – January 17, 2014

工作需求,要幫某網站全部轉為 HTTPS 加密傳輸。

有兩種方法,一是透過 .htaccess,二是用 PHP 來處理。

用 .htaccess 處理的方式如下

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

用 PHP 來處理:

if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
    if(!headers_sent()) {
        header("Status: 301 Moved Permanently");
        header(sprintf(
            'Location: https://%s%s',
            $_SERVER['HTTP_HOST'],
            $_SERVER['REQUEST_URI']
        ));
        exit();
    }
}

個人比較偏好用 .htaccess 來處理。不過若是沒有全部頁面都需要的話,PHP 比較方便。

 

Check if PHP session has already started

Posted by – January 16, 2014

用 PHP 寫系統基本上躲不掉 session 相關的一系列問題的。因為個人偏好 CodeIgniter,所以很多時候用 Session Library 或是開源的 Native Session Library 就可以避開一些煩瑣的麻煩。

不過很多時候碰到沒有使用 framework 當基礎,直接用 PHP 開發的專案時,程式碼裡 "充斥" 著 session_start() 也是非常普遍的事情,接著就會一直在 error_log 裡看到這樣的訊息:

PHP Notice:  A session had already been started

看了有夠礙眼,也讓人覺得很不專業。

其實這問題是可以避免的。只要在宣告前檢查是否啟動就好。

isset($_SESSION) or session_start();

如果使用的是 PHP 5.4 以上的版面,還有更可靠的寫法。

(session_status() !== PHP_SESSION_ACTIVE) or session_start();

乾乾淨淨的是 error_log 才是開發者的王道。