Category: 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 才是開發者的王道。

Disable Cache for PHP 5.5.3 in MAMP

Posted by – January 3, 2014

前些時間接手一個已經上線的案子,進行改版作業。從 Git 上把 code 拉下來,執行一下就... 爆了。發現同事用了相當新的 PHP 語法。我本機的開發環境是 PHP 5.2.x,無法執行。

馬上想到 MAMP 可以切換成新的版本,切換成 5.5.3 問題就解決了。

MAMP PHP version switch More

Codeigniter: Check if libraries/helper/core files is loaded

Posted by – September 13, 2013

使用 CodeIgniter 框架開發時,會透過 $this->load->library() 之類的方法來載入特定的檔案。在 CI 的 routing 規則中,同一個檔案只會被載入一次,因此多寫幾次 $this->load->library() 是不會發生什麼慘劇的。

不過有時候還是想確認到底某 class 有沒有載入過,而不想到處丟 $this->load->xxx() 的話,可以用以下方法來確認。

if (class_exists('Library'))
{
    $this->library->myMethod();
}

要注意的是 CI 有別名的規則,比如說有個 model 叫 'file_model'。使用別名載入時,會寫成以下

$this->load->model('file_model', 'file');
$this->file->do_something();

這種情況下,若要檢查是否已載入,要用原名。如下:

if (class_exists('File_model'))
{
    // do something
}

Installing ionCube Loader on MAMP

Posted by – June 10, 2013

在幫客戶 debug 的時候注意到 SVN 裡一小部份程式碼居然被 ionCube 加密過了.... 加密後的程式碼丟到 SVN 是有個屁用啊。

算了,這不重要。重點是我的開發機變成也要可以執行 ionCube 才行。要在 Mac 上的 MAMP 下安裝 ionCube loader 還蠻簡單的,隨手筆記一下。ionCube 的 loader 是免費的,encoder 才要付費。

先到 Loaders for ionCube Encoded Files 下載對應的 loader。我的開發環境是 OS X (x86-64)。下載了 .zip 包,解開來有幾個檔案。

Unpacked ionCube Loader File

其中的數字是 PHP 的版本,例如 5.2, 5.3 等,_ts 是指 Thread Safety,MAMP 的 PHP 沒有 Thread Safety,所以選用一般版即可。如果不確定環境有沒有 ts,可以用 phpinfo 看一下。

建議將 loader-wizard.php 丟到 MAMP/htdocs 下面執行一下,會提示安裝路徑和是否安裝成功等訊息。

以我的環境來說,我是把 ioncube_loader_dar_5.3.so copy 到

/Applications/MAMP/bin/php/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/ioncube_loader_dar_5.3.so

接著打開 /Applications/MAMP/bin/php/php5.3.6/conf/php.ini,在 1085 行左右加上以下設定:

zend_extension="/Applications/MAMP/bin/php/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/ioncube_loader_dar_5.3.so"

注意:如果 php.ini 裡沒有出現 zend_extension,這行加在任一位置,否則,把它加在第一個 zend_extension 語句前。

存檔,重新啟動 server,再用 loader-wizard.php 檢查一下,確定 loader 安裝成功。

Install ionCube successfully

Formula Error in PHPExcel

Posted by – June 7, 2013

最近在幫客戶開發報表功能,其中一個小功能是將查詢結果匯出成 Excel 檔案,對於會計人員來說相當依賴 Excel。

如果沒有什麼特別的需求,通常會用 PHPExcel 來製作檔案。

一個典型的儲存格設定如下:

$objPHPExcel->setActiveSheetIndex(0)
              ->setCellValue('B6', $order['order_no']);

就在這個匯出功能即將完成時,發現有一個欄位只要一存入內容,PHPExcel 就會出現 Formula Error。

回頭去看資料庫,發現是 VCHAR 欄位,那寫入應該沒啥問題啊。再看看資料內容...

看了兩眼才注意到這個純文字欄位有幾格是 = (等號) 開頭... bingo!

PHPExcel 在指值的時候,碰到等號會轉換成公式,自然就爆掉了。解決方法是把 setCellValue() 換成 setCellValueExplicit(),這樣就不會被轉換了。

收工!

PHP FIG Group PSR standard 程式風格

Posted by – January 25, 2013

這兩天在查資料的時候才知道有 PSR 風格。這是由 PHP FIG Group 所訂的規範。FIG 這個組織是由一群開發 Open Source PHP 專案的開發者所組成,目的在於以最低程度的限制來統一各個專案的 coding style,避免各家自行發展的風格阻礙了程式設計師撰寫的困擾。

PSR 規範有四套,分別為

PSR-0 (Autoloading standard)

PSR-1 (Basic Coding Standard)

PSR-2 (Coding Style Guide)

PSR-3 (Logger Interface)

依順序有繼承性,每一篇都很簡短,蠻建議看看。

PHP: Finding the year quarter for a date

Posted by – November 29, 2012

在工商業應用中,時間紀錄是系統中非常基本的東西,不過時間的表示方法就五花八門了。最近碰到一個需求是同一個時間欄位可能是 Y-m-d (ex. 2012-11-29)、quarter (Q4 2012)、month (Nov 2012),或是 year (2012),甚至是 TBA (to be announced)。

其中轉換成季 (quarter) 會用到一點點技巧,在此筆記如下:

<?
$timestamp = mktime(0, 0, 0, 11, 29, 2012);
echo ceil(date("m", $timestamp)/3);
?>

只要前面再加個 'Q' 就是第幾季了。
在資料庫部份如何分辨客戶是儲存哪種型態,我的做法是多開一個 date_type 的欄位。

CREATE TABLE `calendar` (
	`c_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
	`c_date` date NOT NULL,
	`c_date_type` char(1) NOT NULL DEFAULT 'd',
	PRIMARY KEY (`c_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

然後用以下 function 來轉換日期

function date_converter($date_type, $date){

	$timestamp = strtotime($date);

	switch($date_type){
		case 'd':
			return $date;
			break;
		case 'm':
			return date("Y-m-d", mktime(23, 59, 59, date('m', $timestamp) + 1, 0, date('Y', $timestamp)));
			break;
		case 'q':
			$the_quarter = ceil(date("m", $timestamp)/3);
			return date("Y-m-d", mktime(23, 59, 59, $the_quarter * 3 + 1, 0, date('Y', $timestamp)));
			break;
		case 'y':
			return date("Y-m-d", mktime(23, 59, 59, 12, 31, date('Y', $timestamp)));
			break;
		case 't':
			return '2099-12-31';
			break;
	}
}

最後將轉換後的日期與日期型態一份存入資料庫,讀取時根據型態做切換就可以了。

Updated:

後來又碰到一個需求,碰到資料庫日期相同時,例如季和月兩種日期都是記錄最後一天,Q1 與 March 在資料庫裡都是 3 / 31,排序的時候就有可能出現 Q1, March, Q1, March 交錯的現象。解決方法是在 SELECT ... ORDER BY ... 加個自訂排序,用前述的 date_type 來排列。

SELECT * FROM `calendar` ORDER BY `c_date` ASC, FIELD(`c_date_type`, `d`, `m`, `q`, `y`, `t`)

如此就可以避免不同型態日期交錯的問題。

 

Sharpen an image using PHP and GD

Posted by – September 26, 2012

同事正在開發一個購物平台,店家可以上傳商品照片,由系統自動縮圖到適合版面的大小,不過 resize 後圖片品質明顯下降,於是同事問我有沒有解法。同事問我並不是因為我比較強,只是因為我的位子在他旁邊。

跟同事討論了一下,試用了 imagecopyresizedimagecopyresampled 兩個函式來縮圖,前者的細節比較好,但部份線條出現明顯的變形,後者圖形正確,但看起來比較模糊。所以先用後者縮圖,再想辦法銳化圖片。

印象中 PHP GD Library 並沒有直接提供 sharpen (銳化) 相關的函式,但是 Google 一下,發現有 imageconvolution 濾鏡可以用,真是有趣,沒想到可以自訂濾鏡,以前在學校修過影像處理課程的回憶.... 都忘光光了。還好不難找到幾組範例。

<?php
// create the image resource from a file
$i = imagecreatefromjpeg('otter.jpg');

// define the sharpen matrix
$sharpen = array(
array(0.0, -1.0, 0.0),
array(-1.0, 5.0, -1.0),
array(0.0, -1.0, 0.0)
);

// calculate the sharpen divisor
$divisor = array_sum(array_map('array_sum', $sharpen));

// apply the matrix
imageconvolution($i, $sharpen, $divisor, 0);

// output the image
header('Content-Type: image/jpeg');
imagejpeg($i);
?>

隨便找一張圖做測試,效果如下。

PHP GD Sharpen Comparison

試了幾種參數組合,以下這組效果比較滿意。

array(0.0, -1.0, 0.0),
array(-1.0, 16.0, -1.0),
array(0.0, -1.0, 0.0)

Regular expression which matches a pattern, or is an empty string

Posted by – July 23, 2012

之前介紹過在 jQuery Validation Plugin 使用正規表達式 (Regular Expression) 的方法。

在某個專案中如常地用這個方法做表單欄位檢查,不過客戶提了一個需求,希望該欄位不是必填,但使用者有填資料時要驗證其內容。

原本的驗證條件是這樣的...

regex : "[0-9]{4}-[0-9]{6}"

也就是使用者一定要輸入正確的手機號碼,如 0919-123456。要變成選填的話... 想了一下,改成下面這樣就可以了。

regex : "([0-9]{4}-[0-9]{6})?"

前後加上括號,然後用最未端的 問號 來表示 前面這串括號裡的文字可以不出現或出現一次

PHP: Constrains a value to not exceed a maximum and minimum value

Posted by – March 19, 2012

幾年前在寫 Processing (P55) 的時候,有個方便的 function 叫 constrain。用法是

constrain(value, min, max)

例如

float mx = constrain(mouseX, 30, 70);

當 mouseX 大於 70 時就回傳 70,小於 30 時就回傳 30,將值限制在 30 ~ 70 這個區間。因為 Processing 大都是拿來寫互動、視覺方面的程式,所以用到此功能的頻率頗高。後來轉換跑道寫其他語言時,發現好像都沒有這個方便的函式。
最近又有 constrain 的需求,想起幾年前見過有人用 min, max 來代替,寫法如下。

<?php
function constrain($value, $min, $max){
    return max( min( $value, $max), $min);
}

echo constrain(29.3, 30, 60);
?>