Archive for September, 2011

作者:AngryFox 分类: Uncategorized September 27th, 2011 暂无评论

PHP语言也有保证数据安全的函数存在,他们的合理运用可以帮助我们实现数据加密功能,提高程序的安全性。PHP加密解密函数authcode.authcode 是使用异或运算进行加密和解密。
原理如下,假如:
加密
明文:1010 1001
密匙:1110 0011
密文:0100 1010

得出密文0100 1010,解密之需和密匙异或下就可以了

解密
密文:0100 1010
密匙:1110 0011
明文:1010 1001

并没有什么高深的算法,密匙重要性很高,所以,关键在于怎么生成密匙。

 

 // 参数解释
// $string: 明文 或 密文
// $operation:DECODE表示解密,其它表示加密
// $key: 密匙
// $expiry:密文有效期
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
    // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;

    // 密匙
    $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);

    // 密匙a会参与加解密
    $keya = md5(substr($key, 0, 16));
    // 密匙b会用来做数据完整性验证
    $keyb = md5(substr($key, 16, 16));
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
    // 参与运算的密匙
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
    // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
    $string_length = strlen($string);
    $result = '';
    $box = range(0, 255);
    $rndkey = array();
    // 产生密匙簿
    for($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }
    // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
    for($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    // 核心加解密部分
    for($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        // 从密匙簿得出密匙进行异或,再转成字符
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if($operation == 'DECODE') {
        // substr($result, 0, 10) == 0 验证数据有效性
        // substr($result, 0, 10) - time() > 0 验证数据有效性
        // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
        // 验证数据有效性,请看未加密明文的格式
        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
            return substr($result, 26);
        } else {
            return '';
        }
    } else {
        // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
        // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
        return $keyc.str_replace('=', '', base64_encode($result));
    }
}
作者:AngryFox 分类: Uncategorized September 21st, 2011 暂无评论

打开discuzx2后台会执行这个websehll

  http://customer.discuz.net/news.php?os=dx&update=dW5pcXVlaWQ9RFhMSkJKakk5ZjUxcHVXWCZ2ZXJzaW9uPVgyJnJlbGVhc2U9MjAxMTA4MTcmcGhwPTUuMy4zJm15c3FsPTUuNS44LWxvZyZjaGFyc2V0PXV0Zi04JmJibmFtZT0lRTclQTAlOTQlRTclQTklQjYlRTYlODklODAlRTclQTQlQkUlRTUlOEMlQkEmbWFzdGVybW9iaWxlPSZzb2Z0d2FyZT1BcGFjaGUlMkYyLjIuMTclMjAlMjhXaW4zMiUyOSUyMFBIUCUyRjUuMy4zJg%3D%3D&md5hash=d9cba4d1&timestamp=1316567858

这个shell在source/function/function_admincp.php中896-912之间

	if($_G['adminid'] == 1 && $_GET['action'] == 'index') {
		$release = DISCUZ_RELEASE;

		$newsurl =  'ht'.'tp:/'.'/cus'.'tome'.'r.disc'.'uz.n'.'et/n'.'ews'.'.p'.'hp?'.siteinformation();
		echo <<<EOT
<script type="text/javascript">
	var newhtml = '';
	newhtml += '<table class="tb tb2"><tr><th class="partition edited">&#x60A8;&#x5F53;&#x524D;&#x4F7F;&#x7528;&#x7684 Discuz! &#x7A0B;&#x5E8F;&#x7248;&#x672C;&#x6709;&#x91CD;&#x8981;&#x66F4;&#x65B0;&#xFF0C;&#x8BF7;&#x53C2;&#x7167;&#x4EE5;&#x4E0B;&#x63D0;&#x793A;&#x8FDB;&#x884C;&#x53CA;&#x65F6;&#x5347;&#x7EA7</th></tr>';
	newhtml += '<tr><td class="tipsblock"><a href="http://faq.comsenz.com/checkversion.php?product=Discuz&version={$version}&release={$release}&charset={$charset}" target="_blank"><img src="{$newsurl}" onload="shownews()" /></a></td></tr></table>';
	\$('boardnews').style.display = 'none';
	\$('boardnews').innerHTML = newhtml;
	function shownews() {
		\$('boardnews').style.display = '';
	}
</script>
EOT;
	}

现在可以直接叉掉了
在\uc_server\control\admin中的frame.php34-36有

		$ucinfo = '<sc'.'ript language="Jav'.'aScript" src="ht'.'tp:/'.'/cus'.'tome'.'r.disc'.'uz.n'.'et/ucn'.'ews'.'.p'.'hp?'.$this->_get_uc_info().'"></s'.'cri'.'pt>';
		$this->view->assign('ucinfo', $ucinfo);
作者:AngryFox 分类: Uncategorized September 13th, 2011 暂无评论

Web系统中,从一个网页跳转到另一个网页,是LAMP项目中最常用的技术之一。页面跳转可能是由于用户单击链接、按钮等引发的,也可能是系统自动产生的。 此处介绍PHP中常用的实现页面自动跳转的方法。

PHP页面跳转一、header()函数

header()函数是PHP中进行页面跳转的一种十分简单的方法。header()函数的主要功能是将HTTP协议标头(header)输出到浏览器。

header()函数的定义如下:

void header (string string [,bool replace [,int http_response_code]])

可选参数replace指明是替换前一条类似标头还是添加一条相同类型的标头,默认为替换。

第二个可选参数http_response_code强制将HTTP相应代码设为指定值。 header函数中Location类型的标头是一种特殊的header调用,常用来实现页面跳转。注意:1.location和“:”号间不能有空格,否则不会跳转。

2.在用header前不能有任何的输出。

3.header后的PHP代码还会被执行。例如,将浏览器重定向到lamp兄弟连官方论坛
< ?php //重定向浏览器 header("Location: http://bbs.
lampbrother.net"); //确保重定向后,后续代码不会被执行 exit;?>
PHP页面跳转二、Meta标签

Meta标签是HTML中负责提供文档元信息的标签,在PHP程序中使用该标签,也可以实现页面跳转。 若定义http-equiv为refresh,则打开该页面时将根据content规定的值在一定时间内跳转到相应页面。

若设置content=”秒数;url=网址”,则定义了经过多长时间后页面跳转到指定的网址。例如,使用meta标签实现疫苗后页面自动跳转到LAMP兄弟连官方论坛。
< meta http-equiv="refresh" content="1;url=http://
bbs.lampbrother.net">
例如,以下程序meta.php实现在该页面中停留一秒后页面自动跳转到bbs.lampbrother.net。
< ?php $url = "http://bbs.lampbrother.net"; ?> < html> < head> < meta http-equiv="refresh" content="1; url=< ?php echo $url; ?>“> < /head> < body> 页面只停留一秒…… < /body> < /html>
PHP页面跳转三、JavaScript

例如,此代码可以放在程序中的任何合法位置。
< ?php $url = "http://bbs.lampbrother.net"; echo "< script language='javascript' type='text/javascript'>“; echo “window.location.href=’$url’”; echo “< /script>“; ?>
以上就是我们向大家介绍的三种PHP页面跳转实现方法。

作者:AngryFox 分类: Uncategorized September 13th, 2011 暂无评论

Fatal error: Maximum execution time of 30 seconds exceeded in a.php
这是一个30秒运行超时的错误。

出现这个错误可能是你的PHP程序出现了不希望出现的死循环。 改掉该死循环一般就可以了。

另外就是你本来就希望执行很长时间,那么就需要修改php.ini来延长最大执行时间。这有两个方法:

第一种是修改php.ini:
max_execution_time = 300 ,单位是秒,可以设置更大,然后重启apache或者iis

第二种方法是在php文件里调用以下函数:
set_time_limit(时间) //0为无限制

php有执行时间限制。。。
ini_set(‘max_execution_time’, ‘xxxxx’);

作者:AngryFox 分类: Uncategorized September 11th, 2011 暂无评论

而且所有整合的程序同步登录,同步退出,同步修改密码。最终用户可以通过它轻松通行在各个应用之中,无需重复登录、注册、退出

一个id可以出入一个站内的所有程序,如cms和bbs,也可以一个id出入www.a.com和www.b.com及www.c.com

a.com的用户可以和b.com的用户互发短消息pm,a.com的用户可以和b.com的用户加好友。

而且可以实现站内信(pm)和其它任意站的任意程序的站内信(pm)互通。

可以实现www.a.com与[url]www.b.com及[url=http://www.c.com]www.c.com[/url][/url]共享一个用户库,www.a.com的用户可以给[url=http://www.b.com]www.b.com[/url]的用户pm短信.

用户组与discuz不对应的问题也得到解决。因为一般应用程序的用户组是单独的一个应用,和discuz再没有关系,而是和ucenter有关系,而ucenter没有用户组的概念。

二、整合方法:

如果单说整合用户的话,整合时应用程序的改动也非常小,原数据库不用动,原写cookies的代码不用动,原写数据库session的代码不用动,原来的程序不用怎么动,只需改动以下4个文件:

longin.php register.php logout.php 修改密码文件.php (忘记密码.php不用动,用原来的就可以)

4个文件中加上和ucenter api通信的语句和逻辑结构。

另需要针对ucenter新增加一个文件uc.php,这个文件就是应用程序接收ucenter传来的指令并执行的文件。而且他利用p3p技术实现反向登录a.com或是同时反向登录a.com/b.com/c.com的dedecms或是phpcms或是任意所有程序。

共计改4个文件,增一个文件。

如果要是整合站内信pm,好友,头像等功能,思路与方法和整合用户类同。

另外有一点,在不同的系统之间注册的用户,在第一次登录这个从来没有登录过的系统时,会让激活。借用这个激活,可以让用户完善在本应用程序中的资料,如企业注册资料。资料不完善,不可以激活。

如在bbs注册一个用户test,第一次来到b2b的程序中,这时,并不会自动登录,而是要求用户激活,而test在bbs注册时填的字段与b2b中要求的不同。这时让用户完善资料。第二次来就会自动登录。

三、 uc原理:

以用户登录为例介绍,其它注销,改密码,消息,头像,好友均类同。

1.

从用户xxx在某一应用程序的login.php,输入用户名,密码讲起。

先用uc_user_login函数到uc server验证此用户和密码,如正确,则写入session,写入cookies,并更新应用程序会员表中的登录ip,登录时间。用户感觉不到这个过程。

2.

然后通过uc_user_synlogin通知uc server 用户xxx登录成功,这个过程可能使用ajax,用户感觉不到通知过程。

3.

uc server收到这个消息后,马上命令手下,把xxx登录的消息,像令牌环一样,发给所有愿意接收(后台中那个是否开启同步登录)这个消息的其它应用程序。其实就是带参数访问一下各应用程序的uc.php,用户感觉不到这个过程。

4.

各应用程序靠api下的uc.php来接收uc server发来的消息,并对uc server言听计从,让干什么就干什么。现在,收到让xxx用户在你的程序中登录的命令,马上执行。

并写本应用程序的session,并且使用p3p, 写入相同域或不同域的cookies. 用户感觉不到这个过程。

5.最后所有和uc整合的程序,xxx均登录成功。用户从www.easway.net/home登录后, 跳到www.easway.net/News同样显示登录。因为bbs 和news系统在后台均已登录。

6.应用程序与uc server的会话结束。

作者:AngryFox 分类: Uncategorized September 11th, 2011 暂无评论

1,用户登录bbs,通过logging.php文件中,使用函数uc_user_login验证,如果验证成功,将调用函数uc_user_synlogin(位于uc_client下的client.php文件中),在这个函数中调用 uc_api_post(‘user’, ‘synlogin’, array(‘uid’=>$uid));之后向UC_API.’/index.php’传递了数据;这里的UC_API就是在config.inc.php中的定义的uc_server之URL地址

2,uc_server的index.php接受参数数据,获得model为user,action为synlogin,就调用control目录下的user.php类中的onsynlogin方法,通过foreach循环,以javascript的方式通知uc应用列表中的应用同步登录;即通过get方式传递给应用目录中api下的uc.php一些数据;

3,uc.php接收通知并处理get过来的数据,并在函数synlogin(位于uc.php中)通过函数_authcode加密数据(默认以UC_KEY作为密钥),用函数_setcookie设置cookie;

4,各个应用在适当的文件中用对应的密钥解码上面设置的cookie,得到用户id等数据;通过这个值来判断用户是否经过其它应用登录过;

以discuz举例:

一、用户登录检查与用户登录验证logging.php

在bbs的logging.php中如下代码段

} elseif($action == ‘login’) {

if($discuz_uid) {

$ucsynlogin = ”;

showmessage(‘login_succeed’, $indexname);

}

检查用户id变量$discuz_uid是否为空来判断,用户是否登录(包括从别的应用登录。)

如果用户从bbs登录,则在登录验证成功后通过如下代码:

$ucsynlogin = $allowsynlogin ? uc_user_synlogin($discuz_uid) : ”;

通知其它应用—-“用户已从bbs登录,请通知其它应用设置cookie”

(uc_server通过javascript调用方式向其它应用的api/uc.php传递数据)

可以在uc应用目录下新建一个名为test.php的文件,来模拟登录成功,请求uc_server通知其它应用。文件内容为:

———————文件内容开始———————-

include_once "config.inc.php";

include_once "./uc_client/client.php";

echo uc_user_synlogin(1);

echo "

“;

var_dump($_COOKIE);

echo “

";

?>

---------------------文件内容结束----------------------

ps:这段测试代码还可以测试同步登录不好使的情况,具体使用方法,你可以思考一下(本文后面也有介绍),有问题可以在此文结尾发表评论与我讨论。

运行后,查看源代码即可看到javascript;

这里要注意了:这些javascript的通知中是不包含用户登录的应用的。也就是说只"通知"用户未登录的应用,因为用户通过uc_server登录成功的当前应用,当然不需要uc_server再通知了。具体代码请参看:webroot\uc_server\control\user.php中的onsynlogin函数的这句:

if($app['synlogin'] && $app['appid'] != $this->app['appid'])

代码解释:

$app['synlogin']是uc应用是否允许同步登录

而且应用id不等于用户当前登录的应用id

$app数组就是uc_server\data\cache\apps.php中的数组$_CACHE['apps'];

$this->app就是用户登录的应用

二、接受其它应用的同步登录通知:

在discuz的api目录下的uc.php中的函数synlogin,在这里接受uc_server发送过来的“同步登录通知”,并设置discuz的cookie,在这个函数中你可以查看到cookie的加密密钥的“算法”;

如果你想看看uc_server发送过的的“通知”是什么数据,你可以这么做:

1,修改要接受通知的应用目录下的api\uc.php,在$action = $get['action'];代码下面添加如下代码:

echo "

";var_dump($get);echo "

";die("


api\uc.php");

2,将上面建立的test.php文件放置在其它允许同步登录的应用目录下,并在浏览器中运行,然后点击页面中对应第一步的应用链接,即可看到uc_server“通知”给改应用的数据;

---------------------------分割线-------------------------------

function synlogin($get, $post)

在这个函数中通过_authcode函数,以密钥$discuz_auth_key加密了cookie;

在这里为了避免cookie名称冲突,在cookie名称(一般为:auth)前加了前缀($cookiepre),这个前缀也就是在config.inc.php中设置的那个cookie前缀值;

请看设置cookie的函数_setcookie:

(通过参数$prefix来判断是否对cookie名称添加前缀$cookiepre)

function _setcookie($var, $value, $life = 0, $prefix = 1) {

global $cookiepre, $cookiedomain, $cookiepath, $timestamp, $_SERVER;

setcookie(($prefix ? $cookiepre : '').$var, $value,

$life ? $timestamp + $life : 0, $cookiepath,

$cookiedomain, $_SERVER['SERVER_PORT'] == 443 ? 1 : 0);

}

密钥“算法”:

$discuz_auth_key= md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);

也就是不同用户加密cookie的密钥可能不同;

三、检查用户是否已登录(无论是那个应用下登录):

discuz的include目录中common.inc.php中有这样的代码:

$discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);

list($discuz_pw, $discuz_secques, $discuz_uid) = empty($_DCOOKIE['auth']) ? array('', '', 0) : daddslashes(explode("\t", authcode($_DCOOKIE['auth'], 'DECODE')), 1);

这段代码就是解码在uc.php中用密钥($discuz_auth_key)加密的cookie值,以获得用户id($discuz_uid)这里的解密函数位于bbs\include\global.func.php中,虽然未给函数传递cookie密钥,但函数中通过全局变量$GLOBALS['discuz_auth_key'])获得密钥。

作者:AngryFox 分类: Uncategorized September 11th, 2011 暂无评论

Discuz X2的ucenter 1.6很多功能是用ajax来封装实现的,做到了无刷新和局部刷新的作用。在uc_client到uc_server中用了socket通信,目前测试不是很稳定,经常会发生 通知失败,我在client.php中75行左右用到了http替换socket,算是一种退步吧

function uc_api_requestdata($module, $action, $arg='', $extra='') {
	$input = uc_api_input($arg);
	$post = "m=$module&a=$action&inajax=2&release=".UC_CLIENT_RELEASE."&input=$input&appid=".UC_APPID.$extra;
	$url =  UC_API.'/index.php?'.$post;
	echo "<meta http-equiv=refresh content='0; url=$url'>";
//	exit;
	return $post;
}

结合Discuz X2用户登陆流程源码来分析整个Ucenter的通信流程原理步骤。
1. 当会员从登陆界面输入正确的用户名和密码单击登陆按钮后,我们看到打开源码discuzx/source/class/class_member.php 找到第155行代码.

$ucsynlogin = $this->setting['allowsynlogin'] ? uc_user_synlogin($_G['uid']) : ''; 

从上面的代码可以看到,当会员登陆验证通过后, 当你在ucenter管理面板里面设置开启同步登陆后,X2将会调用uc_user_synlogin($_G['uid']) 方法发出同步登陆的通知。
2. 调用uc_user_synlogin($_G['uid']) 方法后,实际调用的是discuzx/uc_client/client.php 中的 uc_user_synlogin函数,我们打开源码看方法签名,在310 – 320 之间:

    function uc_user_synlogin($uid) {
                $uid = intval($uid);
                if(@include UC_ROOT.'./data/cache/apps.php') {
                        if(count($_CACHE['apps']) > 1) {
                                $return = uc_api_post('user', 'synlogin', array('uid'=>$uid));
                        } else {
                                $return = '';
                        }
                }
                return $return;
        } 

跟着这个方法流程,我们继续看 uc_api_post 方法签名, 仍然是在discuzx/uc_client/client.php文件,找到54-74行

    function uc_api_post($module, $action, $arg = array()) {
                $s = $sep = '';
                foreach($arg as $k => $v) {
                        $k = urlencode($k);
                        if(is_array($v)) {
                                $s2 = $sep2 = '';
                                foreach($v as $k2 => $v2) {
                                        $k2 = urlencode($k2);
                                        $s2 .= "$sep2{$k}[$k2]=".urlencode(uc_stripslashes($v2));
                                        $sep2 = '&';
                                }
                                $s .= $sep.$s2;
                        } else {
                                $s .= "$sep$k=".urlencode(uc_stripslashes($v));
                        }
                        $sep = '&';
                }
                $postdata = uc_api_requestdata($module, $action, $s);
                return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);
        }  

可以看到这个方法主要调用

uc_api_requestdata

来组装一个post请求需要发送的数据,看这个方法的签名

    function uc_api_requestdata($module, $action, $arg='', $extra='') {
               $input = uc_api_input($arg);
               $post = "m=$module&a=$action&inajax=2&release=".UC_CLIENT_RELEASE."&input=$input&appid=".UC_APPID.$extra;
               return $post;
       }  

注意看uc_fopen2(UC_API.’/index.php’, 500000, $postdata, ”, TRUE, UC_IP, 20)函数中的 UC_API和 UC_APPID常量实际就是我们在 discuzx/config/config_ucenter.php 文件中配置的预定的常量,相信到这里大家已经明白了这几个常量的用意,UC_API就是你定义的uc_server的URL.

return uc_fopen2(UC_API.’/index.php’, 500000, $postdata, ”, TRUE, UC_IP, 20);

define('UC_API', 'http://www.itkuaixun.com/xxxx');  //uc_server地址
   define('UC_APPID', '1'); 

3. 接着看uc_fopen2(UC_API.’/index.php’, 500000, $postdata, ”, TRUE, UC_IP, 20) 方法签名和调用原理

    function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {
                $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;
                if($__times__ > 2) {
                        return '';
                }
                $url .= (strpos($url, '?') === FALSE ? '?' : '&')."__times__=$__times__";
                return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block);
        }  

最终由uc_fopen函数调用PHP函数fsockopen 或者 pfsockopen 打开一个socket 连接将数据用流的形式发送通知数据到uc_server

    function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {
                $return = '';
                $matches = parse_url($url);
                !isset($matches['host']) && $matches['host'] = '';
                !isset($matches['path']) && $matches['path'] = '';
                !isset($matches['query']) && $matches['query'] = '';
                !isset($matches['port']) && $matches['port'] = '';
                $host = $matches['host'];
                $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/';
                $port = !emptyempty($matches['port']) ? $matches['port'] : 80;
                if($post) {
                        $out = "POST $path HTTP/1.0\r\n";
                        $out .= "Accept: */*\r\n";
                        //$out .= "Referer: $boardurl\r\n";
                        $out .= "Accept-Language: zh-cn\r\n";
                        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
                        $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
                        $out .= "Host: $host\r\n";
                        $out .= 'Content-Length: '.strlen($post)."\r\n";
                        $out .= "Connection: Close\r\n";
                        $out .= "Cache-Control: no-cache\r\n";
                        $out .= "Cookie: $cookie\r\n\r\n";
                        $out .= $post;
                } else {
                        $out = "GET $path HTTP/1.0\r\n";
                        $out .= "Accept: */*\r\n";
                        //$out .= "Referer: $boardurl\r\n";
                        $out .= "Accept-Language: zh-cn\r\n";
                        $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
                        $out .= "Host: $host\r\n";
                        $out .= "Connection: Close\r\n";
                        $out .= "Cookie: $cookie\r\n\r\n";
                }
                if(function_exists('fsockopen')) {
                        $fp = @fsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout);
                } elseif (function_exists('pfsockopen')) {
                        $fp = @pfsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout);
                } else {
                        $fp = false;
                }
                if(!$fp) {
                        return '';
                } else {
                        stream_set_blocking($fp, $block);
                        stream_set_timeout($fp, $timeout);
                        @fwrite($fp, $out);
                        $status = stream_get_meta_data($fp);
                        if(!$status['timed_out']) {
                                while (!feof($fp)) {
                                        if(($header = @fgets($fp)) && ($header == "\r\n" ||  $header == "\n")) {
                                                break;
                                        }
                                }
                                $stop = false;
                                while(!feof($fp) && !$stop) {
                                        $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
                                        $return .= $data;
                                        if($limit) {
                                                $limit -= strlen($data);
                                                $stop = $limit <= 0;
                                        }
                                }
                        }
                        @fclose($fp);
                        return $return;
                }
        }  

至此,uc_client的调用流程结束,转入uc_server部份.

4. 继续打开

discuzx/uc_server/index.php

部份,找到52行,我们就可以完全理解ucenter的内部通知机制,当接收到

uc_fopen2(UC_API.’/index.php’, 500000, $postdata, ”, TRUE, UC_IP, 20) 方法发送过来的请求后,

    if(in_array($m, array('app', 'frame', 'user', 'pm', 'pm_client', 'tag', 'feed', 'friend', 'domain', 'credit', 'mail', 'version'))) {
                if(file_exists(UC_ROOT.RELEASE_ROOT."control/$m.php")) {
                        include UC_ROOT.RELEASE_ROOT."control/$m.php";
                } else {
                        include UC_ROOT."control/$m.php";
                }
                $classname = $m.'control';
                $control = new $classname();
                $method = 'on'.$a;
                if(method_exists($control, $method) && $a{0} != '_') {
                        $data = $control->$method();
                        echo is_array($data) ? $control->serialize($data, 1) : $data;
                        exit;
                } elseif(method_exists($control, '_call')) {
                        $data = $control->_call('on'.$a, '');
                        echo is_array($data) ? $control->serialize($data, 1) : $data;
                        exit;
                } else {
                        exit('Action not found!');
                }
        } else {
                exit('Module not found!');
        }  

在这个方法里面查找对应的通知模块,这里我们以用户同步登陆为例,其它原理都是一样的,所以这里实际调用的是user我们接着打开 discuzx/uc_server/control/user.php 文件源码, 在第32行开始,可以看到最终它调用onsynlogin方法,查询缓存的所有开启同步通知的应用,

    // -1 未开启
                function onsynlogin() {
                        $this->init_input();
                        $uid = $this->input('uid');
                        if($this->app['synlogin']) {
                                if($this->user = $_ENV['user']->get_user_by_uid($uid)) {
                                        $synstr = '';
        //这里循环从缓存中读取所有需要发送通知的应用
                                        foreach($this->cache['apps'] as $appid => $app) {
                                                if($app['synlogin']) {
                                                        $synstr .= '<script type="text/javascript" src="'.$app['url'].'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1"></script>';
                                                        if(is_array($app['extra']['extraurl'])) foreach($app['extra']['extraurl'] as $extraurl) {
                                                                $synstr .= '<script type="text/javascript" src="'.$extraurl.'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1"></script>';
                                                        }
                                                }
                                        }
                                        return $synstr;
                                }
                        }
                        return '';
                }  

使用Firefox安装好firebug可以看到,这就是为什么返回我们看到返回的一段javascript代码,如果不成功将返回-1(false) ,

$app['url'].’/api

看到这段代码,这也是为什么ucenter API文档中定义我们必须在根目录下面创建一个api文件夹, 使用P3P协议来解决cookie发送的问题.到这里相信你已经明白整个UCenter的运行流程和原理,赶紧动手去整合一个。

作者:AngryFox 分类: Uncategorized September 11th, 2011 暂无评论

// 设置一些基本的变量
$host = “192.168.1.99″;
$port = 1234;
// 设置超时时间
set_time_limit(0);
// 创建一个Socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die(“Could not createsocket\n”);
//绑定Socket到端口
$result = socket_bind($socket, $host, $port) or die(“Could not bind tosocket\n”);
// 开始监听链接
$result = socket_listen($socket, 3) or die(“Could not set up socketlistener\n”);
// accept incoming connections
// 另一个Socket来处理通信
$spawn = socket_accept($socket) or die(“Could not accept incomingconnection\n”);
// 获得客户端的输入
$input = socket_read($spawn, 1024) or die(“Could not read input\n”);
// 清空输入字符串
$input = trim($input);
//处理客户端输入并返回结果
$output = strrev($input) . “\n”;
socket_write($spawn, $output, strlen ($output)) or die(“Could not write
output\n”);
// 关闭sockets
socket_close($spawn);
socket_close($socket);

下面是其每一步骤的详细说明:

1.第一步是建立两个变量来保存Socket运行的服务器的IP地址和端口.你可以设置为你自己的服务器和端口(这个端口可以是1到65535之间的数字),前提是这个端口未被使用.
[Copy to clipboard]
PHP CODE:
// 设置两个变量
$host = “192.168.1.99″;
$port = 1234;

2.在服务器端可以使用set_time_out()函数来确保PHP在等待客户端连接时不会超时.
[Copy to clipboard]
PHP CODE:
// 超时时间
set_time_limit(0);

3.在前面的基础上,现在该使用socket_creat()函数创建一个Socket了—这个函数返回一个Socket句柄,这个句柄将用在以后所有的函数中.
[Copy to clipboard]
PHP CODE:
// 创建Socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die(“Could not create
socket\n”);

第一个参数”AF_INET”用来指定域名;
第二个参数”SOCK_STREM”告诉函数将创建一个什么类型的Socket(在这个例子中是TCP类型)

因此,如果你想创建一个UDP Socket的话,你可以使用如下的代码:
[Copy to clipboard]
PHP CODE:
// 创建 socket
$socket = socket_create(AF_INET, SOCK_DGRAM, 0) or die(“Could not create
socket\n”);

4.一旦创建了一个Socket句柄,下一步就是指定或者绑定它到指定的地址和端口.这可以通过socket_bind()函数来完成.
[Copy to clipboard]
PHP CODE:
// 绑定 socket to 指定地址和端口
$result = socket_bind($socket, $host, $port) or die(“Could not bind to
socket\n”);

5.当Socket被创建好并绑定到一个端口后,就可以开始监听外部的连接了.PHP允许你由socket_listen()函数来开始一个监听,同时你可以指定一个数字(在这个例子中就是第二个参数:3)
[Copy to clipboard]
PHP CODE:
// 开始监听连接
$result = socket_listen($socket, 3) or die(“Could not set up socket
listener\n”);

6.到现在,你的服务器除了等待来自客户端的连接请求外基本上什么也没有做.一旦一个客户端的连接被收到,socket_accept()函数便开始起作用了,它接收连接请求并调用另一个子Socket来处理客户端–服务器间的信息.
[Copy to clipboard]
PHP CODE:
//接受请求链接
// 调用子socket 处理信息
$spawn = socket_accept($socket) or die(“Could not accept incoming
connection\n”);

这个子socket现在就可以被随后的客户端–服务器通信所用了.

7.当一个连接被建立后,服务器就会等待客户端发送一些输入信息,这写信息可以由socket_read()函数来获得,并把它赋值给PHP的$input变量.
[Copy to clipboard]
PHP CODE:
// 读取客户端输入
$input = socket_read($spawn, 1024) or die(“Could not read input\n”);
?>

socker_read的第而个参数用以指定读入的字节数,你可以通过它来限制从客户端获取数据的大小.

注意:socket_read函数会一直读取壳户端数据,直到遇见\n,\t或者\0字符.PHP脚本把这写字符看做是输入的结束符.

8.现在服务器必须处理这些由客户端发来是数据(在这个例子中的处理仅仅包含数据的输入和回传到客户端).这部分可以由socket_write()函数来完成(使得由通信socket发回一个数据流到客户端成为可能)
[Copy to clipboard]
PHP CODE:
// 处理客户端输入并返回数据
$output = strrev($input) . “\n”;
socket_write($spawn, $output, strlen ($output)) or die(“Could not write
output\n”);

9.一旦输出被返回到客户端,父/子socket都应通过socket_close()函数来终止
[Copy to clipboard]
PHP CODE:
// 关闭 sockets
socket_close($spawn);
socket_close($socket);

———————————————————————————————-
socket_accept() 接受一个Socket连接
socket_bind() 把socket绑定在一个IP地址和端口上
socket_clear_error() 清除socket的错误或者最后的错误代码
socket_close() 关闭一个socket资源
socket_connect() 开始一个socket连接
socket_create_listen() 在指定端口打开一个socket监听
socket_create_pair() 产生一对没有区别的socket到一个数组里
socket_create() 产生一个socket,相当于产生一个socket的数据结构
socket_get_option() 获取socket选项
socket_getpeername() 获取远程类似主机的ip地址
socket_getsockname() 获取本地socket的ip地址
socket_iovec_add() 添加一个新的向量到一个分散/聚合的数组
socket_iovec_alloc() 这个函数创建一个能够发送接收读写的iovec数据结构
socket_iovec_delete() 删除一个已经分配的iovec
socket_iovec_fetch() 返回指定的iovec资源的数据
socket_iovec_free() 释放一个iovec资源
socket_iovec_set() 设置iovec的数据新值
socket_last_error() 获取当前socket的最后错误代码
socket_listen() 监听由指定socket的所有连接
socket_read() 读取指定长度的数据
socket_readv() 读取从分散/聚合数组过来的数据
socket_recv() 从socket里结束数据到缓存
socket_recvfrom() 接受数据从指定的socket,如果没有指定则默认当前socket
socket_recvmsg() 从iovec里接受消息
socket_select() 多路选择
socket_send() 这个函数发送数据到已连接的socket
socket_sendmsg() 发送消息到socket
socket_sendto() 发送消息到指定地址的socket
socket_set_block() 在socket里设置为块模式
socket_set_nonblock() socket里设置为非块模式
socket_set_option() 设置socket选项
socket_shutdown() 这个函数允许你关闭读、写、或者指定的socket
socket_strerror() 返回指定错误号的详细错误
socket_write() 写数据到socket缓存
socket_writev() 写数据到分散/聚合数组

作者:AngryFox 分类: Uncategorized September 10th, 2011 暂无评论

关键词高亮

function highlight($sString, $aWords) {
	if (!is_array ($aWords) || empty ($aWords) || !is_string ($sString)) {
		return false;
	}

	$sWords = implode ('|', $aWords);
 	return preg_replace ('@\b('.$sWords.')\b@si', '<strong style="background-color:yellow">$1</strong>', $sString);
}

自动生成密码

function generatePassword($length=9, $strength=0) {
	$vowels = 'aeuy';
	$consonants = 'bdghjmnpqrstvz';
	if ($strength >= 1) {
		$consonants .= 'BDGHJLMNPQRSTVWXZ';
	}
	if ($strength >= 2) {
		$vowels .= "AEUY";
	}
	if ($strength >= 4) {
		$consonants .= '23456789';
	}
	if ($strength >= 8 ) {
		$vowels .= '@#$%';
	}

	$password = '';
	$alt = time() % 2;
	for ($i = 0; $i < $length; $i++) {
		if ($alt == 1) {
			$password .= $consonants[(rand() % strlen($consonants))];
			$alt = 0;
		} else {
			$password .= $vowels[(rand() % strlen($vowels))];
			$alt = 1;
		}
	}
	return $password;
}

压缩多个CSS文件

header('Content-type: text/css');
ob_start("compress");
function compress($buffer) {
  /* remove comments */
  $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
  /* remove tabs, spaces, newlines, etc. */
  $buffer = str_replace(array("\r\n", "\r", "\n", "\t", '  ', '    ', '    '), '', $buffer);
  return $buffer;
}

/* your css files */
include('master.css');
include('typography.css');
include('grid.css');
include('print.css');
include('handheld.css');

ob_end_flush();

根据生日计算年龄

function age($date){
	$year_diff = '';
	$time = strtotime($date);
	if(FALSE === $time){
		return '';
	}

	$date = date('Y-m-d', $time);
	list($year,$month,$day) = explode("-",$date);
	$year_diff = date("Y") – $year;
	$month_diff = date("m") – $month;
	$day_diff = date("d") – $day;
	if ($day_diff < 0 || $month_diff < 0) $year_diff–;

	return $year_diff;
}

阻止CSS样式被缓存

<link href="/stylesheet.css?<?php echo time(); ?>" rel="stylesheet" type="text/css" /&glt;
作者:AngryFox 分类: Uncategorized September 10th, 2011 暂无评论
$xml = simplexml_load_string($result);
$_result = json_decode(json_encode($xml), true);
$xml = simplexml_load_string($result);
$_result = (array)$xml->children();