因为php的语法要求不严格,字符串也可以当数组使用,这样就存在一个问题:当使用非数字作为key访问字符串中的内容时,就可能会带来一些不一致的情况,如下面的代码
$hello = "hello";
var_dump($hello['abc']);
var_dump($hello['0']);
var_dump($hello['1abc']);
var_dump($hello['12abc']);
输出的结果就不说了,简单运行一下就可以知道,我想原因是由intval这个东东引起的,时间关系,没有去确认zend的代码,不过下面代码的运行结果页说明了一些问题
var_dump(intval('abc'));
var_dump(intval('0'));
var_dump(intval('1abc'));
var_dump(intval('12abc'));
这个东东,对code review或者测试时候是一个很有杀伤力的bug,如果一个函数设计的返回值不好,有时候返回数组,有时候返回字符串,在使用之前,就一定要先判断返回结果是否为数组,否则就会在这个bug上杯具
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2011/03/16/php-array-with-string.html
---------------------------------------------------------------
作为一个QA,我本来是很少写代码的,不过这段代码用的次数比较多,每次用的时候都改一些,比较烦,所以整理了一个通用的,作为个人代码库的第一块石头吧
升级于2010-7-26,修复bug
class sql_runner{
private static $_arr_self = array();
private $_addr = false;
private $_user = false;
private $_passwd = false;
private $_db_connection = false;
static function get_instance($addr, $user, $passwd){
$key = $addr.'#'.$user.'#'.$passwd;
if(false === array_key_exists($key, self::$_arr_self)){
sql_runner::$_arr_self[$key] = new sql_runner($addr, $user, $passwd);
}
return sql_runner::$_arr_self[$key];
}
private function __construct($addr, $user, $passwd){
$this->_addr = $addr;
$this->_user = $user;
$this->_passwd = $passwd;
}
public function run_sql($sql){
if(false === $this->_db_connection || !mysql_ping($this->_db_connection)){
UB_LOG_DEBUG("connecting db $this->_addr, $this->_user");
$this->_db_connection = mysql_connect($this->_addr, $this->_user, $this->_passwd);
if(false === $this->_db_connection){
UB_LOG_FATAL("connect db failed: ".mysql_error());
return false;
}else{
mysql_set_charset('latin1', $this->_db_connection);
}
}
$result = false;
$result = mysql_query($sql, $this->_db_connection);
if(false === $result){
UB_LOG_FATAL("[$sql] execute failed:". mysql_error($this->_db_connection)."\n");
return false;
}
$result_arr = array();
if(is_resource($result)){
while($row = mysql_fetch_assoc($result)){
$result_arr[] = $row;
}
mysql_free_result($result);
}
UB_LOG_DEBUG("[$sql] execute succed, selected result:".print_r($result_arr, true));
return $result_arr;
}
}
/**for log functions*/
if(!function_exists('UB_LOG_DEBUG')){
function UB_LOG_DEBUG($log){
print($log);
}
}
if(!function_exists('UB_LOG_FATAL')){
function UB_LOG_FATAL($log){
print($log);
}
}
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/07/23/sql-to-run-a-simple-package.html
---------------------------------------------------------------
我看网上应该有不少搜索这个区别的问题,但是回答的都不全面,其中sigterm与sigint尤其有一点区别比较重要,但大都没有提及,今天我就遇到了这个问题,纠结了20分钟才搞明白咋回事。
首先,对于说这几个信号都是终止程序运行的说法不太准确,因为程序收到信号后,如果不对信号处理,就会导致程序退出,但如果程序捕获信号进行处理,按照它的逻辑,它是不一定会退出的。
在这三个信号中,sigkill是不能被捕获的,程序收到这个信号后,一定会退出。这就是kill -9一定能保证将程序杀死的原因。
下面说一下sigterm与sigint的区别,其中有一点区别区别很多文章都没有提及,也是我写这篇blog的原因(如果人家都写了,我就不用写了呗)
| 信号 |
产生方式 |
对进程的影响 |
| sigint |
通过ctrl+c将会对当进程发送此信号 |
信号被当前进程树接收到,也就是说,不仅当前进程会收到信号,它的子进程也会收到 |
| sigterm |
kill命令不加参数就是发送这个信号 |
只有当前进程收到信号,子进程不会收到。如果当前进程被kill了,那么它的子进程的父进程将会是init,也就是pid为1的进程 |
下面这两个代码片段就能够验证这种情况(注意使用pcntl的时候,一定要declare ticks,要不然会杯具的发现函数没有被调用,进程不退出,信号发过去没有作用。php手册竟然没有强调这一点):
文件:loadhelper.php
#为了pcntl能够截获信号
declare(ticks = 1);
$arr_processes = array();
function terminate($signo){
echo "aaaaaaaaaaa\n";
}
pcntl_signal(SIGTERM, "terminate", true);
pcntl_signal(SIGINT, "terminate", true);
foreach($argv as $key => $operation){
if(0 === $key){
continue;
}
$pipes = array();
$process = proc_open($operation, array(), &$pipes);
if(false === $process){
exit(-1);
}
$arr_processes[] = $process;
}
while(true){
sleep(100);
}
文件:child.php
declare(ticks=1);
pcntl_signal(SIGINT, "terminate");
pcntl_signal(SIGTERM, "terminate");
function terminate($signo){
echo "test_child\n";
}
while(true){
sleep(100);
}
使用命令php loadhelper.php “php test.php”可以启动这个测试。
1.输入ctrl+c发送sigint可以看到,父进程与子进程的terminate都得到了执行,都有输出,但父进程不会退出,因为子进程还没有退出
2.通过kill向父进程的pid发送sigterm,可以看到,只有父进程输出
遗留问题:
父进程(loadhelper)接受到一次信号后,如果在terminate函数中调用exit,它还是不能退出的,因为还有子进程没有退出。但是从此以后它就不能再接收信号了(子进程还是能够接收到sigint),可能是exit使进程进入了待回收状态,具体还 需要后续在分析一把。
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/05/20/sigterm-sigint-sigkill-difference.html
---------------------------------------------------------------
在这里,我不得不再一次感叹php语言库函数的山寨与不专业。getopt函数就是一个典型的例子,通常用的时候,大家可能觉得没有什么,但在某些情况 下,就真的让人很囧。一个简单的函数,稍微多花几分钟就弄得更好一些了,但这个语言有个随意的开端,就有个随意的实现啊。
在linux中,使用getopt时候,有两种情况:
1.取得的参数解析成字符串:“php test_arg.php -c abc”,这种情况c参数取得的结果就是abc这个字符串
2.取得的参数解析成数组:“php test_arg.php -c abc -c abc123”,这种情况c参数取得的结果就是包含abc与abc123的数组
但是遇到这种情况呢:“php test_arg.php -c abc*”?由于linux的shell已经帮程序做了输入参数的解析,这时候c参数得到的既不是abc*这个结果也不是一个数组,而是被shell展开成了很多文件名后的第一个。
可能getopt用的童鞋很少,但这种山寨的设计,实在太让人憋屈了,自己花个10分钟写一个就比它的要好,为了避免大家重复劳动,分享一个代码片段
function mygetopt(){
global $argv;
$result = array();
$current_key = false;
foreach($argv as $opt){
$matches = array();
if(1 === preg_match("/^-{1,2}(.*)$/", $opt, $matches)){
$current_key = $matches[1];
if(false === isset($result[$current_key])){
$result[$current_key] = false;
}
}else if (false !== $current_key){
if(false === $result[$current_key]){
$result[$current_key] = $opt;
}else{
if(false === is_array($result[$current_key])){
$result[$current_key] = array($result[$current_key]);
}
$result[$current_key][] = $opt;
}
}
}
return $result;
}
为了方便使用,将新版本的getopt函数设置为不接受任何参数,但是解析的结果可以输出所有的参数内容。因为php官方的getopt函数使用后,也无非是对输出的数组进行foreach之后进行switch,还不如方便点,直接解析所有呢。除了这一点,这个getopt函数的输出结果与php官方的完全一致
php官方getopt函数参考文档:http://cn.php.net/manual/en/function.getopt.php
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/05/17/php-and-repair-defects-in-the-getopt.html
---------------------------------------------------------------
php中,使用&表明这个引用是指针,这样在两个引用可以指向同一个内存空间。但其实不使用&的情况下,php也是写时拷贝的,zend引擎只有在修改的时候才会发生内存拷贝,不修改的话是不会产生消耗的。在实际使用中,我还发现使用&符号反而性能会降低。
在不需要修改的情况下,建议尽量不要使用&符号,否则不仅降低效率,还有可能造成出现bug。今天查看最近对测试框架的升级,就踩上了simpletest上面的一个bug,请看simpletest中,想testsuit加上case的函数:
function addTestCase(&$test_case) {
$this->_test_cases[] = &$test_case;
}
这么简单的函数,我想大家一般都会认为没有问题吧,正常情况下它都能很好的工作,但是如果你这样用,就bug了:
foreach($arr_cases as $case){
$this->test_suit->addTestCaseOpenTest($case);
}
你会发现_test_cases这个数组里面,只哟foreach最终的那个元素,因为函数都是接受的值引用,foreach改变$case的值,就把已经传入_test_cases数组的内容都给改写了,于是就悲剧了。对于simpletest来说,它并不需要修改用户的case程序,这样做值引用显然是多此一举,还产生了bug。
关于性能降低,用一个简单的代码测试一下就知道了
$a = array('a','c','n');
function printArray(&$arr)
{
print(count($arr));
}
for($i=0;$i<100000;$i++){
printArray($a);
}
用time命令跑一次,把printArray的&符号去掉再跑一次,可以看到大致下面的结果(机器不同,结果不同啊)
| 带有&符号 |
不带有&符号 |
real 0m0.183s
user 0m0.130s
sys 0m0.053s |
real 0m0.160s
user 0m0.101s
sys 0m0.060s |
可见使用&反而会使性能下降的,所以除非有必要,不建议使用&符号
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/05/07/php-in-the-ampersand-on-the-abuse-of.html
---------------------------------------------------------------
以前也曾写过这样的代码,但没有特殊注意过,今天大家讨论收银台的问题时候,再提起这样的设计,却感觉非常有效。
因为分支很多,使用switch-case几乎是不可避免的,而这对与代码的维护与理解却不是一件很好的事情,在增加分支的时候,也是一种比较郁闷的事情。利用语言的动态特性与模块化的设计,可以在一定程度上减轻这种问题。
class A{};
class B{};
$arr_actions = array(
'a' => 'A',
'b' => 'B',
);
function do_action_no_switch($action){
global $arr_actions;
if(!array_key_exists($action, $arr_actions)){
return false;
}
$job = new $arr_actions[$action];
//job->do...
}
do_action_no_switch('a');
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/04/21/reduce-the-use-of-the-dynamic-nature-of-language-switch.html
---------------------------------------------------------------