2015年10月28日 星期三

PHP使用fsockopen() post資料到URL並取得內容

在 php使用curl上傳檔案 中,PHP使用curl去呼叫API,
在ubuntu系統中 curl必須安裝 php5-curl
除了curl還有另一種方法,使用fsockopen()

測試API接口
http://test.localhost/PHP/wget_server.php
echo json_encode($_REQUEST);

stackoverflow的範例
// http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen
$fp = fsockopen('test.localhost', 80); // 必須寫 test.localhost。不能加protocol、位址和參數 http://test.localhost/PHP/wget_server.php?bear=2

// post的資料
$vars = array(
    'hello' => 'world',
    'bear' => 123
);
$content = http_build_query($vars); // $content:hello=world&bear=123

fputs($fp, "POST /PHP/wget_server.php?bear=2 HTTP/1.1\r\n"); // 位址和參數寫在這,使用POST
fputs($fp, "Host: test.localhost\r\n"); //再寫一次host
fputs($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-Length: ".strlen($content)."\r\n");
fputs($fp, "Connection: close\r\n");
fputs($fp, "Referer: https://www.google.com.tw/?gws_rd=ssl\r\n"); //偽造 $_SERVER['HTTP_REFERER']
fputs($fp, "Cookie: test=456;ai=789\r\n"); // 設定$_COOKIE
fputs($fp, "\r\n");

fputs($fp, $content);

header('Content-type: text/plain'); // 設定Content-Type為純文字
while (!feof($fp)) {
    echo fgets($fp, 1024);
}

fclose($fp);
結果:
HTTP/1.1 200 OK
Date: Wed, 28 Oct 2015 07:16:26 GMT
Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8
X-Powered-By: PHP/5.6.8
Content-Length: 30
Connection: close
Content-Type: text/html; charset=UTF-8

{"bear":"123","hello":"world"}
$_SERVER['HTTP_REFERER']:https://www.google.com.tw/?gws_rd=ssl
$_COOKIE:Array
(
    [test] => 456
    [ai] => 789
)

說明:
Content-Type,内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
如果未指定 ContentType,默认为TEXT/HTML
指令為text/plain 後,瀏覽器會直接顯示<tag>而不會視為HTML

blog.hsin.tw 的範例
//接收POST參數的URL
$url = "http://test.localhost/PHP/wget_server.php?bear=2";

//POST參數,在這個陣列裡,索引是name,值是value,沒有限定組數
$postdata = array('post_name' => 'post_value', 'acc' => 'hsin', 'nick' => 'joe');

//函式回覆的值就是取得的內容
$result = sendpost($url, $postdata);

echo "\$result:";
print_r($result);

function sendpost($url, $data) {
    
    //先解析url 取得的資訊可以看看http://www.php.net/parse_url
    $url = parse_url($url);
    $url_port = $url['port'] == '' ? (($url['scheme'] == 'https') ? 443 : 80) : $url['port'];
    if (!$url) return "couldn't parse url";
    echo "\$url_port:".$url_port;
    echo "\$url:";
    print_r($url);
    
    //對要傳送的POST參數作處理
    $encoded = "";
    while (list($k, $v) = each($data)) {
        $encoded.= ($encoded ? '&' : '');
        $encoded.= rawurlencode($k) . "=" . rawurlencode($v);
    }

    echo "$encoded:";
    print_r($encoded);
    
    //開啟一個socket
    $fp = fsockopen($url['host'], $url_port);
    if (!$fp) return "Failed to open socket to " . $url['host'];
    
    //header的資訊
    fputs($fp, 'POST ' . $url['path'] . ($url['query'] ? '?' . $url['query'] : '') . " HTTP/1.0\r\n");
    fputs($fp, "Host: " . $url['host'] . "\n");
    fputs($fp, "Content-type: application/x-www-form-urlencoded\n");
    fputs($fp, "Content-length: " . strlen($encoded) . "\n");
    fputs($fp, "Connection: close\n\n");
    fputs($fp, $encoded . "\n");
    
    //取得回應的內容
    $line = fgets($fp, 1024);
    if (!eregi("^HTTP/1.. 200", $line)) return;
    $results = "";
    $inheader = 1;
    while (!feof($fp)) {
        $line = fgets($fp, 2048);
        if ($inheader && ($line == "\n" || $line == "\r\n")) {
            $inheader = 0;
        } 
        elseif (!$inheader) {
            $results.= $line;
        }
    }
    fclose($fp);
    return $results;
}
結果:
$url_port:80
$url:Array
(
    [scheme] => http
    [host] => test.localhost
    [path] => /PHP/wget_server.php
    [query] => bear=2
)
$encoded:post_name=post_value&acc=hsin&nick=joe
$result:{"bear":"2","post_name":"post_value","acc":"hsin","nick":"joe"}

說明:
該網站上的範例有誤,某些換行特殊符號忘記跳脫,如
if($inheader&&($line == "n" || $line == "rn")){
應為
if($inheader&&($line == "\n" || $line == "\r\n")){

\r\n通常是微軟的文件會產生的,在其它的編輯器裡面,會在一行的節尾看到 ^M,那就是\r\n
在php中,字串中的\r\n或\n,要用雙引號才有效。
'\r'是回車,'\n'是換行,前者使光標到行首,後者使光標下移一格。通常用的Enter是兩個加起來

參考資料:
http://blog.hsin.tw/2009/php-post-method-fsockopen/ php 傳送POST到別的URL並取得回應內容 使用fsockopen
http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen PHP Post data with Fsockopen
http://www.t086.com/code/php/function.php-fsockopen.php 函数:fsockopen()
http://seacatcry.pixnet.net/blog/post/13732061-%E3%80%90%E8%BD%89%E8%B2%BC%E3%80%91%5Cr%5Cn%E5%92%8C%5Cn%E7%9A%84%E5%B7%AE%E7%95%B0 【轉貼】\r\n和\n的差異


2015年10月23日 星期五

PHPExcel 使用心得


如果Excel欄位的數字長度太長(如身分證號、訂單編號)。造成資料儲存問題
http://stackoverflow.com/questions/5753469/problem-reading-numbers-from-excel-with-phpexcel
解法:
ini_set("precision", "20");  // 預設值是12




PHP Array 相加與 Array_merge


例:
$arr_a = array('a'=>1, 'b'=>2, 1=>3);
$arr_b = array('b'=>1, 4, 5);

print_r(array_merge($arr_a, $arr_b));

$arr_c = $arr_a + $arr_b;
print_r($arr_c);
結果:
array_merge($arr_a, $arr_b):Array
(
    [a] => 1
    [b] => 1
    [0] => 3
    [1] => 4
    [2] => 5
)

$arr_c:Array
(
    [a] => 1
    [b] => 2
    [1] => 3
    [0] => 4
)

結論:
array_merge() - key 相同=>後蓋前。沒有 key (流水號 key)的值,則會以附加在尾端 (append) 的方式合併上去,而所有流水號 key 的 index 則會重排
array + array - 有 key 的值的部分是相反的前蓋後,而沒有 key(流水號 key)的部分也會前蓋後,流水號 index 不會重排


參考資料:
http://blog.hsatac.net/2012/11/php-array-plus-array-versus-array-merge/ PHP Array 相加與 Array_merge

2015年10月22日 星期四

在windows和linux使用網路共享資料夾( samba 和cifs),非NFS

因為在 在Windows 7 使用docker  中要配置Virtual Box和windows 7 的共享資料夾失敗
所以改用samba和cifs的方式

在windows上產生共享資料夾
1. 開啟網路和共用中心 => 變更進階共用設定 => 開啟網路探索  /  開啟檔案及印表機共用 => 保存
2. 右鍵你要共享的資料夾 => 內容 => 共用 => 進階共用 => 勾選"共用此資料夾" => 權限 =>
選 Everyone 後下方勾選 "完全控制"
3. 右鍵你要共享的資料夾 => 內容 => 安全性 => 編輯 => 新增 => 輸入物件名稱來選取 填入 Everyone => 確定

在linux上存取windows的共享資料夾
$ sudo apt-get install cifs-utils
$ mkdir ~/windows_shared
掛載
$ sudo mount.cifs //192.168.169.140/test_shared /home/bear/windows_shared -o gid=1000,uid=1000

$ sudo mount -t cifs -o uid=1000,gid=1000 //192.168.169.140/test_shared /home/bear/windows_shared
注意: -o 後面的參數需以逗號(,)分開,不能用空白
查uid 和 gid
$ id
必須指定uid和gid,否則資料夾權限在linux會變成root:root

卸載
$ sudo umount -a -t cifs -l windows_shared/

NFS和samba的差異
In a closed network (where you know every device), NFS is a fine choice.
在封閉網路中( 你知道每個裝置 ),NFS是不錯的選擇
It might look more complicated than it really is but it's solid, predictable and fast. Something you can't level against Samba... At least, in my experience.
NFS比較複雜,但他可靠的、可預見的和快速的。有些事情無法和Samba放在同一層級比較
Samba will probably be a bit slower but is easy to use, and will work with windows clients as well..
Samba 可能會慢一點,但容易使用。並且能與windows端工作

$ sudo apt-get install nfs-kernel-server => 看字面就知道意思 nfs已經算linux模組了
samba其實就是網路芳鄰

在Linux上產生共享資料夾
安裝samba
# sudo apt-get install samba
建立samba的使用者和密碼
# smbpasswd -a bear
建立共享資料夾
$ mkdir ~/ubuntu_shared
編輯設定檔
# vim /etc/samba/smb.conf
格式:
[<folder_name>]
path = /home/<user_name>/<folder_name>
available = yes
valid users = <user_name>
read only = no
browsable = yes
public = yes
writable = yes
Ex.
[ubuntu_shared]
path = /home/bear/ubuntu_shared
available = yes
valid users = bear
read only = no
browsable = yes
public = yes
writable = yes
重啟samba
# service smbd restart

在windows上存取linux的共享資料夾
(假設要建在桌面)在桌面上點右鍵 => 新增 => 捷徑 =>
輸入項目的位置:
\\192.168.169.147\ubuntu_shared

之後便可在sublime 將該捷徑加入project

Windows開分享資料夾給Windows(以前的網路上的芳鄰、網芳)
https://support.microsoft.com/zh-tw/kb/2702421
先在資料夾上點右鍵 => 內容 => 共用 => 共用(按鈕) => 新增Everyone => 共用(按鈕) => 完成
其他電腦嘗試連接該共用資料夾時,出現詢問帳號密碼的對話視窗,若您沒有共用資料夾電腦上的使用者名稱及密碼是無法順利存取該資料夾。
問題的發生原因
這是因為預設是啟用以密碼保護共用,所以共用對象若沒有共用資料夾所在之電腦的使用者帳戶和密碼,即便是開放 Everyone 仍無法存取共用的檔案。
問題的解決方法
控制台 => 網路和網際網路 => 網路和共用中心 => 變更進階共用設定 => ( 家用或工作場所 => 以密碼保護的共用 )選取"關閉以密碼保護的共用"
取消共用
資料夾上點右鍵 => 內容 => 共用 => 進階共用(按鈕) => 取消勾選"共用此資料夾"

參考資料:
http://www.howtogeek.com/176471/how-to-share-files-between-windows-and-linux/ How to Share Files Between Windows and Linux
http://stackoverflow.com/questions/74626/how-do-you-force-a-cifs-connection-to-unmount  How do you force a CIFS connection to unmount
http://askubuntu.com/questions/7117/which-to-use-nfs-or-samba  Which to use NFS or Samba?









PHP和javascript 的 hex、byte陣列、string轉換

某次使用SlowAES  函數cryptoHelpers.generateSharedKey(8)產生的iv經過base64加密後的結果如下
R1We4y0JRP5w06Z8tUBPAw==

先看 https://code.google.com/p/slowaes/source/browse/trunk/js/cryptoHelpers.js?r=33 的 generateSharedKey 怎麼產生的
generateSharedKey:function(len)
{
 if(len === null)
  len = 16;
 var key = [];
 for(var i = 0; i < len*2; i++)
  key.push(this.getRandom(0,255));
 return key;
}
產生長度為8*2 ,內容為 0-255 的 byte 陣列

使用 cryptoHelpers.js 對他做處理,觀察各個型態的內容
// need include cryptoHelpers.js
var base64 = 'R1We4y0JRP5w06Z8tUBPAw==';
console.log(cryptoHelpers.base64.decode(base64)); // base64 decode => byte array
console.log(cryptoHelpers.convertByteArrayToString(cryptoHelpers.base64.decode(base64))); // to string
console.log(cryptoHelpers.toHex(cryptoHelpers.base64.decode(base64))); // to hex

結果:
cryptoHelpers.base64.decode(base64):[71, 85, 158, 227, 45, 9, 68, 254, 112, 211, 166, 124, 181, 64, 79, 3]
cryptoHelpers.convertByteArrayToString(cryptoHelpers.base64.decode(base64)):GUžã- DþpÓ¦|µ@O
cryptoHelpers.toHex(cryptoHelpers.base64.decode(base64)):47559ee32d0944fe70d3a67cb5404f03

使用PHP處理
$iv = 'R1We4y0JRP5w06Z8tUBPAw==';

echo "
base64_decode(\$iv):".base64_decode($iv); // base64 decode => binary string

$iv64 = base64_decode($iv);
echo "
strlen(\$iv64):".strlen($iv64);  // 長度16
echo "
pack('H*', bin2hex(\$iv64)):".pack('H*', bin2hex($iv64)); // 與base64_decode($iv)結果相同
echo "
bin2hex(\$iv64):".bin2hex($iv64); // to hex

// to bytes array
for ($i=0; $i < strlen($iv64); $i++) { 
    $data[] = ord(substr($iv64,$i,1)); // 使用ord將字元轉成int
}
echo "
\$data:";
print_r($data);
echo "
";

結果:
base64_decode($iv):GU��- D�pӦ|�@O
strlen($iv64):16
pack('H*', bin2hex($iv64)):GU��- D�pӦ|�@O
bin2hex($iv64):47559ee32d0944fe70d3a67cb5404f03
$data:Array
(
    [0] => 71
    [1] => 85
    [2] => 158
    [3] => 227
    [4] => 45
    [5] => 9
    [6] => 68
    [7] => 254
    [8] => 112
    [9] => 211
    [10] => 166
    [11] => 124
    [12] => 181
    [13] => 64
    [14] => 79
    [15] => 3
)

還可以去 Unicode/UTF-8-character table 做字元最後的檢查
http://dev.networkerror.org/utf8/?start=0&end=255&cols=4&search=&show_uni_int=on&show_uni_hex=on&show_html_ent=on&show_raw_hex=on&show_raw_bin=on  0-255 的 Unicode Number (int) / Unicode Number (hex) / Char
http://www.scarfboy.com/coding/unicode-tool?s=000047  以hex 搜尋字元

參考資料:
http://stackoverflow.com/questions/11044802/php-hex-and-binary PHP Hex and Binary






2015年10月20日 星期二

在Windows 7 使用docker

下載安裝 docker-install.exe , 如遇上這個錯誤:
https://github.com/boot2docker/windows-installer/issues/67
則移除VirtualBox 後重裝 VirtualBox 4.3.12 版本,重啟boot2docker-vm

中間為了讓virtualbox 與Win7共用資料夾 ( 失敗 )
http://askubuntu.com/questions/456400/why-cant-i-access-a-shared-folder-from-within-my-virtualbox-machine
必須從 C:\Program Files\Oracle\VirtualBox\VBoxGuestAdditions.iso 安裝 Guest Additions
在開機前先設定掛載的光碟
然後設定共享文件夾
開機進入VM後
1. I had to manually mount the CD:
$ sudo mount /dev/cdrom /media/cdrom
掛載之後 /media/cdrom 下面就有光碟內的東西了
/media/cdrom# ls
32Bit  AUTORUN.INF  cert  runasroot.sh            VBoxSolarisAdditions.pkg        VBoxWindowsAdditions.exe
64Bit  autorun.sh   OS2   VBoxLinuxAdditions.run  VBoxWindowsAdditions-amd64.exe  VBoxWindowsAdditions-x86.exe

2. Install the necessary packages:
$ sudo apt-get install make gcc linux-headers-$(uname -r)
3. Install the Guest Additions:
$ sudo /media/cdrom/VBoxLinuxAdditions.run
中間報錯
...
The headers for the current running kernel were not found. If the following
module compilation fails then this could be the reason.

Building the main Guest Additions module ...done.
Building the shared folder support module ...fail!
(Look at /var/log/vboxadd-install.log to find out what went wrong)
...
Setting up the Window System to use the Guest Additions ...done.
You may need to restart the hal service and the Window System (or just restart
the guest system) to enable the Guest Additions.

Installing graphics libraries and desktop services components ...done.

嘗試mount共享資料夾
$ mkdir new
$ sudo mount -t vboxsf New ~/new
/sbin/mount.vboxsf: mounting failed with the error: No such device

嘗試用其他方法解
http://stackoverflow.com/questions/28328775/virtualbox-mount-vboxsf-mounting-failed-with-the-error-no-such-device
# cd /opt/VBoxGuestAdditions-4.3.12/init/
# ./vboxadd setup
與跑 VBoxLinuxAdditions.run 一樣的錯誤

嘗試解決 Error: kernel headers not found.的問題
http://askubuntu.com/questions/98416/error-kernel-headers-not-found-but-they-are-in-place
# apt-get install xserver-xorg xserver-xorg-core
安裝了以下東西
The following NEW packages will be installed:
  libmtdev1 libxi6 libxinerama1 xserver-xorg xserver-xorg-input-all
  xserver-xorg-input-evdev xserver-xorg-input-mouse
  xserver-xorg-input-synaptics xserver-xorg-input-vmmouse
  xserver-xorg-input-wacom
0 upgraded, 10 newly installed, 0 to remove and 1 not upgraded.

嘗試解決The headers for the current running kernel were not found.
http://linuxconfig.org/ubuntu-the-headers-for-the-current-running-kernel-were-not-found-solution
# apt-get install linux-headers-`uname -r` dkms build-essential
報錯:
...
The following packages have unmet dependencies:
 build-essential : Depends: libc6-dev but it is not going to be installed or
                            libc-dev
                   Depends: g++ (>= 4:4.4.3) but it is not going to be installed
E: Unable to correct problems, you have held broken packages.

原因:
# apt-get install libc6-dev
...
libc6-dev : Depends: libc6 (= 2.19-0ubuntu6) but 2.19-0ubuntu6.5 is to be installed
E: Unable to correct problems, you have held broken packages.
# apt-show-versions libc6
libc6:amd64 2.19-0ubuntu6.5 newer than version in archive
看來是libc6 太新了
切勿移除libc6重裝,剛剛VM就這樣被我弄掛的。還好昨天剛好有備份

嘗試更新Virtual Box到最新版 4.3.30
更新到最新版要執行原本的虛擬電腦時報錯:
unable to load r3 module vboxdd.dll (vboxdd) getlasterror=1790 (verr_unresolved_error)
嘗試過這篇 http://blog.sina.com.cn/s/blog_4dc988240102vj8a.html 但無效
只好把Virtual Box再還原回4.3.12版


PHP 顯示syntax error


如果在寫php時忘記加分號( Semicolon ),訪問下面這個頁面會得到空白頁
ini_set("display_errors", "1");
error_reporting(E_ALL);
$bear = array(1,2,3)
如何顯示錯誤呢?
error_setting.php
ini_set("display_errors", "1");
error_reporting(E_ALL);
include 'error.php';
error.php
$bear = array(1,2,3)
echo "test";
訪問error_setting.php會得到錯誤訊息:
Parse error: syntax error, unexpected 'echo' (T_ECHO) in /var/www/html/test.vm/error.php on line 
但直接訪問error.php仍然會得到空白頁

20160519 找到原因
修改 /etc/php5/fpm/php.ini
error_reporting = E_ALL
error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT #忽略notice和Strict Standards錯誤,但會報Fatal錯誤
syntaxfatal 的error log 與  /etc/php5/fpm/php-fpm.conf 無關,與nginx /etc/nginx/sites-available/test.vm 的 error_log /var/www/html/test.vm/logs/error.log; 有關
這樣畫面上和error log 都有錯誤出現
http://stackoverflow.com/questions/1911920/does-php-error-reporting0-affect-error-logging-or-just-display
結論:看來沒辦法只顯fatal和syntax和notice示錯誤在error log,除非用error_log()或用框架

參考資料:
http://stackoverflow.com/questions/16933606/error-reportinge-all-does-not-produce-error  error_reporting(E_ALL) does not produce error