讓 PHP 支援 PUT FileUpload
由於PHP對於PUT Method所傳送的資料不會做處理,所以對於使用PUT所傳送的資料必須手動作處理,簡單的方式就是去讀取php://input
再做處理
<?php
// do something...
$put_data = file_get_contents('php://input');
// handle put data...
簡單的資料處理起來還OK但是遇到form-data
的時候就困難了,之前在實做RESTful Server的時候就有寫過一隻PHP Function去做處理檔案上傳
但是難免會有 BUG,所以那時候就想看看到底PHP在初始化的時候哪邊是在做FileHandle的相關處理,所以就去查詢相關的文章及Trace PHP Source Code
結果發現在 main/SAPI.c 中的 sapi_activate Function 中有這麼一段Code
/* main/SAPI.c:456 */
if (SG(request_info).content_type && !strcmp(SG(request_info).request_method, "POST"))) {
/* HTTP POST may contain form data to be processed into variables
* depending on given content type */
sapi_read_post_data(TSRMLS_C);
} else {
// ...
}
它用來比較HTTP Request Method是否為POST,然後底下所呼叫的Function sapi_read_post_data 是用來處理檔案上傳的,所以就對它做一些修改
/* main/SAPI.c:456 */
if (SG(request_info).content_type && (!strcmp(SG(request_info).request_method, "POST") || !strcmp(SG(request_info).request_method, "PUT"))) {
/* HTTP POST may contain form data to be processed into variables
* depending on given content type */
sapi_read_post_data(TSRMLS_C);
} else {
// ...
}
既然有了這麼一個Sample然後在看看還有哪些程式中也有相同的處理機制,所以就用 grep 吧
$ grep -rn \"POST\" ./main
然後發現底下的程式中也有相同的判斷,所以就再多做修改,這邊
/* main/php_content_type.c:44 */
if (!strcmp(SG(request_info).request_method, "POST") || !strcmp(SG(request_info).request_method, "PUT")) {
if (NULL == SG(request_info).post_entry) {
/* no post handler registered, so we just swallow the data */
sapi_read_standard_form_data(TSRMLS_C);
}
// ...
}
還有這邊
/* main/php_variables.c:681 */
if (PG(variables_order) &&
(strchr(PG(variables_order),'P') || strchr(PG(variables_order),'p')) &&
!SG(headers_sent) &&
SG(request_info).request_method &&
(!strcasecmp(SG(request_info).request_method, "POST") || !strcasecmp(SG(request_info).request_method, "PUT"))) {
sapi_module.treat_data(PARSE_POST, NULL, NULL TSRMLS_CC);
vars = PG(http_globals)[TRACK_VARS_POST];
} else {
// ...
}
所以一共改了3個地方,都多加了PUT的判斷,接下來就開始編譯了
./configure --prefix=/home/scarwu/php \
--with-config-file-path=/home/scarwu/php/etc \
--with-config-file-scan-dir=/home/scarwu/php/var/db \
--with-pear=/home/scarwu/php/lib/php \
--disable-all \
--enable-dom \
--enable-exif \
--enable-hash \
--enable-json \
--enable-mbregex \
--enable-mbstring \
--enable-phar \
--enable-session \
--enable-short-tags \
--enable-libxml \
--enable-simplexml \
--enable-tokenizer \
--enable-xml \
--enable-xmlreader \
--enable-xmlwriter \
--with-xsl \
--with-xmlrpc \
--with-mhash \
--with-pcre-regex \
--with-zlib=/usr \
--with-curl=/usr \
--with-gettext=/usr \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--enable-pdo \
--with-sqlite3 \
--with-pdo-sqlite \
--enable-fpm
make
make install
測試環境Server部份 Nginx + php-fpm 而Client是自己用 PHP + curl 寫得,至於Server設定就不多說,以下是測試用的PHP Code
<?php
print_r($_SERVER);
echo "\n";
print_r($_POST);
echo "\n";
print_r($_FILES);
試著用PUT傳檔案及Params上去之後,回傳結果為此,PHP已經幫你處理好了,該出現的都出現了
Array
(
...
[REQUEST_METHOD] => PUT
[CONTENT_TYPE] => multipart/form-data; boundary=----------------------------a24f01ed52c0
[CONTENT_LENGTH] => 1446
[SCRIPT_FILENAME] => /var/www/index.php
...
)
Array
(
[json] => {"key":"value"}
)
Array
(
[file] => Array
(
[name] => upload.txt
[type] => application/octet-stream
[tmp_name] => /tmp/phpIXeGvM
[error] => 0
[size] => 1121
)
)
結論
隨然這樣子對PHP做修改可以達到目的,但畢竟這不是一個正確的選擇,會造成維護上的困難,接著就是在此環境開發的Code不一定能在其他環境中使用
畢竟這不是一個標準的PHP,接下來在找時間看如何把這個寫成PHP Extension,還是說提個Issue...