PHP:使用curl请求聚合支付的驾考宝典题库接口数据, 并渲染到页面上;composer 常用的指令;mvc pathinfo 路由映射实现
一. API 基础知识
- url 的相关知识
1.1 一切皆”请求”
常见的请求场景如下:
搜索引擎查询信息
商城通过物流接口获取快递状态
微信通过支付回调 URL 通知用户支付情况
调用运营商接口发送短信
调用天气信息展示在 APP 或网站上
调用百度 REST API 接口实现文字转语音,实现支付语音提示
调用百度图片审核接口过滤非法色情图片
调用地图接口展示附近指定商户
…
1.2 url 组成
任何请求都必须通过 url 发起
一个典型的 URL 格式如下:
http://hostname:80/path/demo.php/a/b/c/id/5/uname/peter?k1=v1&k2=v2#anchor
-https 端口443 ftp 端口21
http: 基于 http 协议的 url, 类似还有 https
hostname: 接收请求的服务器 ip 地址或主机名(域名)
80: 端口号,如果省略则默认为 80
path: 脚本在服务器上的路径
index.php: 真正访问的脚本文件,实际上浏览器只关注到这个位置,后面的内容由服务器解释
a/b/c: PATH_INFO,一种附加参数的添加方式
?后面的内容: k1=v1&k2=v2, 查询字符串,也是 url 附加参数,get 请求
anchor: 锚点,当前页面内跳转,可视为页面内部路由
parse_url()函数
demo1.php
- Chrome 浏览器
全球开发者使用最多的是 Chrome 浏览器,所以我们也会使用它
Chrome 开发者工具面板
Elements: html,css…
console: 内置的 JavaScript 调试器,可编写简单的 js 代码
Sources: 网站中有哪些文件,图片等,可进行代码单步调试
Network: 查看浏览器发送了哪些请求,以及响应状态
Application: 查看 cookie,本地会话数据等
目前先了解以上几个面板功能即可
- url 编码
百度搜索:php 中文网, 在 chrome 开发者工具 Network 中发现 php 后面的中文网变成了%E4%B8%…
因为 url 仅允许数字/字母,以及部分特殊字符!*’();:@&=+$,/?#[],所以中文需要进行编码
除了-_.之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号+
此编码与表单 POST 数据的编码方式一样
此编码与 enctype application/x-www-form-urlencoded 的媒体类型编码方式一样
urlencode() 和 urldecode()
demo2.php php 站点运行原理与访问流程
用户通过浏览器访问站点,如php.cn
浏览器将用户的 http 请求发送到php.cn站点所在的 web 服务器
web 服务器监听 80 端口接受请求后,发现是 php 脚本自己不能直接处理
web 服务器如已安装 php 扩展模块,则将请求转发给 php 模块(解释器)处理
php 根据请求,从数据库/缓存/文件中读取数据,并生成对应的 html 页面
php 模块将生成的页面转发给 web 服务器
web 服务器将 php 生成的 html 页面做为 http 请求的响应返回到客户端
客户端浏览器将收到的 html,css,js 等进行解析渲染最终生成完整页面
图示:服务器端的各个角色
5.1 web 服务器
主流: Apache / Nginx
如果是请求的是静态资源,如 html,图片等, 则直接响应即可
如果是动态请求(html 需要动态生成),则需要将请求转发给相应的扩展去处理
5.2 php 解释器
做为 web 服务器的扩展模块, 负责解析处理 php 脚本
生成 html 页面, 或者指定格式的字符串,如json
5.3 数据库
动态网站的重要特征就是数据的动态生成
数据库是网站数据的最佳保存方式
MySQL 数据库免费强大,是 PHP 的最佳拍档
5.4 缓存
将用户经常请求的内容, 或者更新不频繁的数据缓存起来, 可加速请求响应速度
当用户请求的数据已在缓存中时, 则不再进行数据库读写,而是直接从缓冲读取
常用缓存方式: Redis, Memcached
5.5 文件
并非所有数据都适合数据库或缓冲,例如数据库配置信息,使用文件保存更合适
使用文件方式,直接用 php 脚本读取,省去数据库访问的开销,效率更高更灵活
几乎所有框架或项目都会有一个config目录,就是这个原因- 场景介绍
本地环境: 本机安装全部开发组件,并通过Git提交供测试
测试环境: 通常是内网服务器,以节约成本
发布环境: 在创建与生产环境一致的环境, 导入真实数据线上测试
生产环境: 目标用户的环境,不允许再次测试,防止破坏用户数据 - 本地环境
学习阶段, 学会搭建本地开发环境即可
集成环境: phpStudy_pro
编辑器: vscode
[选学]版本控制系统(VCS): Gitee(码云)
7.1 phpStudy
下载: https://www.xp.cn/download.html
配置本地域名: php.edu
本地项目目录: WWW/php/
php 版本: 7.3+
web 服务器: Apache
Gitee
创建帐号: https://gitee.com/
安装 Git: https://git-scm.com/
Git 手册: https://git-scm.com/book/zh/v2
script
初始化Git,创建本地版本库
cd php
git init
设置用户名和邮箱
git config --global user.name peter
git config --global user.email peter@php.cn
配置远程版本库
git remote add origin https://gitee.com/bnkt/php_edu.git
查看远程库
git remote -v
添加到暂存区
git add .
提交到本地版本库
git commit -m 'first commit'
查看本地状态
git status
提交到远程版本库
git push origin master
从远程仓库拉到项目到本地
git pull https://gitee.com/bnkt/php_edu.git
从远程仓库克隆项目到本地
git clone https://gitee.com/bnkt/php_edu.git
如果添加.gitignore不生效,要先清空本地缓存后再提交
git rm -r --cached
<?php
namespace mvc;
class UserController
{
public function index()
{
return '<p>这是首页</p>';
}
}
// var_dump($_SERVER['PATH_INFO']);
//分割成数组,然后过滤空值,最后重置键
$pathinfo = array_values(array_filter(explode('/',$_SERVER['PATH_INFO'])));
//拼接成实例化的类
$controller = __NAMESPACE__."\\".ucfirst(array_shift($pathinfo)).'Controller';
$action = array_shift($pathinfo);
echo call_user_func([(new $controller),$action]);
<?php
namespace mvc;
class UserController
{
public function index($id,$name)
{
return "你好 $name , 你的id是 $id";
return '<p>这是首页</p>';
}
}
// var_dump($_SERVER['PATH_INFO']);
//分割成数组,然后过滤空值,最后重置键
$pathinfo = array_values(array_filter(explode('/',$_SERVER['PATH_INFO'])));
//拼接成实例化的类
$controller = __NAMESPACE__."\\".ucfirst(array_shift($pathinfo)).'Controller';
$action = array_shift($pathinfo);
//http://php.edu/0518/api/demo1.php/user/index/id/3/name/peter 从 url中提取参数 参数也是通过path_info方式提供
//创建一个空数组,把过滤的值放到空数组中
$params = [];
for ($i=0; $i <count($pathinfo) ; $i+=2) {
if (isset($pathinfo[$i+1]) && !empty($pathinfo[$i+1])) {
$params[$pathinfo[$i]] = $pathinfo[$i+1];
}
}
echo call_user_func_array([(new $controller),$action],$params);
天气预报代码块
<?php
//请求天气预报接口
//curl 初始化 获取一个curl句柄 发起http请求
$url = 'http://apis.juhe.cn/simpleWeather/query?';
$key = 'eb8f148b35bb2f22c3fd59804f58084c';
$city = '上海';
//http_build_query — 生成 URL-encode 之后的请求字符串
$query = http_build_query(['key'=>'eb8f148b35bb2f22c3fd59804f58084c','city'=>'上海']);
//curl_init — 初始化 cURL 会话
$ch = curl_init();
//curl_setopt — 设置 cURL 传输选项
//CURLOPT_URL 需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。
curl_setopt($ch,CURLOPT_URL,$url.$query);
//CURLOPT_HTTPGET true 时会设置 HTTP 的 method 为 GET,由于默认是 GET,所以只有 method 被修改时才需要这个选项。
curl_setopt($ch,CURLOPT_HTTPGET,true);
//CURLOPT_HEADER 启用时会将头文件的信息作为数据流输出。
curl_setopt($ch,CURLOPT_HEADER,false);
//CURLOPT_RETURNTRANSFER true 将curl_exec()获取的信息以字符串返回,而不是直接输出。
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
// 抓取URL并把它传递给浏览器
$apiData = curl_exec($ch);
//关闭cURL资源,并且释放系统资源
curl_close($ch);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
table {
color: #555;
background-color: #efefef;
border-collapse: collapse;
width: 600px;
text-align: center;
margin: auto;
}
td {
border: 2px solid #FFF;
padding: 5px;
}
table caption {
font-size: 1.2rem;
margin-bottom: 15px;
}
table thead tr:first-of-type {
background-color: darkturquoise;
color: white;
}
</style>
<body>
<script>
const obj = <?=$apiData?>;
// console.log(obj);
// 创建表格元素
const table = document.createElement('table');
// 表头: 城市 + 标题, 如:上海天气预报
table.createCaption().textContent = obj.result.city + '天气预报';
// 创建表头,并尾部添加新行,将表头参数填入
const tr = table.createTHead().insertRow(-1);
tr.insertCell(0).innerText = '日期';
tr.insertCell(1).innerText = '气温';
tr.insertCell(2).innerText = '雨雪';
tr.insertCell(3).innerText = '风向';
// 遍历未来几天的天气对象数组
obj.result.future.forEach(item => {
// 生成一个新行,并插入到表尾
const row = table.insertRow(-1);
// 转日期格式
let date = new Date(Date.parse(item.date.replace(/-/g, '/')));
// 组装成符合国人阅读习惯的格式
let timeStr = `${date.getFullYear()}年${date.getMonth()+1}月${date.getDate()}日`;
// 遍历每一天的天气对象数组,并填充到生成的单元格中
row.insertCell(0).innerText = timeStr;
row.insertCell(1).innerText = item.temperature;
row.insertCell(2).innerText = item.weather;
row.insertCell(3).innerText = item.direct;
// 将生成的该行插入到表格元素中
table.appendChild(row);
});
// 最后将表格添加到页面中
document.body.appendChild(table);
</script>
</body>
</html>