DIYism schooner能逆风行驶的纵帆船是生命的极好象征,贝努利效应正是化逆为顺的经典!--呆仁 收藏本站 涂鸦本站 来信本站 跟我MSN 跟我QQ GTalk 思想农业天文生物文化饮食娱乐硬件健康语言心理网络物理政治个人编程软件工具 php学习笔记
javascript学习笔记
flash学习笔记
css学习笔记
xsl学习笔记
sql学习笔记
ubuntu安装配置
centos安装配置
damnsmall安装配置
python学习笔记
呓语录
(k94f)
============================================================================================
linux里安装php:
cd /usr/local/php
tar zxvf php-5.1.6.tar.gz
cd php-5.1.6
./configure --prefix=/usr/local/php \
--with-apxs2=/usr/local/apache2/bin/apxs \     #生成libphp5.so文件
--enable-sockets \
--with-config-file-path=/usr/local/php \设置配置文件路径
--with-mysql=/usr/local/mysql \ #添加mysql扩展
--enable-so #不能去掉, 否则启动apache报undefined symbol: php_escape_html_entities
make      #php编译较耗时
make install
cp php.ini-dist /usr/local/php/php.ini
cd ..
rm php-5.1.6.tar.gz
添加扩展:
cd /usr/local/php/php-5.1.6/ext/mbstring
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install
cp modules/mbstring.so /usr/local/php/ext/mbstring.so
将php.ini里
extension_dir = "./"改成extension_dir = /usr/local/php/ext
extension=php_mbstring.dll去掉注释改成extension=mbstring.so
/etc/rc.d/init.d/httpd -k restart
=============================================================================================
windows apache下使用mod_rewrite, 在httpd.conf里:
LoadModule rewrite_module modules/mod_rewrite.so #D:\ProgXP\Apache Group\Apache2\modules下编译好的各个so可以直接用
NameVirtualHost 192.168.100.118:80   #如果局域网IP不固定, 这里可用主机名如"kexianbin:80"
<VirtualHost 192.168.100.118:80>   #如果局域网IP不固定, 这里可用主机名(如果上面IP这里必IP, 上面*这里必*)
ServerName root.diy
DocumentRoot E:\www\PHPApps\ROOT
RewriteEngine on
RewriteRule ^/(.+)/$ /open_url.php?username=$1
</VirtualHost>
伪静态页面规则(所谓的搜索引擎优化):
RewriteRule ^/forum/([^/]+)/([^/]+)/([^/]+)$ /forum.php?forumId=$1&sortId=$2&forumPage=$3
=============================================================================================
ob_implicit_flush(1);不让服务器缓存输出, 而是直接输出到客户端
要输出缓冲, 必须同时flush();ob_flush();
要输出大文件:ini_set('max_execution_time','3600');//set_time_limit(0);表不限时长
输出页面比较大时可以在代码第一行加上"ob_start('ob_gzhandler');", 这样浏览器支持压缩通讯时就自动采用压缩通讯以加速,
或在php.ini里改成"zlib.output_compression = On", 或者在apache.conf里LoadModule deflate_module mod_deflate.so,
不要同时使用, 否则报conflicts警告
如果有mod_deflate而想临时在单个php里禁用压缩传输, 这个也没用:apache_setenv('no-gzip', '1');
=============================================================================================
在什么地方重开session就在什么地方恢复所有session变量,前面同名变量的值将
被覆盖,GET、POST过来的变量在重开session之前,因而其重名变量肯定会被覆盖
=================================================================
用户session锁住导致其它页面无法打开(不论linux还是windows服务器):
test.php:
session_start();
for ($i=0;$i<10;++$i)
    {sleep(1);
     echo 'hell<br>';
    }
test1.php:
session_start();
echo 'hello';
访问test.php, 将导致当前用户在10秒内无法看到test1.php的输出(其它用户没有问题).
可以在test.php里循环之前session_write_close()以避免问题.
=================================================================
放在双引号里的字串变量会被自动解析, 相当于用"."串上这个变量, 并不对其内"\"再次转义,
如echo "return $Supplier_out[0][name];";得到的是"return Array[name];"
这不是我们所期望的,所以需要纯字串时最好还是使用单引号,
需要解析字串内变量时统一加{}:
echo "{$username},welcolm";
mysql_query("select * from tb1 where id='{$sql_safe($userid)}'");//注意$sql_safe='sql_safe';
有时候需要直接使用大段字串(不用处理单引号或双引号):
$str=
@<<<DOC
Example \nof $kk /*jkj*/
spanning 'multiple' lines
using "heredoc" syntax.
DOC;
注意:
开始标志后必须换行,结束标志所在行只能有结束标志及分号,不能含有空格等,最前面和最后面的换行都不算在字串里.
但是该字串内形如转义符和变量的部分仍会受到影响,如果想取得未受影响的$str可以让这个php文件读取自己再截串:
function get_doc($doc_name,$file_name)
         {$file_self=file_get_contents($file_name);
          $pos1=strpos($file_self,"\${$doc_name}=");
          $pos2=strpos($file_self,"\r\nDOC",$pos1);
          $len=strlen($doc_name)+13;
          $str=substr($file_self,$pos1+$len,$pos2-$pos1-$len);
          return $str;
         }
echo htmlspecialchars(get_doc('str',__FILE__));
get_doc函数要求here document的定义部分严格为:
$<str_name>=
@<<<DOC
为了节约内存第一行可以://$<str_name>=
注意:如果字串内有"{$aVar='string';"或"$aVar['jack']"会触发语法错误, "@<<<DOC"拦不住语法错误
有人已经做了叫nowdoc的补丁以"<<<~DOC"(或php5.3支持, 以"<<<'DOC'")表示类似单引号的heredoc,
如果不是自己的服务器, 那就只好把大段字串放到单独文件里
还有一种做法:
function segment_doc1()
         {$ob_before=ob_list_handlers();
          if (count($ob_before)>0)
             {$doc_before=ob_get_contents();
              ob_end_clean();
             }
          ob_start();
//wirte doc below:
?>
Example \nof $kk /*jkj*/
spanning 'multiple' lines
using "heredoc" syntax.
<?
          $doc=ob_get_contents();
          ob_end_clean();
          if (count($ob_before)>0)
             {ob_start($ob_before[0]);
              echo $doc_before;
             }
          return $doc;
         }
$doc1=segment_doc1();
echo htmlspecialchars($doc1);
不过doc内的"<?"和"?>"还是会产生冲突, 最可靠的做法还是读文件
=================================================================
调试的时候,可以用
<<<DOC
...
DOC;
来注释含有/*...*/而不能再外套/*...*/的大段代码
=================================================================
要调试一个循环, 输出循环过程中变量变化而又不影响循环, 最方便的办法是:
$_SESSION['debug'][]=$the_var_in_cycle;
然后在test.php里var_export($_SESSION['debug']);
这比输出日志文件再查看来得方便
还可以用ob_start();...ob_clean();拦截输出
放到$_SESSION['debug']['output']=ob_get_contents();
还可以把中间消息注册到末尾输出:
$status['var1']=$var1;$status['var2']=$var2;...
register_shutdown_function(create_function('$pars', 'var_export($pars);echo "<hr>";'), $status);
比如找出执行时间比较长的sql语句:
$t1=time().substr(microtime(),2,6);
mysql_query($query_string,$this->link_id);
$t2=time().substr(microtime(),2,6);
if (isset($_GET['debug']))
   {$_SESSION['debug']=$_GET['debug'];
   }
if ($_SESSION['debug']) #需要预先session_start();
   {if (($t3=(float)$t2-(float)$t1)>1000) //需要跟踪指定语句时加: || strpos($query_string, "SELECT area_name FROM area_code")!==false
       {ob_start();debug_print_backtrace();$tmp=strtr(ob_get_contents(),array("\n"=>'<br>'));ob_clean();
        register_shutdown_function('var_export', $query_string.':<font color=red>'.$t3.'(too long)</font><br>'.$tmp.'<hr>');
       }
    else
        {register_shutdown_function('var_export', $query_string.':'.$t3.'<hr>');
        }
   }
=================================================================
用DBG调试器来debug(发现错误)和profile(发现性能问题):
在测试服务器上编译对应php版本的DBG服务端(从http://dd.cron.ru/dbg/下载最新版binary即可)
生成dbg.so放到php.ini里extension_dir指定位置,并设置php.ini:
extension=dbg.so
[debugger]
debugger.enabled = true
debugger.profiler_enabled = true
debugger.JIT_host = 10.1.134.19  #程序员所在机器IP
debugger.JIT_port = 7869
在程序员机器的phpedit安装目录里的dbg listener也要从http://dd.cron.ru/dbg/下载对应DBG服务端的版本
在程序员机器的phpedit启动的php dbg listener里设置:
bind address: 10.1.134.19  #程序员所在机器IP
port: 7869
code page: 简体中文GB2312 #程序员机器操作系统编码页
breakpoint on script start: yes
在程序员机器的phpedit里Debug\Debug Setting里设置HTTP(SAPI or remote CGI):
Lauch debug session in web browser: yes
HTTP Server root URL: http://<testserver ip>
Local Root directory: E:\download\cvsroot #程序员工作目录(可以是本机, 也可以是测试服务器ftp)
Remote Root directory: /www #测试服务器web root目录
在程序员机器网页浏览器里启动phpedit调试:
http://<testserver ip>/index.php?DBGSESSID=1@10.1.134.19:7869 #用DBGSESSID=0来临时停止调试
(DBGSESSID后的三个参数不要写掉了, 否则会导致apache死掉)
(不能跟eAccelerator扩展同时启用, 否则profiler失效)
=================================================================
有时实在没有办法将测试服务器跟生产服务器同步准现场, 只能在生产服务器上测试时,
可以在保证代码没有错误的前提下用判断PHPSESSID是否你正在调试会话的方法来打印调试结果
欲构建准现场测试服务器, 需要能回滚测试数据, 然后才能快速同步生产服务器上的数据
=================================================================
采用php.ini-dist反而比php.ini-recommended速度快
在输出报表这样比较大的文件时,需要设置php.ini里的output_buffering = On,
否则客户端会不断刷屏,而且输出结果也不正确.
=================================================================
为使mssql_query("select getdate()")返回标准格式日期:
php.ini中mssql节内mssql.datetimeconvert默认是On要改为Off
=================================================================
判断一个传回数组中的元素个数最好用is_array($aArr)?count($aArr):0,因为如果传回的不是数组count也会为1
要注意,定义传出结果数组的函数时,要在添加数组元素的循环前面定义空数组,要不然传出结果不是空数组,而是空变量
判断一个变量是否数字索引数组最好用is_array && is_numeric(key($arr)),因为如果传回字串则isset($arr[0])为真
=================================================================
header("Cache-Control:no-cache");点击"转到"一次就删除了缓存文件.
flickr的架构师认为需要写全这些:
<?
# 让它在过去就“失效”
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
# 永远是改动过的
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
# HTTP/1.1
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
# HTTP/1.0
header("Pragma: no-cache");
?>
让一个php在10年内浏览器上不失效:
<?
header("Expires: ".gmdate("D, d M Y H:i:s", time()+315360000)." GMT");
header("Cache-Control: max-age=315360000");
?>
和rewrite配置里:
RewriteEngine on
RewriteRule ^/(.*.)v[0-9.]+.(css|js|gif|png|jpg)$ /$1$2 [L]
让一个静态文件在10年内浏览器上不失效:
RewriteEngine on
RewriteRule ^/(.*.)v[0-9.]+.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE
Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE
基本意思就是能在需要时改变URL里文件名来触发一个10年不失效文件的再次下载
==========================================================
php中接收所有post或get的字串都自动进行了转义处理('变成\',"变成\",\变成\\,null变成\x00),
以确保串到sql或其它字串中不发生语法错误,
(magic_quotes_gpc默认开启,所有GET,POST,COOKIE数据都自动addslashes()了)
比如一个搜索函数,既可能使用外面传进来的参数也可能使用内部代码中的参数,
则不应该在这个函数内使用addslashes或mysql_real_escape_string,
而是在内部代码每个调用这个函数的地方使用addslashes,
这非常麻烦,而且这些数据传出来显示在网页上时如果出现在链接或value等字串内时也会有麻烦,
因而最好办法是根本就不让这样的数据进入数据库,在各种表单提交的地方拦住或过滤掉它们,
比如"<script>"这样的会造成网页错误的也要拦住
--以上论述有问题,实际上只要到网页前使用htmlspecialchars就可以保证不出错,而input、textarea或select
会自动恢复htmlspecialchars处理过的数据送回程序
反函数htmlspecialchars_decode
==========================================================
将文件上传到数据库:
$array_in['img'] = addslashes(file_get_contents($img));
$g_DataAccess->Add ('news', $array_in, $result);
//GET,POST,COOKIE数据都自动addslashes()了但上传到temp文件夹的文件是保持原样的,
//则凑成sql语句之前需要addslashes
//$img也可以换成$_FILES['img']['tmp_name']
//$_FILES['img']下元素有tmp_name, name, type, size, error.
//用move_uploaded_file()能最快地复制临时文件
客户端form内必须有enctype="multipart/form-data",否则只会传上去一个本地路径而不传文件,
以前被用户利用来攻击服务器, 但现在使用$_FILES['img']['tmp_name'], 就不需要is_uploaded_file()检查了.
==========================================================
服务器环境设置没有开启magic_quotes_gpc时:
function my_add_slashes(&$var)
         {if (is_array($var))
             {foreach ($var as &$v)
                      {my_add_slashes($v);
                      }
             }
          else
              {$var=addslashes($var);
              }
         }
if (!get_magic_quotes_gpc())
   {my_add_slashes($_GET);
    my_add_slashes($_POST);
    my_add_slashes($_COOKIE);
    my_add_slashes($_REQUEST);
   }
注意这段不要放在非单体类的构造函数内(常见的Request.class), 否则可能重复创建实例而导致重复addslashes
==========================================================
转义掉正则表达式字符:
$str_result=preg_replace(array('/(\^|\$|\.|\*|\+|\?|\=|\!|\:|\||\\|\/|\(|\)|\[|\]|\{|\})/'), array('\\\\\0'), stripslashes($str_input));
最终串在一个正则表达式当中.
==========================================================
日常拼sql语句时, 往往既需要addslashes(如insert字段), 也需要不addslashes(如where条件):
$name="myname";
$value="lk,jdsk'jlkjdf";
function safe($sql)
         {$sql=strtr($sql, array('"'=>'\"', '\\'=>'\\\\'));
          return 'return "'.preg_replace(array('/\{#(.*?)\}/'), array('".addslashes($\1)."'), $sql).'";';
         }
$sql="insert into z_test (name, value) values ('{$name}', '{#value}')";
mysql_query(eval(safe($sql)));
之所以要写成"mysql_query(eval(safe"而不能集中到一个自定义query函数里,
是因为只有获得当前作用域(current scope)的函数get_defined_vars,
没有办法不传参数而直接获得主调函数作用域(caller's scope).
==========================================================
碰到insert into很多字段时, 直接串在sql里单行字串太长折行后很难辨认, 可疑先放到数组里, 然后再implode到sql:
$in=array('KeyID'=>$in['KeyID'],
          'Keyword'=>addslashes($in['Keyword']),
          ......
          'AddTime'=>date('Y-m-d H:i:s')
         );
$sql="insert into sem_TodoList (".implode(',', array_keys($in)).") values ('".implode("','", array_values($in)."')";
$this->objDB->query($sql);
==========================================================
mysql_real_escape_string或addslashes都不能正确处理非可见字符, 用pdo的prepare可以:
(注意那些":"打头的变量都不能加引号, 否则就相当于两重引号了)
$sql=$pdo->prepare("select *
                    from sem_SearchChangeShow
                    where Keyword=:Keyword
                          and BatchNum=:BatchNum
                          and OldChannelID=:ApvChannelID_old
                          and OldCategoryID=:ApvCategoryID_old
                  ");
$sql->execute(array(':Keyword'=>$v['Keyword'],
                    ':BatchNum'=>$v['BatchNum'],
                    ':ApvChannelID_old'=>$v['ApvChannelID_old'],
                    ':ApvCategoryID_old'=>$v['ApvCategoryID_old']
                   )
             );
$tmp=$sql->fetch(PDO::FETCH_ASSOC);
但不论用问号还是用冒号开头的标记都相当于将映射关系做了两边, 很不方便
如果想取出数字索引数组改用PDO::FETCH_NUM
==========================================================
数据流的字符转换:
a.到程序前:addslashes作用于\x00, \, ', "四个字符(magic_quotes_gpc启用时自动addslashes), 似乎只处理\和'就行
b.到数据库前:mysql_real_escape_string根据当前字符集设置而作用于\x00, \, ', ", \n(newline), \r(return), \x1a(替补符,win下像ctrl+z)七个字符, 前四个在网页到程序的时候已经自动做了, 但\r\n(win)或\r(\x0d:mac)或\n(\x0a:unix)不处理好像也没有问题,七个字符最终被转成(\0\\\'\"\r\n\Z).
(防范sql注入攻击:
function sql_safe($str,$is_bin=0)
         {if (!$is_bin && get_magic_quotes_gpc())
             {$str = stripslashes($str);
             }
          return strtr($str,array("\x00"=>'\\0','\\'=>'\\\\','\''=>'\\\'','"'=>'\"',"\n"=>'\n',"\r"=>'\r',"\x1a"=>'\Z'));
         }
mysql_real_escape_string需要先连数据库,因而不如自己实现了转换.
注意如果要sql_safe的字串是从文件直接过来的则要传第二个参数:1.
sql语句一定要写规范以避免sql注入:
$start=intval($_GET['start']);
"select a,b,c from tb where b>'{$b}' order by `{$fd1}` limit {$start},20"
//limit参数(以及mysql5.0以上strict mode下数值字段)转整, 否则可被串上"0 union select user(),CONCAT(database(),':',version(),'<br>'),load_file(char(...))/*", "char(...)"也可以用类似"cast(0x444543...36F72%20AS%20CHAR(4000))"
//char(47, 101, 116, 99, 47, 112, 97, 115, 115, 119, 100)可以绕过magic_quotes_gpc生成路径字串如'/etc/passwd'
//如果magic_quotes_gpc未启用则可"0 union select '','','<?...?>' into outfile 'test.php'/*"
//where里字串条件要加上单引号, 避免在select/order by里用字段名变量,
//php5下sql语句里避免使用$_SERVER['argv'][0], $_SERVER['HTTP_REFERER']等, 因magic_quotes_gpc=On对其无效
//否则可被串上"0 union select '','','<?...?>' into outfile 'test.php'/*"
//或子查询"(select '<?...?>' into outfile 'test.php')/*"
)
从界面录入"John's", 拼凑到sql语句里成了'John\'s', 到数据库里后成了"John's",
如果从数据库取到php再拼sql语句时, 就要addslashes了, 否则会拼成非法sql语句:'John's'
mysql_real_escape_string需要先连一下数据库, 可以自己写函数实现这几个字符的替换
c.到网页前:
htmlspecialchars(string[,quote_style(ENT_COMPAT(default)|ENT_QUOTES|ENT_NOQUOTES)]):
'&' (ampersand) becomes              '&amp;'
'"' (double quote) becomes           '&quot;'           |'&quot;'  |'"'
''' (single quote) becomes           '''                |'&apos;'  |'''
'<' (less than) becomes              '&lt;'
'>' (greater than) becomes           '&gt;'
在html或js的单引号字串内要使用ENT_QUOTES以转单引号
==========================================================
有时需要在命令行里直接执行php程序(比如需要apache用户受限的读写权限时):
/usr/bin/php test.php -p valueofp -j valueofj
代码内去参数值:
$opts=getopt('p:j:');
echo $opts['p'];
php 5.3起, getopt也可以在windows下使用了.
==========================================================
php和mysql对无效转义符的处理不同:
php里echo '\wji'=='wji';得到假(即php认为其有效),
mysql里select '\wji'=='wji';得到真.
因而要往数据库里存正则表达式时要预先addslashes
==========================================================
xml内的非法字符:
[\x00-\x08\x0b\x0c\x0e-\x1f]
即0到255内只有上述29个字符非法,
0到31内只有tab,return,newline这三个字符合法.
数据进到数据库的时候简单一点可以这样替换:
preg_replace(array('/[\x00-\x1f]/'), array(''), 'ab');
==========================================================
包含文件路径变量攻击主因:
1.包含路径由GPC数据直接串成, 应该根本不允许这么做
2.register_globals = On且有些php文件里路径变量没有初始化(主要就是被包含文件没有用.inc扩展名并保护)
3.在路径变量定义之后$_GET,$_POST,$_COOKIE被人为extract出来了
传给fopen或readfile等(读取文件被直接echo出来)的地址未用http://打头,
或直接传给require/include包含地址,
都会泄漏自己网站的php代码或其它文件,
应该:
1.用basename处理地址,将读取限定在指定文件夹
2.传过来的参数截最后一节(/和\\)后再添上扩展名,将读取限定在特定文件类型
3.用一个数组来转换参数和地址,将读取限定在指定文件集
(对php4.3.9和5.0.2以下版本也有效,虽然这些版本的addslashes将NULL(URL里%00)变成\NULL而非\0,而require只认\NULL前路径)
(上传文件的文件名也要处理, 因为可以构造操作系统不允许的文件名如"..'test.php", addslashes后变成"..\\'test.php")
(上传文件时, 检查文件类型(有include漏洞时)或文件扩展名(无include漏洞时)都是无效的, apache会将test.php.xxx当作php执行)
http://  file://  ftp://  c://  四种文件访问方式.
fopen("...","w")来修改文件,应该用"c://".
"c://"等于"c:/"等于"/".
==============================================================
通过文件访问监视软件可知在if(...){...}里的require等包含语句如果条件不成立确实没有执行,
那么if(!class_exists('...')){require '...';}和if(!function_exists('...')){require '...';}
的写法可以在一定程度上实现动态载入而节省服务器资源
==============================================================
require或include如果允许传参数包含文件, 而又开着allow_url_fopen, 就有可能被访客包含远程非法程序运行
虽然require/include等远程包含时(php5.2.0起增allow_url_include=Off), 相当于echo file_get_contents(), 并不相当于eval(file_get_contents())
allow_url_include=Off虽不过滤"php://...",但如下攻击无效(因php://input里"<"被转成"%3C"):
require($_GET['url'])受到"?url=php://input"同时$_POST['v1']='<?echo \'hell\'?>'攻击
(php4.3.0以上file_get_contents('php://input')得到post数据拼成的类似get传参的字串)
==============================================================
要特别注意逻辑等号是"=="而非"=",逻辑不等号是"!="而非"<>".
严格逻辑等号是"===",严格逻辑不等号是"!==".
=============================================================
字串连接符:"."
============================================================
list($usec, $sec)=explode(" ", microtime());
substr((float)$usec,1)是时间微秒值(即gettimeofday()["usec"])
(float)$sec是秒计数(即gettimeofday()["sec"]亦time())
time().substr(microtime(),2,6)为微秒计数
(float)$sec+(float)$usec为精确到微秒的秒计数
=============================================================
htmlentities转换成html格式字串(96个高位字符加上四个标准htmlspecialchars)
rawurlencode转换成url格式字串(所有非字母数字且非"-_."的字符都将被替换成百分号(%)后跟两位十六进制数)
urlencode同rawurlencode,除了空格( )不是被编程%20而是加号(+)
===============================================================
bin2hex能将winhex编辑器里16进制子串转成文字子串, 其反函数不是hex2bin, 而是用pack('H*', $data);
===============================================================
最好在最后加上PREG_SET_ORDER(默认是PREG_PATTERN_ORDER), 这样就跟preg_match有很好的对应关系:
preg_match_all($pattern, $str, $matches, PREG_SET_ORDER);
如果$str如果是从文件读入, 一定要用var_export把$str打出来, 对照着这个单引号的字串来写正则表达式, 而不是原文件.
===============================================================
对于GBK字串, 等同js里escape函数的实现:
function escape($str)
         {preg_match_all("/[\x80-\xff].|[\x01-\x7f]+/",$str,$r);
          $ar = $r[0];
          foreach ($ar as $k=>$v)
                  {if (ord($v[0]) < 128)
                      {$ar[$k] = rawurlencode($v);
                      }
                   else
                       {$ar[$k] = "%u".bin2hex(iconv("GBK","UCS-2",$v));
                       }
                  }
          return join("",$ar);
         }
等同js里unescape函数的实现:
function unescape($str)
         {$str=rawurldecode($str);
          preg_match_all("/(?:%u.{4})|.+/",$str,$r);
          $ar=$r[0];
          foreach ($ar as $k=>$v)
                  {if (substr($v,0,2)=="%u" && strlen($v)==6)
                      {$ar[$k]=iconv("UCS-2","GB2312",pack("H4",substr($v,-4)));
                      }
                  }
          return join("",$ar);
         }
将所有字母数字也escape:
function my_escape($str)
         {preg_match_all("/[\x80-\xff].|[\x01-\x7f]+/",$str,$ma);
          $m = $ma[0];
          foreach ($m as $k=>$v)
                  {if (ord($v{0}) < 128)
                      {$tmp='';
                       for ($i=0;$i<strlen($v);++$i)
                           {$tmp = $tmp.'%'.bin2hex($v{$i});
                           }
                       $m[$k]=$tmp;
                      }
                   else
                       {$m[$k] = "%u".bin2hex(iconv("GBK","UCS-2",$v));
                       }
                  }
          return join("",$m);
         }
将unicode字符10进制ord转成utf-8字符(如将&#8226;转成中文标点):
function chr_utf8($ord_utf8)
         {$str='';
          if ($ord_utf8<0x80)
             {$str.=$ord_utf8;
             }
          else if ($ord_utf8<0x800)
                  {$str.=chr(0xC0 | $ord_utf8>>6);
                   $str.=chr(0x80 | $ord_utf8 & 0x3F);
                  }
          else if ($ord_utf8<0x10000)
                  {$str.=chr(0xE0 | $ord_utf8>>12);
                   $str.=chr(0x80 | $ord_utf8>>6 & 0x3F);
                   $str.=chr(0x80 | $ord_utf8 & 0x3F);
                  }
          else if ($ord_utf8<0x200000)
                  {$str.=chr(0xF0 | $ord_utf8>>18);
                   $str.=chr(0x80 | $ord_utf8>>12 & 0x3F);
                   $str.=chr(0x80 | $ord_utf8>>6 & 0x3F);
                   $str.=chr(0x80 | $ord_utf8 & 0x3F);
                  }
          return $str;
         }
function ord_utf8($chr_utf8)
         {switch (strlen($chr_utf8))
                 {case 1:
                       return ord($chr_utf8);
                  case 2:
                       $n = (ord($chr_utf8[0]) & 0x3f) << 6;
                       $n += ord($chr_utf8[1]) & 0x3f;
                       return $n;
                  case 3:
                       $n = (ord($chr_utf8[0]) & 0x1f) << 12;
                       $n += (ord($chr_utf8[1]) & 0x3f) << 6;
                       $n += ord($chr_utf8[2]) & 0x3f;
                       return $n;
                  case 4:
                       $n = (ord($chr_utf8[0]) & 0x0f) << 18;
                       $n += (ord($chr_utf8[1]) & 0x3f) << 12;
                       $n += (ord($chr_utf8[2]) & 0x3f) << 6;
                       $n += ord($chr_utf8[3]) & 0x3f;
                       return $n;
                 }
         }
===============================================================================================
对于简单的次级数据可以不必建从表,而用一个长字串字段:
$send_method1='送货方式1';$send_price1=3.5;
$send_method2='送货方式2';$send_price2=3.5;
串成:$aStr=htmlspecialchars($send_method1).'&'.$send_price1.'>'.htmlspecialchars($send_method1).'&'.$send_price1;
解出:$aArr=explode('>',$aStr);$bArr=explode('&',$aArr[0]);$cStr=html_entity_decode($bArr[0]);
严格点, 为免空字串时出现数组空元素, 应$aArr=$aStr?explode('>',$aStr):array();
===============================================================
随header送出cookie可以用:
setcookie(name:string[,value:string[,expire:int[,path:string[,domain:string[,secure:bool]]]]])
setcookie("TestCookie", $value);
setcookie("TestCookie", $value, time()+3600);
setcookie("TestCookie", $value, time()+3600, '/script/', '.example.com', false);
//最后参量用来指示只在https协议下采用,默认为false或不设则在所有协议下有效)
给$_COOKIE里的元素赋值没有任何意义,对$_COOKIE的操作是在header送出之后发生的.
----------杂项-----------
一个cookie的"联合主键"(按先后顺序):domain,path,name
注意用firebug测试时如果只向客户端送cookie触发不了firebug的net监视,要带上点正文才行
要查看全站cookie还是要到'选项\隐私\Cookies\查看Cookies'里查看
如果value未设或为'', 表示删除cookie
如果expire未设或为0, 表示cookie在浏览器关闭时失效(类似PHPSESSID)
要清空cookie把expire设成1而不是0, 表示"expires=Thu, 01-Jan-1970 00:00:01 GMT"
也可以这么写:setcookie("counter=4;expires="+date("D, j M Y H:i:s GMT",time()+60000));
直接用header送出cookie是等同于setrawcookie(注意送多个cookie时, header第二参数要false)
js里document.cookie里同名cookie:最前面的为当前页面有效cookie,后面是异层同名有效cookie,最后是同层\异层同名无效cookie
(即js和php中cookie的有效域是相同的, js中cookie的可见域比php大)
当无效同名cookie前面的cookie被删掉后,该无效cookie被激活
------------domain--------------
domain:设为'.example.com'表示包含'example.com'和'*.example.com'
domain:设为'example.com'等同于'.example.com','1.example.com'等同于'.1.example.com'
------------domain显隐关系--------------
domain:如果domain未设或为'',效果为当前域名(注意不继承到子孙域名,当然也不会有下面的主动设置方式下"母站先则垄断"问题)(类似PHPSESSID)
domain:等价domain时显示和隐式写法在该域名层次是互为先行垄断关系,后行者种上一个同层同名无效cookie
------------domain母子关系--------------
domain:在example.com上种'.1.example.com'的cookie,任何cookie(包括无效cookie)都没种上(母站不能种子站)(子站之间互种也是什么都没种上)
domain:在1.example.com上种'.example.com'的cookie,可以种上,'1.example.com'上也有效(子站可以种母站)
母站php和js都不能读子站,子站php不能读母站(如果有同名子站cookie)\js都能读
感觉:子站可以任意修改或删除母站的cookie,前人定的标准安全性怪怪的
可见:无法用php或js一次性清除所有站群cookie,为了方便管理可以规定所有子站cookie都种在母站层上
------------domain先后关系--------------
domain:如果已种上'.example.com'的cookie_a,则连子站上也改不了'.1.example.com'的cookie_a的值,而种了一个异层同名无效cookie(母站先则垄断)
domain:如果已种上'.1.example.com'的cookie_a,则可以再种'.example.com'的cookie_a(母站后可嵌套,有子作用域)
------------path--------------
path:如果path未设或为'', 默认为当前脚本所在路径及所有子孙路径
path:'/script'等同于'/script/'
path:在任何路径下可以种上任何路径的cookie
path:无论先后种根路径和子路径的cookie_a,都产生同样的cookie嵌套
根路径php和js都不能读子路径,子路径php不能读根路径(如果有同名子路径cookie)\js都能读
可见:无法用php或js一次性清除所有路径cookie,为了方便管理可以规定所有子路径cookie都种在根路径上
================================================================
cookie文件夹内的cookie值在请求某网站内页面时被浏览器自动搜索到后
post到服务器,所以php读取cookie就像读取post传送过来的变量一样简单,
而且是网站内跨网页的.
=================================================================
session就像是服务器端cookie文件,但是它的文件名还是用session id cookie
发给客户的内存(而非缓存文件夹,但仍可用document.cookie读取,可以用于窃入邮箱,但是
这并不意味着你可以获得服务器端session file里面的变量及其值):
创建或修改之页面:session_start();session_register("you");$you="hello";
调用之页面:session_start();session_register("you");echo $you;
后面的版本(>=4.1)可以session_start()再直接使用$_SESSION['you']或$HTTP_SESSION_VARS['you']来赋值或取值
在函数内使用session,就要先global $you;或global $_SESSION;或global $HTTP_SESSION_VARS一下
=================================================================
通过给当页各session变量赋null销毁所有全会话session变量:session_start();$_SESSION=array();//对服务器端
销毁session id cookie:setcookie(session_name(),'',time()-42000,'/');//对客户端
仅销毁当页session变量(不影响到全会话),不可用session_start()恢复当页对全会话的影响:session_start();unset($_SESSION);
仅销毁全会话session变量(不影响当页),可用session_start()恢复当页对全会话的影响:session_start();session_destroy();
=================================================================
在登录提交的地方删除上次登录session文件,以踢出同账号的前次登录:
(只相当于清空session:$_SESSION=array(), 并未删除session_id)
$rs=
mysql_db_query('db_this',
               "select *
                from user
                where user_name='{$_POST['user_name']}'
               ",
               $dbh
              );
$rslt=rs2ar($rs);
@unlink(session_save_path().'/sess_'.$rslt[0]['session']);
mysql_db_query('db_this',
               "update turn_user
                set session='".session_id()."'
                where user_name='{$_POST['user_name']}'
               ",
               $dbh
              );
=================================================================
同一主机的不同虚拟主机是共用session的,只要有phpsessid:
先在example.com创建session, 然后在example1.com种上phpsessid的cookie, 就可以在example1.com里直接使用example.com的session变量了
=================================================================
用ini_set()可以方便的修改php的系统变量,而不必用"$"或"php_"之类.
在关闭了错误报告服务器里可以用
ini_set('error_reporting', E_ALL & ~E_NOTICE); #为了方便这个可以在服务器设好, 只用下面那个就可方便调试程序了
ini_set('display_errors', 'On');
来显示程序错误.
但是注意:如果程序在编译时已出错, 则ini_set根本未执行到, 所以编译错误无法报出来, 除非是在php.ini里设这两个参数
=================================================================
在函数内回溯函数调用路由:
echo '<pre>';debug_print_backtrace();echo '</pre>';
追踪函数所在文件:
在开启php报错情况下不带参数执行函数, 就可以在报错里看到函数所在文件,
不过如果该函数所有参数都有默认值那仍不会报错, 更可靠的方法是重复定义函数(重名函数)来引发报错.
报错需要设置php.ini里error_reporting = E_ALL, 重复定义类报错不要这样把E_STRICT排除了: error_reporting = E_ALL & ~E_STRICT
打印所有已包含文件也有帮助:
ob_start();//包含文件里可能有输出, 用这一句和第三句过滤掉
include '...';
ob_clean();
$tmp=get_included_files();
foreach ($tmp as $v)
        {echo '<br>'.$v.':<br><textarea style="width:100%;height:100px;">'.htmlspecialchars(file_get_contents($v)).'</textarea>';
        }
exit();
或者不严格地获得整体代码(需要放全部包含语句之后, 因extract的只是最终变量值, 而在函数或对象内的变量无法传到my_eval):
function my_eval($str,$str1)
         {extract($GLOBALS);
          $str=eval('return '.strtr($str,array('\\"'=>'"','__FILE__'=>'\''.$str1.'\'')).';');
          $str1=($str!='' && $str{0}!=='/' && $str{1}!==':')?(dirname($str1).'/'):'';
          return realpath($str1.$str);
         }
function whole_code($str)
         {extract($GLOBALS);
          static $arr_path=array();
          if (!in_array($str,$arr_path))
             {$arr_path[]=$str;
              $tmp=file_get_contents($str);
              $instead='"?>\r\n<?//==========begin:".my_eval(\'\1\',\''.$str.'\')."==========?>\r\n".whole_code(my_eval(\'\1\',\''.$str.'\'))."\r\n<?//==========end:".my_eval(\'\1\',\''.$str.'\')."==========?>\r\n<?"';
              return preg_replace(array('/(?<=\s)(?:include|include_once|require|require_once)[\s]*\((.+?)\)[\s]*;/e','/(?<=\s)(?:include|include_once|require|require_once)[\s]+(.+?)[\s]*;/e'),array($instead,$instead),$tmp);
             }
          else
              {return '<?//==========Have required once!('.strtr($str,array('/'=>'|')).')==========?>';
              }
         }
function whole_output()
         {echo '<textarea style="width:100%;height:300px;">'.htmlspecialchars(whole_code(__FILE__)).'</textarea>';
         }
flush();register_shutdown_function('whole_output');
//不能使用create_function, 因其内无法使用__FILE__.
/*When zlib.output_compression is enabled or use "ob_start('ob_gzhandler')"(<=php5.0), register_shutdown_function doesn't work properly.
  Anything output from your shutdown function will not be readable by the browser.
  Firefox just ignores it. IE6 will show you a blank page.
  The workaround that worked for me is to add "flush();" before register_shutdown_function:
*/
在最终生成的大段代码里确定某段代码所属文件的方法:
先记下该段代码的行号, 向下找"end:"获得一个文件路径, 然后向上找该文件路径对应的"begin:", 
如果所在行号小于前记行号则改文件路径就是所属文件,否则再找该"end:"后的"end:", 直到找到对应的"begin:"小于前记行号.
=================================================================
分析代码结构可以用code visual editor看流程图(先要对php文件进行5项处理, 见软件包)
=================================================================
只要在 php.ini 文件中激活了 allow_url_fopen 选项,您可以在大多数
需要用文件名作为参数的函数中使用 HTTP 和 FTP URL 来代替文件名.
=================================================================
mysql4.1以上强制要求客户端(包括php引擎)连接数据库时要指定客户端编码character_set_client,
注意mysql里是utf8而非utf-8:mysql_query('set names utf8');
否则mysql将把客户端数据都当作latin1编码,比如从php传到数据库的utf8编码将被当作latin1进行latin1_2_inner()存入数据库,
传出时再inner_2_latin1()输出, 页面header指定utf8时仍可能将这种latin1正常显示为汉字, 但极有可能丢失部分文字,
而且这样的数据就没有办法利用mysql的编码自动转换功能,比如无法通过set names gbk转换成gbk编码输出了,
正确的做法只能是在数据接口的地方设置客户端编码, 如set names utf8
mysql4.1之下没有character_set_client概念(即直接进出)
mysql4.1以上5.0之下, character_set_client默认是latin1(即latin1_2_inner()进入,inner_2_latin1()出来,会导致数据丢失)
mysql5.0以上, character_set_client默认是utf8(即utf8_2_inner()进入,inner_2_utf8()出来)
查看当前使用的客户端编码:select @@character_set_client
或show variables like 'character%'
"set names"命令控制mysql系统变量:character_set_client,character_set_connection,character_set_results
即使mysql5.0如果编译mysql时没有加-with-charset=utf8, 则character_set_database无论如何也改不了utf8,
即使表里设了CHARSET=utf8(要存utf8则必设), 连接mysql时也必须先'set names utf8'(php必须在db类query()里加, sqlyog客户端能自动检测character_set_client而先执行'set names utf8'), 否则将是inner_2_inner进出.
=================================================================
列出所有库(也可以用sql:"show databases;"):
<?
mysql_connect ('172.16.18.1:3306','marco','111111');
$result = mysql_list_dbs ();
$i = 0;
while ($i < mysql_num_rows ($result)) 
      {$db_names[$i] = mysql_dbname ($result, $i);
       echo $db_names[$i] . "<BR>";
       ++$i;
      }
?>
列出某库的所有表:
<?
mysql_connect ('172.16.18.1:3306','marco','111111');
$result = mysql_list_tables ("test");
$i = 0;
while ($i < mysql_num_rows ($result))
      {$tb_names[$i] = mysql_tablename ($result, $i);
       echo $tb_names[$i] . "<BR>";
       ++$i;
      }
?>
列出某库某表的所有字段信息:
mysql_list_fields相当于sql:'show columns from tb1',
mysql_field_name(只字段名)或mysql_fetch_field(字段信息)也可以用于普通数据集,
要一次取出所有字段名, 可以用array_keys($row0);
取得字段序号(column index):array_search('CategoryID', array_keys($arrOut[0]));
<?
mysql_connect ('172.16.18.1:3306','marco','111111');
$result = mysql_list_fields ("test","tb_person");
$i = 0;
while ($i < mysql_num_fields ($result))
      {$fd_infos[$i] = "name:".mysql_field_name($result, $i).",".
                       "type:".mysql_field_type($result, $i).",".
                       "leng:".mysql_field_len($result, $i).",".
                       "flag:".mysql_field_flags($result, $i).";";
       echo $fd_infos[$i] . "<BR>";
       ++$i;
      }
?>
列出某库某表的所有行:
<?
mysql_connect ('172.16.18.1:3306','marco','111111');
$result = mysql_db_query("test","select * from tb_person");
while ($row = mysql_fetch_array($result))
      {echo $row[0].";".$row[1].";".$row[2]."<br>";//或者用$row["Age"]等
      }
?>
libmysql的结果集要移动结果集指针(result pointer):
mysql_data_seek($result, 0);
但pdo的结果集里不行, php5.2.5里的PDO_MYSQL都还没支持PDO::CURSOR_SCROLL, $pdo->fetch($result, PDO::FETCH_ORI_FIRST)没用.
===========================================================
先mysql_connect ('172.16.18.1:3306','marco','111111');
再:
mysql_create_db("db_test");//创建库
或者:
mysql_query("CREATE DATABASE IF NOT EXISTS db_test");
mysql_drop_db("db_test");//移除库
==========================================================
先mysql_connect ('172.16.18.1:3306','marco','111111');
mysql_select_db('db_test');
再:
mysql_query("CREATE TABLE tb_person (Age int,Gender varchar(2),Name varchar(20))");//创建表
//mysql_query只能一个表一个表地创建,而不能像在mysqlcc内一样使用分号
mysql_query("DROP TABLE tb_person");//删除表

mysql_query("insert into tb_person values ('12','男', 'malcolm')");//创建行
mysql_query("insert into tb_person (Age,Gender,Name) values ('12','男', 'malcolm')");//创建行
mysql_query("delete from tb_person where name='ddd'");//删除行
mysql_query("update tb_person set sex='男',age='12' where name='ddd'");//更新行
============================================================
多机多库连接时:
$m_DBH = mysql_connect ('mysqlserver' . ":" . '3306', 'root', 'Adfj89cc/*-+');
$m_DBH_user = mysql_connect ('mysqlserver' . ":" . '5306', 'root', 'Adfj89cc/*-+');

function rs2ar($rs)
         {$rslt = array();
          if (is_resource($rs))
             {while ($row = mysql_fetch_array($rs))
                    {$rslt[] = $row;
                    }
             }
          else
              {$rslt=false;
              }
          return $rslt;
         }

$orders_rs=
mysql_db_query('design',
               "select tb1.user_id,count(*) as cnt,sum(tb2.ORDER_INFO_money) as sum
                from qiang_gou_orders tb1
                     left join qiang_gou_ORDER_INFO tb2 on tb2.ORDER_INFO_order_id=tb1.id
                where tb2.ORDER_INFO_state in ('1','2','3','4')
                group by tb1.user_id
               ",
               $m_DBH
              );

$orders=rs2ar($orders_rs);
============================================================
0==""==array()==FALSE==NULL, 当心结果不能传递, 比如'hello'==0是真
empty($var)等价于$var==='', $var===0, $var==='0', $var===null, $var===false, $var===array()
而is_null()等价于!isset()等价于===null可以判断是NULL不是"",
//其实并不完全等价,当判断的对象未设时,isset不返回notice,而is_null返回notice警告
注意:==号比较结果并不能传递,如0=="0"且0==NULL,但"0"!=NULL,
$a!=0  <=>  $a!=='0' && $a!==NULL
详参php手册附录"PHP 类型及比较符表"
===================================================================================
if ($a and $b)等同if ($a?$b:false),php自身有这种优化功能
===============================================================================================
对于循环标志变量,为避免报错"变量未定义",可以这样写:
function myfunc()
         {if (isset($flg) && $flg==1)
             {...
              $flg=0;
             }
          ...
         }
===================================================================================
array创建数组时跳过的元素则为!key_exists(),访问则===NULL
数组内元素的实际顺序与数字索引无关
=====================================================================================
$aArray=array('a','b'=>'b','c','d'=>'d')
等价于$aArray=array(0=>'a','b'=>'b',1=>'c','d'=>'d')
等价于$aArray[]='a';$aArray[b]='b';$aArray[]='c';$aArray[d]='d';
=====================================================================================
$aArray['']='a'完全不同于$aArray[]='a',前者是$aArray的空字串索引元素,
后者是$aArray的自建数字索引元素,后者不能用于读取.
$aArray[NULL]不同于$aArray['NULL'],前者被转成$aArray[''],后者是索引为"NULL"的元素.
=====================================================================================
经key()检查得知$aArray['2']被转成了$aArray[2],因而DI_DataAccess.php串条件时
需$terms[$i][1]!==""严格检查,否则上层传过来的某侧条件为0时被错误地忽略了.
=====================================================================================
unset($aArray[1])结果:$aArray[1]===NULL且!key_exists()且count减1
(但占用最大数字索引,可以用$aArray=(array)$aArray消除最大数字索引)
(如果整个数组全数字索引,unset某些元素后,可以用(array), array_values()重整数字索引)
$aArray[1]=NULL结果:$aArray[1]===NULL且key_exists().
=====================================================================================
在数组循环内unset元素时要当心,unset后就不需要next了:
reset($q_user);
while (key($q_user)!==null)
      {if (current($q_user)=='')
          {unset($q_user[key($q_user)]);
          }
       else
           {next($q_user);
           }
      }
$q_user=array_values($q_user);
=====================================================================================
$aArray['0']等价于$aArray[0],$aArray['a']等价于$aArray[a],
array(a=>'a')等价于array('a'=>'a')等价于array(a=>a),
$aArray[02]等价于$aArray[2]等价于$aArray['2'],但不等价于$aArray['02'].
==================================================================================
aArray[<value>]=<value>,将索引与值等同而创建数组就可以作无序集用了
如果只要求不重复集合可以用array_merge()
==================================================================================
统计数组内某值个数:
$tmp=array_count_values($aArray);
$cnt=$tmp[$the_value]?$tmp[$the_value]:0;
==================================================================================
用array_intersect_assoc计算数组交集,排位靠前数组的索引有优先权
======================================================
在函数外面定义了变量,然后在任何函数内只要使用global声明一下就可以使用了,或者用超全局变量$GLOBALS['变量名']来直接访问
如果要列出$GLOBALS里的所有变量不要直接用var_export, 会迭代死循环, 应先用pre_var_export处理下,
注意:var_export导出的resource类型的变量都变成了NULL, 当检查数据库连接类时你容易误以为数据库连接失败,这样:
function pre_var_export($var)
         {$rtn=is_object($var)?clone $var:$var;
          foreach ($rtn as $k=>&$v)
                  {$v=is_resource($v)?(string)$v:$v;
                   ($k=='GLOBALS')?($v='GLOBALS'):'';
                  }
          return $rtn;
         }
echo var_export(pre_var_export($GLOBALS),true);
上句不会列出局部变量
======================================================
在一个函数内声明为static的变量,那么在该函数的多次调用过程中会继承该变量的值
======================================================
在一个类里面定义了变量,在其内使用$this->变量名来使用
=============================================================
在一个类外面定义了变量,可以在类里面的函数内使用global声明或超全局变量$GLOBALS来访问
两种方式有区别, 前者本质上是创建了一个局部变量("global $aVar;"等价于"$aVar=&$GLOBALS['aVar'];");
则unset($aVar);和unset($GLOBALS['aVar']);是不一样的, 前者只是销毁传址于全局变量的一个局部变量,
其他情况的传址变量unset也不会影响源变量, 比如在foreach($a as &$v)之内unset($v)无效,
应该在foreach($a as $k=>$v)内unset($a[$k]), 注意:unset一次之后的&$v都失效了, 所以这里未用$k=>&$v.
如果该变量定义为static,则表示只在该页面内继承使用
======================================================
当心:foreach ($a as &$v) {...}之后不要用$v再做循环变量foreach ($b as $v) {...},
否则$a的最后一个元素会被修改掉, 可以养成循环后unset传址引用的习惯:foreach ($a as &$v) {...} unset($v);
======================================================
一个被调函数欲获得其主调作用域(caller's scope, 可能在函数里, 也可能最顶层),
可以预先用get_defined_vars()获得当前作用域的所有可用变量, 通过参数传到被调函数里.
======================================================
php里的数组可以是一个杂数组,即各元素类型不必统一,这样的数组更像一个记录,甚至这个数组尺度也未限定
=====================================================
用get_class()可以取得一个对象属于的类,用get_object_vars或(array)可以将对象的所有属性及值传到一个数组里
======================================================
$date_tmp=split('[/.-]','2005-1-3');
date("Y-m-d",mktime(0,0,0,$date_tmp[1],$date_tmp[2],$date_tmp[0]));
或者date("Y-m-d",strtotime('2005-1-3'));
这样就把用户输入的日期时间字串转成了标准日期时间字串
一次性取年份:eval('$tmp=split("[/.-]","2005-1-3");return $tmp[0];')
//复杂处理preg_split比split更快,如果不需要正则表达式,则explode最快,可以用"\r\n"作分隔符(要用双引号,preg_replace里可用'/\r\n/')
//preg是PCRE(perl compatible regex) regex的缩写
//ereg是(POSIX)Extended regex的缩写,editplus里用的这种
//preg功能比ereg完善,php里preg比ereg速度快很多
======================================================
求两个时间的秒差:
$datetime_tmp1=split('[- :]','2005-1-3 12:30:45');$datetime_tmp2=split('[- :]','2005-1-3 12:33:45');
echo mktime($datetime_tmp2[3], $datetime_tmp2[4], $datetime_tmp2[5], $datetime_tmp2[1], $datetime_tmp2[2], $datetime_tmp2[0])
 - mktime($datetime_tmp1[3], $datetime_tmp1[4], $datetime_tmp1[5], $datetime_tmp1[1], $datetime_tmp1[2], $datetime_tmp1[0]);
其实可以用echo strtotime('2005-1-3 12:33:45')-strtotime('2005-1-3 12:30:45');
要注意strtotime是将输入时间扣除了GMT时差后再与1970-1-1 00:00:00相减的.
但时间戳在用的时候会自动加回GMT时差:
date('Y-m-d H:i:s',strtotime("+1 day"));
计算当日与某日天的日数差:
(strtotime(date('Y-m-d'))-strtotime('2007-3-1'))/(60*60*24);
$time1与$time0月份差:
12*(date('Y',$time1)-date('Y',$time0))+date('m',$time1)-date('m',$time0);
$time1与$time0月差(按简易规则,类似下面的floor/ceil):
ceil:12*(date('Y',$time1)-date('Y',$time0))+date('m',$time1)-date('m',$time0)+(date('d',$time1)<date('d',$time0)?0:1);
floor:12*(date('Y',$time1)-date('Y',$time0))+date('m',$time1)-date('m',$time0)+(date('d',$time1)<date('d',$time0)?0:1)-1;
$time1与$time0月差(按30日/月或30.4日/月,跟据需要用floor/round/ceil):
round(($time1-$time0)/(60*60*24*30));
指定月的最后一天:
date('Y-m-d',strtotime(substr($the_month,0,4).'-'.(substr($the_month,-2,2)+1).'-01 -1 days'));
=====================================================
取得当前标准日期时间:date("Y-m-d H:i:s");
也可以用来分解时间戳date('Y',$dt);
取得当天起始时间戳strtotime(date('Y-m-d'));
strtotime('1970-1-1 00:00:00 +8 hours')==0;
为了方便循环可以统一用复数'days':
strtotime(date('Y-m-d').'+1 days')
======================================================
在复杂的系统中使用require命令调用文件最好改用require_once命令以避免重复调用公共文件时发生冲突
require或require_once虽然是编译指令, 但仍像函数一样有返回值, 包含成功true,失败false
==================================================================
即使只转换一个子串,也要使用strtr($str,array($str_a=>$str_b));以免strtr($str,$str_a,$str_b)截串问题
strtr可以替换"\r\n"的, 不过要注意用双引号
不需要正则时用strtr,否则用preg_replace(array('/ circle="(.+?)"/', '/a.+?b/'), array('', 'ab'), $str);
要替换第一个或前几个出现应该用preg_replace的limit参数
==================================================================
$a=='i'?'j':'k',更强大的表达:strtr($a,array($a=>'','i'=>'jack','o'=>'kool','m'=>'desck'));
//数组第一元素加入$a=>''可以利用数组的索引覆盖特性保证正常转换又能在无匹配时传出空串

但strtr不能处理复杂条件的情况,可以用:
$tmp=array_search(TRUE,array(
    jack=>($k<=3),
    kool=>(($k>3) and ($k<7)),
    'desck'.$js=>($k>=7)
));
同时有多个真时,array_search低版php中返回最后一个(可以倒过来写),高版php中返回第一个.
如果找不到会返回null或false,此时可特别赋值给$tmp
array_search可以用代码字串作索引,外面再eval的方法代替下面的switch

但array_search不方便处理非字串转换的情况,可以用:
case后必须有break,否则后面的语句包括default都会执行
default后面的case:...break;无效
==================================================================
switch的非常用法(可将一群if集中, 条理上好看点):
switch (true)
{case $k<=3:
      echo $k+1;
 break;

 case ($k>3) and ($k<7):
      echo $k+2;
 break;

 case $k>=7:
      echo $k+3;
 break;
}
switch(expression)内的expression可以是数字, 小数, 或字串, 不能是对象或数组,
case里比较值时js用的是严格比较, php是宽松比较.
==================================================================
list,each不光能用来遍历数组,也可以用来遍历对象的属性
==================================================================
PHP4.2.0之前:
用array_search搜索一个数字索引数组时,判断是否搜到不能用!=NULL,
因为如果找到索引0也会==NULL的,应使用!==NULL
=====================================================================
当心:
(TRUE and TRUE)===TRUE是假(TRUE and TRUE)===1是真
(TRUE && TRUE)===TRUE是假(TRUE && TRUE)===1是真
(php4.0.6后面的版已经解决了(TRUE and TRUE)===1的问题)
'hello'==0是真
======================================================================
php中的所有对象和数据都是活动对象和数组,可以随时不经声明使用新的元素,导致错误元素名亦不会报警
======================================================================
a文件内调用了b文件,当c文件或d文件再分别来调用a文件时,会分别按照它们各自的相对路径来寻找b文件,
而导致a文件内调用b文件的相对路径失效,因而所有文件调用应该转换成"系统绝对路径"
(不同于header('Location:')可以使用"wwwroot绝对路径"),
想到的一个方法是所有的文件都如下调用其它文件:
if (!defined('REALROOTPATH')) define('REALROOTPATH', substr(__FILE__, 0, -strlen(<本文件的"approot绝对路径">)));
//approot的系统绝对路径
//使用__FILE__而非$_SERVER['SCRIPT_FILENAME']是为了适应更多种类的web server

if (!defined('ROOTPATH')) define('ROOTPATH', substr($_SERVER['SCRIPT_NAME'], 0, -strlen(<本文件的"approot绝对路径">)));
//approot的wwwroot绝对路径
//(approot是web虚拟目录时,在这里就像是wwwroot下的子目录)

require REALROOTPATH . '<被调文件的"approot绝对路径">';
//test1.php的"系统绝对路径"
//(不能用wwwroot的系统绝对路径与ROOTPATH凑出approot的系统绝对路径,因ROOTPATH可能是虚拟路径,且$_SERVER['DOCUMENT_ROOT']格式不稳定)

header('Location: ' . ROOTPATH . '<被调文件的"approot绝对路径">');//注意Location后要有空格
//test1.php的"wwwroot绝对路径"

(注意:index.php的<approot绝对路径>是'')("wwwroot绝对路径"类同于javascript里的window.location.pathname)
因为$HTTP_SESSION_VARS其实是“全会话变量”而不止是“全局变量”
常量是类似全局变量,且在函数内不需要global声明
php里没有"全应用变量",可以用(速度从慢到快):
file<database<memcached(memory cache daemon)</dev/ram0(ram disk)<apc(Alternative PHP Cache)
来实现, 最方便的还是apc和memcached(自动存储区容量,过期时间,对象类型), cache分布式用memcached, apache分布式用apc
还有不需要改动程序的缓存方法squid(发音/skwid/), 自动缓存URL正则指定页面并设过期时间(不应缓存跟session相关的网页)
缓存结果集最好能有一个快速的sql语句规格化的函数(phpmyadmin里的还不够), 免得中间只相差一个空格的sql语句结果集重复缓存

如果将所有文件都放在顶层main文件夹内,则可:
if (!defined('REALROOTPATH')) define('REALROOTPATH', substr(__FILE__, 0, strpos(__FILE__,'main')+4));
if (!defined('ROOTPATH')) define('ROOTPATH', substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'],'main')+4));

简单的解决:
require_once dirname(__FILE__).'/../lib/common.php';
=======================================================================
要在ORM里分析出将要查询的sql条件中哪些数据已取出不现实, 但是分析出哪些sql条件已查询是可能的,
这就成为一种减少同一个页面内重复查询的方法(sql汇聚预处理, 各个函数并不真正返回数据而是只登记需要把数据放到什么地方):
select f1 from t1 where c1与select f2 from t1 where c1合并成select f1,f2 from t1 where c1
select f1,f2,f3 from t1 where c1与select f1,f2,f3 from t1 where c2合并成select f1,f2,f3 from t1 where c1 or c2
update t1 set f1=v1 where c1与update t1 set f2=v2 where c1合并成update t1 set f1=v1,f2=v2 where c1
各个函数不实际载入数据, 而是注册需求:
in fun1:$GLOBALS['pre_sql']->about('t1')->need('f1', 'c1');
in fun2:$GLOBALS['pre_sql']->about('t1')->need('f2', 'c1');
而在映射数据到模板前:$GLOBALS['pre_sql']->load();
映射数据到模板:$tpl['f1']=$GLOBALS['pre_sql']->about('t1')->data[0]['f1'];
而这需要"reduction of comparison logical expression"的函数
比如将"id=3 or id=5 or id=8 or id>7"合并成"id in (3,5) or id>7"
上述实际中没有什么意义, 因为fun1, fun2很可能不能预先知道查询条件, 必须等一个查询结果出来后才知道后面的查询条件.
=======================================================================
apc(Alternative PHP Cache)在php.ini里的配置:
(windows的php_apc.dll从http://museum.php.net/php5/下)
(linux从http://pecl.php.net/package/APC下载后编译, 版本依赖未明, 比如php5.0.5中实不能高于apc3.0.8)
extension=apc.so
apc.enabled = 1 #启用APC
apc.cache_by_default = 0 #关闭网页opcode缓存功能, 只使用用户变量缓存
apc.user_ttl = 3600 #充满时过期时间
apc.shm_segments = 2 #共享内存块数
apc.shm_size = 10 #单块容量(M)
有时需要单独杀apc线程, 先ps auxf|grep apc, 然后再kill
apc使用:
write.php:
apc_store('var1', array('kkk'), 10);
read.php:
var_export(apc_fetch('var1'));
将resource类型存入APC将得到NULL, 在同一页面都不行
=======================================================================
当以下代码作为test1.php被包含在test.php中执行:
echo $_SERVER['SERVER_NAME'].'<br>';      /* www.diyism.net 由服务器设置决定的 */
echo $_SERVER['HTTP_HOST'].'<br>';        /* www.diyism.net 由当前访问取得 可由客户端发送欺骗,不可靠*/
echo $_SERVER['SERVER_PORT'].'<br>';      /* 80 */

echo $_SERVER['PHP_SELF'].'<br>';         /* /phptest/test.php 由客户端请求取得, 如/phptest_alias//Test.php*/
echo $_SERVER['SCRIPT_NAME'].'<br>';      /* /phptest/test.php 由客户端请求取得, 如/phptest_alias//Test.php*/
echo strtr($_SERVER['SCRIPT_FILENAME'],array($_SERVER['DOCUMENT_ROOT']=>'')).'<br>';/* /phptest/test.php 实际执行(转换了路径别名)*/
echo dirname($_SERVER['PHP_SELF']).'<br>';/* /phptest 注意dirname('/test.php')得到的是"\" */

echo $_SERVER['QUERY_STRING'].'<br>';     /* kk=jj&bb=dd */

echo $_SERVER['REQUEST_URI'].'<br>';      /* /phptest/test.php?kk=jj&bb=dd 可由客户端发送欺骗,不可靠*/
echo $_SERVER['HTTPS'].'<br>';            /* 'on' or 'off' */

echo $_SERVER['SCRIPT_FILENAME'].'<br>';  /* e:/www/PHPApps/phptest/test.php */
echo realpath('./test.php').'<br>';                /* e:\www\PHPApps\phptest\test.php */
echo __FILE__.'<br>';                              /* e:\www\PHPApps\phptest\sub\test1.php */
/* 注意:realpath不支持所有wwwroot绝对路径 */

echo basename('./test.php').'<br>';               /* test.php */
echo dirname('./test.php').'<br>';               /* . */

echo $_SERVER['DOCUMENT_ROOT'].'<br>';   /* e:/www/PHPApps/ROOT 有些web server使用"\"代替"/",并且末尾多个"/" */
/* wwwroot的系统绝对路径 */
echo $_SERVER['PATH_TRANSLATED'].'<br>'; /* e:/www/PHPApps/ROOT/sub/test.php 有时是其它结果 有些web server使用"\"代替"/" */

串成自身的url:
'http'.(strtolower($_SERVER['HTTPS'])=='on'?'s':'').'://'.$_SERVER['HTTP_HOST'].':'.$_SERVER['SERVER_PORT']
.$_SERVER['SCRIPT_NAME'].($_SERVER['QUERY_STRING']==''?'':'?').$_SERVER['QUERY_STRING'];
客户端访问url:
'http'.(strtolower($_SERVER['HTTPS'])=='on'?'s':'').'://'.$_SERVER['HTTP_HOST'].':'.$_SERVER['SERVER_PORT']
.$_SERVER['REQUEST_URI'];
=======================================================================
看客户端信息,比如是不是robot:$_SERVER['HTTP_USER_AGENT']
=======================================================================
php与其它web服务器语言不同,传递checkbox多选参数时要求将checkbox命名成未定长数组,
即变量名加两方括号的形式,这个名字被javascript直接调用会产生错误,
可以给每个checkbox另加一个正常变量名id的方法以便javascript调用.
================================================================================
php 5.2.0以后JSON(JavaScript Object Notation)内建:
$arr=array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
echo json_encode($arr);//得字串'{"a":1,"b":2,"c":3,"d":4,"e":5}'
但是反过来json_decode得到的却是对象, 而不是文字索引数组, 要加个参数:
var_export(json_decode('{"a":1,"b":2,"c":3,"d":4,"e":5}', 1));
在ajax的里仅需obj_rtn=eval('('+responseText+')');//必须加圆括号, 因对象声明未进入表达式前并未建立
//或者这样写:eval('obj_rtn='+responseText);
如果服务器端返回json串不可信任, 比如夹杂了非对象js代码(比如串上"&& alert('')"), 最好用专门的js库解析json字串
php将数组转js对象(json), js里用eval获得对象, 然后用TrimPath JS Template搭配模板解析出来(见本目录)
JST放在Smarty的模板内时要加literal:{literal}<textarea id="out_tpl" style="display:none;">...</textarea>{/literal}
如果为了搜索引擎能索引页面, 可以倒过来, 页面大框架里直接填json代码, 然后再用xmlhttp或iframe把小块的模板载进来供js套用.
================================================================================
可以这样判断是否从ajax过来的请求(firefox, ie皆可):
isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest'
================================================================================
php里虽有create_function函数, 但由于没有类单引号的多行字串(heredoc), 也不能直接使用函数返回值,
而无法使用类似js里创建临时函数并立即使用以构造封闭代码段(变量不对全局变量产生影响),只能简单写法:
function my_closed_block()
         {...
         }
my_closed_block();
或者(类似eval, 但是不会泄露变量):
${!${''}=create_function('$v', 'echo $v;')}('hello');
php 5.3里可以这么干了:
$my_fun=create_function('$v', <<<'fun_code'
echo $v;
fun_code
');
create_function结果即使没有赋值给变量也会占用内存, 如果在循环内就会导致内存溢出, 可以写成:
isset($fn)?$fn:$fn=create_function('$v', 'echo $v;');
================================================================================
由于checkbox不打勾时什么数据也不传, 对数据更新操作不方便, 只好每个checkbox弄个hidden字段对应:
<input type="hidden" name="method[0]" value="0">
<input type="checkbox" onclick="document.getElementsByName('method[0]')[0].value=(this.checked?1:0);">A、有组织的汽车团购<br>
<input type="hidden" name="method[1]" value="0">
<input type="checkbox" onclick="document.getElementsByName('method[1]')[0].value=(this.checked?1:0);">B、自发性的汽车团购<br>
一组radio如果未设初始值也不会提交,应该默认选上一个:
<input type="radio" name="my_car_method" value="0">A、有组织的汽车团购<br>
<input type="radio" name="my_car_method" value="1" checked>B、自发性的汽车团购<br>
而value最好从1开始以方便接受页面里判断
==========================================================================
c语言中,++$i是将$i增1并返回现$i,这比较符合逻辑习惯,建议统一采用++$i,而不使用$i++,万一要返回i原值可以写为++$i-1
==========================================================================
eval里面的字串的执行结果要return出来必须:
return eval("...return ...");
$the_name=eval('return $name_'.$i.';');
==========================================================================
由于比较运算符(<,>,===等)的优先级(粘连度)高于逻辑运算符&&(或and)和||(或or),所以不用担心"$k>3 and $k<7"返回不了期望结果
要注意逻辑运算符最好用&&和||,因为and和or的优先级低于赋值号=,则$flag= true and false;的$flag将得到true
而$flag=true?false:true倒是不用担心会得到true
==========================================================================
在mysql4.1以上可以使用mysqli_multi_query来执行多条sql查询,
在mysql4.1以前可以这样实现mysql_multi_query:
function mysql_multi_query($str_sql)
         {$str_sql=sql_safe($str_sql);/* 防范sql注入攻击 */
          $str_sql=preg_replace(array('/#.*\s/','/-- .*\s/','/\/\*[\S\s]*?\*\//'), array('','',''), $str_sql);
          $arr_sql=explode(';',$str_sql);
          for ($i=0,$cnt=count($arr_sql);$i<$cnt;++$i)
              {if (trim($arr_sql[$i]))
                  {@mysql_query($arr_sql[$i]);
                  }
               if (mysql_error())
                  {return false;
                  }
              }
          return true;
         }
注意:mysql是执行单行sql的,多行的sql字串会被它自动去掉换行符,
这将导致"-- "或"#"注释阻止它后面一行sql语句的执行,
所以先过滤掉了注释
==========================================================================
mysql_error()传出mysql传给php服务器的报错,不使用该函数则只能看到php服务器执行数据库函数本身的报错
用if (mysql_error()!=null) echo $sql;来显示出错对应的sql
=========================================================================
包含文件执行中的变量并不跨页面,因而页面之间不会共用mysql连接,那么在一个页面内create temporary table
即使与其它页面中的temporary table同名也不会互相干扰,该页面数据库连接结束后,temporary table会自动删除
==========================================================================
'978'<'8734324'===TRUE,因为php会自动将两个数字字串转为数字后再比较
==========================================================================
输出文件:
header('Content-Type: application/octetstream');
header('Content-Disposition: inline; filename="' . $filename . '"');
将inline改用attachment则强制下载:
$filename="img\ok.png";
$fp = fopen($filename, 'rb');
header("Content-Type: application/octetstream");
header('Content-Disposition: attachment; filename="' . $filename . '"');
fpassthru($fp);
在ie下, 如果要在送出文件类型前session_start()来检查user权限等,
则需要在session_start()前session_cache_limiter('public');否则下载时会报找不到文件
(虽然session_start()不会送出header信息,在header()前后都可以)
有时ie6直接打开强制下载zip文件会报error: cannot open ... temporary internet files ...,
是因为ie6认为linux服务器zip的文件夹非法而未缓存.
===========================================================================
输出excel文件:
先要把excel模板存为xml表格文件,
<?
header('Content-Type: application/octetstream');
header('Content-Disposition: attachment; filename="stat.xls"');
?>
excel文件导出的xml代码
(注意:
1.前两行要处理一下:
<<??>?xml version="1.0"?<??>>
<<??>?mso-application progid="Excel.Sheet"?<??>>
2.注意ExpandedRowCount要计算正确,否则报错
3.所有中文输出要确保为utf-8:mb_convert_encoding($user[0]['username'],'UTF-8','GBK');
)
===========================================================================
10进制转36进制,返回字串:base_convert('20', 10, 36);
==========================================================================
aFunction() or exit($aString)功能是如果前面函数执行失败则输出$aString并停止执行后面的脚本
exit或die是一个语法结构,而非函数
整数参数表错误代码,要输出整数:exit(''.$aInteger)或exit(strval($aInteger)), 因exit并不自动转型
最好用重载过的exit函数: $php->exit($aInt);
"echo true;"是将true转'1'输出.
====================================================================
substr($aStr,index_start,length(>=0)/index_after_end(<0))
index_start:0|起位,-1|末位,负超长|当零,正超长|执行失败;length:省略或正超长|当末长,-1|末位,负超位|当0起位
操作多字节字串:mb_substr($rslt[$i]['title'],0,21,'utf-8')
====================================================================
搜索子串的位置:strpos(str_hay,str_needle), 未找到返回false
搜索最后一个子串的位置:strrpos(str_hay,str_needle), 未找到返回false
====================================================================
注意">="、"<="运算符里的"="号不是严格比较,最好分成">"和"==="两句写
====================================================================
"continue;"跳过当前循环,"continue 2;"跳过两次循环
"break;"停止整个循环,"break 2;"停止所在循环及上层循环
for、while、switch都属于循环结构
====================================================================
将一个未设变量当作对象并对其假想属性赋值,会自动创建一个内定类stdClass的实例,"(object)NULL"获得stdClass的一个空实例(对象)
===============================================================================================
用gettype取得变量类型,用get_class取得对象变量的类
===============================================================================================
将数组的数组按索引排序后再依次取出:
ksort($aArray);
reset($aArray);
while (key($aArray)!==null)
      {echo $aArray[key($aArray)][type].'<br>';
       echo $aArray[key($aArray)][qty].'<br>';
       next($aArray);
      }
注意:不能在do后使用while (next($aArray)),因next返回$aArray的一个元素可能==false(如0,null等),
也不能使用while (next($aArray)!==false),因如果返回的一个元素真的===false,则next这个函数无法分辨是否失败.
也不能使用while (each($aArray)!==false),因each在来到最后末位元素的时候,取出它的键和值作为返回值,并把指针移出末位.
上面语句也可写成:
ksort($aArray);
for (reset($aArray);key($aArray)!==null;next($aArray))
    {echo $aArray[key($aArray)][type].'<br>';
     echo $aArray[key($aArray)][qty].'<br>';
    }
===============================================================================================
期望php有直接取元素值的函数,以免因目前current的结果不支持方括号取值而使用$aArray[key($aArray)]消耗资源.
(还可以用eval('$tmp=current($aArray);return $tmp[type];')更麻烦的写法)
还是自己写个函数(据测试这种写法执行效率比赋值后取元素来得高):
function &arr_ele(&$aArray,$key)
         {return $aArray[$key];
         }
那样上面的$aArray[key($aArray)][type]就可写为arr_ele(current($aArray),'type');
还有一种恐怖的写法:
array_shift(array_slice(current($aArray),array_search('type',array_keys(current($aArray))),1));
如果只是要取出数组的第n(>=0)个元素值:
reset(array_slice($aArray,n,1));
也可以这样写:echo ${!${''}=&explode(',','a,b,c')}[1];(见http://bugs.php.net/bug.php?id=23022)
取第一个元素和最后一个元素可以分别用reset()和end()都有返回值, 但是都会影像指针位置
===============================================================================================
往数组指定位置前插入元素:
array_splice($aArr, $index_insert_at, 0, array($ele));
替换元素:
$arr_replace_out=array_splice($aArr, $index, 1, array($ele_replace_in));
===============================================================================================
全排列函数:
function array_permute(&$items, $perms=array(array()))
         {$aNewOrder=array();
          $vInsert = $items[count($perms[0])];
          for ($i=0; $i<count($perms); $i++) 
              {$aOld=$perms[$i];
               for ($j=0; $j<=count($perms[$i]); $j++)
                   {$aNew=$aOld;
                    array_splice($aNew, $j, 0, $vInsert);
                    array_push($aNewOrder, $aNew);
                   }
              }
          if (count($aNewOrder[0])==count($items))
             {return $aNewOrder;
             }
          return array_permute($items, $aNewOrder);
         }
$arr=array(1,2,3,4);
$result=array_permute($arr);
用array_permute()全排列来转化多遍正则匹配为一遍正则匹配,
比如对这样形式的表达式('&'表无顺序必有, '|'表或有, '&'比'|'优先):"jack|lyrics&madonna|peter":
function ampsreg_to_preg($reg)
         {$reg=explode('|', $reg);
          foreach ($reg as &$v)
                  {$v=trim($v);
                   $v=explode('&', $v);
                   foreach ($v as &$v1)
                           {$v1=trim($v1);
                           }
                   $v=array_permute($v);
                   foreach ($v as &$v2)
                           {$v2=implode('.*', $v2);
                           }
                   $v=implode('|', $v);
                  }
          unset($v);unset($v1);unset($v2);
          return implode('|', $reg);
         }
===============================================================================================
new aClass()不能直接取属性, php5里可以重新封装new aClass为create函数:
function create($name_cls)  //这里不需要前缀&符号, 类似赋值(=)运算, 返回(return)对象默认就是传址的
         {$object=new $name_cls;
          return $object;
         }
例:
class my_cls
      {var $a='access a new object';
      }
echo create(my_cls)->a;
//$aObj实际是一个引用, "$aObj->par1"里先自动对$aObj解引用, "function1()->a"里到php5才自动对function1()解引用
但php5里没有实现返回数组时直接取元素(虽然函数返回的数组并不是一个引用), 见:
http://bugs.php.net/bug.php?id=23022
php的开发者不打算实现支持"function1()[...]"语法(虽然不需要做自动解引用)
//dereference的意思:c语言里的*就是个"解引用运算符", *aPointer就是对aPointer解引用以获得实体
===============================================================================================
foreach也可传址操作元素:
foreach ($rtn as &$v)
        {$v=$v."\r\n";
        }
===============================================================================================
判断当前节点是否最末节点:
function array_is_last($arr)
         {$tmp=array_slice($arr, -1, 1, true);
          return key($arr)===key($tmp);
         }
如果在循环中用,最好在循环前先用:
function last_key($arr)
         {$tmp=array_slice($arr, -1, 1, true);
          return key($tmp);
         }
$last_key=last_key($arr);
再在循环里:
...$k===$last_key...
也可:key($array)===array_pop(array_keys($array));(php5.2里似乎要写成...array_pop($tmp=array_keys(...)
===============================================================================================
array_flip()是调换数组的索引与值的位置, array_reverse()是调换数组元素的前后次序.
当心array_reverse()默认会修改数字索引, 所以最好用array_reverse($arr, 1);以保持数字索引.
===============================================================================================
array_multisort会将array(row1=>array($col1[1],$col2[1]),row2=>array($col1[2],$col2[2]),
row3=>array($col1[3],$col2[3]))先按$col1排序,在按col2排序,最后保持文字索引和重整数字索引,并没有按索引排序.

自己写的排序函数:
/* SORT_DESC,SORT_ASC;SORT_REGULAR,SORT_NUMERIC,SORT_STRING */
function get_array_multisort($db_table, $col_typ_dirs)
         {$i=-1;
          $col=array();
          reset($db_table);
          while (key($db_table)!==null)
                {for ($j=0,$cnt=count($col_typ_dirs);$j<$cnt;++$j)
                     {$tmp = explode(',', $col_typ_dirs[$j]);
                      eval('$col'.$tmp[0].'[]=$db_table[key($db_table)][\''.$tmp[0].'\'];');
                     }
                 $the_key['key_'.key($db_table)]=++$i;
                 next($db_table);
                }

          $par_arr='';
          for ($j=0,$cnt=count($col_typ_dirs);$j<$cnt;++$j)
              {$par_arr=$par_arr.'$col'.$col_typ_dirs[$j].',';
              }
          eval('array_multisort('.$par_arr.'$the_key);');

          $arr_tmp=array();
          reset($the_key);
          while (key($the_key)!==null)
                {$arr_tmp[substr(key($the_key),4)]=$db_table[substr(key($the_key),4)];
                 next($the_key);
                }

          return $arr_tmp;
         }

print_r(get_array_multisort($data,array('volume,SORT_REGULAR,SORT_ASC','edition,SORT_REGULAR,SORT_ASC')));
===============================================================================================
四舍六入(注意不是"四舍五入","五"随其它位上数字均分)函数:
round($aFloat,$aInt);
当$aInt为0时表示将$aFloat四舍六入为整数,
当$aInt为2时表示将$aFloat四舍六入为保留两位小数,
当$aInt为-3时表示将$aFloat四舍六入到万位.
求乘方:
pow(2,3)==8
===============================================================================================
函数内的函数用global访问的是顶层的变量而不是其上层函数的变量
===============================================================================================
碰到一个查了半天才查出来的问题,原来是有一个汉字空格混在了代码中导致编译不通过,看起来是空格,
但在editplus中灰点才是真正空格,软换行的地方才是空的.
===============================================================================================
number_format($number,2)将数字以标准会计格式字串输出,
一般使用number_format($number,2,'.','')即不进行三位分隔,
php在echo数字时会自动去尾零,而round的结果是数字不是字串仍会被echo去尾零
===============================================================================================
取得文字索引数组$aArray第$i号元素的键和值:
key(array_slice($aArray, $i, 1))
$aArray[key(array_slice($aArray, $i, 1))]//不要用current,否则不便处理元素为数组的情况
这种方法对于数字索引数组无效,因php5.0.2以前array_slice自动重整了数字索引
===============================================================================================
关于单据状态的一次错误设计:
一.错误设计:
a.单据的初始状态是相同的
b.一个审核权(一个command)将单据从a或b状态变更到c状态,即根据目的状态定义审核权
二.真实需求:
a.单据的初始状态随其属性而不同
b.一个审核权(一个command)将单据从a状态变更到c状态或d状态,即根据当前状态定义审核权
===============================================================================================
session_start会将php.ini里session.cache_limiter的值作为该页面在客户端的缓存方式,
为了方便表单提交出错回退能显示已填数据,可以在session_start前面session_cache_limiter('none');
===============================================================================================
php直接访问ms access数据库(odbc sql语法可以参考ms access里面的帮助):
odbc_connect ('DRIVER=Microsoft Access Driver (*.mdb);DBQ='.$REALROOTPATH.'/di/smithjxc.mdb;', $User, $Pw);
查询用odbc_exec(connection_id, query_string),取得一行结果用odbc_fetch_array
===============================================================================================
access:文本/备注/双精数字/整型数字/日期时间
odbc_columns:VARCHAR/LONGCHAR/DOUBLE/SMALLINT/DATETIME
odbc_exec(typename()):String/String/Double/Integer/Date
后面两者都算ms jet sql
===============================================================================================
不论是使用(int)...还是intval(...)还是settype(...,'integer')来将浮点数类型转换整数类型,
都必须先将浮点数round:
(int)((0.1+0.7)*10);得到7
(int)round((0.1+0.7)*10);得到8
(string)((0.1+0.7)*10);得到8
===============================================================================================
在三段逻辑运算符中, 经常要让一个表达式的返回值为空字串: <?=$first?($first=0&&false):'un_'?>
===============================================================================================
读取文件用file(整个文件按行进数组)和file_get_contents(整个文件进字串)比较方便, 已开文件句柄可以用stream_get_contents
fgets从文件读取一行(结合fseek_ln效率比较高, 见下),fread从文件读取指定数量字节(结合fseek效率比较高);
写操作只有一个函数fwrite(等价fputs)
function fseek_ln($fp, $start)
         {fseek($fp,0);
          for ($i=0;$i<$start;++$i)
              {fgets($fp);
              }
         }
当心:不论是file还是fgets得到的行都是未去除换行符的, file()可以加第二参数FILE_IGNORE_NEW_LINES来去除换行.
去除换行符不要直接用rtrim, 而应该用rtrim($str, "\r\n"), 以免误删了" ", "\t", "\0", "\x0b".
遍历文件所有行:
while ($row=fgets($fp)) {...}
===============================================================================================
readfile和fpassthru不是用来读取文件的,而相当于一个文件中继,读取马上就输出
===============================================================================================
fopen (<path>, <mode>);其中mode:
r,只读取
r+,读取且覆写
w,只重写,创无
w+,重写且可读,创无
a,只补写,创无
a+,补写且可读,创无
注意:
a或a+时不管指针在何处写操作总是在末尾;
r+,w+,a+在写后的读操作需要先使用ftell和fseek来取位和定位
===============================================================================================
删除文件使用unlink(<path>),创建文件用fopen(,'w+')
===============================================================================================
四种基本文件操作:只读取、覆写、插入(包括补写)、截去(包括截尾,重写)
可以分别用r,r+,r+(结合fread),r+(结合ftruncate)完成,
则w,w+,a,a+四种文件打开模式皆可不用,为了模块化方便连r都可不用,统统用r+,而binary操作时用rb+
===============================================================================================
如果一个php程序以只读打开并逐行读取一个文件echo出来, 同时另一个进程在php未执行完成时覆盖该文件,
则结果是php会把旧内容完整输出并将新内容里超过旧内容长度的后面部分全部输出,
如果新内容短于旧内容则只完整输出旧内容,
如果另外一个进程移除文件, 结果也是能输出完整旧内容.
(在windows和linux下分别测试结果一样)
===============================================================================================
To acquire a shared lock, a process must wait until there are no processes holding any exclusive locks. To acquire an exclusive lock, a process must wait until there are no processes holding either kind of lock.
利用flock构造原子操作块(in linux有效, 不要用flock($fp, 2), 因等待解锁可能导致死锁, +4就表示不等待):
$path_file_lock='counter.lock';
$fp=file_exists($path_file_lock)?fopen($path_file_lock, 'r+'):fopen($path_file_lock, 'w+');
for ($i=0, $time_out=3; $i<$time_out; ++$i)
    {if (flock($fp, 2+4))
        {$is_locked=1;
         break;
        }
     sleep(1);
    }
if (isset($is_locked))
   {/* 原子操作块 */
   }
else
    {/* failed to lock */
    }
fclose($fp);//don't lose, to release file lock
===============================================================================================
如果用file_exists判断文件不存在则使用fopen(,'w+')替代fopen(,'r+')(否则会报错),后面的操作如中间覆写可完全不变:
$fp=file_exists($path_file_lock)?fopen($path_file_lock, 'r+'):fopen($path_file_lock, 'w+');
所有操作完成后记住关闭文件fclose
===============================================================================================
关于ie错误地处理url参数部分中汉字的问题:
(ie中使用utf-8发送所有url的设置只对url的路径部分有影响,而对参数部分没有作用)

test.php使用utf-8编码:
在地址栏提交"http://localhost/test.php?kk=我",接收到kk乱码"B/"(是GBK系列编码),地址栏仍"http://localhost/test.php?kk=我"
点击链接"http://localhost/test.php?kk=我",接收到kk乱码"戯"(被ie当做GBK纠错后送出),地址栏仍"http://localhost/test.php?kk=我"
在post form的action内提交kk=我,input text内jj=冬,kk乱码"戯",jj正常接收,地址栏"http://localhost/test.php?kk=我"
在get form的action内提交kk=我,input text内jj=冬,kk被忽略,jj正确接收,地址栏"http://localhost/test.php?jj=%E5%86%AC"

两个utf-8汉字被ie视作三个合法的gbk汉字而不纠错,一个utf-8汉字被ie视作一个合法的gbk汉字(前两字节)和一个非法字节(统转%AF)
问题就出在ie对url参数部分的GBK纠错,既然它不知道目标页的编码就不应该擅自对url参数部分进行GBK纠错
firefox/opera浏览器不存在"地址栏提交"和"链接提交"的问题,因为它不会擅自改动url(即使同ie一样也进行urlencode)

如果你用utf-8做国际站(同时显示多种文字),而访客群使用ie浏览器比较多,你就不要想在链接url参数部分放汉字/日文/韩
语了(因已被ie破坏),也不要想你的访客在地址栏url参数部分使用汉字/日文/韩语(因ie传过来的编码种类各不相同,你无法处理)
解决办法是出现在链接中的中日韩文字须先行urlencode
===============================================================================================
如果文件内没有高段字符,则utf8编码和gbk系编码没有区别,导致在editplus中往该文件添加汉字后自动保存为了gbk编码
===============================================================================================
$str='this';$str[2]='bad';echo $str;得到thbs,可见字符串数组的元素(单字符型)并不像其它数组元素能随意变换类型,
实际上php4以后字串不再被当做数组,访问字串中字符的标准语法是$str{2}
===============================================================================================
phplib对于表格内的行循环、多个变量的组合运算等并不能提供方便,可能需要{arr1[var1,var2]}和{var1*var2}这样
的高级数据标记以及支持这些标记的网页编辑器,才能像模像样地实现界面人员与代码人员的分解作业,但实际上
在诸如数据分页这样的地方两者根本无法分解作业,还有界面人员更接近业务设计人员,界面的变更往往就是需求在变更
将导致其下业务逻辑层改变,即业务逻辑层和界面层虽然分立但都是供业务设计人员(界面人员辅助美术)使用的,代码人员
负责底层接口和解析高级数据标记.在这种高级数据标记及其编辑器没有实现之前,只有代码人员包打天下了.
对于简单的页面变动数据,比如多语言文字标签,可以采用动态包含各语言define列表文件的方法.
===============================================================================================
webapp架构:

index.php:跳转页(to /main/ui/main/main.php)
main:顶层目录(为了方便定位代码)

1.di:数据接口层(勉强对应MVC中的model层),存取数据,包括简单实体和复杂实体(复杂实体也可放在数据库的预储过程)
-stnd.php:标准接口,search是按需取数据
-main:各种实体具体接口,类似标准接口search是按需算数据,按实体分文件

2.bl:业务逻辑层(勉强对应MVC中的controller层),处理数据和调用di存取数据,按ui分文件(一个ui两个bl:进数/出数)
(如果一个ui有多个出口,则可在第x个出口的action上写成target.php?act=x,方便处理出数的bl分辨)
-main:对应页面节点的业务操作
-mdls:比较大的或公共的业务操作独立出来

3.ui:用户接口层(对应MVC中的view层),使用echo和循环显示bl传来的数据,并用js等传回数据
-prts:界面零件,所有css,js,图标等
-lngs:界面多语言词条定义文件
-main:界面主框架,数据流节点
-mdls:界面模块,如页眉、页脚,翻页列表组件
架构的要点是: 相关聚合, 非关分离.
===============================================================================================
mt_rand生成随机数比rand快且域更大,两者在php4.2.0以后皆无需置随机种子
===============================================================================================
设置密码时:
$salt = substr(mt_rand(), 3, 2);
$pw_enc = md5($salt.$pw_set).':'.$salt;
测试密码时:
$stack = explode(':', $pw_enc);
if (md5($stack[1] . $pw_test) == $stack[0])
为什么要这么写?
===============================================================================================
测试一段代码的php执行时间和页面执行时间(微秒计数和微秒差):
echo 'php:'.($t1=time().substr(microtime(),2,6));
?>
<br><script>document.write('js:'+(new Date()).getTime());</script><br>
<?
kk();
?>
<br><script>document.write('js:'+(new Date()).getTime());</script><br>
<?
echo 'php:'.($t2=time().substr(microtime(),2,6));
echo 'time_length:'.((float)$t2-(float)$t1);
===============================================================================================
未经zend编译的php代码,
包含(require_once)一万行加法代码的时间(200ms)是执行这一万行代码(100ms)的两倍,
而输出这一万行结果到页面的时间(30000ms)是执行的300倍,
包含(require_once)一万行sql select代码的时间(400ms)是执行这一万行代码(8000ms)的1/20倍,
可见包含代码时间基本是个固定量(如果结构化而不是直接一万行代码则时间急剧减少),
而执行代码时间则随执行次数线性增加,输出时间更是巨大,
因而要重点考虑执行效率和输出效率
===============================================================================================
标准写法字符集定义要放在标题前面(虽然firefox/opera可以正常解析,但ie会出错):
<META http-equiv=Content-Type content="text/html; charset=utf-8">
<TITLE><?=cart;?></TITLE>
===============================================================================================
用php执行外部命令时shell_exec('cd /');只对该行php有效, 用echo shell_exec('cd /;pwd');可看出,
要用chdir('/');才能让后面的php语句里也有效.
用sudo crontab -e在cron任务表里添加(每三分钟访问一次网页):
*/2 * * * * /usr/bin/wget -q -O /dev/null http://192.168.0.1/up_ip.php
可以这样用php执行命令行:
(先用sudo visudo -f /etc/sudoers里加上:www-data ALL=(ALL) NOPASSWD:/etc/init.d/proftpd restart)
shell_exec('sudo /etc/init.d/proftpd restart');
shell_exec最方便(四种方式的all lines都不是命令行里真正返回的all lines):
(apache账户没有权限的shell命令不会有stdout或stderr, 因而加" 2>&1"也没用
事实上所有文件输出符号都无效, 即使输出到子命令或从子命令输出:shell_exec("wc -l <(echo -e 'kk\\nkk')");
改成这样就行了:shell_exec("echo -e 'kk\\nkk' | wc -l");
一个完整的例子:echo shell_exec("echo -e 'he\\nl\\nlo' | (tmp=\$(</dev/stdin);echo \"\$tmp\" | wc -l;echo \"\$tmp\")");
要想返回错误可以用proc_open:
$proc=proc_open("echo -e $(</dev/stdin) | wc -l", array(0=>array('pipe', 'r'), 1=>array('pipe', 'w'), 2=>array('pipe', 'w')), $pipes);
fwrite($pipes[0], 'h\\nel\\nlo');fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]);fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]);fclose($pipes[2]);
$rtn=proc_close($proc);
echo 'stdout:'.htmlspecialchars($stdout).'<br>';
echo 'stderr:'.htmlspecialchars($stderr).'<br>';
echo 'return:'.htmlspecialchars($rtn).'<br>';
)
○string shell_exec ( string cmd )(PHP 4, PHP 5)
(all lines)(command)
Execute command via shell
○string exec ( string command [, array &output [, int &return_var]] )(PHP 3, PHP 4, PHP 5)
(last line)(command)(append every line,without trailing whitespace,\n etc)(return status)
Execute command via shell
○string system ( string command [, int &return_var] )(PHP 3, PHP 4, PHP 5)
(last line or FALSE on failure)(command)(return status)
Execute command via shell and display the output(automatically flush web server buffer each line)
○void passthru ( string command [, int &return_var] )(PHP 3, PHP 4, PHP 5)
(null)(command)(return status)
Execute command via shell and display raw output(ie. binary data)
===============================================================================================
把用sqlyog从mysql导出的csv文件当作数据库来查询(好处是能跨表查询及排序):
(awk 3.1.4开始regular expression matching支持utf-8字符)
$fields_ori=array('KeyID','Keyword','ChannelID','CategoryID','ChlScore','CatScore','ApvChannelID','ApvCategoryID','AccountID','CampaignID','BatchNum','AdgroupID','ApproveMethod','Type','AddTime','LastChangeTime');
$fields=array_flip(array_merge(array(''), $fields_ori));//first blank element for awk index

$order='';
$arr_order=array('1'=>'', '2'=>'r');
$arr_order_char=array('1'=>'', '2'=>'n');
$order.=$items['order_keyword']?"-k{$fields['Keyword']},{$fields['Keyword']}{$arr_order_char[$items['order_keyword']]}{$arr_order[$items['order_keyword']]} ":'';
$order.=$items['order_addtime']?"-k{$fields['AddTime']},{$fields['AddTime']}{$arr_order_char[$items['order_addtime']]}{$arr_order[$items['order_addtime']]} ":'';
$order=$order?"| sort -t\$'\\t' ".$order:'';

$limit='';
$start=$items['pg_size']*($items['pg_no']-1);
$limit="NR>=".($start+1)." && NR<".($start+1+$items['pg_size']);//index start from 1, so added 1

$where='';
$where.="\${$fields['Keyword']} ~ /".preg_to_ereg(stripslashes($items['search_keyword']))."/ && ";
$where.="\${$fields['BatchNum']} == \"{$batch_id}\" && ";
$where=$where?substr($where, 0, -3):'';

$table="kw_{$data['ChannelID']}_*";

$cmd="cd ../../keyword_search/;awk 'BEGIN {FS=\"\\t\";IGNORECASE=1} {$where}' {$table} {$order} | awk '{$limit} {print} {++count} END {print count}'";//exit($cmd); //reset FS because default field seperator is space and tab
$out=shell_exec($cmd);
$out=explode("\n", substr($out, 0, -1));
$total_count=array_pop($out);
array_walk($out, create_function('&$v, $k, $arrIn', '$v=array_combine($arrIn, explode("\t", $v));'), $fields_ori);

return array('total'=>$total_count, 'data'=>$out);
其中转换preg为ereg的函数:
function preg_to_ereg($reg)
         {//convert preg in grep command to ereg in awk
          return strtr($reg, array('\S'=>'[^[:space:]]',
                                   '\s'=>'[[:space:]]',
                                   '\D'=>'[^[:digit:]]',
                                   '\d'=>'[[:digit:]]',
                                   '\b'=>'\y',
                                   '\''=>'\\47'
                                  )
                      );
         }
===============================================================================================
ie等浏览器不能自动识别php传出的xml文件的头部(被当做htm来处理了),必须发头消息:
header('Content-type: text/xml; charset=utf-8');
头消息比头部更早到达客户端, 有优先权
===============================================================================================
函数传址的写法:
$the_array=array();
$name_element_parent='channel__item category=&quot;column_headline&quot;__item category=&quot;marquee&quot;__item category=&quot;link&quot;__0__link';

function &path2ele($path_full, &$array_root)
         {$ref_rtn=&$array_root;
          $path_exp=explode('__', $path_full);
          for ($i=0,$cnt=count($path_exp)-1;$i<$cnt;++$i)
              {$ref_rtn=&$ref_rtn[html_entity_decode($path_exp[$i+1])];
              }
          return $ref_rtn;
         }

$ele=&path2ele($name_element_parent, $the_array);
$ele='jack';
var_export($the_array);
===============================================================================================
返回提交页面并刷新数据:
header('Location: ' . $_SERVER['HTTP_REFERER']);
解出来源页面url:
$url_ref=parse_url($_SERVER['HTTP_REFERER']);
注意:在Apache里, 送出"HTTP/1.1 302 Found"后会送出cookie然后再跳转(可以用输出js跳转而不用header的方法来解决),
而奇怪的IIS, 送出"HTTP/1.1 302 Object Moved"后立即跳转而不会送cookie(包括session cookie)
===============================================================================================
array_merge:后面数组的相同字串索引的元素会覆盖前面数组里的对应元素但相同数字索引元素不覆盖而是重新排队索引(作用于第一层元素),
array_merge_recursive:则将相同字串索引的元素累并到以该字串素引的一个下级数组元素里但相同数字索引元素不累并而是重新排队索引(作用于任意层),
+:后面数组的相同索引(字串或数字)的元素不覆盖前面数组里的对应元素(丢弃),前面数组没有该索引的元素合并进来(只作用于第一层元素).
欲往一个数组的头部插入带索引的元素, 用array_unshift不行(自动建索引), 用array_merge也不行(破坏数字索引),
用"+"才行.
有时explode一个字串, 为免数量不够而到后面用isset检查, 可以先用空array来补(非空不会被覆盖):
$tmp=explode(',', $v)+array('', '', '', '');
===============================================================================================
(服务器要装上libxslt(for linux):ftp://xmlsoft.org/libxslt/libxslt-1.1.20.tar.gz或启用php_xsl.dll(for windows))
将xml和xsl转成html:
$url=(($_GET['trg']=='http://www.xmnext.com/')?'http://www.xmnext.com/main/ui/index.xml':$_GET['trg']);
$xml = new DOMDocument;
$xml->load($url);

//载入XSL文件
$xsl = new DOMDocument;
$xsl->load(str_replace('.xml','.xsl', $url));

//转换为HTML
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); // 加载xsl
echo strtr($proc->transformToXML($xml), array('<body>'=>'<body><img src="/main/ui/images/noxml.gif" border="0" style="align:center" onclick="javascript:window.open(\'/main/ui/msxml3sp7cn.msi\');"></img>','<br></br>'=>'<br />'));
要注意所有的"<br />"都被转成了"<br></br>", 在ie下的xhtml里<br></br>被错误地解析成了两个换行, 所有要在写文件前把"<br></br>"都替换"<br />"
===============================================================================================
如果一批function是有规律的话, 可以通过eval来批量声明function
===============================================================================================
对象复制: $aObject=clone $bObject;直接赋值的话是传址的:$aObject=$bObject;
函数的参数如果是对象则也是传址的.
===============================================================================================
可以直接创建一个对象:
$aObject=(object)array('host'=>'smtp.163.com', 'user_name'=>'kexianbin@163.com');
===============================================================================================
$aObject->aEle='aFun';
可以这样调用变名函数:call_user_func_array($aObject->aEle, array($p1,$p2,...));
而不能直接$aObject->aEle($p1,$p2,...), 这样调的是$aObject的aEle()这个method
还有这种用法:
$aName='aEle';
echo $aObject->$aName;等价于echo $aObject->aEle;
===============================================================================================
class里面const,static元素都是属于class的而不是属于object的, 用"aClass::"访问;
public元素(默认)都是属于object的, 用"aObject->"访问
===============================================================================================
class内元素不能直接赋值为对象, 而要在初始化中赋值:
class aClass
      {var $aObject;
       function aClass()
                {$this->aObject=(object)array('host'=>'smtp.163.com','user_name'=>'disuse@163.com');
                }
      }
===============================================================================================
//让method在class内外皆可用
<?
class aClass
      {var $kk='ddd';
       var $bb='expert';
       function aFun($in,&$out,$use=array('$this->kk','$this->bb'))
                {if ($use!=array('$this->kk','$this->bb'))
                    {eval('global '.$use[0].';');
                     eval('global '.$use[1].';');
                    }
                 $out=$in.'hell'.eval('return '.$use[0].';').eval('return '.$use[1].';');
                }
      }

$aObj=new aClass;
$aObj->aFun('ren',$out);
echo $out;?><br><?

$p='ddd';$t='expert';
aClass::aFun('ren',$out1,array('$p','$t'));
echo $out1;?><br><?
?>
===============================================================================================
//可以直接建立只有属性的object,不可能直接建立含method的object,因为method是属于class的,而stdClass不可能修改
//可以近似实现:
$aObject=(object)array('aEle'=>'kkk', 'bEle'=>'ccc', 'aFun'=>create_function('$a,$b', 'return $a+$b;'));
$aFunName=$aObject->aFun;echo $aFunName(25, 36);
不允许直接: $aObject->aFun(25, 36);那是在调aFun这个方法而不是属性作函数名.
//或echo call_user_func_array($aObject->aFun, array(25, 36));
//或者echo ${!${''}=$aObject->aFun}(25, 36);
===============================================================================================
动态按需载入class文件(class1.php, class2.php):
<?
function __autoload($class_name)
         {require_once $class_name.'.php';
         }
$obj  = new class1();
$obj2 = new class2();
?>
===============================================================================================
//mb_detect_encoding并不能准确测出编码:
echo mb_detect_encoding('联通','ASCII,CP936,UTF-8',1);//必须这样顺序'ASCII,CP936,UTF-8'
echo mb_detect_encoding('虹口','ASCII,CP936,UTF-8',1);//必须这样顺序'ASCII,CP936,UTF-8'
判断是否utf-8编码应该用这个函数:
function is_utf8($string)
         {return preg_match('%^(?:
                              [\x09\x0A\x0D\x20-\x7E]            # ASCII
                            | [\xC2-\xDF][\x80-\xBF]            # non-overlong 2-byte
                            |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
                            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
                            |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
                            |  \xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
                            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
                            |  \xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
                               )*$%xs',
                            $string
                           );
         }
匹配utf-8汉字:
php里不能使用\u4e00-\u9fa5的写法([u4e00-u9fa5]想当于0-u所有的数字字母),应该这样写:
preg_match_all('/[\xe4-\xe9][\x80-\xbf]{2}/',$str,$mchs);
匹配gbk汉字(这并不可靠,如'判刑'中'判'字后半和"刑"字前半刚好是gbk的"行"字,下面的"手的"中间刚好是"值"字,等等):
preg_match_all('/[\x81-\xFE][\x40-\x7E\x80-\xFE]/',$str,$mchs);
匹配gbk中指定个数多字节字符或单字节字符(转码及用u修饰符):
$tmp=preg_replace(array('/(.{15})('.mb_convert_encoding($keyword,'UTF-8','GBK').')(.{15})/u'),array(']]>\1<![CDATA['),$content);
$content_rp=mb_convert_encoding($tmp,'GBK','UTF-8');
function is_gbk($str)
         {return preg_match('/(?:[\x00-\x80]|[\x81-\xfe][\x40-\x7e\x80-\xfe])*/',$str);
         }
清理gbk内的非gbk字符(不能用正则替换, 因可能从整字中间截掉):
function gbk_clean($str)
         {$rtn='';
          $cnt=strlen($str);
          for ($i=0;$i<$cnt;++$i)
              {if (ord($str{$i})>=0x00 && ord($str{$i})<=0x80)
                  {$rtn.=$str{$i};
                  }
               else if (ord($str{$i})>=0x81 && ord($str{$i})<=0xfe)
                       {if (ord($str{$i+1})>=0x40 && ord($str{$i+1})<=0x7e || ord($str{$i+1})>=0x80 && ord($str{$i+1})<=0xfe)
                           {$rtn=$rtn.$str{$i}.$str{$i+1};
                            $i=$i+1;
                           }
                       }
              }
          return $rtn;
         }
===============================================================================================
仔细研究《编码对照表》,你会发现unicode编码中的汉字编码区间是连续的,位于4E00和9FA0之间,用《进制转换页面》把16进制转换成2进制:
4E00 :  0100 1110 0000 0000
9FA0 :  1001 1111 1010 0000

上面的二进制编码就是要写入1110 ____ 10__ ____ 10__ ____的16个空位,就像:
1110 0100 1011 1000 1000 0000 : E4 B8 80
1110 1001 1011 1110 1010 0000 : E9 BE A0

精确拆分的结果如下:
第一段:E4 B8 80 到 E4 BF BF
第二段:E5 80 80 到 E8 BF BF
第三段:E9 80 80 到 E9 BE A0

正则:
\xe4\xb8[\x80-\xff]|
\xe4[\xb9-\xbe][\x00-\xff]|
\xe4\xbf[\x00-\xbf]|

\xe5\x80[\x80-\xff]|
[\xe5-\xe8][\x81-\xbe][\x00-\xff]|
\xe8\xbf[\x00-\xbf]|

\xe9\x80[\x80-\xff]|
\xe9[\x81-\xbd][\x00-\xff]|
\xe9\xbe[\x00-\xa0]

正则合并:
(?:\xe4\xb8|[\xe5\xe9]\x80)[\x80-\xff]|
(?:\xe4[\xb9-\xbe]|[\xe5-\xe8][\x81-\xbe]|\xe9[\x81-\xbd])[\x00-\xff]|
[\xe4\xe8]\xbf[\x00-\xbf]|
\xe9\xbe[\x00-\xa0]

粗糙正则:
[\xe4-\xe9][\x80-\xbf]{2}
===============================================================================================
当心GBK下多字节替换:
echo strtr('夏季出手的希望',array('值'=>'hell'));//将得到奇怪的"夏季出蔴ell南M"
echo preg_replace(array('/值/'),array('hell'),'夏季出手的希望');//将得到"夏季出蔴ell南M"
由于没有对应的多字节操作,进行类似操作时可以将要替换的字串和关键字都先转为utf-8再用strtr:
$mbc='mb_convert_encoding';
echo $mbc(strtr($mbc('夏季出手的希望','utf-8','gbk'),array($mbc('季','utf-8','gbk')=>'hell')),'gbk','utf-8');
echo $mbc(preg_replace(array('/'.$mbc('季','utf-8','gbk').'/u'),array('hell'),$mbc('夏季出手的希望','utf-8','gbk')),'gbk','utf-8');
写成函数:
function gbk_strtr($content,$arr_tr,$num=-1,$uft8=0)
         {$arr_tr_utf8=array();
          foreach ($arr_tr as $k=>$v)
                  {$k='/'.mb_convert_encoding($k,'utf-8','gbk').'/u';
                   $arr_tr_utf8[$k]=mb_convert_encoding($v,'utf-8','gbk');
                  }
          $content_tmp=mb_convert_encoding($content,'utf-8','gbk');
          $content_tmp=preg_replace(array_keys($arr_tr_utf8),array_values($arr_tr_utf8),$content_tmp,$num);
          return $uft8?$content_tmp:mb_convert_encoding($content_tmp,'gbk','utf-8');
         }
对应关系:
(editplus里)unicode big endian = (php里)ucs-2 ~= utf-16
(editplus里)unicode = (php里)ucs-2le ~= utf-16le
===============================================================================================
在一系列循环出来的select,紧跟每个select后用js给select赋值(避免第一个select时尚不是数组的问题):
<script>the_form.the_select<?=$i?'['.$i.']':''?>.value="<?=$rslt[$i]['the_select']?>";</script>
===============================================================================================
left join两个表select出来的fields可能重名, 这样处理:
$r=mysql_query('select * from registration left join reg on reg.email=registration.email '.$whr);
$n=mysql_num_fields(mysql_list_fields($DBname,"registration"));
for ($i=0;$i<mysql_num_fields($r);++$i)
    {$flds[]=($i<$n?'registration.':'reg.').mysql_field_name($r,$i);
    }
while ($row = mysql_fetch_row($r))
      {$tmp=array();
       for ($i=0,$cnt=count($row);$i<$cnt;++$i)
           {$tmp[$flds[$i]]=$row[$i];
           }
       $rslt[]=$tmp;
      }
如果重名的字段不多,可以在sql里处理:
select *,tb1.username as tb1_username,tb2.username as tb2_username,tb1.id as id
from tb1 left join tb2 on tb1.id=tb2.id
//注意:为了避免重表空时值被覆盖, 重名的关联字段也必须写上:tb1.id as id
===============================================================================================
避免串sql字串出错的方法:
$row_user_info=mysql_fetch_array(mysql_query(strtr(
"select *
 from 17wan_user
      left join user_4list on 17wan_user.id = user_4list.id and
                              user_4list.host_n_prd_id = '{1}'
 where 17wan_user.id = '{2}'
",
array('{1}'=>$host_n_prd_id,'{2}'=>$_COOKIE['pauserid'])
)));
如sql串日期比较:
strtr(
"select user.userid,
        {md_1_date},
        {md_2_date},'".$date_edition."'
 from userwant
      left join user on userwant.userid=user.userid
 where card_state=2
       and
       (({md_1_date} - INTERVAL 3 MONTH) <'".$date_edition."' and ({md_1_date} + INTERVAL 3 MONTH)>'".$date_edition."'
        or
        ({md_2_date} - INTERVAL 3 MONTH) <'".$date_edition."' and ({md_2_date} + INTERVAL 1 MONTH)>'".$date_edition."'
       )
 group by userwant.userid
",
array('{md_1_date}'=>"DATE_FORMAT(CONCAT(userwant.md_1_year,'-',userwant.md_1_month,'-','1'),'%Y-%m-%d')",
      '{md_2_date}'=>"DATE_FORMAT(CONCAT(userwant.md_2_year,'-',userwant.md_2_month,'-','1'),'%Y-%m-%d')"
     )
);
===============================================================================================
<script src="http://othersite/test.js.php"></script>
这个跨域的test.js.php的header不会往浏览器的任何域下种上phpsessid,
反过来浏览器上任何域的cookie也不会上到test.js.php,
只有本域的js.php才会种上phpsessid,
但test.js.php的内容js里可以往thissite种下cookie,
也能用js取得thissite的cookie送回othersite.
===============================================================================================
常用的逗号字段插入:
mysql_query("update 17wan_user set list_invite_me=CONCAT(list_invite_me,CASE WHEN FIND_IN_SET('".$_SESSION['id']."',list_invite_me)>0 THEN '' ELSE CASE WHEN list_invite_me>'' THEN ',".$_SESSION['id']."' ELSE '".$_SESSION['id']."' END END) where id='".$_GET['id']."'");
===============================================================================================
在正则替换前先行处理(用到了'/e'修饰符,"\0"代表完整匹配):
echo preg_replace(array('/dd/i','/jj(.*?)kk/e'),array('hell','"a".chr(\1)."c"'), 'jj65kk456DD');//\1传进去的是数字
//"chr(\\1)"才等同于'chr(\1)'的写法
还可以用自定义函数处理复杂情形(比如进行第二层正则替换):
echo preg_replace(array('/dd/i','/jj(.*?)kk/e'),array('hell','"a".myfun("\1")."c"'), 'jj65kk456DD');//"\1"传进去的是字串
传进去的字串可能需要将所有的"\'"替换成"'", 因为最外层的单引号的原因(见whole_code()里的实现)
使用/e修饰符时要避免直接处理用户输入变量, 否则可能遭到类似eval注入攻击
要在替换里面加序号/行号之类:
$cnt=0;
echo preg_replace(array('/kk/e'), array('"pp".($cnt++)'), 'aaakkbbbbkkeeeekkdddd');
'?'作数量符之限定符(或非贪婪模式)时, 指不贪婪第一个边界字符后面的字符.
===============================================================================================
可以用preg_match($str_preg, $str)===false;来判断正则表达式不合法.
preg_grep('/'.$str_preg.'/i', file($file_path, FILE_IGNORE_NEW_LINES));
相当于linux下的"grep -iP {$str_preg} {$file_path}"命令行,
grep是global regular expression print的缩写.
===============================================================================================
preg_replace或preg_match里"."默认是只不包括新行符\n(不同于[\S]是不包含\x20,\r,\n,\f,\t), 加上修饰符/s后才相当于[\S\s]:
preg_match('/\r\n\r\n(.*?)\r\n\r\n/s',$str,$match);
或者:
preg_match('/\r\n\r\n([\S\s]*?)\r\n\r\n/',$str,$match);
/m修饰符用来控制"^"和"$"是作为全文头/全文尾还是句头/句尾
"\b"直接使用指word boudary(非字母数字下划)等价于在前"(?<=\W|^)"和在后"(?=\W|$)", 即不占用匹配位置, 在"[\b]"里使用指backspace等价于"\x08".
===============================================================================================
将preg正则表达式(没有addslashes过的)转成msyql里可用:
function preg_to_mysql($reg)
         {//convert perl_reg(preg_match) to mysql_reg(rlike/regexp)
          return strtr($reg, array('\W'=>'[^[:alnum:]_]',
                                   '\w'=>'[[:alnum:]_]',
                                   '\S'=>'[^[:space:]]',
                                   '\s'=>'[[:space:]]',
                                   '\D'=>'[^[:digit:]]',
                                   '\d'=>'[[:digit:]]',
                                   '\b'=>'(^|[^[:alnum:]_]|$)',
                                   '\\'=>'\\\\',
                                   '\''=>'\\\''
                                  )
                      );
         }
===============================================================================================
常用检查(电邮,邮编,身份证,手机,姓名,链接,数值):
preg_match('/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,4}$/', $_POST['email'])
preg_match('/^\d{6}$/',$_POST['zip'])
preg_match('/^\d{15}(\d{2}[A-Za-z0-9])?$/', $_POST['id_card'])
preg_match('/^1\d{10}$/', $_POST['cellphone'])
preg_match('/^['.chr(0xa1).'-'.chr(0xff).']+$/', $_POST['real_name'])//GBK
preg_match('/^(?:[\xe4-\xe9][\x80-\xbf]{2})+$/', $_POST['real_name'])//UTF-8
preg_match("/^https?:\/\/([\w-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6}(:\d{1,5})?(\/.*)?$/i",$url);
preg_match("/^[0-9]+([\.][0-9]+)?$/", $float);
===============================================================================================
"(...)"既是匹配子板也是分支定界,"(?:...)"只是分支定界,例:
'the white queen'匹配/((red|white) (king|queen))/, 得到\1是white queen, \2是white, \3是queen
'the white queen'匹配/((?:red|white) (king|queen))/, 得到\1是white queen, \2是queen
"(?=...)"表示局部匹配后再扩展检查,符合才选入(用在前面的是"(?<=...)"), 例:
'JavaScript,hello,jack'匹配/(JavaScript(?=,hello)),jack/, 得到\1是JavaScript,jack
"(?!...)"表示局部匹配后再扩展检查,不符合才选入(用在前面的是"(?<!...)"), 例:
'JavaScript,hello,jack'匹配/(JavaScript(?!,hello))/, \1没有得到值
'dell,jack'匹配/((?!hello),jack)/, \1是,jack
===============================================================================================
用libcurl里的CURLOPT_HTTPHEADER来post数据也很方便:
$handle = curl_init('https://sspro.searchmarketing.yahoo.com/reports/overview.html');
curl_setopt($handle, CURLOPT_HTTPHEADER, array("Host: sspro.searchmarketing.yahoo.com", "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/2008052906 Firefox/3.0", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language: zh-cn,zh;q=0.5", "Accept-Encoding: gzip,deflate", "Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7", "Keep-Alive: 300", "Connection: keep-alive", "Cookie: B=cmvl5kl47o630&b=3&s=qo; SMC_SESSION=093118b62d9167fab52af2899d1244e6", "Cache-Control: max-age=0"));
curl_setopt($handle, CURLOPT_POST, 1);
curl_setopt($handle, CURLOPT_POSTFIELDS, $postData);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 2);
$result = curl_exec($handle);
curl_close($handle);
exit($result);
===============================================================================================
用php生成js动态内容包含文件(posttohost函数见"php模拟提交表单"):
(需要登录的机器在posttohost里加句:fputs($fp, 'Authorization: Basic '.base64_encode($username.':'.$password)."\r\n");就行了,登录成功则base64_encode($username.':'.$password)像PHPSESSID cookie一样存放在浏览器内存里)
(需要用代理的地址:
$fp=fsockopen($url['proxy_host'], $url['proxy_port']);
fputs($fp, "\x05\x01\x00");
fgets($fp, 10);
fputs($fp, "\x05\x01\x00\x03".chr(strlen($url['host'])%256).$url['host'].pack('n', $url['port']));
fgets($fp, 10);
fputs($fp, sprintf("POST %s%s%s HTTP/1.0\r\n", $url['scheme'].'://'.$url['host'].':'.$url['port'].$url['path'], $url['query'] ? "?" : "", $url['query']));)
js_pxy.php:
$file_js_inc=posttohost('http://'.$_GET['trg'], array(), $_COOKIE, $_SERVER['HTTP_REFERER'], $head);
for ($i=0,$cnt=count($head);$i<$cnt;++$i)
    {if (substr($head[$i],0,10)=='Set-Cookie')
        {header($head[$i]);
        }
    }
echo 'document.write("'.strtr($file_js_inc,array('"'=>'\"',"\r"=>'\r',"\n"=>'\n')).'");';
调用:
<script src="js_pxy.php?trg=17wan.cc/front.js_inc.php?tpl=1%26host=www.domysite.net"></script>
内容:
<?
echo ...;
?>
js_pxy.php可以用这个取代(注意要用串行方式):
<script>XMLHttp.sendReq('GET', 'logon_form.php', '',false, 'document.write(objXMLHttp.responseText);');</script>
===============================================================================================
获得http报头:
$fp = fopen($url, 'r');
$meta_data = stream_get_meta_data($fp);
$head = $meta_data['wrapper_data'];
/* 如果是fsockopen()好像不怎么好用, 要直接用fgets来得到报头 */
/* PHP4.3.0之前可以直接用$http_response_header(对于不返回指针的file_get_contents()也可以用这个) */
===============================================================================================
可以这样匹配query字串:
preg_match('/(^|&|\?)keyword=(.*?)(&|$)/',$url['query'],$match);
但不能:
preg_match('/[^|&|\?]keyword=(.*?)[&|$]/',$url['query'],$match);
匹配出来的字串要解码:
rawurldecode($match[2])
===============================================================================================
稍微精简的分页页码:
<?if ($pg_no-1>0){?><a href="?page=<?=$pg_no-1?>">上一页 </a>&nbsp;<?}?>
<?for ($i=0;$i<5;++$i){if ($pg_no-(5-$i)>0){?><a href="?page=<?=$pg_no-(5-$i)?>"><U><?=$pg_no-(5-$i)?></U></a>&nbsp;<?}}?>
<a href="?page=<?=$pg_no?>" style="text-decoration:none;"><B><?=$pg_no?></B></a>&nbsp;
<?for ($i=0;$i<5;++$i){if ($pg_no+$i+1<=ceil($cnt/$pg_size)){?><a href="?page=<?=$pg_no+$i+1?>"><U><?=$pg_no+$i+1?></U></a>&nbsp;<?}}?>
<?if ($pg_no+1<=ceil($cnt/$pg_size)){?><a href="?page=<?=$pg_no+1?>">下一页 </a><?}?>
===============================================================================================
一个跨域传递cookie的方法(比如在通行证中用到):
客户在一个域登录时,将cookie放到一个文件中:
$hash=md5(serialize($_COOKIE));//用来作为保存cookie 数据的文件名
$data=base64_encode(serialize($_COOKIE));//要保存的cookie数据
$h=fopen($hash,"wb+");
fwrite($h,$data);
然后跳转到另一个域时把文件名传过去?hash=<?=$hash?>, 解出cookie再种上:
foreach (unserialize(base64_decode(file_get_contents("http://otherdomain".$_GET['hash']))) as $k => $v)
        {setcookie($k,$v);
        }
用跨域的js应该也可以实现:<script src="http://othersite/test.js.php"></script>
===============================================================================================
如果一个函数内的两个缺省参数的缺省需要是不确定的, 不能使用一般的缺省值设置方法, 而是要传NULL值,
然后在函数内部代码赋予缺省值:
function aFun($a, $b, $c, $d)
         {if ($c===NULL){$c='...';}
          if ($d===NULL){$d='...';}
          ...
         }
aFun('...','...',NULL,'jack');
也可以不定义所有不确定的参数,而通过func_get_args()获得:
function aFun($aPar)
         {return count(func_get_args()).$aPar;
         }
echo aFun('a','b','c');
给函数少传参数会报错, 但是给函数多传参数即使里面不处理也没问题.
===============================================================================================
给图片补白并调整尺寸的函数:
function img_pad($file_src,$file_trg,$width_trg,$height_trg,$color_pad)
         {$type_img=function_exists('exif_imagetype')?
                    exif_imagetype($file_src):
                    array_shift(array_slice(getimagesize($file_src),2,1));
          switch ($type_img)
                 {case IMAGETYPE_JPEG: $img_src = imagecreatefromjpeg($file_src); break;
                  case IMAGETYPE_BMP: $img_src = imagecreatefromwbmp($file_src); break;
                  case IMAGETYPE_GIF: $img_src = imagecreatefromgif($file_src); break;
                  case IMAGETYPE_PNG: $img_src = imagecreatefrompng($file_src); break;
                 }
          if(!is_resource($img_src)){return false;}
          $img_trg = imagecreatetruecolor($width_trg, $height_trg);
          imagefill($img_trg,$width_trg/2,$height_trg/2,$color_pad);
          list($width_orig, $height_orig) = getimagesize($file_src);
          if ($width_orig/$height_orig<$width_trg/$height_trg)
             {$ratio = $height_trg/$height_orig;
             }
          else
              {$ratio = $width_trg/$width_orig;
              }
          imagecopyresampled($img_trg, $img_src,
                             ($width_trg-$width_orig*$ratio)/2, ($height_trg-$height_orig*$ratio)/2, 0, 0,
                             $width_orig*$ratio, $height_orig*$ratio,
                             $width_orig, $height_orig
                            );
          return imagejpeg($img_trg, $file_trg, 100);
         }
使用:
img_pad('upload/'.$_COOKIE['userid'].'.m.jpg','upload/'.$_COOKIE['userid'].'.s.jpg',50,50,0xFFFFFF);
===============================================================================================
"<?"紧前的回车输出到html(可以在其上一行"<??>"来解决), "?>"紧后的回车不输出到html:
<?
reset($array_cnf);
while (key($array_cnf)!==null)
      {?><span class="tab"><?
         ?><span class="span_h"></span><br><?
         ?><span class="span_v"></span><span style="vertical-align:bottom;"><?=$array_cnf[key($array_cnf)]['title']?></span><?
       ?></span><?
       next($array_cnf);
      }
?>
===============================================================================================
有时想将数组的索引直接用做变量名, 这样:'x'.base64_encode(<索引>)是不行的, 因为base64里还有"/","+","="三个字符非法
要转换:'x'.strtr(base64_encode(<索引>),array('+'=>'¢','/'=>'£','='=>'¥')),做成函数叫inv2val(),
转回的时候:base64_decode(strtr(substr(<变量名>,1),array('¢'=>'+','£'=>'/','¥'=>'='))),做成函数叫val2inv()
===============================================================================================
每次随机生成图片验证码的数字并种在服务器端的session变量里,提交核对时也要清除这个session变量,以防用url过滤作弊
===============================================================================================
字符编码转换(还可以用iconv,也可用自己写的utf82gb,但mb_convert_encoding在php5中已是默认扩展):
mb_convert_encoding($file, 'UTF-8', 'GBK');//从GBK到UTF-8
===============================================================================================
获得插入的最后一行数据的id:mysql_insert_id(), PDO里是$pdo->lastInsertId();
====================================================================================
取得域名指向的ip很简单:gethostbyname('diyism.com');
===============================================================================================
用xml_rss配置文件及下面的xsl_raw模板文件来装配php程序:
模板:
<?
<xsl:value-of select="title" />
echo "you are <xsl:value-of select="name" />";
?>
<font color="red">you are <xsl:value-of select="title" /></font>
<xsl:for-each select="item[@category='left_top']">
<a href="<xsl:value-of select="item[@category='pic']/link" />"><img src="<xsl:value-of select="item[@category='pic']/enclosure" />" width="131" height="105" style="border:none;"/></a><br />
</xsl:for-each>
函数:
function xsl2php($path)
         {$xsl_text=array('<?xml version="1.0" encoding="utf-8"?>'.
                          '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">'.
                          '<xsl:output method="text" encoding="utf-8"/>'.
                          '<xsl:template match="/rss/channel">',
                          '</xsl:template>'.
                          '</xsl:stylesheet>'
                         );

          $xml = new DOMDocument;
          $xml->load($path);

          $content_xsl = file_get_contents(str_replace('.xml','.xsl', $path));
          $content_xsl = preg_replace(array('/<\/?xsl:[\S\s]*?>/'), array(']]>\0<![CDATA['), $content_xsl);
          $content_xsl = '<![CDATA['.$content_xsl.']]>';

          $xsl = new DOMDocument;
          $xsl->loadXML($xsl_text[0].$content_xsl.$xsl_text[1]);

          $proc = new XSLTProcessor;
          $proc->importStyleSheet($xsl);

          return $proc->transformToXML($xml);
         }
======================================================================================
防范合法用户的攻击:
没有任何办法可以防范用户修改浏览器缓存的本站页面后提交非法数据, 所以安全要求高的页面必须在服务器
接收端也进行数据合法性检查
===============================================================================================
防范钓鱼攻击:
像银行这种网站的数据接收端(包括get,post)是可以通过检查$_SERVER['HTTP_REFERER']来避免钓鱼攻击的(一个合法
用户被诱骗从非法网站点击了向银行网站提交的表单),因为非法网站无法同时提交伪造的$_SERVER['HTTP_REFERER']和合法
用户的session:要么提交了合法用户的session却没伪造referer,要么伪造了referer却提交不了合法用户的session,
但是非法网站可以多绕一圈,先把鱼钓到不检查session的银行网站页面,再通过对该页面伪造js自动二次提交数据到真正的
接收端,所以银行网站即使不需检查session的普通页面(比如首页)也要防范提交数据对页面的影响,最好不允许普通页面接收
数据(包括url里get方式以及cookie)
===============================================================================================
简易html模板方法(需要在php 5.3里):
(因为直接采用php函数进行模板操作, 所以不需要像smarty那样将smarty模板语言转php语言再缓存):
(注意, 在$if()内的判断条件里的变量需要用双引号引起来, 因为:
$s='substr';$k='abcdefg';echo "{$s("{$k}", 0, 4)}";得到'abcd', 而
$s='substr';$k='abcdefg';echo "{$s('{$k}', 0, 4)}";得到'{$k}'.
)
(注意, 所有明文html里的双引号都要写成"&quot;", 否则模板eval时出错,
如果php 5.3正式版支持nowdoc嵌套于heredoc内(现在的5.3.0.beta1只支持nowdoc嵌在双引号内),
就不需要转&quot;了, 我提了47516的bug后, PHP 5.3.0RC2已经支持nowdoc嵌套于heredoc内了)
my_tpl.php:
<?
function my_foreach($dat,$tpl)
         {$if='my_if';
          $htm='';
          foreach ($dat as $v)
                  {extract($v);
                   $htm.=eval("return <<<TPL\n".$tpl."\nTPL;\n");
                  }
          return $htm;
         }

function my_if($case,$yes,$no)
         {return $case?$yes:$no;
         }

function my_tpl($dat,$tpl)
         {$my_tpl='my_tpl';
          $import='my_import';
          $foreach='my_foreach';
          $if='my_if';
          extract($dat);
          $tpl=file_get_contents($tpl);
          $htm=eval("return <<<TPL\n".$tpl."\nTPL;\n");
          return $htm;
         }
?>

test.tpl:
{$company}&nbsp;职员:<br>
<br>
{$foreach($info,<<<'FUN'
信息:{$if("{$name}"=='jack',"姓名:{$name}电话:{$tel}",'保密')}<br>
FUN
)}
<br>
{$foreach($info,<<<'FUN'
姓名:{$name}电话:{$tel}<br>
FUN
)}
<br>
"会员":{$if("{$flag}"=='1','是的呀',$if("{$flag}"=='0','不是',''))}<br>
<br>
{$my_tpl($dat,'footer.tpl')}

footer.tpl:
Bye, {$company}

test.php:
<?
require_once 'my_tpl.php';

$the_name='公司甲';
$d['company']=$the_name;
$d['info'][0]=array('name'=>'kexianbin','tel'=>'7894');
$d['info'][1]=array('name'=>'jack','tel'=>'7411');
$d['flag']='1';

echo my_tpl($d,'test.tpl');
?>
===============================================================================================
有时候偷懒, 想直接在双引号字串内使用函数:
echo "123{${!${''}=substr}('abc',2,1)}456";
===============================================================================================
不管大小写的字串比较:strtolower(aStr)!=strtolower(bStr)
===============================================================================================
去掉所有js\css\html\注释\空白,转换html实体:
//依赖chr_utf8函数
function strip_all($str)
         {$search = array ("'<script[^>]*?>.*?</script>'si", // remove javascript
                           "'<style[^>]*?>.*?</style>'si", // remove css
                           "'<[/!]*?[^<>]*?>'si", // remove html tags
                           "'<!--[/!]*?[^<>]*?>'si", // remove comments
                           "'[\r\n][\s]*'", // remove newline and beginning spaces
                           "'&(quot|#34);'i", // replace html entities
                           "'&(amp|#38);'i",
                           "'&(lt|#60);'i",
                           "'&(gt|#62);'i",
                           "'&(nbsp|#160);'i",
                           "'&(iexcl|#161);'i",
                           "'&(cent|#162);'i",
                           "'&(pound|#163);'i",
                           "'&(copy|#169);'i",
                           "'&#(\d+);'e");

          $replace = array ("",
                            "",
                            "",
                            "",
                            "",
                            "\"",
                            "&",
                            "<",
                            ">",
                            " ",
                            chr(161),
                            chr(162),
                            chr(163),
                            chr(169),
                            'chr_utf8(\1)');

          return preg_replace($search, $replace, $str);
         };
===============================================================================================
http://diyism.net/index.php?openid.identity=kkk参数名被apache改成了$_GET['openid_identity']("."变成了"_")
===============================================================================================
将大表单分步骤提交有两种做法:
1.提交按钮用于隐藏一个步骤,显示下一个步骤;
2.提交按钮提交数据到下一个步骤页面,在下一个步骤页面里php生长前一个页面的隐藏表单
==============================================================================================
http://diyism.com/?j这里参数$_GET['j']==='', 而不是一个null变量
==============================================================================================
抽取多个不重复随意数:
function get_rnd($start,$end,$num)
         {$rnd=array();
          if ($num>$end-$start+1)
             {$num=$end-$start+1;
             }
          while (count($rnd)<$num)
                {if (!in_array($tmp=mt_rand($start,$end),$rnd))
                    {$rnd[]=$tmp;
                    }
                }
          return $rnd;
         }
==============================================================================================
近似实现重载任何内建函数或用户函数(可以写链式代码):
(要重载函数时只须在sys.php内添加define和function即可)
sys_core.php:
<?class cls_sys
        {function __call($cmd, $pars)
                  {eval('$cmd='.$cmd.';');
                   return call_user_func_array($cmd,$pars);
                  }
         function cls_sys($obj)
                  {$this->val=$obj;
                   $GLOBALS['sys']=&$this;
                  }
        }
  function sys($obj)
           {return new cls_sys($obj);
           }
?>
sys.php:
<?define('substr', 'my_substr');
  function my_substr()
           {$p=func_get_args();
            foreach ($p as &$v)
                    {if ($v===null)
                        {$v=$GLOBALS['sys']->val;
                        }
                     break;
                    }
            $GLOBALS['sys']->val=substr($p[0],$p[1],$p[2]);
            return $GLOBALS['sys'];
           }
?>
model.php:
<?$show=sys('abcd')->substr(null,2,2)->val;
?>
page.php:
<?echo $show;
?>
==============================================================================================
mysql_connect没有自定超时退出功能(3306端口不通0s退出, host不通20s才退出), 可以用上面class cls_sys方法重载一下:
<?define('mysql_connect', 'my_mysql_connect');
  function my_mysql_connect($server, $username, $password, $new_link=false, $client_flags=NULL, $timeout=5)
           {$tmp_server=explode(':', $server);
            if ($fp=fsockopen($tmp_server[0], $tmp_server[1]?$tmp_server[1]:'3306', &$errno, &$errstr, $timeout))
               {fclose($fp);
                return mysql_connect($server, $username, $password, $new_link, $client_flags);
               }
            else
                {return false;
                }
           }
?>
mysql查询没有像apache一样设置超时的地方, 可以写个php脚本定时"SHOW FULL PROCESSLIST"找到里面超时的进程"KILL"掉
==============================================================================================
让属性自动触发私有方法:
class user
      {public $username='jack';
       public function __get($prop_name)
              {return $this->$prop_name();
              }
       private function degree()
               {return '到数据库查询得到的结果';
               }
      }
$aUser=new user();
echo $aUser->degree; //这样干还有一个好处, php里不能$aUser->degree()[1], 却能$aUser->degree[1]
echo $aUser->username;
//对象是用来描述不可分解的不得不牵一发而动全身的数据集合的
<?
class user
      {//没有牵连关系的部分
       public $has_read_username=0;
       public $has_write_nickname=0;
       public $has_read_nickname=0;
       public $has_write_username=0;
       //有牵连关系的部分
       private $_fake=array('username'=>'jack',
                            'nickname'=>'peter'
                           );
       public function __get($k)
              {$prop_name='_get_'.$k;
               if (method_exists($this, $prop_name))
                  {return $this->$prop_name();
                  }
               else
                   {return $this->_fake[$k];
                   }
              }
       public function __set($k, $v)
              {$prop_name='_set_'.$k;
               if (method_exists($this, $prop_name))
                  {$this->$prop_name($v);
                  }
              }

       const fake_list='username,nickname';
       private function _set_username($v)
               {$this->_fake['username']=$v;
                $this->has_write_username=1;
               }
       private function _get_nickname()
               {$this->has_read_nickname=1;
                return $this->_fake['nickname'];
               }
      }
$aUser=new user();
$aUser->username='my_username';
echo $aUser->nickname;
echo user::fake_list;
echo $aUser->has_write_username;
?>
==============================================================================================
常见设计模式里单体(singleton)的写法:
class db
      {private static $_instance;
       private function __construct() {}
       public static function instance()
                              {if (self::$_instance===null)
                                  {self::$_instance=eval('return new '.__CLASS__.';');
                                  }
                               return self::$_instance;
                              }
      }
$a_db=db::instance();
由于__construct()里不能通过return而返回创建对象, 也不能给$this赋值而返回创建对象,
因而需要单独设置instance()方法, 且将__construct设为private而禁止用new创建单体.
==============================================================================================
输出一个四维数组为二维多级表格, 并最后一维拉平(统计报告里经常要这么干):
<table width="90%" cellspacing="1" cellpadding="4" border="0" align="center">
<tr class="header"><td>地区</td><td>大版</td><td>子版</td><?foreach ($cols as $v) {?><td><?=$v?></td><?}?></tr>
<?for ($i=0,$cnt=count($out),$keys=array_keys($out);$i<$cnt;++$i)
      {for ($j=0,$cnt1=count($out[$keys[$i]]),$keys1=array_keys($out[$keys[$i]]);$j<$cnt1;++$j)
           {$cnt_total[$i]+=count($out[$keys[$i]][$keys1[$j]]);
           }
      }
  for ($i=0,$cnt=count($out),$keys=array_keys($out);$i<$cnt;++$i)
      {for ($j=0,$cnt1=count($out[$keys[$i]]),$keys1=array_keys($out[$keys[$i]]);$j<$cnt1;++$j)
           {for ($k=0,$cnt2=count($out[$keys[$i]][$keys1[$j]]),$keys2=array_keys($out[$keys[$i]][$keys1[$j]]);$k<$cnt2;++$k)
                {
?>
<tr class="row1">
               <?if($j==0 && $k==0){?><td rowspan="<?=$cnt_total[$i]?>"><?=$keys[$i]?></td><?}?>
               <?if($k==0){?><td rowspan="<?=$cnt2?>"><?=$keys1[$j]?></td><?}?>
               <td><?=$keys2[$k]?></td>
               <?foreach ($cols as $v){?><td><?=$out[$keys[$i]][$keys1[$j]][$keys2[$k]][$v]?></td><?}?>
</tr>
<?              }
           }
      }
?>
</table>
==============================================================================================
创建目录要单独再chmod, 在mkdir里0777无效:
mkdir('templates_c/dir_test');
chmod('templates_c/dir_test',0777);
==============================================================================================
HTTP/1.1 Status Code Definitions(常见HTTP/1.1状态代码定义):
200 OK //直接访问正常
206 Partial Content //静态文件的断点续传, 浏览器下载静态文件意外中断(故障或人为), 再刷新时从断点继续下载
301 Moved Permanently //类似于302, 只是明确告诉浏览器/搜索引擎页面因移动而跳转, 原页面已不存在,
                      //通常在httpd.conf内对应虚拟主机段里设:
                      //RewriteEngine on
                      //RewriteRule ^/old.php$ /new.php [R=301,L]
                      //访问不含index.php的文件夹如"/publish"也会依次触发301和403
302 FOUND //访问到一个跳转页, 比如里面有php代码:header('Location: red.php;');
304 Not Modified //浏览器再次请求一个静态文件(包括手动刷新)时只送出比较文件修改日期请求, 如果相同则服务器返回304
403 FORBIDDEN //访问的文件被禁止, 比如在httpd.conf里设置禁止直接访问包含文件:<FilesMatch "\.inc$">Order allow,deny</FilesMatch>
404 NOT FOUND //访问的网页不存在
500 Internal Server Error //服务器配置有误
==============================================================================================
"@"符号压制不了pdo的意外(虽然其它对象的method可以这么压制):
$dbh = new PDO("mysql:host=dbserver.com;port=3306;dbname=db1", $user, $password);
@$dbh->query($sql);
只能来捕获意外:
$rtn=true;
try
    {$dbh->query($sql);
    }
catch (Exception $e)
      {$rtn=false;
      }
if ($rtn)
   {...
   }
else
    {...
    }
==============================================================================================
php中的try {} catch {}或set_error_handler()都捕获不了fatal error, 必然导致程序终止,
虽然无法让程序继续执行, 但是仍然可以输出致命错误发生前的程序状态:
$ob_before=ob_list_handlers();
if (count($ob_before)>0)
   {$doc_before=ob_get_contents();
    ob_end_clean();
   }
if (function_exists('handle_fatal')==false)
   {function handle_fatal($output_try)
             {if (preg_match("/<b>(.+?)\serror<\/b>:(.*?)<br/i", $output_try, $match))
                 {return $GLOBALS['status'];
                 }
              else
                  {return $output_try;
                  }
             }
   }
ob_start('handle_fatal');
$GLOBALS['status']='status';

/*可能发生致命错误的地方:begin*/
echo $this->kk;
//echo 1/0;
/*可能发生致命错误的地方:end*/

$doc=ob_get_contents();
ob_end_clean();
if (count($ob_before)>0)
   {ob_start($ob_before[0]);
    echo $doc_before;
   }
echo $doc;
==============================================================================================
用PDO(PHP Data Objects)操作数据库非常方便, 比如:
$rs=$dbh->query($sql);
$filters=$rs->fetchAll(PDO::FETCH_COLUMN, 0);直接把select里的第一列取到数组里(并不一定是表里第一列)
$filter=$rs->fetchColumn(0);直接把第一行第一列取到变量, 不能fetch(PDO::FETCH_COLUMN, n)
连接写法:
$cfg_db=array('host'=>'test.com',//or use ip
              'port'=>'3306',
              'name'=>'db1',
              'user'=>'user1',
              'pass'=>'password'
             );
$dbh=new PDO("mysql:host={$cfg_db['host']};port={$cfg_db['port']};dbname={$cfg_db['name']}", $cfg_db['user'], $cfg_db['pass']);
虽然可以$dbh->query($sql, PDO::FETCH_COLUMN, 0);但是仍需foreach as才能取到数据(不如fetchAll), 所以不用这种写法.
要加工一遍从db server选出的大量数据的时候, 应该用while ($v=$rs->fetch(PDO::FETCH_ASSOC))而不要用fetchAll,
因为query不会消耗web server的内存, while...fetch只占一行数据的内存, fetchAll则占所有行的内存,
用while...fetch至少可以省一半的内存, 除非加工后结果仍存到fetchAll得到数组的对应元素里.
==============================================================================================
经常要把结果集里的id弄成索引, 用foreach不直观, 可以用array_map:
$arrIds=array_map(create_function('$v', 'return $v[\'id\'];'), $arrOut);
要用id查其它数据:$arrOut[array_search($id, $arrIds)]['name'];
array_map也可以处理到索引的:
array_map(create_function('$k, $v', 'return $k.":".$v;'), array_keys($arrOut), array_values($arrOut));
甚至可以引入用户变量:
array_map(create_function('$k, $v', 'return $k.":".$v;'), array_keys($arrOut), array_pad(array(), count($arrOut), &$u));
//不能使用array_fill(), 因不能传址, array()是能传址的, 比如array(1, 2, &$arr);
如果想将数据库查询结果集转成以ID索引可以(避免与原索引冲突加'_'):
array_walk($arrOut, create_function('&$v, $k, $arr_rtn', 'if ($k{0}!="_"){$arr_rtn["_".$v[\'ID\']]=$v;unset($arr_rtn[$k]);}'), &$arrOut);
注意, 这里很特别, 传址符号放在调用函数的参数里, 例外于整个php的参数传址规则(&放在定义函数的参数里).
php5.2.5时即使在php.ini里设置allow_call_time_pass_reference也仍会报
Warning: Call-time pass-by-reference has been deprecated - argument passed by value,
可以这样:
array_walk($arrOut, create_function('&$v, $k, $arr_rtn', 'if ($k{0}!="_"){$arr_rtn[0]["_".$v[\'ID\']]=$v;unset($arr_rtn[0][$k]);}'), array(&$arrOut));
其实等价于:
$my_walk=create_function('&$arr_rtn', 'foreach ($arr_rtn as $k=>&$v){if ($k{0}!="_"){$arr_rtn["_".$v["ID"]]=$v;unset($arr_rtn[$k]);}}');
$my_walk($arrOut);
==============================================================================================
用PDO::FETCH_COLUMN|PDO::FETCH_GROUP一次性取出索引字段(由tb.*前面加字段指定), 和一个对应值:
//只能取出两个字段:索引(默认位置只能第一个), 对应值(由第二参数指定, 默认是1, 即select里第2字段)
//注意第二字段在遍历数组值的第二层: foreach ($rtn as $v) {echo $v[0];}
$sth=$dbh->query("SELECT name, tb.* FROM z_test tb");
$rtn=$sth->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP, 3);
var_export($rtn);
得到: array('apple' => array ( 0 => 'green', 1 => 'green'),
            'pear' => array ( 0 => 'yellow', 1 => 'yellow')
           );
==============================================================================================
联查很消耗数据库服务器资源, 如果不是在查询条件里必须用的联结,
可以先把主表数据查出来:$arrOut=$rs->fetchAll(PDO::FETCH_ASSOC);
把从表id也弄出来:
$arrChnIds=array_unique(array_map(create_function('$v', 'return $v[\'ChannelID\'];'), $arrOut));
然后串起来在从表里查一次:
$arrChnOut=$this->objDB->query("select ID, ChannelName from sem_ClassifyChannel where ID in (".implode(',', $arrChnIds).")")->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
foreach ($arrOut as &$v) //循环分配到主表结果集数组里
        {$v['AlgoChannelName']=$arrChnOut[$v['ChannelID']][0];//这里是"[0]"
        }
如果要取出从表的多个字段:
$arrChnOut=$this->objDB->query("select * from sem_ClassifyChannel where ID in (".implode(',', $arrChnIds).")")->fetchAll(PDO::FETCH_ASSOC);
array_walk($arrChnOut, create_function('&$v, $k, $arr_rtn', 'if ($k{0}!="_"){$arr_rtn["_".$v[\'ID\']]=$v;unset($arr_rtn[$k]);}'), &$arrChnOut);
foreach ($arrOut as &$v) //循环分配到主表结果集数组里
        {$v['AlgoChannelName']=$arrChnOut['_'.$v['ChannelID']]['ChannelName'];//这里是"['ChannelName']"
        }
当心:拼凑" in (...)"字串时, 如果"()"内为空(empty list)则会出错, 可以串上一个不可能进行"="操作的值null,
因为in (null)相当于"=null"是永远不成立的(须用is null), 即使数据里有null值.
==============================================================================================

  您的网址:(可能:)呆元:2385年05月19日(17-06-23)访客总人次:433220(自k5a1)  
Copyleft 2364-2378 DIYism
Powered by Ubuntu Server & ZPC-GX
全民备TOR 安全上网