Feeds:
文章
迴響

Archive for the ‘PHP’ Category

這是困擾許久可是一直被擱置的問題,直到今天又遇到這個問題,再次尋找終於找到解法,問題簡單的描述,

架構

Browser –> Apache –> php-fpm

基於效能考量每個階段都會被緩衝(buffer),因此對於下面這種程式

<?php

for($i=1;$i<=3;++$i){
    echo $i;
    sleep(5);
}

?>

你只會一次看到 “123″, 而不會每隔 5 秒印出一個數字,解法我最早在[1]找到,不過沒有寫原因,後來又找到[2]有專屬討論這個問題,簡單的改法如下:

<?php

   header('Content-Type: text/HTML; charset=utf-8');
   header('Content-Encoding: none;'); // 關閉 apache 壓縮
   ini_set('output_buffering', 'Off'); // 關閉 php 輸出緩衝
   @ini_set('zlib.output_compression',0); // 關閉 zip 壓縮
   @ini_set('implicit_flush',1); // 啟用自動 flush
   @ob_end_clean(); // 清除緩衝區資料
   ob_implicit_flush(1); // 啟用自動 flush
   for($i=1;$i<=3;++$i){
        echo $i;
        flush(); 
        echo str_repeat(' ',1024*64); // 填滿緩衝區,導致強制輸出
        sleep(5);
   }

 

 

參考資料

[1] https://github.com/Eugeny/ajenti-v/issues/84

[2] http://stackoverflow.com/questions/4706525/php-flush-not-working

廣告

Read Full Post »

有一個需求是兩個模組介面相同,可是內部實做不同,如何用最少的程式碼去替換成另一個模組

模組結構

/module

/v1

– WebService.php

– Function.php

– …

/v2

– WebService.php

– Function.php

– …

主程式

use \module\v1\WebService;

use \module\v1\Function;

$ws = new WebService();

$ws->doIt();

如果上述程式此時要替換成 v2 一個簡單的作法就是在程式的每個地方都改為 v2

不過太費工了,於是用了 autoload + class_alias 解決

完整的概念驗證代碼

核心就是下方的紅框處, 在 autoload 時,取得 v1 並在內部動態引入替換的檔案, 然後用 class_alias 將新的模組 namespace 替換掉舊的

然後程式執行結果就是 v2 … 不過缺點是,後續維護的人可能會 confuse

20140711-02

 

 

 

Read Full Post »

PHP5.3 – PHP5.4 模擬 finally

PHP5.5 將提供 try..catch.finally… 不過之前版本無緣享用

[1] 提供 __invoke + closure 方式模擬 finally, 程式片端摘錄如下

class FinallyEmulator
{
    public function __construct($callable)
    {
        if (!is_callable($callable))
            throw new ErrorException('Ooops, bad callable.');
        $this->callable = $callable;
    }

    public function __destruct()
    {
        $this->invoke();
    }

    public function __invoke()
    {
        $this->invoke();
    }

    private function invoke()
    {
        if ($this->callable)
        {
            $callable = $this->callable;
            $this->callable = NULL;
            call_user_func($callable);
        }
    }

    private $callable;
}

/**
 * Note that return value of $callable is totally ignored.
 */
function finally($callable)
{
    return new FinallyEmulator($callable);
}
class A
{
    public function doSomething()
    {
        $my_resource = open_my_resource();
        $finally = finally(function () use ($my_resource)
        {
            free_my_resource($my_resource);
        });
        try
        {
            $result = use_my_resource($my_resource);
        }
        catch (Exception $e)
        {
            fprintf(STDERR, "Error: %s\n", $e->getMessage());
            throw $e;
        }
        $finally();
        return $result;
    }
}

參考資料
[1] https://athos.blogs.balabit.com/2011/02/try-catch-finally-in-php/

Read Full Post »

自從開始寫單元測試之後,一直很想知道哪些程式碼上尚未測試過,以前用C#時VS整合的很好,很容易可以看到這份報表。而PHP呢?

後來發現 PHPUnit 有支援 Code Coverage 的報表,可惜一直沒有時間研究,拖到現在終於可以把收集到的資料整理一下。首先 PHPUnit 的 Code Coverage 是利用 XDebug 的 Code Coverage 分析結果所進行的後處理, XDebug 的分析是透過攔截OPCode 進行分析[1]。所以,簡單的來說只要Code Coverage資訊不一定要 PHPUnit。

因為個人不喜歡用 PHPUnit,雖然他框架完整而強大,但也造成門檻高,不一定符合自己的程式流程, 總之這是個人偏好,所以,我想抽離這部份的功能。[2] 已經有描述如何只用含概率的功能,不過還是需要 PHPUnit, 所以先保留。不過該文提到可透過  php.ini 的 auto_prepend_file 將程式碼注入已經存在的程式碼中,這是相當不錯的方式我將其吸收。

那剩下分析 XDebug 的含概率數據的後處理問題,[3]給了我想要的範本,我將 code_coverage_class.php 微調成我要的資訊。另外我將 auto_prepend_file 概念融入產生 code_coverage_prepend.php  方便程式分析。

以下是我整理的測試

php -d auto_prepend_file=code_coverage_prepend.php b.php

其中  auto_prepend_file=code_coverage_prepend.php 就是應用 [2] 提到的注入方式。

圖片

如下是執行結果報表 (code_coverage_reports/b.php.html)

黃色是有執行的部份,紅色則是未執行到的部份。而上面的摘要簡單的呈現執行過得程式碼涵蓋比例。

圖片

有興趣的人可到套件首頁 https://github.com/cwchiu/php_code_coverage_reports 試玩

補充1:記得 php.ini 要打開 XDebug

[xdebug]
zend_extension=ext/php_xdebug.dll
xdebug.coverage_enable=1

補充2: XDebug 的 code coverage 是在 PHP 中使用 xdebug_start_code_coverage/xdebug_get_code_coverage/xdebug_stop_code_coverage 來取得分析資料,所以如果想要分析小區段,可直接應用這些函數

補充3:  PHPCoverage[5]的報表做的蠻漂亮可以學習

 

參考資料

[1] http://blog.csdn.net/qw_study/article/details/6212260

[2] http://www.phpclasses.org/browse/file/29511.html

[3] http://shiftlock.wordpress.com/2009/11/01/standalone-php-code-coverage-without-phpunit/

[4] http://xdebug.org/docs/code_coverage

[5] http://sourceforge.net/projects/phpcoverage/

Read Full Post »

PHP curl 奇怪的錯誤

測試環境

1) windows xp 32bits

2) apache 2.2.16

3) php 5.3.8

一個簡單的官方的檔案上傳範例

$ch = curl_init();

$data = array(‘name’ => ‘Foo’, ‘file’ => ‘@c:/a.js’);
curl_setopt($ch, CURLOPT_URL, ‘http://10.62.8.220:8008/rest/viewcam/test&#8217;);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_exec($ch);

執行結果非常好

 

 

變更一下順序

$ch = curl_init();

$data = array(‘name’ => ‘Foo’, ‘file’ => ‘@c:/a.js’);
curl_setopt($ch, CURLOPT_URL, ‘http://10.62.8.220:8008/rest/viewcam/test&#8217;);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POST, 1);
curl_exec($ch);

啊~ 死了

Wireshark 攔截封包觀察發現,設定位置不同會導致 Content-Length 錯誤 orz

Read Full Post »

讓物件支援序列化。如下範例

<?php

class My implements Serializable {
private $data;
function __construct($data){
$this->data = $data;
}

function serialize(){
return implode(‘,’, $this->data);
}

function unserialize($s){
$this->data = explode(‘,’, $s);
}
}

$aMy = new My(array(1, 2, 3, 4, 5));
echo ‘[init State]<br/>’;
print_r($aMy);
echo ‘<hr/> [Serialize]<br/>’;
$data = serialize($aMy);
echo ‘Serialize: >>’ . $data;
echo ‘<hr/> [Unserialize]<br/>’;
$aMy2 = unserialize($data);
print_r($aMy2);

?>

執行結果

image

Read Full Post »

利用 PHP 取得網路時間

<?php
/**
 * 取得網路恔正時間
 *
 **/
$server = 'time.nist.gov';
$port	= 13;

$fp = fsockopen($server, $port, $errno, $errstr, 30);
if (!$fp) {
   echo "$errstr ($errno)
n"; } else { while (!feof($fp)) { echo fgets($fp, 128); } fclose($fp); } ?>

Read Full Post »

Older Posts »