@artman328
2022-09-19T00:31:56.000000Z
字数 35125
阅读 3003
php
web
Web 应用是指那些人们通过浏览器(或其它基于HTTP协议的应用程序)完成诸如信息浏览、数据提交、查询、修改、更新等任务的应用程序体系。它由以下几个部分构成。
客户端程序是那些在计算机或移动设备上与人直接交互的应用程序,如:浏览器、基于 HTTP 协议的其它应用程序等。这些应用程序接受用户输入,将用户的请求(Request)发送到服务器端。
最常用的客户端是各种浏览器。现代浏览器都内置了一个计算机语言编程平台——JavaScript,它包括了一个语言解释器(用于解释并执行程序指令)和常用的程序库(一些通用功能函数和对象)。在这个平台上编程,浏览器就可以和用户直接互动(如用户输入的数据验证,拖拽操作,互动特效等),必要时才将请求发往服务器。在这个平台上的编程叫客户端编程。
Web 服务器程序是接受来自客户端请求并给出响应(Response)的应用程序。Web服务器的主要任务是根据请求将服务器上的各种资源发送给客户端。这些资源包括各种文件,如:网页、图片、音乐、视频等。
在服务器上,根据预定规则,服务器会将一些请求的特定资源(一般为程序文件)再发送给特定的程序解释器进行处理,在处理过程中可能会和数据库服务器进行交互以便对数据库中的数据进行增、删、改、查等操作。程序解释器处理完后将结果交给 Web 服务器,Web 服务器再将其发往客户端。
数据库服务器用以存储WEB应用的数据。
WEB 应用的运作过程可以下图来说明。
在web应用程序中,浏览器和服务器之间是通过 HTTP/HTTPS 协议来交互的。
HTTP 协议,称为超文本传输协议,HTTPS 协议,称为安全的 HTTP 协议。超文本传输协议定义了客户端与服务器端通信的规范。HTTP 是一种无状态协议,意思是服务器端在接受请求并给出响应的过程中,并没有记住客户端任何信息的规范和机制,客户端的每一次访问,对服务器端来说,客户端都是陌生的。
请求由请求的资源地址(URL) 和请求数据段(请求头和请求体)构成。
客户端向服务器端发出请求,是基于 URL 进行的,URL(Universal Resource Locator) 叫做统一资源定位器。
URL 的基本形式是:
http://域名(或 IP 地址):端口/到资源/的具体/路径/资源名
如:
http://www.mysite.com:80/products/images/computer.jpg
浏览器是以 IP 地址的形式发出请求的。人们输入的域名首先被浏览器发送到远程DNS(Domain Name Server)域名服务器,从那里得到对应的 IP 地址,再通过这个地址发出请求。事实上,系统在真正向 DNS 发出查询前,先会向操作系统中的一个特殊配置文件进行查询,如果查询到了对应的 IP ,就不再向 DNS 发出请求了。在 Windows 系统中,这个文件是 C:\Windows\System32\drivers\etc\hosts。在 Linux 和 Unix 系统中,这个文件是 /etc/hosts。
这个文件以如下形式定义了 IP 与 域名的对应关系:
ip 域名
如:
127.0.0.1 www.mysite.com
127.0.0.1 www.example.net
134.179.210.2 www.domywork.org
请求数据段是请求的描述(请求头)和数据(请求体)的集合。
在请求描述中,最关键的是请求的行为。
请求的基本行为有 GET(获取)、POST(粘贴)、PUT(放置)、DELETE(删除)等。最为常用的是前面两者。
响应由服务器给出。响应由响应头和响应数据构成。响应头用于描述响应。在响应头中,有关于响应的状态码。常用状态码是:
如果请求得到正常响应,响应数据就是具体的资源。
XAMPP 是一套预先配置好的 Apache、MySQL、PHP 服务器软件,除服务器软件外,还提供了管理工具和调试工具等。
XAMPP 可以从 http://www.apachefriends.org/ 下载得到。
根据安装程序提示,一步一步安装即可。
以下假定服务器安装在 D:\xampp 下。
安装完毕后,需要将以下几个路径加到系统级的环境变量 PATH 中。
D:\xampp\php;D:\xampp\mysql\bin;
Apache 是著名的 WEB 服务器,主程序是 httpd.exe 。对它的配置主要在 D:\xampp\apache\conf\httpd.conf 中。以下是几个重要的配置项。
DocumentRoot 文件系统中的具体位置
如:
DocumentRoot D:/www/public
<Directory 根路径>
配置项1
配置项2
...
</Directoey>
如:
<Directory D:/www/public>
Options
Allowoverride None
</Directory>
如果访问如下地址:
http://www.mysite.com/
这个地址只指定了文档根路径,并没有指定资源,即要这个路径下的哪个文件。此时服务器有两个选择,一是列出所有资源供选择,二是指定一个缺省资源提供给客户端。如果要指定一个缺省资源,其形式是:
DirectoryIndex 资源名称列表
如:
DirectoryIndex index.php index.html default.html default.htm
这样,当没有指定资源时,服务器会在指定的路径下按以上顺序找到某个资源,如果找到就送出,如果最终没能找到,可能会列出当前路径下的所有资源(由配置决定),或者报告错误。
MySQL/MariaDB 是著名的数据库服务器,主程序是 mysqld。
PHP 是应用最为广泛的 WEB 应用服务器端编程语言。
网页是 Web 应用典型的用户界面(User Interface, UI)。网页文件是一个文本文件,即它是由可见字符构成的。网页文件从服务器传到浏览器后,浏览器根据网页内容渲染出图文并茂的网页界面。
网页的内容是用HTML,即 Hypertext Markup Language(超文本标记语言)来组织的,是用 CSS,即 Cascading Style Sheet 层叠样式表语言来进行布局及显示风格定义的。同时,网页中还可插入 JavaScript 语言程序,实现人与网页的互动。
为了正确显示和布局网页中的不同内容,浏览器需要知道网页的内容构成。网页的内容构成是通过“标签”来标明的。这种通过标签来表明内容类型的语言,叫作标记语言,专用于网页内容标记的语言,就叫做“超文本标记语言”。
超文本标记语言一般用以下形式来标记内容:
<标记>标记内容</标记>
如:<p>这是一个段落</p>
不带斜线的是开始标记,带斜线的是对应的结束标记。
有的标记没有标记内容,可把开始和结束标记写成同一个。如:
<input type="text" value="" />
以及:
<br />
标记可以属性。
如:
<p id="mypara">这是一个段落</p>
其中,id 就是标记 p 的一个属性,用于标明这个标记的识别号。这个识别号在同一个文档中是唯一的。
语言格式:
选择器 {
属性名:属性值;
...
}
如以下把网页中的全部段落的首行统一缩进两个字宽,并把行距设定为字体高度的1.5倍:
p {
text-indent: 2em;
line-height: 1.5em;
}
JavaScript 是一种编程语言,可实现人与网页的直接交互或网页与服务器的直接交互。本教程将不涉及 JavaScript 语言。
网页主要由要两大部分构成,一是头部(head),二是体部(body)。以下是一个网页的基本结构。
<!-- 这个标记里的是注释内容,浏览器不会显示出来 -->
<!doctype html> <!--说明这是一个 html 文档,并且采用了 html 语言的第 5 版本 -->
<html> <!-- html 文档的开始标记 -->
<head> <!-- 头部开始 -->
...
</head> <!-- 头部结束 -->
<body> <!-- 体部开始 -->
...
</body> <!-- 体部结束 -->
</html> <!-- html 文档结束 -->
头部是关于网页的一些说明和外部资源链接,它们不会被显示在浏览器的窗口中。
内容将显示在浏览器窗口中。
用于记录某些元数据(元数据:meta data,说明数据的数据)
如:
<meta charset="utf-8">
<meta name="description" content="Description of this page">
<meta name="author" content="Bill Taut">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
用于链接外部文件内容到本文档。如:
<link href="https://zybuluo.com/static/img/favicon.png" type="image/x-icon" rel="icon">
<link href="https://zybuluo.com/static/assets/1bc053c8.base.lib.min.css" rel="stylesheet" media="screen">
用于定义文档标题,显示于浏览器窗口标题栏或标签页标题栏。如:
<title>为什么要学PHP</title>
用于界定样式定义块。如:
<style>
body{
font-size: 14px;
}
</style>
用于界定程序块或连接外部程序文件内容到本文档。
如定义程序块:
<script>
alert("这会显示一个消息框。")
</script>
链接外部程序文件:
<script src="main.js"></script>
h: headline (标题行)
<h1>这是一级标题</h1>
p: paragraph (段落)
<p>
这是段落1。
</p>
<p>这是段落2。</p><p>这是段落3。</p>
这是段落1。
这是段落2。
这是段落3。
span (跨度)
世界上最高的山峰是<span style="color:red;">珠穆朗玛峰</span>,这我是知道的。
世界上最高的山峰是珠穆朗玛峰,这我是知道的。
ul: unordered list (无序列表)
ol: ordered list (有序列表)
li: list item (列表项)
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
<ol>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ol>
table: 表格
thead: table head, 表头
tbody: table body, 表体
tr: table row, 表格行
th: table head cell, 表头单元格
td: table data cell, 表格数据单元格
<table>
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>性别</th>
<th>出生日期</th>
<th>部门</th>
</tr>
<thead>
<tbody>
<tr>
<td>1</td>
<td>李明</td>
<td>男</td>
<td>1987-08-20</td>
<td>人事</td>
</tr>
<tr>
<td>2</td>
<td>张小敏</td>
<td>女</td>
<td>1989-12-10</td>
<td>公关</td>
</tr>
<tr>
<td>3</td>
<td>周娜</td>
<td>女</td>
<td>1990-02-10</td>
<td>公关</td>
</tr>
</tbody>
</table>
序号 | 姓名 | 性别 | 出生日期 | 部门 |
---|---|---|---|---|
1 | 李明 | 男 | 1987-08-20 | 人事 |
2 | 张小敏 | 女 | 1989-12-10 | 公关 |
3 | 周娜 | 女 | 1990-02-10 | 公关 |
form: 表单
<form action="/add-contact.php" method="post">
标签和输入域
</form>
label: 标签
<label for="">文字内容</label>
input: 输入
<input type="text" id="name" name="name" value="" />
password: 密码
<input type="passowrd" id="password" name="password" value=""/>
<select id="book" name="book">
<option value="1">Java 面向对象程序设计</option>
<option value="1">关系数据库基础</option>
<option value="1">Git 应用基础</option>
<option value="1">Linux 操作系统基础</option>
<option value="1">Laravel 框加编程</option>
</select>
<select id="book" name="book" multiple>
<option value="1">Java 面向对象程序设计</option>
<option value="1">关系数据库基础</option>
<option value="1">Git 应用基础</option>
<option value="1">Linux 操作系统基础</option>
<option value="1">Laravel 框加编程</option>
</select>
<input type="radio" name="gender" value="M" /> 男
<input type="radio" name="gender" value="F" /> 女
<input type="checkbox" name="hobbie" value="运动"> 运动
<input type="checkbox" name="hobbie" value="阅读"> 阅读
<input type="checkbox" name="hobbie" value="看电影"> 看电影
<input type="checkbox" name="hobbie" value="听音乐"> 听音乐
出生日期:<input type="date" value="" />
数量:<input type="number" name="quantity">
<input type="file" name="uplaod_file" />
div: division, 部分,区域
<div class="important">
<h2>Hello</h2>
<p>...</p>
</div>
<img src="/img/avatar.jpg" alt="avatar" />
所谓预处理,是指 PHP 语言的程序代码是嵌入到 html 网页中的,当客户端浏览器向 Web 服务器请求一个扩展名为 .php
的网页文件时,它会被 Web 服务器交给一个叫做 PHP 语言解释器
的程序进行处理时,解释器会从头开始寻找并执行网页中的程序代码。执行完毕某段程序代码后,这段代码会被从网页中移除,如果这段代码有输出内容,则会把输出内容加入到这段代码的位置。整个网页处理完毕,所有 PHP 代码都会被移除,从而成为一个纯粹的 html 文档,此时才被 web 服务器作为响应传递到客户端浏览器进行显示。
PHP 语言的起始标记是 <?php
,结束标记是 ?>
,处于这对标记中的内容就是 php 语言的程序代码。
为便于开发及测试,PHP 解释器自带了一个内置的 Web 服务器。
启动这个服务器的方法是:
php -S IP:端口 [-t 网站根目录]
如果 IP 为:
如果省略 -t 网站根目录
,由默认当前目录为网站根目录。
在 D:\mysite 目录下创建一个叫做 front.php 的文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Site</title>
</head>
<body>
<h1><?php echo "This is my site!" ?></h1>
</body>
</html>
然后启动 PHP 内置服务器,指定 D:\mysite 为网站根目录:
php -S 127.0.0.1:80 -t d:\mysite
然后通过浏览器访问:
http://127.0.0.1/front.php
查看网页内容,并查看网页源代码,然后与 front.php 文件内容做比较。
echo, 英文是回声、回响的意思,在 php 中用于显示内容。
计算机处理的信息叫“数据”。 如:一首歌、一段影片、一个数字、一个名字、一个日期……
为便于用程序处理数据,需要处理的数据会被“装”在被命名了的“容器”中,这些容器实际上是计算机内部的内存单元。这些容器就叫做“变量”(因为它们装的内容能够发生变化)。
变量是通过以下形式来定义的:
// 变量名=值(数据);
$name = "Billy";
执行以上代码后,$name 这个变量里就装着一个数据(一个字符构成的串——字符串):"Billy"。
变量名的命名规则:
常量事实上是某个数据的“绰号”,以便于容易记住它。这个绰号代表的数据一经指定,就不能再改动,这就是常量的含义。
以下定义了一个叫做 PI 的常量,用它来代表圆周率 3.14。
define('PI', 3.14);
// 或者
const PI = 3.14;
常量的命名规则:
在计算机内部,所有信息数据都是用“0”和“1”的组合来表示的,为了正确处理数据,必须让计算机知道这些用“0”和“1”的组合表示的数据是什么类型。
字符串是以0 到多个字符构成的文字信息,如姓名、手机号码(尽管由数字构成,但没有数值上的含义)、地址等。
字符串数据用单引号或双引号来括住,如:
"张强"(双引号)
'13023561098' (单引号)
"I'm happy!"(双引号,内部可直接使用单引号作为普通字符)
'"I saw you last night." she said'(单引号,内部可直接使用双引号作为普通字符)
'I\'m happy!'(单引号,内部作为普通字符的单引号必须用 \ 符号来转义)
"\"I saw you last night\", she said"(双引号,内部作为普通字符的双引号必须用 \ 符号来转义)
<?php
$greeting="Hello!";
echo is_string($greeting) . "<br>";
echo gettype($greeting) . "<br>";
var_dump($greeting);
?>
整数数值信息,直接用数字表示,如:
123(十进制整数)
-238(十进制整数)
077(八进制整数)
0x3F0E(十六进制整数)
0b11011100(二进制整数)
12_652_000( 十进制整数,用下划线表示千分符,php 7.4.0 及以上)
<?php
$age=35;
echo is_int($age) . "<br>";
echo gettype($age) . "<br>";
var_dump($age);
?>
小数数值信息,直接用数字表示,如:
1.23
2.4e3 ()
3e-8()
3_144.56( 用下划线表示千分符,php 7.4.0 及以上)
<?php
$length = 1.23;
echo is_float($length) . "<br>";
echo is_double($length) . "<br>";
echo gettype($length) . "<br>";
var_dump($length);
?>
代表两个极端的信息,“非黑即白”。只有两个标准值:
true(真)
false(假)
<?php
$logged = false;
echo is_bool($logged) . "<br>";
echo gettype($logged) . "<br>";
var_dump($logged);
?>
数组代表一组数据信息。表示方法:
[索引0=>数据0, 索引1=>数据1, 索引2=>数据2, ..., 索引n=>数据n]
或者:
[数据0, 数据1, 数据2, ..., 数据n]
, 此时的 索引0 默认为 0,索引1 默认为1,索引 n 默认为 n。相当于:
[0=>数据0, 1=>数据1, 2=>数据2, ..., n=>数据n]
如:
['name'=>"Billy", 'gender'=>"Male", 'age'=>28]
[100, 'abc', 12.34, false] 相当于:
[0=>100, 1=>'abc', 2=>12.34, 3=>false]
数组可以嵌套:
['name'=>'hat','colors'=>['blue','red','black']]
<pre>
<?php
$data=[12,45,776,233];
echo is_array($data) . "<br>";
echo gettype($data) . "<br>";
var_dump($data);
print_r($data);
?>
</pre>
数据中的数据用其索引名(也叫下标)来获取。
<?php
$colors = ['blue','red','black','white'];
echo $colors[2]; // 'black'
$school=['name'=>'YNTC','telephone'=>'0871-88769808','country'=>'China'];
echo #school['telephone']; // '0871-88769808'
$school['built_in'] = 1958;
// ['name'=>'YNTC','telephone'=>'0871-88769808','country'=>'China','built_in'=>1958]
?>
以后会讲到。
空值是一种特殊的数据类型,代表“没有数据”。
以后会讲到。
序号 | 运算符 | 数据类型 | 含义 | 举例 |
---|---|---|---|---|
1 | + | 数值类型 | 相加 | a+b |
2 | - | 数值类型 | 相减 | a-b |
3 | * | 数值类型 | 相乘 | a*b |
4 | / | 数值类型 | 相除 | a/b |
5 | % | 数值类型 | 取除法余数 | a%b |
6 | ++ | 数值类型 | 增1 | a++或++a 相当于 a = a + 1 |
7 | -- | 数值类型 | 减1 | a--或--a 相当于 a = a - 1 |
8 | +=,-=,*=,/= | 数值类型 | a+=b 相当于 a=a+b,依此类推 | |
9 | . | 字符串类型 | 相加(相连) | 'abc' . '123 结果为:'abc123' |
运算结果为真(true)和假(false)。
序号 | 运算符 | 含义 | 举例 |
---|---|---|---|
1 | > | a>b | |
2 | >= | a>=b | |
3 | < | a | |
4 | <= | a<=b | |
5 | == | 不严格相等,自动转换到同一数据类型再比较 | a==b (1=='1' 为真) |
6 | === | 严格相等,数据类型和值都必须一致 | a===b,(1==='1') 为假 |
7 | !=, !== | 不等于,分别对应不严格相等和严格相等 | a!=b, a!==b |
将比较运算组合起来,运算结果为真(true)和假(false)。
序号 | 运算符 | 含义 | 举例 | 规则(1:真,0:假,*:与,+:或) | 说明 |
---|---|---|---|---|---|
1 | &&/and | 与 | (a>b) && (c>d) 或: (a>b) and (c>d) |
1*0=0;0*1=0;0*0=0;1*1=1 | 有假必假,全真才真 |
2 | ||/or | 或 | (x==y) || (z>0) 或: (x==y) or (z>0) |
1+0=1;0+1=1;0+0=0;1+1=1 | 有真必真,全假才假 |
3 | ! | 非 | !x, !(a>=b) | !0=1; !1=0 | 取反 |
表达式是一个算式,求得一个结果。如:
, , , ...
语句是程序的最小执行单元,是构成程序的基本元素。一个语句用";(分号)"号结束,但以语句块结束的语句不需要用“;”结尾。
语句块是用一对花括号{ }括起来的零条到多条语句,语句块是封闭的,外面“看”不到它内部的数据(变量),而它可以“看”到外部的数据(变量)。
根据条件执行相应代码。
格式:
// 语句一
if(条件表达式){
...
}
// 语句二:
if(条件表达式){
...
}
else{
...
}
// 语句三:
if(条件表达式1){
...
}
else if(条件表达式2){
...
}
else if(条件表达式3){
...
}
...
else{
...
}
// 语句四(条件赋值):
变量 = 条件表达式 ? 值1(或表达式1) : 值2(或表达式2);
// 语句四相当于:
if(条件表达式){
变量 = 值1(或表达式1);
}
else{
变量 = 值2(或表达式2);
}
动手环节
input-name.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>输入姓名</title>
</head>
<body>
<form action="greeting.php" method="get">
请输入您的姓名:
<input type="text" name="name">
<input type="submit" value="提交">
</form>
</body>
</html>
greeting.php
<?php
$timing = "";
// getdate() 返回关于日期时间的数组。
$hour = getdate()['hours'];
if($hour>0 && $hour<=12){
$timing="早上";
}
else if($hour>12 && $hour<=18){
$timing="下午";
}
else{
$timing="晚上";
}
$name = "";
// $_GET, $_POST, $_REQUEST
// $_GET 数组装着 get 过来的表单数据
// $_POST 数组装着 post 过来的表单数据
// $_REQUEST 数组装着 get 和 post 过来的表单数据
// isset(变量) 函数用于判断变量是否存在
if(isset($_REQUEST['name'])){
$name = $_REQUEST['name'];
}
if($name==""){
$name = "我的朋友";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>来自服务器的问候</title>
</head>
<body>
<p>当前日期时间:<?php echo date("Y年m月d日 H时i分s秒") ?></p>
<p><?php echo $timing ?>好, <?php echo $name ?>!</p>
<a href="input-name.php">重新输入姓名</a>
</body>
</html>
根据变量取值执行相应代码。
格式:
switch(变量){
case 值1:// 如果变量的值==值1
...
[break]; // 跳出语句块
case 值2:
...
[break];
case 值3:
...
[break];
...
default:
...
}
特点:
只要与某个取值相同,其后的 case 和 default 下的所有语句都会被顺序执行,除非碰到一个 break 语句而直接跳出语句块。
动手环节
input-season.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>输入季节</title>
</head>
<body>
<form action="about-season.php" method="get">
请输入您喜欢的季节:
<input type="text" name="season">
<input type="submit" value="提交">
</form>
</body>
</html>
about-seasion.php
<?php
$season_str = isset($_REQUEST['season'])? $_REQUEST['season'] : '';
$season = mb_substr($season_str,0,1);
$message = '';
switch($season){
case '春':
$message = "我也喜欢{$season_str},因为此时是全年最舒服的季节!";
break;
case '夏':
$message = "我也喜欢{$season_str},因为此时我可以去游泳了!";
break;
case '秋':
$message = "我也喜欢{$season_str},因为此时市场上到处都是水果!";
break;
case '冬':
$message = "我不太喜欢{$season_str},因为此时我真不想出门!";
break;
default:
$message = "请告诉我您喜欢的季节!";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于季节</title>
</head>
<body>
<p>您输入了:<?php echo $season_str ?></p>
<p><?php echo $message ?></p>
</body>
</html>
训练说明
请将程序中的全部或部分 break 删除,观察程序执行效果。
根据条件进行循环操作(先检测条件)。
while(条件表达式){
...
}
举例:
php > $i=10;
php > while($i>0){echo $i."\n"; $i--;}
10
9
8
7
6
5
4
3
2
1
php >
根据条件进行循环操作(后检测条件)。
do {
...
} while(条件表达式);
举例:
php > $i=10;
php > do {echo $i . "\n"; $i--;} while($i>0);
10
9
8
7
6
5
4
3
2
1
php >
比较:
php > $i=0;
php > do {echo $i . "\n"; $i--;} while($i>0);
0 //做了一次!
php >
设置初值、条件及步进,在满足条件的情况下循环操作。
for(变量=初值; 条件; 步进){
...
}
如:
for($idx=0; $idx<100; $idx++){
...
}
//相当于:
$idx=0;
while($idx<100){
...
$idx = $idx + 1; // 或:$idx ++;
}
针对数组元素进行循环操作。
foreach(数组 as 键=>值){
// 处理键和值
}
// 或:
foreach(数组 as 值){
// 处理值
}
1、continue
continue 用于结束本次循环操作(语句块内的后续语句不再执行),回头进行下一次循环。
2、break
break 用于结束本次操作(语句块内的后续语句不再执行),跳出并结束循环。
3、return
用在函数中,结束函数的执行(函数中的后续语句不再执行),并返回指定的结果(不指定则返回NULL)。
语句:
throw new Exception("错误信息")
用 try {} catch() {} finally{}
进行异常处理(参见PHP异常处理)
举例:
function getSoldPrice($price,$rate){
if(!is_numeric($price) || !is_numeric($rate)){
throw new Exception("两个参数都必须是合法的数字!");
}
return $price * $rate;
}
try{
actualPrice = getSoldPrice(1200,'1abc');
}
catch(Exception $e){
echo $e->getMessage();
}
finally{
echo "无论如何,此句都会被执行。";
}
字符串是 PHP 中处理的重要数据类型之一。
strlen(字符串) 返回字符串的字节数。
mb_strlen(字符串,[字符编码=内部编码]) 返回特定编码的字符串的字符数。
php > echo strlen('IT');
2
php > echo mb_strlen('IT');
2
php > echo strlen('信息技术');
12
php > echo mb_strlen('信息技术');
4
strpos(字符串1,字符串2,[开始位置=0]) 返回从开始位置(没有设置则从头开始)以后,字符串2第一次出现在字符串1中的位置(从0开始的位置序号)。可用此函数判断某个字符串是否以某些字符开头。
php > echo strpos('信息技术','信息')===0? 'Yes':'No';
Yes
php >
数组是数据集合,是 PHP 中重要的数据类型之一。
1、头部添加
array_unshift(数组,元素1,元素2,...);
// 函数返回添加的元素个数
2、尾部添加
array_push(数组,元素1,元素2,...);
// 函数返回添加的元素个数
// 如果只在数组变量$array尾部添加一个元素,最有效的方法是:
$array[] = 要添加的元素;
3、内部插入
无专门函数,可用 array_slice 结合 array_merge 实现。
也可用 array_splice 函数实现,但不能保留原键名。
4、头部移除
5、尾部移除
6、内部移除
数组中的每一项数据叫做数组元素,每一个数组元素由一对数据构成:“键=>值”,其中的“键”(key)用于说明值的位置序号(整数)或名称(字符串),“值”(key)是对应的数据。如果没有指明“键”,则会自动为数据指定位置序号,从“0”开始。
数组是可以嵌套的,即数组的元素本身也可以是数组。
为了查看数组的数据内容,可用 print_r(数组变量)
函数来显示数组内容。
可用以下两种形式遍历数组元素。
// 以位置索引为键的数组
for($i=0; $i<count($arrar); $i++){
// 处理 $array[i]
}
// 或:
foreach($array as $key=>$value){
// 处理 $key 或 $value
}
// 也可以不要键
foreach($array as $value){
// 处理 $value
}
在面向对象的编程中,把一切涉及的事物都看作是“活体”,它们具有自己的属性和行为。事务的管理就是通过这些“活体”的交互来完成的。比如:一张“发票”具有“开票日期”、“开票人”、“客户名称”、“所购商品列表”等属性,同时,它也具有往自己的“所购商品列表”中增加、修改和删除商品项的行为、具有说出自己的总额的行为等。
<?php
//类定义
class Invoice {
//----- 属性 --------
public $date = null;
public $operator = "";
public $customer = "";
public $items = [];
//----- 方法 --------
//构造函数
function __construct($date,$op,$cust){
$this->date = $date;
$this->operator = $op;
$this->customer = $cust;
}
function addItem(item){
$this->items[] = item;
}
function removeItem(id){
foreach($this->items as $key=>$item){
if($item['id']==id){
array_split($this->items,$key,1);
}
}
}
function getSum(){
foreach($this->items as $key=>$item){
}
}
//生成对象 (调用__construct构造函数)
$inv = new Invoice("2020-10-10","Ben","Tina"); // $inv 为一个 Invoice 类的对象(也叫实例)
?>
可使用 public、protected、private 来修饰对象的属性和方法。
使用不同修饰符的属性和方法其可被访问的权限也不同:
<?php
class Employee {
private $id;
private $name;
public function __construct($id,$name){
$this->id = $id;
$this->name = $name;
}
public function getId(){
return $this->id;
}
public function getName(){
return $this->name;
}
}
$e = new Employee(1,'Ben');
echo $e->name; // 错误,无权直接读取私有属性
echo $e->getName() . "\n"; // 正确,方法为公有,可访问,并得到 name
?>
如果声明一个类继承(extend)自另一个类,这个类叫做被继承类的子类,而被继承的类叫父类。因为继承,子类不用声明和定义就具有了父类的属性和方法,子类也可有自己额外定义的属性和方法。
类的继承使用关键词extends,在子类中可使用parent访问父类的方法。在子类中可重写父类的方法。
PHP的继承为单继承,即一个类只能继承自一个父类。
一个类可以继承自:
普通类所有方法均有具体实现。
<?php
class Vehicle {
public $name;
public $brand;
public function __construct($name, $brand){
$this->name = $name;
$this->brand = $brand;
}
public function description(){
return "This vehicle name is: " . $this->name . " and brand is: " . $this->brand . ".";
}
}
class Car extends Vehicle {
public $seat_count;
public function __construct($name, $brand, $seat_count){
parent::__construct($name, $brand);
$this->seat_count = $seat_count;
}
}
$car = new Car('CRV','HONDA',5);
echo $car->description() . " Seat count is: " . $car->seat_count . "\n";
抽象类有的方法没有具体实现(抽象方法),如果子类要成为普通类,必须实现那些抽象方法,否则自己也必须是抽象类。
<?php
abstract class Shape {
public function desc(){
return "I'm a ". get_class($this) . ".";
}
public abstract function calcArea(); // 抽象方法没有函数体!
}
class Circle extends Shape {
public $radius=0;
public function calcArea(){
return $this->radius * $this->radius * 3.14;
}
}
$shape = new Shape(); // 错误:抽象类不能实例化,即不能产生对象
$circle = new Circle();
$circle->radius = 10;
echo $circle->desc() . " My area is: " . $circle->calcArea();
接口相当于所有方法都没有具体实现的抽象类。和类不同,接口能继承另一个和多个其它接口,一个类也可“继承”(称为实现:implements)一个或多个接口。由于声明实现了某个接口的普通类必须实现接口的所有方法,因此,接口就成为了一种“契约”。
<?php
interface CanFly {
function fly();
}
interface CanWalk {
function walk();
}
class FlyingDragon implements CanFly, CanWalk {
public function fly(){
// ...
}
public function walk(){
// ...
}
}
继承自相同父类并重写父类方法(或实现相同接口)的子类,相同的方法表现的行为不一样,称为多态。多态使得同一类别的对象的相同方法具有了不同的行为,使编程的灵活性大大增加。
<?php
interface Output {
function out($content);
}
class Screen implements Output{
public function out($content){
echo $content;
}
}
class File implements Output {
public function out($content){
// save to file
echo "Content have saved to a file.";
}
}
class Content {
public $content;
public $output
// 参数 $output 为一个具有out方法的对象
public function __construct($content,$output){
$this->content = $content;
$this->output = $output;
}
public function output(){
$this->output->out($this->content);
}
}
$content = new Content("Hello world!",new Screen());
$content->output();
$content = new Content("Hello world!",new File());
$content->output();
PHP 的PDO(数据对象) 扩展为PHP访问数据库定义了一个轻量级的一致接口。
PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。
PDO 和所有主要的驱动作为共享扩展随 PHP 一起发布,要激活它们只需简单地编辑 php.ini 文件,并添加以下扩展:
extension=php_pdo.dll
除此之外还有以下对应的各种数据库扩展:
;extension=php_pdo_firebird.dll
;extension=php_pdo_informix.dll
;extension=php_pdo_mssql.dll
;extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_oci8.dll
;extension=php_pdo_odbc.dll
;extension=php_pdo_pgsql.dll
;extension=php_pdo_sqlite.dll
PDO类的一个实例代表一个数据库连接。以下是创建一个PDO实例连接MySQL数据库的例子:
<?php
//Data Source Name (DSN)的PDO命名惯例为:PDO驱动程序的名称,后面为一个冒号,再后面是可选的驱动程序连接数据库变量信息,如主机名、端口和数据库名。
$dsn = 'mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4';
$user = 'dbuser';
$password = 'dbpass';
$options = [
PDO::ATTR_EMULATE_PREPARES => false, // 关闭仿真模式,采用真实模式的prepare
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //采用例外错误模式
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 以数组方式获取记录
PDO::ATTR_PERSISTENT => true, // 为相同的$user保持连接,避免每次重新连接
];
try {
$conn = new PDO($dsn, $user, $password, $options);
} catch (Exception $e) {
error_log($e->getMessage()); //将错误记录到日志,供技术人员参考
exit('连接数据库时发生错误!'); //输出用户能够理解的错误信息
}
将PDO实例变量设为 null(以及其它所有参照此实例的变量设为null),可关闭连接。否则,当脚本结束时会自动关闭。如果希望相同用户多次使用相同连接(脚本结束时不关闭连接),需要在建立连接时在选项中传入:PDO::ATTR_PERSISTENT => true
执行一条非select查询语句:
PDO::exec(语句)
函数返回受影响的记录数。
插入记录
$name = "Printer";
$brand = "Epson";
$row_count = $conn->exec("insert into products(name,brand) values('$name','$brand')");
$insertedId = $conn->lastInsertId(); //获取新插入的记录主键。
修改记录
$increased = 10;
$row_count = $conn->exec("update employees set salary=salary+$increased");
删除记录
$id = 109;
$row_count = $conn->exec("delete from employees where id=$id");
查询记录
格式:
PDO::query(语句)
函数返回结果集(PDOStatement对象)。
如:
$employees = $conn->query("select * from employees");
for($row = $employees->fetch()){
echo $row["id"] . "<br>";
...
}
注意:使用以上语句如果不对数据做任何处理,直接将客户端输入的数据嵌入SQL
语句中,是极不安全的,可能受到 SQL注入攻击!
SQL 注入攻击:黑客将特定字符嵌入SQL语句中,实现数据的盗取或破坏。
SQL注入攻击实例1:破坏数据
$id = "1 or 1=1"; // 此值来自客户端黑客的输入
$conn->exec("delete from employees where id=$id");
//执行的SQL: delete from employees where id=1 or 1=1; 表中记录将被全部删除!
SQL注入攻击实例2:登录系统
$username = "haha"; // 此值来自客户端黑客的输入
$password = "hehe' or '1=1"; // 此值来自客户端黑客的输入
$user = $conn->query("select * from users where username='$username' and password='$password'")->fetch();
if($user){
// 登录成功
}
//执行的SQL: select * from users where username='haha' and password='hehe' or '1=1'; 结果会以uses表中第一个用户的身份登录成功!
PDO::prepare(sql) 和 PDOStatement::execute([选项])配合使用能够提高查询效率和查询安全性(避免SQL
注入攻击)。
插入记录
//使用占位符
$stmt = $conn->prepare("insert into products(name,brand) values(?,?)");
$stmt->execute([$_POST['name'],$_POST['brand']])
// 或者,使用命名参数:
$stmt = $conn->prepare("insert into products(name,brand) values(:name,:brand)");
$stmt->execute([':name'=>$_POST['name'],':brand'=>$_POST['brand']]) //此句中的:name 和 :brand 前面的冒号可省略。最佳实践:保留。
$insertedId = $conn->lastInsertId(); //获取新插入的记录主键。
修改记录
//使用占位符
$stmt = $conn->prepare("update products set price=? where id=?");
$stmt->execute([$_POST['price'],$_POST['id']])
// 或者,使用命名参数:
$stmt = $conn->prepare("update products set price=:price where id=:id");
$stmt->execute([':price'=>$_POST['price'],':id'=>$_POST['id']]) //此句中的:price 和 :id 前面的冒号可省略。最佳实践:保留。
删除记录
//使用占位符
$stmt = $conn->prepare("delete from products where id=?");
$stmt->execute([$_POST['id'])
// 或者,使用命名参数:
$stmt = $conn->prepare("delete from products where id=:id");
$stmt->execute([':id'=>$_POST['id']]) //此句中的 :id 前面的冒号可省略。最佳实践:保留。
查询记录
//使用占位符
$stmt = $conn->prepare("select * from users where username=? and password=?");
$stmt->execute([$_POST['username'],$_POST['password'])
// 或者,使用命名参数:
$stmt = $conn->prepare(""select * from users where username=? and password=?"");
$stmt->execute([':username'=>$_POST['username'],'password'=>$_POST['password']]) //此句中的 :username 和 :password 前面的冒号可省略。最佳实践:保留。
在使用PDO处理数据时,应当对可能出现的错误进行捕获并处理。以下是一个典型的含错误处理的代码。
try {
$stmt = $pdo->prepare("INSERT INTO persons (name, age) VALUES (?, ?)");
if(!$stmt->execute(['李明', 18])) throw new Exception('Statement Failed');
$stmt = null;
$stmt = $pdo->prepare("SELECT * FROM products WHERE price > ?");
if(!$stmt->execute([500])) throw new Exception('Statement Failed');
$arr = $stmt->fetchAll();
$stmt = null;
//以下操作包含在一个事务(Transaction)中
try {
$pdo->beginTransaction(); //开始事务
$stmt1 = $pdo->prepare("INSERT INTO persons (name, province) VALUES (?, ?)");
$stmt2 = $pdo->prepare("UPDATE persons SET age = ? WHERE id = ?");
if(!$stmt1->execute(['王晓', '云南'])) throw new Exception('Stmt 1 Failed');
else if(!$stmt2->execute([27, 139])) throw new Exception('Stmt 2 Failed');
$stmt1 = null;
$stmt2 = null;
$pdo->commit(); //提交事务
} catch(Exception $e) {
$pdo->rollback(); //回滚事务
throw $e; //再次抛出
}
} catch(Exception $e) {
error_log($e);
exit('数据库操作出现错误!');
}
问题:
1、如何获取新插入记录的主键(自动增长型)?
2、如何获得DELETE/UPDATE/INSERT语句所影响的行数?SELECT呢?
单个数据的呈现,用<?php 产生输出的语句 ?>
如<?php echo 变量或表达式 ?>
的方式进行。
按条件呈现单个数据:
...
<?php if(isset($user_name)){ ?>
<div><?php echo $user_name ?></div>
<?php } else { ?>
<div><?php echo "未登录" ?>
<?php } ?>
...
集合数据的呈现,结合循环与单个数据呈现进行。
以下代码在一个表格中显示数据(来自数组)。
...
<table>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>手机</th>
<th>电邮</th>
</tr>
</thead>
<tbody>
<?php foreach($contacts as $contact){ ?>
<tr>
<td><?php echo $contact["id"] ?></td>
<td><?php echo $contact["name"] ?></td>
<td><?php echo $contact["gender"] ?></td>
<td><?php echo $contact["mobile"] ?></td>
<td><?php echo $contact["email"] ?></td>
</tr>
<?php } ?>
</tbody>
</table>
...
[编程实例] 将联系人从数据库中按查询条件读出,并显示在页面上。并利用PDO::prepare()和PDOStatement::execute()对表中数据进行维护操作(增、删、改)。
(1)数据库脚本:
drop database if exists `contact_db`;
create database `contact_db` default charset utf8;
use contact_db;
create table `contacts`(
`id` int not null auto_increment,
`name` varchar(50) not null,
`gender` char(1) not null,
`mobile` char(11) not null,
`email` varchar(100),
primary key(`id`)
unique key(`email`)
);
insert into `contacts`
values
(null,'王丹丹','女','13987965732','wdd@theserver.com'),
(null,'李权','男','18987657899','lquan@theserver.com'),
(null,'周敏敏','女','13887879412','zmmin@theserver.com'),
(null,'王强生','男','13389701265','wqsh@theserver.com'),
(null,'周吾','男','18987876783','zhouwu@theserver.com'),
(null,'李中明','男','18997851256','lizm@theserver.com'),
(null,'赵周怀','男','13089018765','zzhuai@theserver.com'),
(null,'吴涵','女','18787542389','wuhan@theserver.com'),
(null,'郑芸','女','18987998754','zhyun@theserver.com'),
(null,'吴佳妮','女','18987658899','wujn@theserver.com')
;
(2)数据查询
根据关键字查找姓名或电话中包含关键字的记录。
(3)数据维护
添加一条记录到表中;修改符合条件的记录;删除符合条件的记录。
会话是一种用于在服务器端“记住”用户的技术(因 HTTP 协议是一种无状态协议,没有记住用户的机制)。它的基本原理是:为每一个客户端连接建立一个身份编号(会话ID),以此ID为标记在服务器端记录用户的相关状态数据(如:用户身份、权限等),然后将此 ID 交给浏览器保存,每次浏览器访问服务器时自动带上此 ID 作为自己的身份标记,以便让服务器识别自己。
Cookie 是一种在浏览器中保存网站相关数据的技术。服务器可要求浏览器记住某些信息,在下次访问浏览器时将这些信息带到服务器。Session 技术就是用 Cookie 在浏览器中记住自己的 ID 的。
[编程实例] 利用 Session 记住登录用户,并利用 Cookie 在浏览器端记住用户名和密码(当用户需要时)。只有登录用户才能访问首页 index.php,否则会被导向 login.php 页面。
PHP的项目依赖包管理工具。访问官网。
数据信息:
drop database if exists `contact_db`;
create database if not exists `contact_db` default charset utf8mb4;
use `contact_db`;
create table `contacts`(
`id` int unsigned not null auto_increment,
`name` varchar(50) not null,
`gender` char(1) not null check(`gender` in ('男','女')),
`birth_date` date null,
`mobile` varchar(30) unique,
`email` varchar(100) unique,
primary key (`id`)
);
create table `users`(
`username` varchar(30) not null,
`password` char(40) not null,
`real_name` varchar(50) not null,
primary key (`username`)
);
insert into `contacts`
values
(1,'李会盟','男','1980-12-23','13009761265','lhm123@theserver.com'),
(2,'张容湘','男','1986-09-18','18787679432','zhangrx@theserver.com'),
(3,'赵睿','女','1990-01-25','13108979877','zhaomin@theserver.com'),
(4,'王艳艳','女','1998-12-12','15982548799','wyy1212@theserver.com'),
(5,'李想','女','1995-03-20','18087876752','lixiang@theserver.com'),
(6,'张洋','男','1978-06-30','18928671113','zhangy@theserver.com'),
(7,'王小小','女','1997-10-28','13031769877','wxx1028@theserver.com'),
(8,'李维','男','1990-01-11','18987872266','liwei@theserver.com'),
(9,'赵明','男','1980-04-12','18787092187','zhaoming@theserver.com'),
(10,'王小璐','女','1999-12-31','13187465177','wangxiaolu@theserver.com')
;
insert into `users`
values
('xiaolu','90e5082e0e77f4e4f25877b13df17e3d4379601e','王小璐'),
('liwei','73e0ca73bc3fb8c697c9838af683decc7d7365e9','李维')
;
drop user if exists contact_db_user@localhost;
create user if not exists contact_db_user@localhost
identified by '000000';
grant all privileges on contact_db.* to contact_db_user@localhost;
flush privileges;
登录用户能够增删改查联系人数据。
login.php
<?php
session_start();
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<title>Document</title>
<style>
form {
width: 300px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-80%);
}
h1{
font-size: 2rem;
text-align: center;
}
.error{
color: red;
}
</style>
</head>
<body>
<div class="container">
<form action="do-login.php" method="post">
<h1>请登录</h1>
<div class="form-group">
<label for="username">用户名*</label>
<input type="text" class="form-control" id="username" name="username" value="<?php echo isset($_SESSION["old"]["username"])? $_SESSION["old"]["username"]:"" ?>">
<p class="error username"><?php echo isset($_SESSION["username_error"])?$_SESSION["username_error"]:"" ?></p>
</div>
<div class="form-group">
<label for="password">密码*</label>
<input type="password" class="form-control" id="password" name="password">
<p class="error password"><?php echo isset($_SESSION["password_error"])?$_SESSION["password_error"]:""?></p>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="remeber-me">
<label class="form-check-label" for="remeber-me" style="user-select: none">记住我</label>
</div>
<p class="error"><?php echo isset($_SESSION["login_error"])? $_SESSION["login_error"]:"" ?></p>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
</html>
do-login.php
<?php
session_start();
require_once "db.php";
$_SESSION["username_error"] = "";
$_SESSION["password_error"] = "";
$_SESSION["login_error"] = "";
$username = isset($_POST["username"])? trim($_POST['username']):"";
$password = isset($_POST["password"])? trim($_POST["password"]):"";
$_SESSION["old"]["username"] = $username;
$_SESSION["old"]["passowrd"] = $password;
if($username==="" or $password===""){
$_SESSION["username_error"] = $username===""? "用户名不能为空!":"";
$_SESSION["password_error"] = $password===""? "密码不能为空!":"";
header("Location: login.php");
exit();
}
$user = null;
try{
$stmt = $db->prepare("select * from users where username=?");
$stmt->execute([$username]);
$user = $stmt->fetchObject();
if($user){
if(!password_verify($password,$user->password)){
$user = null;
}
else{
$_SESSION['login_user'] = $user;
header("Location: index.php");
exit();
}
}
}catch (Exception $e){
echo $e->getMessage();
}
$_SESSION["login_error"] = "用户名或密码错误!";
header("Location: login.php");
logout.php
<?php
session_start();
session_destroy();
header("Location: login.php");
db.php
<?php
$db=null;
try {
$db = new PDO("mysql:host=127.0.0.1;dbname=contact_db;charset=utf8mb4", 'contact_db_user', '000000');
}
catch(Exception $e){
echo $e->getMessage(); // 调试使用
echo "连接数据库失败,请联系有关人员或稍后再试。";
exit();
}
index.php
<?php
session_start(); //打开 Session!
if(!isset($_SESSION['login_user'])){
header("Location: login.php");
exit();
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<title>联系人列表</title>
</head>
<body>
<div class="container">
<a href="logout.php">退出登录</a>
<h1>联系人列表</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
</html>