PHP-6: 文件加载知多少?
- 文件加载: 可简单的理解为将外部文件内容复制到当前文档中
- 根据被加载文件的特征和重要性,可分为: "条件加载" 和 "强制加载" 二种方式
1. 条件加载
加载外部文件,如果失败报警告级(Warning)错误,不会中断程序
| 序号 | 语法 | 描述 |
|---|---|---|
| 1 | include |
条件加载 |
| 2 | include_once |
去重(chong)条件加载 |
1.1 include
模板样式文件:style.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
color: #ddd;
}
a:hover {
color: white;
background-color: coral;
}
nav {
background-color: #333;
height: 40px;
display: flex;
}
nav > a {
min-width: 100px;
text-align: center;
line-height: 40px;
}
nav > a:last-of-type {
margin-left: auto;
}
自定义导航模板文件:cumstom.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义导航模板</title>
<style>
@import "style.css";
</style>
</head>
<body>
<nav>
<a href="">首页</a>
<a href="">视频</a>
<a href="">软件</a>
<a href="">下载</a>
<a href="">社区</a>
<a href="">登录</a>
</nav>
</body>
</html>
默认导航模板文件:default.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>默认导航模板文件</title>
<style>
@import "style.css";
</style>
</head>
<body>
<nav>
<a href="">首页</a>
<a href="">默认模板栏目</a>
<a href="">访问官网</a>
</nav>
</body>
</html>
客户端访问脚本demo1.php
// 响应标头: 服务器响应请求返回html之前设置客户端默认编码集
// header(): 函数向客户端发送原始的 HTTP 报头/标头
header('content-type:text/html;charset=utf-8');
// 常规用法, 它的参数是字符串表示的文件名, 单引号, 双引号都可以
include 'custom.php';
// include 是语言结构,但支持以函数方式传参,与echo 类似
include('custom.php');
// include 支持动态加载, 文件名可来自一个变量
$file = 'custom.php';
// 有的编辑器可能会有一个警告,提醒动态文件有可能会失败
include $file;
// 如果不想看到这样的警告, 可以将这个存有文件名的变量放在"双引号"中
include "$file";
// 注意, 文件名参数不支持表达式
$file = 'custom';
// 字符串拼装操作符无效, 文件引入失败
include "$file" ."php";
// 正因为include加载文件失败不会中止程序运行,所以可以实现条件加载,即按需加载
// 以下代码实现: 如果加载用户自定义模板失败, 就加载默认模板文件
if (!include 'hello.php') {
include 'default.php';
}
// 如果不想看到那个Warning警告,可以使用@屏蔽警告, 或者修改php.ini,或设置警告级别
if (@!include 'hello.php') {
include 'default.php';
}
// 加载外部文件前,应该先检查文件是否存在,以及文件类型是否正确
$tpl = 'custom1.php';
if (file_exists($tpl) && is_file($tpl)) {
include "{$tpl}";
} else {
include "default.php";
}
echo '如果看到我, 说明程序没有因文件加载失败而终止';
1.2 include_once
创建一个默认函数库文件: common.php
function fetch(): string
{
return sprintf('函数名: %s( )', __FUNCTION__);
}
客户端调用脚本: demo2.php
// include_once(): 仅允许加载一次
// php中全局成员包括: 函数,常量, 类, 接口
// php不支持函数重载: 函数名相同, 根据签名不同执行不同的行为(签名即参数)
// 所以, 在一个程序中不允许重复声明同名函数,否则引起命名冲突,产生致命错误(Fatal error)
// common.php文件重复引入, 等价于重复声明了fetch()函数
// include 'common.php';
// include 'common.php';
// 所以,当文件中存在全局成员时,加载到当前脚本时应该先进行"去重检测"
// php提供了条件去重加载语法: include_once
// include_once 在加载文件前先检查该是否已被加载,可确保文件只会被加载一次
include_once 'common.php';
include_once 'common.php';
// 当外部文件,需要多次加载到当前脚本中时, 必须使用include 才有效
echo fetch();
2. 强制加载
加载外部文件,如果失败报致命级(Fatal error)错误,并中断程序
| 序号 | 语法 | 描述 |
|---|---|---|
| 1 | require |
强制加载 |
| 2 | require_once |
去重强制加载 |
// 1. require: 强制加载
// 强制加载经常用来加载非常重要的外部文件,例如配置文件,类库文件等,没了他们,代码就失去了的价值
// 加载数据库连接文件,如果失败,必须终止程序,`connect.php`可以用空文件代替, 后面再填充具体内容
require 'connect.php';
// 如果加载失败, 触发致命错误,执行中断,以下代码不会继续执行,与include完全不同
// 也支持动态加载
$fileName = 'connect.php';
require "$fileName";
// 2. require_once: 强制去重加载, 功能与include_once类似
require 'common.php';
// 再次重复加载时会报错,因为重复声明了fetch()函数
// require 'common.php';
// 换成require_once 就会防止重复加载,不再报错
require_once 'common.php';
echo fetch();
// 3. require, require_once: 都不能充当判断条件
// if (!require_once "$fileName") {
// echo '失败';
// }
// 但require可以用在分支体中, 不会报错
if (file_exists($fileName)) require_once 'common.php';
3. 文件与作用域
-
只要在同一个作用域内, 无论变量定义在文件内部还是外部,都可以正确访问
-
为了程序的健壮性,非当前脚本定义的变量, 推荐设置一个默认值
-
外部文件:
file1.php
// 当前脚本并没有定义变量$sitename, 显然这个变量应该来自外部
// 为了程序健壮性, 为该变量设置一个默认值: 默认站点
// 注意, 这里千万不要用字符串连接符: ".", 想一下为什么?
// "."二边必须是一个确切的字符串表达式
echo '站点名称: ' , $sitename ?? '默认站点';
$email = 'peter@php.cn';
- 外部文件:
file2.php
echo '站点名称: ', $sitename ?? '默认站点';
$goods = '笔记本电脑';
- 客户端:
// 文件加载与作用域
// 只要在同一作用域,文件外部变量可以在被加载的文件中使用
$sitename = 'php中文网';
include 'file1.php';
echo '<hr>';
// 同样原理, 'file1.php'中定义的变量,也可以用在当前作用域中
echo $email;
echo '<hr>';
// 在函数作用域,也是一样的
function test1()
{
include 'file1.php';
echo '<br>';
echo $email ?? 'demo@email.com';
}
// 调用函数查看
test1();
echo '<hr>';
function test2()
{
// 在file2.php中无法访问全局作用域中定义的变量$sitename,所有输出默认值
include 'file2.php';
}
test2();
echo '<br>';
// 因为不在同一个作用域,全局作用域中也无法访问到file.php中定义的变量$goods
echo $goods ?? '默认商品';
- 执行结果
站点名称: php中文网
peter@php.cn
站点名称: 默认站点
peter@php.cn
站点名称: 默认站点
默认商品
4. 案例演示
- 以一个简单的数据库查询为例,演示常用的文件加载方式
- 以流行的 PDO 方式操作数据库, 重点在于理解文件加载,具体操作细节请忽略
- 案例涉及到的文件
| 序号 | 文件名 | 描述 |
|---|---|---|
| 1 | config.php |
数据库配置参数 |
| 2 | connect.php |
数据连接操作 |
| 3 | demo5.php |
客户端数据库操作 |
4.1 数据库配置参数
// 数据库配置参数
return [
'type' => 'mysql',
'host' => 'localhost',
'dbname' => 'phpedu',
'charset' => 'utf8',
'port' => '3306',
'username' => 'root',
'password' => 'root',
];
4.2 数据库连接操作
// 数据库连接
// 这里没必须使用require_once,因为重复加载只会重复给$config赋同样的值而已
$config = require 'config.php';
try {
// 配置数据源: DSN
$dsn = sprintf('%s:host=%s;dbname=%s', $config['type'], $config['host'], $config['dbname']);
// 连接数据库
$pdo = new PDO($dsn, $config['username'], $config['password']);
// 默认数据表字段获取模式: 关联数组
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// 打印出错信息并中止脚本
die($e->getMessage());
}
4.3 客户端测试
// 小案例: 数据库连接查询
// 数据库操作暂未学到, 这里仅做演示,理解文件加载的原理与作用即可
// 这里require,require_once都可以
// 为什么用require,是因为我希望重复加载时得到错误报告
// 为什么不用incluce / incode_once,是因为数据库连接是否成功,对后面的操作极其重要
// 连接数据库
require 'connect.php';
// 查询测试
$sql = 'SELECT `id`,`name`,`email` FROM `users` LIMIT 3';
$res = $pdo->query($sql)->fetchAll(PDO::FETCH_NUM);
// 解构遍历查询结果
foreach ($res as list($id, $name, $email)) {
printf('%s : %s ( %s ) <br>', $id, $name, $email);
}