最近发现一些易忽视的php错误,这些错误在大多数情况下是不会有什么问题的,但如果咬写开源代码,这种问题绝不可犯,否则一定或多或少存在安全漏洞,给不法分子钻了空子。
      1、switch语句条件中的0,不理解原因,感叹一下这神奇的0:

switch(0){
    case 'xxx':
        echo 'haha1';
        break;
    default:
        echo 'hehe';
}//haha1
switch(0){
    case true: //true换成数字一样
        echo 'haha2';
        break;
    default:
        echo 'hehe';
}//hehe

      先不说解决方法,在继续来一个例子:
      2、in_array中的0

var_dump(in_array(0, array('x', 'a'))); //true
var_dump(in_array(0, array(1, true, false))); //true
var_dump(in_array(0, array(1, true, 2))); //false

      这个错误和上面的错误貌似有异曲同工之妙,看来可以大胆推测下面这个等式的成立:

var_dump(0 == 'xxx'); //true
var_dump(0 == false); //true
var_dump(strval(0) == 'xxx'); //false
var_dump(true == 'xxx'); //true

      所以避免这种问题的办法是strval($arg)使之与比较对象具有相同类型。
      3、empty中的’0’字符串

$a = '0'; //同数字0、false
if(empty($a)){
    echo 'xxx';
} //xxx

      这个问题在官方文档中有说明,不小心容易忽视。
      4、非全等判断

var_dump('0123' == '123'); //true

      这个东西就更不用说了,公司用户名曾犯的错,记忆深刻
      5、一些‘类’函数结构

$somevar = 12;
$func="unset";
$func($somevar); //undefined function unset()
//function_exist('unset'); //false

      像unset()、echo()这种都属于语言结构的一部分,并非函数。
      6、unset全局变量的解决办法:

$id = 1;
function test()
{
   global $id;
   unset($id);
}
test();
echo($id);  //1

      这种方式在PHP3中运行正常(虽然我没试),在php4以上需要使用:

unset($GLOBALS["id"]);

      好了,没了,先就写这么多,欢迎大家来抨击。

无标签信息 2 条

      小秘书把人搞烦了,改需求,等接口。算了,休息一下,写段小程序。前端php掉后端服务接口的应用对一个公司来说很正常,一般的解决方案都是使用中间件,我们公司一直使用的是ice,怎么样呢,蛮好,3.4蛮好,但还是有点麻烦,所以就有了下面这个东西了,如果只是做小测试或者写简单demo用这个是没有问题的,主要是快。
      首先建个服务,最快方法,用python,不说了,来代码:

import time
import json
import socket
import traceback
import threading
import SocketServer
class Too:
    def __init__(self):
        print 'Welcome!'
    def test(self, args):
        return 'xxx:%s' % args;
    def error(self, args):
        return 'not function!'
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                data = self.request.recv(1024)
                if not data:
                    print 'end'
                    break
                data = json.read(data)
                res =  getattr(self._object, data['func'], 'error')(data['args'])
                if not res:
                    res = ''
                res = str(len(res)).rjust(8, '0')+str(res)
                self.request.send(res)
            except:
                print 'error in ThreadedTCPRequestHandler :%s, res:%s' % (traceback.format_exc(), data)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass
class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        ThreadedTCPServer.allow_reuse_address = True
    def open(self, object):
        ThreadedTCPRequestHandler._object = object
        server = ThreadedTCPServer((self.host, self.port), ThreadedTCPRequestHandler)
        server.serve_forever()
if __name__ == '__main__':
    Server('0.0.0.0', 12580).open(Too())

      在写个前端脚本调用,还是最快原则,php,不多说,上代码:

class server{
    private $_host;
    private $_port;
    private $_error;
    private $_socket;
    private $_class;
    function __construct($host, $port){
        $this->_host = $host;
        $this->_port = $port;
        $this->_class = '';
        $this->_connect();
    }
    private function _connect(){
        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if($sock===false){
            $this->_error('error socket_create'.socket_strerror(socket_last_error()));
        }
        $ret = socket_connect($sock, $this->_host, $this->_port);
        if($ret===false){
            $this->_error('error socket_connect'.socket_strerror(socket_last_error()));
        }
        $this->_socket = $sock;
    }
    public function object($class){
        $this->_class = $class;
        return $this;
    }
    public function __call($func, $args){
        $data = array('class' => $this->_class, 'func' => $func, 'args' => $args);
        $data = json_encode($data);
        $socket = $this->_socket;
        $res = socket_write($socket, $data, strlen($data));
        if($res===false){
            $this->_error('error socket_write'.socket_strerror(socket_last_error()));
        }
        $res = socket_read($socket, 1024, PHP_BINARY_READ);
        $result = substr($res, 8);
        $len = intval(substr($res, 0, 8));
        while(true){
            if($len != strlen($result)){
                $result .= socket_read($socket, 1024, PHP_BINARY_READ);
            }else{
                break;
            }
        }
        return $result;
    }
    private function _error($errMsg = ''){
        $this->_error = $errMsg;
        echo $errMsg;
        exit();
    }
    public function __destruct(){
        $socket = $this->_socket;
        socket_write($socket, '', 0); //结束
        socket_close($socket);
    }
}
function microtime_float(){
   list($usec, $sec) = explode(" ", microtime());
   return ((float)$usec + (float)$sec);
}
$start = microtime_float();
$s = new server('192.168.0.254', 12580);
$res1 = $s->object('Too')->test('a', 'b');
$res2 = $s->object('Too')->test('a', 'b');
$end = microtime_float();
echo $res1.$res2.'
'; echo 'cost:'.($end-$start);//速度在0.002秒左右

      怎么样,好用不,你需要做的就是完成自己的接口类,如测试中的Too类,而php什么都不需要关心,只需要知道服务的地址和端口,及接口名和相应方法就ok了。当然这只是一个最简单原型,可能有些问题没有考虑,需要大家指出在完善。

07 / 06 / 2011 buling

      因为个人中心准备改版,我们也想做微博化用户体验,所以这几天一直在实现一个简单的demo,俩功能:发布和关注,主要是想以这个demo做后端存储选型,今天一天做的事情很少,花了大部分时间再handlersocket上,网上的资料比较有限,自写的demo就更是少了。一天不容易,总结一下,希望看到此文的你能有所获。

第一句代码

    $hs = new HandlerSocket(DB_HOST, DB_PORT);

      如果你的服务没有安装handlersocket,下载一个handlersocket.so拷贝到/usr/lib/php/modules/之下,在/etc/php.ini中添加一行extension = handlersocket.so;重启httpd服务。当然mysql服务器安装handlersocket就不多说了,网上教程很多。
      DB_HOST:mysql服务器地址,如:127.0.0.1(localhost);DB_PORT:连接端口,一般有两个端口(除了mysql服务端口,默认为3306),一个是只读端口(默认9998),一个是读写端口(默认9999),一般而言,对于查询使用只读端口,增删改用读写端口。

第二句代码

    $hs->openIndex($id, DB_NAME, $table, $index, $fields);

      这还没开始处理逻辑,相当于操作前的一个预加载过程。$id即HandlerSocket ID:1 SELECT, 2 UPDATE, 3 INSERT, 4 DELETE。DB_NAME就没什么说的,数据库名;$table为表名;$index为索引名,主要是为后面操作中的where条件中使用,eg: 指定为主键:HandlerSocket::PRIMARY;$fields 操作中涉及到的字段名(多个字段名,用逗号分隔),可为空。如果你有疑惑,请继续往后面阅读。其返回值为true/false,如果是false,可使用$hs->getError()获取错误信息。下面开始讲增、删、查、改。

第三句代码-查询语句

    $hs->executeSingle(1, $operation, $fields, $number, $skip);

      1是指HandlerSocket ID,此处不再赘述;$operation为where条件中的判断标识,有如下可选项:‘=’, ‘>=’, ‘<=', '>‘, ‘<', '+';$fields为where语句中出现的值,跟表index索引顺序一致,如你建了一个test的索引,包含两列(id, name),那么openIndex中的index值应为test,此处的fields为array(2, 'jaychou'),同where id=2 and name='jaychou';$number为取出到少条数据,同limit m,n中的n;$skip为偏移量,同limit m, n中的m。

第四句代码-增加

    $hs->executeInsert(3, $row);

      $row是指插入的数据列,同openIndex中的$columns一一对应。

第五句代码-删除

    $hs->executeDelete(4, $operation, $fields, $number, $skip);

      这几个值选项同上面的说明,就不啰嗦了。

第六句代码-更改

    $hs->executeUpdate(2, $operation, $fields, $values, $number, $skip);

      在上面的代码中只多了一个$values:UPDAET 时指定修改的值。

最后给大家推荐两篇文章:
1、google的php说明文档:http://code.google.com/p/php-handlersocket/wiki/Classes
2、一篇很不错的学习笔记:http://www.livingelsewhere.net/2011/06/02/php-extension-for-interfacing-with-mysql-handler-socket

最后发一段粗糙的类,眼前用而已:

 12, 'f_uname' => 'too');
 * $db->insert($row);
 *
 * #读取操作
 * #取出的列、索引名、条件关系符、条件对应的数据(与索引顺序一致 )、limit $skip, $number
 * $db->query($columns, $index, '=', $fields, $number=1, $skip=0);
 *
 * #更新操作
 * $data = array('f_uid' => 13, 'f_uname' => 'tooo');
 * #更新数据、索引、条件关系符、条件对应的数据(与索引顺序一致 )、limit $skip, $number
 * $db->update($data, $index, $operation, $fields, $number=1, $skip=0);
 *
 * #删除操作
 * #索引、条件关系符、条件对应的数据(与索引顺序一致 )、limit $skip, $number
 * $db->delete($index, $operation, $fields, $number=1, $skip=0)
 *
 * @author tuyl
 *
 */
class HSMysql{
    protected static $_db, $_rdb;
    protected $_table;
    public function __construct($t_name){
        $this->setTable($t_name);
    }
    public function setTable($t_name){
        $this->_table = DB_PREFIX.$t_name;
    }
    public function getInstance($readonly = 1){
        if($readonly){
            if(!self::$_rdb){
                self::$_rdb = new HandlerSocket(DB_HOST, DB_HS_PORT);
            }
            return self::$_rdb;
        }else{
            if(!self::$_db){
                self::$_db = new HandlerSocket(DB_HOST, DB_HS_WRPORT);
            }
            return self::$_db;
        }
    }
    public function openIndex($id, $index = '', $columns = array(), $readonly = 0){
        $cls = implode(',', $columns);
        $db = $this->getInstance($readonly);
        if (!$db->openIndex($id, DB_NAME, $this->_table, $index, $cls)){
            $this->_error('open index :'.$hs->getError());
        }
        return $db;
    }
    public function query($columns, $index, $operation, $fields, $number=1, $skip=0){
        $hs = $this->openIndex(1, $index, $columns, 1);
        $res = $hs->executeSingle(1, $operation, $fields, $number, $skip);
        if($res === false){
            $this->_error('query error:'.$hs->getError());
        }
        return $res;
    }
    public function insert($rows){
        $hs = $this->openIndex(3, HandlerSocket::PRIMARY, array_keys($rows));
        $f = $hs->executeInsert(3, array_values($rows));
        if($f === false){
            $this->_error('executeInsert :'.$hs->getError());
        }
        return $f;
    }
    public function update($data, $index, $operation, $fields, $number=1, $skip=0){
        $hs = $this->openIndex(2, $index, array_keys($data));
        return $hs->executeUpdate(2, $operation, $fields, array_values($data), $number, $skip);
    }
    public function delete($index, $operation, $fields, $number=1, $skip=0){
        $hs = $this->openIndex(4, $index, '');
        return $hs->executeDelete(4, $operation, $fields, $number, $skip);
    }
    public function asc($data, $index){
        return $this->_sort($data, $index, SORT_ASC);
    }
    public function desc($data, $index){
        return $this->_sort($data, $index, SORT_DESC);
    }
    private function _sort($data, $index, $sort){
        if(!$data || !is_array($data)) return $data;
        $args = array();
        foreach ($data as $k => $v) {
            $args[] = $v[$index];
        }
        array_multisort($args, $sort, $data);
        return $data;
    }
    private function _error($msg){
        echo $msg;
        exit();
    }
}

      类中提供了desc和asc方法是为query提供的,因为HandlerSocket不支持order by操作,所以这里用PHP实现。
      HandlerSocket的确很好用,首先nosql,少去了sql解析花费的时间,其次数据能持久化,当nosql不能实现时可以用sql进行弥补。感觉比单纯memcache+mysql实现好,demo主要是在显示用户当前最新n条微博消息时使用了nosql,后期会针对发布异步化,消息表按时拆分,对用户关注量分别采取push和pull两种方式做优化。

无标签信息 2 条