2008年2月27日星期三

Prototype and jQuery are used in same time

[js]
<script language="javascript" type="text/javascript">var jQuery=$;</script>
<script language="javascript" type="text/javascript">jQuery.noConflict();</script>
[/js]

2008年2月11日星期一

魔术引号

FROM:

魔术引号(Magic Quote)是一个自动将进入 PHP 脚本的数据进行转义的过程。最好在编码时不要转义而在运行时根据需要而转义。

什么是魔术引号?
当打开时,所有的 ' (单引号), " (双引号), \ (反斜线)和 NULL 字符都会被自动加上一个反斜线进行转义。这和 addslashes() 作用完全相同。

一共有三个魔术引号指令:

  • magic_quotes_gpc 影响到 HTTP 请求数据(GET,POST 和 COOKIE)。不能在运行时改变。在 PHP 中默认值为 on 。参见 get_magic_quotes_gpc()

  • magic_quotes_runtimeoff 。参见 set_magic_quotes_runtime()get_magic_quotes_runtime()

  • magic_quotes_sybase 如果打开的话,将会使用单引号对单引号进行转义而非反斜线。此选项会完全覆盖 magic_quotes_gpc。如果同时打开两个选项的话,单引号将会被转义成 '' 。而双引号、反斜线 和 NULL 字符将不会进行转义。如何取得其值参见ini_get()



为什么要用魔术引号

  • 对初学者很有用
    魔术引号在 PHP 中用来实现避免初学者的代码更危险。尽管 SQL 注入 在魔术引号打开的情况下仍然有可能实现,但起码系统的风险减少很多了。

  • 方便使用
    当向数据库中插入数据时,魔术引号所做的就是自动对所有的 GET、POST、COOKIE 数据运用 addslashes() 函数。



为什么不用魔术引号

  • 可移植性
    编程时认为其打开或并闭都会影响到移植性。可以用 get_magic_quotes_gpc() 来检查是否打开,并据此编程。

  • 性能
    由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。在运行时调用转义函数(如 addslashes())更有效率。
    尽管 php.ini-dist 默认打开了这个选项,但是 php.ini-recommended 默认却关闭了它,主要是出于性能的考虑。

  • 不便
    由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。针对这个问题,可以使用 stripslashes()函数处理。



关闭魔术引号
magic_quotes_gpc 指令只能在系统级关闭,不能在运行时。也就是说不能用 ini_set()
[bash title="下面是一个通过 php.ini 文件把这些选项设为 Off 的范例"]
; Magic quotes
;

; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off

; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off

; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = Off
[/bash]

如果不能修改服务器端的配置文件,使用 .htaccess 也可以。范例如下:
[bash]
php_flag magic_quotes_gpc Off
[/bash]

为了能写出移植性较强的代码(可以运行于任何环境),例如不能修改服务器配置的情况,下面的例子可以在运行时关闭 magic_quotes_gpc 。但是这样做比较低效,适当的修改配置才是更好的办法。

[php title="在运行时关闭魔术引号"]
if ( get_magic_quotes_gpc()) {
function stripslashes_deep ($value) {
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
return $value ;
}

$_POST = array_map ('stripslashes_deep', $_POST);
$_GET = array_map ('stripslashes_deep', $_GET);
$_COOKIE = array_map ('stripslashes_deep', $_COOKIE);
}

function addslashes_deep($var) {
if(is_array($var)) {
return array_map('addslashes_deep',$var);
} else {
if(get_magic_quotes_gpc()) {
// 如果 magic_quotes_sybase=On,我们先把 " 替换成 ',然后再addslashes
if(ini_get('magic_quotes_sybase')) {
$var = str_replace("\"", "'", $var);
$var = addslashes($var);
}
} else {
$var = addslashes($var);
}
return trim($var);
}
}
[/php]

PHP程序员的十个建议性技巧


  • 使用 ip2long()long2ip() 函数来把 IP 地址转化成整型存储到数据库里。这种方法把存储空间降到了接近四分之一(char(15) 的 15 个字节对整形的 4 个字节),计算一个特定的地址是不是在一个区段内页更简单了,而且加快了搜索和排序的速度(虽然有时仅仅是快了一点)。

  • 在验证 email 地址的时候使用 checkdnsrr() 函数验证域名是否存在。这个内置函数能够确认指定的域名能够解析成 IP 地址。该函数的PHP 文档的用户评论部分有一个简单的用户自定义函数,这个函数基于 checkdnsrr(),用来验证 email 地址的合法性。对于那些认为自己的 email 地址是 "joeuser@wwwphp.net" 而不是 "joeuser@php.net" 的家伙们,这个方法可以很方便的抓住他们。

  • 如果你使用的是 PHP 5 和 MySQL 4.1 或者更高的版本,考虑抛弃 mysql_* 系列函数, 改用改进版的 mysqli_* 系列函数。一个很好的功能就是你可以使用预处理语句,如果你在维护一个数据库密集型站点,这个功能能够加快查询速度。

  • 学会爱上三元运算符。

  • 如果你在项目中感觉到有可复用的部分,在你写下一行代码前先看看 PEAR 中是否已经有了。很多 PHP 程序员都知道 PEAR 是一个很好的资源库,虽然还有很多程序员不知道。这个在线资源库包含了超过 400 个可以复用的程序片段,这些程序片段你可以立即用在你的程序里。除非说你的项目真的是非常特别的,你总能找到帮你节省时间的 PEAR 包. (参见 PECL)

  • highlight_string(), highlight_file() 来自动的打印出格式化的很漂亮的源代码。如果你在留言板、IRC 这些地方寻求一个脚本的帮助的话,这个函数用起来非常的顺手。当然了,要小心不要意外的泄露出你的数据库连接信息和密码等。

  • 使用 error_reporting(0) 函数来防止用户看到潜在的敏感错误信息。在理想情况下,发布服务器应该在 php.ini 里完全禁止。但是如果你用的是一个共享的 web 服务器的话,没有自己的 php.ini 文件,那么这种情况下最好的选择就是在所有脚本的第一行前加上 error_reporting(0);(或者使用 require_once()方法)。这就能够在出错的时候完全屏蔽敏感的 SQL 查询语句和路径名。

  • 在网数据库中存储很大的字符串之前使用 gzcompress()gzuncompress() 来显式的压缩/解压字符串。这个 PHP 内置函数使用 gzip 算法,可以压缩普通文本达 90%。在我每次要读写 BLOB 类型的字段的时候都使用这些函数。唯一额例外就是当我需要全文检索的时候。

  • 通过引用 传递参数的方法从一个函数中得到多个返回值。就像三元运算符一样,大部分受过正式编程训练的程序员都知道这个技巧。但是那些 HTML 背景大于 Pascal 背景的程序员都或多或少的有过这样的疑问 "在仅能使用一次 return 的情况下,从一个函数里返回多个值?" 答案就是在变量前加上一个 & 符号,通过引用传递而非"值"传递。

  • 完全理解魔术引号 SQL 注入的危险性。

PHP 注意事项


  • 页面之间无法传递变量 get,post,session
    php5后自动全局变量是关闭的,所以要从上一页面取得提交过来得变量要使用$_GET['test'], $_POST['test'], $_SESSION['test'] 来得到
    当然也可以修改自动全局变量,将register_globals = On 考虑到安全性和兼容性,最好不要这样操作。


  • Win32下apache2 用get方法传递中文参数会出错
    test.php?a=你好&b=你也好 传递参数是会导致一个内部错误
    解决办法: "test.php?a=" . urlencode('你好') . "&b=" . urlencode('你也好');

  • win32下的session不能正常工作
    php.ini 默认的 session.save_path = /tmp, 这是*nix下的配置,win32下php无法读写session文件导致session无法使用

  • 显示错误信息
    当php.ini的display_errors = On并且error_reporting = E_ALL 时,将显示所有的错误和提示

  • Win32下 mail()不能发送电子邮件
    在*nix下配置好的 sendmail 可以发送,在win32下需要有smtp服务器才可以发送电子邮件
    修改php.ini的
    [bash]
    SMTP = ip //ip是不带验证功能的smtp服务器
    [/bash]
    php发送邮件的最好解决方法是用socket直接发送到对方email服务器而不用转发服务器


  • 初装的mysql如果没有设置密码, 应该使用
    [sql]
    UPDATE mysql.user SET PASSWORD ="yourpassword" WHERE user="root";
    [/sql]

  • header already sent
    这个错误通常会在使用header()的时候出现,可能是几种原因:
    A. 在使用header()前有print 或者echo
    B. 当前文件前面有空行
    C. 可能include了一个文件,该文件尾部有空行或者有输出


  • 更改php.ini后没有变化
    重新启动web server,比如IIS,Apache等等,然后才会应用最新的设置


  • php在2003上面安装
    PHP4的php4isapi.dll好像和2003有些冲突,只能用CGI模式安装

  • isset()和empty()的区别
    两者都是测试变量,但是isset()是测试变量是否被赋值,而empty()是测试一个已经被赋值的变量是否为空
    如果一个变量没被赋值就引用在php里是被允许的,但会有notice提示
    如果一个变量被赋空值,$foo=""或者$foo=0或者 $foo=false,那么empty($foo)返回真,isset($foo)也返回真,就是说赋空值不会注销一个变量。
    要注销一个变量,可以用 unset($foo)或者$foo=NULL

  • mysql查询语句包含有关键字
    php查询mysql的时候,有时候mysql表名或者列名会有关键字
    这时候查询会有错误。例如表名是order,查询时候会出错
    简单的办法是sql语句里表名或者列名加上`[tab键上面]来加以区别, 例如 SELECT * FROM `order`

  • 通过HTTP协议一次上传文件
    form 需要 enctype="multipart/form-data" 这个属性



include,require,include_once,require_once的区别
A. include()函数会将指定的档案读入并且执行里面的程序。
被导入的档案中的程序代码都会被执行,而且这些程序在执行的时候会拥有和源文件中呼叫到include()函数的位置相同的变量范围 (variable scope) 。你可以导入同一个服务器中的静态档案,甚至可以通过合并使用include()与fopen()函数来导入其它服务器上面的档案。

B. include_once()函数的作用和include()是几乎相同的
唯一的差别在于include_once()函数会先检查要导入的档案是不是已经在该程序中的其它地方被导入过了,如果有的话就不会再次重复导入该档案 (这项功能有时候是很重要的,比方说要导入的档案里面宣告了一些你自行定义好的函数,那么如果在同一个程序重复导入这个档案,在第二次导入的时候便会发生错误讯息,因为PHP不允许相同名称的函数被重复定义) 。

C. require()函数会将目标档案的内容读入,并且把自己本身代换成这些读入的内容。
这个读入并且代换的动作是在PHP引擎编译你的程序代码的时候发生的,而不是发生在PHP引擎开始执行编译好的程序代码的时候 (PHP 3.0引擎的工作方式是编译一行执行一行,但是到了PHP 4.0就有所改变了,PHP 4.0是先把整个程序代码全部编译完成后,再将这些编译好的程序代码一次执行完毕,在编译的过程中不会执行任何程序代码) 。require()通常来导入静态的内容,而include()则适合用来导入动态的程序代码。

D. 如同include_once()函数,require_once()函数会先检查目标档案的内容是不是在之前就已经导入过了,如果是的话,便不会再次重复导入同样的内容。
如果要包含的文件不存在,include提示notice,然后继续执行下面的语句,require提示致命错误并且退出

E. 对include()来说,在include()执行时文件每次都要进行读取和评估;而对于require()来说,文件只处理一次 (实际上,文件内容替换了require()语句) 。

F. include(), The include() 语句包括并运行指定文件。
以下文档也适用于 require()。这两种结构除了在如何处理失败之外完全一样。include() 产生一个警告而 require() 则导致一个致命错误。换句话说,如果你想在遇到丢失文件时停止处理页面就用 require()。include() 就不是这样,脚本会继续运行。同时也要确认设置了合适的 include_path。

当一个文件被包括时,其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。
[php title="基本的 include() 例子 vars.php"]

$color = 'green';
$fruit = 'apple';

[/php]

[php title="test.php"]
echo "A $color $fruit"; // A
include 'vars.php';
echo "A $color $fruit"; // A green apple
[/php]

如果 include 出现于调用文件中的一个函数里,则被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。
[php title="函数中的包括"]
function foo() {
global $color;
include 'vars.php';
echo "A $color $fruit";
}

/* vars.php 是在foo()函数中被include, 所以$fruit 在foo()函数外它是不可用的,
* 而$color已被定义为全局变量,所以它是可用的
*/

foo(); // A green apple
echo "A $color $fruit"; // A green
[/php]
当一个文件被包括时,语法解析器在目标文件的开头脱离 PHP 模式并进入 HTML 模式,到文件结尾处恢复。由于此原因,目标文件中应被当作 PHP 代码执行的任何代码都必须被包括在有效的 PHP 起始和结束标记之中。

如果 "URL fopen wrappers" 在 PHP 中被激活 (默认配置) ,可以用 URL (通过 HTTP) 而不是本地文件来指定要被包括的文件。如果目标服务器将目标文件作为 PHP 代码解释,则可以用适用于 HTTP GET 的 URL 请求字符串来向被包括的文件传递变量。严格的说这和包括一个文件并继承父文件的变量空间并不是一回事;该脚本文件实际上已经在远程服务器上运行了,而本地脚本则包括了其结果。

Notice:Windows 版本的 PHP 目前还不支持该函数的远程文件访问,即使 allow_url_fopen 选项已被激活。
[php title="通过 HTTP 进行的 include()"]
/* This example assumes that www.example.com is configured to parse .php *
* files and not .txt files. Also, 'Works' here means that the variables *
* $foo and $bar are available within the included file. */

/*Won't work; file.txt wasn't handled by www.example.com as PHP include
'http://www.example.com/file.txt?foo=1&bar=2';
*/

// Won't work; looks for a file named 'file.php?foo=1&bar=2' on the
// local filesystem.
include 'file.php?foo=1&bar=2';

// Works.
include 'http://www.example.com/file.php?foo=1&bar=2';

$foo = 1;
$bar = 2;
include 'file.txt'; // Works.
include 'file.php'; // Works.
[/php]

相关信息参见使用远程文件,fopen() 和 file()。
因为 include() 和 require() 是特殊的语言结构,在条件语句中使用必须将其放在语句组中 (花括号中) 。
[php language="title="与条件语句组"]
// This is WRONG and will not work as desired.
if ($condition)
include $file;
else
include $other;


// This is CORRECT.
if ($condition) {
include $file;
} else {
include $other;
}
[/php]

处理返回值:可以在被包括的文件中使用 return() 语句来终止该文件中程序的执行并返回调用它的脚本。同样也可以从被包括的文件中返回值。可以像普通函数一样获得 include 呼叫的返回值。

注: 在 PHP 3 中,除非是在函数中调用否则被包括的文件中不能出现 return。在此情况下 return() 作用于该函数而不是整个文件。
[php title="include() 和 return() 语句 return.php"]
$var = 'PHP';
return $var;
[/php]
[php title="noreturn.php"]
$var = 'PHP';
[/php]
[php title="testreturns.php"]
$foo = include 'return.php';
echo $foo; // prints 'PHP'
$bar = include 'noreturn.php';
echo $bar; // prints 1
[/php]

$bar 的值为 1 是因为 include 成功运行了。注意以上例子中的区别。第一个在被包括的文件中用了 return() 而另一个没有。其它几种把文件 包括 到变量的方法是用 fopen(),file() 或者 include() 连同输出控制函数一起使用。

如果一个文件不想被包含多次可以使用include_once或require_once读取,写入文档数据
[php]
function r($file_name) {
$filenum = @fopen($file_name, "r");
@flock($filenum, LOCK_SH);
$file_data = @fread($filenum, filesize($file_name));
@fclose($filenum);
return $file_data;
}
function w($file_name, $data, $method = "w"){
$filenum = @fopen($file_name, $method);
flock($filenum, LOCK_EX);
$file_data = fwrite($filenum, $data);
fclose($filenum);
return $file_data;
[/php]