2022年7月27日 星期三

PHP 8.1 測試

PHP 8.1 使用 VS 2016 來編譯的。

ODBC 的考量

參考 Microsoft 的 [Docs/SQL  連線/ODBC/Windows] 的 系統需求、安裝和驅動程式檔案

SQL 的版本相容性,因為不曉得連結那時會失效,擷圖如下


支援的作業系統,擷圖如下。測試的結果,在 Windows 7 和 Windows 10 上都可以用。


但是在使用 sqlsrv_connect() 來連 SQL Server 2008 時,還是碰到一些問題。

連接的程式片段如下。注意必須指定語言編碼,不然 SQL 丟回的錯誤訊息會變一堆亂碼。

$serverName = "10.161.86.186";
$connectionInfo = array(
    "UID" => 'sa', "PWD" => 'abcd1234', "Database"=>"db_test" , 
    "CharacterSet" => "UTF-8"
);

$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false )
{
    echo "Could not connect.\n";
	echo '<pre>'.print_r( sqlsrv_errors(), true).'</pre>';
    exit;
}

由瀏覽器觀看結果如下

Could not connect.
Array
(
    [0] => Array
        (
            [0] => IM006
            [SQLSTATE] => IM006
            [1] => 0
            [code] => 0
            [2] => [Microsoft][ODBC 驅動程式管理員] 驅動程式的 SQLSetConnectAttr 失敗
            [message] => [Microsoft][ODBC 驅動程式管理員] 驅動程式的 SQLSetConnectAttr 失敗
        )

    [1] => Array
        (
            [0] => 01000
            [SQLSTATE] => 01000
            [1] => 5701
            [code] => 5701
            [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]已將資料庫內容變更為 'NTU_test'。
            [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]已將資料庫內容變更為 'NTU_test'。
        )

    [2] => Array
        (
            [0] => 01000
            [SQLSTATE] => 01000
            [1] => 5703
            [code] => 5703
            [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]已將語言設定變更為 繁體中文。
            [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]已將語言設定變更為 繁體中文。
        )

)

搜尋得到的解答,最快的方法是升級 ODBC driver 的版本,但升級之後就不支援 SQL Server 2008 了,但有人提供一個簡單的作法,請參照 Cannot connect to SQL Server with PHP script

作法是在程式開頭加上 sqlsrv_configure('WarningsReturnAsErrors', 0); 。因為 sqlsrv_connect() 傳回的只是 warning,並非 error。

後來又測了 pdo_sqlsrv,就很正常,不會出現上面的錯誤。


關於 null array 的 element

到現在才知道,在 PHP 中,存取 null array 的 element 並不會出錯,直到 PHP 7.4 之後 ...

Actually, John Vargo was correct. If a variable is null, accessing it as if it were an array will simply return null without notices. This will change in the upcoming 7.4 version, then it will produce a notice.

Notice: Trying to access array offset on value of type null

The actual output is still the same.


2022年7月21日 星期四

使用 LibreOffice 將 doc 或 ppt 轉換成 pdf

動機

有些網頁服務,會要求使用者上傳文件,如課程大綱的摘要,有許多人會上傳 word 檔,在網頁無法直接預覽其上傳的內容。要在網頁預覽,必須將其轉換成 pdf 比較方便。Openoffice 有一種 headless 的執行模式,可用 PHP 來呼叫,於線上完成轉檔。

作法

本以為 Openoffice 都有這功能,我的電腦原來是裝 Apache Openoffice 4.1,在 cmd 視窗都輸入指令,都會開啟視窗。後來確認 Apache Openoffice 4.1 無法使用 headless 模式轉換 pdf。移除後,改裝 LibreOffice 7.3 即可成功執行。

使用命令視窗來測試

c:\Program Files\LibreOffice\program>soffice.com --headless --convert-to pdf c:\temp\test.doc --outdir c:\temp
convert C:\temp\test.doc -> c:\temp\test.pdf using filter : writer_pdf_Export
Overwriting: c:\temp\test.pdf

大約等 10 秒,順利完成轉檔。

透過 PHP 來執行轉檔

$soffice_cmd = 'c:\Program Files (x86)\LibreOffice\program\soffice.com';

$conv_cmd = "\"{$soffice_cmd}\" --headless --convert-to pdf c:/temp/test.dot --outdir c:/temp ";
        
echo "轉成PDF檔案: $conv_cmd<br>\n";
$exec_flg = exec($conv_cmd, $res_out, $res_code);

var_dump($exec_flg);
var_dump($res_out);
var_dump($res_code);

從瀏覽器執行,會等很久,超過執行時間而失敗。

在命令視窗,直接用 PHP 執行該 script 檔,則能順利執行。

從 Google 找答案,大約就是權限問題,因為透過 IIS 執行,其 home 目錄不曉得是那裡,LibreOffice 無法建立暫存檔,就掛在那邊。解決辦法是指定 -env 參數。

修正後的程式如下

$soffice_cmd = 'c:\Program Files (x86)\LibreOffice\program\soffice.com';

$conv_cmd = "\"{$soffice_cmd}\" -env:UserInstallation=file:///c:/temp --headless --convert-to pdf c:/temp/test.dot --outdir c:/temp ";
        
echo "轉檔指令: $conv_cmd<br>\n";
$exec_flg = exec($conv_cmd, $res_out, $res_code);

var_dump($exec_flg);
var_dump($res_out);
var_dump($res_code);

請注意,目錄的格式是URL的形式。

至於 Linux 下,則是指定 HOME 參數

$conv_cmd = "export HOME=/tmp && soffice --headless --convert-to pdf ".$doc_path." --outdir /tmp/";
echo "轉檔指令: $conv_cmd<br>\n";
exec($Linux_command);




2022年7月3日 星期日

Larevel 5.4 以後,設定 DB 的 fetch mode 為 PDO::FETCH_ASSOC

參考網址

Go to: app/Providers/EventServiceProvider.php

Add this to the top of the file:

use Illuminate\Database\Events\StatementPrepared;

In the boot method add:

Event::listen(StatementPrepared::class, function ($event) {
    $event->statement->setFetchMode(\PDO::FETCH_ASSOC);
});

In t

網誌存檔