├── 01-essential.md ├── 02-php.md ├── 03-http.md ├── 04-form.md ├── 05-session.md ├── 06-mysql.md ├── 07-ajax.md ├── README.md ├── diagram.xml └── media ├── 1505988989079.png ├── 1505995465005.png ├── 1505995651671.png ├── 1505996335305.png ├── 1505997349857.png ├── 1505998898900.png ├── 1505999461533.png ├── 1506001971125.png ├── 1506004529597.png ├── 1506132097583.png ├── 1506132675132.png ├── 1506136421939.png ├── 1506312179608.png ├── apache-php.png ├── apache-process.png ├── browser-server.png ├── cookie.png ├── excel-files.png ├── excel-tables.png ├── google-suggest.png ├── http-protocol.png ├── http-req-res.png ├── multiple-network.png ├── netstat.png ├── request-structure.png ├── request.png ├── response.png ├── session-flow.png ├── session-structure.png ├── single-network.png └── web-server.png /01-essential.md: -------------------------------------------------------------------------------- 1 | # 服务端开发基础 2 | 3 | ## 建立你的第一个网站(目标) 4 | 5 | > **前端开发** 最终还是属于 **Web 开发** 中的一个分支,想要成为一名合格的前端开发人员,就必须要 **充分理解 Web 的概念**。 6 | 7 | 构建一个专业的网站是一项巨大的工作!对于新手我们应该从小事做起,也就是说咱们不可能立马就要求自己能够开发出跟淘宝一样的电商平台,但是对咱们来说建立一个属于自己的 Blog 网站并不难(其实再大的系统也是由一些基础功能叠加出来的),所以咱们就从这个话题开始聊。 8 | 9 | ## 如何建立一个 Blog 网站 10 | 11 | > 提问:到底什么是网站? 12 | > 13 | > - 可以在浏览器上通过一个地址直接访问使用 14 | > - 用于提供一种(或多种)特定服务的一系列具备相关性的网页组合的整体 15 | > - 例如:博客、门户、电商、在线教育等 16 | 17 | 有了明确的目标过后,我们需要规划具体的业务方案,学习特定的技能,完成各项功能,解决各种过程中出现的问题。 18 | 19 | ```flow 20 | s=>start: 开始 21 | o1=>operation: 明确业务 22 | o2=>operation: 根据分析需求 23 | o3=>operation: 设计功能 24 | o4=>operation: 具体实现功能 25 | o5=>operation: 部署上线 26 | e=>end: 结束 27 | s->o1->o2->o3->o4->o5->e 28 | ``` 29 | 30 | ### 之前学习了什么? 31 | 32 | 在之前的学习过程中,我们很专注,没有关心这些东西在整体中是什么角色,起到什么作用。这里我们是时候总结一下我们之前学过了的内容: 33 | 34 | - 网页开发技术(硬性) 35 | - HTML —— 网页内容结构(GUI) 36 | - CSS —— 网页外观样式(GUI) 37 | - JavaScript —— 编程语言(**可以用于**调用浏览器提供的 API) 38 | - Web APIs —— 网页交互(界面功能) 39 | - jQuery —— 便捷手段(糖果而已,不是必要的) 40 | - 编程能力 / 编程思想 / 解决问题的思路(软性) 41 | - 我要做什么(我要得到什么),我目前有什么(我能拿到什么) 42 | 43 | 至此,我们已经可以独立完成网页开发了,具体能完成的东西就是一个一个的网页,而且还能给这个页面加上一些动态的交互。但是这距离成为一个网站还有一些路要走。 44 | 45 | ```flow 46 | webpage=>operation: 网页开发 47 | website=>operation: 网站开发 48 | application=>operation: 应用开发 49 | 50 | webpage(right)->website(right)->application 51 | ``` 52 | 53 | ### 还需要学习什么? 54 | 55 | 想要完成完整的 Web 网站,还需要学习什么? 56 | 57 | - 搭建 WEB 服务器(提供网站服务的机器) 58 | - HTTP(浏览器与服务端的通讯协议) 59 | - 服务端开发(动态网页技术) 60 | - 数据库操作(服务端存储数据方式) 61 | - AJAX(浏览器与服务端的数据交互方式) 62 | 63 | ## 搭建 Web 服务器 64 | 65 | > - 服务器(提供服务)指的就是一台**安装特定的软件的公共计算机**,用于专门用于提供特定的服务。 66 | > - 按照服务类型的不同,又划分为:Web 服务器、数据库服务器、文件服务器等等。 67 | > - 客户端(使用服务)指的是在一次服务过程中使用这个服务的设备(网络端点)。 68 | > - 目前咱们最常见的客户端就是浏览器 69 | 70 | 我们手头上的这些网页,如果想要成为一个网站,首先要完成的第一件事就是有一台公共的 Web 服务器,把这一系列的页面放到这台 Web 服务器上,让用户可以通过服务器的地址访问到这些网页。 71 | 72 | ![Web Server](media/web-server.png) 73 | 74 | > 思考:为什么不把这些网页放在我们自己电脑上呢? 75 | 76 | 那么,哪里有这样的服务器呢? 77 | 78 | 我们手头上的电脑都可以是一台服务器,因为服务器是一个相对的概念,只要能提供服务就可以是一个服务器(提供服务的时候就是服务端,使用服务的时候就是客户端)。 79 | 80 | 既然服务器就是安装特定的软件的计算机,那么要让自己的成为 Web 服务器就是要安装一个 Web 服务器软件。 81 | 82 | ### Web 服务器软件 83 | 84 | - Nginx ········································ 反向代理 85 | - Apache ····································· PHP 86 | - IIS ·············································· ASP.NET 87 | - Tomcat ····································· Java 88 | 89 | ### 安装 Web 服务器软件 90 | 91 | 这里我们选择一个比较常用的 Web 服务器软件:Apache HTTP Server。 92 | 93 | > 如果使用的是安装版,与其他软件相同,安装无外乎就是一路点下一步,只是需要注意安装目录路径中不要有中文。 94 | 95 | 由于最新的 Apache 已经不提供 Windows 的安装版本了,所以我们这里使用的是解压版。 96 | 97 | > - 下载地址:https://www.apachelounge.com/download/ 98 | > - 使用说明:https://httpd.apache.org/docs/current/platform/windows.html 99 | 100 | 安装方式如下,先解压到纯英文路径的文件夹,然后执行以下命令: 101 | 102 | ```shell 103 | # 注意:需要使用管理员身份运行命令行!!! 104 | # 切换到 Apache 解压路径中的 bin 目录 105 | $ cd <解压目录>/bin 106 | # 安装 Apache 服务,-n 参数是指定服务名称 107 | $ httpd.exe -k install -n "Apache" 108 | # 如果需要卸载 Apache,可以执行以下命令 109 | $ httpd.exe -k uninstall -n "Apache" 110 | ``` 111 | 112 | 执行安装命令过后会报一个错,原因是默认的配置文件有问题,需要先调整一下配置文件 `conf/httpd.conf`,才能正常启动服务。 113 | 114 | ![1505988989079](media/1505988989079.png) 115 | 116 | 找到 Apache 解压目录中的 conf 目录下的 httpd.conf 文件,定位到 37 行,将 `c:/Apache24` 改为解压目录,我这里解压到路径是 `C:/Develop/apache`,所以我这里修改 117 | 118 | ![1505995465005](media/1505995465005.png) 119 | 120 | 修改完以后,执行以下命令重新测试配置文件是否通过。 121 | 122 | ```shell 123 | $ httpd.exe -t 124 | ``` 125 | 126 | 这里任然报错: 127 | 128 | ![1505995651671](media/1505995651671.png) 129 | 130 | 通过错误信息得知,这里是因为另外一个地方配置的目录不存在导致的,所以接着调整 246 行的 `DocumentRoot` 选项: 131 | 132 | ![1505996335305](media/1505996335305.png) 133 | 134 | 随即,我们发现这个配置文件中有很多默认配置选项中的路径都是 `c:/Apache24`,所以我们批量都修改为我们解压的目录路径。 135 | 136 | 然后重新执行 `httpd.exe -t` 测试配置文件,这时候应该提示 `Syntax OK`。 137 | 138 | > 如果有关于 `ServerName` 的警告提示,不用管它,暂时还不会影响我们接下来的使用和操作。 139 | 140 | 接着运行以下命令重新启动 Apache 服务: 141 | 142 | ```shell 143 | # 注意:需要使用管理员身份运行命令行!!! 144 | $ httpd.exe -k start -n "Apache" 145 | # 重新启动 Apache 服务 146 | $ httpd.exe -k restart -n "Apache" 147 | # 停止 Apache 服务 148 | $ httpd.exe -k stop -n "Apache" 149 | ``` 150 | 151 | 回到浏览器中,地址栏输入:http://localhost/,回车访问,这时正常应该看到 `It works!` 152 | 153 | ![1505997349857](media/1505997349857.png) 154 | 155 | > **注意**:新版本 Apache 的配置文件已经简化,可以直接通过修改 `SRVROOT` 常量为 Apache 所在目录即可 156 | 157 | ### 提供 Web 服务 158 | 159 | 启动 Apache,让别人可以使用你机器上安装的 Apache 提供的 Web 服务,访问你机器上的网站。**这种情况下你的机器就是服务器,别人的机器就是客户端**。 160 | 161 | **注意**: 162 | 163 | - 确保配置文件语法检查通过 164 | - 确保 `80` 端口没有被其他程序占用 165 | - 确保防火墙允许 `80` 端口的请求,或者干脆关掉防火墙 166 | - 如果出现 **Forbidden** 情况,确保配置文件 `httpd.conf` 中 247 行(`DocumentRoot` 之后)的 `Directory` 配置的与 `DocumentRoot` 路径相同 167 | - 我们在开发阶段大多数都是自己访问自己机器上的网站,那这种情况下,我们既是服务端又是客户端。**对于新手来说,最常见的问题就是分不清楚哪是客户端应该有的,哪是服务端应该有的。**这种时候一定要保持清醒,客户端局限在浏览器窗口,代码以及 Apache 相关的文件和配置都是放在服务端的。 168 | 169 | ## 网络基础概念(必要) 170 | 171 | ### IP 地址 172 | 173 | > Internet Protocol Address 174 | 175 | 设备在某一个网络中的地址,目前最常见的格式:`[0-255].[0-255].[0-255].[0-255]` 即为四个 0-255 的数字组成。 176 | 177 | 作用就是标识一个网络设备(计算机、手机、电视)在**某一个具体的网络**当中的地址。 178 | 179 | 127.0.0.1 是本地回环地址 180 | 181 | #### 单个网络情况 182 | 183 | 在单个局域网下,结构非常简单,就是我们所连接的网络设备(网关)给我们分配了一个地址,在**这个范围之内**我们都可以通过这个地址找到我们的这个设备。 184 | 185 | > 如果设备没有连接任何网络情况下,我们会有一个本地回环地址 127.x.x.x 186 | 187 | ![单个局域网情况](media/single-network.png) 188 | 189 | #### 多个网络情况 190 | 191 | 但是当一个设备同时处于多个网络下(比如同时连接了有线网卡和无线网卡),就会变得稍微复杂一点: 192 | 193 | ![multiple-network](media/multiple-network.png) 194 | 195 | > 例如: 196 | > 197 | > 小明这个同学同时报名了两个课程,在 A 班级小明是班长,所有 A 班级的同学都管他叫班长(叫班长就能找到他)。而在 B 班级小明是课代表,所有 B 班的同学都管他叫课代表(叫课代表就能找到他)。 198 | > 199 | > 同样的一个人在不同的环境有不同的身份,这些身份只有特定的环境才生效。 200 | 201 | > 纸上得来终觉浅,绝知此事要躬行!多尝试,多思考才能更好的理解这个问题。 202 | 203 | ### 域名 204 | 205 | 由于 IP 地址都是没有规律的一些数字组成的,很难被人记住,不利于广泛传播,所以就有人想出来要给 IP 起名字(别名)。 206 | 207 | > 域名是需要花钱注册的 208 | 209 | ### DNS 210 | 211 | 通过宽带运营商提供的服务器解析一个域名背后对应的 IP,这个过程叫做 **DNS 寻址**,帮你完成 DNS 寻址过程的服务器叫做 **DNS 服务器**。 212 | 213 | #### hosts 文件 214 | 215 | 操作系统在发起对 DNS 服务器的查询请求之前,会优先检查本机的 hosts 文件。如果这个文件中包含了对当前需要解析的域名的配置,则不再发起对 DNS 服务器的请求,直接使用 hosts 文件中的配置。 216 | 217 | **文件所在路径:** 218 | 219 | - Windows:`C:\Windows\System32\drivers\etc\hosts` 220 | - macOS:`/etc/hosts` 221 | 222 | > **注意:** 223 | > 224 | > - 本机的 hosts 文件配置只能到影响本机的 DNS 寻址 225 | > - 只有以管理员权限运行的编辑器才有权利修改 `hosts` 文件 226 | 227 | ### 端口 228 | 229 | 计算机本身是一个封闭的环境,就像是一个大楼,如果需要有数据通信往来,必须有门,这个门在术语中就叫端口,每一个端口都有一个编号,每台计算机只有 65536 个端口(0-65535)。 230 | 231 | > 一般我们把“占门”的过程叫做监听 232 | 233 | 可以通过在命令行中运行: `netstat -an` 命令监视本机端口使用情况: 234 | 235 | ![netstat](media/netstat.png) 236 | 237 | > 参考链接: 238 | > 239 | > - https://baike.baidu.com/item/%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E5%8F%A3 240 | > - https://baike.baidu.com/item/%E7%AB%AF%E5%8F%A3 241 | > - `http` 默认的端口 `80` 242 | > - `https` 默认的端口是 `443` 243 | 244 | ### URL 245 | 246 | URL(Uniform Resource Locator),统一资源定位符,通俗点来说就是表示网络当中某一个网页的完整访问地址,它具有一定的格式: 247 | 248 | ``` 249 | ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ 250 | │ href │ 251 | ├──────────┬──┬─────────────────────┬─────────────────────┬───────────────────────────┬───────┤ 252 | │ protocol │ │ auth │ host │ path │ hash │ 253 | │ │ │ ├──────────────┬──────┼──────────┬────────────────┤ │ 254 | │ │ │ │ hostname │ port │ pathname │ search │ │ 255 | │ │ │ │ │ │ ├─┬──────────────┤ │ 256 | │ │ │ │ │ │ │ │ query │ │ 257 | " https: // user : pass @ sub.host.com : 8080 /p/a/t/h ? query=string #hash " 258 | │ │ │ │ │ hostname │ port │ │ │ │ 259 | │ │ │ │ ├──────────────┴──────┤ │ │ │ 260 | │ protocol │ │ username │ password │ host │ │ │ │ 261 | ├──────────┴──┼──────────┴──────────┼─────────────────────┤ │ │ │ 262 | │ origin │ │ origin │ pathname │ search │ hash │ 263 | ├─────────────┴─────────────────────┴─────────────────────┴──────────┴────────────────┴───────┤ 264 | │ href │ 265 | └─────────────────────────────────────────────────────────────────────────────────────────────┘ 266 | ``` 267 | 268 | 例如:https://admin:123@zce.me:80/schools/students?id=18&name=zce#photo 269 | 270 | ## 请求响应流程 271 | 272 | ![HTTP请求和响应](media/http-req-res.png) 273 | 274 | 1. 用户打开浏览器 275 | 2. 地址栏输入我们需要访问的网站网址(URL) 276 | 3. 浏览器通过 DNS 服务器获取即将访问的网站 IP 地址 277 | 4. 浏览器发起一个对这个 IP 的请求 278 | 5. 服务端接收到这个请求,进行相应的处理 279 | 6. 服务端将处理完的结果返回给客户端浏览器 280 | 7. 浏览器将服务端返回的结果呈现到界面上 281 | 282 | ## 配置 Apache 283 | 284 | > 配置文档:http://httpd.apache.org/docs/current/ 285 | > 286 | > 配置文件中行首的 `#` 指的是注释 287 | > 288 | > **注意**:以下所记录的行号仅供参考,不同版本的配置文件可能不尽相同。 289 | 290 | ### 监听端口 291 | 292 | 监听端口可以随意修改为任意一个未被其他程序监听的端口,可以通过设置配置文件 `httpd.conf` 中的 `Listen` 指令后面的数字修改。 293 | 294 | ![1506132097583](media/1506132097583.png) 295 | 296 | ### 网站根目录 297 | 298 | > **网站根目录**就是存放我们网站文件的最顶层目录,通常 URL 中域名后面的第一个斜线对应(映射)的就是网站根目录。 299 | > 300 | > **默认文档**指的是我们在访问某一个目录时(没有指定具体的文件),默认访问的文件叫做默认文档 301 | > 302 | > **注意**:动态网站情况会比较特殊,需要单独考虑,不一定是这个规则。 303 | 304 | 默认 Apache 的网站根目录是安装目录中的 `htdocs` 文件夹,为了方便对网站文件的管理,一般我们会将其设置在一个自定义目录中(如果你不介意其实不修改也无所谓)。 305 | 306 | 如果需要设置网站根目录,可以通过修改配置文件 `httpd.conf` 中的网站根目录选项切换。 307 | 308 | ![1505998898900](media/1505998898900.png) 309 | 310 | ### 默认文档 311 | 312 | > 当客户端访问的是一个目录而不是具体文件时,服务端默认返回这个目录下的某个文档(文件),这个文档就称之为**默认文档**。 313 | 314 | 配置文件 `httpd.conf` 的 280 行的 `DirectoryIndex`,默认文档可以配置多个(有前到后依次去找,找到为止,如果没找到任何一个则启用目录浏览): 315 | 316 | ![1506132675132](media/1506132675132.png) 317 | 318 | ### 虚拟主机 319 | 320 | 如果一台机器上只有一个网站的话,没有任何问题,但是如果想要在一台机器上部署多个站点,就必须通过配置虚拟主机的方式解决。 321 | 322 | > 由于后期对虚拟主机的配置操作非常常见,所以我们一般将虚拟主机的配置单独放到一个配置文件中,然后在主配置文件中引入,避免破坏主配置文件中的其他配置。 323 | > 324 | > `Include conf/extra/httpd-vhosts.conf` 配置的作用就将另外一个配置文件引入(使其生效) 325 | 326 | 具体的操作方式就是在主配置文件 `httpd.conf` 的 505 行取消注释: 327 | 328 | ![1505999461533](media/1505999461533.png) 329 | 330 | 然后找到 Apache 的虚拟主机配置文件,添加一个如下的虚拟主机配置节点,然后重新启动 Apache。 331 | 332 | > 这个文件中有两个默认的示例配置,可以注释掉 333 | 334 | ![1506136421939](media/1506136421939.png) 335 | 336 | 如果真的要使用 `baixiu.com` 这个域名的话,就只能通过修改 `hosts` 文件达到目的,原因很简单:这个域名不是我们自己的,我们没有办法修改这个域名在公网上的 DNS。 337 | 338 | > 注意: 339 | > - 如果使用了虚拟主机,则默认必须全部使用虚拟主机,即之前的默认网站也必须通过虚拟主机方式配置,否则访问不到。参考:http://skypegnu1.blog.51cto.com/8991766/1532454 340 | > - 如果虚拟主机的端口使用的不是 `80`,则需要在主配置文件中添加一个对这个端口的监听: 341 | > ![1506001971125](media/1506001971125.png) 342 | > Forbidden 情况通过添加一个 Directory 节点解决 343 | 344 | ## 静态网站与动态网站 345 | 346 | 至此,我们已经可以把这些静态页面放到服务器上了,客户端也可以通过域名请求这个网站,但是对于我们来说,Apache 能够完成的事情过于简单,无外乎就是找到你请求对应的文件 → 读取文件 → 将文件内容响应给客户端浏览器(**文件原封不动的给你**)。**无法满足让网页内容动起来(随着数据动态变化)的需求。** 347 | 348 | 于是乎,就有人提出了**服务端动态网页的概念**,这种实现这种概念的技术有很多种:JSP、ASP.NET、PHP、Node 等等。 349 | 350 | 这些技术的原理就是:不再将 HTML 固定写死,每次用户请求时,动态执行一段代码,临时生成一个用户想要的 HTML 页面。 351 | 352 | ![apache-process](media/apache-process.png) 353 | 354 | **动态网站指的也就是每次请求时服务端动态生成 HTML 返回给用户的这种网站。** 355 | 356 | 这里我们选择 PHP 作为我们了解服务端动态网页开发的技术方案,注意:**我们学习的重心不在 PHP,而是了解服务端开发,以及某些其他对前端开发有帮助的东西。** 357 | 358 | ### 配置 PHP 支持 359 | 360 | > PHP 文件的扩展名就是 `.php` 361 | 362 | 我们可以尝试在刚刚配置的网站中添加一个扩展名为 `php` 的文件,然后到浏览器中访问它。 363 | 364 | ```php 365 | 366 | 367 | ``` 368 | 369 | 结果出乎意料,并没有显示我们想要的 `Hello PHP`,而是将我们的代码原封不动的返回给浏览器了。 370 | 371 | ![1506004529597](media/1506004529597.png) 372 | 373 | 原因很简单:Apache 只能处理静态文件请求,对于后缀名为 `.php` 这种动态文件,它无法执行,所以就当成是一个静态文件直接返回了。 374 | 375 | **解决方法**: 376 | 377 | - 在服务器上安装 PHP 378 | - 解压 php 到纯英文路径目录中 379 | - 在 Apache 中添加支持 PHP 的配置 380 | - 在 Apache 添加 PHP 处理模块 381 | ```ini 382 | # php support 383 | LoadModule php7_module C:/Develop/php/php7apache2_4.dll 384 | ``` 385 | - 在 `` 节点中添加 `.php` 扩展名解析支持 386 | ```ini 387 | # parse .php files 388 | AddType application/x-httpd-php .php 389 | ``` 390 | - 默认文档配置节点 `` 中添加 `index.php` 391 | ```ini 392 | 393 | DirectoryIndex index.html index.php 394 | 395 | ``` 396 | - 重启 Apache 397 | 398 | > 默认文档指的是在访问一个目录而不是具体文件名时,默认执行的文件名 399 | 400 | ### Apache 与 PHP 401 | 402 | 对于很多初学者来说,很容易把 Apache 和 PHP 混在一起 混为一谈,其实他们两者各自有各自负责的领域,各自的职责,但是我们在使用 PHP 做动态网站开发时,两者就会产生联系,具体如下: 403 | 404 | > Apache 是根据文件的扩展名找到文件的类型,然后挨个问一下每一个模块能否处理这个类型的文件,如果这些模块都不能处理,那么 Apache 就自己处理(按照静态文件的方式处理) 405 | 406 | ![apache-php](media/apache-php.png) 407 | 408 | > 你可以理解为:Apache 是一家没有太多能力的公司,只能处理一些简单的业务(静态网站),但是心很大想做更多的事(动态网站),所以就想到了外包,所有额外的业务都需要外包给其他程序,而 PHP 就是理解为一个专门能够处理 php 业务的外包公司 409 | 410 | ## 作业 411 | 412 | - 安装 Apache 并配置,确保本机可以访问。 413 | - 同桌相互访问对方提供的 Web 服务。 414 | - 让 Apache 支持 php 文件的运行。 415 | - 可以配置网站的默认文档。 416 | -------------------------------------------------------------------------------- /02-php.md: -------------------------------------------------------------------------------- 1 | # PHP 2 | 3 | > 详细参考文档:http://php.net/manual/zh/index.php 4 | 5 | > 借助于 PHP 理解 Web 开发中动态网站的作用及相关概念 6 | 7 | ## 起步 8 | 9 | - PHP 是什么? 10 | - PHP 写在哪? 11 | - PHP 能做啥? 12 | 13 | > 超文本标记是用普通文本描述富文本的一种方式 14 | 15 | PHP(PHP: Hypertext Preprocessor)是一种被广泛应用的脚本语言,它可以被嵌入到 HTML中,尤其适合做动态网站开发开发。 16 | 17 | 我们接下来会在 PHP 中看到的许多代码特性和其他编程语言类似,例如:变量、函数、循环,等等。 代码语法看起来不同,但是在概念上是基本类似的。 18 | 19 | 我们使用 PHP 的目的就是能让静态网页变成动态网页,能称之为动态网页的核心就是让 HTML 上的内容不再被写死,而是通过在 HTML 中嵌入一段可以在服务端执行的代码,从而达到动态网页的目标。 20 | 21 | 例如:我们需要有一个网页,这个网页每次打开都可显示当前的年月日,如果采用 HTML 处理: 22 | 23 | ```html 24 | 25 | 26 | 27 | 28 | 当前日期 29 | 30 | 31 |

2020-01-01

32 | 33 | 34 | ``` 35 | 36 | 我们必须每天到服务器上修改这个网页,从而让它保持显示最新日期,但是有了 PHP 这种能够在服务端执行的脚本语言就可以很轻松实现: 37 | 38 | ```php 39 | 40 | 41 | 42 | 43 | 当前日期 44 | 45 | 46 |

47 | 48 | 49 | ``` 50 | 51 | 从以上这个最最简单的基础案例就能看出:PHP 无外乎为了可以在网页中动态输出最新内容的一种技术手段。 52 | 53 | > 历史使人明智:http://php.net/manual/zh/history.php.php 54 | 55 | ### PHP 标记 56 | 57 | > http://php.net/manual/zh/language.basic-syntax.phpmode.php 58 | 59 | - `` 可以让代码退出“PHP 模式” 61 | 62 | ```php 63 | 64 | 65 | 66 | 67 | 这是一个包含 PHP 脚本的网页 68 | 69 | 70 |

这是一个包含 PHP 脚本的网页

71 |

这里原封不动的输出

72 | 73 | 78 | 79 |

这里也不变

80 | 81 |

这还是 PHP 输出的'; ?>

82 | 83 | 84 | ``` 85 | 86 | 类似于在 HTML 中使用 JavaScript,但是不同的是 JavaScript 运行在客户端,而 PHP 运行在服务端。 87 | 88 | **只有处于 PHP 标记内部的代码才是 PHP 代码,PHP 标记以外都原封不动。** 89 | 90 | #### 省略结束标记 91 | 92 | 如果 PHP 代码段处于整个文件的末尾,建议(必须)删除结束标记,这样不会有额外的空行产生。 93 | 94 | ### 输出内容方式 95 | 96 | - echo: 97 | 98 | ```php 99 | `helloworld` 106 | ``` 107 | 108 | - print: 109 | 110 | ```php 111 | Parse error: syntax error ... 116 | ``` 117 | 118 | - var_dump: 119 | 120 | ```php 121 | 'string(9) "hello php"' 126 | ``` 127 | 128 | > 还有一些输出函数(可以通过查手册自学,用到再说),例如:`exit()` / `print_r()` 等等 129 | 130 | ### 与 HTML 混编 131 | 132 | #### 普通嵌入 133 | 134 | ```php 135 |

136 | ``` 137 | 138 | #### 语句混编 139 | 140 | ```php 141 | = 18) { ?> 142 |

成年人

143 | 144 |

小朋友

145 | 146 | ``` 147 | 148 | ##### 更常见的用法 149 | 150 | ```php 151 | 18): ?> 152 |

成年人

153 | 154 |

小朋友

155 | 156 | ``` 157 | 158 | ### 注释 159 | 160 | 你可以在代码中添加注释,从而增强我们代码的可阅读性。PHP 中注释有两种方式(与 JavaScript 相同): 161 | 162 | #### 单行注释 163 | 164 | ```php 165 | 变量是编程语言中临时存放数据的容器。 209 | 210 | PHP 中申明一个变量是用一个美元符号后面跟变量名来表示。变量名同样是区分大小写的。 211 | 212 | PHP 中变量无需声明类型,变量的类型根据值的类型来推断。 213 | 214 | ```php 215 | `hello\nworld` 253 | echo 'I\'m a better man'; 254 | // => `I'm a better man` 255 | echo 'OS path: C:\\Windows'; 256 | // => `OS path: C:\Windows` 257 | 258 | // ====== 双引号 ====== 259 | echo "hello\nworld"; 260 | // => `hello 261 | // world` 262 | $name = 'zce'; 263 | echo "hello $name"; 264 | // => `hello zce` 265 | ``` 266 | 267 | > 字符串函数 268 | > - http://php.net/manual/zh/ref.strings.php 269 | > - http://www.w3school.com.cn/php/php_string.asp 270 | 271 | ##### 数组 272 | 273 | PHP 中数组可以分为两类: 274 | 275 | ###### 索引数组 276 | 277 | 与 JavaScript 中的数组基本一致 278 | 279 | ```php 280 | 'value1', 'key2' => 'value2'); 298 | var_dump($arr); 299 | 300 | // PHP 5.4 以后定义的方式可以用 `[]` 301 | $arr2 = ['key1' => 'value1', 'key2' => 'value2']; 302 | var_dump($arr2); 303 | ``` 304 | 305 | #### 数据类型转换 306 | 307 | > 参考:http://php.net/manual/zh/language.types.type-juggling.php 308 | 309 | ```php 310 | 'zhangsan', 'age' => '18'); 351 | 352 | foreach ($arr as $key => $value) { 353 | echo $key . ' ' . $value; 354 | } 355 | ``` 356 | 357 | 指令式的 if、for、foreach、while 单独掌握 358 | 359 | ```php 360 | 0) : 363 | echo 'ok' 364 | endif; 365 | 366 | // for foreach while 也是一样 367 | for ($i = 0; $i < 10; $i++) : 368 | echo $i; 369 | endfor; 370 | ``` 371 | 372 | ### 函数 373 | 374 | 定义与使用函数的方式与 JavaScript 相同: 375 | 376 | ```php 377 | 建议在 PHP 中采用下划线式(snake_case)做命名规则,不管是函数还是变量 391 | 392 | ## 特性 393 | 394 | ### 变量作用域 395 | 396 | 关于变量作用域这一点,PHP 与绝大多数语言也都不同:**默认函数内不能访问函数所在作用域的成员。** 397 | 398 | 在 JavaScript 中,我们可以在函数作用域中使用父级作用域中的成员: 399 | 400 | ```javascript 401 | var top = 'top variable' 402 | 403 | function foo () { 404 | var sub = 'sub variable' 405 | 406 | console.log(top) 407 | // => `top variable` 408 | 409 | function bar () { 410 | console.log(top) 411 | // => `top variable` 412 | console.log(sub) 413 | // => `sub variable` 414 | } 415 | 416 | bar() 417 | } 418 | 419 | foo() 420 | ``` 421 | 422 | 而在 PHP 中: 423 | 424 | ```php 425 | 无法拿到 433 | 434 | function bar () { 435 | echo $top; 436 | // => 无法拿到 437 | 438 | echo $sub; 439 | // => 无法拿到 440 | } 441 | 442 | bar(); 443 | } 444 | 445 | foo(); 446 | ``` 447 | 448 | 如果需要访问全局变量,可以通过 `global` 关键字声明: 449 | 450 | ```php 451 | `top variable` 462 | 463 | function bar () { 464 | // 声明在当前作用域中获取全局作用域中的 `$top` 和 `$bar` 465 | global $top, $bar; 466 | 467 | echo $top; 468 | // => `top variable` 469 | 470 | echo $sub; 471 | // => 任然无法拿到,因为 `$sub` 不再全局范围,而是在 `foo` 函数中定义 472 | } 473 | 474 | bar(); 475 | } 476 | 477 | foo(); 478 | ``` 479 | 480 | ### 超全局变量 481 | 482 | > http://www.w3school.com.cn/php/php_superglobals.asp 483 | 484 | PHP 中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可用。在函数或方法中无需执行 global $variable; 就可以访问它们。 485 | 486 | 这些超全局变量是: 487 | 488 | - $GLOBALS — 引用全局作用域中可用的全部变量 489 | - $_SERVER — 获取服务端相关信息 490 | - $_REQUEST — 获取提交参数 491 | - $_POST — 获取 POST 提交参数 492 | - $_GET — 获取 GET 提交参数 493 | - $_FILES — 获取上传文件 494 | - $_ENV — 操作环境变量 495 | - $_COOKIE — 操作 Cookie 496 | - $_SESSION — 操作 Session 497 | 498 | 本节会介绍一些超全局变量,并会在稍后的章节讲解其他的超全局变量。 499 | 500 | #### $GLOBALS 501 | 502 | $GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。 503 | 504 | PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。 505 | 506 | 下面的例子展示了如何使用超级全局变量 $GLOBALS: 507 | 508 | ```php 509 | 100 520 | ``` 521 | 522 | ### 常量定义与使用 523 | 524 | > 常量跟变量一样也是一个数据容器,但是不同的是一旦申明过后就不允许被修改。 525 | 526 | #### 定义常量 527 | 528 | ```php 529 | API(Application Programming Interface) 582 | > 583 | > 接口都是提供某种特定能力的事物,特点是有输入有输出,而我们在开发时(写代码时)用到的接口称之为 API(应用程序编程接口) 584 | > 585 | > 任何编程语言本身并没有太多的能力,具体的能力大多数都来源于 API。 586 | 587 | PHP 的能力来源于它有 1000+ 内置函数,不是每一个函数都默认直接可以使用,有一些需要安装或者启用额外的"插件" 扩展 588 | 589 | ### 字符串处理 590 | 591 | > 宽字符集函数需要开启 `php_mbstring` 扩展 592 | 593 | #### 开启 PHP 扩展 594 | 595 | 1. 将PHP目录中的 php.ini-development 复制一个 修改为 php.ini 596 | 2. 修改扩展文件所在目录 extension_dir 597 | 3. 修改文件中的部分选项(; 是注释符) 598 | 4. 在 Apache 配置文件中申明一下 php.ini 的所在目录 599 | 600 | #### 字符串处理函数 601 | 602 | - 字符串截取 603 | - `string substr ( string $string , int $start [, int $length ] )` 604 | - `string mb_substr ( string $str , int $start [, int $length = NULL [, string $encoding = mb_internal_encoding() ]] )` 605 | - 字符串长度 606 | - `int strlen ( string $string )` 607 | - `mixed mb_strlen ( string $str [, string $encoding = mb_internal_encoding() ] )` 608 | - 大小写转换 609 | - `string strtolower ( string $string )` 610 | - `string strtoupper ( string $string )` 611 | - 去除首尾空白字符 612 | - `string trim ( string $str [, string $character_mask = " \t\n\r\0\x0B" ] )` 613 | - `string ltrim ( string $str [, string $character_mask ] )` 614 | - `string rtrim ( string $str [, string $character_mask ] )` 615 | - 查找字符串中某些字符首次出现位置 616 | - `mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )` 617 | - `int mb_strpos ( string $haystack , string $needle [, int $offset = 0 [, string $encoding = mb_internal_encoding() ]] )` 618 | - 字符串替换 619 | - `mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )` 620 | - 重复字符串 621 | - `string str_repeat ( string $input , int $multiplier )` 622 | - 字符串分割 623 | - `array explode( string $char, string $input )` 624 | 625 | ![1506312179608](media/1506312179608.png) 626 | 627 | ### 数组处理 628 | 629 | - 获取关联数组中全部的键 / 值 630 | - `array_keys() / array_values()` 631 | - 判断关联数组中是否存在某个键 632 | - `array_key_exists()` 633 | - 去除重复的元素 634 | - `array_unique()` 635 | - 将一个或多个元素追加到数组中 636 | - `array_push()` 637 | - `$arr[] = 'new value'` 638 | - 删除数组中最后一个元素 639 | - `array_pop()` 640 | - 数组长度 641 | - `count()` 642 | - 检测存在 643 | - `in_array()` 644 | - 获取元素在数组中的下标 645 | - `array_search()` 646 | 647 | ### 时间处理 648 | 649 | - 时间戳:`time()` 650 | - 从 Unix 纪元(格林威治时间 1970-01-01 00:00:00)到当前时间的秒数 651 | - 格式化日期:`date()` 652 | - 获取有格式的当前时间 653 | - 格式化一个指定的时间戳 654 | - 可以通过 `strtotime()` 将有格式的时间字符串转换为时间戳 655 | 656 | ### 文件操作 657 | 658 | | 函数 | 描述 | PHP | 659 | | ---------------------------------------- | ----------------------- | ---- | 660 | | [basename()](http://www.w3school.com.cn/php/func_filesystem_basename.asp) | 返回路径中的文件名部分。 | 3 | 661 | | [copy()](http://www.w3school.com.cn/php/func_filesystem_copy.asp) | 复制文件。 | 3 | 662 | | [dirname()](http://www.w3school.com.cn/php/func_filesystem_dirname.asp) | 返回路径中的目录名称部分。 | 3 | 663 | | [disk_free_space()](http://www.w3school.com.cn/php/func_filesystem_disk_free_space.asp) | 返回目录的可用空间。 | 4 | 664 | | [disk_total_space()](http://www.w3school.com.cn/php/func_filesystem_disk_total_space.asp) | 返回一个目录的磁盘总容量。 | 4 | 665 | | [fclose()](http://www.w3school.com.cn/php/func_filesystem_fclose.asp) | 关闭打开的文件。 | 3 | 666 | | [file()](http://www.w3school.com.cn/php/func_filesystem_file.asp) | 把文件读入一个数组中。 | 3 | 667 | | [file_exists()](http://www.w3school.com.cn/php/func_filesystem_file_exists.asp) | 检查文件或目录是否存在。 | 3 | 668 | | [file_get_contents()](http://www.w3school.com.cn/php/func_filesystem_file_get_contents.asp) | 将文件读入字符串。 | 4 | 669 | | [file_put_contents()](http://www.w3school.com.cn/php/func_filesystem_file_put_contents.asp) | 将字符串写入文件。 | 5 | 670 | | [filesize()](http://www.w3school.com.cn/php/func_filesystem_filesize.asp) | 返回文件大小。 | 3 | 671 | | [fopen()](http://www.w3school.com.cn/php/func_filesystem_fopen.asp) | 打开一个文件或 URL。 | 3 | 672 | | [glob()](http://www.w3school.com.cn/php/func_filesystem_glob.asp) | 返回一个包含匹配指定模式的文件名/目录的数组。 | 4 | 673 | | [is_dir()](http://www.w3school.com.cn/php/func_filesystem_is_dir.asp) | 判断指定的文件名是否是一个目录。 | 3 | 674 | | [is_file()](http://www.w3school.com.cn/php/func_filesystem_is_file.asp) | 判断指定文件是否为常规的文件。 | 3 | 675 | | [mkdir()](http://www.w3school.com.cn/php/func_filesystem_mkdir.asp) | 创建目录。 | 3 | 676 | | [move_uploaded_file()](http://www.w3school.com.cn/php/func_filesystem_move_uploaded_file.asp) | 将上传的文件移动到新位置。 | 4 | 677 | | [pathinfo()](http://www.w3school.com.cn/php/func_filesystem_pathinfo.asp) | 返回关于文件路径的信息。 | 4 | 678 | | [rename()](http://www.w3school.com.cn/php/func_filesystem_rename.asp) | 重名名文件或目录。 | 3 | 679 | | [rmdir()](http://www.w3school.com.cn/php/func_filesystem_rmdir.asp) | 删除空的目录。 | 3 | 680 | | [unlink()](http://www.w3school.com.cn/php/func_filesystem_unlink.asp) | 删除文件。 | 3 | 681 | 682 | > 参考:http://www.w3school.com.cn/php/php_ref_filesystem.asp 683 | -------------------------------------------------------------------------------- /03-http.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | 3 | ## 概要 4 | 5 | ### 定义 6 | 7 | HTTP(HyperText Transfer Protocol,超文本传输协议)最早就是计算机与计算机之间沟通的一种标准协议,这种协议限制了通讯**内容的格式**以及各项**内容的含义**。 8 | 9 | ![http-protocol](media/http-protocol.png) 10 | 11 | 随着时代的发展,技术的变迁,这种协议现在广泛的应用在各种领域,也不仅仅局限于计算机与计算机之间,手机、电视等各种智能设备很多时候都在使用这种协议通讯,所以一般现在称 **HTTP 为端与端之间的通讯协议**。 12 | 13 | Web 属于 B/S 架构的应用软件,在 B/S 架构中,浏览器与服务器沟通的协议就是 HTTP 协议,作为一个合格的 Web 开发者,了解 HTTP 协议中约定的内容是一门必修课。 14 | 15 | > 应用软件架构一般分为两类: 16 | > - B/S 架构:Browser(浏览器) ←→ Server(服务器),这种软件都是通过浏览器访问一个网站使用,服务器提供数据存储等服务。 17 | > - C/S 架构:Client(客户端) ←→ Server(服务器),这种软件通过安装一个软件到电脑,然后使用,服务器提供数据存储等服务。 18 | 19 | ### 约定内容 20 | 21 | - 请求 / 响应报文格式 22 | - 请求方法 —— GET / POST / etc. 23 | - 响应状态 —— 200 / 404 / 302 / 304 / etc. 24 | - 预设的请求 / 响应头 25 | 26 | ### 约定形式 27 | 28 | 1. 客户端通过随机端口与服务端某个固定端口(一般为80)**建立连接** 三次握手 29 | 2. 客户端通过这个连接**发送请求**到服务端(这里的请求是名词) 30 | 3. 服务端监听端口得到的客户端发送过来的请求 31 | 4. 服务端通过连接响应给客户端状态和内容(响应报文) 32 | 33 | > 要求:接下来的一个月,每次上网打开任何一个页面时都要能够脑补这个画面,默念这个流程。 34 | 35 | ## 核心概念 36 | 37 | ### 报文 38 | 39 | #### 请求报文 40 | 41 | ![request](media/request.png) 42 | 43 | ![request-structure](media/request-structure.png) 44 | 45 | ##### 请求行 46 | 47 | `GET /demo.php HTTP/1.1` 48 | 49 | 请求方式 + 空格 + 请求路径 + 空格 + HTTP 协议版本 50 | 51 | ##### 请求头 52 | 53 | 客户端想要告诉服务端的一些额外信息,以下为常见的请求头: 54 | 55 | | 键 | 值 | 56 | | --------------- | --------------------------- | 57 | | Host | 请求的主机 | 58 | | Cache-Control | 控制缓存(例如:max-age=60 缓存 60 秒) | 59 | | Accept | 客户端想要接收的文档类型,逗号分隔 | 60 | | User-Agent | 标识什么客户端帮你发送的这次请求 | 61 | | Referer | 这次请求的来源 | 62 | | Accept-Encoding | 可以接受的压缩编码 | 63 | | Cookie | 客户端本地的小票信息 | 64 | 65 | ##### 请求体 66 | 67 | 这次请求客户端想要发送给服务端的数据正文,一般在 GET 请求时很少用到,因为 GET 请求主观上都是去“拿东西”。 68 | 69 | #### 响应报文 70 | 71 | ![response](media/response.png) 72 | 73 | ##### 状态行 74 | 75 | `HTTP/1.1 200 OK` 76 | 77 | HTTP 协议版本 + 空格 + 状态码 + 空格 + 状态描述 78 | 79 | ##### 响应头 80 | 81 | 服务端想要告诉客户端的一些额外信息,常见的有以下: 82 | 83 | | 键 | 值 | 84 | | -------------- | ---------- | 85 | | Date | 响应时间 | 86 | | Server | 服务器信息 | 87 | | Content-Type | 响应体的内容类型 | 88 | | Content-Length | 响应的内容大小 | 89 | | Set-Cookie | 让客户端设置一个小票 | 90 | 91 | 如果需要在程序中设置自定义的响应头(不是预设的),建议使用 `X-` 规则 92 | 93 | ##### 响应体 94 | 95 | 这次请求服务端想要返回给客户端的数据正文,一般返回的都是 HTML,也可以返回 JavaScript 或者 CSS(需要修改响应头中的响应类型)。 96 | 97 | #### 应用场景 98 | 99 | - 设置响应文件类型 100 | - `header('Content-Type: text/css');` 101 | - 常见的 HTTP MIME type:`text/css` `text/html` `text/plain` `applcation/javascript` 102 | - 重定向(跳转到其他网页) 103 | - `header('Location: https://www.baidu.com');` 104 | - 下载文件 105 | ```php 106 | // 让文件下载 107 | header('Content-Type: application/octet-stream'); 108 | // 设置默认下载文件名 109 | header('Content-Disposition: attachment; filename=demo.txt'); 110 | ``` 111 | - 图片防盗链 112 | - 通过判断请求来源 `Referer` 是否为本网站从而区分是否是合法请求 113 | 114 | ### 请求方式 115 | 116 | > - http://www.w3school.com.cn/tags/html_ref_httpmethods.asp 117 | > - http://www.runoob.com/http/http-methods.html 118 | 119 | #### GET 120 | 121 | 字面意思:拿,获取 122 | 123 | #### POST 124 | 125 | 字面意思:发,给 126 | 127 | #### 对比 GET 与 POST 128 | 129 | | | GET | POST | 130 | | -------- | ---------------------------------------- | ---------------------------------------- | 131 | | 后退按钮/刷新 | 无害 | 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。 | 132 | | 书签 | 可收藏为书签 | 不可收藏为书签 | 133 | | 缓存 | 能被缓存 | 不能缓存 | 134 | | 编码类型 | application/x-www-form-urlencoded | application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。 | 135 | | 历史 | 参数保留在浏览器历史中。 | 参数不会保存在浏览器历史中。 | 136 | | 对数据长度的限制 | 是的。当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 | 无限制。 | 137 | | 对数据类型的限制 | 只允许 ASCII 字符。 | 没有限制。也允许二进制数据。 | 138 | | 安全性 | 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET ! | POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。 | 139 | | 可见性 | 数据在 URL 中对所有人都是可见的。 | 数据不会显示在 URL 中。 | 140 | 141 | ### 状态码 142 | 143 | > 了解即可,不用刻意去记忆,用多了自然就忘不了。 144 | > - http://www.w3school.com.cn/tags/html_ref_httpmessages.asp 145 | > - https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status 146 | 147 | 状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。 148 | 149 | - 1xx:指示信息 —— 表示请求已接收,继续处理。 150 | - 2xx:成功 —— 表示请求已被成功接收、理解、接受。 151 | - 3xx:重定向 —— 要完成请求必须进行更进一步的操作。 152 | - 4xx:客户端错误 —— 请求有语法错误或请求无法实现。 153 | - 5xx:服务器端错误 —— 服务器未能实现合法的请求。 154 | 155 | 常见状态代码、状态描述的说明如下。 156 | 157 | - 200 OK:客户端请求成功。 158 | - 400 Bad Request:客户端请求有语法错误,不能被服务器所理解。 159 | - 401 Unauthorized:请求未经授权,这个状态代码必须和 `WWW-Authenticate` 报头域一起使用。 160 | - 403 Forbidden:服务器收到请求,但是拒绝提供服务。 161 | - 404 Not Found:请求资源不存在,举个例子:输入了错误的URL。 162 | - 500 Internal Server Error:服务器发生不可预期的错误。 163 | - 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。 164 | 165 | ### 其他常见请求信息 166 | 167 | https://segmentfault.com/a/1190000006689767 168 | -------------------------------------------------------------------------------- /04-form.md: -------------------------------------------------------------------------------- 1 | # 表单处理 2 | 3 | 表单的概念在生活中很常见,就像是问卷调查表一样,别人先把问卷发给你,你照着问卷的要求填写,完事过后再将填完的问卷发给别人,从而达到一个将别人需要的信息传递给别人的一种方式。 4 | 5 | 传统的网页大多数的作用都是展示数据,就是将信息传递给用户。而在现代化的 Web 开发中,非常注重信息交互,所以表单也随处可见,只是形式上变成网页,性质上还是一模一样的。主要的作用任然是**收集指定的用户信息**。 6 | 7 | > 信息交互:例如 [简书](http://www.jianshu.com) 这个平台,除了展示文章(展示信息),还可以发布文章(收集信息) 8 | 9 | ## 表单基本使用 10 | 11 | HTML 中有一个专门用于提交数据的标签:`
`,通过这个标签可以很容易的收集用户输入。 12 | 13 | > form 标签有两个必要属性: 14 | > - action:表单提交地址(填完了,交给谁) 15 | > - method:表单以什么方式提交 16 | 17 | 例如,我们需要在登录界面上收集用户输入的用户名和密码: 18 | 19 | ```html 20 | 21 | 22 | 23 | 24 | 登录 25 | 26 | 27 | 28 |
29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 | 39 | 40 | ``` 41 | 42 | 按照目前的情况,用户第一次请求得到这个表单页面,填写完表单内容,点击登录,表单会自动发送到 `login.php`,剩下的问题就是要考虑如何在 `login.php` 中获取到用户提交过来的内容。 43 | 44 | > PHP 中有三个超全局变量专门用来获取表单提交内容: 45 | > - `$_GET`:用于获取以 GET 方式提交的内容,更标准的说法:接收 URL 地址问号参数中的数据 46 | > - `$_POST`:用于获取以 POST 方式提交的内容,更标准的说法:接收 请求体 中的数据 47 | > - `$_REQUEST`:用于获取 GET 或 POST 方式提交的内容 48 | 49 | 借助 `$_POST` 或者 `$_REQUEST` 就可以获取到表单提交的内容: 50 | 51 | ```php 52 | 75 |
76 | 77 |
78 | 79 |
80 | 81 |
82 | ``` 83 | 84 | > 鲁棒性:指的是我们的程序应对变化的能力 85 | 86 | ### 提交方式 87 | 88 | `method` 可以用于设置表单提交的方式,目前我们所认识的就是最常见两种表单提交方式:`GET` 和 `POST`。 89 | 90 | 从效果上来看,两者都可以将数据提交到服务端,但是从实现提交的原理上两者有很大的不同: 91 | 92 | - GET 93 | - **表单数据是通过 URL 中的 ? 参数传递到服务端的** 94 | - 可以在地址栏中看到提交的内容 95 | - 数据长度有限制,因为 URL 地址长度有限(2000个字符) 96 | - POST 97 | - **表单数据是通过请求体传递到服务端的**,我们在界面上看不到 98 | - 可以提交任何类型的数据,包括文件 99 | - 由于界面上看不见,浏览器也不储存,所以更安全 100 | 101 | 至于什么情况下应该选用哪种方式,这个需要结合业务场景和这两种方式各自的特点来决定,没有绝对的答案,只能给出一些原则: 102 | 103 | - 绝不能使用 GET 来发送密码或其他敏感信息!!! 104 | - **应该想清楚这次请求到底主要是去拿东西,还是去送东西** 105 | 106 | ## 常见表单元素处理 107 | 108 | 至于表单元素中的文本框文本域一类的元素,都是直接将元素的 `name` 属性值作为键,用户填写的信息作为值,发送到服务端。 109 | 110 | 但是表单元素中还有一些比较特殊的表单元素需要单独考虑: 111 | 112 | ### 单选按钮 113 | 114 | ```html 115 | 116 | 117 | 118 | ``` 119 | 120 | ### 复选按钮 121 | 122 | ```html 123 | 124 | 125 | 126 | 127 | ``` 128 | 129 | 如果需要同时提交多个选中项,可以在 `name` 属性后面 跟上 `[]`: 130 | 131 | > http://php.net/manual/zh/faq.html.php#faq.html.arrays 132 | 133 | ```html 134 | 135 | 136 | 137 | ``` 138 | 139 | 最终提交到服务端,通过 `$_POST` 接收到的是一个索引数组。 140 | 141 | ### 选择框 142 | 143 | ```html 144 | 150 | ``` 151 | 152 | ## 案例 153 | 154 | ### 基于文件的注册和登录 155 | 156 | 1. 注册 157 | 1. 用户请求一个注册页面 158 | 2. 服务端返回一个注册表单 159 | 3. 用户填写表单并提交 160 | 4. 服务端接收用户提交的信息 161 | 5. 判断是否正确填写内容以及是否勾选同意 162 | 6. 如果出现异常界面给出提示,并返回表单 163 | 7. 如果都正常,则保存到文件中(每个用户一行) 164 | 2. 登录 165 | 1. 自己分析 166 | 167 | ## 文件上传 168 | 169 | > http://php.net/manual/zh/features.file-upload.php 170 | 171 | > **注意:** 172 | > - 修改 `php.ini` 中的 `post_max_size` 配置,让服务端可以接受更大的请求体体积 173 | > - 修改 `php.ini` 中的 `upload_max_filesize` 配置,让服务端支持更大的单个上传文件 174 | > *暂时作为了解 175 | 176 | `type` 属性为 `file` 的 `input` 元素可以通过表单提交文件(上传文件),服务端 PHP 可以通过 `$_FILES` 获取上传的文件信息。 177 | 178 | ```php 179 | 0 181 | // 详细的错误码说明:http://php.net/manual/zh/features.file-upload.errors.php 182 | if ($_FILES['file']['error'] === 0) { 183 | // PHP 在会自动接收客户端上传的文件到一个临时的目录 184 | $temp_file = $_FILES['file']['tmp_name']; 185 | // 我们只需要把文件保存到我们指定上传目录 186 | $target_file = '../static/uploads/' . $_FILES['file']['name']; 187 | if (move_uploaded_file($temp_file, $target_file)) { 188 | $image_file = '/static/uploads/' . $_FILES['file']['name']; 189 | } 190 | } 191 | ``` 192 | 193 | `$_FILES` 同样也是一个关联数组,键为表单的 `name`,内容如下: 194 | 195 | ```php 196 | array(1) { 197 | ["avatar"]=> 198 | array(5) { 199 | ["name"]=> 200 | string(17) "demo.jpg" 201 | ["type"]=> 202 | string(10) "image/jpeg" 203 | ["tmp_name"]=> 204 | string(27) "C:\Windows\Temp\php786C.tmp" 205 | ["error"]=> 206 | int(0) 207 | ["size"]=> 208 | int(29501) 209 | } 210 | } 211 | ``` 212 | 213 | ## 音乐列表案例 214 | 215 | > 基于文件存储的音乐数据**增删改查** 216 | 217 | ### 思路分析 218 | 219 | > 绝大多数情况下我们编写的应用功能都是在围绕着某种类型的数据做增删改查(Create / Read / Update / Delete)。 220 | 221 | 对于被增删改查的数据有几点是可以明确的: 222 | 223 | - 结构相同的多条数据(可以认为是:一个数组,数组中的元素都具有相同的属性结构) 224 | - 可以持久化(长久保存) 225 | 226 | #### 数据放在哪? 227 | 228 | 我们第一件事就是考虑数据放在哪(怎么存怎么取)? 229 | 230 | 目前我们接触到的技术方案中,只有文件可以持久化的保存内容(数据),所以一定是用文件存我们要操作的数据。 231 | 232 | 但是由于我们要存放的是一个有着复杂结构的数据,并不是简简单单的值,所以我们必须设计一种**能表示复杂结构数据的方式**。 233 | 234 | 例如,一行为一条数据,不同信息之间用 `|` 分割,同时约定好每个位置的数据含义: 235 | 236 | ``` 237 | 59d632855434e | 错过 | 梁咏琪 | /uploads/img/1.jpg | /uploads/mp3/1.mp3 238 | 59d632855434f | 开始懂了 | 孙燕姿 | /uploads/img/2.jpg | /uploads/mp3/2.mp3 239 | 59d6328554350 | 一生中最爱 | 谭咏麟 | /uploads/img/3.jpg | /uploads/mp3/3.mp3 240 | 59d6328554351 | 爱在深秋 | 谭咏麟 | /uploads/img/4.jpg | /uploads/mp3/4.mp3 241 | ``` 242 | 243 | 这种方式很简单,但是缺点也非常明显,所以我们迫切需要一个更好更方便的表示有结构数据的方式。 244 | 245 | ### JSON 246 | 247 | JSON(JavaScript Object Notation) 是一种通过普通字符串描述数据的手段,用于表示有结构的数据。类似于编程语言中字面量的概念,语法上跟 JavaScript 的字面量非常类似。 248 | 249 | #### 数据类型 250 | 251 | - null 252 | ```json 253 | null 254 | ``` 255 | - string 256 | ```json 257 | "hello json" 258 | ``` 259 | - number 260 | ```json 261 | 2048 262 | ``` 263 | - boolean 264 | ```json 265 | true 266 | ``` 267 | - object 268 | ```json 269 | { 270 | "name": "zce", 271 | "age": 18, 272 | "gender": true, 273 | "girl_friend": null 274 | } 275 | ``` 276 | - array 277 | ```json 278 | ["zhangsan", "lisi", "wangwu"] 279 | ``` 280 | 281 | #### 注意 282 | 283 | 1. JSON 中属性名称必须用双引号包裹 284 | 2. JSON 中表述字符串必须使用双引号 285 | 3. JSON 中不能有单行或多行注释 286 | 4. JSON 没有 `undefined` 这个值 287 | 288 | #### JSON 表述 289 | 290 | 有了 JSON 这种格式,我们就可以更加容易的表示拥有复杂结构的数据了。 291 | 292 | ```json 293 | [ 294 | { 295 | "id": "59d632855434e", 296 | "title": "错过", 297 | "artist": "梁咏琪", 298 | "images": ["/uploads/img/1.jpg"], 299 | "source": "/uploads/mp3/1.mp3" 300 | }, 301 | { 302 | "id": "59d632855434f", 303 | "title": "开始懂了", 304 | "artist": "孙燕姿", 305 | "images": ["/uploads/img/2.jpg"], 306 | "source": "/uploads/mp3/2.mp3" 307 | }, 308 | { 309 | "id": "59d6328554350", 310 | "title": "一生中最爱", 311 | "artist": "谭咏麟", 312 | "images": ["/uploads/img/3.jpg"], 313 | "source": "/uploads/mp3/3.mp3" 314 | }, 315 | { 316 | "id": "59d6328554351", 317 | "title": "爱在深秋", 318 | "artist": "谭咏麟", 319 | "images": ["/uploads/img/4.jpg"], 320 | "source": "/uploads/mp3/4.mp3" 321 | } 322 | ] 323 | ``` 324 | 325 | ### 功能实现 326 | 327 | > 在服务端开发领域中所谓的**渲染**指的是经过程序执行得到最终的 HTML 字符串这个过程。 328 | 329 | #### 列表数据展示(展示类) 330 | 331 | - 文件读取 332 | - JSON 反序列化 333 | - json_decode 需要注意第二个参数 334 | - 如果希望以关联数组的方式而非对象的方式操作数据,可以将 json_decode 的第二个参数设置为 true 335 | - 数组遍历 foreach 336 | - PHP 与 HTML 混编 337 | 338 | #### 新增数据(表单类) 339 | 340 | ```sequence 341 | 客户端->服务端: GET /add.php\n获取一个添加音乐的表单 342 | 服务端->客户端: 响应一个空的表单页面 343 | Note left of 客户端: 用户填写表单内容 344 | 客户端->服务端: POST /add.php\n提交用户数据的内容和选择的文件 345 | Note right of 服务端: 接收并处理提交的数据 346 | 服务端->客户端: 跳转回列表页 347 | ``` 348 | 349 | - 表单使用(form action method enctype,input name label for id) 350 | - 服务端表单校验并提示错误消息 351 | - empty 判断一个成员是否没定义或者值为 false(可以隐式转换为 false) 352 | - 上传文件 353 | - 文件数量 354 | - 文件种类 355 | - 如果需要考虑文件重名的情况,可以给上传的文件重新命名(唯一名称) 356 | - 单文件域多文件上传 357 | - name 一定 以 [] 结尾,服务端会接收到一个数组 358 | - JSON 序列化 359 | - 文件写入 360 | 361 | #### 删除数据 362 | 363 | - 问号传参 364 | - 一般情况下,如果需要超链接点击发起的请求可以传递参数,我们可以采用 `?` 的方式 365 | ```html 366 | 删除 367 | ``` 368 | - 数组中找到指定元素 369 | - `array_search` 370 | - 数组移除元素 371 | - `array_splice` 372 | 373 | ## 参考链接 374 | 375 | - HTML 中的 form 标签:http://www.w3school.com.cn/html/html_forms.asp 376 | - PHP 中处理表单:http://www.w3school.com.cn/php/php_forms.asp 377 | - Emmet 手册:https://docs.emmet.io/cheat-sheet/ 378 | -------------------------------------------------------------------------------- /05-session.md: -------------------------------------------------------------------------------- 1 | # HTTP 会话 2 | 3 | ## Cookie 4 | 5 | HTTP 很重要的一个特点就是**无状态**(每一次见面都是“初次见面”),如果单纯的希望通过我们的服务端程序去记住每一个访问者是不可能的,所以必须借助一些手段或者说技巧让服务端记住客户端,这种手段就是 **Cookie**。 6 | 7 | ![cookie](media/cookie.png) 8 | 9 | Cookie 就像是在超级市场买东西拿到的小票,由超市(Server)发给消费者(Browser),超市方面不用记住每一个消费者的脸,但是他们认识消费者手里的小票(Cookie),可以通过小票知道消费者之前的一些消费信息(在服务端产生的数据)。 10 | 11 | ### PHP 中操作 Cookie 12 | 13 | > http://php.net/manual/zh/function.setcookie.php 14 | 15 | ```php 16 | // 设置 cookie 17 | setcookie('key1', 'value1'); /* 1 小时过期 */ 18 | // 获取 cookie 19 | echo $_COOKIE["key1"]; 20 | // 删除 cookie 21 | // 原理:设置过期时间为一个过去时间 22 | setcookie('key1'); 23 | // 传递第三个参数是设置过期时间 24 | // 不传递就是 会话级别的 Cookie (关闭浏览器就自动删除) 25 | setcookie('key2', 'value2', time() + 1 * 24 * 60 * 60); 26 | // 设置作用路径范围 27 | setcookie('key3', 'value3', time() + 1 * 24 * 60 * 60, '/users'); 28 | ``` 29 | 30 | #### path 31 | 32 | 设置 cookie 的作用路径范围 33 | 34 | - `/` => 只要是在网站根目录下的所有连接地址中都可访问这个 Cookie 35 | - `/users` => 只能在 `users` 目录下的路径才能访问 36 | 37 | #### domain 38 | 39 | 设置 cookie 的作用域名范围 40 | 41 | - `day-10.io` => 所有的 `day-10.io` 的子域 都可以访问到 42 | - `bar.day-10.io` √ 43 | - `foo.day-10.io` √ 44 | - `day-11.io` × 45 | - `bar.day-10.io` => 所有的 `bar.day-10.io` 的子域 都可以访问到 46 | - `a.bar.day-10.io` √ 47 | - `foo.day-10.io` × 48 | 49 | #### httponly 50 | 51 | 一旦 cookie 的 httponly 为真,那么只能在服务端获取,JS 无法操作 52 | 53 | #### 记住登录名案例 54 | 55 | ##### 登录功能实现流程 56 | 57 | ```sequence 58 | 客户端->服务端: Request GET /login.php 59 | 服务端->客户端: Response 空白表单页面 60 | Note left of 客户端: 用户填写表单 61 | 客户端->服务端: Request POST /login.php 表单数据 62 | Note right of 服务端: 服务端对提交过来的数据进行校验 63 | 服务端->客户端: Response Location: /main.php\n跳转到 main.php 64 | 客户端-->服务端: Request GET /main.php 65 | 服务端-->客户端: Response Welcome 66 | Note over 客户端,服务端: .......... 67 | 客户端->服务端: Request GET /login.php 68 | 服务端->客户端: Response 包含上一次使用的登录名的表单页面 69 | ``` 70 | 71 | ### JavaScript 中操作 Cookie 72 | 73 | #### Pure JavaScript 74 | 75 | > 参考:http://www.runoob.com/js/js-cookies.html 76 | 77 | ```javascript 78 | // 新增一条 cookie,注意:cookie 是有大小限制,约为 4k 79 | // 格式固定:=[; expires=][; path=<作用路径>][; domain=<作用域名>] 80 | // 除了键值以外其余属性均有默认值,可以省略 81 | // expires 表示 cookie 失效的时间,默认为关闭浏览器时 82 | // path 表示 cookie 生效的路径,默认为当路径 83 | // / /foo.php /abc/foo.php 84 | // /foo /bar/abc.php 85 | // domain 表示 cookie 生效的域名,默认为当前域名 86 | 87 | document.cookie = 'name=value; expires=Tue, 10 Oct 2017 16:14:47 GMT; path=/; domain=zce.me' 88 | // 获取全部 cookie 89 | console.log(document.cookie) 90 | // => 'key1=value1; key2=value2' 91 | // 得到的结果是字符串,需要自己通过字符串操作解析 92 | ``` 93 | 94 | #### jQuery plugin 95 | 96 | https://github.com/carhartl/jquery-cookie 97 | 98 | #### without jQuery 99 | 100 | https://github.com/js-cookie/js-cookie 101 | 102 | ## Session 103 | 104 | 由于 Cookie 是服务端下发给客户端**由客户端本地保存**的。换而言之客户端可以在本地对其随意操作,包括删除和修改。如果客户端随意伪造一个 Cookie 的话,对于服务端是无法辨别的,就会造成服务端被蒙蔽,构成安全隐患。 105 | 106 | 于是乎就有了另外一种基于 Cookie 基础之上的手段:**Session** 107 | 108 | ![session-structure](media/session-structure.png) 109 | 110 | Session 区别于 Cookie 一个很大的地方就是:Session 数据存在了服务端,而 Cookie 存在了客户端本地,存在服务端最大的优势就是,不是用户想怎么改就怎么改了。 111 | 112 | Session 这种机制会更加适合于存放一些属于用户而又不能让用户修改的数据,因为客户端不再保存具体的数据,只是保存一把“钥匙”,伪造一把可以用的钥匙,可能性是极低的,所以不需要在意。 113 | 114 | ![session-flow](media/session-flow.png) 115 | 116 | > http://php.net/manual/zh/session.examples.basic.php 117 | 118 | ### 猜数字游戏 119 | 120 | > 参考:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash 121 | > 122 | > 注意:这里是 JavaScript 的实现,经供参考 123 | > 124 | > 建议:好好看看里面写的一些内容 125 | 126 | 我想让你创建一个可以猜数字的游戏,它会在 1~100 以内随机选择一个数, 然后让玩家挑战在10轮以内猜出这个数字,每一轮都要告诉玩家正确或者错误, 如果出错了,则告诉他数字是低了还是高了,并且还要告诉玩家之前猜的数字是什么。 一旦玩家猜测正确,或者他们用完了回合,游戏将结束。 游戏结束后,可以让玩家选择再次开始。 127 | 128 | ```html 129 | 130 | 131 | 132 | 133 | 猜数字 134 | 157 | 158 | 159 |

猜数字游戏

160 |

Hi,我已经准备了一个 0 - 100 的数字,你需要在仅有的 10 机会之内猜对它。

161 |
162 | 163 | 164 |
165 | 166 | 167 | ``` 168 | -------------------------------------------------------------------------------- /06-mysql.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | ## 准备工作 4 | 5 | ### 简介 6 | 7 | > 目标: 8 | > - 搞明白什么是数据库 9 | > - 如何通过工具操作数据库 10 | > - 如何通过代码操作数据库 11 | 12 | 数据库就是数据的仓库,用来按照特定的结构去组织和管理我们的数据,有了数据库我们就可以更加方便、便捷的操作(C / R / U / D)我们需要保存的数据。 13 | 14 | 不管是什么数据库,最终都是将数据存到文件(硬盘)中,只是存储的格式不同于文本文件。 15 | 16 | 一个 Excel 文件就可以看做一个数据库: 17 | 18 | ![excel-files](media/excel-files.png) 19 | 20 | 一个 Excel 文件中可以包含多个数据表: 21 | 22 | ![excel-tables](media/excel-tables.png) 23 | 24 | ### 安装与配置 25 | 26 | 在开发领域我们存储数据一般都是使用专门的数据库服务器专门提供的数据库服务,如果需要让自己的机器也可以提供数据库服务,那么就需要安装特定的数据库服务器软件,这种类型的软件也有很多: 27 | 28 | - Oracle 29 | - MySQL 30 | - SQL Server 31 | 32 | 这里我们选择一个很常见的数据库服务器软件:MySQL 33 | 34 | MySQL 的安装同样建议采用解压版(目的是了解那些自动安装过程都做了哪些事) 35 | 36 | > 下载地址:https://dev.mysql.com/downloads/mysql/ 37 | 38 | #### 安装过程 39 | 40 | 1. 解压到纯英文路径 41 | 42 | 2. 解压目录添加 `my.ini` (可选) 43 | 44 | > 参考: 45 | > - http://www.cnblogs.com/Ray-xujianguo/p/3322455.html 46 | > - https://gist.github.com/hanjong/1205199 47 | > - https://dev.mysql.com/doc/refman/5.5/en/mysqld-option-tables.html 48 | 49 | ``` 50 | [mysqld] 51 | # MySQL 安装目录 52 | basedir=C:/Develop/mysql 53 | # 数据文件所在目录 54 | datadir=C:/Develop/mysql/data 55 | ``` 56 | 57 | 3. 以管理员身份运行 CMD 执行以下命令,安装一个 MySQL 服务 58 | 59 | ```shell 60 | # 定位到安装目录下的 bin 文件夹 61 | $ cd /bin 62 | # 初始化数据所需文件以及获取一个临时的访问密码 63 | $ mysqld --initialize --user=mysql --console 64 | # 将 MySQL 安装为服务 可以指定服务名称 65 | $ mysqld --install MySQL 66 | ``` 67 | 68 | 4. 登入 MySQL 服务器,重置密码 69 | 70 | ```shell 71 | # 先通过用户名密码进入 MySQL 操作环境 72 | $ mysql -u root -p 73 | Enter password: # 输入临时密码 74 | 75 | # 设置数据库访问密码,一定要加分号 76 | mysql> set password for root@localhost = password('123'); 77 | # 新版本使用以下语句 78 | mysql> alter user 'root'@'localhost' identified by '新密码'; 79 | ``` 80 | 81 | 82 | ## 基础操作 83 | 84 | ### 数据库管理工具 85 | 86 | 数据库管理工具本质上就是一个使用数据库服务器软件(Server)提供的服务的数据库客户端(Client)。 87 | 88 | #### 命令行工具 89 | 90 | 一般如果只是简单操作数据库,推荐使用 MySQL 内置的命令行工具完成: 91 | 92 | 通过命令行运行解压目录下 `bin` 目录中的 `mysql.exe`: 93 | 94 | ```shell 95 | # 定位到 bin 目录 96 | $ cd <解压目录>/bin 97 | # 运行 mysql,-u 指定数据库用户名,-p 指定密码 98 | $ mysql -u root -p wanglei 99 | # 一般不建议在命令中填写密码,因为这样会暴露你的密码,一般只加一个 -p 但是不给值 100 | $ mysql -u root -p 101 | Enter password: # 这时会要求你输入密码 102 | ``` 103 | 104 | 进入 MySQL 客户端的 REPL 环境过后,可以通过标准的 SQL 语句操作数据库。 105 | 106 | 常见的操作指令: 107 | 108 | ```sql 109 | mysql> show databases; -- 显示全部数据库 110 | mysql> create database ; -- 创建一个指定名称的数据库 111 | mysql> use ; -- 使用一个数据库,相当于进入指定的数据库 112 | mysql> show tables; -- 显示当前数据库中有哪些表 113 | mysql> create table (id int, name varchar(20), age int); -- 创建一个指定名称的数据表,并添加 3 个列 114 | mysql> desc ; -- 查看指定表结构 115 | mysql> source ./path/to/sql-file.sql -- 执行本地 SQL 文件中的 SQL 语句 116 | mysql> drop table ; -- 删除一个指定名称的数据表 117 | mysql> drop database ; -- 删除一个指定名称的数据库 118 | mysql> exit|quit; -- 退出数据库终端 119 | ``` 120 | 121 | #### 可视化工具 122 | 123 | 如果需要复杂的操作,推荐 Navicat Premium 124 | 125 | > 下载地址:http://www.navicat.com.cn/download/navicat-premium 126 | > 127 | > 这是一个付费软件,可以免费试用 14 天 128 | 129 | ### 基本概念 130 | 131 | - 数据库 132 | - 数据库服务软件 133 | - 数据的仓库 134 | - 表 135 | - 字段 —— 指的就是列 136 | - 字段类型 —— 指的就是列能够存储的数据种类 137 | - int 138 | - char() 139 | - varchar() 140 | - date 141 | - decimal 142 | - 数据库查询:指的是操作数据库的过程(查、增、删、改) 143 | - 数据库查询语言:SQL 144 | 145 | ### 基本查询语句 146 | 147 | #### 查询 148 | 149 | ```sql 150 | -- 查询数据 151 | -- select 字段[, 字段2] from 表名 152 | select id, name, birthday from users; 153 | select `id`, `title`, `name` from `users`; 154 | 155 | -- 通配 * 找到表中所有列 156 | select * from users; 157 | ``` 158 | 159 | #### 增加 160 | 161 | ```sql 162 | -- 新增数据 163 | -- 插入全部字段 164 | insert into users values (null, '王五', 0, '2020-12-12', '12312'); 165 | 166 | -- 指定字段 167 | insert into users (name, gender, avatar) values ('王五', 0, '12312'); 168 | ``` 169 | 170 | #### 修改 171 | 172 | ```sql 173 | -- 更新数据 174 | update users set name = '麻子', gender = 0 175 | ``` 176 | 177 | #### 删除 178 | 179 | ```sql 180 | -- 删除 181 | -- 删除语句必须指定条件 182 | delete from users 183 | ``` 184 | 185 | #### 筛选条件 186 | 187 | 子语句,意思是不能单独执行,必须配合 删除 修改 查询 语句 188 | 189 | ```sql 190 | delete from users where id = 6 191 | delete from users where id = 6 and gender = 0 192 | delete from users where id = 6 or gender = 0 193 | delete from users where id > 6 194 | delete from users where id in (4, 5) 195 | ``` 196 | 197 | ### 常见查询函数 198 | 199 | - 总条数 —— count 分页功能,查询总页数 200 | - 最大值、最小值 —— max/min 201 | - 平均值 —— avg 202 | 203 | ```sql 204 | select fn(field1) from table 205 | ``` 206 | 207 | ### 分页查询数据 208 | 209 | 子语句 210 | 211 | `limit , ` 212 | 213 | `skip = (page - 1) * length` 214 | 215 | ## PHP 操作数据库 216 | 217 | 如何在 PHP 代码中操作数据库是我们能否在自己的程序中使用数据库的核心。 218 | 219 | > 数据库扩展:http://php.net/manual/zh/refs.database.php 220 | 221 | 如果需要使用 MySQLi 扩展,需要在 php.ini 文件中打开这个扩展(解除注释) 222 | 223 | ```php 224 | // 假定数据库用户名:root,密码:wanglei,数据库:baixiu 225 | $connection = mysqli_connect("localhost", "root", "wanglei", "baixiu"); 226 | 227 | if (!$connection) { 228 | // 如果连接失败报错 229 | die('

Connect Error (' . mysqli_connect_errno() . ') ' . mysqli_connect_error() . '

'); 230 | } 231 | 232 | $sql = "select * from users"; 233 | $result = mysqli_query($connection, $sql); 234 | 235 | // 查询数据填充到关联数组 236 | while ($row = mysqli_fetch_assoc($result)) { 237 | echo $row["id"] . " - " . $row["username"]; 238 | } 239 | 240 | // 释放结果集 241 | mysqli_free_result($result); 242 | 243 | mysqli_close($connection); 244 | ``` 245 | 246 | > - mysqli: 247 | > - http://php.net/manual/zh/book.mysqli.php 248 | > - http://www.runoob.com/php/php-ref-mysqli.html 249 | > - *pdo:http://php.net/manual/zh/ref.pdo-mysql.php 250 | 251 | ### 执行查询语句 252 | 253 | ### 执行非查询语句 254 | 255 | ## 案例 256 | 257 | 基于数据库的增删改查 258 | 259 | ### 列表功能 260 | 261 | - 查询数据 262 | 263 | ### 增删改 264 | 265 | - 增删改数据 266 | 267 | ## 全部配置总结 268 | 269 | ``` 270 | 1. Apache 271 | 1.1. PHPIniDir 272 | 1.2. LoadModule 273 | 1.3. AddType 274 | 275 | 2. PHP 276 | 2.1. extension_dir 277 | 2.2. php_mbstring.dll 278 | 2.3. php_mysqli.dll 279 | 2.4. data.timezone 280 | 2.5. upload_max_filesize 281 | 2.6. post_max_size 282 | ``` 283 | 284 | -------------------------------------------------------------------------------- /07-ajax.md: -------------------------------------------------------------------------------- 1 | # AJAX 2 | 3 | ## 概述 4 | 5 | > Web 程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问。 6 | 7 | ![browser-server](media/browser-server.png) 8 | 9 | 在此之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获得服务端的数据: 10 | 11 | - 地址栏输入地址,回车,刷新 12 | - 特定元素的 href 或 src 属性 13 | - 表单提交 14 | 15 | 这些方案都是我们无法通过或者很难通过代码的方式进行编程(对服务端发出请求并且接受服务端返回的响应),**如果我们可以通过 JavaScript 直接发送网络请求,那么 Web 的可能就会更多,随之能够实现的功能也会更多,至少不再是只能开发“单机游戏”。** 16 | 17 | > 对 XXX 进行编程指的就是用代码的方式操作它。 18 | 19 | ### Google Suggest 20 | 21 | AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 [Google Suggest](http://google-suggest.tumblr.com/),是在浏览器端进行网络编程(发送请求、接收响应)的技术方案,它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载页面。让 Web 更能接近桌面应用的用户体验。 22 | 23 | ![google-suggest](media/google-suggest.png) 24 | 25 | 说白了,**AJAX 就是浏览器提供的一套 API,可以通过 JavaScript 调用,从而实现通过代码控制请求与响应。实现网络编程。** 26 | 27 | > 能力不够 API 凑。 28 | 29 | ## 快速上手 30 | 31 | 使用 AJAX 的过程可以类比平常我们访问网页过程 32 | 33 | ```javascript 34 | // 1. 创建一个 XMLHttpRequest 类型的对象 —— 相当于打开了一个浏览器 35 | var xhr = new XMLHttpRequest() 36 | // 2. 打开与一个网址之间的连接 —— 相当于在地址栏输入访问地址 37 | xhr.open('GET', './time.php') 38 | // 3. 通过连接发送一次请求 —— 相当于回车或者点击访问发送请求 39 | xhr.send(null) 40 | // 4. 指定 xhr 状态变化事件处理函数 —— 相当于处理网页呈现后的操作 41 | xhr.onreadystatechange = function () { 42 | // 通过 xhr 的 readyState 判断此次请求的响应是否接收完成 43 | if (this.readyState === 4) { 44 | // 通过 xhr 的 responseText 获取到响应的响应体 45 | console.log(this) 46 | } 47 | } 48 | ``` 49 | 50 | ### readyState 51 | 52 | 由于 `readystatechange` 事件是在 `xhr` 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次,所以我们有必要了解每一个状态值代表的含义: 53 | 54 | | readyState | 状态描述 | 说明 | 55 | | ---------- | ---------------- | ------------------------------------ | 56 | | 0 | UNSENT | 代理(XHR)被创建,但尚未调用 `open()` 方法。 | 57 | | 1 | OPENED | `open()` 方法已经被调用,建立了连接。 | 58 | | 2 | HEADERS_RECEIVED | `send()` 方法已经被调用,并且已经可以获取状态行和响应头。 | 59 | | 3 | LOADING | 响应体下载中, `responseText` 属性可能已经包含部分数据。 | 60 | | 4 | DONE | 响应体下载完成,可以直接使用 `responseText`。 | 61 | 62 | #### 时间轴 63 | 64 | ```flow 65 | s=>start: UNSENT 66 | o1=>operation: OPENED 67 | o2=>operation: HEADERS_RECEIVED 68 | o3=>operation: LOADING 69 | e=>end: DONE 70 | 71 | s(right)->o1(right)->o2(right)->o3(right)->e 72 | ``` 73 | 74 | ```flow 75 | s=>start: 初始化 76 | o1=>operation: 建立连接 77 | o2=>operation: 接收到响应头 78 | o3=>operation: 响应体加载中 79 | e=>end: 加载完成 80 | 81 | s(right)->o1(right)->o2(right)->o3(right)->e 82 | ``` 83 | 84 | ```javascript 85 | var xhr = new XMLHttpRequest() 86 | console.log(xhr.readyState) 87 | // => 0 88 | // 初始化 请求代理对象 89 | 90 | xhr.open('GET', 'time.php') 91 | console.log(xhr.readyState) 92 | // => 1 93 | // open 方法已经调用,建立一个与服务端特定端口的连接 94 | 95 | xhr.send() 96 | 97 | xhr.addEventListener('readystatechange', function () { 98 | switch (this.readyState) { 99 | case 2: 100 | // => 2 101 | // 已经接受到了响应报文的响应头 102 | 103 | // 可以拿到头 104 | // console.log(this.getAllResponseHeaders()) 105 | console.log(this.getResponseHeader('server')) 106 | // 但是还没有拿到体 107 | console.log(this.responseText) 108 | break 109 | 110 | case 3: 111 | // => 3 112 | // 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整 113 | 114 | // 在这里处理响应体不保险(不可靠) 115 | console.log(this.responseText) 116 | break 117 | 118 | case 4: 119 | // => 4 120 | // 一切 OK (整个响应报文已经完整下载下来了) 121 | 122 | // 这里处理响应体 123 | console.log(this.responseText) 124 | break 125 | } 126 | }) 127 | ``` 128 | 129 | 通过理解每一个状态值的含义得出一个结论:一般我们都是在 `readyState` 值为 `4` 时,执行响应的后续逻辑。 130 | 131 | ```javascript 132 | xhr.onreadystatechange = function () { 133 | if (this.readyState === 4) { 134 | // 后续逻辑...... 135 | } 136 | } 137 | ``` 138 | 139 | ### 遵循 HTTP 140 | 141 | 本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求任然是 HTTP 请求,同样符合 HTTP 约定的格式: 142 | 143 | ```javascript 144 | // 设置请求报文的请求行 145 | xhr.open('GET', './time.php') 146 | // 设置请求头 147 | xhr.setRequestHeader('Accept', 'text/plain') 148 | // 设置请求体 149 | xhr.send(null) 150 | 151 | xhr.onreadystatechange = function () { 152 | if (this.readyState === 4) { 153 | // 获取响应状态码 154 | console.log(this.status) 155 | // 获取响应状态描述 156 | console.log(this.statusText) 157 | // 获取响应头信息 158 | console.log(this.getResponseHeader('Content-Type')) // 指定响应头 159 | console.log(this.getAllResponseHeaders()) // 全部响应头 160 | // 获取响应体 161 | console.log(this.responseText) // 文本形式 162 | console.log(this.responseXML) // XML 形式,了解即可不用了 163 | } 164 | } 165 | ``` 166 | 167 | > 参考链接: 168 | > - https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest 169 | > - https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest 170 | 171 | ## 具体用法 172 | 173 | ### GET 请求 174 | 175 | > 通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的 `?` 参数传递。 176 | 177 | ```javascript 178 | var xhr = new XMLHttpRequest() 179 | // GET 请求传递参数通常使用的是问号传参 180 | // 这里可以在请求地址后面加上参数,从而传递数据到服务端 181 | xhr.open('GET', './delete.php?id=1') 182 | // 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传 183 | xhr.send(null) 184 | xhr.onreadystatechange = function () { 185 | if (this.readyState === 4) { 186 | console.log(this.responseText) 187 | } 188 | } 189 | 190 | // 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据 191 | ``` 192 | 193 | ### POST 请求 194 | 195 | > POST 请求过程中,都是采用请求体承载需要提交的数据。 196 | 197 | ```javascript 198 | var xhr = new XMLHttpRequest() 199 | // open 方法的第一个参数的作用就是设置请求的 method 200 | xhr.open('POST', './add.php') 201 | // 设置请求头中的 Content-Type 为 application/x-www-form-urlencoded 202 | // 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据 203 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') 204 | // 需要提交到服务端的数据可以通过 send 方法的参数传递 205 | // 格式:key1=value1&key2=value2 206 | xhr.send('key1=value1&key2=value2') 207 | xhr.onreadystatechange = function () { 208 | if (this.readyState === 4) { 209 | console.log(this.responseText) 210 | } 211 | } 212 | ``` 213 | 214 | ### 同步与异步 215 | 216 | 关于同步与异步的概念在生活中有很多常见的场景,举例说明。 217 | 218 | > - 同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去做别的事,只是等待 219 | > - 异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待 220 | 221 | `xhr.open()` 方法第三个参数要求传入的是一个 `bool` 值,其作用就是设置此次请求是否采用异步方式执行,默认为 `true`,如果需要同步执行可以通过传递 `false` 实现: 222 | 223 | ```javascript 224 | console.log('before ajax') 225 | var xhr = new XMLHttpRequest() 226 | // 默认第三个参数为 true 意味着采用异步方式执行 227 | xhr.open('GET', './time.php', true) 228 | xhr.send(null) 229 | xhr.onreadystatechange = function () { 230 | if (this.readyState === 4) { 231 | // 这里的代码最后执行 232 | console.log('request done') 233 | } 234 | } 235 | console.log('after ajax') 236 | ``` 237 | 238 | 如果采用同步方式执行,则代码会卡死在 `xhr.send()` 这一步: 239 | 240 | ```javascript 241 | console.log('before ajax') 242 | var xhr = new XMLHttpRequest() 243 | // 同步方式 244 | xhr.open('GET', './time.php', false) 245 | // // 同步方式 执行需要 先注册事件再调用 send,否则 readystatechange 无法触发 246 | // xhr.onreadystatechange = function () { 247 | // if (this.readyState === 4) { 248 | // // 这里的代码最后执行 249 | // console.log('request done') 250 | // } 251 | // } 252 | xhr.send(null) 253 | // 因为 send 方法执行完成 响应已经下载完成 254 | console.log(xhr.responseText) 255 | console.log('after ajax') 256 | ``` 257 | 258 | 演示同步异步差异。 259 | 260 | 一定在发送请求 `send()` 之前注册 `readystatechange`(不管同步或者异步) 261 | 262 | - 为了让这个事件可以更加可靠(一定触发),一定是先注册 263 | 264 | 了解同步模式即可,切记不要使用同步模式。 265 | 266 | 至此,我们已经大致了解了 AJAX 的基本 API 。 267 | 268 | ### 响应数据格式 269 | 270 | > 提问:如果希望服务端返回一个复杂数据,该如何处理? 271 | 272 | 关心的问题就是服务端发出何种格式的数据,这种格式如何在客户端用 JavaScript 解析。 273 | 274 | #### XML 275 | 276 | 一种数据描述手段 277 | 278 | 老掉牙的东西,简单演示一下,不在这里浪费时间,基本现在的项目不用了。 279 | 280 | 淘汰的原因:数据冗余太多 281 | 282 | #### JSON 283 | 284 | 也是一种数据描述手段,类似于 JavaScript 字面量方式 285 | 286 | 服务端采用 JSON 格式返回数据,客户端按照 JSON 格式解析数据。 287 | 288 | > **注意**: 289 | > - 不管是 JSON 也好,还是 XML,只是在 AJAX 请求过程中用到,并不代表它们之间有必然的联系,它们只是数据协议罢了。 290 | > - 不管服务端是采用 XML 还是采用 JSON 本质上都是将数据返回给客户端 291 | > - 服务端应该设置一个合理的 Content-Type 292 | 293 | ### 处理响应数据渲染 294 | 295 | > 模板引擎: 296 | > - artTemplate:https://aui.github.io/art-template/ 297 | 298 | 模板引擎实际上就是一个 API,模板引擎有很多种,使用方式大同小异,目的为了可以更容易的将数据渲染到HTML中 299 | 300 | ### 兼容方案 301 | 302 | XMLHttpRequest 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替 303 | 304 | ```javascript 305 | var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP') 306 | ``` 307 | 308 | ## 封装 309 | 310 | ### AJAX 请求封装 311 | 312 | > 函数就可以理解为一个想要做的事情,函数体中约定了这件事情做的过程,直到调用时才开始工作。 313 | 314 | ```javascript 315 | /** 316 | * 发送一个 AJAX 请求 317 | * @param {String} method 请求方法 318 | * @param {String} url 请求地址 319 | * @param {Object} params 请求参数 320 | * @param {Function} done 请求完成过后需要做的事情(委托/回调) 321 | */ 322 | function ajax (method, url, params, done) { 323 | // 统一转换为大写便于后续判断 324 | method = method.toUpperCase() 325 | 326 | // 对象形式的参数转换为 urlencoded 格式 327 | var pairs = [] 328 | for (var key in params) { 329 | pairs.push(key + '=' + params[key]) 330 | } 331 | var querystring = pairs.join('&') 332 | 333 | var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP') 334 | 335 | xhr.addEventListener('readystatechange', function () { 336 | if (this.readyState !== 4) return 337 | 338 | // 尝试通过 JSON 格式解析响应体 339 | try { 340 | done(JSON.parse(this.responseText)) 341 | } catch (e) { 342 | done(this.responseText) 343 | } 344 | }) 345 | 346 | // 如果是 GET 请求就设置 URL 地址 问号参数 347 | if (method === 'GET') { 348 | url += '?' + querystring 349 | } 350 | 351 | xhr.open(method, url) 352 | 353 | // 如果是 POST 请求就设置请求体 354 | var data = null 355 | if (method === 'POST') { 356 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') 357 | data = querystring 358 | } 359 | xhr.send(data) 360 | } 361 | 362 | ajax('get', './get.php', { id: 123 }, function (data) { 363 | console.log(data) 364 | }) 365 | 366 | ajax('post', './post.php', { foo: 'posted data' }, function (data) { 367 | console.log(data) 368 | }) 369 | ``` 370 | 371 | > **委托**:将函数作为参数传递就像是将一个事情交给别人,这就是委托的概念 372 | 373 | ### jQuery 中的 AJAX 374 | 375 | jQuery 中有一套专门针对 AJAX 的封装,功能十分完善,经常使用,需要着重注意。 376 | 377 | > 一个你会用我会用他会用到的点,就一定有一个已经封装好的 378 | 379 | > 参考: 380 | > - http://www.jquery123.com/category/ajax/ 381 | > - http://www.w3school.com.cn/jquery/jquery_ref_ajax.asp 382 | 383 | #### $.ajax 384 | 385 | ```javascript 386 | $.ajax({ 387 | url: './get.php', 388 | type: 'get', 389 | dataType: 'json', 390 | data: { id: 1 }, 391 | beforeSend: function (xhr) { 392 | console.log('before send') 393 | }, 394 | success: function (data) { 395 | console.log(data) 396 | }, 397 | error: function (xhr) { 398 | console.log(xhr) 399 | }, 400 | complete: function (xhr) { 401 | console.log('request completed') 402 | } 403 | }) 404 | ``` 405 | 406 | 常用选项参数介绍: 407 | 408 | - url:请求地址 409 | - type:请求方法,默认为 `get` 410 | - dataType:服务端响应数据类型 411 | - contentType:请求体内容类型,默认 `application/x-www-form-urlencoded` 412 | - data:需要传递到服务端的数据,如果 GET 则通过 URL 传递,如果 POST 则通过请求体传递 413 | - timeout:请求超时时间 414 | - beforeSend:请求发起之前触发 415 | - success:请求成功之后触发(响应状态码 200) 416 | - error:请求失败触发 417 | - complete:请求完成触发(不管成功与否) 418 | 419 | #### $.get 420 | 421 | GET 请求快捷方法 422 | 423 | `$.get(url, data, callback)` 424 | 425 | #### $.post 426 | 427 | POST 请求快捷方法 428 | 429 | `$.post(url, data, callback)` 430 | 431 | #### 全局事件处理 432 | 433 | > http://www.jquery123.com/category/ajax/global-ajax-event-handlers/ 434 | 435 | #### 自学内容(作业) 436 | 437 | - `$(selector).load()` 438 | - `$.getJSON()` 439 | - `$.getScript()` 440 | 441 | 简单概括以上方法的作用和基本用法。 442 | 443 | ## 跨域 444 | 445 | ### 相关概念 446 | 447 | 同源策略是浏览器的一种安全策略,所谓同源是指**域名**,**协议**,**端口**完全相同,只有同源的地址才可以相互通过 AJAX 的方式请求。 448 | 449 | 同源或者不同源说的是两个地址之间的关系,不同源地址之间请求我们称之为**跨域请求** 450 | 451 | 什么是同源?例如:http://www.example.com/detail.html 与一下地址对比 452 | 453 | | 对比地址 | 是否同源 | 原因 | 454 | | ---------------------------------------- | ---- | ------- | 455 | | http://api.example.com/detail.html | 不同源 | 域名不同 | 456 | | https://www.example.com/detail.html | 不同源 | 协议不同 | 457 | | http://www.example.com:8080/detail.html | 不同源 | 端口不同 | 458 | | http://api.example.com:8080/detail.html | 不同源 | 域名、端口不同 | 459 | | https://api.example.com/detail.html | 不同源 | 协议、域名不同 | 460 | | https://www.example.com:8080/detail.html | 不同源 | 端口、协议不同 | 461 | | http://www.example.com/other.html | 同源 | 只是目录不同 | 462 | 463 | ### 解决方案 464 | 465 | 现代化的 Web 应用中肯定会有不同源的现象,所以必然要解决这个问题,从而实现跨域请求。 466 | 467 | > 参考:http://rickgray.me/solutions-to-cross-domain-in-browser 468 | 469 | #### JSONP 470 | 471 | **JSON** with **P**adding,是一种借助于 `script` 标签发送跨域请求的技巧。 472 | 473 | 其原理就是在客户端借助 `script` 标签请求服务端的一个动态网页(php 文件),服务端的这个动态网页返回一段带有函数调用的 JavaScript 全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。 474 | 475 | 以后绝大多数情况都是采用 JSONP 的手段完成不同源地址之间的跨域请求 476 | 477 | 客户端 http://www.zce.me/users-list.html 478 | 479 | ```html 480 | 481 | ``` 482 | 483 | 服务端 http://api.zce.me/users.php?callback=foo 返回的结果 484 | 485 | ```javascript 486 | foo(['我', '是', '你', '原', '本', '需', '要', '的', '数', '据']) 487 | ``` 488 | 489 | **总结一下**:由于 XMLHttpRequest 无法发送不同源地址之间的跨域请求,所以我们必须要另寻他法,script 这种方案就是我们最终选择的方式,我们把这种方式称之为 JSONP,如果你不了解原理,先记住怎么用,多用一段时间再来看原理。 490 | 491 | 问题: 492 | 493 | 1. JSONP 需要服务端配合,服务端按照客户端的要求返回一段 JavaScript 调用客户端的函数 494 | 2. 只能发送 GET 请求 495 | 496 | > 注意:JSONP 用的是 script 标签,更 AJAX 提供的 XMLHttpRequest 没有任何关系!!! 497 | 498 | jQuery 中使用 JSONP 就是将 dataType 设置为 jsonp 499 | 500 | 其他常见的 AJAX 封装 库: 501 | 502 | - Axios 503 | 504 | #### CORS 505 | 506 | Cross Origin Resource Share,跨域资源共享 507 | 508 | ```php 509 | // 允许远端访问 510 | header('Access-Control-Allow-Origin: *'); 511 | ``` 512 | 513 | 这种方案无需客户端作出任何变化(客户端不用改代码),只是在被请求的服务端响应的时候添加一个 `Access-Control-Allow-Origin` 的响应头,表示这个资源是否允许指定域请求。 514 | 515 | > https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS 516 | 517 | ## XMLHttpRequest 2.0 518 | 519 | > 暂作了解,无需着重看待 520 | 521 | HTML5 中对 XMLHttpRequest 类型全面升级,更易用,更强大 522 | 523 | ### onload / onprogress 524 | 525 | ```javascript 526 | var xhr = new XMLHttpRequest() 527 | xhr.open('GET', './time.php') 528 | xhr.onload = function () { 529 | // onload readyState === 4 530 | console.log(this.readyState) 531 | } 532 | xhr.onprogress = function () { 533 | // onprogress readyState === 3 534 | console.log(this.readyState) 535 | } 536 | xhr.send(null) 537 | ``` 538 | 539 | ### FormData 540 | 541 | 以前 AJAX 操作只能提交字符串,现在可以提交 二进制 的数据 542 | 543 | ### 案例 544 | 545 | 异步上传文件 546 | 547 | ## 参考链接 548 | 549 | - http://www.w3school.com.cn/ajax/index.asp 550 | - https://aui.github.io/art-template/zh-cn 551 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web-essential-docs 2 | 3 | > Web 开发基础手册,以 PHP 为例 4 | -------------------------------------------------------------------------------- /diagram.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /media/1505988989079.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505988989079.png -------------------------------------------------------------------------------- /media/1505995465005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505995465005.png -------------------------------------------------------------------------------- /media/1505995651671.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505995651671.png -------------------------------------------------------------------------------- /media/1505996335305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505996335305.png -------------------------------------------------------------------------------- /media/1505997349857.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505997349857.png -------------------------------------------------------------------------------- /media/1505998898900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505998898900.png -------------------------------------------------------------------------------- /media/1505999461533.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1505999461533.png -------------------------------------------------------------------------------- /media/1506001971125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506001971125.png -------------------------------------------------------------------------------- /media/1506004529597.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506004529597.png -------------------------------------------------------------------------------- /media/1506132097583.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506132097583.png -------------------------------------------------------------------------------- /media/1506132675132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506132675132.png -------------------------------------------------------------------------------- /media/1506136421939.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506136421939.png -------------------------------------------------------------------------------- /media/1506312179608.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/1506312179608.png -------------------------------------------------------------------------------- /media/apache-php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/apache-php.png -------------------------------------------------------------------------------- /media/apache-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/apache-process.png -------------------------------------------------------------------------------- /media/browser-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/browser-server.png -------------------------------------------------------------------------------- /media/cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/cookie.png -------------------------------------------------------------------------------- /media/excel-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/excel-files.png -------------------------------------------------------------------------------- /media/excel-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/excel-tables.png -------------------------------------------------------------------------------- /media/google-suggest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/google-suggest.png -------------------------------------------------------------------------------- /media/http-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/http-protocol.png -------------------------------------------------------------------------------- /media/http-req-res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/http-req-res.png -------------------------------------------------------------------------------- /media/multiple-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/multiple-network.png -------------------------------------------------------------------------------- /media/netstat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/netstat.png -------------------------------------------------------------------------------- /media/request-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/request-structure.png -------------------------------------------------------------------------------- /media/request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/request.png -------------------------------------------------------------------------------- /media/response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/response.png -------------------------------------------------------------------------------- /media/session-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/session-flow.png -------------------------------------------------------------------------------- /media/session-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/session-structure.png -------------------------------------------------------------------------------- /media/single-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/single-network.png -------------------------------------------------------------------------------- /media/web-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/web-essential-docs/116dbda06e27673653bbc7e72adc6ae8df06eb22/media/web-server.png --------------------------------------------------------------------------------