Server-side Programming 杂记

COMP3322 课程 Server-side technologies 相关内容拾遗。

从 HTML, CSS, JavaScript (JQuery) 一路走来,我们接触的几乎都是 client-side technologies。接下来我们将完成 web technologies 的另一块拼图 — server-side technologies。


  This article is a self-administered course note.

  It will NOT cover any exam or assignment related content.


Server-side Programming

Server-side Programming 是 dynamic sites 的基础。

Dynamic Site Workflow

主角们分别是:

  • Browser: 某个客户端主机。
  • Web server: 某个服务器主机。
  • File system: 服务器主机的 file system,存储 static resource 与 HTML template。
  • Web application: 服务器主机搭载的各种程序 (任何 server-side codes),即本节所介绍的内容。
  • Database: 某个数据库。

Client-side browser 与 Server 是如何进行交互的?

  • Browser 向 server 发出 HTTP request。
  • Server 将 request 中存储的信息 (URL, GET/POST data, Cookies) 作为输入执行 web applications。
  • Web application 从 file system 中获取 HTML 模板,从 database 中获取对应的数据插入 HTML 模板的 placeholders 中生成 HTML page。
  • Server 从 file system 中获取 static resource 进一步更新该 HTML page。
  • Server 将该 HTML page 作为 HTTP response 对 browser 进行回应。
  • Browser 执行本地的 applications (HTML, CSS, JavaScript;或进行 cross-site request) 再进一步更新该 HTML page。最后进行渲染并将该 HTML page 呈现在屏幕上。


PHP

PHP 是一种著名的 hypertext preprocessor

PHP code can be embedded directly with an HTML file.

我们基本可以将 .html.php 视作同一种文件。

在 HTML/PHP 文件中,被 <?php, ?> 标签包围的是 PHP code,在该标签外的是 HTML code。PHP code 可以镶嵌在 HTML code 中的任意一处,用法十分灵活。

需要注意的是,server 传递给 client 的 HTML/PHP 文件一定是由纯 HTML code 组成的。这是因为在生成 HTTP response 之前,server 将首先执行 HTML/PHP 文件中的所有 PHP code,再将生成的新 HTML/PHP 文件作为 response 发送给 client;这也是为什么 PHP 被称为 hypertext pre-processor。

PHP 是一种 dynamically typed language,其语法和 JavaScript 十分类似。

Variable Scope

PHP 中的变量以 $ 开头。

  • local scope: variables inside a function can be referenced solely in that function.
  • global scope: variables outside a function can only be accessed outside a function.
  • static scope: variable inside a function continues to exist after the function terminates.

唯一要注意的是 PHP 中的全局变量默认是不能在函数内部访问的;这和传统的 lexical scope 很不同。我们需要通过添加 global 关键字从函数内部访问全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$a = "outside";

function go_1() { // inside
$a = "inside";
echo $a;
}

function go_2() { // outside
global $a;
echo $a;
}
?>

另外,PHP 中所有函数的默认 scope 是 global scope。

  • They can be called outside a function even if they were defined inside another function.

Arrays

PHP 中的 arrays 默认是 associative arrays。

  • Array keys must be either integers or strings and need not be sequential.
  • We keys are not explicitly defined, they are 0, 1, ...
1
2
3
4
5
$array[0] = "Narukami Yu";
$array[1] = "Hanamura Yosuke";
$array["meat_lover"] = "Satonaka Chie";
$array[5] = "Tatsumi Kanji";
$array[] = "Kujigawa Rise";

在这个例子中,理世将被添加到 $array[6] 中:这是因为 5 是最后一个确定的 numeral array index。

Superglobal Variables

Predefined assotiative arrays in PHP that can always be accessible, regardless of scope.

  • $_GET: name/value pairs sent from the client with GET method.
  • $_POST: name/value pairs sent from the client with POST method.
  • $_COOKIE: cookie name/value pairs.
  • $_SESSION: session name/value pairs.
  • $_REQUEST: contents of $_GET, $_POST and $_COOKIE.
  • $_SERVER


MySQL & PHP Support

Relational DBMS (database management system)

  • 行 (rows) 为 records
  • 每个 row 有若干个 columns;即每个 record 有若干个 fields
  • 每个 table 有至多一个 special field — primary key,用于 uniquely identify each record。

MySQL Command

使用 CREATE 创建一个 table:

1
2
3
4
5
6
CREATE TABLE stdRecord (
uid VARCHAR(255) NOT NULL,
val VARCHAR(255) NOT NULL,
timestamp VARCHAR(255) NOT NULL,
PRIMARY KEY(uid)
);

MySQL 命令的 semantic meaning 很明显,因此不在此详细介绍。除 CREATE 之外的 keywords 还有:

  • INSERT:插入新的 record。配合 INTO (table) 与 VALUES (value)。
  • DELETE:删除 record(s)。配合 FROM (table) 与 WHERE (key 或其他 fields)。
  • SELECT:提取 record(s)。配合 FROM (table) 与 WHERE (key 或其他 fields)。
  • UPDATE:更新 record(s)。配合 SET (需要更新的 fields) 与 WHERE (key 或其他 fields)。

PHP support for MySQL

第一步:opens a new connection to MySQL server. $conn is a connection object.

1
2
3
4
5
6
7
define("DB_HOST", "mydb");
define("USERNAME", "dummy");
define("PASSWORD", "c3322b");
define("DB_NAME", "db3322");

$conn = mysqli_connect(DB_HOST, USERNAME, PASSWORD, DB_NAME)
or die("Connection Error! ".mysqli_connect_error());

第二步,使用 mysqli_query() 执行指定的 query。$record is a mysqli_result object.

  • successful SELECT, SHOW, DESCRIBE, or EXPLAIN: return a mysqli_result object.
  • successful DELETE, UPDATE: return TRUE.
  • failed query: return FALSE.
1
2
3
4
5
6
$query = "SELECT * FROM stdRecord WHERE uid = '$user_id'";
$record = mysqli_query($conn, $query);
if ($record == false) {
http_response_code(500);
die("Query Error! ".mysqli_error($conn));
}

第三步,对 mysqli_result 对象进行处理,提取数据。

1
2
3
4
5
6
7
8
9
if (mysqli_num_rows($record) > 0) {
while ($row = mysqli_fetch_array($record)) {
echo "<p>".$row['uid']."<br>";
echo $row['val']."<br>";
echo $row['timestamp']."</p>";
}
} else {
echo "<p>No record.</p>";
}

第四步,free resources and close connections.

1
2
mysqli_free_result($record);
mysqli_close($conn);


Cookies & Sessions

有点难搞的两个概念,花了挺久才彻底弄明白。

Cookies

Cookies are the key/value pairs maintained by browsers.

一定要记住,cookies 是在 browser (client-side) 被存储与更新的。那么具体来说 cookies 是如何工作的呢?

Server-side (PHP application):

  • 收到 browser 发送的 HTTP request。
    • GET: 通常携带与 user_id 有关的 cookie。
    • PUT/POST: 通常携带与 user preference 有关的 cookie。
  • PHP 端通过 $_COOKIE['cookie_name'] 获取 request 中储存的 cookie。
  • 根据获取的 cookie 执行相应的代码,生成 PHP/HTML 文件并作为 HTTP response 发回给 browser。
  • 若 HTTP request 中没有携带 cookie (e.g. !isset($_COOKIE['user_id'])),(如需要) 将在 PHP 端生成对应的 cookies 作为 HTTP response 的一部分发回给 browser。
    • 具体来说,PHP 端通过 setcookie() 函数在 response 中添加 Set-Cookie header。

Client-side (JavaScript application):

  • 收到 server 发回的 HTTP response。
  • browser 将 response 中的 Set-Cookie header 指定的 cookies 存储在 document.cookie 中。(一般来说,若 cookie expires,document.cookie 中对应的 cookie 将消失)
  • JavaScript 端能够访问或更改 document.cookie
  • 此后所有对 /index.php 发起的 HTTP request,browser 都会自动将 document.cookie 中存储的 cookies 作为 request 的一部分发送给 server。

为什么说 cookies 是 browser-maintained 的,答案就在于 document.cookie。我之前一直以为其与 PHP 中的 $_COOKIE[] 起到的作用相同,但其实大相径庭。

  • document.cookie 存储在 browser 中。
  • document.cookie 能被 PHP 端间接修改 (browser 根据 response 中的 Set-Cookie header 进行修改),也能被 JavaScript 端直接修改。
  • document.cookie 将自动 (implicitly) 包含在 HTTP request (Cookie header) 中。
  • $_COOKIE[] simply 是 server 接收到的 request 中的 cookie 信息的接口。对 $_COOKIE[] 的修改是没有意义的 (因为这并不会影响到 document.cookie 中存储的 cookies)。

Sessions

Sessions 本质上也是一种 cookies,只不过它在 PHP 端添加了对应的 authenticate 机制。PHP 端将 session id 存储在 $_SESSION[] 中以便进行 authentication。

  • A session starts with the PHP session_start() function.
  • 与上述过程几乎一致。区别在于当 server 通过设置 $_SESSION[] 而不使用 setcookie() 来操纵 session id。类似 $_SESSION['sid'] = $sid 这样的 statements 有两个作用:
    • 将 session id 存储在 $_SESSION[] 中。
    • 类似于 setcookie(),将 session id 发回并指导 browser 将其存储在 document.cookie 中。
  • server 收到 response 后,通过 $_COOKIE[] 获取 session id,并将其与 $_SESSION[] 中存储的信息进行比较并进行 authentication。若 success,解禁 session storage 中存储的 session data。

session id 与普通 cookie 的另一个区别在于其有效期:我们可以通过 setcookie() 来指定 cookie 的有效期,在 cookie expire 之前它都将一致存储在 document.cookie 中。

而 session id 并不通过 setcookie() 来操纵。这是因为 session id 的 life span 很短:session 通常是临时的,一旦用户关闭对应的 tab 或 window,session 就将被销毁。


Reference

  This article is a self-administered course note.

  References in the article are from corresponding course materials if not specified.

Course info: Code, COMP3322. Lecturer, Dr. Tam Anthony Tat Chun.

-----------------------------------そして、次の曲が始まるのです。-----------------------------------