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

[Java] Invalid HTTP method: PATCH

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