1、shtmlspecialchars
//取消HTML代码 function shtmlspecialchars($string) { if(is_array($string)) { foreach($string as $key => $val) { $string[$key] = shtmlspecialchars($val); } } else { $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/', '&\\1', str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string)); } return $string; }
以上代码的意思是将html里的敏感字符比如,<>&"都转义成类似于<这样的东西的。这个函数利用了一个递归的思想,是个人也能看懂。这里着重讲一下那个
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/', '&\\1', str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string));
后面的str_replace是把 array('&', '"', '<', '>')和array('&', '"', '<', '>')进行相对应的匹配替代。但是如果仅仅这样做是有问题的,比如我之前有一个表示"---",那么替换后就变成这个显然是不正确的,我们希望这种情况下保留原字符串。所以需要用'/&((#(\d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/'去匹配像这样的被替换过的字符串,将其还原成的形式。后面的"\\1"会匹配#127。
但是我有一个问题,为什么要先替换掉"&"呢?
2、saddslashes
//SQL ADDSLASHES function saddslashes($string) { if(is_array($string)) { foreach($string as $key => $val) { $string[$key] = saddslashes($val); } } else { $string = addslashes($string); } return $string; }
这里其实是为了sql安全考虑,防止sql注入。会把引号和双引号都转义成\"和\'的形式,这里还会转义其他字符,我就不多讲了。
3、ssetcookie
//cookie设置 function ssetcookie($var, $value, $life=0) { global $_SGLOBAL, $_SC, $_SERVER; setcookie($_SC['cookiepre'].$var, $value, $life?($_SGLOBAL['timestamp']+$life):0, $_SC['cookiepath'], $_SC['cookiedomain'], $_SERVER['SERVER_PORT']==443?1:0); }
这里%_SC['cookiepre']默认是uchome_,这个前缀表示这个cookie是和uchome相关的。那个life其实是一个unix时间戳,由于$_SGLOBAL['timestamp']是表示这段程序执行的时间,所以加上一个$life就表示过期的日子,这里是按照秒记的;如果是0的话,cookie的默认日期是当前浏览器,关闭就清除了。$_SC['cookiepath']是指有效的cookie的路径,如果你是设的“/”的话,然后又是localhost访问的,你会发现在你任何用localhost访问的网页都有这个cookie,最好还是设成你的项目路径如"/play",这里表示的是目录,其对其子目录也一样有效。后面的$_SC['cookiedomain']表示有效域名,这里如果你设成是www.example.com就表示只在www.example.com有效,如果你设成是.example.com就表示在子域名都有效。
4、dbconnect()
//数据库连接 function dbconnect() { global $_SGLOBAL, $_SC; include_once(S_ROOT.'./source/class_mysql.php'); if(empty($_SGLOBAL['db'])) { $_SGLOBAL['db'] = new dbstuff; $_SGLOBAL['db']->charset = $_SC['dbcharset']; $_SGLOBAL['db']->connect($_SC['dbhost'], $_SC['dbuser'], $_SC['dbpw'], $_SC['dbname'], $_SC['pconnect']); } }
这里面的那些变量其实都是在uchome里面的config.php配置的,每一次访问页面执行common.php的时候都会执行这个函数,以后每次执行数据库调用只要用$_SGLOBAL['db']就可以,这是一个封装数据库操作的类,有很多有用的方法。
其中的pcconnect(persistent connect)其实就是是否是否用数据连接池的意思(默认为0,也就是不用连接池),connect函数其有一段
if($pconnect) { if(!$this->link = @mysql_pconnect($dbhost, $dbuser, $dbpw)) { $halt && $this->halt('Can not connect to MySQL server'); } } else { if(!$this->link = @mysql_connect($dbhost, $dbuser, $dbpw, 1)) { $halt && $this->halt('Can not connect to MySQL server'); } }
默认的话会执行第else的那个子句,这里的那个1就是new_link的意思。也就是当用相同的参数连接数据库的时候总是会新建一个连接。而上面的那个mysql_pconnect应该就是利用连接池里的资源,对应$pconnect为1。
5、showmessage
function showmessage($msgkey, $url_forward='', $second=1, $values=array()) { global $_SGLOBAL, $_SC, $_SCONFIG, $_TPL, $space, $_SN; obclean(); //去掉广告 $_SGLOBAL['ad'] = array(); //语言 include_once(S_ROOT.'./language/lang_showmessage.php'); if(isset($_SGLOBAL['msglang'][$msgkey])) { $message = lang_replace($_SGLOBAL['msglang'][$msgkey], $values); } else { $message = $msgkey; } //手机 if($_SGLOBAL['mobile']) { include template('showmessage'); exit(); } //显示 if(empty($_SGLOBAL['inajax']) && $url_forward && empty($second)) { header("HTTP/1.1 301 Moved Permanently"); header("Location: $url_forward"); } else { if($_SGLOBAL['inajax']) { if($url_forward) { $message = "$message"; } //$message = " ".$_SGLOBAL['msglang']['box_title']."
X "; echo $message; ob_out(); } else { if($url_forward) { $message = "$message"; } include template('showmessage'); } } exit(); }
obclean()就是将之前的曾有过的输出echo print全部都清楚掉,因为这里肯定是页面要跳转了,所以之前的逻辑都不用考虑了,只需要重定向就行了。
在
showmessage.php里可以看到,有一些如
'no_privilege_friendnum' => '您需要添加 \\1 个好友之后,才能进行本操作,点这里添加好友', 'no_privilege_email' => '您需要验证激活自己的邮箱后才能进行本操作,点这里激活邮箱',
这里其实有一个本地化的思想,我可以写几种的showmessage.php,前面的键保留不变,因为那是逻辑部分,后面我可以写成法语或是马来西亚语,我只要挂在不同的地方用不同的php文件就可以了。
这里的
if(isset($_SGLOBAL['msglang'][$msgkey])) { $message = lang_replace($_SGLOBAL['msglang'][$msgkey], $values); } else { $message = $msgkey; }
比如我调用了showmessage("no_prililege_friendnum",'club.php',2,array(8))
首先会查看是否有no_prililege_friendnum这样的key,发现有,再调用
lang_replace($_SGLOBAL['msglang'][$msgkey], $values)
把
"您需要添加 \\1 个好友之后,才能进行本操...."里的\\1变成我们传进去的8,这里用到了lang_replace这个函数具体是
//语言替换
function lang_replace
($text, $vars) {
if($vars) {
foreach ($vars as $k => $v) {
$rk = $k + 1;
$text = str_replace('\\'.$rk, $v, $text);
}
}
return $text;
}
如果是手机访问这个页面直接返回那个showmesage的html,不会跳转。如果是正常的网页行为(非ajax请求)且用户设定$second为0,则直接301跳转。如果是ajax模式,且用户指定了跳转的url就返回
"$message"
没有指定url就返回那个本地化的信息message。
最后一个判断肯定是当url指定了并且second不为0的情况,这个也是最一般的情况,会先渲染出showmessage这个页面然后再设定时间跳转。
6、formhash
通过这个函数来探讨表单验证,以login举例
//产生form防伪码 function formhash() { global $_SGLOBAL, $_SCONFIG; if(empty($_SGLOBAL['formhash'])) { $hashadd = defined('IN_ADMINCP') ? 'Only For UCenter Home AdminCP' : ''; $_SGLOBAL['formhash'] = substr(md5(substr($_SGLOBAL['timestamp'], 0, -7).'|'.$_SGLOBAL['supe_uid'].'|'.md5($_SCONFIG['sitekey']).'|'.$hashadd), 8, 8); } return $_SGLOBAL['formhash']; }
这里的formhash我本以为应该是每一次请求的时候都不一样的,因为这里涉及到一个$_SGLOBAL['timestamp']当前时间的。但是我做实验的时候每一次都是一样的,为什么呢,因为这里$_SGLOBAL['timestamp']是时间戳,这里截取的是0到-7,也就是只要是在10的7次方秒内都可以算出来是一样的,当然我这里不够严谨,我的意思是formhash在相当长的一段时间内是不变的(如果是相同的supeid的话),为什么呢?为什么要这样做呢? 。
然后在html表单上总有一个隐形input,里面填的就是formhash,还有一个submit input填入了submit的类型
<input type ="submit" id ="loginsubmit" name ="loginsubmit" value ="登录" class ="submit" / >
所以当我们用submitcheck("loginsubmit")时会是这样,
function submitcheck($var) { if(!empty($_POST[$var]) && $_SERVER['REQUEST_METHOD'] == 'POST') { if((empty($_SERVER['HTTP_REFERER']) || preg_replace("/https?:\/\/([^\:\/]+).*/i", "\\1", $_SERVER['HTTP_REFERER']) == preg_replace("/([^\:]+).*/", "\\1", $_SERVER['HTTP_HOST'])) && $_POST['formhash'] == formhash()) { return true; } else { showmessage('submit_invalid'); } } else { return false; } }
首先判断提交的类型是否是预期的,也就是loginsubmit,并且判断是否为post提交,这里有点莫名其妙,你都从$_POST里拿数据了,难道还不是post?然后有一个比较复杂的if语句,就是判断当前的域名和用户填写表单时所在的页面的url是否是同一个域名,在我本机上,就是localhost,再判断那个formhash等不等于当前。天啊!太麻烦了!为什么要这么做的呢?
7、updatetable
//更新数据 function updatetable($tablename, $setsqlarr, $wheresqlarr, $silent=0) { global $_SGLOBAL; $setsql = $comma = ''; foreach ($setsqlarr as $set_key => $set_value) {//fix $setsql .= $comma.'`'.$set_key.'`'.'=\''.$set_value.'\''; $comma = ', '; } $where = $comma = ''; if(empty($wheresqlarr)) { $where = '1'; } elseif(is_array($wheresqlarr)) { foreach ($wheresqlarr as $key => $value) { $where .= $comma.'`'.$key.'`'.'=\''.$value.'\''; $comma = ' AND '; } } else { $where = $wheresqlarr; } $_SGLOBAL['db']->query('UPDATE '.tname($tablename).' SET '.$setsql.' WHERE '.$where, $silent?'SILENT':''); }
除了这个updatetable其实还有一个iinserttable,我这里就只放一个updatetable。这里其实有点类似于DAO。把所有的CRUD都封装为一个函数,任何实体的CRUD都只要调用这个函数,因为操作都是差不多的。注意这里 $comma的妙用。
还有这里的query里的参数soelnt,这里如果设置成了silent则在mysql出错的俄时候不会在页面上显示MySQL Query Error等等的错误而是直接把那个错误忽略了。