php正则中的修饰符

php中函数preg_match_all( ) 的作用是进行全局正则表达式匹配,在手册上看到了这个例子:

<?php
preg_match_all ("|<[^>]+>(.*)</[^>]+>|U",
    "<b>example: </b><div align=left>this is a test</div>",
    $out, PREG_PATTERN_ORDER);

/*
<一个或多个未包含‘>’的字符>零个或多个字符</一个或多个未包含‘>’的字符>
*/

print_r ($out);

?>
上面那个正则表达式的意义已经写在程序里了。

结果是:
Array
(
[0] => Array
(
[0] => <b>example: </b>
[1] => <div align="left">this is a test</div>
)

[1] => Array
(
[0] => example:
[1] => this is a test
)

)
注意到了程序里面的那个U。把U去掉的结果是:

Array
(
[0] => Array
(
[0] => <b>example: </b><div align="left">this is a test</div>
)

[1] => Array
(
[0] => example: <div align="left">this is a test
)

)
很有意思,于是我以为这个U的作用是不理会前面的语句。

关于这个修饰符意义如下:

/regexp/i
不区分大小写的匹配

/regexp/s
使句点(.)匹配任何字符,包括换行符( )

/regexp/x
从模式中删除空白符和注释

/regexp/m
使^匹配换行符 ( )之后的内容,美元符号($)匹配换行符 ( )之前的内容

/regexp/e
如果替换字符串是PHP代码,使用eval()执行该代码来得到实际的替换字符串。

PHP的Perl兼容正则表达式函数也支持在Perl中不支持的其他修饰符:

修饰符
意 义

/regexp/U
颠倒子模式的贪婪性;*和+尽可能少地匹配而不是尽可能多。

/regexp/u
把模式字符串当作UTF-8编码对待

/regexp/X
如果一个反斜杠之后跟着没有特殊意义的字符,将产生一个错误

/regexp/A
把锚定位在字符串的开头就像模式中有^一样

/regexp/D
使$字符仅匹配一行的末尾

/regexp/S
使表达式解析器更加小心地检查模式的结构,使得第二次运行时(如在一个循环中)加快速度。

多个div在一行

<style type="text/css">
<!--

.all {background: #F00;width:100%;height:100px;text-align:center;}
.main {width:200px;margin:0 auto} /*这里width:200px是里面div的宽度总和*/
.main1,.main2,.main3,.main4 {float:left}
.main1{background:#000;width:50px;height:100px}
.main2{background:#FF0;width:50px;height:100px}
.main3{background:#00F;width:50px;height:100px}
.main4{background:#F0F;width:50px;height:100px}
-->
</style>


<div class="all">
<div class="main">
<div class="main1"></div>
<div class="main2"></div>
<div class="main3"></div>
<div class="main4"></div>
</div>
</div>

asp 自动创建多级文件夹

'自动创建多层文件夹
Function AutoCreateFolder(strPath) 'As Boolean
 On Error Resume Next
 Dim astrPath, ulngPath, i, strTmpPath
 Dim objFSO
 If InStr(strPath, "") <=0 or InStr(strPath, ":") <= 0 Then
  AutoCreateFolder = False
  Exit Function
 End If
 Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
 If objFSO.FolderExists(strPath) Then
  AutoCreateFolder = True
  Exit Function
 End If
 astrPath = Split(strPath, "")
 ulngPath = UBound(astrPath)
 strTmpPath = ""
 For i = 0 To ulngPath
  strTmpPath = strTmpPath & astrPath(i) & ""
  If Not objFSO.FolderExists(strTmpPath) Then
   objFSO.CreateFolder(strTmpPath)
  End If
 Next
 Set objFSO = Nothing
 If Err = 0 Then
  AutoCreateFolder = True
 Else
  AutoCreateFolder = False
 End If
End Function

测试代码:

 MyPath = "C:3gcomet"
If AutoCreateFolder(MyPath) Then
 Response.Write "创建文件夹成功"
Else
 Response.Write "创建文件夹失败"
End If

php升级到5.3.2后的处理

PHP: 5.3.2-1ubuntu4 with Suhosin-Patch (cli)
cacti: Version 0.8.7e
ubuntu: server 10.04 32bit

ubuntu server 从8.04升级到10.04后,php也升级到5.3.2,结果问题就来了。前3个是cacti的问题

1.登录页面提示:
Deprecated: Function ereg_replace() is deprecated in /usr/share/cacti/site/include/global.php on line 89

解决:
/etc/php5/apache2/php.ini 的,把
;mbstring.func_overload = 0
修改为:
mbstring.func_overload = 7

2.登录页面提示:
Warning: Cannot modify header information - headers already sent by (output started at /usr/share/cacti/site/include/global.php:89) in /usr/share/cacti/site/include/global.php on line 120
Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at /usr/share/cacti/site/include/global.php:89) in /usr/share/cacti/site/include/global.php on line 130

解决:
/etc/php5/apache2/php.ini 的,把
output_buffering = Off
改成
output_buffering = 4096

3.登录后提示:
Deprecated: Function session_unregister() is deprecated in /usr/share/cacti/site/lib/functions.php on line 359

解决:
/usr/share/cacti/site/lib/functions.php 的359行,把
session_unregister($var_name);
改成
$_SESSION[$var_name] = null;

4.Zend Optimizer 3.3.9 用不了

解决:
暂时不支持 php 5.3.2 ,换到php 5.2.* 吧。 @_@

openfetion 的分析

今天再看了一下 openfetion 的代码,学学 php 。

openfetion:http://sourceforge.net/projects/openfetion/

nathan博客:http://hi.baidu.com/nathan2007/blog  分析得比较早,被转载最多的博客。想详细研究飞信最好去看看。

php 在线手册:http://www.php.net/manual/en/index.php 

以下是 oepnfetion 的代码,换成新的IP和域名了,否则登录不了:

<?php
/**
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 * 
 * @author c.young@xicabin
 * @license GPL
 * @version 1.0
 */
 
// 2009年11月换了新的IP和域名
define('FETION_URL', 'http://221.176.31.42/ht/sd.aspx');
define('FETION_LOGIN_URL', 'https://uid.fetion.com.cn/ssiportal/SSIAppSignIn.aspx');
 
//define('FETION_URL', 'http://221.130.44.194/ht/sd.aspx');
//define('FETION_LOGIN_URL', 'https://nav.fetion.com.cn/ssiportal/SSIAppSignIn.aspx');

// FETION_CONFIG_URL : get fetion system config, not used
define('FETION_CONFIG_URL', 'http://nav.fetion.com.cn/nav/getsystemconfig.aspx');
define('FETION_SIPP', 'SIPP');
 
static $fetion_proxy = null;
static $fetion_debug = false;
 
/**
 * generate guid for windows / *nix
 */
function guid(){
    if (function_exists('com_create_guid')){    //如果存在 com_create_guid 函数,就调用该函数生成 GUID
        return com_create_guid();
    }else{
        mt_srand((double)microtime()*10000);//optional for php 4.2.0 and up.
        $charid = strtoupper(md5(uniqid(rand(), true)));
        $hyphen = chr(45);// "-"
        $uuid = chr(123)// "{"
                .substr($charid, 0, 8).$hyphen
                .substr($charid, 8, 4).$hyphen
                .substr($charid,12, 4).$hyphen
                .substr($charid,16, 4).$hyphen
                .substr($charid,20,12)
                .chr(125);// "}"
        return $uuid;
    }
}
 
/**
 * debug output
 * 
 * @msg message
 * @data addtional data
 */
function fetion_debug($msg, $data = null) {
    global $fetion_debug;
    if ($fetion_debug) {
        print "[*] $msgrn";
        if (!empty($data)) {
            print_r($data);
        }
    }
}
/**
 * create sip package
 * 
 * @invite sip invite
 * @fields array of fields
 * @arg argument to send
 */
function fetion_sip_create($invite, $fields, $arg = '') {
    $sip = $invite."rn";
    foreach ($fields as $k=>$v) {
        $sip .= "$k: $vrn";
    }
    $sip .= "L: ".strval(strlen($arg))."rnrn{$arg}";
    return $sip;
}
 
/**
 * create a curl handle with fetion option
 * 
 * @url url
 * @ssic user identification
 * @post data to post
 */
function fetion_curl_init($url, $ssic = null, $post = null) {
    // create a new guid, and keep it !
    static $guid = null;
    if ($guid == null) {
        $guid = strtolower(trim(guid(), "{}"));        //调用 guid 函数生成 GUID,去掉{}后让字符串变小写
    }
    // set headers, e.g. pragma
    $headers = array('Content-Type: application/oct-stream', 'Pragma: xz4BBcV'.$guid);
    $ch = curl_init();        //初始化 curl ,参数看 http://www.php.net/manual/en/function.curl-setopt.php
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_USERAGENT, 'IIC2.0/PC 3.2.0540');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);        //把 curl_exec() 的返回值当成字符串保存下来,而不是直接输出到屏幕
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    // ssic
    if ($ssic != null) {
        curl_setopt($ch, CURLOPT_COOKIE, "ssic=$ssic");
    }
    // post data
    if ($post != null) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    }
    // proxy
    global $fetion_proxy;
    if ($fetion_proxy != null) {
        curl_setopt($ch, CURLOPT_PROXY, $fetion_proxy);
    }
    return $ch;                //返回 $ch
}
 
/**
 * run a curl query
 * 
 * @see fetion_curl_init
 */
function fetion_curl_exec($url, $ssic = null, $post = null) {
    $ch = fetion_curl_init($url, $ssic, $post);            //第2次调用 fetion_curl_init
    $succeed = curl_exec($ch);
    echo $succeed;
    //die();
    if (!$succeed) {
        error_log(curl_error($ch));
    }
    curl_close($ch);
    return $succeed;
}
 
/**
 * login
 * 
 * @mobileno mobile number
 * @pwd password
 */
function fetion_login($mobileno, $pwd) {
    $login_url = FETION_LOGIN_URL."?mobileno=$mobileno&pwd=$pwd";    //构造 login URL
    $ssic_regex = '/ssics+(.*)/s';
    $sid_regex = '/sip:(d+)@(.+);/s';// sid@domain
    $cookie_file = date('YmdHis').'_cookie.txt';// create a tmp file to save cookie
    //上面的是保存 cookie 的文件。如果网址错误而造成登录失败,这个文件就是空的。登录成功里面会有自己的 ssic 信息。
    $return_val = false;
 
    $ch = fetion_curl_init($login_url, null, null);        //第1次调用 fetion_curl_init
    // do not verify host
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // save cookie for further process
    file_put_contents($cookie_file, '');
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
    $succeed = curl_exec($ch);                //执行 curl,结果为 xml 格式,会有自己的 sip
    // close first, in order to make cookie file written
    curl_close($ch);
    fetion_debug("login to uid.fetion.com.cn");
 
    if (!$succeed) {
        error_log(curl_error($ch));
        return false;
    }
    // get ssic from cookie
    $ssic = false;
    $matches = array();        //创建一个空数组
    if (!preg_match($ssic_regex, file_get_contents($cookie_file), $matches)) {
        // 读取 $cookie_file 内容,用 $ssic_regex 进行匹配
        error_log("Fetion Error: No ssic found in cookie");
        return false;
    }
    $ssic = trim($matches[1]);
    fetion_debug("ssic: ".substr($ssic, 0, 10)."...");
    // get other login info from output
    $result_xml = simplexml_load_string($succeed);    //把 $succeed 解释为 xml
    $return_val = array(
        'ssic' => $ssic,
        'status-code' => strval($result_xml['status-code']),    //strval:获取变量值
        'uri' => strval($result_xml->user['uri']),
        'mobile-no' => strval($result_xml->user['mobile-no']),
        'user-status' => strval($result_xml->user['user-status'])
    );
    // extract sid and domain for further use
    if (preg_match($sid_regex, $return_val['uri'], $matches)) {
        $return_val['sid'] = $matches[1];
        $return_val['domain'] = $matches[2];
    }
    fetion_debug("sid: {$return_val['sid']}");
    unlink($cookie_file);        //删除 $cookie_file
    return $return_val;            //返回数组 $return_val 
}
 
/**
 * get fetion system config, not used
 */
function fetion_get_system_config() {
    $post_fields = '<config><client type="PC" version="3.2.0540" platform="W5.1" /><client-config version="0" /></config>';
    return fetion_curl_exec(FETION_CONFIG_URL, null, $post_fields);
}
 
/**
 * hex to binary
 *
 * @hex string hex code
 */
function fetion_hex2bin($hex) {
    $bin = '';
    $len = strlen($hex);
    for ($I = 0; $I < $len; $I += 2) {
        $bin .= chr(hexdec(substr($hex, $I, 2)));        //hexdec:把16进制转成10进制,chr:根据ASC值返回字符
    }
    return $bin;
}
 
/**
 * get hash password
 * 
 * @password real password
 */
function fetion_hash_password($password) {
    // in fact, salt is constant value                    // salt 实际是个常量
    $salt = chr(0x77).chr(0x7A).chr(0x6D).chr(0x03);    // wzm3 ? 作者的名字?
    $src = $salt.hash('sha1', $password, true);            // 用 sha1 算法加密 密码
    return strtoupper(bin2hex($salt.sha1($src, true)));    // 再来一次 sha1,接着转成16进制,最后变成大写
}
 
/**
 * create a random cnonce
 */
function fetion_calc_cnonce() {
    return sprintf("%04X%04X%04X%04X%04X%04X%04X%04X",
        rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
        rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
        rand() & 0xFFFF, rand() & 0xFFFF);
}
 
/**
 * get salt from real password
 * 
 * @password real password
 */
function fetion_calc_salt($password) {
    return substr(fetion_hash_password($password), 0, 8);    //只返回前面8位
}
 
/**
 * calculate response
 * 
 * @sid fetion id
 * @domain domain
 * @password real password
 * @nonce nonce from server
 * @cnonce cnonce
 */
function fetion_calc_response($sid, $domain, $password, $nonce, $cnonce) {
    $password = fetion_hash_password($password);
    $str = fetion_hex2bin(substr($password, 8));
    $key = sha1("$sid:$domain:$str", true);
    $h1 = strtoupper(md5("$key:$nonce:$cnonce"));
    $h2 = strtoupper(md5("REGISTER:$sid"));
    $res = strtoupper(md5("$h1:$nonce:$h2"));
    return $res;
}
 
/**
 * get url with next request number
 * 
 * @t i don't known
 */
function fetion_next_url($t = 's') {
    static $seq = 0;
    ++$seq;
    return FETION_URL."?t=$t&i=$seq";
}
 
/**
 * get next call id
 */
function fetion_next_call() {
    static $call = 0;
    ++$call;
    return $call;
}
 
/**
 * register to server
 * 
 * @ssic user identification
 * @sid fetion id
 * @domain domain
 * @password real password
 */
function fetion_http_register($ssic, $sid, $domain, $password) {
    $nonce_regex = '/nonce="(w+)"/s';
    $ok_regex = '/OK/s';
    $arg = '<args><device type="PC" version="44" client-version="3.2.0540" />';
    $arg .= '<caps value="simple-im;im-session;temp-group;personal-group" />';
    $arg .= '<events value="contact;permission;system-message;personal-group" />';
    $arg .= '<user-info attributes="all" /><presence><basic value="400" desc="" /></presence></args>';
 
    fetion_debug("begin register");
    $call = fetion_next_call();
    fetion_curl_exec(fetion_next_url(), $ssic, FETION_SIPP);        //第1次 fetion_next_url 和 fetion_curl_exec
    $msg = fetion_sip_create('R fetion.com.cn SIP-C/2.0', array('F'=>$sid, 'I'=>$call, 'Q'=>'1 R'), $arg).FETION_SIPP;    //构造协议
    fetion_curl_exec(fetion_next_url('i'), $ssic, $msg);            //第2次
    $msg = fetion_curl_exec(fetion_next_url(), $ssic, FETION_SIPP);    //第3次
    fetion_debug("recv nonce...");
    $matches = array();
    if (!preg_match($nonce_regex, $msg, $matches)) {
        error_log('Fetion Error: no nonce found');
        return false;
    }
    $nonce = $matches[1];
    $salt = fetion_calc_salt($password);        //$salt 到这里只有8位了
    $cnonce = fetion_calc_cnonce();                //生成随机的 cnonce
    $response = fetion_calc_response($sid, $domain, $password, $nonce, $cnonce);    //计算响应的值
    fetion_debug("nonce: $nonce");
    fetion_debug("salt: $salt");
    fetion_debug("cnonce: $cnonce");
    fetion_debug("response: $response");
    $msg = fetion_sip_create('R fetion.com.cn SIP-C/2.0', array('F'=>$sid, 'I'=>$call, 'Q'=>'2 R', 'A'=>"Digest algorithm="SHA1-sess",response="$response",cnonce="$cnonce",salt="$salt""), $arg).FETION_SIPP;
    fetion_debug("send response...");
    fetion_curl_exec(fetion_next_url(), $ssic, $msg);                //第4次
    $msg = fetion_curl_exec(fetion_next_url(), $ssic, FETION_SIPP);    //第5次
    return preg_match($ok_regex, $msg);
}
 
/**
 * send sms use http
 * 
 * @ssic user identification
 * @sid fetion id
 * @to receiver mobile number or sid
 * @content sms content
 */
function fetion_http_send_sms($ssic, $sid, $to, $content) {
    $ok_regex = '/Send SMS OK/s';
    $msg = fetion_sip_create('M fetion.com.cn SIP-C/2.0', array('F'=>$sid, 'I'=>fetion_next_call(), 'Q'=>'1 M', 'T'=>$to, 'N'=>'SendSMS'), $content).FETION_SIPP;
    fetion_debug("send sms...");
    fetion_curl_exec(fetion_next_url(), $ssic, $msg);                //第6次,这里两次是发信息的
    $msg = fetion_curl_exec(fetion_next_url(), $ssic, FETION_SIPP);    //第7次
    return preg_match($ok_regex, $msg);
}
 
/**
 * get buddy list
 * 
 * @ssic user identification
 * @sid fetion id
 */
function fetion_get_buddy_list($ssic, $sid) {
    $buddy_regex = '/.*?rnrn(.*)'.FETION_SIPP.'s*$/is';
    $arg = '<args><contacts><buddy-lists /><buddies attributes="all" /><mobile-buddies attributes="all" /><chat-friends /><blacklist /></contacts></args>';
    $msg = fetion_sip_create('S fetion.com.cn SIP-C/2.0', array('F'=>$sid, 'I'=>fetion_next_call(), 'Q'=>'1 S', 'N'=>'GetContactList'), $arg).FETION_SIPP;
    fetion_curl_exec(fetion_next_url(), $ssic, $msg);                //第6次,这里两次是获取好友列表的
    $msg = fetion_curl_exec(fetion_next_url(), $ssic, FETION_SIPP);    //第7次
    $matches = array();
    if (!preg_match($buddy_regex, $msg, $matches)) {
        error_log("Fetion Error: No buddy list found");
        return false;
    }
    $buddy_list = simplexml_load_string($matches[1]);
    $buddies = array();
    foreach ($buddy_list->contacts->buddies->buddy as $buddy) {                //列出注册的好友
        $buddies[strval($buddy['uri'])] = strval($buddy['local-name']);
    }
    foreach ($buddy_list->contacts->{'mobile-buddies'}->{'mobile-buddy'} as $buddy) {    //列出只有手机的好友
        $buddies[strval($buddy['uri'])] = strval($buddy['local-name']);
    }
    return $buddies;
}
 
/**
 * usage
 */
function usage() {
    echo "Usage: fetion [options] user_mobile passwordrn";
    echo "       fetion [options] user_mobile password sendto_sid contentrn";
    echo "rn";
    echo "  * if no sendto_sid specified, all available sid will be displayedrn";
    echo "  * you can not send sms to yourselfrn";
    echo "  * use sid as 'sendto_sid' instead of mobile number or fetion numberrn";
    echo "  -p <proxy[:port]>   Proxyrn";
    echo "  -d                  Debug outputrn";
}
 
/**
 * main
 * 
 * @args command line args
 */
function main($argc, $argv) {
    global $fetion_proxy;
    global $fetion_debug;
    $user_mobile = null;
    $password = null;
    $sendto_sid = null;
    $content = null;
 
    if ($argc < 2) {    //如果参数小于2,显示用法
        usage();
        return 1;
    }
    for ($I = 1; $I < $argc; ++$I) {    //第一个参数是文件名,需要先跳过
        if ($argv[$I] == '-p') {
            $fetion_proxy = $argv[++$I];
        } else if ($argv[$I] == '-d') {
            $fetion_debug = true;
        } else {
            $user_mobile = $argv[$I++];
            $password = $argv[$I++];
            if (isset($argv[$I])) {            //如果还有参数,就给 sid 和 content 赋值
                $sendto_sid = $argv[$I++];
                $content = $argv[$I];
            }
            break;
        }
    }
 
    $login_info = fetion_login($user_mobile, $password);    //调用 fetion_login
    if ($login_info === false) {                            //$login_info 为数组
        print "[*] login failedrn";
        return 1;
    }
    $ssic = $login_info['ssic'];
    $sid = $login_info['sid'];
    $domain = $login_info['domain'];
    print "[*] login successfulrn";
    $ok = fetion_http_register($ssic, $sid, $domain, $password);    //调用 fetion_http_register,向服务器登记,过程很复杂
    if ($ok === false) {
        print "[*] register failedrn";
        return 1;
    }
    print "[*] register successfulrn";
 
    if (empty($sendto_sid) || empty($content)) {                    //判断收信人和信息是否为空
        $buddies = fetion_get_buddy_list($ssic, $sid);                //如果为空则获取好友列表
        if ($buddies === false) {
            print "[*] get buddy list failedrn";
        } else {
            print "                                [sid]    [name]rn";
            foreach ($buddies as $sid=>$name) {
                printf("  %35s => %srn", $sid, $name);            //$name 为 local-name,应该是备注,保存到服务器才有
            }
        }
    } else {
        $ok = fetion_http_send_sms($ssic, $sid, $sendto_sid, $content);            //调用 fetion_http_send_sms,发信息
        print "[*] send sms ".strval($ok ? 'successful' : 'failed')."rn";        //发送得太快会失败,可以通过这里来判断并重发
    }
}
 
main($argc, $argv);
?>

php 的 argc 和 argv

   在 php 命令行中有两个参数 $argc 和 $argv 分别代表 参数的个数 和 全部参数组成的数组。

<?php

# testvar.php
var_dump ($argc);
var_dump ($argv)
?>
 

php testvar.php 3gcomet

int(2)    //这个表示有两个参数,下面的是列出全部参数,注意参数包括文件名 testvar.php
array(2) {
  [0]=>
  string(5) "testvar.php"
  [1]=>
  string(7) "3gcomet"
}