An Introduction to PHP and Web Programming

这篇文章是我给信管专业同学大二暑期实习进行培训所准备的Syllabus,包含了对PHP语言的介绍,以及如何使用jQuery进行简单的Ajax编程。

0.Preparation


PHP(“PHP: Hypertext Preprocessor”,超文本预处理器的字母缩写)是一种被广泛应用的开放源代码的多用途脚本语言,它可嵌入到HTML中,尤其适合web开发。
PHP

谁在用PHP:

  • Facebook
  • Baidu
  • Yahoo!
  • Wikipedia
  • 腾讯
  • 淘宝 & 天猫
  • 新浪 & 新浪微博
  • 搜狐
  • 优酷

准备工作:

  • 环境配置 XAMPP
  • 文本编辑器 推荐vim or sublime text2
  • 打开DEBUG选项,在php.ini文件中编辑
display_errors = On
display_startup_errors = On
  • phpinfo() 函数

流程:

1.PHP Syntax


1.1 Basic Syntax

PHP可以与HTML混排,即插入HTML代码中。但需要以<?php开始, 以?>结束,每句分号结尾:

echo语句为输出语句,相当与C语言中的printf函数。

<html>
<body>
<?php
echo "Hello World";
?>
</body>
</html>

echo & 注释

echo语句的输出会直接写入HTML

<?php
echo "Comment will not appear"; // 单行注释
/*
这是一段
注释块
*/
?>


1.2 Variables and Constants

PHP是一门松散类型语言(Loosely Typed Language),不需要在使用前声明变量,也不需要声明变量类型。

变量名以字母或_开头,变量名只能包含数字、字符和下划线,大小写敏感。

类型:

  • Boolean
  • Integer
  • Float
  • String
  • Array
  • Object
  • Resource

所有变量以$开头:

<?php
//合法的变量
$a = "this is a string";
$b = 12;
$pi = 3.14;
echo $pi;
?>

定义常量:

<?php
define("FOO", "something");
define("FOO2", "something else");
echo FOO;
?>

var_dump() 函数查看变量的值,可适用于任何变量:

<?php
$s = "this is a string";
var_dump($s);

$arr = array("lisp", "scheme", "haskell");
var_dump($arr);
?>


1.3 String

字符串以'"扩起。

<?php
echo "Hello PHP";
echo 'Arnold once said: "I\'ll be back"'; //输出: Arnold once said: "I'll be back"
?>

字符串内嵌变量:

<?php
$great = 'fantastic';
echo "This is {$great}";
echo "This is ${great}";
?>

字符串连接:

<?php
$str1 = "Hello";
$str2 = "PHP";
echo $str1 . " " . $str2;
?>

strlen()

<?php
$str1 = "Hello";
echo "Len of str1: " . strlen($str1);
?>

strpos() 字符串查找

<?php
$str1 = "This is a string";
echo strpos($str1, "is"); // 5
?>

substr() 截取子字符串

<?php
$str1 = "This is a string";
echo substr($str1, 10, strlen($str1));
?>

trim() 修剪字符串

<?php
$str1 = "   This is a string  \n ";
echo trim($str1);
?>

处理中文字符串参考:多字节字符串

1.4 Array

数组是key到value的映射,通过通过key可以访问value。key 可以是 integer 或者 string。value 可以是任意类型。

key如果未指定,PHP 将自动使用之前用过的最大 integer 键名加上 1 作为新的键名。

<?php
// 创建数组
$names = array("PHP", "Java", "Python");
// Same as above
$names[0] = "PHP";
$names[1] = "Java";
$names[2] = "Python";

echo "Names are " . $name[0] . " " . $names[1] . " " . $names[2];
echo "Names are ${names[0]} {$names[1]} ${names[2]}"; // Same as above

// 创建关联数组
$array = array(
    "foo" => "bar",
    "bar" => "foo",
);

// 另一种方法,自 PHP 5.4 起可用
$array = [
    "foo" => "bar",
    "bar" => "foo",
];

echo $array["foo"] . " " . $array["bar"];

$array = array(
    1    => "a",
    "1"  => "b",
    1.5  => "c",
    true => "d",
);
var_dump($array);    //所有键值都被转为1,同时后定义的键名会覆盖之前定义的

// 删除键值
$a = array(1 => 'one', 2 => 'two', 3 => 'three');
unset($a[2]);
var_dump($a);    // $a = array(1 => 'one', 3 => 'three');

// 删除后重新排列键值
$b = array_values($a);

// 多维数组
$fruits = array ( "fruits" => array("a" => "orange",
                                    "b" => "banana",
                                    "c" => "apple"
                                    ),
                  "numbers" => array( 1,
                                      2,
                                      3,
                                      4,
                                      5,
                                      6
                                     ),
                  "holes" => array("first",
                                   5 => "second",
                                   "third"
                                   )
                );

echo $fruits["holes"][5];    // prints "second"
echo $fruits["fruits"]["a"]; // prints "orange"
?>


1.5 Flow Control

1.5.1 if & else & elseif

<?php 
    $number = 11;
    if (10 == $number) {
?>
    <p>This will show if the expression is true.</p>
<?php 
    } elseif (12 > $number) { 
?>
    <p>This will show if the elseif condition is true.</p>
<?php 
    } else {
?>
    <p>Otherwise this will show.</p>
<?php } ?>

<?php 
    // Same as above
    $number = 11;
    if (10 == $number): 
?>
    <p>This will show if the expression is true.</p>
<?php elseif (12 > $number): ?>
    <p>This will show if the elseif condition is true.</p>
<?php else: ?>
    <p>Otherwise this will show.</p>
<?php endif; ?>


1.5.2 while

<?php
$i = 1;
while ($i <= 10) {
    echo $i++;
}

// same as above
$i = 1;
while ($i <= 10):
    print $i;
    $i++;
endwhile;
?>


1.5.3 for foreach

基本语法

<?php
for (initialization; condition; increment)
{
    code to be executed;
}

initialiaztion;
while (condition) {
    code to be executed;
    increment;
}
?>

四种不同的方式输出数字1到10:

<?php
for ($i = 1; $i <= 10; $i++) {
    echo $i;
}

for ($i = 1; ; $i++) {
    if ($i > 10) {
        break;    // break终止循环
    }
    echo $i;
}

$i = 1;
for (;;) {
    if ($i > 10) {
        break;
    }
    echo $i;
    $i++;
}

for ($i = 1, $j = 0; $i <= 10; $j += $i, print $i, $i++);
?>

foreach,在循环数组时特别有用:

<?php
$colors = array('red', 'blue', 'green', 'yellow');
foreach ($colors as $color) {
    echo "Do you like $color?\n";
}

$fruits = array("apple" => "red", "pear" => "yellow", "orange" => "orange");
foreach ($fruits as $key => $color) {
    $fruit[$key] = strtoupper($color);
}
print_r($fruits);
?>


1.6 Function and Class

使用函数:

<?php
function multiple($arr) // 复制传值
{
    return $arr[0] * $arr[1];
}
echo multiple(array(5, 6)); // 向参数传递一个数组

function multiple2(&$arr) // 引用传值,改变了原数组的值
{
    $arr[2] = $arr[0] * $arr[1];
}
$a = array(3, 4);
multiple2($a);
var_dump($a);

// 默认参数
function myrange($start, $end, $step = 1)
{
    $res = array();
    for($i = $start; $i <= $end; $i += $step) 
    {
        $res[] = $i;
    }
    return $res;
}
$a = myrange(1, 20);
var_dump($a);
$a = myrange(1, 20, 3);
var_dump($a);

// 递归函数
function recursion($a)
{
    if ($a < 20) {
        echo "$a\n";
        recursion($a + 1);
    }
}
recursion(1);
?>

面向对象编程

<?php
class foo
{
    function do_foo()
    {
        echo "Doing foo."; 
    }
}

$bar = new foo;
$bar->do_foo();
?>

参考:PHP官方手册中关于类和对象的说明

2.Interact with Browser


2.1 HTML form

当用户单击确认按钮时,表单的内容会被传送到另一个文件。表单的动作属性定义了目的文件的文件名。由动作属性定义的这个文件通常会对接收到的输入数据进行相关的处理。

所有表单项需放在<form></form>之间,每个表单项必须有name属性。

<form>需设置action属性及method属性,action可以设置为get或post,get和post都是HTTP的标准方法。

2.2 GET

$_GET 变量包含了来自 method=”get” 的表单中的值。GET方法发送时,变量名和值都会显示在 URL 中,URL可持久化。

传送的值不能超过100个字符。

Filename: get.html

<form action="get_action.php" method="get">
姓名: <input type="text" name="name" /> <br />
年龄: <input type="text" name="age" /> <br />
性别:<input type="radio" name="sex" value="male" /> 男
<input type="radio" name="sex" value="female" /> 女
<input type="submit" />
</form>

Filename: get_action.php

您输入的姓名是 <?php echo $_GET["name"]; ?>.<br />
您的年龄是 <?php echo $_GET["age"]; ?> 岁. <br />
您的性别是 <?php echo $_GET["sex"] == "male" ? "男" : "女" ?>

URL形如:
http://127.0.0.1/get_action.php?name=Lily&age=12&sex=female

2.3 POST

$_POST 变量包含了来自 method=”post” 的表单中的值。表单所传递的参数和值不会显示与URL上,理论上可传递无限多个参数,长度也没有限制。

Filename: post.html

<form action="post_action.php" method="post">
用户名: <input type="text" name="username" /> <br />
密码: <input type="password" name="password1" /> <br />
密码确认: <input type="password" name="password2" /> <br />
<input type="submit" />
</form>

Filename: post_action.php

<?php
if ($_POST['password1'] == $_POST['password2']) {
    echo "注册成功";
}
else {
    echo "您两次输入的密码不一样";
    echo "<br /><a href='post.html'>返回</a>";
}
?>

URL仍然是http://127.0.0.1/post_action.php,但服务器已经收到数据。

Cookie是服务器留在用户计算机中的小文件,由浏览器维护。通过PHP,可以创建并取回 cookie 的值。cookie常用于识别用户。

Cookie须在HTML文件的内容输出(echo语句)之前设置。Cookie是HTTP协议头的一部分,用于浏览器和服务器之间传递信息,所以必须在任何属于HTML文件本身的内容输出之前调用Cookie函数。

Cookie是限制在客户端的,最大为4KB。

设置Cookie:

<?php
// Filename: setcookie.php
$r = rand(1, 100);
setcookie('random', $r);

setcookie('random', $r, time()+24*60*60);    // 第三个参数为失效时间,秒计
?>

设置后的Cookie在浏览下一个页面时才生效。

读取Cookie:

<?php
// Filename: readcookie.php
echo $_COOKIE['random'];
?>

删除Cookie:

<?php
// Filename: deletecookie.php
setcookie('random', '');

setcookie('random', 'anything', time()-1);    // 浏览完即失效
?>


2.5 Session

Session机制是一种服务器端的机制,每一个网站访客都会被分配给一个唯一的标志符,即会话ID。PHP的会话机制是通过设置Cookie,在Cookie中保存会话ID(Session ID),并在服务器端生成session文件,与用户进行关联。

启动会话,session_start() 函数必须位于 标签之前:

<?php 
// Filename: start_session.php
session_start(); 
?>

<html>
<head>
</head>
<body>
</body>
</html>

设置Session变量:

<?php
// Filename: set_session.php
session_start();
if (!isset($_SESSION['count'])) {
  $_SESSION['count'] = 0;
} else {
  $_SESSION['count']++;
}
?>

销毁Session变量或结束会话:

<?php
// Filename: unset_session.php
session_start();
unset($_SESSION['count']);

session_destroy();
?>


3.Interact with MySQL


3.1 Overview

三种PHP内置的连接MySQL的方式:

  • mysqli (mysql improved),我们就用这个
  • PDO (PHP Data Objects)
  • mysql (不建议使用,在PHP5.5.0中已经不被支持,在下一个版本中将被移除)

三种方式的示例:

<?php
// just samples of mysql, mysqli and PDO
// mysqli
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = $mysqli->query("SELECT 'Hello, dear MySQL user!' AS _message FROM DUAL");
$row = $result->fetch_assoc();
echo htmlentities($row['_message']);

// PDO
$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT 'Hello, dear MySQL user!' AS _message FROM DUAL");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['_message']);

// mysql
$c = mysql_connect("example.com", "user", "password");
mysql_select_db("database");
$result = mysql_query("SELECT 'Hello, dear MySQL user!' AS _message FROM DUAL");
$row = mysql_fetch_assoc($result);
echo htmlentities($row['_message']);
?>

数据导入:

CREATE DATABASE mytest;
USE mytest;
SET NAMES 'utf8';
CREATE TABLE user (
    `userid` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `username` CHAR(15) NOT NULL,
    `password` CHAR(40) NOT NULL,
    `description` VARCHAR(200),
    PRIMARY KEY(userid, username)
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT INTO user (`username`, `password`, `description`) 
    VALUES ("lily", "19930501", "This is Lily");
INSERT INTO user (`username`, `password`, `description`) 
    VALUES ("john", "19940621", "This is John");
INSERT INTO user (`username`, `password`, `description`) 
    VALUES ("christopher", "19930101", "This is Christopher");
INSERT INTO user (`username`, `password`, `description`) 
    VALUES ("bill", "19920814", "This is Bill");


3.2 Basic Query

面向对象风格与过程式风格:

<?php
// 使用面向对象风格调用mysqli
// Filename: mysqli.php
$mysqli = new mysqli("localhost", "user", "password", "mytest");
if ($mysqli->connect_errno) {
    echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
$mysqli->query("SET NAMES 'utf8'");
$res = $mysqli->query("SELECT * FROM user");
while($row = $res->fetch_assoc()) {
    var_dump($row);
    echo '<br />';
}

// 使用过程式风格调用mysqli
$mysqli = mysqli_connect("localhost", "user", "password", "mytest");
if (mysqli_connect_errno($mysqli)) {
    echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$mysqli->query("SET NAMES 'utf8'");
$res = mysqli_query($mysqli, "SELECT * FROM user");
while ($row = mysqli_fetch_assoc($res)) {
    var_dump($row);
    echo '<br />';
}
?>


3.3 Example

一个完整的用户注册、登录、注销的例子。

Filename: reg.php

<?php
function check_username(&$mysqli, $username)
{
    $sql = "SELECT * FROM `user` WHERE `username`='${username}'";
    $res = $mysqli->query($sql);
    $res_cnt = $res->num_rows;
    if ($res_cnt == 0)
        return FALSE;    // 可以注册
    return TRUE;    // 此用户名已注册
}

if (array_key_exists('username', $_POST) && 
    array_key_exists('password1', $_POST) && 
    array_key_exists('password2', $_POST))
{
    if ($_POST['password1'] == $_POST['password2'])
    {
        $mysqli = new mysqli("localhost", "user", "password", "mytest");
        if ($mysqli->connect_errno) 
        {
            echo "连接MySQL失败: " . $mysqli->connect_error;
        }
        $mysqli->query("SET NAMES 'utf8'");
        if (check_username($mysqli, $_POST['username'])) 
        {
            echo "此用户名已注册";
            echo "<br /><a href='reg.php'>返回</a>";
        }
        else
        {
            $sql = "INSERT INTO `user` (username, password) 
                    VALUES ('{$_POST['username']}', '{$_POST['password1']}')";
            $mysqli->query($sql);
            echo "注册成功";
        }
    }
    else 
    {
        echo "您两次输入的密码不一样";
        echo "<br /><a href='reg.php'>返回</a>";
    }
}
else
{
    echo <<<EOF
    <form action="reg.php" method="post">
    用户名: <input type="text" name="username" /> <br />
    密码: <input type="password" name="password1" /> <br />
    密码确认: <input type="password" name="password2" /> <br />
    <input type="submit" />
    </form>
EOF;
}

?>

Filename: login.php

<?php
session_start();
if (isset($_SESSION['username']))
{
    echo "Welcome " . $_SESSION['username'];
    echo "<br /><a href='logout.php'>登出</a>";
}
elseif (array_key_exists('username', $_POST) && 
    array_key_exists('password', $_POST))
{
    $mysqli = new mysqli("localhost", "user", "password", "mytest");
    if ($mysqli->connect_errno)
    {
        echo "连接MySQL失败: " . $mysqli->connect_error;
    }
    $mysqli->query("SET NAMES 'utf8'");
    $sql = "SELECT * FROM `user` WHERE `username`='{$_POST['username']}' 
        AND `password`='{$_POST['password']}'";
    $res = $mysqli->query($sql);
    if ($res->num_rows)
    {
        $_SESSION['username'] = $_POST['username'];
        echo "登录成功";
        echo "<br /><a href='login.php'>刷新本页</a>";
    }
    else
    {
        echo "密码错误或用户不存在";
        echo "<br /><a href='reg.php'>注册</a>";
        echo "<br /><a href='login.php'>重新登录</a>";
    }
}
else
{
    echo <<<EOF
    <form action="login.php" method="post">
    用户名: <input type="text" name="username" /> <br />
    密码: <input type="password" name="password" /> <br />
    <input type="submit" />
    </form>
EOF;
}
?>

Filename: logout.php

<?php
session_start();
if (isset($_SESSION['username']))
{
    session_destroy();
    echo "Now will logout with " . $_SESSION['username'];
    echo "<br /><a href='login.php'>Login</a>";
}
?>


3.4 安全问题

4.AJAX Introduction


4.1 JavaScript与AJAX

JavaScript是运行与浏览器上的编程语言,通过JavaScript可以动态地操作HTML和CSS。

AJAX则是Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),简单地说,通过AJAX可以在不刷新页面的情况下,由浏览器后台发起和服务器之间的通讯,从而异步地更改网上的数据。而JavaScript是AJAX的基础。

JavaScript和Java除了名字上有四个字母相同,没有任何关系。

<html>
<body>

<p>
JavaScript 能够直接写入 HTML 输出流中:
</p>

<script type="text/javascript">
document.write("<h1>This is a H1</h1>");
document.write("<p>This is a paragraph.</p>");
</script>

</body>
</html>


4.2 jQuery

jQuery 是一个 JavaScript 库,通过jQuery可以简化前端开发,方便地操作DOM、进行AJAX编程和实现动画效果。

  • 导入jQuery

    <head></head>中插入<script src="jquery-1.10.2.min.js"></script>

  • $美元符号

  • 操作HTML内容

    $("#status").html("anything");

  • 使用$.ajax()发起AJAX请求,默认使用get方法

4.3 Example

重新编写用户注册页面,实时检查用户名是否可用:

Filename: user_status.php

<?php
// Filename: user_status.php
function check_username($username)
{
    $mysqli = new mysqli("localhost", "user", "password", "mytest");
    if ($mysqli->connect_errno) 
    {
        echo "连接MySQL失败: " . $mysqli->connect_error;
    }
    $mysqli->query("SET NAMES 'utf8'");
    $sql = "SELECT * FROM `user` WHERE `username`='${username}'";
    $res = $mysqli->query($sql);
    $res_cnt = $res->num_rows;
    if ($res_cnt == 0)
        return FALSE;    // 可以注册
    return TRUE;    // 此用户名已注册
}

if (isset($_GET['username']))
{
    if (check_username($_GET['username']))
    {
        echo "{$_GET['username']} 已经被注册了";
        return ;
    }
    echo "{$_GET['username']} 可以注册";
}
?>

Filename: reg_ajax.php

<html>
<head>
<script src="jquery-1.10.2.min.js"></script>
<script type="text/javascript">
function check(str)
{
    console.log(str);
    if (str.length==0)
    {
        $( "#status" ).html("");
        return
    }
    $.ajax({
        url: "user_status.php",
        data: {
            username: str
        },
        success: function( data ) {
            $( "#status" ).html( "<strong>" + data + "</strong>" );
        }
    });
}
</script> 
</head>
<body>
<?php
function check_username(&$mysqli, $username)
{
    $sql = "SELECT * FROM `user` WHERE `username`='${username}'";
    $res = $mysqli->query($sql);
    $res_cnt = $res->num_rows;
    if ($res_cnt == 0)
        return FALSE;    // 可以注册
    return TRUE;    // 此用户名已注册
}

if (array_key_exists('username', $_POST) && 
    array_key_exists('password1', $_POST) && 
    array_key_exists('password2', $_POST))
{
    if ($_POST['password1'] == $_POST['password2'])
    {
        $mysqli = new mysqli("localhost", "user", "password", "mytest");
        if ($mysqli->connect_errno) 
        {
            echo "连接MySQL失败: " . $mysqli->connect_error;
        }
        $mysqli->query("SET NAMES 'utf8'");
        if (check_username($mysqli, $_POST['username'])) 
        {
            echo "此用户名已注册";
            echo "<br /><a href='reg_ajax.php'>返回</a>";
        }
        else
        {
            $sql = "INSERT INTO `user` (username, password) 
                    VALUES ('{$_POST['username']}', '{$_POST['password1']}')";
            $mysqli->query($sql);
            echo "注册成功";
        }
    }
    else 
    {
        echo "您两次输入的密码不一样";
        echo "<br /><a href='reg.php'>返回</a>";
    }
}
else
{
    echo <<<EOF
    <form action="reg_ajax.php" method="post">
    用户名: <input type="text" name="username" onkeyup="check(this.value)" /> 
    是否可以注册: <span id="status"></span> <br />
    密码: <input type="password" name="password1" /> <br />
    密码确认: <input type="password" name="password2" /> <br />
    <input type="submit" />
    </form>
EOF;
}
?>
</body>
</html>


5.选择一个框架加速开发


5.1 为什么要用框架

框架是
优点:

  • 提供了一个可用的架构和工具集
  • 将开发任务抽象和分工,提高开发效率
  • 集中精力于业务逻辑,而不是编码细节
  • 不要重新发明轮子
  • 控制复杂度,软件开发是与复杂度做斗争
  • 易于维护和扩展

缺点:

  • 需要付出额外的学习成本


5.2 MVC Overview

主流的Web开发框架,当然也包括PHP开发框架一般都采用MVC模式:

  • 模型 (Model) 表示和数据库交互的部分。通常来说,模型类将包含取出、插入、更新你的数据库资料这些功能。
  • 视图 (View) 是展示给用户的信息。一个视图通常是一个网页,也可以是一个页面片段,如页头、页尾,通常包含HTML、CSS和JavaScript代码。
  • 控制器 (Controller) 是模型、视图以及其他任何处理 HTTP 请求所必须的资源之间的中介,并生成网页。


5.3 主要PHP框架

  • Zend Framework
  • Yii
  • CodeIgniter
  • CakePHP
  • Symfony2


6.Reference & Reading List


PHP手册

PHP语言参考

PHP函数参考

PHP之道

W3C School教程

jQuery API参考

CodeIgniter用户指南

一些软件设计的原则

W3C School教程

对技术的态度

程序员技术练级攻略

PECL

PHP单元测试框架PHPUnit

PEAR:PHP Extension and Application Repository