2014年12月1日 星期一

[PHP] parent::__construct()

class A {}
class B extends A {
    public function __construct() {
        parent::__construct(); // PHP Fatal error:  Cannot call constructor in test.php on line 5
    }
}
new B();

執行上面的程式會發生錯誤。

在 Java 這樣寫是合法的,由此可知 PHP 把 __construct() 當成一種特殊方法使用, 因此既有方法的功能,有又建構子的功能。

PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年7月8日 星期二

[PHP] 轉換 HTML entity 在字串中

在處理 BIG5 轉換 UTF-8 的過程中,字串還存在一些 HTML entity 需要額外處理。 以下還順便處理缺省分號造成沒有轉換的問題。

function entity2utf8($s)
{
   return preg_replace_callback('|&#\d+;?|', function ($matches) {
         $match = $matches[0];
         if (mb_strpos($match, ';') === false) {
             $match .= ';';
         }
         return mb_convert_encoding($match, 'UTF-8', 'HTML-ENTITIES');
      }, $s);
}

$s ='英國皇冠♔';
echo entity2utf8($s); // output: 英國皇冠♔
PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年6月24日 星期二

[MySQL] 匯出 & 匯入

使用 mysqldump 會產生 SQL 語法,以下是從官網介紹直接撈出資料的方式,用於大量匯入會比較快。

$ mysqldump db1 table1 > table1.sql
...
INSERT INTO `table1` VALUES ('0000-00-00 00:00:00',1,NULL,'apple'),('0000-00-00 00:00:00',2,'tw','apple');
...

$ mysql -u user1 table1 < table1.sql


mysql> SELECT * INTO OUTFILE '/tmp/table1.txt' FROM table1;

0000-00-00 00:00:00^I1^I\N^Iapple
0000-00-00 00:00:00^I2^Itw^Iapple

mysql> SELECT * INTO OUTFILE '/tmp/table1.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' FROM table1;

"0000-00-00 00:00:00",1,\N,"apple"
"0000-00-00 00:00:00",2,"tw","apple"


mysql> LOAD DATA INFILE 'table1.txt' REPLACE INTO TABLE table1 FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';

參考資料:

2014年5月28日 星期三

[PHP] stream resource output

最近看到有一段代碼是要輸出 CSV 檔案:

$rows = array();
$rows[] = array(1,2,3,"四");

ob_start();
$fh = fopen("php://output", "w");
foreach ($rows as $r) {
   fputcsv($fh, $r);
}
fclose($fh);
$csv = ob_get_clean();

var_dump($csv);

從上述代碼看到,轉換的方式是使用 Output Control Functions。

其實這麼寫並不妥當。整段代碼被 ob 給限制住了,如果還要做一些錯誤處理,會使得代碼變得相當複雜。 而且個人相當排斥使用 ob。

有寫過 Java 應該都會先想到 java.io 提供的 InputStream。 所以試著查詢 PHP Manual,找到相似的方法,重新改寫一下:

$rows = array();
$rows[] = array(1,2,3,"四");

$fh = fopen("php://temp", "w+");
foreach ($rows as $r) {
   fputcsv($fh, $r);
}
rewind($fh);
$csv = stream_get_contents($fh);
fclose($fh);

var_dump($csv);

去掉 ob 之後,整個資料流就都在 stream resource 處理,看起來會比較適當。 日後有需要修改或除錯也都變得比較容易多。

參考資料:http://www.php.net/manual/en/function.stream-get-contents.php

PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年5月8日 星期四

[PHP] usort()

這次要來討論 usort()。

我們假設以下情況依照水果名稱排序:

$fruits = array(
   array('id' => 1, 'name' => 'apple'),
   array('id' => 2, 'name' => 'orange'),
   array('id' => 3, 'name' => 'banana'),
   array('id' => 4, 'name' => 'banana'));

usort($fruits, function ($a, $b) {
   if ($a['name'] === $b['name']) { return 0; }
   return $a['name'] < $b['name'] ? -1 : 1;
});

/**
 * Output:
 * $fruits = array(
 *    array('id' => 1, 'name' => 'apple'),
 *    array('id' => 4, 'name' => 'banana'),
 *    array('id' => 3, 'name' => 'banana'),
 *    array('id' => 2, 'name' => 'orange'));
 */
結果似乎有點不符預期,banana (id=3) 不是出現在 banana (id=4) 前面。

再測試一次,這次改為回傳總是 0,即左右等值,所以預期順序應該不會有任何變化。 但是做完 usort() 之後,結果卻是幫我們進行一次反向排序。

usort($fruits, function ($a, $b) { return 0; });

/**
 * Expect:
 * $fruits = array(
 *    array('id' => 1, 'name' => 'apple'),
 *    array('id' => 2, 'name' => 'orange'),
 *    array('id' => 3, 'name' => 'banana'),
 *    array('id' => 4, 'name' => 'banana'));
 * Acutal:
 * $fruits = array(
 *    array('id' => 4, 'name' => 'banana'),
 *    array('id' => 3, 'name' => 'banana'),
 *    array('id' => 2, 'name' => 'orange'),
 *    array('id' => 1, 'name' => 'apple'));
 */

查閱 PHP Manual 之後, 發現到這段說明:

Note: If two members compare as equal, their relative order in the sorted array is undefined.
原來等值並沒有規定怎麼處理。

由於等值並沒有規範,所以我們需要再做一些判斷,才會是我們預期的結果。

usort($fruits, function ($a, $b) {
   if ($a['name'] === $b['name']) { return $a['id'] > $b['id']; }
   return $a['name'] < $b['name'] ? -1 : 1;
});

/**
 * Output:
 * $fruits = array(
 *    array('id' => 1, 'name' => 'apple'),
 *    array('id' => 3, 'name' => 'banana'),
 *    array('id' => 4, 'name' => 'banana'),
 *    array('id' => 2, 'name' => 'orange'));
 */

PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年5月2日 星期五

[PHP] 轉換資料 Ⅱ:for-loop vs. array_reduce()

/**
 * Origin:
 *  array(
 *    array('id' => 'x', 'name' => 'apple'),
 *    array('id' => 'y', 'name' => 'banana'),
 *    array('id' => 'z', 'name' => 'orange'),
 *  );
 * Expect:
 *  array(
 *    'x' => 'apple',
 *    'y' => 'banana',
 *    'z' => 'orange',
 *  );
 */
function calc($t0, $t1)
{
   $v0 = array_sum(explode(' ', $t0));
   $v1 = array_sum(explode(' ', $t1));
   $sub = $v1 - $v0;
   echo "$v1 - $v0 = $sub\n";
   return $sub;
}

$origin = array(
   array('id' => 'x', 'name' => 'apple'),
   array('id' => 'y', 'name' => 'banana'),
   array('id' => 'z', 'name' => 'orange'));

$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) {
   $expect = array();
   foreach ($origin as $o) {
      $expect[$o['id']] = $o['name'];
   }
}
$t1 = microtime();
$method1 = calc($t0, $t1);     // output: 1420770579.8306 - 1420770578.0456 = 1.785040140152

$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) {
   $expect = array_reduce($origin, function ($result, $item) {
      $result[$item['id']] = $item['name']; return $result; });
}
$t1 = microtime();
$method2 = calc($t0, $t1);     // output: 1420770588.2167 - 1420770579.8308 = 8.3859748840332

echo ($method2 - $method1) / $method2; // output: 0.78713981798936

從測試中可以知道,使用 array_reduce() 比之 for-loop 較慢,雖然看起來比較簡潔。

PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年4月3日 星期四

[PHP] 轉換資料:for-loop vs. array_map()

/**
 * Origin:
 *  array(
 *    array('id' => 'x'),
 *    array('id' => 'y'),
 *    array('id' => 'z'),
 *  );
 * Expect:
 *  array('x', 'y', 'z');
 */
function calc($t0, $t1)
{
   $v0 = array_sum(explode(' ', $t0));
   $v1 = array_sum(explode(' ', $t1));
   $sub = $v1 - $v0;
   echo "$v1 - $v0 = $sub\n";
   return $sub;
}

$origin = array(array('id' => 'x'), array('id' => 'y'), array('id' => 'z'));

$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) {
   $expect = array();
   foreach ($origin as $o) {
      $expect[] = $o['id'];
   }
}
$t1 = microtime();
$method1 = calc($t0, $t1);     // output: 1420770195.046 - 1420770193.6821 = 1.3638761043549

$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) {
   $expect = array_map(function ($o) { return $o['id']; }, $origin);
}
$t1 = microtime();
$method2 = calc($t0, $t1);     // output: 1420770202.2368 - 1420770195.0461 = 7.1907830238342

echo ($method2 - $method1) / $method2; // output: 0.81032995991755

從測試中可以知道 array_map() 比 for-loop 慢。不過 array_map() 寫法比較簡潔。

補充,引用外部參數:

   $key = 'id';
   $expect = array_map(function ($o) use ($key) { return $o[$key]; }, $origin);
PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

2014年1月21日 星期二

[PHP] __DIR__ vs. dirname(__FILE__)

PHP 5.3 開始支援 __DIR__,從測試中可以知道 __DIR__ 略快于 dirname(__FILE__)。

function calc($t0, $t1)
{
   $v0 = array_sum(explode(' ', $t0));
   $v1 = array_sum(explode(' ', $t1));
   $sub = $v1 - $v0;
   echo "$v1 - $v0 = $sub\n";
   return $sub;
}
$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) require_once __DIR__.'/a.php';
$t1 = microtime();
$new = calc($t0, $t1);     // output: 1420769613.0535 - 1420769612.1561 = 0.89736700057983

$t0 = microtime();
for ($i = 0; $i < 1E6; $i++) require_once dirname(__FILE__).'/a.php';
$t1 = microtime();
$old = calc($t0, $t1);     // output: 1420769614.9498 - 1420769613.0548 = 1.8950188159943

echo ($old - $new) / $old; // output: 0.52646011057731

參考資料:http://www.php.net/manual/en/language.constants.predefined.php

PHP 5.3.24 (cli) (built: Jun 10 2013 16:42:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
    with Xdebug v2.2.0rc1, Copyright (c) 2002-2012, by Derick Rethans

[Java] Invalid HTTP method: PATCH

最近系統需要使用 Netty4,所以把衝突的 Netty3 拆掉,然後就出現了例外。 pom.xml <dependency> <groupId>com.ning</groupId> <artifactId>as...