Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

Tuesday, August 31, 2010

Problems with tracing location in callback-ed functions/methods

Problems with tracing location in callback-ed functions/methods

Exceptions thown in a function/method been called via 'call_user_func' or 'call_user_func_array', have key 'file' and 'line' disappeared in their first element (array[0]) from Exception::getTrace().
getTrace());
}

function callback() {
 call_user_func('test');
}

try {
 callback();
} catch (Exception $e) {
 var_dump($e->getTrace());
}
?>

G:\wwwroot\Test\Exception>php trace_func.php
#0  test() called at [G:\wwwroot\Test\Exception\trace_func.php:8]
array(1) {
  [0]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(8)
    ["function"]=>
    string(4) "test"
    ["args"]=>
    array(0) {
    }
  }
}
#0  test()
#1  call_user_func(test) called at [G:\wwwroot\Test\Exception\trace_func.php:14]
#2  callback() called at [G:\wwwroot\Test\Exception\trace_func.php:18]
array(3) {
  [0]=>
  array(2) {
    ["function"]=>
    string(4) "test"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(14)
    ["function"]=>
    string(14) "call_user_func"
    ["args"]=>
    array(1) {
      [0]=>
      string(4) "test"
    }
  }
  [2]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(18)
    ["function"]=>
    string(8) "callback"
    ["args"]=>
    array(0) {
    }
  }
}
Similar result given by debug_print_backtrace(), is this a bug or an unexpected feature ? XD

Wednesday, August 25, 2010

foreach 赋值引用 + refcount 引发的一个问题

第一个问题,封装了 mysqli_stmt 的bind_param方法,为了实现 $stmt->execute(array(typelist, var1, var2, ...)); 的操作。
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }

这个操作的结果显然是错误的。于是有了个下边的测试:
<?php
$array = array(
1,2,3,4,5
);

$to0 = array();
foreach ($array as $k=>$v) {
 $to0[$k] = & $v;
}
var_dump($to0);


$to1 = array();
foreach ($array as $k=>$v) {
 $to1[$k] = & $v;
 unset($v);
}
var_dump($to1);


$to2 = array();
foreach ($array as $k=>&$v) {
 $to2[$k] = & $v;
}
var_dump($to2);

/*
Output:

G:\wwwroot\Test>php reference3.php
array(5) {
  [0]=>
  &int(5)
  [1]=>
  &int(5)
  [2]=>
  &int(5)
  [3]=>
  &int(5)
  [4]=>
  &int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}

*/


第一个问题结束
---------------------------------------------------------

第二个问题,
最上边代码后来的版本是这样的:
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>&$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }
  unset($info);

  $stmt = $db->prepare($query);
  $ret = $stmt->execute($values);

结果传递多于一个字段时,execute报错。
假设foreach 循环结束后,$values 内容是:
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}
而在execute()的时候,却变成了
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  &int(5)
}

除了最后一个元素外,其他的引用都消失了。。。
在几个点放上debug_zval_dump,一看便明白了。

foreach 循环结束的时候,$values 数组涉及到引用的元素,除了最后一个 refcount 为3,其余均为2. 因为对于其他的元素,$info 和 $values 两个数组的元素都对目标进行了引用,所以refcount为2,而最后一个,因为 foreach 的临时变量 $v 还对这个有引用,所以 refcount 是3.
当执行了 unset($info) 的操作后,所有的refcount均 -1,于是变成了 11112。
这个时候var_dump,自然会发现只有最后一个元素还处于引用的状态。

Monday, March 29, 2010

Tests about using tokyotyrant with pecl memcached.

CentOS: 5.4
PHP: 5.2.13
Apache: 2.2.14
TokyoTyrant: 1.1.39

libmemcached pecl memcached result
0.37 1.0.0 works perfectly.

0.37 1.0.1 works perfectly on cli mode
zend_mm_heap corrupted in apache log(non-debug php compilation)
apache SegFaults (php --enable-debug)

0.37 1.0.2(?) works perfect.
======================================================================

0.38 1.0.0 Cannot disconnect after script execution

0.38 1.0.1 Cannot disconnect after script execution

0.38 1.0.2(?) Cannot disconnect after script execution


1.0.2(?) is the github version from http://github.com/tricky/php-memcached by Teddy(commit 078544ae226313dbdf56775bc004136c7e6b6e28).

Friday, March 12, 2010

php 处理 <? 的错误。

刚才 @iammutex 同学写 prepare 方法的时候,把一个数组dump出来,结果发现var_dump输出被截断。
大致是 array('uid=?', 'fid<?'); 的

写了个测试代码
<?php
$array = array('1', '2', '3<? 1+1', '4>111?');
var_dump($array);


猜猜输出是啥

cli 模式下:

0> /data4/space/bin/php test.php
array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3<? 1+1"
[3]=>
string(6) "4>111?"
}


嗯,没有问题。

但是。。。 Apache + php ...

array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3111?"
}



LOL ...

PS: 测试环境 php5.3.1 + apache 2.2.14
看到这个的人来验证一下其他的版本或者其他的httpd吧。

pecl memcached 处理默认参数就是一悲剧

这错误也怪我,之前在这个扩展的不同的地方栽了一下,后来还没注意。
代码贴在了 pastebin.com 。。。 可怜的 pastebin.ca,也在墙外了。。。

虽然贴了代码,还是有必要简要提一下,要不然哪天pastebin.com也在墙外了。
<?php
class spdLibMemcached {
private $mc;
public function __construct($persistent_id=null) {
$this->mc = new Memcached($persistent_id);
}
public function addServer($host, $port, $weight=null) {
return $this->mc->addServer($host, $port, $weight);
}
public function getServerList() {
return $this->mc->getServerList();
}
}


$a = new memcached;
$b = new memcached;
$a->addServer('10.1.0.55', 22201);
$b->addServer('10.1.3.52', 11211);

$c = new spdlibmemcached;
$d = new spdlibmemcached;
$c->addServer('10.1.0.55', 22201);
$d->addServer('10.1.3.52', 11211);

var_dump(
$a->getServerList(),
$b->getServerList(),
$c->getServerList(),
$d->getServerList()
);

你会发现 $c 和 $d 取得的结果是 。。。 两个值。。。。

刚才还去麻烦小天天看代码,也是没看出问题。

最后索性继续精简代码,写了个最简单的例子,居然是好的。一细看:
new Memcached; 和 new Memcached(null);
根本不是一回事。。。

于是构造的时候必须

<?php
class spdLibMemcached {
private $mc;
public function __construct($persistent_id=null) {
if ($persistent_id === null) {
$this->mc = new Memcached;
} else {
$this->mc = new Memcached($persistent_id);
}
}
}



继续改其他的去。。。

Sunday, February 21, 2010

使用 php_memcached 连 memcacheq 遇到的诡异问题

使用 php_memcached 连 memcacheq 遇到的诡异问题


服务器环境:
Centos 5.4 http://centos.org/
PHP 5.3.1 http://www.php.net/
php_memcached 1.0.0 http://pecl.php.net/package/memcached
libmemcached 0.37 http://tangent.org/552/libmemcached.html
libevent 1.4.13 http://www.monkey.org/~provos/libevent/
Memcacheq 0.2.0 http://memcachedb.org/memcacheq/

给 php_memcached 做了一次封装。其中有个方法是这样写的

<?php

/**
* Memcached Class
*
*/
class spAMLMemcached
{
private $mc;

......
/**
* get方法,增加批量模式
*
* @param string|array $key
* @param callback $cache_cb 批量模式不可用
* @param float|array $cas_token
* @param int $flags
* @return mixed
*/
public function get($key, $cache_cb=null, &$cas_token=null, $flags=null)
{
if (is_array($key)) {
return $this->mc->getMulti($key, $cas_token, $flags);
} else {
return $this->mc->get($key, $cache_cb, $cas_token);
}
}
......
}


方法看起来貌似没问题。
关于 Memcached::get 的说明:link to php manual;

Memcached::get

(PECL memcached >= 0.1.0)

Memcached::get — Retrieve an item

Description

public mixed Memcached::get ( string $key [, callback $cache_cb [, float &$cas_token ]] )
Memcached::get() returns the item that was previously stored under the key . If the item is found and cas_token variable is provided, it will contain the CAS token value for the item. See Memcached::cas for how to use CAS tokens. Read-through caching callback may be specified via cache_cb parameter.

Parameters

key
The key of the item to retrieve.

cache_cb
Read-through caching callback or NULL.

cas_token
The variable to store the CAS token in.

Return Values

Returns the value stored in the cache or FALSE otherwise. The Memcached::getResultCode will return Memcached::RES_NOTFOUND if the key does not exist.



从这里看也没啥问题。
但是,实际调用的时候,用了这样的代码。
<?php
$a = new spAMLMemcached;
$a->addServer('10.**.*.**', 22201);
var_dump($a->get('example'), $a->getResultCode());
var_dump($a->get('example', null), $a->getResultCode());


结果很遗憾,两个返回的都是false。
getResultCode 出来的结果是 8 即 Memcached::RES_PROTOCOL_ERROR 协议错误。

无法理解。
以前也没遇到过这个问题啊。
tcpdump 抓包看了看
tcpdump -XXAfN -ttt -i eth3  "ip dst 10.**.*.**"

包的数据居然是 。。。
..gets.example..


gets ...

如果仅仅是对 memcached 而言,这个没问题。高版本的memcached 都支持这个。
但是。。。用的是 Memcacheq
根本就不支持这个。
于是协议错误。。。


好吧,查原因。

最后写了个简单的测试代码。
<?php
$s = new Memcached;
$s->addServer('10.**.*.**', 22201);
var_dump($s->get('example'), $s->getResultCode());
var_dump($s->get('example', null), $s->getResultCode());

输出一切正常。
于是不由得怀疑起封装时对 &$cas_token=null 的处理。

做了如下的测试代码。

<?php
$q = new Memcached;
$q->addServer('10.**.*.**', 22201);
var_dump($q->get('example'), $q->getResultCode());
var_dump($q->get('example', null), $q->getResultCode());


echo "\nmcdgettest: \n";
var_dump(mcdgettest('example'), $q->getResultCode());
var_dump(mcdgettest('example', null), $q->getResultCode());

echo "\nmcdgettest1: \n";
var_dump(mcdgettest1('example'), $q->getResultCode());

echo "\nmcdgettest2: \n";
var_dump(mcdgettest2('example'), $q->getResultCode());

echo "\nmcdgettest3: \n";
var_dump(mcdgettest3('example'), $q->getResultCode());

echo "END\n\n";
unset($q);

$q = new Memcached;
$q->addServer('10.**.*.**', 22201);
var_dump($q->get('example'), $q->getResultCode());
var_dump($q->get('example', null), $q->getResultCode());

echo "\nmcdgettest3: \n";
var_dump(mcdgettest3('example'), $q->getResultCode());

echo "\nmcdgettest2: \n";
var_dump(mcdgettest2('example'), $q->getResultCode());

echo "\nmcdgettest1: \n";
var_dump(mcdgettest1('example'), $q->getResultCode());

echo "\nmcdgettest: \n";
var_dump(mcdgettest('example'), $q->getResultCode());
var_dump(mcdgettest('example', null), $q->getResultCode());

echo "END\n\n";
unset($q);

$q = new Memcached;
$q->addServer('10.**.*.**', 22201);
var_dump($q->get('example'), $q->getResultCode());
var_dump($q->get('example', null), $q->getResultCode());

echo "\nmcdgettest3: \n";
var_dump(mcdgettest3('example'), $q->getResultCode());

echo "\nmcdgettest1: \n";
var_dump(mcdgettest1('example'), $q->getResultCode());

echo "\nmcdgettest2: \n";
var_dump(mcdgettest2('example'), $q->getResultCode());

echo "\nmcdgettest: \n";
var_dump(mcdgettest('example'), $q->getResultCode());
var_dump(mcdgettest('example', null), $q->getResultCode());

echo "END\n\n";
unset($q);



function mcdgettest($key, $cache_cb=null, &$cas_token=null) {
global $q;
return $q->get($key, $cache_cb, $cas_token);
}

function mcdgettest1($key, $cache_cb=null) {
global $q;
return $q->get($key, $cache_cb);
}

function mcdgettest2($key, &$cas_token=null) {
global $q;
return $q->get($key, null, $cas_token);
}

function mcdgettest3($key) {
global $q;
return $q->get($key);
}



结果如下:

[root@localhost 3.53]: /data1/www/htdocs/i.sina.com.cn/source/apps/daemon/cron
0> php testMemcachedGet.php
int(140)
int(0)
int(141)
int(0)

mcdgettest:
bool(false)
int(8)
bool(false)
int(8)

mcdgettest1:
bool(false)
int(8)

mcdgettest2:
bool(false)
int(8)

mcdgettest3:
bool(false)
int(8)
END

int(142)
int(0)
int(143)
int(0)

mcdgettest3:
int(144)
int(0)

mcdgettest2:
bool(false)
int(8)

mcdgettest1:
bool(false)
int(8)

mcdgettest:
bool(false)
int(8)
bool(false)
int(8)
END

int(145)
int(0)
int(146)
int(0)

mcdgettest3:
int(147)
int(0)

mcdgettest1:
int(148)
int(0)

mcdgettest2:
bool(false)
int(8)

mcdgettest:
bool(false)
int(8)
bool(false)
int(8)
END


另外一边抓包

[root@localhost 3.53]: /data1/www/htdocs/i.sina.com.cn/source/datadrv/memcacheq
130> tcpdump -XXAfN -ttt -i eth3 "ip dst 10.**.*.**" | grep get
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth3, link-type EN10MB (Ethernet), capture size 96 bytes
0x0040: b19b 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b19c 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b19c 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b19d 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b19d 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b19e 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b19e 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b19f 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b19f 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b19f 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b1a0 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b1a0 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b1a1 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b1a1 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b1a2 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b1a2 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b1a2 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b1a2 6765 7420 6578 616d 706c 6520 0d0a ..get.example...
0x0040: b1a3 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
0x0040: b1a3 6765 7473 2065 7861 6d70 6c65 200d ..gets.example..
81 packets captured
82 packets received by filter
0 packets dropped by kernel


对着一行行看吧。

第一个结论很明显:对于每组测试,只要有一次进入 gets 那后边全都是 gets。
第二个结论就得一点点比较分析了,结论是:只要对 $cas_token 这个引用传值的参数进行了封装,后续的结果就会出错。

为了验证这个错误会不会对最基本的调用产生影响,我在 第二组测试的 321 的 2后边 直接 调用了 $q->get('example') 很遗憾,又是一个 protocol error.

而且,不知道大家注意了没有,从测试脚本输出的最后的false有三个,而抓包只有两个gets。我试着不在结束的时候ctrl+c,然后对跑了两次测试,终于最后位置有 三个 gets。


从源码分析。
首先看 libmemcached。因为 gets 的操作命令来自这里。
get.c 中:Line200 memcached_mget_by_key_real() 中

if (ptr->flags.support_cas)
{
get_command= "gets ";
get_command_length= 5;
}

这是唯一的有 gets 的地方。
于是下一步查哪儿修改了 ptr->flags.support_cas 的这个标志位。
定位到 behavior.c line 81 memcached_behavior_set() 函数内

case MEMCACHED_BEHAVIOR_SUPPORT_CAS:
ptr->flags.support_cas= set_flag(data);
break;


从 php_memcached 的 php_memcached.c 中 找到几次 MEMCACHED_BEHAVIOR_SUPPORT_CAS 的设置。
分别在

static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)

第一个是 Memcached::get 方法所在的位置。



if (cas_token) {

uint64_t orig_cas_flag;

/*
* Enable CAS support, but only if it is currently disabled.
*/
orig_cas_flag = memcached_behavior_get(i_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS);
if (orig_cas_flag == 0) {
memcached_behavior_set(i_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1);
}
......

/*
* Restore the CAS support flag, but only if we had to turn it on.
*/
if (orig_cas_flag == 0) {
memcached_behavior_set(i_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag);
}
return;

} else {
......

如果 $cas_token 这个变量传入了 就会进到这个if判断里。首先会强制开启 MEMCACHED_BEHAVIOR_SUPPORT_CAS 这个选项,当操作完整结束的时候,再将这个选项恢复。
这个的唯一的问题在于,Line 395

if (php_memc_handle_error(status TSRMLS_CC) < 0) {
memcached_result_free(&result);
RETURN_FALSE;
}

出错退出的时候,直接 free result 然后 return false,并未将 这个选项恢复。
等到下一次取这个对象的时候,因为这次已经设为真,即便下一次没走cas_token 的这个值,但是调用 memcached_mget_by_key 的时候,原始这个选项还是会污染新的操作,因为这一次不涉及到恢复这个选项的问题。
这就能解释 为什么 按 3 2 1 的顺序执行的代码 1 会被污染成protocol error.
解决方案是在
php_memcached.c line 395 :


if (php_memc_handle_error(status TSRMLS_CC) < 0) {
memcached_result_free(&result);
RETURN_FALSE;
}


RETURN_FALSE 前 做一次恢复。



if (php_memc_handle_error(status TSRMLS_CC) < 0) {
memcached_result_free(&result);

/*
* Restore the CAS support flag, but only if we had to turn it on.
*/
if (orig_cas_flag == 0) {
memcached_behavior_set(i_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag);
}

RETURN_FALSE;
}

Saturday, January 23, 2010

顺手把新版本的 eAccelerator 编译了

上次编译大概是两周前吧,写makefile成功后的第二次编译,但是当时svn拿到的代码 php5.3.1 下编译有错,具体记不得了。
刚才svn update了下,改动不少。
重新export一份到 php-5.3.1\ext\eAccelerator 下,makefile.txt 依旧在 win32下。
Visual Studio 2008 Command Prompt 进到目录,直接 nmake -f makefile.txt 轻松成功。把当前目录生成的dll copy到 php53\ext 下,php -v php -m 都成功了。
顺手把 php 5.2 的源码目录和二进制目录都更新了。5.2.12。把eAccelerator 的目录也copy到了相应目录下。

5.3 和 5.2 的扩展编译的第一个区别是 buildconf + configure.

5.3 下上次编译的时候,直接解压的源码包,进到ext目录,nmake -f makefile.txt 貌似报错是 config.h 不存在还是什么的。回到 php 源码根目录,buildconf.bat && cscript configure.js 再去编译就好了。
当然需要把那个 php5ts.lib copy到 eAccelerator的目录里。

5.2 这次编译,我第一下也去 buildconf + configure 了,结果发现还需要把win32build 那个环境搭起来。印象中以前编译那些乱七八糟的扩展基本都没这么折腾。直接尝试去 nmake,一切顺利。不提示缺那个文件。
PS: VS2008下提示
        cl.exe /nologo ../optimize.c /Fo"optimize.o" /O1 /c /I ../../../ /I ../../../Zend /I ../../../TSRM /I ../../../main /I ../../ /D NDEBUG /D ZEND_DEBUG=0 /D WIN32 /D _WINDOWS /D _USRDLL /D HAVE_EACCELERATOR /D COMPILE_DL_EACCELERATOR /D ZEND_WIN32 /D PHP_WIN32 /D ZTS=1 /D HAVE_EXT_SESSION_PHP_
SESSION_H=1 /D WITH_EACCELERATOR_CRASH_DETECTION /D WITH_EACCELERATOR_OPTIMIZER /D WITH_EACCELERATOR_SESSIONS /D WITH_EACCELERATOR_CONTENT_CACHING /D WITH_EACCELERATOR_SHM /D WITH_EACCELERATOR_INFO
optimize.c
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/stat.inl(44) : error C2466: cannot allocate an array of constant size 0
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/stat.inl(49) : error C2466: cannot allocate an array of constant size 0
g:\build\phpbuild\vc9\x86\php-5.2.12\tsrm\readdir.h(10) : warning C4005: '_WIN32_WINNT' : macro redefinition
C:\Program Files\Microsoft SDKs\Windows\v6.1\include\sdkddkver.h(186) : see previous definition of '_WIN32_WINNT'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/utime.inl(39) : error C2466: cannot allocate an array of constant size 0
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/utime.inl(44) : error C2466: cannot allocate an array of constant size 0
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/utime.inl(49) : error C2466: cannot allocate an array of constant size 0
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\sys/utime.inl(78) : error C2466: cannot allocate an array of constant size 0
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\wtime.inl(37) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\wtime.inl(43) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(38) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(44) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(51) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(57) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(64) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(69) : warning C4133: 'function' : incompatible types - from 'const time_t *' to 'const __time32_t *'
F:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\time.inl(81) : warning C4133: 'function' : incompatible types - from 'time_t *' to '__time32_t *'
NMAKE : fatal error U1077: '"F:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe"' : return code '0x2'


直接进到 源码的
\main\config.w32.h
注释掉
#define _USE_32BIT_TIME_T 1




不过,php 5.2 编译的时候,需要单独编译 eAccelerator 目录的 fnmatch.c 否则找不到符号
cl.exe /nologo php5ts.lib odbc32.lib odbccp32.lib /link /out:"eAccelerator.dll" optimize.o opcodes.o mm.o eaccelerator.o ea_store.o ea_dasm.o debug.o ea_info.o ea_restore.o /dll
Creating library eAccelerator.lib and object eAccelerator.exp
eaccelerator.o : error LNK2019: unresolved external symbol _fnmatch referenced in function _ea_match
eAccelerator.dll : fatal error LNK1120: 1 unresolved externals
NMAKE : fatal error U1077: '"F:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe"' : return code '0x2'
Stop.
php 5.3.1 的 fnmatch 直接在 php的源码包 \win32 下有了。

Saturday, October 3, 2009

Seg fault when calling php memcache::addServer

addServer('localhost', 11211);
var_dump($mc_0->get('ss'));
var_dump($mc_0->set('ss', 1, NULL, 100));
var_dump($mc_0->get('ss'));

$mc_1 = new Memcache;
$mc_1->addServer('localhost', 11211, NULL);
var_dump($mc_1->get('ss'));
var_dump($mc_1->set('ss', 2, NULL, 100));
var_dump($mc_1->get('ss'));


$mc_2 = new Memcache;
$mc_2->addServer('localhost', 11211, NULL, 1); 
var_dump($mc_2->get('ss'));
var_dump($mc_2->set('ss', 3, NULL, 100));
var_dump($mc_2->get('ss'));

$mc_4 = new Memcache;
$mc_4->addServer('localhost', 11211, NULL, 1, NULL);
var_dump($mc_4->get('ss'));
var_dump($mc_4->set('ss', 4, NULL, 100));
var_dump($mc_4->get('ss'));

$mc_5 = new Memcache;
$mc_5->addServer('localhost', 11211, NULL, 1, NULL, NULL);
var_dump($mc_5->get('ss'));
var_dump($mc_5->set('ss', 5, NULL, 100));
var_dump($mc_5->get('ss'));

$mc_6 = new Memcache;
$mc_6->addServer('localhost', 11211, NULL, 1, NULL, NULL, NULL);
var_dump($mc_6->get('ss'));
var_dump($mc_6->set('ss', 6, NULL, 100));
var_dump($mc_6->get('ss'));

$mc_7 = new Memcache;
$mc_7->addServer('localhost', 11211, NULL, 1, NULL, NULL, NULL, NULL);
var_dump($mc_7->get('ss'));
var_dump($mc_7->set('ss', 7, NULL, 100));
var_dump($mc_7->get('ss'));




Guess whatz the result :?

sskaje@newborn:~/test$ php test3.php 
string(1) "7"
bool(true)
string(1) "1"
string(1) "1"
bool(true)
string(1) "2"
string(1) "2"
bool(true)
string(1) "3"
string(1) "3"
bool(true)
string(1) "4"
string(1) "4"
bool(true)
string(1) "5"
string(1) "5"
bool(true)
string(1) "6"
string(1) "6"
bool(true)
string(1) "7"
Segmentation fault
sskaje@newborn:~/test$ 

Noticed ? Thatz a seg fault at the very end. Now look above to see where error occurs.
Hmm...
addServer('localhost', 11211, NULL, 1, NULL, NULL, NULL, NULL);

Yup, this is the line where that seg fault occurs.
Compare this line with the $mc_2 lines. Yes, you saw the difference is the forth argument when calling addServer. The $mc_2 uses 1 and $mc_7 uses NULL.

I'll check the source out to find what exactly causes this error when I'm really that free.

Tuesday, August 25, 2009

SinaMySQLClass 增加 mysqli 支持

重新封装了一个mysqli的操作类,只是为了希望从 php_mysql 到 php_mysqli 切换的时候,能够不影响前端代码的使用。
结果改来改去最终还是决定将两组类分开写,毕竟 mysqli 的很多方法没法在 php_mysql 上找到,不愿意为旧的应用放弃新的特性,例如直接可用的 autocommit/commit/rollback 等。
但是作为直接调用 sinamysql.class.php 或 sinamysql.multiservices.class.php 的代码,不会受到影响,毕竟这一层代码只是适应底层的改动稍作了修正,没有太多其他的东西。

不过,其实是不是可以考虑上边放一个专门的文件,给外部代码调用,这样就不用改include/require了

http://code.google.com/p/sinamysqlclass/source/checkout
代码依旧很乱
目的只是为了支持多组主从服务器对,不希望看到某代码几乎每执行一次查询就重建一个连接的2行为。

Tuesday, August 18, 2009

【对比上篇】MySQLi 对于相同用户名密码连接不同库的结果

两个版本的代码,一个po,一个oo
都是连到同一个服务器,相同的用户密码,只是不同的库

代码1
<?php
$db_1 = mysqli_connect('localhost', 'root', '31343540');
mysqli_select_db($db_1, 'ftpunion');
$db_2 = mysqli_connect('localhost', 'root', '31343540');
mysqli_select_db($db_2, 'ftpunion_new');


var_dump(mysqli_fetch_assoc(mysqli_query($db_1, 'select database();')));


var_dump(mysqli_fetch_assoc(mysqli_query($db_2, 'select database();')));

#mysqli_select_db('ftpunion', $db_1);
var_dump(mysqli_fetch_assoc(mysqli_query($db_1, 'select database();')));


第二段代码
<?php
$db_1 = new mysqli('localhost', 'root', '31343540');
$db_1->select_db('ftpunion');

var_dump($db_1->query('select database();')->fetch_assoc());

$db_2 = new mysqli('localhost', 'root', '31343540');
$db_2->select_db('ftpunion_new');


var_dump($db_2->query('select database();')->fetch_assoc());

#$db_1->select_db('ftpunion');
var_dump($db_1->query('select database();')->fetch_assoc());


两个输出

G:\wwwroot\Test\mysql>php mysqli_select_db.php
array(1) {
["database()"]=>
string(8) "ftpunion"
}
array(1) {
["database()"]=>
string(12) "ftpunion_new"
}
array(1) {
["database()"]=>
string(8) "ftpunion"
}

G:\wwwroot\Test\mysql>php mysqli_select_db_oo.php
array(1) {
["database()"]=>
string(8) "ftpunion"
}
array(1) {
["database()"]=>
string(12) "ftpunion_new"
}
array(1) {
["database()"]=>
string(8) "ftpunion"
}


两次sleep的时候,netstat的结果

G:\Projects\sina\Slot>netstat -ano |find "368"
TCP 127.0.0.1:2538 127.0.0.1:3306 ESTABLISHED 3688
TCP 127.0.0.1:2539 127.0.0.1:3306 ESTABLISHED 3688

G:\Projects\sina\Slot>netstat -ano |find "283"
TCP 127.0.0.1:2618 127.0.0.1:3306 ESTABLISHED 2836
TCP 127.0.0.1:2619 127.0.0.1:3306 ESTABLISHED 2836


还有谁想说mysql_select_db 那个是个feature么 :x

php_mysql my_select_db 多库交叉使用的bug

<?php
$db_1 = mysql_connect('localhost', 'root', 'root');
mysql_select_db('ftpunion', $db_1);

var_dump(mysql_fetch_assoc(mysql_query('select database();', $db_1)));

$db_2 = mysql_connect('localhost', 'root', 'root');
mysql_select_db('ftpunion_new', $db_2);

var_dump(mysql_fetch_assoc(mysql_query('select database();', $db_2)));

#mysql_select_db('ftpunion', $db_1);
var_dump(mysql_fetch_assoc(mysql_query('select database();', $db_1)));


结果是。。。
array(1) {
["database()"]=>
string(8) "ftpunion"
}
array(1) {
["database()"]=>
string(12) "ftpunion_new"
}
array(1) {
["database()"]=>
string(12) "ftpunion_new"
}

一个页面使用 相同的用户名和密码 连接使用 相同服务器 不同的库 的时候,只能是在切换库查询的时候,先得对对应的mysql 连接资源进行一次 mysql_select_db 操作,然后再执行查询

要不然你只能得到一个 错误的库.正确的表 不存在 的提示。。。因为貌似这个过程 php_mysql 实际只创建了一个连接,查询的库以最近一次的mysql_select_db的操作为准。。。。

sleep了20秒,看看netstat出来的链接数。。。。只有这一个

G:\Projects\sina\Slot>netstat -ano | find "4320"
TCP 127.0.0.1:1244 127.0.0.1:3306 ESTABLISHED 4320



其实这算是一个无奈的选择,

去给php 报bugs去。。。只是不知道这算不算bug
或者说。。。。It's not a bug, it's a feature, not sure if it's expected or not.


昨晚这个折腾了好久
好不容易写的类,不可能有逻辑问题,结果发现是这个2问题。。。

Thursday, July 30, 2009

自定义Session handler时 session_id() 发送 set-cookie头的处理

自定义的Session Handler,在 session_start 之前,做了如下处理
<?php
static protected function _config()
{
session_name(SS_SESSION_NAME);

session_set_cookie_params(
SS_SESSION_EXPIRATION ? SS_SESSION_EXPIRATION + parent::$buclass->timestamp : 0,
SS_COOKIE_PATH,
SS_COOKIE_DOMAIN,
SS_COOKIE_SECURE,
SS_COOKIE_HTTPONLY
);

global $table_sessions;
self::$_table_sessions = & $table_sessions;

# 如果要通过URL传递Session ID
if (SS_SESSION_PASSSIDVIAURL)
{
$sess_id = parent::$buclass->gpc->get(SS_SESSION_NAME);
}
else
{
$sess_id = parent::$buclass->gpc->cookie(SS_SESSION_NAME);
}

if (!empty($sess_id))
{
if (!preg_match('#^[a-zA-Z0-9]+$#', $sess_id))
{
throw new Exception('不合法的SESSION ID');
}

session_id($sess_id);
}
} ?>


每次刷新页面的时候,都发现http响应中有Set-Cookie的头,设置session name的。翻了翻php手册,http://www.php.net/session_id,参数的地方,有个Note
Note: When using session cookies, specifying an id for session_id() will always send a new cookie when session_start() is called, regardless if the current session id is identical to the one being set. 


于是,每次调用 session_id($sess_id); 这句的时候,都会发送一个set-cookie头。

回到代码,代码的设计是为了考虑到URL传递Session ID的情况,所以分别允许GET方式和COOKIE方式获取session name的值。
而代码里明显可以看到,这里获取 $_GET 和 $_COOKIE的值,并不是直接用这些超级全局变量的,而是通过了一个 gpc的类对象。自己写的一个gpc变量的封装,里边把$_GET, $_POST, $_COOKIE, $_FILES, $_REQUEST的值先加了引用,然后unset了。

结果。。。默认设置情况下,unset了$_COOKIE的值,或者说unlink了$_COOKIE对cookie变量的引用,session机制无法获取 session id,从而代码里会反复地设置session 的cookie变量。

Newborn Project里,gpc的unset变量环节是独立的,也就是
系统初始化->初始化核心类->初始化gpc->初始化session->清理gpc的变量(unset)...
而这里,
系统初始化->初始化核心类->初始化gpc(外加清理gpc的变量(unset))->初始化session...

结果又出问题了。。。

暂时没想出不增加gpc类与其他类耦合度的方法


==================================================================

Update:
最终还是决定跟Newborn Project一样把unset vars的操作单提出来

<?php
static protected function _config()
{
session_name(SS_SESSION_NAME);

session_set_cookie_params(
SS_SESSION_EXPIRATION ? SS_SESSION_EXPIRATION + parent::$buclass->timestamp : 0,
SS_COOKIE_PATH,
SS_COOKIE_DOMAIN,
SS_COOKIE_SECURE,
SS_COOKIE_HTTPONLY
);
global $table_sessions;
self::$_table_sessions = & $table_sessions;

# 如果要通过URL传递Session ID
if (SS_SESSION_PASSSIDVIAURL)
{
$sess_id = parent::$buclass->gpc->get(SS_SESSION_NAME);
if (!empty($sess_id))
{
session_id($sess_id);
}
}
else
{
if (!($sess_id = parent::$buclass->gpc->cookie(SS_SESSION_NAME))==$_COOKIE[SS_SESSION_NAME])
{
session_id($sess_id);
}
}
}



<?php
public function s_init()
{
buComponent::$buclass = & $this;

$this->_init_vars();

$this->filter = buFilter::instance();
$this->cache = buCache::instance();

$this->_init_gpc();
$this->_init_cookie();

if (SS_INTERFACE == SS_INTERFACE_WEB)
{
$this->_init_session();
}
else
{
$this->db_open();
}

$this->gpc->unset_vars();
}

Runkit是个好东西

可惜对系统函数不靠谱。。。

Monday, July 27, 2009

Session Handler引发的zend_mm_heap corrupted

不确定最终原因是什么
但是,从代码的结构看,与Newborn Project一样
加上了session handler之后,用kernel.class下的一个session handler类开启session handler
其他代码还没来得及改,大体都是dz2.0的结构。


只要在代码模板中调用discuz_exit或者exit,抑或是在 函数中调用,就会触发这个错误
具体是不是,还不敢确定
因为有时候stderr会写这个
有时候只是5.2 cli会弹出Windows下程序崩溃的提示,module显示unknown
5.3 cli到输出结束就一直挂起没响应,得ctrl+c
没心思去调试php这个错误来源。。。



最终解决方案是把output写进了session类的destructor里,然后kernel加了个end的方法调用exit()

网上找了很久都没有找到一个类似的情况

问题应该是session的结束和exit还有输出流的结束的顺序关系或者调用关系的冲突
但是。。懒得管了

Wednesday, June 3, 2009

图片分层透明化

给JeRegle的幼儿园分色,给我的是一个gif文件,除了底色白色之外,只有三种颜色。
需要将这三种颜色的点分别提取出来,并背景透明化存成独立文件。

之前处理图片的时候,也给他做过一次背景透明化的东西,用gd库的imagecolortransparent清理背景色。
<?php
$image = imagecreatefromgif('totrans.gif');
$dim = array(imagesx($image), imagesy($image));

var_dump($dim);
$im = imagecreatetruecolor($dim[0], $dim[1]);
$bg = imagecolorallocate($im, 255,255,255);

imagecopyresampled($im, $image, 0, 0, 0, 0, $dim[0], $dim[1], $dim[0], $dim[1]); 

$white = imagecolorallocate($im, 255,255,255);
imagecolortransparent($im, $white);
imagegif($im, 'trans.gif');?>


但是这次分色的时候,发现imagegif出来gif颜色有问题。生成了好几次,觉得有可能是gif色库的问题。印象中标准gif只能描述256种颜色,所以需要一个类似于bmp的调色板的东西。

用imagepng生成之后,继续去颜色,然后列出一个表格:
original:   gif   :   png  
#93B1FD : #94B2FC : #93B1FD
#B6CBFE : #B4CAFC : #B6CBFE
#D9E4FE : #DCE6FC : #D9E4FE

虽然png的颜色没问题,但是给他之后,居然ps没法识别背景的透明化。

突然想到了 ImageMagick。
有打算用imagick或者magickwand for php来试试
但是有点不大靠谱,没时间翻文档。
突然想到ImageMagick的一个二进制程序 convert
印象中见有人用它做过透明化的处理。
先把gd代码的透明化给注视掉,让背景色全为白色,然后上网查了下,
E:\sskaje>convert 4_1.png -background transparent -transparent white 5_1.png
然后把图给他,测试没问题。

php代码:
<?php
/*
original:   gif   :   png  
#93B1FD : #94B2FC : #93B1FD
#B6CBFE : #B4CAFC : #B6CBFE
#D9E4FE : #DCE6FC : #D9E4FE

transparent the background with imagemagick/convert:
 E:\sskaje>convert 4_1.png -background transparent -transparent white 5_1.png
*/
$image = imagecreatefromgif('3.gif');
$dim = array(imagesx($image), imagesy($image));
$prefix = '4_';
$im = imagecreatetruecolor($dim[0], $dim[1]);
$bg = imagecolorallocate($im, 255,255,255);
$white = imagecolorallocate($im, 255,255,255);
$red = imagecolorallocate($im, 255, 0, 0);

$layers = array(
 1 => array(
  imagecolorallocate($im, 0x93,0xb1,0xfd),
  0x93B1FD,
 ),
 2 => array(
  imagecolorallocate($im, 0xb6,0xcb,0xfe),
  0xB6CBFE,
 ),
 3 => array(
  imagecolorallocate($im, 0xd9,0xe4,0xfe),
  0xD9E4FE,
 ),
);

foreach ($layers as $k=>$v)
{
 echo $k, "\n";

 imagecopyresampled($im, $image, 0, 0, 0, 0, $dim[0], $dim[1], $dim[0], $dim[1]); 

 for ($i=0; $i<$dim[0]; $i++)
 {
  for ($j=0; $j<$dim[1]; $j++)
  {
   if (imagecolorat($im, $i, $j) != $v[1])
   {
    imagefill($im, $i, $j, $white);
   }
   else
   {
    imagefill($im, $i, $j, $v[0]);
   }
  }
 }

 #imagecolortransparent($im, $white);
 #imagegif($im, $prefix.$k.'.gif');
 imagepng($im, $prefix.$k.'.png');
}?>

Sunday, May 24, 2009

又被php5-sqlite3给弄崩了


又被php5-sqlite3给弄崩了

ubuntu下的,又是

虽然不是pecl的sqlite3,好吧,我也能忍了,写这么多也习惯了

但是,居然连ORDER的支持也不好

 

今天却又发现,不支持  group by

服了

 

换了好几种方案,实在不敢自己处理数据结构,要不然指不定乱成啥样的

 

最后看了看 sqlite3的命令行参数,发现可以直接接sql,然后system试了试,很不爽的是,没有任何结果输出

改了全路径,依旧无效

然后 2>&1 结果报无法打开文件

 

去改了改apparmor的配置,给apache加了目录文件读取权限

至少能获取数据了

虽然很恶心

Monday, March 30, 2009

php5.3 rc2-beta居然把_GET扔进argc/argv里...

居然。。。
这个版本的argc/argv包括了 GPC变量。。。。
太诡异了
之前的那个版本的dev版还是没有的,结果刚才把5.3的环境一更新,却发现自己的cli模式判断无效了



访问
http://localhost/test/argc.php?1=2&12=3

php5.2.9下 结果为 0
php5.3.0RC2-dev 结果 为 1

尝试用 POST表单和手工加入的cookies提交,结果数据还是1
只有$_GET 才被算进去了
不懂为啥了


php5.3的这个dev的信息:
Build Date Mar 29 2009 12:47:23
Zend Extension Build API220090115,TS,VC9
PHP Extension Build API20090115,TS,VC9

Tuesday, March 24, 2009

致所有的WAMP-ers

I apologize, sincerely
所有的WAMP-ers:

1 如果你不确定代码足够安全,千万不要留phpinfo的页面,或者你真的要留,也别用 info.php, phpinfo.php, test.php, 1.php, i.php ...

2 如果你PHP经验不足,那就强制转换所有的变量,或者,在入库前用 mysql/mysqli 等库自带的 real_string_escape 或者类似的函数,转义一下提交数据。

3 如果你的代码没有管理mysql全库的需求,例如管理用户之类的,那就用个单库权限的用户吧,千万不要给FILE权限。

4 如果你的Apache不需要用来管理系统,千万不要让它跑在默认的System权限下,建一个用户、改一改执行权限,没什么难的。

5 不管怎样,phpMyAdmin之类的东西,别放在那种常见的文件夹下

6 不管怎样,列目录的权限都得关掉,除非你确认能列的目录下没有敏感文件;或者你放个默认的空的index页都行

7 线上环境,不要开错误提示,或者至少说,错误提示不要能爆出脚本的物理路径。

8 明确地说,以eval、exec等为关键词查找php shell,是没用的

9 。。。。待续

然后我的开发环境是不安全的,只要你能找到我放的shell

最后
I apologize, sincerely

Thursday, March 12, 2009

A PHP Class for Srun3000

本来打算写个完整的客户端的,但是却一直没有想好cli下怎么跟php的进程进行通信

于是写好了一个php的类,简单地封装了 登陆、注销、保持连接、查询信息 的操作

/**
* Srun3000客户端类
*
* @author sskaje sskaje@gmail.com
* @version 1.0.0
* @todo 提供一个通信的机制
*/
class sscSrun3000forBIT
{
private $username;
private $password;
private $mode = 1;
private $client_type = 1;
private $n = 1;

private $url_login = 'http://10.0.0.55/cgi-bin/do_login';
private $url_logout = 'http://10.0.0.55/cgi-bin/do_logout';
private $url_keepalive = 'http://10.0.0.55/cgi-bin/keeplive';

private $modes = array(0=>'国际模式', 1=>'国内模式');
private $client_types = array(1=>'WEB 模式', 2=>'客户端模式');

private $user_agent = '';

private $uid = 0;

public $debug = 0;
/**
* cookie文件存放路径
*
* @var string
*/
private $cookie_file = 'cookie.txt';
/**
* cURL 资源
*
* @var cURL
*/
private $ch;
/**
* 构造函数,默认参数构造
*
* @param string $username 用户名
* @param string $password 密码
* @param int $mode 登陆模式,1->国内,0->国际
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function __construct($username, $password, $mode=1, $client_type=1)
{
$this->username = $username;
$this->password = $password;
$this->change_mode($mode);
$this->set_client_type($client_type);
}
/**
* 设置客户端模式
*
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function set_client_type($client_type)
{
if ($client_type == 1)
{
$this->client_type = $client_type;
$this->n = 1;
$this->user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser)';
}
else if ($client_type == 2)
{
$this->client_type = $client_type;
$this->n = 3;
$this->user_agent = 'my session';
}
else
{
throw new Exception('Bad client type');
}
}
/**
* 设置登陆模式
*
* @param int $mode 登陆模式,1->国内,0->国际
*/
public function change_mode($mode)
{
if ($mode != 1 && $mode != 0)
{
throw new Exception('Bad login mode');
}
else if ($mode == 0)
{
echo "将以国际模式登陆\n";
}
else
{
echo "将以国内模式登陆\n";
}
$this->mode = $mode;
}
/**
* 设置cookie文件路径
*
* @param string $cookie_file
*/
public function set_cookie_fileloc($cookie_file)
{
$this->cookie_file = $cookie_file;
}
/**
* 初始化资源
*
*/
public function init()
{
$this->ch = curl_init();
curl_setopt($this->ch, CURLOPT_VERBOSE, 0);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->ch, CURLOPT_COOKIEJAR, $this->cookie_file);

if ($this->client_type == 2)
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'my session');
curl_setopt($this->ch, CURLOPT_PORT, 3333);
}
else
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser) ');
}
}
/**
* 登陆
*
*/
public function login()
{
if ($this->client_type == 2)
{
$password = $this->passencode($this->password);
}
else
{
$password = $this->password;
}

$login_str =
'username=' . urlencode($this->username) .
'&password=' . urlencode($password) .
'&drop=' . $this->mode .
'&type=' . $this->client_type .
'&n=' . $this->n;

$this->debug('Login: ' . $login_str);

curl_setopt($this->ch, CURLOPT_URL, $this->url_login);
curl_setopt($this->ch, CURLOPT_REFERER, $this->url_login);
curl_setopt($this->ch, CURLOPT_POST, 1);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $login_str);
$out = curl_exec($this->ch);

$this->debug('Login response: ' . $out);

curl_setopt($this->ch, CURLOPT_COOKIE, $this->username.'%7C'.$this->password);

if (preg_match('#^\d+$#', $out))
{
$this->uid = $out;
return;
}
$this->login_error($out);
}
/**
* 注销
*
*/
public function logout()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_logout);
curl_setopt($this->ch, CURLOPT_POST, 1);
$logout_str = 'uid='.$this->uid;

$this->debug('Logout query: '.$logout_str);

curl_setopt($this->ch, CURLOPT_POSTFIELDS, $logout_str);
$out = curl_exec($this->ch);

$this->debug('Logout: ' . $out);

if ($out == 'logout_ok')
{
echo 'Successfully logout.';
}
else
{
echo 'Failed to log u out';
}
}
/**
* 保持连接
*
* @return string
*/
public function keepalive()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_keepalive);
curl_setopt($this->ch, CURLOPT_POST, 1);
$keepalive_str = 'uid='.$this->uid;

$this->debug('Keepalive query: '.$keepalive_str);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $keepalive_str);
$out = curl_exec($this->ch);

$this->debug('Keepalive: ' . $out);

if (preg_match('#^\d+,\d+,\d+$#', $out))
{
return $out;
}
else
{
die('Disconnected.');
}
}

/**
* 客户端模式密码加密
*
* @param string $pass
* @return string
*/
public function passencode($pass)
{
$atom = 70;
$len = strlen($pass);
for ($i=0; $i<$len; $i++)
{
$pass[$i] = chr(ord($pass[$i]) ^ $atom);
}
return $pass;
}
/**
* 登陆提示
*
* @param string $out
*/
public function login_error($out)
{
switch($out)
{
case "user_tab_error":
die("认证程序未启动");
break;

case "username_error":
die("用户名错误");
break;

case "non_auth_error":
echo "您无须认证,可直接上网";
break;

case "password_error":
die("密码错误");
break;

case "status_error":
die("用户已欠费,请尽快充值。");
break;

case "available_error":
die("用户已禁用");
break;

case "ip_exist_error":
die("用户已存在");
break;

case "usernum_error":
die("用户数已达上限");
break;

case "online_num_error":
die("该帐号的登录人数已超过限额\n如果怀疑帐号被盗用,请联系管理员。");
break;

case "mode_error":
die("系统已禁止WEB方式登录,请使用客户端");
break;

case "time_policy_error":
die("当前时段不允许连接");
break;

case "flux_error":
die("您的流量已超支");
break;

case "minutes_error":
die("您的时长已超支");
break;

case "ip_error":
die("您的IP地址不合法");
break;

case "mac_error":
die("您的MAC地址不合法");
break;

default:
die("找不到认证服务器");
break;
}
}
/**
* debug数据
*
* @param string $message
*/
public function debug($message)
{
if ($this->debug)
echo "\n=====================================\n[DEBUG] ", date('Ymd H:i:s '), $message, "\n=====================================\n";
}
/**
* 查询状态
*
*/
public function query()
{
$msg = $this->keepalive();
$nums = explode(',', $msg);
echo "
Username: {$this->username}
UID : {$this->uid}
Mode : {$this->modes[$this->mode]}
Type : {$this->client_types[$this->client_type]}

Data in : {$nums[1]} bytes
Data out: {$nums[2]} bytes
Connection time: {$nums[0]} seconds
";
}
}



调用方法示例
 require('Srun3000forBIT.class.php'); 
try
{
#$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 2);
$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 1);
$srun->debug = 0;
$srun->init();

$srun->login();
$srun->keepalive();
$srun->query();
$srun->logout();
#$srun->keepalive();
}
catch (Exception $e)
{
echo $e->getMessage();
}

Wednesday, March 11, 2009

被Exception Handler虐了。。

调整了一下代码的结构,把几个库扔进了namespace,结果代码就开始跑不通了

但凡有异常被抛出,apache就会挂

apache的错误没有任何帮助,全都是些
[Wed Mar 11 09:54:23 2009] [notice] Parent: child process exited with status 3221225477 -- Restarting.
的东西
没有任何意义的返回码

于是试着把代码回滚,从r104到r100,一点点merge
终于出现了一个有点用的错误提示

PHP Fatal error: Exception thrown without a stack frame in Unknown on line 0

google了下,看到了php文档的comments
http://cn.php.net/manual/en/function.set-exception-handler.php#88082

然后把代码改动了
原始版本:
/**

* Exception handler and some default exception definitions
*
* @file /include/class/kernel/exception.class.php
* @author sskaje (sskaje@gmail.com)
* @lastedit 2009-03-05 12:56
* @version 1.0.0
* @package SS Kernel
* @subpackage exception
*/
declare(encoding='UTF-8');
namespace ssns;

/**
* Exception Handler
*
*/
final class ssExceptionhandler
{
private function __construct()
{
}
/**
* 自定义异常
*
*/
static public function custom_exception_handler()
{
set_exception_handler(array(__CLASS__, 'exception_handler'));
}
/**
* custom exception handler
*
* @param Exception $exception
*/
static public function exception_handler($exception)
{
if (SS_INTERFACE == SS_INTERFACE_CLI)
{
$exception_name = get_class($exception);
sscDebug::stderr('Exception {'.$exception_name.'} : '.$exception, '[Exception]');
exit;
}

debug_print_backtrace();

exit;

if ( $exception instanceof ssExceptionFatal)
{
self::error_exit_page($exception);
}
elseif ( $exception instanceof ssExceptionMsgbox )
{
msgbox($exception);
}
else
{
die( 'Unknown Exception : ' . $exception );
}
}
/**
* error_exit_page
*
* @param string $message
*/
static public function error_exit_page($message)
{
$email = predisp_email(SS_EMAIL);
$version = SS_VERSION;

echo <<<TEXT
...
TEXT; exit;
}
}

改动后
/**

* Exception handler and some default exception definitions
*
* @file /include/class/kernel/exception.class.php
* @author sskaje (sskaje@gmail.com)
* @lastedit 2009-03-05 12:56
* @version 1.0.0
* @package SS Kernel
* @subpackage exception
*/

declare(encoding='UTF-8');
namespace ssns;

/**
* Exception Handler
*
*/
final class ssExceptionhandler extends sscComponent // implements ssifComponent
{
private function __construct()
{
}
/**
* 自定义异常
*
*/
static public function custom_exception_handler()
{
set_exception_handler(array(__CLASS__, 'exception_handler'));
}
/**
* custom exception handler
*
* @param Exception $e
*/
static public function exception_handler($e)
{
try
{
if (SS_INTERFACE == SS_INTERFACE_CLI)
{
$exception_name = get_class($e);
sscDebug::stderr('Exception {'.$exception_name.'} : '.$e);
exit;
}

throw $e;
}
catch (ssExceptionFatal $e)
{
self::error_exit_page($e);
}
catch (ssExceptionMsgbox $e)
{
msgbox($e);
}
catch (Exception $e)
{
die( 'Unknown Exception : ' . $e->getMessage() );
}
}
/**
* error_exit_page
*
* @param string $message
*/
static public function error_exit_page($message)
{
$email = predisp_email(SS_EMAIL);
$version = SS_VERSION;

echo <<<TEXT
...
TEXT;

exit;
}
}