Jun
20
=> How to call a php function in a php extension
详细的说明参见:
[php-5.2.17]
/Zend/zend_execute_API.c +623 & +636 call_user_function_ex
/ext/standard/basic_functions.c +5174 PHP_FUNCTION(call_user_func_array)
/ext/pcre/pcre.c +833 (in function "preg_do_repl_func")
http://man.chinaunix.net/develop/php/php_manual_zh/html/zend.calling-user-functions.html
http://www.phpfreaks.com/forums/index.php?topic=10272.0
详细的说明参见:
[php-5.2.17]
/Zend/zend_execute_API.c +623 & +636 call_user_function_ex
/ext/standard/basic_functions.c +5174 PHP_FUNCTION(call_user_func_array)
/ext/pcre/pcre.c +833 (in function "preg_do_repl_func")
http://man.chinaunix.net/develop/php/php_manual_zh/html/zend.calling-user-functions.html
http://www.phpfreaks.com/forums/index.php?topic=10272.0
PHP_FUNCTION (caller)
{
//call_user_function_ex
zval *function, *str, *arr;
zval **params[10];
MAKE_STD_ZVAL(function);
ZVAL_STRING(function, "var_dump", 1); //I wanna call var_dump
/* //pass a string as its param
MAKE_STD_ZVAL(str);
ZVAL_STRING(str, "Hello, world!", 1);
params[0] = &str;
*/
//pass an array as its param
MAKE_STD_ZVAL(arr);
array_init(arr);
add_assoc_string(arr, "name", "felix021", 1);
params[0] = &arr;
zval *ret;
if (call_user_function_ex(CG(function_table), NULL,
function, &ret, 2, params, 0, NULL TSRMLS_CC) != FAILURE)
{
*return_value = *ret;
zval_copy_ctor(return_value);
zval_ptr_dtor(&ret);
return;
}
else {
RETURN_FALSE;
}
}
{
//call_user_function_ex
zval *function, *str, *arr;
zval **params[10];
MAKE_STD_ZVAL(function);
ZVAL_STRING(function, "var_dump", 1); //I wanna call var_dump
/* //pass a string as its param
MAKE_STD_ZVAL(str);
ZVAL_STRING(str, "Hello, world!", 1);
params[0] = &str;
*/
//pass an array as its param
MAKE_STD_ZVAL(arr);
array_init(arr);
add_assoc_string(arr, "name", "felix021", 1);
params[0] = &arr;
zval *ret;
if (call_user_function_ex(CG(function_table), NULL,
function, &ret, 2, params, 0, NULL TSRMLS_CC) != FAILURE)
{
*return_value = *ret;
zval_copy_ctor(return_value);
zval_ptr_dtor(&ret);
return;
}
else {
RETURN_FALSE;
}
}
Jun
18
网上摘下来的代码,没什么好说的了(因为那一堆 mcrypt_* 函数太乱了,用就是了):
<?php
class DES
{
public static function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
public static function pkcs5_unpad($text) {
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) {
return false;
}
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, -1 * $pad);
}
public static function encrypt($key, $data) {
$size = mcrypt_get_block_size('des', 'ecb');
$data = DES::pkcs5_pad($data, $size);
$td = mcrypt_module_open('des', '', 'ecb', '');
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
@mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $data;
}
public static function decrypt($key, $data) {
$td = mcrypt_module_open('des','','ecb','');
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$ks = mcrypt_enc_get_key_size($td);
@mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$result = DES::pkcs5_unpad($decrypted);
return $result;
}
}
/* test code
$in = "04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw==";
$des = base64_decode($in);
echo DES::decrypt("12345678", $des);
echo "\n";
$in = "cea3e8e1659582206e0be32539729e9f";
$des = DES::encrypt("12345678", $in);
$out = base64_encode($des);
echo $out;
echo "\n";
// */
?>
class DES
{
public static function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
public static function pkcs5_unpad($text) {
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) {
return false;
}
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, -1 * $pad);
}
public static function encrypt($key, $data) {
$size = mcrypt_get_block_size('des', 'ecb');
$data = DES::pkcs5_pad($data, $size);
$td = mcrypt_module_open('des', '', 'ecb', '');
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
@mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $data;
}
public static function decrypt($key, $data) {
$td = mcrypt_module_open('des','','ecb','');
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$ks = mcrypt_enc_get_key_size($td);
@mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$result = DES::pkcs5_unpad($decrypted);
return $result;
}
}
/* test code
$in = "04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw==";
$des = base64_decode($in);
echo DES::decrypt("12345678", $des);
echo "\n";
$in = "cea3e8e1659582206e0be32539729e9f";
$des = DES::encrypt("12345678", $in);
$out = base64_encode($des);
echo $out;
echo "\n";
// */
?>
Jun
18
在"libc 4.6.27 and later, and glibc 2.1 and later"中,提供了 rpc/des_crypt.h这个头文件,其中有几个函数,比如:
这个函数可以用于DES的加密/解密。详情可以看 man des_crypt ,以下说 ecb_crypt() 函数几个比较坑爹的地方:
1. 虽然提供给DES的密钥(key)是8个字节,但是实际上只用到了其中的56个bit,另外8个bit是用于奇偶校验的(从用户处取得一个64位长的密码key ,去除64位密码中作为奇偶校验位的第8、16、24、32、40、48、56、64位,剩下的56位作为有效输入密钥)。所以需要调用 des_setparity(key) 来处理key。
2. 必须在data后面补上1~8个 "\x8",以将datalen补齐到8的倍数。对,是1~8个。假设要加密的data是32个字节,需要先补齐到40个字节。
3. 传给des_setparity()的key和给ecb_crypt的data会被直接改写。
以下是样例代码:
void des_setparity(char *key);
int ecb_crypt(char *key, char *data, unsigned datalen, unsigned mode);
int ecb_crypt(char *key, char *data, unsigned datalen, unsigned mode);
这个函数可以用于DES的加密/解密。详情可以看 man des_crypt ,以下说 ecb_crypt() 函数几个比较坑爹的地方:
1. 虽然提供给DES的密钥(key)是8个字节,但是实际上只用到了其中的56个bit,另外8个bit是用于奇偶校验的(从用户处取得一个64位长的密码key ,去除64位密码中作为奇偶校验位的第8、16、24、32、40、48、56、64位,剩下的56位作为有效输入密钥)。所以需要调用 des_setparity(key) 来处理key。
2. 必须在data后面补上1~8个 "\x8",以将datalen补齐到8的倍数。对,是1~8个。假设要加密的data是32个字节,需要先补齐到40个字节。
3. 传给des_setparity()的key和给ecb_crypt的data会被直接改写。
以下是样例代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <rpc/des_crypt.h>
//注意:这里有个坑,需要保证 data 可以存放的字符串在后面补了\8 不会越界。否则结果是不确定的。
void des_encrypt(const char *key, char *data, int len)
{
char pkey[8];
strncpy(pkey, key, 8);
des_setparity(pkey);
do {
data[len++] = '\x8';
} while (len % 8 != 0);
ecb_crypt(pkey, data, len, DES_ENCRYPT);
}
void des_decrypt(const char *key, char *data, int len)
{
char pkey[8];
strncpy(pkey, key, 8);
des_setparity(pkey);
ecb_crypt(pkey, data, len, DES_DECRYPT);
}
int main(int argc, char *argv[])
{
/*
* char data[4096] = "cea3e8e1659582206e0be32539729e9f";
* des_encrypt("12345678", data, strlen(data));
* printf("%s\n", data);
* //should be "04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw=="
*/
char data[4096];
int i = 0;
while (EOF != (data[i] = fgetc(stdin))) {
i++;
}
data[i] = '\0';
des_decrypt("12345678", data, strlen(data));
printf("%s\n", data);
/*
* echo -n 04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw== | base64 -d | ./des
* should be "cea3e8e1659582206e0be32539729e9f"
*/
return 0;
}
#include <unistd.h>
#include <string.h>
#include <rpc/des_crypt.h>
//注意:这里有个坑,需要保证 data 可以存放的字符串在后面补了\8 不会越界。否则结果是不确定的。
void des_encrypt(const char *key, char *data, int len)
{
char pkey[8];
strncpy(pkey, key, 8);
des_setparity(pkey);
do {
data[len++] = '\x8';
} while (len % 8 != 0);
ecb_crypt(pkey, data, len, DES_ENCRYPT);
}
void des_decrypt(const char *key, char *data, int len)
{
char pkey[8];
strncpy(pkey, key, 8);
des_setparity(pkey);
ecb_crypt(pkey, data, len, DES_DECRYPT);
}
int main(int argc, char *argv[])
{
/*
* char data[4096] = "cea3e8e1659582206e0be32539729e9f";
* des_encrypt("12345678", data, strlen(data));
* printf("%s\n", data);
* //should be "04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw=="
*/
char data[4096];
int i = 0;
while (EOF != (data[i] = fgetc(stdin))) {
i++;
}
data[i] = '\0';
des_decrypt("12345678", data, strlen(data));
printf("%s\n", data);
/*
* echo -n 04fMaWegkH1/BL9CNYxgusFpYK8wdraBX06mPiRmxJP+uVm31GQvyw== | base64 -d | ./des
* should be "cea3e8e1659582206e0be32539729e9f"
*/
return 0;
}
Jun
15
我喜欢在自己的 $HOME 下面建立一个 bin ,然后添加到 $PATH 中,这样我可以方便地执行自己的程序:
$ mkdir ~/bin
$ vi .bashrc
export PATH=$PATH:~/bin
不过坑爹的 crontab 对 .bashrc 并不买账,通过
* * * * * echo $PATH > ~/cronpath.txt
可以看出, crontab 执行命令时没有把环境变量给切过来。
$ cat ~/cronpath.txt
/usr/bin:/bin
$ mkdir ~/bin
$ vi .bashrc
export PATH=$PATH:~/bin
不过坑爹的 crontab 对 .bashrc 并不买账,通过
* * * * * echo $PATH > ~/cronpath.txt
可以看出, crontab 执行命令时没有把环境变量给切过来。
$ cat ~/cronpath.txt
/usr/bin:/bin
Jun
14
#UPDATE: 并不是不能处理反引号,而是不能直接写%,需要转义。
某脚本x.sh,接受一个参数,比如20110517,用于处理20110517的日志。
处理前一天的日志,非常理所当然地使用如下命令
由于是每天早上8点30分执行,所以很理所当然地:
$ crontab -e
30 8 * * * PATH_TO_SCRIPT/x.sh `date -d "-1 day" +%Y%m%d`
但是过了几天发现脚本根本就没有被处理(在某些机器上,cron fail时会将邮件发送给用户"You have new mail in /var/spool/mail/USERNAME")。
原因是坑爹的cron没法正确处理反引号。
解决办法是,额外写个脚本将这句命令包装,在crontab中执行包装脚本 在%前面都加上反斜杠。
某脚本x.sh,接受一个参数,比如20110517,用于处理20110517的日志。
处理前一天的日志,非常理所当然地使用如下命令
PATH_TO_SCRIPT/x.sh `date -d "-1 day" +%Y%m%d`
由于是每天早上8点30分执行,所以很理所当然地:
$ crontab -e
30 8 * * * PATH_TO_SCRIPT/x.sh `date -d "-1 day" +%Y%m%d`
但是过了几天发现脚本根本就没有被处理(在某些机器上,cron fail时会将邮件发送给用户"You have new mail in /var/spool/mail/USERNAME")。
解决办法是,
May
27
前些天写的程序在上线的时候出现BUG了,错误提示就是call_user_func_array无法调用指定的函数。
代码大概是这样的
追了一下发现是PHP版本的问题,开发环境使用的是php 5.2.17,而线上环境是旧版本的5.1.6。5.1版的php的call_user_func / call_user_func_array函数没法调用类的静态函数,所以出现了这个问题。
没有很好的解决方法,大致就是
1. 升级PHP(最后我们是这么做的)
2. 使用字符串拼出一段php代码,然后用eval来执行。很挫,但是也能用。
代码大概是这样的
class foo {
public static function bar($arg1, $arg2) {
//do sth.
}
}
call_user_func_array("foo:bar", array(1, 2));
public static function bar($arg1, $arg2) {
//do sth.
}
}
call_user_func_array("foo:bar", array(1, 2));
追了一下发现是PHP版本的问题,开发环境使用的是php 5.2.17,而线上环境是旧版本的5.1.6。5.1版的php的call_user_func / call_user_func_array函数没法调用类的静态函数,所以出现了这个问题。
没有很好的解决方法,大致就是
1. 升级PHP(最后我们是这么做的)
2. 使用字符串拼出一段php代码,然后用eval来执行。很挫,但是也能用。
May
21
支持到 万亿,精确到角分。
<?php
$_NUMS = array('零','壹','贰','叁','肆','伍','陆','柒','捌','玖');
function four_num($num)
{
global $_NUMS;
$unit = array("", "拾", "佰", "仟");
$str = '';
$i = 0;
if ($num == 0)
return '零';
$arr_str = array();
while ($num > 0) {
$x = $num % 10;
if ($x == 0)
$arr_str[] = $_NUMS[0];
else
$arr_str[] = $_NUMS[$x] . $unit[$i];
$num = floor($num / 10);
$i++;
}
$arr_str = array_reverse($arr_str);
$str = join("", $arr_str);
$str = preg_replace('/(零)+/', '零', $str);
$str = preg_replace('/零$/', '', $str);
return $str;
}
function rmb($amount)
{
//去除开头的空格、正负号、前导0、小数点、符号
$amount = preg_replace('/(^\s*(\+|-)?0*)|\.|¥/', '', $amount);
if ($amount == '')
return '零元整';
if (strlen($amount) == 1) $amount = '0' . $amount;
$int_part = substr($amount, 0, '-2');
$float_part = substr($amount, -2);
$jiao = intval($float_part{0});
$fen = intval($float_part{1});
$unit = array('', '万', '亿', '万');
while (strlen($int_part) % 4 != 0) $int_part = '0' . $int_part;
$groups = str_split($int_part, 4);
$groups = array_reverse($groups);
//print_r($groups);
$arr_str = array();
for ($i = 0; $i < count($groups); $i++) {
$arr_str[] = four_num($groups[$i]) . $unit[$i];
if ($groups[$i] < 1000)
$arr_str[] = '零';
}
$arr_str = array_reverse($arr_str);
//print_r($arr_str);
$ret = join('', $arr_str);
$ret = preg_replace('/^(零)+/', '', $ret);
$ret = preg_replace('/(零)+/', '零', $ret);
$ret = preg_replace('/零万/', '', $ret);
$ret = preg_replace('/零亿/', '亿', $ret);
$ret = preg_replace('/(零)$/', '', $ret);
if ($ret !== '') $ret .= '元';
if ($float_part == 0) {
$ret .= '整';
}
else {
global $_NUMS;
if ($ret !== '' && ($groups[0] % 10 == 0 || $jiao == 0)) {
$ret .= '零';
}
if ($jiao != 0) $ret .= $_NUMS[$jiao] . '角';
if ($fen != 0) $ret .= $_NUMS[$fen] . '分';
}
return $ret;
}
//TEST CASE
case_test("壹仟肆佰零玖元伍角", rmb('1409.50'));
case_test("陆仟零柒元壹角肆分", rmb('6007.14'));
case_test("壹仟陆佰捌拾元零叁角贰分", rmb('1680.32'));
case_test("壹拾万柒仟元零伍角叁分", rmb('107000.53'));
case_test("壹万陆仟肆佰零玖元零贰分", rmb('16409.02'));
case_test("叁佰贰拾伍元零肆分", rmb('325.04'));
case_test("壹亿壹仟元零叁角", rmb('10000100030'));
function case_test($stdans, $ans)
{
if ($stdans == $ans)
echo "TEST OK\n";
else
echo "$stdans <==> $ans\n";
}
?>
$_NUMS = array('零','壹','贰','叁','肆','伍','陆','柒','捌','玖');
function four_num($num)
{
global $_NUMS;
$unit = array("", "拾", "佰", "仟");
$str = '';
$i = 0;
if ($num == 0)
return '零';
$arr_str = array();
while ($num > 0) {
$x = $num % 10;
if ($x == 0)
$arr_str[] = $_NUMS[0];
else
$arr_str[] = $_NUMS[$x] . $unit[$i];
$num = floor($num / 10);
$i++;
}
$arr_str = array_reverse($arr_str);
$str = join("", $arr_str);
$str = preg_replace('/(零)+/', '零', $str);
$str = preg_replace('/零$/', '', $str);
return $str;
}
function rmb($amount)
{
//去除开头的空格、正负号、前导0、小数点、符号
$amount = preg_replace('/(^\s*(\+|-)?0*)|\.|¥/', '', $amount);
if ($amount == '')
return '零元整';
if (strlen($amount) == 1) $amount = '0' . $amount;
$int_part = substr($amount, 0, '-2');
$float_part = substr($amount, -2);
$jiao = intval($float_part{0});
$fen = intval($float_part{1});
$unit = array('', '万', '亿', '万');
while (strlen($int_part) % 4 != 0) $int_part = '0' . $int_part;
$groups = str_split($int_part, 4);
$groups = array_reverse($groups);
//print_r($groups);
$arr_str = array();
for ($i = 0; $i < count($groups); $i++) {
$arr_str[] = four_num($groups[$i]) . $unit[$i];
if ($groups[$i] < 1000)
$arr_str[] = '零';
}
$arr_str = array_reverse($arr_str);
//print_r($arr_str);
$ret = join('', $arr_str);
$ret = preg_replace('/^(零)+/', '', $ret);
$ret = preg_replace('/(零)+/', '零', $ret);
$ret = preg_replace('/零万/', '', $ret);
$ret = preg_replace('/零亿/', '亿', $ret);
$ret = preg_replace('/(零)$/', '', $ret);
if ($ret !== '') $ret .= '元';
if ($float_part == 0) {
$ret .= '整';
}
else {
global $_NUMS;
if ($ret !== '' && ($groups[0] % 10 == 0 || $jiao == 0)) {
$ret .= '零';
}
if ($jiao != 0) $ret .= $_NUMS[$jiao] . '角';
if ($fen != 0) $ret .= $_NUMS[$fen] . '分';
}
return $ret;
}
//TEST CASE
case_test("壹仟肆佰零玖元伍角", rmb('1409.50'));
case_test("陆仟零柒元壹角肆分", rmb('6007.14'));
case_test("壹仟陆佰捌拾元零叁角贰分", rmb('1680.32'));
case_test("壹拾万柒仟元零伍角叁分", rmb('107000.53'));
case_test("壹万陆仟肆佰零玖元零贰分", rmb('16409.02'));
case_test("叁佰贰拾伍元零肆分", rmb('325.04'));
case_test("壹亿壹仟元零叁角", rmb('10000100030'));
function case_test($stdans, $ans)
{
if ($stdans == $ans)
echo "TEST OK\n";
else
echo "$stdans <==> $ans\n";
}
?>