Idle Works, Idle Thoughts

PHP学习笔记

PHP是一门主要用在Web Server端的编程语言,它的主要作用是接受HTTP请求,读取数据库,生成HTML。

PHP基础常识

PHP的代码,应该放在如下的代码框架中:

<?php
    /* your PHP codes */
?>

PHP代码可以直接嵌入到HTML中,如:

<title><?php echo $title; ?></title>    

编译PHP时,通常会生成供Apache等HTTPServer调用的动态库,同时也会生成一个叫做php的命令行工具。它有一些基础用法。

一、查看PHP的基本信息:

$ php -i

会输出PHP的版本信息、编译参数、php.ini文件的位置,以及所有的PHP动态运行时参数。这条命令等同于:

$ php -r "phpinfo();"

查看 PHP 的编译参数:

$ php -i | grep "Configure Command"

查看php.ini的位置:

$ php --ini

php.ini中有一些常用的配置参数:

max_file_uploads = 20    
upload_max_filesize = 2M
post_max_size = 8M
date.timezone = Asia/Shanghai

二、查看加载的模块信息:

$ php -m

常见的模块如mysql、json、memcached等。以下是一些常见模块说明。

2.1 Memcache模块和Memcached模块。

这两个模块几乎有一样的名字,接口和完成的功能也大同小异,在php.ini也各有一组配置参数。那么,你到底应该用哪一个?

区别 Memcache 模块 Memcached 模块
依赖 zlib 库 libmemcached 库
初始化 $m = new Memcache() $m = new Memcached()

总体而言,Memcached有更好的性能,但名字取得太烂,它的名字和Memcache Server的进程名字(memcached)一样啦。

2.2 mysql, mysqli 和 mysqlnd 模块。

mysql最早是由MySQL AB公司(现在是Oracle)提供的驱动(libmysql)。是一个比较老的驱动,PHP 5.3后就建议不要使用mysql驱动,而应该使用mysqlnd。

mysqlnd是mysql native driver之意,因为是由PHP源码直接提供的mysql驱动支持,因此叫做native driver。

mysqlnd驱动提供给PHP程序员访问MySQL服务器的一套API接口,叫做mysqli,也就是mysql improved之意。因此,我们应该用libmysqli取代libmysql,这是毫无疑问的。

从源代码编译 PHP 模块/扩展

下载 PHP 源码解压后,有一个 ext 目录,是 PHP 扩展,可以单独编译。方法:

$ cd ext/ext-name

$ phpize

$ ./configure

$ make

$ sudo make install

有些模块必须编译到 PHP 中,而不能作为动态库加载,例如 mysqlnd。在完成以上步骤后,ext-name.so 会被复制到扩展目录下,应该修改 php.ini 文件,并添加 extension=ext-name.so 启用它。可以通过 php -i 查看扩展目录:

$ php -i | grep extension_dir
extension_dir => /usr/lib64/php/modules

用 pear/pecl 下载 PHP 模块/扩展

PEAR 是 PHP Extension and Application Repository 的缩写,也就是一个 PHP 扩展和应用的管理工具,相当于 Linux 的 rpm / yum,Perl 的 CPAN。PHP 安装时,通常会绑定核心的 PEAR 包(例如 PEAR 本身)。

与PEAR类似的PHP扩展有PECL(PHP Extension Community Library)。PECL发音类似pickle,是用C编写的PHP扩展(PEAR是用PHP写的扩展)。

查看已安装的Pear扩展:

$ pear list

搜索一个扩展:

$ pear search GeoIP

查看Pear的配置:

$ pear config-show

查看单个Pear的配置,如扩展的安装目录:

$ pear config-get php_dir

修改Pear配置,例如修改扩展的安装目录:

$ pear config-set php_dir /usr/share/pear

PHP 语法

数组

普通数组:

$arr = [1, 2, 3];
$arr = array(1, 2, 3);

关联数组:

$arr = [ "name" => "idleworks", age => 27 ];
$arr = array( "name" => "idleworks", age => 27 );

foreach遍历数组:

foreach($arr as $elem) {}

遍历关联数组:

foreach($arr as $key => $value) {}

如果要修改数组内容,需要引用数组元素:

foreach($arr as &$elem) {}
foreach($arr as $key => &$value) {}

判断数组是否是关联数组:

function is_assoc($arr)
{
    return (array_keys($arr) !== range(0, count($arr) - 1));        
}

字符串

声明字符串:

$s = "hello, world!";

.拼接字符串:

$s = "hello, " . "world!";
$s .= "Welcome to my site.";    

字符串和数字进行加减,会自动将字符串先转为数字,如:

echo "640" - 30 . "\n";     // 610
echo "1,640" - 30 . "\n";   // -29

两个字符串进行加减,依然会先转数字,如:

echo "640" - "30" . "\n";     // 610
echo "1,640" - "30" . "\n";   // -29

密码

PHP 中存储与验证用户密码的安全写法:

$hash = password_hash($password, PASSWORD_DEFAULT);

// save hash in database

if(password_verify($pass, $hash)) {
    // password is valid.
}

password_hash() 生成 Hash 值并存储;从数据库读取 Hash,并用password_verify()验证密码是否正确。在password_hash() 内部,会随机生成 Salt,因此对于同一个密码,每次生成的 Hash 不同,且不必将 Salt 存储在数据库中,因为password_verify()会自行处理。这两个函数需 PHP 5.5.0 以上版本。

如果你的 PHP 版本低于 5.5.0,可以考虑使用 phpass 这个库,使用方法:

$hasher = new PasswordHash(8, FALSE);
$hash = $hasher->HashPassword($pass);           // length: 60

// save hash in database

if($hasher->CheckPassword($pass, $hash)) {
    // password is valid.
}

如果你要自己写(不建议),较为安全存储密码的方式如:

$salt = openssl_random_pseudo_bytes(16);        // 16 bytes
$salthex = bin2hex($salt);                      // length: 32
$hash = hash('sha512', $salthex . $pass);       // length: 128

// save hash & salt in database

if($db_hash == hash('sha512', $db_salt . $user_pass)) {
    // password is valid.
}

SALT 的关键是要足够长、足够随机。SAH-512 算法要比 MD5、SHA1 安全,但依然容易被破解。可以连续运行 SHA-512 100 次,这样会更安全。

日期与时间

设置时区:

date_default_timezone_set("Asia/Chongqing");

格式化时间:

string date(string $format, [, int $timestamp = time() ])

日期文本转Unix时间戳:

int strtotime(string $time, [, int $now = time() ]);

获取当前日期:

date("Y-m-d");      // 2015-11-21

常用格式:

字符 说明
Y 4位数字的年
m 01-12的月份
M Jan-Dec的月份
d 01-31的日期
D Mon-Sun的日期
j 1-31的日期,无前缀0
l 小写L, Sunday-Saturday

strtotime()

日期比较:

$start = strtotime("20150415");
$end = strtotime("2015-04-13");
if($start < $end) { }

日期推算

明天:

date('Y-m-d', strtotime("+1 day"));

本周五:

date("Y-m-d", strtotime("this Friday"));

下周五:

date('Y-m-d', strtotime("next Friday"));

PHP函数式编程

PHP函数式编程的基础在于array_walk()系列函数。

array array_map ( callable $callback , array $array1 [, array $... ] )

bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )

mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )

array array_filter ( array $array [, callable $callback ] )

array_map()是把数组视为普通数组,一个元素作为一条记录来处理。

array_walk()是把数组视为关联数组,作为key/value对来处理。

这两个函数参数中的callback(回调函数)极为不同。而array_walk()的callback函数原型是:

function(&$value, $key);

用 PHP 处理客户请求

将 HTML 代码保存为 .html 文件

可以直接在 .php 中写 HTML 代码,如:

<?php? ob_start(); ?>
<html>
    <head>
        <title>Save HTML codes to file</title>
    </head>
    ...
</html>
<?php
    $str = ob_get_contents();

    // handle $str, like save to file or email it, etc.

    ob_flush();
?>

multiple提交

HTML页面,注意这里要用name="likes[]"这样的写法:

<form action="/sel-likes.php">
    <select name="likes[]" multiple="multiple">
        <option value="sports">体育</option>
        <option value="movie">电影</option>
        <option value="book">读书</option>
    </select>
    <input type="submit" value="提交" />
</form>

PHP端的处理:

foreach($_GET["msel"] as $sel) {
    echo $sel;
}