php修练手册

php走的是主流路线他的语法跟其他的都差不多
语言特点是快
主要用于开发各种类型的网站程序
比如 内容管理系统CMS 论坛 商城/购物网站 

细节功能
1.收集表单数据
2.生成动态网页
3.字符串处理
4.动态输入图像 ?
5.处理服务器端文件???
6.与数据库交互
7.会话跟踪 ?
8.处理XML文件支持大量的网络协议 ?
9.服务器的其它相关操作

php 是脚本语言不需要事先编译,可直接在服务端运行

脚本语言不需要编译,可以直接用,由解释器来负责解释。

脚本也是一种语言,其同样由程序代码组成。
注:脚本语言一般都有相应的脚本引擎来解释执行。 他们一般需要解释器才能运行。JAVASCRIPT,ASP,PHP,PERL都是脚本语言。C/C++编译、链接后,可形成独立执行的exe文件。

php可以运行在windows linux(各个版本)等系统上
吐槽一下:很多语言都是如此,不知道为什么要单独拿来说


php开发的是网站程序
得对 web网页 工作原理有点了解

web网页浏览器 中输入网址
既会展示内容

众多普通人通过网线访问 到 该网站机房电脑上的 某个文件
可是直接访问 文件夹 肯定是访问不通的

因此计算机还需要安装一个web服务器(server软件)

一个管理资源并为用户提供服务的计算机软件
通常为
1.文件服务器(能使用户在其它计算机访问文件)
2.数据库服务器
3.应用程序服务器

运行以上软件的计算机,可称为网络主机


server软件有很多个
此处我们使用的是 apache 

用户通过网线访问机房 通过 apache 取出机房 指定文件 返回给用户

文件 包括 
字符串 
各种格式的图片

但是如果你请求的是一个 .php文件
                 不是 .html了
                图片也不是.jpg了

.php 文件 返回给 浏览器
肯定是 无法 正确打开并执行的

所以 直接返回 .PHP 是不行的
因此 
除了 server软件 
我们应该再安装一个软件

叫 php 应用服务器
用户请求流程会被改变

1.用户通过网线请求 即将返回 .php文件时

2.server软件(apache) 会识别 
浏览器和自身都不能执行 . php文件
但是它会发现 
它有个好哥们  php 应用服务器
能够执行

因此就会交给 php 应用服务器 
到时候 php 应用服务器 
就会去解释执行 php 代码


3.php 应用服务器 
解释执行 php 代码 后
将代码(应该是成了.HTML/json数据流) 转给 apache

4.apache再返回给用户的浏览器

因此php代码实际上是在
服务器端的
php 应用服务器 上运行的


当用户不是请求数据
而是注册之类的操作进行保存表单数据

服务器肯定要将其保存起来
直接放在一个文件里吗
1.用户比较多,注册的内容比较多,不利于管理
2.也不安全,在文件夹里面

所以我们要借助其他类的工具
进行保存用户数剧

服务器的数据管理比较多
比如我们要用的 
mysql数据库管理系统


也就是说
用户通过网线通过apache 
提交数据到php应用服务器
接收到表单数据后 直接给 mysql
mysql 会帮我们全部搞定
不用去管了


主机
操作系统
server软件 apache
文件夹 存放静态纯天然资源和.php文件/或其他
php应用服务 解释 .php文件来使浏览器互通
mysql 存放表数据 包括二进制图片 视频


且 其中的 mysql 数据库管理系统
可以安装在另外单独的一台计算机里面

当然全部安装到一起也没有关系


互联网有很多服务器
用户通过的是 url 地址
格式  为
 http://host[:port][abs_path]

http://表示要通过HTTP协议来定位网络资源
host表示合法的Internet主机域名或者IP地www.baidu.com
port指定一个端口号,可以省略默认为80 :80
abs_path被请求资源的位置

http://www.baidu.com/ 行
其实访问的是
http://www.baidu.com:80/ 行


https://www.baidu.com:80/ 不行
http://www.baidu.com:801/ 不行

主机其实都是 IP 地址
那为什么能够通过 域名  www.baidu.com

找到他呢
因为这中间还有一个 DNS 的服务器
他会记录 IP地址 所对应的 域名
202.108.22.5        www.baidu.com

本地
计算机/C盘/Windows/System32/drivers/etc

hosts 文件无后缀记事本打开

会在本地先找找 有没有这个域名
假如有
就会
192.168.0.107 tp6demo.wo2n.com
进入前面的局域网地址 访问局域网或本机上开启的server


PHP运行环境的安装

我们需要安装 3 个软件
1)web服务器      apache
2)PHP应用服务器    PHP     解释,执行我们便写的PHP程序 
3)数据库 管理系统/服务器  MySQL 保存用户数据

三个软件往往不是初学者能够独立配置的
因此我们推荐初学者
使用 phpstudy   集成环境(直接解压 纯绿色 无安装)
或者 wampserver 集成环境
或者 lamp       集成环境
或者 xamp       集成环境

IP地址       域名         本 地/机
127.0.1.1    location   安装路径/www文件夹里的 demo.php
  

更改 默认主页
默认主页在本机的:
	安装路径/www文件夹里的 demo.php

默认主页时Apache配置好的
要更改主页 需要更改apache里面的配置文件
//失败方式
配置文件在本机的:
	安装路径/Apache/conf/httpd.conf

打开>使用记事本
#号是配置文件里面的注释
不要管他删掉也没事

ctrl + f 搜索 www

搜索到如下内容
DocumentRoot  "D:\phpStudy\PHPTutorial\WWW"

改完           "C:\web" 

之后重启服务器会访问失败 
因为你还没有权限

此时访问 "D:\phpStudy\PHPTutorial\WWW" 依旧正常

注意 此情况只在集成环境下会出现

作者对此进行了改良
同目录下 还有一个 vhosts.conf 配置文件

这个文件只有10行左右
在这里面改

<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
#指定我的 默认主页 虚拟目录(专业术语)
  <Directory "D:\phpStudy\PHPTutorial\WWW">
#让我有权限访问 我的 默认主页 虚拟目录(专业术语)
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
#          允许 拒绝
    Allow from all
#   允许       所有(拒绝会出现forbidden)
    Require all granted
  </Directory>
#文件夹
</VirtualHost>

注意
httpd.conf 配置文件中 引入了 
vhosts.conf 配置文件

因此我们修改的完全没有生效
并且被覆盖回去了
所以之前的页面仍旧可以打开
但是我们 想改的页面却打开不了

如果将 vhosts.conf 配置文件 改为
<VirtualHost _default_:80>
DocumentRoot "C:\web"
</VirtualHost>

重启 PHPstudy
此时我们能够访问 c/web/aa.html
因为 html 是不需要权限的
但是我们还是无法访问  c/web/aa.php
(此处可以访问 应该是浏览器有缓存)
未知bug

总结 :修改默认主页 虚拟目录(专业术语)
在 vhosts.conf 配置文件
修改两个路径即可 一个是路径 一个是允许访问该路径

该内容文件如下:
<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
#指定我的 默认主页 虚拟目录(专业术语)
  <Directory "D:\phpStudy\PHPTutorial\WWW">
#让我有权限访问 我的 默认主页 虚拟目录(专业术语)
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
#          允许 拒绝
    Allow from all
#   允许       所有(拒绝会出现forbidden)
    Require all granted
  </Directory>
#文件夹
</VirtualHost>


由于
上面修改默认主页 
虚拟目录(专业术语) 
讲的非常失败

在  apache/conf/httpd.conf 中
我们注释掉 

#Inclue conf/vhosts.conf

此时不再受 同级目录下的 vhosts.conf 配置覆盖干扰

重新 ctrl+f 搜索 到 www

//定位并且修改
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
#指定我的 默认主页 虚拟目录(专业术语)
  <Directory />
#让我有权限访问 我的 默认主页 虚拟目录(专业术语)
    Options +Indexes +FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
#          允许 拒绝
    Allow from all
#   允许       所有(拒绝会出现forbidden)
    Require all granted
  </Directory>
#文件夹

//为
DocumentRoot "C:\web" 

#此时没有权限 

访问默认主页 
虚拟目录(专业术语) 会提示 Forbidden(拒绝访问)
默认主页 为空白 不执行

#重新更改为
DocumentRoot "C:\web" 
#指定我的 默认主页 虚拟目录(专业术语)
  <Directory />
#让我有权限访问 我的 默认主页 虚拟目录(专业术语)
    Options +Indexes +FollowSymLinks +ExecCGI
# 让你的目录结构 以 选项/索引 的方式展现出来
#(危险极大) 上线时千万不要用 +代表启动 -代表不启用
    AllowOverride All
#  分布式部署需要这句话 现在用不到
    Order allow,deny
#   顺序 先允许 再拒绝
# 按 Order顺序  后者的权限 会覆盖前者的权限 以后者为主
    Allow from all
#   允许       所有
  # deny from all
#   拒绝      所有(拒绝会出现forbidden)
    Require all granted
#   所有权限都包含(默认)
</Directory>
#文件夹

可以正常访问
此时访问 目录 会展示所有文件
此时访问 文件 会尝试执行

若注释掉下面这句话
#Options +Indexes +FollowSymLinks +ExecCGI
# 让你的目录结构 以 选项 / 索引的方式展现出来

则没有展示所有文件的权限 会提示Forbidden(无权限)


例题一:
Order allow,deny#只与这里的顺序有关
Allow from all
# 允许所有请求访问

例题二:
Order allow,deny#只与这里的顺序有关
Allow from all
Deny from all
# 拒绝所有请求访问

练习三:
Order allow,deny#只与这里的顺序有关
Deny from all
Allow from all
# 拒绝所有请求访问



练习四:
<Directory "C:/PHP/Apache/htdocs">
	Order deny, allow
	Allow from 192.168.101.50
	Deny from 192.168
</Directory>
# 拒绝192.168开头的IP访问
但不包括 192.168.101.50 例外


练习五:
<Directory "C:/PHP/Apache/htdocs">
	Order deny, allow
	Allow from 192.168.101.50
	Deny from all
</Directory>
# 只允许 192.168.101.50 访问


练习六:
<Directory "C:/PHP/Apache/htdocs">
	Order allow, deny
	Allow from 192.168
	Deny from 192.168.101.50
</Directory>
# 允许 192.168 访问
# 除 192.168.101.50 例外

只会应用在考试上
        或内部网各部门的权限


//更改 默认首页(专业术语)

在 /PHPstudy/Apache/conf/httpd.conf 中

ctrl+f 搜索 DirectoryIndex

找到如下内容
<IfModule dir_module>
DirectoryIndex index.html index.php index.htm l.php
</IfModule>

没有 预设的 index.html index.php index.htm l.php 主页
则打开 该目录索引(有权限展示目录时)
开发时肯定是 不展示目录(危险极大)

有预设的打开预设的 可以放好多个优先级从前往后

#修改默认端口


在 /PHPstudy/Apache/conf/httpd.conf 中

ctrl+f 搜索 Listen 
会找到 如下内容

 Listen 80

80可更改为 其它 例如81
但是81不能有服务

可以同时启动监听 多个 端口范围 0-65535
Listen 82
Listen 81

两个都能访问到同一个地址

查看端口的占用情况
桌面cmd 进入黑窗口
netstat -ano

我们已经懂得
php集成环境的搭建
和启动知识相关的基本概念了

在主页中我们要如何写 php 语言呢

类似于 js 的 
<script>
...js代码
</script> 
一样

在 php语言中 我们称之为 定界符
<?php  
...php代码
?>

比如 打印 Hello World
<?php
echo "Hello World";
?>

注意: 如果整个页面都是php代码
      可以省略 php定界符的结束符
       (推荐省略)


php定界符 可简写为(需要在php配置文件中开启支持短标记)
打开 phpstudy 软件
其它选项菜单>打开配置文件>php-ini
此处配置文件里分号表示注释
       apache是#号

ctrl+f 搜索 short_open_tag 
搜到如下内容
short_open_tag = Off
改成
short_open_tag = On
重启 即可开启 短标记简写模式

<?
...php代码
?>
生效

因为php是脚本语言所以需要定界符



//需求  一台主机配置多个域名网站(虚拟主机(专业术语))
输入 ww.baidu.com 打开web1的网站
输入 ww.sina.com  打开web2的网站

此时 web1 为www
     web2 为www的副本

此时相当于我有两个网址(主机)
    电脑还是一台电脑

那么我们该如何配置呢

在 phpstudy/apache/conf/vhosts.conf 中

<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
ServerName location #域名(默认可不写)
DirectoryIndex index.php #默认主页可不写
  <Directory "D:\phpStudy\PHPTutorial\WWW">
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>


全部内容如上 
VirtualHost 虚拟主机的意思
你只要再配置一遍以上内容就是一台新主机
<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
ServerName www.baidu.com #域名
DirectoryIndex aa.php #默认主页
  <Directory "D:\phpStudy\PHPTutorial\WWW">
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>


<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW副本"
ServerName ww.sina.com #域名
DirectoryIndex bb.php #默认主页
  <Directory "D:\phpStudy\PHPTutorial\WWW副本">
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>

此时一台电脑就开起了两个 网站
但是我们没有配置域名 解析 DNS(是无需重启的)

本地
计算机/C盘/Windows/System32/drivers/etc/hosts
127.0.0.1        www.baidu.com
127.0.0.1        ww.sina.com

在 phpstudy/apache/conf/httpd.conf 中 223行
注释掉 
原先不用的 location默认的
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
重启 此时两个 网址
1  www.baidu.com
2  ww.sina.com
均会指向 127.0.0.1 

apache启动失败
语法查错功能 复制文件夹目录
D:\phpStudy\PHPTutorial\Apache\bin 回车
启用apache 语法检查

D:\phpStudy\PHPTutorial\Apache\bin>httpd -t
会显示出 错误在哪一行 什么错误


虚拟目录 www文件夹(配权限)
虚拟主机  (虚拟目录+域名+dns配置同ip 多域名)
默认主页 index.php
站点     保存所有与网站有关的素材

至此 环境搭建完毕

接下来开始细究语法

所有的语言分为两种
一个是 解释型语言 每次访问编译一次
一个是 编译型语言 每次访问的都是保存好的可执行文件

所有的语言都会经历这么一个过程
源代码>编译>可执行文件>执行

根据 有没有 保存好可执行文件(储存中)
来区分
有 编译型
否 解释型

php执行过程
读取源代码>词法分析>语法分析>语法分析>编译(生成opcode代码)
>opcode代码保存在内存中

从这个角度来说是编译型的语言(即使是保存在的内存中)

php 4.几的时候是解释型的
    5.几以后全部生成中间代码保存可执行文件


php中使用注释
单行注释: //
          # 也是单行注释
多行注释: /*  */
同js

php中使用打印
echo '我是输出不是控制台打印是js的print打印'; 常用
print('会额外返回一个值 成功返回1 失败返回0看不到因为失败既报错');

echo print('输出返回的值');//1或失败返回0看不到因为失败既报错

print_r(Arr) 输出数组
var_dump('会连带数据类型长度')//string(27)

在 utf8下一个汉字占3个字节


接下来正式进入语法的学习
如果有js的基础学起来会比较容易

注意以下几点

变量 => $变量
       
      
var a=''; //js写法
$a='';    //php写法

<?php
	$a='tom';
	$b='a'
	echo $$b; //tom

$b 是 'a'  
$'a'又是一个变量的开始
所以是 tom

$$b 这种写法被称为可变变量
     可以将任意一个变量的字符串名字
     当做 变量 来使用
   
//地址传递  &取地址的符号
$a=&$b //将b的地址赋给a
	a 被改变时 b 也会被改变
	a 成了 b 的壳子
	a 被 b 夺舍了

销毁变量(?为什么要销毁它)
用unset()来销毁变量

<?php
$a=10;
unset($a);
echo $a;//Undefined

unset($a);销毁的是变量名 a 
	  10没有被销毁
          10是随后由php的垃圾回收机制销毁 这一点同js
          10 也可以在 销毁前 赋地址 给别人 
	   此时10不会被回收
	   且别人等于10
 
比如
<?php
$a=10;
$b=&$a
unset($a);
echo $b;//10
//夺舍后消除自己的躯壳 



常用命令 DOS命令

切换盘符 盘符+冒号
           C:  回车

进入目录 cd 目录地址
        cd [ C:]/xx/xx/xx/index.html

查看 apache 的版本号
 在 Apache/bin/ 中
目录 cmd  
之后
httpd -v 回车
结果:server verion: apache/2.4.23(win32)
     server buil: Jul 1 2016 16:42:20

检测运行环境(语法是否有报错)
(有可能修改配置文件后 apache 运行起不来了)

还是在上面的目录中 

httpd  -t 回车
结果:syntax ok

以上操作的apache 
依靠的其实是 
apache/bin 下的 httpd.exe






PHP查看版本号
在 PHPTutorial/php/php-7.0.12-nts 中
目录 cmd
php -v 回车

吐槽:(目录都写了版本号 还查看干什么)

结果:
PHP 7.0.12 (cli) (built:Oct 13 2016 11:04:07) (NTS)
Copyight (c) 1997-2016 The PHP Group
Zend Engine v3.0.0 Copyight (c) 1998-2016 Zend Technologies


卖个关子
启动phpstudy
在 www/demo.php中
<?php 
echo '锄禾日当午<br>';
echo '汗滴河下土<br>';
?>

浏览器访问 www/demo.php 该位置
结果:
	锄禾日当午
	汗滴河下土

重新修改为:(去掉一个分号)
	<?php 
	echo '锄禾日当午<br>';
	echo '汗滴河下土<br>'
	?>

结果还是:
	锄禾日当午
	汗滴河下土
并没有报错

但是

第三次修改为:(都去掉分号)
	<?php 
	echo '锄禾日当午<br>'
	echo '汗滴河下土<br>'
	?>

结果就会报错:
	Parse error:syntax error,unexpected'echo'
 
此时会出现一个神奇的现象
我们又修改为: (去掉一个分号 和 ?>定界符 )
	<?php 
	echo '锄禾日当午<br>';
	echo '汗滴河下土<br>'

结果报错 :
	Parse error:syntax error,unexpected'echo'


这是因为 ?> 定界符里 有一个隐藏的分号


作业
phpstudy安装完毕后,有一个phpmyadmin的管理数据库,
默认情况下,放在虚拟目录下,这样不合理
请重新配置虚拟主机访问phpmyadmin

输入 phpmyadmin.com打开phpmyadmin管理软件

// 1.在PHPTutorial/www 中
剪切整个文件夹 phpMyAdmin

到 /PHPTutorial 中

// 2.在 PHPTutorial/Apache/conf/vhosts.conf 中
<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\WWW"
  <Directory "D:\phpStudy\PHPTutorial\WWW">
    Options -Indexes -FollowSymLinks +ExecCGI
# - 没有主页 拒绝展开目录
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>

<VirtualHost _default_:80>
DocumentRoot "D:\phpStudy\PHPTutorial\phpMyAdmin"
 ServerName phpmyadmin.com
#配置 ip 的域名 这里 需要 额外 配置
#本地
#计算机/C盘/Windows/System32/drivers/etc/hosts
#127.0.0.1                www.baidu.com
#127.0.0.1(ip是不变的)   phpmyadmin.com(域名变了就是新主机)
#虚拟主机  (虚拟目录+域名+dns配置同ip 多域名)
#域名访问的目录不一样
#ip 默认域名
  <Directory "D:\phpStudy\PHPTutorial\phpMyAdmin">
    Options -Indexes -FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>

定义常量 
常量 
有变量就有常量
在整个运行过程中
如果值保持不变 , 就是常量

那么 常量怎么去定义呢

只要是运行过程中不变 
那就把它称之为常量

常量固定不变那怎么去定义常量呢

用define关键字
叫define()函数吧
定义常量

写法
// 在 1.php 中
<?php
define('NAME','tom'); //定义常量
define('PI',3.14);    //定义常量
echo NAME,PI;      //输出两个 逗号隔开

语法:
	define(常量名,值)
常量名一般是不带$符的
$表示什么 表示变量的开始
而且常量一般来说都是大写 
我就写小写怎么滴
没怎么的 没事
但一般来说常量都是大写
写小写没有关系啊
这只是一种习惯

常量名没有 $ 符
因为$符表示的是变量

常量名推荐使用大写
你写小写 有没有关系
没关系
这只是一种习惯

常量名区分大小写
如果不想区分大小写有没有办法?
我们百度一下

搜索  php 常量不区分大小写

php7中常量不区分大小写和区分大小写用法

define('name','value',false)//(默认)区分大小写
define('name','value',true)//     不区分大小写


define(常量名,值,是否区分大小写(默认区分))

实例:
<?php
define('PI',3.14,true);    //定义常量
echo  '<br>',Pi;  


我能不能使用特殊字符
来做变量名呢
使用特殊字符来做变量名?
特殊字符不能做变量名
那如果我想这么做怎么办呢

那就必须使用 constant (译:常量)关键字

<?php
define('%-%','tom');
echo constant ('%-%'); 
//通过使用 constant 使用特殊字符命名的常量
如果使用特殊字符做常量名必须使用
constant  获取

常量是什么
是固定不变的是吧
那常量能改值吗
很显然他是要报错的
常量  某某 已经被定义了
就是不能被定义

那么这时候有可能出现什么情况呢
有可能你写代码 写代码
写了好几百行了 
你忘了 (某个常量)是不是定义了

所以这时候我是不是这样
我判断一下常量是否定义

那么我怎么来判断

判断常量是否定义 
是 defined 不是 define

if (!defined('NAME')) {       //如果没定义
    define('NAME', 'berry');  //则定义常量
}                             //否则啥也不干


好下面我们说
除此以外
除了 define 定义常量以外
还可以使用什么呢 
还可以使用 const ( constant(调用的常量名为特殊字符时使用) 的缩写)

//const 常量
const NAME = 'tom';
echo NAME;           //tom

那这时候肯定有人会说
const NAME = 'tom'; 定义 常量
和  
define('NAME','tom'); 定义 常量 
有什么区别呢

我说了你也不会明白
等到后面我会跟大家说
是有区别的

那么下面我小结一下
现在你只要知道
定义常量有几种方式?
答: 两种

1.定义常量有两种方式
   const NAME = 'tom'; 
   和
   define('NAME','tom',false);//(默认false区分大小写)

2.常量在整个运行过程中值保持不变
  如果变了它就要报错了

3.常量名为特殊字符时 
  必须使用constant 才能正确获取

4.if (!defined('NAME')) {       //如果没定义
    define('NAME', 'berry');    //则定义常量
  }                             //否则啥也不干

defined用来判断常量 是否已经定义过了


刚才呢
我们说的是自定义的
就自己定义的常量

那像我们说的预定义常量
同学们什么叫预定义啊
通过中区老祖宗取得这个名字
预: 预先定义好的
别人定义好的是吧
那预定义常量有哪些
我给你们

<?php
echo
PHP_VERSION, 'PHP版本号', '<br>', //7.2.1
PHP_OS, 'PHP操作系统', '<br>',    //WINNTPHP
PHP_INT_MAX, '返回当前计算机能够计算的最大值', '<br>';
               //2147483647

这就是PHP预先定义好的常量

下面再来一个魔术常量
魔术常量啥意思呢

请问
常量能不能变化
答:  不能

不能是吧
这个魔术常量啊
他是可以变化的常量

有人说他是变量吧 
他跟变量不一样

魔术常量告诉你啊
你不能自己定义

比如说我自己定义一个魔术常量
对不起
你没那个本事

名字 系统自带的
魔术常量是 系统定义好的
就那么几个
不能乱动

echo
__LINE__, '<br>', //当前行号。
__FILE__, '<br>', //读取当前所在文件的绝对路径
__DIR__, '<br>';// 读取当前所在目录的绝对路径

暂时掌握这三个就可以了 (总共有7个)

其他的不管他了


好了
不管是常量也好
还是变量也好
不管是常量还是变量
他都是内存中的一个空间

既然有这个空间
那这个空间是干么用的
这个好理解吧

你比如说咱们这个空间
咱们这个屋这个房子
是不是空间啊

如果这个房子是空间
那这个房子是干嘛呢

如果这个房子是养猪的
那就叫猪圈
它空间干嘛用的对吧

养鸡的
叫鸡窝

如果说有人住的
就叫卧室吧

开饭馆
那就是餐厅

每个空间
是不是有每个空间的
作用嘛

那么
数据是一个空间

那么
空间是不是也是他的数据类型啊

空间是干嘛用的
数据是干嘛用的

数据类型就是空间的存储的
数据是属于某一个类型的

也就是这个称之为数据类型

数据类型 :
    存储数据空间的类型

那么我们说
这个数据类型有两种
    数据类型有两种

一种是强类型
一种是弱类型

强类型和弱类型有什么区别
js是强类型还是弱类型啊

答 弱类型
哦
js 弱类型
那什么是强类型
什么是弱类型呢

隐式转换
类如 1+''

就由数值变成了字符串


那隐式转换就是弱类型吗
好~来 !
我来为你们解释一下啊

我这有个杯子和一个罐

这有个罐是不是空间呐
存储东西的
是不是一个变量啊
是变量吧

我现在问你我这个罐儿

是糖罐
还是银罐啊

水罐?
如果我放糖
他就是糖罐

如果我放盐
他就是盐罐

那么这个罐
就是弱类型的

他的类型完全取决于
我放什么东西

这个罐什么类型
完全取决于我放什么东西

我放一种
这一种类型就是什么

这一种数据类型 就是什么 
就是弱类型


关于强类型的解释
第二种
我告诉你
这是糖罐

你就不能放盐

此时这里的盐的数据类型
这就是强类型

js里面
注意了
js里面说
var x=10;
那 x就是数字类型对不对
为什么是数字类型

因为啊 你里面放的是数字

x什么类型完全取决于我放的什么东西
我放数字他就是数字类型的
我放字符串他就是字符串类型的
也就是说这个罐里面
它放的是盐放的是糖
完全取决于我后面放什么东西

我放某种数据  他就是某种数据的类型
这种存放数据的空间 被称为 弱类型


那么比如说
像java c语言
int 
比如这么写
int x; 
表示这是什么类型
我就想放一个字符串
x='aa'
对不起 报错了

像 int x; 就是什么类型
就是强类型

这就是弱类型和强类型的区别



弱类型(指内存空间)什么意思呢
他(指内存空间)的类型
完全取决于里边放的什么值

强类型是声明的这个空间只用来存储某个类型


那php也是弱类型的
搞半天和js一样用
那费半天劲解释干什么
弄得我以为php是强类型的

当然PHP7.0往后已经支持了强类型
后面我会带大家说
我告诉你
这里面只能放数字
对不起
你不能放其它东西
7.0以后已经支持了强类型
当前使用的就是7.0之后的

也就是说PHP7.0以后
既支持强类型
又支持弱类型

好了

那么我们下面继续往下说
既然
那么数据类型有哪些呢

PHP呢他的类型有这么8种
他的类型
标量类型 4
1.boolean(布尔型)

同学们下面 我们说布尔型
是不是就是真假啊
布尔型我们有什么可说的
就是输出布尔型

假设我要输出一个 true 结果是个啥
<?php
echo true;//1
如果看到是  1 的话
你哪知道是1还是true啊
我们能不能输出布尔型啊
有同学说false
<?php
echo false;// 输出空
false你看输出啥
他输出的是空 啥也没有

echo 只能输出数字和字符串
print 也只能输出数字和字符串

如果你想输出布尔类型必须
使用 var_dmmp()

var_dump(false);//bool(false)

print 和echo 不具备解析布尔类型的功能
一般建议使用 var_dump(false);












2.integer(整型 整数数字 极限范围正负2的31次方 )
PHP_INT_MAX获取整型最大值
PHP支持8,10,16进制的整数
<?php
echo
$num1 = 10, '<br>', //默认10进制            10
$num1 = 010, '<br>', //这种写法 就是8进制    8
$num1 = 0x10 //这种写法就是16进制           16
;
进制关于转换的绝招
让计算机 PHP 帮我算 
有几个单词记好之后 就可以调用 php自带的转换函数

进制     缩写        单词
2        bin        binary 比如可执行文件 .exe
8        oct       octonary
10       dec      decimalist
16       hex     hexdecimalist

用法
<?php
//进制转换
     10 2
echo decbin(123);//10进制转2进制  1111011
注释:有的并没有两两组合函数 没有就没有呗

这还有众多例子
<?php
echo
decbin(123), '<br>',     //10转 2 1111011
bindec(1111011), '<br>', //2 转10   123
dechex(123), '<br>',     //10转16   7b
hexdec('7b'), '<br>',    //16转10   123
decoct(123);             //10转 8   173
...


3.float(浮点型.也称作 double)
浮点 是什么
这个点是浮的
有浮点 必有什么 必有定点

当然啦 php里面没有提供定点这一个说法
我们后边肯定会学到的
我会带大家学到的
浮点 就是小数 嘛
你们理解的就是小数 嘛
但是你有没有想过这个小数叫浮点呐

这样说吧
按你们的理解叫小数
那么小数 
这个小数在内存里面保存的是近似值
浮点数在内存里面保存的是近似值
对吧 你们学过吧
js应该也是一样的
既然是近似值能不能作比较

问大家一下
var_dump() 是打印他的数据类型
var_dump(0.9 === (1 - 0.1));//bool(true)

相等吧
再输出一下
var_dump(0.1 === (1 - 0.9));//bool(false)
应该是相等的 但是却为false 表示它不相等

由于浮点数在内存中 
存储的是近似值
所以有时候它可能算对了
有时候它可能算错了
js也是这样的
所以浮点数要不要参与比较
浮点数不能去参与比较

不要试图去做小数的相等判断

如果我就想比较怎么办呢

比如 0.312 
     0.3121 
多一位都不等
如果浮点数要比较必须确定比较的位数
小数都是近似值 近似值保留几位啊

必须确定比较的位数

//对比小数
var_dump(bccomp(0.1, 1 - 0.9, 5));//5 只比较小数后5位数
//结果 int(0) 表示两个相等

bccomp(a,b,5)//a大返回-1
	        b大返回1 
	       相等返回0
//只比较小数点后5位

有人说 0.1,0.9哪来小数点后的5位
这不是只有1位吗
你把0.1转为 2进制试试
你把0.9转为 2进制试试

绝对不止只是一味
小数点后不止一位嘞
很多很多位嘞
而且有可能是无限循环

看就看5位
只要5位 相等我就算你相等了

好了这是浮点数啊

浮点数的范围比整型要大

提醒:
 如果 整型有没有范围啊
 整型有 范围 
如果整型范围超出了
如果一个整数超出了整型范围怎么办呢
会自动的转成浮点数(型)
浮点型的范围比整数要大的多得多

整型的范围是这样子的
以我们32位为例的话应该是 正负2的31次方
大概是20多亿

这20多超出了怎么办
他就会转换为浮点型
浮点型的范围很广
大概是
308*10的38次方 挺大的没见过那么大的数

这是浮点型


4.string(字符串)
下面我们来说字符串

唉~ 字符串就是重点
请问在js中字符串
是不是有单引号字符串
和双引号字符串
有区别吗
答: 没有

没区别是吧 
那么在php里面会怎么样呢
我们来看一看
<?php
echo
'锄禾日当午<br>',//锄禾日当午
"锄禾日当午<br>";//锄禾日当午

大家看有没有什么区别
看不出来?
确实没啥区别 

.......  那你着重讲,我还以为有区别


但是下面我改进一下
<?php
$name = 'tom';
echo '$name'; //$name 单引号被解释为字符串
echo "$name"; //tom 双引号被解释为变量
echo $name; //tom

所以说双引号里面的变量是不是进行运算啊
单引号是真正的字符串它就是字符串
双引号里面的变量要做替换明白意思吧

在PHP中单引号字符串和双引号字符串
是有区别的
单引号字符串是真正的字符串
双引号字符串要解析字符串中的变量

例题 如上

有问题吗 
没问题我们
下面再做个作业出个例题

--双引号输出不了常量吗 可能不能吧

$name = 'tom';
echo '$name是我的名字'; //$name是我的名字
echo "$name是我的名字"; //报错
echo "$name 是我的名字"; //tom 是我的名字 残留空格
echo "{ $name}是我的名字"; //{ tom}是我的名字 残留空格
echo "{$name}是我的名字"; //tom是我的名字 完美输出
echo "${name}是我的名字"; //tom是我的名字 完美输出
echo "是我的名字$name";//tom是我的名字 完美输出
   //变量在 最后 {} 可省略

echo '毛主席说:\'上课不要睡觉\'';//毛主席说:'上课不要睡觉'
//在引号中输出引号 

echo '文件保存在c:\\';//文件保存在c:\
//在引号中输出\ 

输出字符串
如果有一长串字符串需要我输出
有意大串需要我输出 
那怎么办呢
我要输出
我要输出什么呢
我要输出

需要使用字符串定界符
echo <<<shi
...要输出的字符串
(吐槽)也不全是字符啊 html标签转义了啊
shi;

实况举例
<?php
echo <<<shi
//字符串 定界符 开始

<strong>锄禾日当午</strong>
<em>汗滴河下土</em>
<u>谁知盘中餐</u>

//字符串 定界符 结束 这区间全当字符串 包括这句注释的话
shi;
?>

但是有人说
既然你这个是当成字符串
那到底是双引号字符串
还是双引号字符串呢

你不是说你是字符串定界符吗
PHP字符串是不是有两种呢

你说我是单引号还是双引号
自己去试一下

<?php
$name = 'tom';
echo <<<shi 

$name

shi;
?>


$name是会被 字符串定界符解析的
	    html标签也会

但是 $name 也能不被解析
<?php
$name = 'tom';
echo <<<'shi'  //此处加单引号 叫 nowdoc
	       //不加默认双引号 叫  heredoc
	       //会解析$name变量

$name

shi;
?>

字符串定界符必须由 <<< 开头 后面跟的是标识符
echo <<<'shi'(不解析变量)
...                         
'shi';                        
 
echo <<<shi(解析变量)
...
shi;

如果
echo <<<shi
<p>我是文字</p>
 shi;//此处加空格 必须顶格写

老师的没报错  
我的直接报错

字符串定界符的结束符必须顶格写
前面不需有任何的空白字符

字符串定界符分为两种
heredoc 不写默认 双引号 解析变量
nowdoc       写单引号   不解析变量

到此为止基本类型就说完了
基础类型有四种
四种标量类型
1.boolean(布尔型) var_dum(false) 才能显示出来 
2.integer(整型) 范围比浮点小 超过转为浮点
3.float(浮点型,也称作 double) 不可比较
4.string(字符串) 双引号解析变量  单引号不行
  echo <<<shi   ....输出大量字符串   shi;












复合类型 2种
那么现在我们说这个复合类型
一个是数组
一个是对象

1.array(数组)
<?php
//1.索引数组的声明
$stu = array('tom', 'berry', 'ketty');
// 被称为索引数组['tom', 'berry', 'ketty']
// 为什么有对象形式?{0:'tom',1: 'berry',2:'ketty'}

echo $stu;//echo 不能输出数组
print_r($stu);// print_r 输出数组结构
//Array ( 
//[0] => tom 
//[1] => berry 
//[2] => ketty )

var_dump($stu); // var_dump 也能输出数组结构
//array(3) { 
//[0]=> string(3) "tom" 
//[1]=> string(5) "berry" 
//[2]=> string(5) "ketty" }

echo $stu[0];//tom 展示数组里的一个值可以用 echo
echo $stu[1];//berry
echo $stu[2];//ketty


//除了通过   数字   做下标
//还可以通过 字符串 做下标 关联数组

//2.关联数组 前面是索引数组
$emp = array('name' => '李白', 'sex' => '男', 'age' => 22);
print_r($emp);//还是使用 print_r 输出数组
//Array ( 
//[name] => 李白 
//[sex] => 男 
//[age] => 22 )

echo $emp['name'];//李白
echo $emp['sex'];//男
echo $emp['age'];//22
?>

好 那么我来给你们小结一下
在PHP中数组有两种形式:

索引数组:用整数做下标,默认从零开始 后面依次加 1
$stu = array('tom', 'ketty');
和
关联数组:用字符串做下标, 通过 => 符号将下标和值关联起来
$emp = array('name' => '李白','age' => 22);

同学们下面我来
同学们
做几个作业
做完这几个作业我们吃饭去

练习 说
写出数组的下标
<?php
$array = array(1 => 'a', 'b', 'c', 'd');
//从 a(初始下标) 改成 1 之后 后面依次 +1
print_r($array);
//Array ( 
//[1] => a 
//[2] => b 
//[3] => c 
//[4] => d )

$array = array('a', 2 => 'b', 'c', 5 => 'd');
//从 b(非初始下标) 改成 2 之后 后面不会改变
print_r($array);
//Array ( 
[0] => a 
[2] => b 
[3] => c 
[5] => d )




再来一个 
$array = array('a', 'name' => 'b', 'c', 'sex' => 'd');
//指定了的 就是指定的  不指定的还是默认的++(不包括指定了的)
print_r($array);
//Array ( 
//[0] => a 
//[name] => b 
//[1] => c 
//[sex] => d )

$array = array(1 => 'a', 1 => 'b', 1 => 'c', 'd');
// 如果指定相同的下标 后面的会覆盖掉前面的值
print_r($array); 
//Array ( 
[1] => c 
[2] => d )

//类似于以下这个例子
$array[1] = 'a';
$array[1] = 'b';
$array[1] = 'c';//最终会等于c
?>


//4.下标的练习
说这个 数组 的 下标 只能是 正整数 和 字符串
那么如果我不是呢
不是会怎么样呢
下面我们一起来看一看
做这么几个练习

$stu[true] = 'tom';
print_r($stu); //Array ( [1] => tom )

转成什么?
转成 1

$stu[false] = 'tom';
print_r($stu); //Array ( [0] => tom )
转成 0

如果是小数呢
比如 12.1呢

$stu[12.1] = 'tom';
print_r($stu); //Array ( [12] => tom )

是12
那么12.9 呢

$stu[12.9] = 'tom';
print_r($stu); //Array ( [12] => tom )

还是12 (取整数部分)
如果放的小数
他会取整数

如果放的是 负数 呢

$stu[-12.9] = 'tom';//负小数 取负整数
print_r($stu); //Array ( [-12] => tom )

是-12 那么换句话说 
负12 负数可以做下标 但是一般不用

如果是字符串 '10' 呢
不用试了 一定是 '10' 
回答错误!!!
$stu['10'] = 'tom';
print_r($stu); //Array ( [10] => tom )

变成数字 10

如果是 '' 空能不能做呢
$stu[''] = 'tom';
print_r($stu); //Array ( [] => tom )

空字符串是可以做下标的

那么如果是 null 呢
$stu[null] = 'tom';
print_r($stu); //Array ( [] => tom )

转成空字符串了吧 和 用 ''做下标一样

短数组语法 

短数组语法就是说
我不用  Array 啊
不用 array 
array这个单词你在js应该是学过的

//5.短数组语法
是简化的方法
 
$stu = ['tom', 'berry', 'ketty'];
print_r($stu); 
//Array ( 
//[0] => tom 
//[1] => berry 
//[2] => ketty )


之前是使用 array() 创建数组实例化 函数
$array = array( 'a',  'name'=>'b', 'c', 'd');

我们 可以 使用 短数组 语法
直接通过 中括号 声明数组

多学一招:
	在PHP7.1中可以支持数组的赋值

这是什么意思呢
是这样的同学们

//6.
我们说用一句话
$num1=10;
$num2=20;

这两个调换一下 
你们怎么调换

你们以前怎么调换的

你们当时是不是引入个中间的变量
但是在php7.1的时候可以这么做的

我知道你们以前怎么做的
$num1 = 10;
$num2 = 20;
$temp=$num1;//中间变量承载 val值 10
$num1=$num2;//把$num2的 val值 20 覆盖 $num1
$num2=$temp;//$num2再 重新赋 $temp 的 val值 的 $num1 的 10
echo $num1, $num2;//2010

加减运算是吧
加减运算也是可以的
你要写两步

你看我一步就把他写完(7.1以上才支持)
应用场景例题(两个数交换)
$num1 = 10;
$num2 = 20;

[$num1,$num2]=[$num2,$num1]

PHP7.1支持数组的赋值
是结构吗 
看似是 实际并不是

这个是 多学一招
同学们上面的是什么数组啊
是不是一维数组啊

那么下面我们说多维数组
啊 多维数组

直接就是二维数组的声明吧

用的最多的就是二维数组的声明
二维数组
数组里面放数组呗

//7.二维数组 
$stu = [ //这是一种 使用 中括号[] 创建的 短数组语法
    ['name'=>'tom','sex'=>'男','age'=>22],
    ['name'=>'berry','sex'=>'女','age'=>23]
];
print_r($stu);//Array ( 
//[0] => Array ( 
		//[name] => tom 
		//[sex] => 男 
		//[age] => 22 ) 


//[1] => Array ( 
		//[name] => berry 
		//[sex] => 女 
		//[age] => 23 ) )

我们让他原样显示 pre 就是原样显示
<pre>格式化打印的数组

重写;例子
//7.二维数组 
$stu = [ //这是一种 使用 中括号[] 创建的 短数组语法
    ['name' => 'tom', 'sex' => '男', 'age' => 22],
    ['name' => 'berry', 'sex' => '女', 'age' => 23]
];
echo '<pre>';//唯一改变的是此处加了 pre标签
print_r($stu);
// Array
// (
//     [0] => Array
//         (
//             [name] => tom
//             [sex] => 男
//             [age] => 22
//         )

//     [1] => Array
//         (
//             [name] => berry
//             [sex] => 女
//             [age] => 23
//         )

// )

那么这个是数组
同学们
除了数组以外还有什么
对象是吧
对象我们后边会专门讲

这是第 2 个

第二个就是对象

2.object(对象)

同学们你看啊
看我们
这写的复合类型有
1.数组
2.对象
那么对象呢

对象在后面专门讲解

在那个章节里面有个面向对象

面向编程

在面向编程里面专门讲这个
在这就不说了
因为在这里面要讲好几天呐
要讲 4 天 啊
这个
在这就不专门去说他了

嗯

现在我给大家小结一下
数组有两种形式
一种索引 一种关联 php里面啊

数组在在PHP里面
有两种形式
一种是索引 一种是关联
我问大家一下
大家已经学过数组了

我问你们一个问题啊
现在讲完了我问一下

数组 它的本质是个变量
对不对
啊?
我不管是 多个还是一个
数组是个变量 
对还是不对
对的吧

那么数组
在内存中是一段连续的空间 
对不对

数组
在内存中是一段连续的空间 
对不对

啊?
你们 
同学们:没有讲

好吧好吧
那我们再稍微说一下吧

再稍微说一下
就这样来说

同学们

这个跟js 讲的是一样的啊
我现在讲的是
和
js是一样的啊

不光是js java也是一样的

$num1 = 10;
$num2 = 20;

请问
$num1 和
$num2
是不是两个空间? 

由.PHP文件应用解释器在计算机中开辟的储存变量的物理内存空间

计算机内存中
划出两道空间啊
大家往这看

计算机内存中
划出两道空间

一个是$num1
一个是$num2 

那么请问
$num1 的物理空间
和
$num2 的物理空间
是不是紧挨着的

是不是?
我再问你
$num1 划分的时候是谁去划分的

是你去划分的吗
不是
是谁去划分的
是操作系统计算机去划分的
那操作系统他把
$num1
和
$num2

划分的是挨在一块吗
不一定
不一定

你别看他写在一块

但是两个它划出的空间不一定是挨着的

不一定是挨着的

他有可能一个在天南一个在地北呢

$stu = ['tom', 'berry', 'kety'];
那么这三个的话
同学们

内存里面它是三个连续的空间
第一个放的是 tom    索引下标 0
第二个放的是 berry  索引下标 1
第三个放的是 kety   索引下标 2

因为他们是连续的所以
下标索引的 0起始 一次 ++

如果不连续它怎么会是 0,1,2 呢

所以我问你是连续的
这里是 0 1 2
听明白这意思吧

所以数组是不是
变量呢?
是变量

数组是变量中一段连续的空间

变量是什么
单独的变量他就是内存中的一段空间

变量他就是内存中的一段空间

但是 数组是什么
数组是 
内存中一段连续的空间

正因为是连续的我才好去编号
你不连续怎么编号呀

学生们:嗯 对!
是不是这个意思

所以我刚才说了
数组是内存中
一段连续空间是对还是不对呢
是对的( js , jsva , php)

好行这是这个
关于数组啊

那么同学们(老师在结束之前似乎有想起了什么)

数组
在什么情况下用的数组啊

数组
在什么情况下用的数组 

我问大家
一个变量
保存几个值

一个变量 保存一个值

我们班如果有100个人
那么我要保存100个人的姓名

需要几个变量
100个变量是不是
那100个变量很显然你记不住啊
是不是

所以这个时候我能不能用一个变量
保存100个值呢

1个变量保存100个值是什么
是数组吧
假设这个数组空间有 几个
有100个

0 到 99 是不是

这一个数组 $stu 就保存多少个值啊
100个

这是不是就是数组啊

这就是一个变量可以保存多个值

这个相当于数组的他的一个特性吧

小结:
    1.数组是内存中一段连续的空间
    正因为它连续才可以通过连续的编号
	来给他做下标嘛 是吧
    这个 呃 啊 

	2.如果要保存这个多个数据
	同一个类型就使用数组\
	比如:
	同一类型保存学生
	同一类型保存dom数据

 关于对象咱们后边再说啊

那么我们再往下小结(老师似乎又想起了什么)

说这个数组 呢
有 
索引数组(用正整数做下标的)
和
关联数组(用字符串做下标的)

如果 
正整数下标是可以指定的
如果 第一个下标 指定是1的话
后面跟着在前面的基础上依次 +1

如果下标是
true    1
false   0
12.9    12取整数部分
-10     -10
-12.9   -12取负整数部分
'10'    10 字符串数字转为数字
''      ''空字符串也可以做下标
null    ''还是空字符串

短数组语法就是说
可以用 中括号 [] 直接声明的这样就可以更简化了
嗯
$a=[ 'a',  'name'=>'b', 'c', 'd'];
初始 $a=array( 'a',  'name'=>'b', 'c', 'd');

二维数组就是
数组里面有两个维度嘛

一维数组 一维数组是
就是一个是吧
一个方向 一个维度

二维数组相当于这个表吧

二维数组相当于这个表

一维数组相当于数轴嘛

一维数组相当于是一个数轴
二维数组相当于是一个表

唉~ 这个没问题吧
这个应该
你们已经是有基础了
(我没有啊 呐喊!)
不说了

那么小结咱们到这

符合和类型
对象
对象我们后面再说

(老师似乎又想起了什么)
我再给大家说一个字符串的

字符串的数组的引用(从7.1开始支持)

就是字符串通过数组的方式引用

这是什么意思呢
就是
echo 'abc'[0];//a 从左边开始取第1个

这是字符串第 0 个
这返回的是不是 a 啊

就是把字符串当做数组啦
那么这样
输出一下
echo 'abc'[-1];//c 从右边开始取第1个
这啥意思呢
哦
-1
是从右边开始取
从左开始是0 
1是从右边开始取

就是字符串
可以通过

数组的方式去 引用/调用


那么下边我们继续往下说

(笔者:呼~)

老师终于讲完了
我还以为他又要想起什么












特殊类型 2种

特殊类型有



1.resource(资源)
资源型 这个呢
同学们
我后边会说

专门会说
有 
  资源 
和
  空 
这两种 才行

两种特殊类型一个是 
资源 resource

一个是

空 null 空就是没有

关于什么是资源
这个用到了
再说
啊
用到的时候再说

现在我说了你肯定还是
迷迷糊糊的

这个用的时候
再说

空就表示 没有
这个好说的啊
这个好说的

好了那么到此

数据类型就 (完了?)

我们就全部说完了

(笔者状态:一脸懵逼 )
不是还有 

四天的对象
和
什么是资源(特殊类型)

两个没讲吗?
???


2.NULL(无类型)
没啥好说的
就一个字 概括
空

有的是到后边
在具体的
事情上

去举例子
现在说完了
你也不是很清楚

所以
我们(现在)主要重点放在哪呢

放在基本类型(标量类型)
4种

整型 整数
布尔 正负
字符 文字
浮点 小数

符合类型
数组 其中包含众多基本类型 
对象

这是重点
其他的先了解一下
后面我会详细去讲

这有

对象
特殊类型

特殊类型有哪些
资源 resource 和
空 null

刚才啊
我们讲了这么多数据类型

那么这些数据类型呢

他们之间可能会出现类型的转换的

类型的转换有两种

第一种
是
1.自动类型转换
还一种是什么呢
还一种是
强制类型转换

自动类型转换我们举个例子
说
if(10)  if(true) 同js

这是第一种

再来

再来
你比如我输出一下
echo '2'-1;// 1 同js
echo 2+'' // 这里 却    报错
echo 2 + '1';//3 不能像js那样 数字转为字符串
             //原因是 PHP中 + 号只有运算功能
	     //这点同 mysql + 不具备拼接字符串的功能

这个时候我们想一下
什么时候自动转换

当提供的类型 和
需要的类型
不一致的时候

会自动进行转换

好了那下面呢
我们说


怎么来做个


2.强制类型转换呢

强制类型转换是这样的 

$num1 = '12';
var_dump($num1);//string(2) "12"

$num1 是字符串对吧
我把强制类型

$num1 = '12';
var_dump($num1, (int)$num1, (float)$num1); 
//string(2) "12",  int(12),  float(12)


语法:(数据类型)数据 
          (int)$num1  

这是强制类型转换
啊 好

在里边最重要的是什么呢

最重要的是其他类型
跟布尔类型
之间的转换

(笔者:我猜和js一样)

var_dump();echo '<br>';

var_dump 没有输出 '<br>' 的功能

if('0')    if(false)   不同于js 转成数字0
if('0.0')  if(true)     同js     字符串 除空都是true
if('00')   if(true)     同js     字符串 除空都是true


根据总结
1.数字转换规律:   除 0/00/0.0 都是true
2.字符串转换规律: 除 '0' 和 空都是true
3.数组            除空都是true

规则 0,空,'0',00,0.0 为假 其它为真


这个是类型转换

变量讲完了
常量讲完了


我们下面就要开始干什么了

就运算了

唉 你看
就这个套路嘛
就这个套路

运算就要讲运算符呗
第一个

算数运算符

算数运算符有这么几个

一元运算符 -(指负) ++ --
二元运算符 + -(指减) * / %

$a=5;
echo -$a;//-5

注意: 在PHP和mysql中 + 号只能做数学运算
      不能做字符串拼接

那同学们就纳闷
那我字符串相加怎么办 mysql 是使用 函数('2','1')//'21'

echo '1'+'2';//3 会自动转为数字 进行算数运算
// 只取数字转换   
//起始不是数字则认为是 0
//全为空 转换失败会报错

++ 和 --
++a 先自增再运算
a++ 先运算再自增

做几个练习

$numk=10;

echo  $numk++;;//10

$numk=10;

echo  ++$numk;;//11



关系运算符
>
>=
<
<=
==
!=

(同学们:全等于呢)
好了同学们
那么这里
还有个
=== 全等
!== 不全等

那么比较运算符的结果是什么
是布尔值吧

这种跟 js
是一样的
有没有同学是不会的
没有
没有那就过了啊


这个跟js是一样的
如果说
那个有问题
可以说出来
没有问题就过了

下面我们说逻辑运算符

逻辑运算符有
&& 与  &  不短路与
|| 或  |  不短路或
!  非  

$a=5;
if($a>10 && ++$a<20){//++$a<20 不会运算
echo '你好吗';//不会输出
}
echo $a;//5


$a=5;
if($a>10 & ++$a<20){//由于写了 &所以 ++$a<20 也会运算
echo '你好吗';//不会输出
}
echo $a;//6


赋值运算符
= 赋值
+= a+=b a=a+b   a+=1 / a++
-=
*=
/=
%=

单纯是赋值

字符串拼接
echo 'a'.'b';//'ab' 相当于 js的+ 输出一个值

讲到这的时候我问同学一下
有的同学看我写的

echo 'a','b';//'a''b'  输出两个值
这两种写法一样吗
竟然是一样的

前面都这样写了好几次了
没有人提出异议

如果我再不提出来 ,一写
echo 'a','b';//输出两个值 

同学们会说 老师你写错了

你脑子立马就乱了

其实这两种写法都是对的


好了
下面这个错误抑制符(@)
错误抑制符就是说
如果有错的话

我强调一下
错误抑制符一般是用来

错误抑制符只对表达式有效

echo ($a*$b);//报错因为没有 $a 和$b
echo @($a*$b);//0 不报错 

这个现在你看不出来有什么作用
和 js的 try 差不多

后面写项目的时候要用它



三元运算符(同js)
同学们学过了吧
语法:
a? 1:2


这个有没有同学不会的
都会
咱么就过啊


下面呢说
null 空 合并运算符(7.0以后才支持的)
相当于 js 的 || 但是 在php使用 || 会报错

讲它呢
我写一个吧


echo $num ?? 1;//不会报错

如果 $num 有不为空 就输出 $num
     否则 输出 1

我去 这和
echo $num || 1;//我试了 会报错

不是一样么

到此咱们的运算符就全部说完了

小结走一下啊

在PHP中
算数运算符主要做算数运算

如果你提供的类型不是数字
他会强制转换为数字

第二个关系运算符
也叫比较运算符
它比较的结果就是true或false


逻辑运算符是用来
连接比价运算符的

我们学的都是短路与
短路或
他的特点
如果你写一个就不短路啊

一般我们用短路的 可稍微提升效率


然后赋值运算符

赋值运算符
由 =
+=  -=  *=  /=  %=

字符串连接符是一个 . 点

啊 是个点

错误抑制符(@)
吧错误给抑制掉

三元运算符 同js
a? 1:2

null空合并运算符 同js的 ||
就是
a = b || 0;//在php中这种写法报错 js不会
$a = $b ?? 0;

 



多学一招
两个经常用来判断的函数



第一个是什么呢
第一个是
isset()
用来判断变量是否被赋值 且不为空
是返回true 不是返回false

null 和 NULL 是一样的不区分大小写的(php中)


第二个
empty() 函数用于检查一个变量是否为空 
	只要能转成false 都是空
	NULL/null/0/'0'/00/0.0/''/[]/{}
        是返回true 不是返回false

echo '<hr>';//下划线 HTML标签


下面我们说一下判断的语法
首先第一个单分支
单分支的语法
if(条件){}
这是单分支

双份支呢
if(条件){}else{}

多分枝
if(条件){

}elseif(条件){//这里与js不同 无空格


}else{}


多路选择
就是case呗
switch(表达式){
 //他跟那个结果是匹配呢
    case 常量1://如果表达式结果=常量1
	//代码块
	break;
    case 常量2:
	//代码块
	break;
    default:
	//代码块
}


同学我问你们这几个语法都用过吗

没有过
但是都讲过吗

讲过就是没做例题是不是

没事首先我们先说语法

先说语法有没有什么问题

除了elseif不加空格其他的都一样


好了 那么下面我们就来做几道练习

就是来巩固这些语法

例题:

1.判断闰年
我输入个年份
一点提交 然后你把
这个年份啊 给我找到

就这么容易的事

首先这个步骤
步骤:
1.创建表单
2.提交数据(好像要来真的了)
3.在服务器判断

第一步先创建一个表单
代码实现

is_numeric( ) 判断数字/能够转为数字的字符串

是返回true 否返回false

首次实战 
前端 后端数据互通
全题解析如下:
//在 1.copy.php 中

	<!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>

<body>
    <form action="" method="post">
        <!-- action="要提交的页面名 不写默认提交给当前页面名" -->
        请输入年份: <input type="text" name='year'><br>
        <input type="submit" name='button' value="判断闰年">
    </form>
    <?php
    // 在本页面上获取 
    //这是什么提交 这是 post 提交 
    //post 提交通过什么获取的 
    // 通过 $_POST  获取的
    // $_POST 这是固定写法 这是一个变量
    //这个变量  $_POST 叫超全局变量

    //但是现在我先不把这个概念抛出来
    //我就告诉你它其实就是一个变量
    //什么类型的变量 它是一个数组
    //这里面保存的是post 提交的数据

    //第一次渲染页面是没有 post 提交的 array(0) { }

    //array(2) { ["year"]=> string(2) "aa" ["button"]=> string(12) "判断闰年" }
    //一点提交是不是又提交到本地
    //是不是又执行一次?

    //这次就有 post 提交了

    //第一次 无人提交不需要展示 $_POST

    //$_POST 是PHP内置的一个变量
    //专门用来保存什么
    //专门用来保存 post 提交的数据的
    //只要是 $符开头的是不是都是变量
    //只要是 $符 开头的 都是变量


    if ($_POST) { //如果不为空
        var_dump('原数组 : ', $_POST); //打印数据类型 及数据
        //这里面你就要开始做他了
        //做什么呢
        //array(2) { ["year"]=> string(2) "aa" ["button"]=> string(12) "判断闰年" }
        //这个数组是个什么数组 是关联数组
        // 我通过 名字是不是 取出 其数组中 对应的 val值了
        //老师在基础班的时候 有没有讲过一定要取名字
        //如果你不给他取名字你是看不到效果的
        //唉? 我取不取名字好像都能显示啊 在基础班的时候
        //也能看得见啊 但是老师肯定会告诉你 一定要取名字
        //如果你不取名字的话 将来到了服务器的时候
        //你是获取不到值的 (使用索引不行么 它是关联数组没有索引?)
        // 肯定说过的你们老师 的确说过
        //因为你在学基础班的时候他偷懒 就不写名字
        //表单一样出来了 干嘛要写名字呢
        //那时候老师肯定会告诉你
        //你现在看上去 好像没事
        //但是将来不行
        //有的同学说 我就不写名字
        //吧name删掉 你不写名字 你提交试试
        //没有名字 有值吗
        //报错了
        //我想起来了 不写name 浏览器默认不提交input 浏览器地址栏看得到 




        $year = $_POST['year']; //我是不是把这个year 取出来
        // 我是不是把 输入的 字符串数据获取到了



        //如果你的年份压根就为空呢
        //那我就输出一句话
        if (!$year) {
            echo '<br>', '您没有输入年份';
        } else { //下输入一堆字母行不行
            //不行一定要输入 数字
            //那么怎么判断是不是一个数字呢
            // 在php中 只要是 is 开头的函数就代表是不是
            //is_numeric() 他是不是一个数字/可转为数字的字符串

            //虽然是数字 但是是字符串类型的
            //你记好了 服务器 获取到的类型都是 字符串类型的
            if (is_numeric($year)) {
                //是数字你一定是整数吗
                //比如12.3 
                //这儿 一定是什么 
                //一定是 整数
                //那么你怎么判断是不是整数呢
                //唉~  is_int() 判断是不是 整型

                //如果你是数字 那你就
                //同学们 $year 是什么型
                // $year 明明是字符串型吧

                //答案显而易见 应该把 $year
                // 转换为 整型 数字 $year*1
                if (is_int($year + 0)) {
                    // 是整数了你就一定是大于0的整数吗
                    //也不一定 
                    if ($year < 1)
                        echo '<br>', $year, '您输入的不是正数';
                    else { //那else就是正整数 闰年了
                        //怎么写 闰年
                        if (!($year % 4) && ($year % 100) || !($year % 400))
                            // 普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年
                            // (如2004年、2020年等就是闰年)。
                            // 世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年
                            // (如1900年不是闰年,2000年是闰年)。
                            echo '<br>', $year, '是闰年';
                        else
                            echo '<br>', $year, '不是闰年';
                        //这就很完善了
                        //这道题主要练的是if else
                        //你看我是不是写了很多 if else 不断的嵌套
                        //感觉是不是 很爽啊?
                        //???
                        // 同学们基础班有没有说过
                        //如果 if 就一句话后面 {} 可不可以省略
                        //可以的吧

                    }
                } else
                    echo '<br>', $year, '您输入的不是整数';
            } else
                echo '<br>', $year, '不是一个数字/可转为数字的字符串';
        }
    }
    ?>
</body>

</html>
全题解析结束

我们小结一下:
 小结:

1. $_POST 是一个变量
   保存post提交的数据(? 所有post 还是只是表单上的post?)

2.action='' 表示在当前 .php 页面 使用 $_POST 接受 数据
  action='2.php' 也可以提交到其它.php 页面

3.is_numeric() 用来判断是否是 数字/可转为数字的字符串
  是反会true 否返回false

4.is_int() 判断变量是否是 整型/不接受字符串
  是反会true 否返回false  php获得数据都是字符串因此 需要 做
	变量*1 / 变量+0 转换为数字方能判断

5.if,else 后面 如果只是一段代码 可不写{} 这句不用记

各位同学
昨天是不是感觉没啥练是吧
我跟你说
你只要跟着我后边
你只要想练
我让你练个海枯石烂我都有

有这样的题啊
多的是

好我再做一道题用来练习多分支

就是判断成绩
判断成绩呢

这个呢是我们的目标

目标:输入语文合和数学

    然后这个判断 这个等级

其实步骤跟上面是一样的
也是判断表单
提交数据

在服务器获取数据并判断
一样的

那么步骤我们就不写了
我们直接就
代码推进

代码解析:
在 1copy.php 中
<!-- 题目:根据成绩评级 -->
<!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>

<body>
    <form action="" method="post">
        语文: <input type="text" name="ch"><br>
        数学: <input type="text" name="math"><br>
        <input type="submit" name='button' value="判断成绩">
        <!-- 同学们注意语文数学必须在 0~100 之间是不是啊 -->

        <!-- 那我们就一点提交 action="" 提交到当前页面上-->
        <?php
        // 如果你点提交了 
        // var_dump('原数组 :', $_POST);
        if ($_POST) { //如果你这个数组不为空的话
            //取出来语文
            $ch = $_POST['ch']; //获取语文成绩
            $math = $_POST['math']; //获取数学成绩
            if (!$ch  || !is_numeric($ch) || $ch < 0 || $ch > 100) {
                //如果语文为空 或者 不是一个数字/可转为数字的字符串
                //语文 小于 0 或者 大于 100

                //那么我这就输一下
                echo '语文成绩必须在0-100之间';
            } elseif (!$math  || !is_numeric($math) || ($math < 0 && $math > 100)) {
                //如果你的数学  重复上面语文的判断
                //($math < 0 && $math > 100) 和  $ch < 0 || $ch > 100 写法效果的一样的
                echo '数学成绩必须在0-100之间';
            } else {
                //那么这里面干什么 
                //如果以上两种情况都没有执行
                //说明什么 语文/数学 成绩他就在 0到100 之间啦
                $avg = ($ch + $math) / 2; //综合成绩
                echo '您的综合成绩是 : ', $avg;
                //来看级别 啊 级别
                if ($avg >= 90) {
                    echo 'A';
                } elseif ($avg >= 80) {
                    echo 'B';
                } elseif ($avg >= 70) {
                    echo 'C';
                } elseif ($avg >= 60) {
                    echo 'D';
                } else {
                    echo 'E';
                }
            }
        } else { //如果没有数组
            echo '输入不能为空';
        }
        ?>

    </form>
</body>

</html>
解析结束

好了
这道题
写5遍

这道题主要练的是什么
练的是多分支

你发现吗
这个题
我给你设计的很精巧的

这里面全都是多个双分支
多个多分支

一起来练习 啊


好啦
这是练习多分支 嗯

那么这个我
我小结一下

首先你要把这个表单写出来
你要点提交按钮
action='' 提交到当前地址上

然后 php定界符里
判断一下
如果
你点了提交按钮 $_POST 有东西
那你就获取语文和数学

那这里面就判断一下
语文不为空   或 
语文是个数字 或
语文小于0    或
语文大于100 

那么数学也是同样的

然后得到的
都是 0~100之间的怎么办

那就把他的综合成绩找出来
	判断他的等级

啊

那么下面呢这道题
我是带大家来学习
那个什么呢

switch-case 语句
那么这个我们要做一个
什么判断呢

目标:
就是
我 
我有一个下拉菜单
要一个下拉列表啊选择
红色 绿色 蓝色

你就选择不同的颜色
这个版面你就变成不同的颜色

目标:
	选择颜色,
	将文字的颜色
	改成选择的颜色


你有一个下拉菜单比如红色绿色蓝色

你选不同的颜色 那我的文字发生变化


<!-- 目标:
	选择颜色,
	将文字的颜色
	改成选择的颜色 -->

<!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>

<body>
    <div id="shi" class="">
        锄禾日当午<br>
        汗滴禾下土<br>
        谁知盘中餐<br>
        粒粒皆辛苦<br>

    </div>
    <form action="" method="post">
        <select name="color" id="">
            <option value="0">请选择颜色</option>
            <option value="1">红色</option>
            <option value="2">绿色</option>
            <option value="3">蓝色</option>
        </select>
        <input type="submit" value="更改颜色" name="button">
    </form>
    <?php
    if ($_POST) {
        var_dump('原数组', $_POST);
        echo $color = $_POST['color']; // 0/1/2/3
        // 那我再来
        switch ($_POST['color']) {
            case '1':
                $color = '#ff0000'; //红色 
                break;
            case '2':
                $color = '#009900'; //绿色
                break;
            case '3':
                $color = '#0000ff'; //蓝色
                break;
            default:
                $color = '#000000'; //黑色
                //颜色都在  $color 里了
                //是不是要放到 html 里
                //那么放到 html 里面有一个问题
                //这是 php的
                //php的东西能控制php吗

                //如果是控制html php服务器能控制 HTML的样式吗
                //肯定不能 他是两台电脑
                //哦 你那台电脑 控制 我这台电脑上 的显示
                //是不可能的吧
                //所以控制这的样式
                //肯定是什么呀
                //script 是不是

        }
        // echo <<<shi   ....输出大量字符串   shi;
        // echo <<<str   输出                str;
        // 两个都可以
        echo <<<shi
        <script>
        window.onload = function() {
            document.getElementById('shi').style.color = '$color';
        }
        //这段js 应该放在php 判断里面
        // 所以怎么写呢
        //这段js 剪切掉 将来是一大串
    </script>
shi;
    }

    ?>


</body>

</html>
//结束

html是先执行好的
解析成网页

当我选择了红色
才执行 $_POST 里面的内容
  进入 switch
	筛选case 颜色变量

   然后 输出颜色变量
	以输出 字符串的形式
	使用动态 js 改变 
        html元素的样式的颜色


这三个例题
颜色改变比较难
你们要多上心

第三天
现在我们回顾
上一堂课
讲的内容

上一堂课
首先讲的是一个常量
什么是常量咩

就是在整个过程中
固定不变的值
我们就可以声明为常量

大家学完常量以后嘞
有没有一个疑惑
有没有一种疑惑
说
你说我定义一个常量
那我定义一个变量不也一样用嘛


有没有这个疑惑
(此时台下一位女同学说:没有)

没有啊
哈哈 哈啊
那你啥时候定义常量
啥时候定义变量呢

为什么要定义常量呢
你说同学们想一下

我定义常量能够解决的问题
我定义变量行不行

行啊
我把值保存在一个常量中
和我把值保存在一个变量中


那不都一样用吗
那我干嘛要定义常量嘞


你比如说我有一个 派(3.14)

3.14有人说 
嗯
我要定义
常量
难道我定义变量就不能用了嘛

也可以啊

那我我干嘛我要去定义一个常量呢

啊?

那你们怎么没有疑惑呢

没有疑惑我就问你们喽

啊? 呵哈哈 啊

同学们常量
常量他的执行效率是要比变量更高的

知道吧

常量 的执行效率是要比变量更高的

如果一个值在执行过程中是不变的那你就
设置成常量

它的效率肯定是高的

肯定是比变量要高

就这个意思

但是前提条件是整个运行过程中
你的值不要变化

如果在运行过程中
你的值可能要变化

那你就要声明一个变量

否则你就声明一个常量

好 那么
我们下面怎么去定义呢
用define 去定义常量

define常量名和值

define('a',123,false);
常量   名字 值  默认false区分大小写

我们说了
一般在开发的时候
让你区分
一般我们开发都要区分的

现在的语言
流行的语言都是区分大小写的

常量名前面是没有$符的
常量名一般推荐用的大写
反正你喜欢小写也没有关系

一般推荐大写

好,那么
一般定义常量可以用特殊字符
但是呢你要用了特殊字符以后呢

必须使用 constant 关键字 才能去调用它

define('%%%',123,false);
常量   名字  值  默认false区分大小写
echo constant('%%%');

常量一般定义好了你不能再定义了
以上前面都讲过不再手打

请自行回到前面回顾 知识


好了这是回顾了前面的知识

现在我们把作业题做一下


是一个计算器

现在我们做的第一步啊
第一步咱们先说

当我一点 等于
之前输入的值应该还在里面 对吧

那怎么办呢

首先php

代码开始
<!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>

<body>
    <?php
    $num1 = '';
    $num2 = '';
    $op = '';
    $result = ''; //运算结果

    $color = 'red';

    if ($_POST) { //数组有才显示
        var_dump('原数组 : ', $_POST);

        $num1 = $_POST['num1']; //取 num1 的 值
        //$num1有了 我再把它显示到 消失了num1的网页上
        //但是这个num1从哪里取
        //从服务器 上取
        //这 nun1 是从 php 里面取出来的

        #value='<?php echo $num1 ? >'
        $op = $_POST['op']; //取 运算符 的 值
        $num2 = $_POST['num2']; //取 num2 的 值

        //然后我们把结果取出来
        //结果是这样的
        switch ($op) {
//主要是 +-*/是字符串 不能直接运算
            case '+':
                $result = $num1 + $num2;
                break;
            case '-':
                $result = $num1 - $num2;
                break;
            case '*':
                $result = $num1 * $num2;
                break;
            case '/':
                $result = $num1 / $num2;
                break;
        }
    }
    ?>
    <form action="" method="post">
        <!-- 这里 HTML 获取了php 中的变量 所以 PHP 要写在前面 -->
        <input style="background-color: <?php echo $color ?>;" type="text" name='num1' value='<?php echo $num1 ?>'>

        <!-- PHP 可以控制行内样式
        前面那到题还用个屁的js -->

        <select name="op" id="">
            <!-- 使用 selected 属性 控制下拉默认选中哪个 -->
            <option value="+" <?php echo $op  == '+' ? 'selected' : '' ?>>+</option>
            <option value="-" <?php echo $op  == '-' ? 'selected' : '' ?>>-</option>

            <!-- 如果 选择的 - 号 显示 - 号 否则显示 '' 
             如果 没选直接显示 '' 为false 啥也不干
            -->
            <option value="*" <?php echo $op  == '*' ? 'selected' : '' ?>>*</option>
            <option value="/" <?php echo $op == '/' ? 'selected' : '' ?>>/</option>
        </select>
        <input type="text" name='num2' value='<?php echo $num2  ?>'>
        <input type="submit" name='button' value="=">
        <input type="text" name='result' value="<?php echo $result ?>">
    </form>

</body>

</html>
代码结束


下面我们开始讲循环语句
循环语句基本上也跟你们
学的js 基本是类似的

你们js 学了什么循环

for
while
do-while 是吧
for in 循环

第一个来说for 循环
for循环 我来先说他的语法

在 js中
for(var i=1;i<9;i++){}

在 php中 和js是一样的
for($i=1;$i<9;$i++){}


for($i=1;$i<9;){死循环 条件为真}

for($i=1;;$i++){死循环}

for($i=1;;){死循环}

for(;;){死循环}

for只要没有条件就是死循环

思考:
    for($i=1;$i!=5;$i++){4次}


for($i=1;$i<9;$i++){}
   初始值 条件 增/减量
     1次  8+1次 8次

在循环结束后 
$i 是否还存在? 

存在 为 9

这是for 循环

请自行在回顾一遍

刚才说了for 循环
那下面我们说

while 和 do-while
这个跟js
是一样的
我把这个语法说一下
就可以了

语法:
	while(条件){}
和
	do{

	}while(条件){}


有个东西呢我要说一下
小结一下:
	说for循环  while循环 和do_while

这 3 个循环是兄弟3人 可以相互的替换
也就是说
你用for循环能做的
while循环 肯定能做
do_while循环 肯定能做
你不要去怀疑

这 3 个循环 可以相互转换

但是呢虽然说可以相互替换
也是有个优先选择的问题

如果明确知道循环多少次数 选 for 循环
如果循环到条件不成立为止 选 while/do_while 循环


下面我们来看多语句表达式

<?php
for ($i = 1, $j = 9; $i <= $j; $i++, $j--) {
    echo "10可以分成{$i}和{$j}<br>";
}
?>
10可以分成1和9
10可以分成2和8
10可以分成3和7
10可以分成4和6
10可以分成5和5

初始值和增量可以写多个
那么条件呢

条件是不是只写了一个?
如果你写多个,那么他就最后一个起作用

如果写好多条件都起作用
他不就乱了吗


foreach循环是用来遍历数组的
我们先说语法

在 js 中
arr.foreach((item,index,arr){


})

在php中 
语法1
foreach(数组 as 值){

}
语法2
foreach(数组 as 键=>值){

}

实例:1
foreach($arr as $item){

}
实例:2
foreach($arr as $index=>$item){

}


那好下面我们说一下这个PHP的跳转语句
跳转语句有两个啊

呃 第一个是 break 中断循环
还一个是  continue 中断一次循环


break 实例:
for ($i = 1; $i <= 10; $i++) {
    echo " {$i} ";
    if ($i == 5) {
        break;//只跳出外层的一个循环
    }
}//1 2 3 4 5

continue 实例:
for ($i = 1; $i <= 10; $i++) {
    //前面的还是会执行的
    if ($i == 5) {//等于5时
        continue;//后面的不执行 没啥用 自己写if判断是一样的
    }
    echo " {$i} ";
}}//1 2 3 4   6 7 8 9 10


那如何中断多层循环呢(php)
break 2;//跳出外层两重循环 不写默认 1


在js 中如何做的呢
outer:
for(var i=0;i<10;i++){
 inter:
  for(var j=0;j<10;j++){
    if(i>5){
    console.log(i); ----6 
     break outer;
    }
  } 
 }



替代语法
php中除了do-while以外
其他的语结构都有
替代语法

替代的语法规则是什么
if的替代语法

if(){

}elseif(){

}else{}

规则 开始 { 变为 :
    中间 { 变为 :           } 全省略
     结束 } 变为 endif

if():

else:

endif

//switch 替代语法
switch():

endswitch;

//for替代语法
for():

endfor;



使用php循环输出 原生的 html
   <?php
    for ($i = 1; $i <= 10; $i++) {
        if ($i) {
    ?>
            <?php echo '我是php变量'; ?> <span>我是HTML元素</span>
    <?php
        }
    }
    ?>

循环输出了 10次 html元素

替代语法更方便理解
   <?php
    for ($i = 1; $i <= 10; $i++) :
        if ($i) :
    ?>
            <?php echo '我是php变量'; ?> <span>我是HTML元素</span>
    <?php
        endif;
    endfor;
    ?>


elseif 不能写为 else if
也是因为 
为替代语法预留了格式
不能这样写 
否则会对替代语法造成逻辑影响
甚至报错

小结:
可以通过替代语法证明elseif 不能分开写




来来来
下面我们开始讲函数了
函数
这里面的函数使用和js也是
基本差不多的
我问一下为什么要学函数

函数就是为了实现
模块化编程的嘛

那么也就是说
我把几个代码封装到函数里面去
函数就是一个
代码块是不是

函数就是一段
代码块

那么这段代码写好之后
可以被多次调用
对吧

而且顺序也可以不同
多次 次数也可以不同

比如我写5个函数用不同的
顺序去调用它

这就是模块化编程

接下来我们学习怎么定义函数

定义函数和我们的js也是一样的

function 函数名(参数,参,参,参){

}

函数名 与变量命名规则差不多
但是 函数名/关键字IF/FOR 不区分大小写(PHP)
      变量名 区分(PHP)


在 js 中全部都是区分大小写的

可变函数
在js 中 在可变变量中 存入函数

那什么是可变函数
就是
将函数名 存储到 变量中
因为只有变量是可变的


<?php
function show($a)
{
    echo $a, '<br>';
}
$b = 'show'; //将函数名保存到变量中

$b('调用函数'); //调用函数
?>

就是因为 可变变量 

php才变得非常优美

是封装的非常整洁吧

匿名函数
就是没有名字的函数

$fun = function () {
    echo '匿名函数就是函数没有名字';
};
$fun();//匿名函数就是函数没有名字



那么下面呢我们说这个参数


参数传递
同学们这个
这个函数啊

我们是不是刚才没有放参数呢
是吧

那么
参数呢有两种
一种是形式参数   简称 形参
还一种是实际参数 简称 实参


形式参数我这么说你看对不对啊

形式参数他是定义函数时候的参数

形式参数是定义函数时候的参数
只起一个形式的作用


function fun($a, $b)
{
    echo $a + $b;
}

$a,$b  只是形参吧 
告诉计算机你要想调用我
必须传递俩参数 $a,$b 

它只起个形式的作用没有具体的值


实际参数是调用函数时候的参数
有没有具体值呀
有

有具体的值

function fun($a, $b)//形式的作用没有具体的值
{
    echo $a + $b;
}
//实参
fun(1, 2); //3 
//有具体的值

默认情况下,数的传递是值传递

$num=10;
function fun($args){//默认值传递
     $args=100; //相当于 $num=10 完全覆盖了  $args=100;
}
fun($num);
echo $num;//10


$num=10;
function fun(&$args){//加 & 为地址传递
     $args=100; //相当于 $num 重新赋值 为 100
}
fun($num);
echo $num;//100

加 & 为地址传递时
只能传递变量
只有变量才有地址

参数的默认值
function fun($args='默认值'){//和js一样
    
}

function fun($a,$args='默认值'){//没有默认值的必须写前面
    
}

不然调用时
fun(10);//实参小于形参的话 报错  默认值在后面就不会

fun(1,2,3);//实参大于形参  只取前面对应的值 后面的就不要了

在js里面 有 arg 防抖那个
传几个用几个

在php里面怎么办呢
function fun(){
   echo func_num_args();//3 获取当前函数 参数的个数
   $args=func_get_args();//获取当前函数 参数的值(一个数组)
   print_r($args);//[1,2,3]
};
fun(1,2,3);

那如果说这个函数我记不住
或者嫌烦
我不想用这个函数该怎么办

我记不住
我能不能这样用一个变量
表示是变长的参数


function fun(...$args){//此处和 封装防抖是一样的
  print_r($args);//会 把 参数 聚集成一个数组
};
fun(1);//Array ( [0] => 1 ) 
fun(1,2);// Array ( [0] => 1 [1] => 2 ) 
fun(1,2,3);// Array ( [0] => 1 [1] => 2 [2] => 3 )


function fun($a,$b,...$args){//此处和 封装防抖是一样的
  //$a,$b必须要写 后面可变的 写也行不写也行
};


function fun(...$args)
{ //此处和 封装防抖是一样的
    print_r($args);
};
fun(1); //[1]
fun(1, 2); //[1,2]
fun([1, 2]); //[[1,2]]
fun(...[1, 2]); //[1,2]

参数的类型约束
function fun($a, $b)
{
    echo $a, $b;
};
fun(1, '哈哈');//1,哈哈

函数参数的类型约束
function fun($a, int $b)//声明$b是整型
{
    echo $a, $b;
};
fun(1, '哈哈');//由于 哈哈 不是整数 直接报错


函数参数的类型约束
function fun(int $a,  $b)
{
    echo $a, $b;
};
fun(1, '哈哈'); //由于 1 本来就是整数 所以正常运行


//函数 return 类型约束
function fun(int $a,  $b):int//这个函数的return 必须是 int	
			//否则报错
{
    echo $a, $b;
};
fun(1, '哈哈'); //由于 1 本来就是整数 所以正常运行

可以约束 string,int,float,bool,array,

function fun():void{//:void 空  约束你不准你带值回来
	return;
};
fun();//不报错 7.1后支持
//return 后有值后 会报错


return

我问你们一下
你们当时学的return是啥意思呢

在 js 里面除了返回值以外
还有其他的两个功能

1.终止脚本继续往后执行 .php中 return 后的一切都不会执行
 需要讲一个新的知识 包含

reauire './2.php'
相当于 2.php 的内容复制到了这里
和 引入.js 完全一样

在  reauire './2.php' 包含中
的return 不会中断当前页面
只会中断 2.php 的页面


有的时候我就想全部终止怎么办呢
如果想全部终止 (当前页面和2.php页面)
你要想其他的方法

将 2.php 中的return 换成 exit()即可
 
或者  die() 这两个是一样的

2.用来做配置文件
返回页面结果
这个是有什么用呢
将来写项目要用的
在2.php 中
return [数组];//不return是 没有数据的  
//print_r($a)也只显示 1 包含成功而已

在当前页面中
 $a=reauire './2.php';
print_r($a);//[数组]

小结:

在项目中引入配置文件就使用这种方法
什么是配置文件?


3.函数的返回和终止 和js完全一样
    1.终止函数执行
	2.返回值


第四天
我们先回顾一下上一堂课讲的内容

请自行回到上面回顾

作业:
通过for循环将数组中值求和,求平均值


//php数组没有 .length语法



<?php
$arr = [1, 20, 53, 23, 14, 12, 15];
//第一个求和
//求和方法有很多
//我这样先来声明一个变量 $sum
$sum = 0; //保存 总和
for ($i = 0; $i < count($arr); $i++) {
    //数组没有 .length语法
    //count 返回数组的个数  count($num)/sizeof$num)
    $sum += $arr[$i];
};
echo '和是 : ', $sum; //和是 : 138
echo '平均值是 : ', $sum / count($arr);
//平均值是 : 19.714285714286
?>


以上代码可以进行优化
for ($i = 0,$length=count($arr); $i <$length ; $i++) {
这样  count($arr) 就会只执行一次
优化了速度

//在 js中优化应该是 这样
for (let i = 0,let length=arr.length; let i <length ; i++) {}

//在 php 中 取小数点后 1位
number_format($sum / count($arr),1);

//在 js 中 取小数点后 2位
num.toFixed(2);


好了 
这道题还有没有什么问题的
没问题是吧
好的

第二道题

第二道题是 数组的翻转


... 回车是 <div class=""></div>


// 第二道题是 数组的翻转
// $str = ['tom', 'berry', 'ketty', 'rose', 'Jake'];
// // 希望结果 Jake rose ketty berry tom
// //上次不是讲了个
// //[a,b ]=[b,a] 值对调的例子吗...
// //难道...
// [$str[0], $str[1], $str[2], $str[3], $str[4]] = [$str[4], $str[3], $str[2], $str[1], $str[0]];
// print_r($str);
//真的可以

// 老师运用的更高级
$str = ['tom', 'berry', 'ketty', 'rose', 'Jake'];
for ($i = 0, $j = count($str) - 1; $i < $j; $i++, $j--) {
    [$str[$i], $str[$j]] = [$str[$j], $str[$i]];
    //$str = ['tom', 'berry', 'ketty', 'rose', 'Jake'];
    // 希望结果 Jake rose ketty berry tom
    //$i 数组依次顺数  $j数组依次倒数
    //结束条件 当 $i>=$j 时
}
print_r($str);
//Array ( 
//[0] => Jake 
//[1] => rose 
//[2] => ketty 
//[3] => berry 
//[4] => tom )

好这有没有什么问题


下面我们说遍历二维数组啊
第三道题
$arr = [
    [1, 2, 3, 4],
    [5, 6, 7, 8, 9]
];
for ($i = 0; $i < count($arr); $i++) {
    for ($j = 0; $j < count($arr[$i]); $j++) {
        echo $arr[$i][$j];
        //1 2 3 4 5 6 7 8 9
    };
};




// 循环输出 1-100 
//优先 其中15的倍数输出 c
//再 其中3的倍数输出 a
//再 其中5的倍数输出 c


//这道题有个很小的逻辑大家要注意一下
// 从1到 100 for
for ($i = 1; $i <= 100; $i++) {
    if (!($i % 15)) {
        echo 'C';;
    } elseif (!($i % 5)) {
        echo 'B';
    } elseif (!($i % 3)) {
        echo 'A';
    } else {
        // echo $i;
    }
    echo '&nbsp';
}


下面打印水仙花数
水仙花数总共是几位数啊
总共是三位数

$a = (int)($i / 100);
//除以100 取整

需要用到 math.pow 底数指数函数
我对只能用来看的无用算法 不感兴趣

有需要的自行百度

同学们今天开始讲

变量的作用域


$a  let a  

$GLOBALS['a']=10   $a=10     let a=10

函数中使用$a  echo $GLOBALS['a']      


$_POST               var POST 不推荐


$GLOBALS 本身是数组包含了所有全局变量 $a 的值


  global 写法
$a = 10;
function fun()
{
    global $a;
    echo  $a;
};
fun();

小结
	

常量是没有作用域的




下面我们说一个静态变量
静态是有个单词叫
static


静态全局变量就是全局变量
所以
一般静态变量都是说函数内部的
局部变量
function fun()
{
    static $a = 1;
    $a++;
    echo $a;
};
fun(); //2
fun(); //3
只 $a=1; 赋值一次


没有加 static  则
function fun()
{
    $a = 1;//会重新赋值 覆盖销毁
    $a++;
    echo $a;
};
fun(); //2 所以结果一样 都是2
fun(); //2 所以结果一样 都是2

这是变量
那如果是常量呢

常量和静态变量的区别
1.常量和静态变量都只初始化一次
2.常量不能变化 静态变量可以改值

function fun1()
{
    define('num', 10);
}

function fun2()
{
    fun1();
    echo num;
}
fun2();//10 随便调



关于匿名函数
默认情况
函数内部不能访问函数外部的变量的
但是 
匿名函数可以通过 use 关键字
将外部的变量引入匿名函数中


$num = 10;
$fun = function () use ($num) {
    echo $num;
};
$fun();//10


匿名函数中本来是无法调用的 外面变量的
但是 使用了 use ($num) 之后就可以了

use ($num)
将外部变量引入到匿名函数中

讲到这
引出一个问题
如何在函数内部访问函数外部变量

1. 将变量存在 $_POST 里面 不推荐

2.变量默认存在 $GLOBALS 数组里 
  函数中使用 $GLOBALS['变量名'] 调用

3.在一般函数中 global $a;声明后 可调用 外部 $a

4.在特殊匿名函数中 $fun = function () use ($num) {
  即可在匿名函数中正确调用  外部 $a

函数变量函数执行完就销毁了
全局变量 函数执行完就销毁了

函数的最后一个问题就是递归
递归
其实你们
js 里面就是函数里面自己调用自己

实例:
输出 9 8 7 6 5 4 3 2 1 0

不许用for 循环
不许用 定时器 

只能使用函数 和if

自 增/减 作为参数时 是先 传参(运算) 的 
所以 要 --/++参数
不能 参数 --/++

最简单的递归
function a($i = 0)
{
    echo $i;
    if ($i == 1) return; //递归出口
    a(--$i); //递归点 这么搞是死循环 必须要有退出
    //递归点前面 必须有递归出口
};
a(9);//987654321


打印出 1到 100
function a($i)
{

    echo $i, '<br>';
    if ($i == 1) return;
    a($i - 1);
}
a(100);


从 1 加到 100
function a($i)
{
    if ($i == 1) return 1;//递归出口
    return $i + a($i - 1);//递归点
//将打印的值返回给上一个自己 相加 
}
echo a(100);

递归有两个元素
1.递归点    从什么时候开始递归
2.递归出口  从什么时候结束递归


下面重复介绍前面的包含
require './2.php'; //路径错误 停止执行当前页面的 往后代码
和
include './2.php'; //路径错误 依旧执行当前页面的 往后代码


require_once './2.PHP';
require_once './2.PHP';
//包含两次也只显示一次


在html 写<?php  ?> 
然后通过 php 解释器 访问 
是不执行的
但是一旦包含进 .php文件
就会被 php 解释器 解释

包含文件编译时不执行
运行时加载到内存
独立编译包含文件

require '2.php';
//不写路径受 include_path配置影响

访问PHP www 文件夹 
展示选项
  ctrl+f 查找 include_path

include_path       .;C:\php\pear

没写.  就找在 C:\php\pear 里面找

<?php
set_include_path('C:\php\pear;D:\php\pear');
//重设默认路径   可以设置多个 应该是直到找到为止
require '2.php';
//不写 . 就是默认路径
?>

web中都是 /
windows \ / 都可以


关于 错误
1.notice:提示     继续执行
2.warning:警告    继续执行
3.error:致命错误  停止执行

错误的显示方法
方法一:显示在浏览器中(默认)
方法二:记录在日志中



记录在日志中 需要更改一些配置
在 D:\phpStudy\PHPTutorial\php\php-7.2.1-nts\php.ini 中

找到 error_reporting = E_ALL
     display_errors = On 默认将错误显示在浏览器上
E_ALL 是常量

error_reporting = E_ALL:报告所有的错误
display_errors = On:将错误显示在浏览器上
log_errors = On:将错误记录在日志中
error_log=’地址’:错误日志保存的地址

开发模式:错误显示在浏览器上,不要记录在日志中
运行模式:刚好相反


ini_set();//设置php配置文件 函数改配置无需重启

<?php
$debug=false;		//假设true是开发模式  false是运行模式
ini_set('error_reporting',E_ALL);	//所有的错误有报告
if($debug){
	ini_set('display_errors','on');	//错误显示在浏览器上
	ini_set('log_errors','off');	//错误不显示在日志中
}else{
	ini_set('display_errors','off');//错误不显示在浏览器上
	ini_set('log_errors','on');//错误显示在日志中
	ini_set('error_log','./err.log');	//错误日志保存的地址
}

  我们下面进入文件的编程
文件的编程
分为两个
1.文件夹的编程
2.文件的编程

例如
创建 txt并 往里面加字


要对文件进行操作
第一步我要讲对文件夹进行操作


所以我们先讲文件夹操作

再讲文件操作

要讲文件夹操作
我们得首先
1
创造文件夹

make //译 创建
directory//译 目录

缩写函数为
mkdir('./aa');//当前目录创建aa文件夹
mkdir('./aa/bb');//没有aa创建失败

语法
mkdir('./aa/gg/hh/jj/jj',0777,true);
//       路径         代表权限 递归创建开启(默认关闭)
//没有aa/gg/hh/jj/ 创建也能创建 ./aa/gg/hh/jj/jj成功



2
删除 文件夹
remove 移除

rmdir('./aa');
//删除的文件必须是 空的 否则删除失败
//为了安全起见 PHP没有 递归删除的语法


3
重命名 文件夹
remove 移除
rename('./aa','./pp');


4
是否是文件夹

is_dir('./aa');
//是文件夹返回 true
//不是返回 false


5.打开 并 读取 文件夹 中内容 然后关闭该文件夹
opendir('./');
//打开当前目录

var_dump(readdir(opendir('./')));

//resource(3) of type (stream)

//资源(指能使用的非php自身文件)

<?php
$a = opendir('./');
var_dump(readdir($a));//string(1) "." 
var_dump(readdir($a));//string(2) ".."
var_dump(readdir($a));// string(12) "1-常量.php"
var_dump(readdir($a));// string(9) "1copy.php"
var_dump(readdir($a));//string(18) "2-数据类型.php" 
var_dump(readdir($a));//string(12) "3-数组.php"
var_dump(readdir($a));//string(42) "4-其他类型和布尔之间的转换.php"
//如果读不到了 就返回 空
//空就是假
//假就退出了

//因此我们可以

//利用while循环 读取所有文件
//亦可以用 for循环 for(;;)里面不写条件即可 

for(;;) {
 if(readdir($a)){
    if (readdir($a) == '.' || readdir($a) == '..')
        continue; //跳出这次循环
    echo  readdir($a);
 }else{break;}
}
closedir($a);// 关闭打开的文件夹 $a = opendir('./');


// 所以说三者 for while switch 是相同的不要去怀疑
// 但是 for 没有 while 简洁

while (readdir($a)) {
    if (readdir($a) == '.' || readdir($a) == '..')
        continue; //跳出这次循环
    echo  readdir($a);
}
closedir($a);// 关闭打开的文件夹 $a = opendir('./');
//后续结果为
//string(18) "6-判断成绩.php" 
//string(10) "8-demo.php" 
//string(2) "cc" 
//string(5) "l.php" 
//string(2) "pp"
?>

如果乱码是因为 编码不同一造成的

php 提供了 
iconv() 
函数 

来转换编码

用法
echo iconv('gbk','utf-8',readdir($a)); 

我这不需要转 当前系统不知道是什么 类型的

1.opendir('./')返回目录里面的 资源
  readdir(opendir('./'));读取打开的资源 
2.每个文件夹 默认都有 . 和 ..
3.iconv('进入的字符编码','转换后摇输出的字符编码','要转换的字符串'); 
   // 编码转换
4.readdir(资源)


昨天我们讲的是文件夹的操作
今天我们来讲文件的操作

比如
将字符串写到文件里面去

我们会用到
file_put_contents() 函数

<?php
$str = '床前明月光';
file_put_contents('./txt', $str);
?>

当前目录会自动生成 txt.txt文件
内容只有一行 为'床前明月光'

如果再次执行 会覆盖掉 之前的操作(必定是清空重写)

在txt中
\r 回车
\n 换行
\t tab

在浏览器中
<br> 换行

<?php
$str = "床\r\n前\r\n明\r\n月\r\n光";
file_put_contents('./txt', $str);
?>
结果为:
床
前
明
月
光

注意 :回车不是换行
      那什么是回车

回车:\r  光标移动到当前行的最前面
换行:\n   将光标下移动一行

按键盘的回车键
计算机做了两步
回车 和 换行

要解释 \r \n 必须使用双引号

单引号为纯字符串

能写肯定就能读
光写有什么用啊

file_put_contents('./txt', $str);//清空重写

方法一:
echo file_get_contents('./txt');//读
方法二:
readfile('./txt');//读取文件并输出
两者相等

在浏览器中 .txt 的  
\r\n  不会解释为换行加回车
而是 一个空格

仅打开文件资源
fopen('./txt', 'read'/'write'/'append' );
// 打开/创建 文件资源 
//后 读/清空重写/追加 ... 共十种
fopen('./txt',  'r'/'w'/'a');//可缩写



例题:
1.打开写
<?php
$fp = fopen('./txt',  'write');//返回的是文件 指针(指指向的地址)
//$fp = fopen('./txt',  'w');
var_dump($fp); //资源类型
//resource(4) of type (stream)

for ($i = 0; $i < 10; $i++) {
    fputs($fp, '关关雎鸠' . "\r\n");
    //清空重写(第一次打开为清空 打开后继续写不会清空)
}

//结果为
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠
//关关雎鸠

//写完后 关闭 一个资源的 指针(指指向的地址)
fclose($fp);
?>

例题2: 打开文件读取(没有文件会报错)

<?php
$fp = fopen('./txt',  'r');//打开文件读
//返回的是文件 指针(指指向的地址)
//echo fgets($fp);

//结果为: 关关雎鸠
//只读取了一行 写虽然只能写一行 但是可以写入换行

需要循环读取
while($a=fgets($fp)){
echo $a,'<br>';
}

//结果为全部内容
//且一行读取完后 加上<br> 换行
//关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠关关雎鸠
//关关雎鸠


?>

例题 3: 追加一句话
<?php
$fp=fopen('./txt','a');
//文件地址=打开文件('文件地址','追加');
fputs($fp,'在河之洲');
//写入(地址,'追加的内容');
//默认在末尾追加
?>

小结:
1.执行 fopen的 写/追加 任何操作 会自动创建 该文件
  读 不会自动创建 该文件 会报错



//判断是不是文件
var_dump(is_file('./txt'));//bool(true)
//是返回true 
//否返回false


//判断 文件/文件夹 是否存在
var_dump(file_exists('./txt')); //bool(true)
//是返回true 
//否返回false


//删除文件(删除不存在的文件会报错)
if(file_exists('./txt')){//因此需要判断 不存在则不执行删除

if(is_dir($path)){//如果是文件夹
rmdir('./txt');//删除文件夹

}else{//否则 是文件
var_dump(unlink('./txt')); //bool(true) 删除文件
//是返回true 
//否返回false
 }
}


//二进制读取
前面讲的读取 全都是 .txt文件的读取
文件的存储有两种流

一种是字符流    
存的是字符
(含有 换行 回车 有结束符 
你看不见 ASI编码(可见:字母键
不可见:回车))

一种是二进制流
01010101
什么是二进制流
凡是存储的文件
在内部 全部是010101二进制
包括图片

两种流的读取方式是不一样的

二进制没有行 没有结束符 X
有结束符 结束符 也是 0101 
这就很难区分

我们采用二进制文件的字节长度来读取
读到长度就截止 剩下的就是 结束符

实际上用的很少
偶尔补充的一个东西


那么现在我们来看
<?php
// 二进制怎么读取呢(方式1:fread)

header('content-type:image/jpeg'); //告诉浏览器 下面给的是一张图片
//在此之前 浏览器必须未发过 标头信息
//否则会报 修改失败的错误

$a = fopen('./face.jpg', 'r'); //打开读 资源地址 
//必须要有后缀 .jpg

//判断 二进制 文件 的 字节数 
$b = filesize('./face.jpg');
// var_dump($b); //int(7360) 

// var_dump(fread($a, $b));
//string(7360) "乱码"


echo fread($a, $b);
// "还是乱码"
//返回的 二进制 010101 
//浏览器默认按字符串来解析了
//所以就会乱码



//二进制怎么读取呢(方式2:file_get_contents())

header('content-type:image/jpeg'); //告诉浏览器 下面给的是一张图片
//在此之前 浏览器必须未发过 标头信息
//否则会报 修改失败的错误
echo file_get_contents('./face.jpg');
//这个功能很强大
//既能读文本流也能读二进制流

以后用  file_get_contents 算了
方便很多



同学们下面我

客户端可以提交了
服务端是不是就可以获取了

地址栏写 ?a=1&math=2

携带参数 a=1,b=2

html 中
input type为button 里  
οnclick="location.href='1.php?a=1&math=2'"

事件 连接跳转并传递参数

οnclick="location.assign='1.php?a=1&math=2'"

事件  连接跳转并 更新且合并 之前传递的参数

action="a=1&math=2";
 get 提交到当前 .php页面

post 不能写在浏览器地址栏里 post 提交的是 name




echo $_REQUEST['a'];//1

$_REQUEST 能同时获得get和post的数据

$_POST
$_GET


那么学到这同学们就有疑惑了
同学们看

$_REQUEST 是万能的 但是底层却做了两次绕弯操作
数据量庞大时 效率会显著降低

除此之外还有一个很致命的问题


当 get与post同时提交了同键名 的数据

$_REQUEST获取的到底是 get 还是 post 的数据?
这取决于配置文件

php/php版本/php.ini  搜索 request_order

request_order = "GP"

先获得 GET 再获得POST 

前者会被后者覆盖

request_order = "PG"
可调换循序




<html>
<?php
if ($_POST) {
    var_dump($_POST['hobby']);
}
?>
<form action="" method="post">
    爱好:
    <input type="checkbox" name="hobby[]" value="爬山">爬山
    <input type="checkbox" name="hobby[]" value="抽烟">抽烟
    <input type="checkbox" name="hobby[]" value="喝酒">喝酒
//多选checkbox 
//传递一组数据 
//需要 name="hobby[]"
    <br>
    <input type="submit" name="button" value="提交">
</form>

</html>






//综合实例 例题
<html>
<?php
if (isset($_POST['button'])) {
    echo '姓名: ', $_POST['username'], '<br>';
    echo '密码: ', $_POST['pwd'], '<br>';
    echo '性别: ', $_POST['sex'], '<br>';

    if (isset($_POST['hobby'])) { //如果有爱好
        echo '爱好: ', implode(',', $_POST['hobby']), '<br>';
        // js 里面 使用 join 连接字符串成数组
        // 这里使用 implode 连接 将一个数组转为字符串
    } else {
        echo '爱好: 无', '<br>';
    }


    echo '籍贯: ', $_POST['jiguan'], '<br>';
    echo '留言: ', $_POST['words'], '<br>';
    // var_dump($_POST);
}
?>
<form action="" method="post">
    姓名: <input type="text" name="username"><br>
    密码:<input type="password" name="pwd"><br>
    性别:
    <input type="radio" name="sex" value='1' checked><!-- 性别 必选  默认选中一个 防止报错-->
    <input type="radio" name="sex" value='2'><br>
    爱好:
    <input type="checkbox" name="hobby[]" value="爬山">爬山
    <input type="checkbox" name="hobby[]" value="抽烟">抽烟
    <input type="checkbox" name="hobby[]" value="喝酒">喝酒
    <br>
    籍贯:
    <select name="jiguan" id="">
        <option value="021">上海</option>
        <option value="010">北京</option>
    </select>
    <br>
    留言:
    <br>
    <textarea name="words" id="" cols="30" rows="10"></textarea>
    <br>
    <input type="submit" name="button" value="提交">
</form>

</html>


上面传的都是字符 除了字符 使用
form里的 post get 传递

还有 二进制 传递
只要是文件他都是二进制传递

开发中需要上传图片音乐
视频



<!-- 文件域(专业术语) -->
<input type="file" name="image">

文件域是二进制
你就这样上传是不行的

同学们你们学表单的时候
有没有学过
enctype 属性啊
<input type="file" name="image" enctype>

这里面有三个值
1.第一个值
application/x-www-from-urlencoded 默认

意思是吧整个表单的格式转成 xml 格式

xml 格式跟html是一样的 
唯一不同的区别是 html标签是定义好的
xml(可扩展标签)  标签是随便定义瞎写的...

转成这样 的.xml(只能传输带格式\n \r 的字符串)
<username>tom</username> 
<sex>1</sex> 

然后提交给 php

2.第二个值
multipart/form-data 复杂的 复合的
既能传字符串 也能传二进制数据

3.下面说第三种
text/plain
也是传递字符串用的
第一种传递的是带 .xml格式的字符串

这种是不带格式的 他就是个文本 记事本
tom1
纯粹的文字
这种 速度快 主要用来做 电子邮件



传好以后
我服务器就要接受了
$_FILES


文件名称
[name] => face.jpg

文件类型
[type] => image/jpeg
//      文件类型/文件格式

临时路径
[tmp_name] => C:\Windows\phpA608.tmp
//在临时路径打包后同一发送 走

上传出错?
[error] => 0表示正确
//  0 1 2 3 4 5 6 7 共八种
1 超过了 php版本/php.ini允许的最大值  ini_get('upload_max_filesize')
   搜 upload_max_filesize = 2M (可以改的)
  整个表单是 8M 但是上传的文件最大的 8M

2 超过了表单允许的最大值
  <input type="hidden" name="MAX_FILE_SIZE" value='2'>
  隐藏域(专业术语) 表单允许的最大值 2字节
   一定要写在 input type=file 上传文件的前面

3 只有部分文件上传 断网了

4. 没有文件上传 上传时未选文件

6. 临时文件丢失

7. 临时文件 走的时候 出错了 文件写入失败

只记 0和4即可

字节数
[size] => 7360

<!-- 二进制文件上传防病毒 防重名 实例 -->
<?php
if (isset($_POST['button'])) {
    // echo $_FILES['image']['type']; //name 里有5个类型


    // 刚才我们说了这个优化的文件名
    // 现在
    // 我们说
    // 验证这个文件的格式
    // 比如我只允许 jpg和png
    // 其它格式不许传

    // 当前任何文件 
    // 字符串 视频 音频 都能传
    // php/php版本/php.ini 搜索 extension=php_fileinfo.dll
    //扩展 多引一个拓展就多一个额外的功能 耗费性能

    //删除前面的分号注释 把功能打开 可防止文件伪装
    //就可以使用 finfo 开头的函数

    //防病毒
    //第一步 : 创建finfo资源
    $a = finfo_open(FILEINFO_MIME_TYPE);
    // var_dump($a);//resource(2) of type (file_info) 
    // 这是个file_info资源
    //资源也是有类别(不同)的


    //第二步 : 将 finfo 资源和文件做比较
    //需要使用到一个函数 

    if ($_FILES['image']['error'] == 0) {
        //如果上传无误
        $b = finfo_file($a, $_FILES['image']['tmp_name']);
        //如果是伪装的  finfo_file 能够发现 伪装的文件的本质
        //甚至能发现 伪装成其它文件的 .txt 里面写了html标签 text/html

        // 返回的 $b 是文件的本质
        $c = array('image/jpeg', 'image/png', 'image/gif');
        echo in_array($b, $c) ? '合法' : '不合法' . '本质为' . $b . '文件 只接受 image/jpeg image/png image/gif 文件';
//implode(',', $allow); 数组转为字符串
        // //手动对比 数组是否包含 是返回true 否返回false
        // //in_array('a', ['a','b','c'])
    }




    // $allow = array('.jpg', '.png', '.gif');
    // //自己定义的 数组 拓展名 (我还以为有什么函数呢)
    // //这种方式不能防止文件伪装
    // $ext = strrchr($_FILES['image']['name'], '.');
    // //提取出 上传文件临时路径中的扩展名

    // //手动对比 数组是否包含 是返回true 否返回false
    // //in_array('a', ['a','b','c'])
    // if (!in_array($ext, $allow)) {
    //     echo '请选择正确的文件类型';
    //     return;
    // }
    // //如果不包含 return 结束代码

    if ($_FILES['image']['error'] == 0) {
        //如果上传无误
        //假如没有错
        //那么就开始移动了

        //从服务端的临时目录
        //移动到 专门存放的 目录地址 
        // move_uploaded_file($_FILES['image']['tmp_name'],  time() . rand(100, 999) . strrchr($_FILES['image']['name'], '.'));

        move_uploaded_file($_FILES['image']['tmp_name'],  uniqid('goods_', true) . strrchr($_FILES['image']['name'], '.'));
        //move_uploaded_file('服务器临时地址','./falce.jpg')

        //为了保证名字唯一防止覆盖 
        //方式二 推荐 通过 uniqid() 生成一个唯一的id 做文件名
        // uniqid('',true). strrchr($_FILES['image']['name'], '.')
        //   '前缀'+ xxxxxxxxxx 随机.jpg
        // uniqid 其实也是通过 微秒生成的
        //也有可能 重复 方式一 和 二 不分高低


        //方式一 通过 time() 时间戳 做文件名

        // strrchr($_FILES['image']['name'],'.');
        //从最右往左开始 第一个 . 开始从左往右截取 到最右
        //截取  .xxx
        //再拼接上 时间戳
        // time().strrchr($_FILES['image']['name'],'.');
        //时间戳.jpg

        //时间戳 并不是最保险的
        // 还有 同时传入的可能
        // time().rand(100,999).strrchr($_FILES['image']['name'],'.')

    } else {
        echo '文件上传失败', '错误码', $_FILES['image']['error'];
    }
    echo '<br>', '上传结果信息格式如下 : ', '<pre>';
    print_r($_FILES);
}
?>
<form action="" method="POST" enctype="multipart/form-data">
    <input type="file" name="image">
    <br>
    <br>
    <input type="submit" name="button" value="上传">
</form>

</html>


重复上传同名 flce.jpg 会被覆盖

搜索 upload_tmp_dir = 
临时目录创在什么地方

本来是被注释状态 系统会帮你找
也可以自己写


file_uploads = On
允许文件上传

max_file_uploads = 20
最多可连续上传 20个

题目:
优化文件上传 
1.验证是否有误
2.验证格式
3.验证大小
4.验证是否是http上传
5.上传实现

//4.验证是否 http 上传 ?

疑惑了啊
难道还可以不是 http 上传执行吗
答案是可以的

我们以前是不是这么说的
php的运行需要环境

第一步先安装环境
现在就来个问题
难道PHP就必须要在 apache 下面吗

如果是
 apache 那肯定是 http
但是我告诉你 啊
他是不一定 

举个例子

进入 php/php版本/php.exe 目录下
不要 进入php.exe 

输入 cmd 回车

php D:\phpStudy\PHPTutorial\WWW\8-demo.php

就能将其执行

<?php
echo '11';


结果为 11

这样的话
就可以开发一个软件
通过这个软件来 
不通过你 apache 的 http

move_uploaded_file 
只能检查 通过PHP 的HTTP POST上传的文件

http 是 bs 的协议


通过上面的方法可以绕过 http协议

因此只要上传 必须通过 http协议来验证它

is_uploaded_file 判断文件是不是 http post 上传的


// 格式化时间戳 
// 时间戳转换为年月日
echo date('Y-m-d H:i:s', time());
//   年月日 时分秒  时间戳(不写默认当前时间戳)
//2021-04-08 04:00:48

exit; //阻止执行(php)


时区不对 
在 php/php版本/php.ini 里面
搜索 ;date.timezone =
默认东 1 区格林威治 英区
中区在东八区

解开注释 并 改为
date.timezone = PRC

PRC:中华人民共和区

保存 重启 服务器
2021-04-08 12:07:47
ok 现在时间一致了
 
完整代码 上传 优化文件上传
<!-- 优化文件上传 -->
<?php
// 格式化时间戳 
// 时间戳转换为年月日
// echo date('Y-m-d H:i:s', time());
//   年月日 时分秒  时间戳(不写默认当前时间戳)
//2021-04-08 04:00:48

// exit; //阻止执行(php)

//1.验证上传后返回的状态码
//有错返回错误 没错就返回null
function check($file)
{
    if ($file['error'] != 0) { //如果有错
        switch ($file['error']) {
            case 1:
                return '超过了 php版本/php.ini允许的最大值' . ini_get('upload_max_filesize');;
            case 2:
                return '超过了表单允许的最大值';
            case 3:
                return '只有部分文件上传 断网了';
            case 4:
                return ' 没有文件上传 上传时未选文件';
            case 5:
                return '没有 5 的状态码 ';
            case 6:
                return '临时文件丢失';
            case 7:
                return '临时文件 走的时候 出错了 文件写入失败';
            default:
                return  '未知错误';
        }
    }
    //2.验证格式
    $info = finfo_open(FILEINFO_MIME_TYPE); //各资源DNA
    $mime = finfo_file($info, $file['tmp_name']); //验证文件是否伪装
    $allow = array('image/jpeg', 'image/png', 'image/gif');
    //允许通过验证的文件类型清单
    if (!in_array($mime, $allow)) {
        //如果不在 清单之内
        return '只能上传' . implode(',', $allow) . '格式';
    }
    //3.允许通过的 上传文件 大小
    $size = 123456789;
    if ($file['size'] > $size) {
        return '文件不能超过' . number_format(($size / 1024), 1) . 'k';
        //在 php 中 取小数点后 1位
        // number_format($sum / count($arr),1);

        //1M=1024kb 千字节
        //1kb=1024b 字节
        //1 兆字节=1048576 字节
    }

    //4.验证是否 http 上传 ?
    if (!is_uploaded_file($file['tmp_name'])) {
        return '文件不是HTTP POST 上传的';
    }

    //5.文件安全
    return null; //一切无误 
    //返回 null 代表验证通过 没有错误
}
if (isset($_POST['button'])) {
    if ($error = check($_FILES['face'])) {
        // 如果错误存在 输出错误
        echo $error;
    } else { //如果没有错误 为null 准备上传

        $foldername = date('Y-m-d'); //年月日做 文件夹名
        $folderpath = "./uploads/{$foldername}"; //定义文件 实际存储地址地址
        if (!is_dir($folderpath)) { //如果这个目录不是文件夹
            mkdir($folderpath, 0777, true);; //那么就创建这个文件夹
        }

        $filename = uniqid('', true) . strrchr($_FILES['face']['name'], '.');
        //生成唯一标识做 文件名 + 文件原本后缀
        $filepath = "$folderpath/$filename";
        // ./uploads/2021-04-08/606e998f2499b4.91547852.jpg

        if (move_uploaded_file($_FILES['face']['tmp_name'], $filepath)) {
            // 服务器临时地址移动到 实际地址
            echo "上传成功,路径是 {$filepath} ";
        } else {
            echo '上传失败';
        }
    }
}
?>
<html>
<form action="" method="POST" enctype="multipart/form-data">
    <input type="file" name="face">
    <h1>优化文件上传</h1>
    <br>
    <br>
    <input type="submit" name="button" value="上传">
</form>

</html>

新的一天开始了
现在我们回顾上一天讲的内容

所有的写都是清空重写


接下来进行数据库的学习
我已经学过了
尽量跳过吧
除了实例


我之前装了 一个mysql服务器
phpstudy里面又有一个 mysql服务器
我现在有两个mysql 服务器
且都是用的 3306 端口


phpstudy 自带了 web 控制数据库的功能
phpMyAdmin


之前配置了域名的
phpmyadmin.com

用户名root 
密码: root
只要有浏览器就能访问数据库
这两行中都是可视化的

接下来的 5 天里我都带你使用命令行来写


在 PHPTutorial/MySQL/bin 中
有许多 .exe文件

 其中
mysql.exe
是用来连接数据库的

mysqld.exe
这个带了d的是启动服务器的

在当前地址栏输入 cmd

弹出黑窗口
之前学过的
为了节省时间

我直接跳到 数据库 与 php 连接的阶段

事件回滚与视图没有学

直接学 数据备份

数据的备份与还原

数据量小的 一周备份一次

数据量大的一周备份一次
一般在夜里12点 人流量比较小的时候备份

难道备份的时候半夜12点起来吗
不需要
将来我们写一个定时器
学linux 的时候
windows 也能写 要开启一个服务
比较麻烦一点

linux要简单一些

备份开始

在 PHPTutorial/MySQL/bin 中
有许多 .exe文件

我们利用 mysqldump.exe 进行备份

地址栏 进入cmd
在黑窗口中输入
mysqldump -uroot -proot data>c:\data.sql
          用户   密码   数据库名 备份地址

备份还可以指定 表
mysqldump -uroot -proot data a b>c:\data.sql


备份还可以指定 导出的语句中 带有创建数据库的语法
mysqldump -uroot -proot -B data>c:\data.sql

数据还原

先登录
还是在当前目录
mysql -uroot -proot; 回车 登录

show databases; 回车展示 数据库

create database data1; 新建 数据库 data1
use data1;选中 数据库 data1

学个屁 这么麻烦用工具 点一下就够了


前端 服务器 数据库
我们都学了

以前我们操作数据库用的是
黑窗口
或
可视化工具

但是今天 我们就用php 操作数据库


 apache php解释器 是 前端的 服务器

但是 mysql 又相当于 apache php解释器 的服务器


为什么我要强调
通过php做mysql的客户端?

因为php做mysql的客户端
那么下面我们去设置
字符编码的时候
按 php utf8 设置mysql为utf8编码

黑窗口是 gbk 编码 之前设置了mysql为 gbk编码

也就是说 mysql的编码 必须跟 客户端一致

默认情况 php是连不了mysql的

那怎么办呢
开启他的拓展(mysqli)
去链接数据库

php/php版本/php.ini

搜索 extension=php_mysqli.dll

已经去掉了注释 开启了功能
官方推出的是 有注释的

做这个phpstudy的人帮你开放了这个功能

然后就可以使用 mysqli 开头的函数了

连接数据库
mysqli_connect();

还没有数据库
我们今天先创建一个数据库

咱们今天做一个新闻的 发布
和修改
新闻的增删改查 news


一次get请求 获得所有数据
//实例开始
<!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>

<body>
    <?php
    //1.连接数据库
    require_once './inc/conn.php'; //$link
    //2.查询 mysql 数据库
    $rs = mysqli_query($link, "SELECT * from news order by id desc;");
    // 返回的 true/ 结果集 /false            id 排序 升3asc默认 降4desc
    $list = mysqli_fetch_all($rs, MYSQLI_ASSOC);
    //获取全部关联数组
    echo '<pre>';
    // print_r($list);

    ?>


    <table>
        <tr>
            <th>编号</th>
            <th>标题</th>
            <th>内容</th>
            <th>时间</th>
            <th>修改</th>
            <th>删除</th>

            <?php
            foreach ($list as $rows) :
                // 实例:1
                // foreach($arr as $item){ # code... }
                // 实例:2
                // foreach($arr as $index=>$item){ # code... }
                // 规则 开始 { 变为 :
                //      中间 { 变为 :           } 全省略
                //      结束 } 变为 end开始
            ?>
        <tr>
            <td>
                <?php
                echo $rows["id"];
                ?>
            </td>
            <td>
                <?php
                echo $rows["title1"];
                ?>
            </td>
            <td>
                <?php
                echo $rows["content"];
                ?>
            </td>
            <td>

                <!--   格式化时间戳 
                    时间戳转换为年月日
                    echo date('Y-m-d H:i:s', time());
                    年月日 时分秒  时间戳(不写默认当前时间戳)
                    2021-04-08 04:00:48 -->
                <?php
                echo date('Y-m-d', $rows["createtime"]);
                ?>
            </td>
            <td>
                <input type="button" value="修改" onclick=''>
            </td>
            <td>
                <input type="button" value="删除" onclick=''>
            </td>
            <style>
                table {
                    width: 780px;
                    border: solid 1px #000;
                    margin: 0 auto;
                }

                th,
                td {
                    border: solid 1px #000;
                }
            </style>
        </tr>
    <?php
            endforeach;
            // 实例:1
            // foreach($arr as $item){ # code... }
            // 实例:2
            // foreach($arr as $index=>$item){ # code... }
    ?>
    </tr>
    </table>
</body>

</html>
//实例结束

新闻里面我们已经写了一个显示新闻
那么下面我们写添加新闻

前面获得get.php 是显示请求
现在我们新建一个 add.php
来做 post 请求
 
首先在上面的 list.php 中
补上一句话(跳转入口)

 <a href="./add.php">添加新闻</a>
 <!-- 添加入口 去add.php 页面提交 -->

然后再
在 add.php中
<!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>

<?php
if (!empty($_POST)) { //如果检测到了 post 数据提交
    // 2.连接数据库
    require_once './inc/conn.php'; //$link
    $time = time(); //定义 时间戳 同mysql中的 UNIX_TIMESTAMP()
    echo $sql = "INSERT INTO news VALUES(NULL,'{$_POST['title1']}','{$_POST['content']}',$time)";
    //定义 sql语句(字符串格式)
    //插入 格式 样本
    // "INSERT INTO news VALUES(NULL,'静夜思','窗前明月光',UNIX_TIMESTAMP());"

    //2.查询 mysql 数据库
    $rs = mysqli_query($link, $sql);
    //在php 中使用 mysql 插入一行测试数据

    if ($rs) { //如果插入成功
        header('location:./list.php'); //跳转到 list.php 页面
    } else {
        echo '<br>' . 'SQL语句插入失败<br>';
        echo '错误码: ' . mysqli_errno($link) . '<br>';
        echo '错误信息: ' . mysqli_error($link); //英文
    }
}
?>

<body>
    <!-- 1.创建表单 -->
    <form action="" method="post">
        标题: <input type="text" name="title1" id=""><br>
        内容:<br> <textarea name="content" id="" cols="30" rows="10"></textarea><br>
        <input type="submit" name="button" value="提交"><br>

    </form>
</body>

</html>
//结束

接下来是做删除功能 
删除功能 mysql需要 唯一标识id 
来进行删除
因此我们需要传递一个 id

同样在
list.php 加入入口文件
修改 
<input type="button" value="删除" onclick=""><input type="button" value="删除" onclick="location.href='./del.php?id=<?php echo $rows['id']; ?>'">
注意删除必定要 提醒
再次修改为
  <input type="button" value="删除" onclick="if(confirm('确定要删除吗')){location.href='./del.php?id=<?php echo $rows['id']; ?>'}">
                <!-- confirm 是确认 和 取消对话框 只返回true 或false -->

为 get 提交

删除功能 我们新建 del.php
在 del.php 中做
方便记录和以后回忆时 较快理解

在 del.php 中 
//开始
 
<?php
if (!empty($_GET)) { //如果检测到了 GET 数据提交
    // 2.连接数据库
    require_once './inc/conn.php'; //$link
    $time = time(); //定义 时间戳 同mysql中的 UNIX_TIMESTAMP()
    echo $sql = "DELETE from news where id={$_GET['id']} ;";
    //定义 sql语句(字符串格式)
    //删除 格式 样本
    // "DELETE from news where id=2 ;"

    //2.查询 mysql 数据库
    $rs = mysqli_query($link, $sql);
    //在php 中使用 mysql 删除 id为 xx的 一行测试数据

    if ($rs) { //如果删除成功
        header('location:./list.php'); //跳转到 list.php 页面
    } else {
        echo '<br>' . 'SQL语句删除失败<br>';
        echo '错误码: ' . mysqli_errno($link) . '<br>';
        echo '错误信息: ' . mysqli_error($link); //英文
    }
}
?>
 //结束

add.php是写了html架构的
而del.php 却没写

这是因为 del.php 没有需要展示的内容
只是做业务逻辑
所以不需要写 html架构

这是删除新闻
好

增删改查
下面一个还有一个就是

修改了
是吧

同样得 做 修改入口
修改  <input type="button" value="修改" onclick=''><input type="button" value="修改" onclick="location.href='./edit.php?id=<?php echo $rows['id']; ?>'">

为 get 请求

然后新建 edit.php

在 ./edit.php 中
// 修改更新数据开始 
<!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>

<?php
if (!empty($_GET)) { //如果当前页面检测到了 GET 数据提交
    // 2.连接数据库
    require_once './inc/conn.php'; //$link
    $time = time(); //定义 时间戳 同mysql中的 UNIX_TIMESTAMP()
    echo $sql = "SELECT * from news where id={$_GET['id']}";
    //定义 sql语句(字符串格式)
    //插入 格式 样本
    //  "SELECT * from news where id=2 ;"

    //3.查询 mysql 数据库
    $rs = mysqli_query($link, $sql);
    //在php 中使用 mysql 查询 id 为 xx 的一行测试数据

    if ($rs) { //如果查询成功
        $rows = mysqli_fetch_assoc($rs);
        //一次获取一行 获取mysql查询结果为关联数组
        //把内容 放入修改框的 value 里面
    } else {
        echo '<br>' . 'SQL语句查询失败<br>';
        echo '错误码: ' . mysqli_errno($link) . '<br>';
        echo '错误信息: ' . mysqli_error($link); //英文
    }

    // 二.执行修改的逻辑
    if (!empty($_POST)) { //如果检测到了 post 提交

        $id = $_GET['id']; //要更新 id为 xx 的数据
        $title1 = $_POST['title1']; //要更新的 标题
        $content = $_POST['content']; //要更新的内容

        echo $sql = "UPDATE news set title1='$title1', content=' $content' where id=$id;";
        //定义 修改更新语句

        //修改更新 mysql 格式样例
        // "UPDATE news set title1='哦哦', content='哈哈' where id=2;"
        //3.查询 mysql 数据库
        $rs = mysqli_query($link, $sql);
        //在php 中使用 mysql 修改更新 id 为 xx 的一行测试数据

        if ($rs) { //如果修改更新成功
            header('location:./list.php'); //跳转到 list.php 页面

        } else {
            echo '<br>' . 'SQL语句查询失败<br>';
            echo '错误码: ' . mysqli_errno($link) . '<br>';
            echo '错误信息: ' . mysqli_error($link); //英文
        }
    }
}
?>

<body>
    <!-- 1.创建表单 -->
    <form action="" method="post">
        <!-- 注意 不写是当前 url http://localhost/edit.php?id=37-->
        <!-- 而不是 http://localhost/edit.php -->
        <!-- 这样 修改post提交时 会自动携带 地址栏的 get 提交 -->
        <!-- 此get提交的id 正好 post 需要用到 -->

        标题: <input type="text" name="title1" value="<?php echo $rows['title1'] ?>" id=""><br>
        内容:<br> <textarea name="content" id="" cols="30" rows="10"><?php echo $rows['content'] ?></textarea><br>
        <input type="submit" name="button" value="提交">
        <input type="button" name="button" value="返回" onclick="location.href='./list.php'"><br>

    </form>
</body>

</html>
// 修改更新数据结束


数据库的数据是比较重要的
有的公司甚至每天都备份
利用的是 mysqldump.exe

不想学这个东西
因为 大多数可视化工具有 这功能


php mysql 的链接 创库 建表 增删改查 到此结束


接下来学习传说中要学习 4天的 
php面向对象

今天我们开始
面向对象的
学习

面向对象是一个编程思想
指导你们怎么去编程

那么这个编程的思想又两个
一个是

面向对象

还一个是

面向过程

我们前面刚讲的就是面向过程

这个面向对象和面向过程有什么区别呢

我比如说
我以去饭馆吃饭为例

如果去面向过程
编程的思路就集中在过程上

比如说
我要点菜
我要做菜
我要上菜
然后吃饭
结账
收拾

对吧

这是不是一个过程啊
这是一个过程

如果面向对象
你要怎么写呢

你要参...(没听清)与对象里面去写
面向对象有服务员

有厨师
有客人

啊

那么这里面面向对象有什么好处呢
好处是这样的

大家想想

我吃饭的时候
今天吃饭啊
先点菜
再做菜
再上菜
再吃菜
再结账
再收拾

结果我 第二天吃饭我不是这样子了

我第二天要什么呢
我是先点菜
然后再做菜
点完
我可能是先点菜
点完菜以后
先去结账
结账的时候 他去做菜
做完菜之后
我就上去吃饭
吃完饭我就走了

是不是顺序就有可能不一样

如果循序不一样
你代码要不要重写一次
要重写一次吧

但是问你顺序不一样
如果是面向过程的话
代码要重写一次

那么面向对象
请问同学们
我顺序不一样
参与的对象(指服务员,厨师,客人) 发生变化了嘛

没有

所以说我参与的对象并没有发生变化
这时候
再怎么变(顺序)
你再怎么变(顺序)
你参与的对象没有变化的话

那么我就不需要重新写代码
就不需要重新写代码

好 
这是一个 关键点

就是说 面向对象思路写的代码
维护起来很方便

这是第一个
第二个 
比如说 我们 3 个人来写代码

我做前两个功能
你做中间两个功能
你做后两个功能
是吧
然后 如果点菜做菜还没写完
收拾 结账他能写吗

是不是写后边的等前面写完才能做啊

但是面向对象
你写服务员
我写初始
他写客人

每人写一个对象
这没问题吧

所以说多人合作是不是也方便

???
那么面向对象编程 到底是什么???

通过这个分析
我们就得出一个结论
面向对象的编程思想
编程思路肯定是
比面向过程的要先进一些

刚才我们说
多人合作很方便
减少代码冗余 现在我们还没发现
后面越讲你越能发现

代码的可重用性发挥到极致
这个目前你也没有发现

哦 这个你可以发现了
是这样子的
如果你 点菜 做菜 上菜 结账 收拾

这个顺序不一样
那么你 点菜是不是写了两次
快点说 面向对象 具体 怎么写吧

这比喻一点也不生动形象
越说越懵

由于顺序不一样你要写两遍代码

你这个时候你发现就是说减少代码冗余

有人说我写进函数里调用两次


但是我告诉你
如果我用
面向对象的话
有一些方法 
他会自动调
只需要掉一次就行了

比如 有100 种顺序

那你至少要调用 100次

但是我如果用面向对象
我能减少你的冗余
连你调用的次数都能减少

所以说 这个功能很强大

可扩展性很强
可扩展性
就是说

如果说 这里面我加一个步骤
那么 顺序是不是又变化了

比如这时候我扩展一个步骤

但是如果面向对象呢

我这么说吧
我在前面 加一个步骤

迎宾 后面顺序全部往后推一步

如果面向对象我改 服务员了 后面的厨师和客人改不改

所以他可拓展性就变得很强

代码动的也很少

小结:
面向对象是一个编程思想
他是一个让你用来指导你
怎么编程的
那么面向对象的编程思想有哪些呢

有 面向过程 和 面向对象

面向过程就是把主要的精力集中到过程上
把你编程的思路 集中在过程上

面向对象是 你把你的编程思路集中在参与的对象上

那么以饭馆吃饭为例 如果你去面向过程
一步一步一步的走

面向对象呢 我只是写参与的对象
他的好处
多人合作很方便

面向过程 多人合作就不方便 需要等
前面的事干完 才能干后面的

面向对象服务就很方便了
比如说我写服务员 跟 厨师客人没关系


...
难道这服务员有自己的思想 ??? 有菜端菜 没菜等待?


相同代码我只要这些一次就够了
连调用我都 也只调用一次就够了
但是你面向过程就不行

好了好了 赶紧让我看看面向对象 涨什么样

代码写完以后非常完美
就像艺术品一样

然后可扩展也很强

步骤变了 后面的步骤也变了
但是面向对象 你改了个服务员厨师客人不用改

拓展性很好

好这是面向对象

面向对象他的缩写是 oop 叫面向对象
除了 oop
以外 还有俩个
一个是   OOA 面向对象的分析
另一个是 OOD 面向对象的设计
         OOP 面向对象编程

以后大家看到 oop 就是面向对象编程


缩写呢 大家知道一下就可以了

好了

下面我们继续讲啊

面向对象 面向对象

他不是有个对象吗

那么我们看什么是对象呢

对象是由什么组成的呢

一辆车就是个对象
对象 他有什么型号
            价格
            里程

那么我们就把这称为对象的属性
属性一般是名词吧

难道对象里面是一个运行的监听函数???

那么这个对象就是对象的那个 
行驶
起动
停车

这是对象的一个动作 是吧

动作 动作 也叫方法
方法有称之为函数

难道 对象里采用了闭包的形式 一直监听外部
对外部的变化做出 预定的反应?

好那么我们再来看
一辆自行车也是一个对象 对吧

他有什么 属性
 车轮 有几个轮
 有几个档

他有动作 
 有刹车
 有加速 是吧

唉 这这这 这是自行车

还有什么呢
这有只 犬

叫什么 名字
       他有颜色
       什么品种

还有动作
 动作有 叫
     有 摇尾巴
     有 吃东西

好了 那么经过我的分析
我说 对象是由什么组成的

属性 和 动作(方法(又称函数))

组成的 

对象是一个具体存在的事物
万事万物皆对象
对象是由什么组成的呢
是由属性 本质为变量
和方法   本质为函数
组成的 

下面啊
我问一下
李白是不是一个对象

是吧 

那我问大家一下
如果没有人类会有李白吗

没有人类就没有李白 是吧

你们家养了个 比如说叫大黄

是吧

大黄是不是对象
如果没有狗类会有养的大黄吗

没有 

这时候 我们是不是就说了

对象和类是不是 
紧紧挨到一起

那么我们下面就讲类

类是什么 类是具有相同属性行为的一组几集合

比如说类有雇员类
有 汽车这一类
有动物这一类

你买的那辆汽车是不是一个对象

没有汽车这一类 
你怎么买对象

秦始皇 没有汽车这一类 到哪买
买都买不到

所以说 类是什么
是相同 属性 行为的一种集合

讲这个干啥
我想讲一个惊天的大秘密
...
哪儿来的 惊天大秘密
什么秘密呢

举个例子啊
你去饭馆吃饭
要不要厨师啊
要厨师

要厨师 还是要厨师那个做饭的动作

最终你是要厨师做的饭是吧

是不是没有厨师是不行的对吧

那你想要的是厨师做饭的那个人
还是厨师做饭的那个动作

其实你来 教室读书
不是要老师

你是要老师讲课的那个动作

但是你想一想
如果没有那个老师在
它能有讲师的动作吗

他就没有
这就牵涉到刚才那个问题了

你到这个饭馆去吃饭
你要的是什么
你不是要厨师
要的是
你要厨师干啥啊
厨师养那么胖天天吃你的
喝你的
你要它干啥
你要它是厨师给我做饭的那个动作

要厨师这个动作
你说其它厨师做饭没这个做的好吃
我就要
厨师这个动作

其实这个厨师做饭做的好吃
我就要这个厨师
从来没有人
说把厨师带你家去
供你吃供你穿
没有的

我要的厨师也是厨师帮我做的那个饭

做饭那个动作
那么
你没有这个厨师你就没有这个动作
是不是

好了
换句话说
如果我要写代码

你想一想
是不是只有厨师调用了那个动作
我的饭才出来

但是写代码的时候
是不是一定得先有厨师
有了厨师以后

是不是才调用那个厨师的动作

其他的那个厨师
比如叫张三
比如叫李四

得出来那个步骤啊
说我们首先
要的时候
要的是这个动作
要的是这个做饭的动作

但是
你要想这个动作必须要有什么

厨师对象
厨师类
他是一个对象的

是不是所以这时候结论
我们要开发的时候开发什么

写什么 写类
有了类以后再去创建对象

从对象里面

所以我刚才讲了这么多

就是得出一个结论

一个开发的顺序
开发的顺序

首先先写 类(专业术语)
通过类去创建对象

通过对象去调用它具体的动作
其实我们最终
最终想要干什么
想要通过动作
去实现我们的效果
但是我们
你要想有这个动作
必须先有对象
要先有这个对象
必须要先有类


所以开发的时候先写类
再写对象
再写方法

所以我们说先写类

通过类去创建对象
通过对象去调用对象的成员

类和对象什么关系呢
一个类可不可以创建
多个对象

类是一个模具
相当于类是一个人
人是人类 
人类可以创建 好多好多人

李白 啊
杜甫啊
白居易啊

人为创造出来的
这是一个类

可以创建
多个对象

下面我小结一下

小结 : 
	对象是由属性和方法组成的
	一个类 是其子对象的相同属性和方法的 源头


在开发的时候先写类
通过类创建对象
通过对象调用其 属性 或函数(方法)

然后一个类 可以创建多个对象

下面我根据这个顺序

咱们以前讲的一个是不是先写类啊

然后是不是再创建对象
是不是

那么现在我们创建类
类里面类通过什么关键字呢

class 类名{
	//属性
	//方法
	//常量
}

类是由 //属性
	//方法
	//常量

组成的
对象是由 //属性
	//方法

组成的

好
下面是类的命名规则
类的命名规则

跟变量的命名规则是一样的
不能用php关键字做类名

注意类名时不区分大小写的
变量名区不区分大小写

变量名是区分的
常量名 可设置 区不区分
函数关键字 不区分大小写

类名用 帕斯卡命名法 (就是驼峰命名法)

创建类
我写一下
怎么写呢

class Student{}

这就已经写好了 一个空类

那么下面 
下面啊

你写好了以后就要实例化了
先创造类

有类以后创建对象
创建对象的过程叫实例化(专业术语)

new Student();

//样例开始
<?php
class Student //创建类
{
}

$a = new Student(); //根据类 衍生出一个对象
$b = new Student;
//小括号可省略
var_dump($b, $a); //object(Student)#2 (0) { } 
//object(Student)#1 (0) { }
//对象 是 学生类 的衍生对象 #号是编号 从1开始

// 那么这两个对象相等吗
var_dump($b == $a); //是相等的(不比较 编号# )
//bool(true)

var_dump($b === $a); //但是不全等( 会比较 编号# )
// bool(false)

// 假设    (所有对象的传递 默认传的是地址)
$c = $b; //传的是地址 传值会令 c成为 #3
var_dump($a, $b, $c); // c还是 #2
// object(Student)#1 (0) { } 
// object(Student)#2 (0) { } 
// object(Student)#2 (0) { }

// 因此
var_dump($c === $b); //为bool(true)

// 接下来往里面添加属性
// 属性的本质就是变量 $name

class Student2 //定义类 样例
{
    public $name; //属性
    //必加
    public $add = "哈哈"; //属性
}
$d = new Student2; // 从类实例化对象  样例    ()可省略
echo '<br><pre>';
print_r($d);
//Student2 Object
// (
//     [name] => 
//     [add] => 哈哈
// )

// $d['name] = '阿萨德';//无法使用常态方法操作 类实例化出来的对象

//2.额外的数据操作方法
$d->name = '阿萨德';
print_r($d);
echo '姓名:', $d->name;
// 姓名:阿萨德

//3.添加属性 同上 只要给类对象添加属性就是公有的
$d->age = 20;
echo '<br>';
print_r($d);
// Student2 Object
// (
//     [name] => 阿萨德
//     [add] => 哈哈
//     [age] => 20
// )

//删除类对象的属性 使用  unset()函数
unset($d->add);
print_r($d);
// Student2 Object
// (
//     [name] => 阿萨德
//     [age] => 20
// )
//add 就被删除了

// js中  对象. 访问对象 属性      delete 对象.            
//php中  对象-> 访问对象 属性      unset(对象->)删除属性
//样例结束

下面我们讲方法
方法的本质就是一个函数

<?php
class a9
{
    public function b9() //对方法时 public 可省略 默认为public
    {                    //对属性时 不可省略
        echo '这是函数方法';
    }             //js里的对象 函数是放在属性里面的 
}

$c9 = new a9;
echo '<br><pre>';
$c9->b9();

?>


public 详解
public 又称为访问修饰符

访问修饰符是用来控制
成员的访问权限

像这种控制权限的有三种
public 公有的
private 私有的     类创造的对象  只能被类自己访问
protected 受保护的

一般来说 类创建对象时属性变量 
设为 私有

而类 创建对象时 函数方法
设为 公有


再通过 也只能通过 公有的
函数方法去操作 类创建对象后的
私有属性变量


那具体是怎么 函数方法 操作 属性变量呢

//公有函数操作私有变量属性开始
<?php
class aaa
{
    private $gg; //私有 属性变量
    private $vv; //私有 属性变量

    public function bbb($a, $b) //存 私有属性变量
    {
        if ($a != 'xx') { //唯一通道
            return; //数据验证
            // exit;//或者
        } //保证数据的安全性

        $this->gg = $a;
        $this->vv = $b; //$ccc 对象调用的这里 
        //this指向 $ccc
    }

    public function ddd() //取 私有属性变量
    {
        echo  $this->gg, $this->vv;
    }
}

$ccc = new aaa;
echo '<br><pre>';
$ccc->bbb('阿萨德', '哈哈'); //存 私有属性变量
$ccc->ddd(); //取 私有属性变量

?>
//公有函数操作私有变量属性结束



接下来介绍一个概念叫封装
生产汽车公司     的     采购经理
需要访问生产齿轮公司的  市场经理

生产齿轮也要访问生产汽车的

但是 齿轮厂 
		制造方法
		库存是多少
		制造成本
肯定不能让 
生产汽车公司 的 采购经理
知道

生产汽车公司 的
            汽车组装方法
		汽车库存
	汽车的组装成本

        也不能让
齿轮厂 知道

什么叫封装呢
就是有选择性的提供数据


公有的别人可以访问
私有的别人不让访问

所以封装就是有选择性的提供数据


属性和方法都各有
公有的 和 私有的

封装就是有选择性的提供数据

那么怎么来实现封装呢
通过 访问修饰符 来实现封装

...难道 很简单

说白了就是

public 公有的
private 私有的     类创造的对象  只能被类自己访问
protected 受保护的

三种

好这是封装里面的概念

接下来讲 类和对象在内存中的分布

这是今天最重要的一个内容
后面你能学的怎么样

完全取决于就是这个知识点的掌握
这就是整个面向对象的一个腰

面向对象你要理解的很深刻

这个 类和对象在内存中的分布
你要把他搞清楚

首先对象的本质
是个什么

对象的本质是个变量
类的本质是个数据类型

类的本质是个数据类型
这个要搞清楚
大家看啊
我比如说

举个例子

class yyy
{
    public $name; //私有 属性变量 
}

这 里的$name 并没有 被声明内存空间

所以 整个 class yyy 只是个数据类型

这个数据类型

是个复杂的数据类型

这个类型里面包括了
两个简单的类型


开始 关于类对象在内存 空间中的实际划分
<?php
class aaa
{
    private $gg; //私有 属性变量
    private $vv; //私有 属性变量

    public function bbb($a, $b) //存 私有属性变量
    {
        if ($a != 'xx') { //唯一通道
            return; //数据验证
            // exit;//或者
        } //保证数据的安全性

        $this->gg = $a;
        $this->vv = $b; //$ccc 对象调用的这里 
        //this指向 $ccc
    }

    public function ddd() //取 私有属性变量
    {
        echo  $this->gg, $this->vv;
    }
}

$ccc = new aaa;
echo '<br><pre>';
$ccc->bbb('阿萨德', '哈哈'); //存 私有属性变量
$ccc->ddd(); //取 私有属性变量

?>



<?php
class student3
{
    private $name;
    private $sex;

    public function show()
    {
        echo '我是个男孩';
    }
}

$stu1 = new student3; //实例化
//实例化的过程 就是分配内存的过程
//不实例化是不 分配内存/划分空间 的

$sum = 10; //什么意思
// 计算机在内存里面划出一道空间

$stu2 = new student3;
// 是不是又划分了一份 空间

// 请问我现在划分了几个name
// $stu1的  $name 和 $stu2的 $name
// 是不是同一个name  
//是两个 $name
//内存中是两份


var_dump($stu1); //结果展示为 (并没有函数)
// object(student3)#6 (2) {
//     ["name":"student3":private]=>
//     NULL
//     ["sex":"student3":private]=>
//     NULL
//   }

//但是我们依旧能调用它
$stu1->show();

//这是什么原因
//这是后面我要讲的 内存
//你把里面的结构搞清楚
//你才知道啥原因

//现在我讲了这么多
//我想说什么呢
//对象是一个复杂的变量

//里面包含了什么
//是不是包含了两个属性啊
//你对象里面你看到方法了吗
//没有对象里面你只看到了
#什么啊
#只有两个属性 且值为 null

//对象里面是没有方法的
//只有两个属性

//老师但是这怎么能调呢
//到了后面我跟你讲原理

/**
 * 但是现在 看到的是什么
 * 是不是就两个普通的属性
 * 所以我说他是个复杂的变量
 * 因为什么
 * 一个复杂的变量 包含了两个普通的变量
 * 
 * 那么这种对象 他是什么类别的对象
 * (反复听了很多遍 没听清 时是流内存的 )
 * 所以类的本质是什么
 * 类的本质是一个复杂的数据类型
 * 对象的本质是一个变量
 * 类的本质是一个自定义的数据类型
 * 
 * 那么下边我们再来
 * 要想理解 类(只是类 不是类对象)里边的结构
 * 有几个区 大家要搞清楚
 * 有 堆区 和 栈区
 * 
 * 当然 这种 区 有很多
 * 我们要想理解的深刻一点
 * 
 * 在内存中共有 4个区

 * 堆区:          较大 速度慢 大文件(比如 类对象)
 * 栈区:          较小 速度快 小文件 大文件的快捷方式(指类对象地址)
 * 代码区:        类 class 存放的地方
 * 常量/静态 区:  
 * 
 */
?>
//结束 关于类对象在内存 空间中的实际划分

接下来我们小结一下
对象的本质是一个复杂的变量

对象里面没有方法
通过 对象(类数据类型) 调用类的方法

所有类对象调用的都是同一个类的同一个方法

对象里面实际上不存方法

虽然用起来 感觉方法在对象里面

1.对象的本质是一个复杂的变量
2.类的本质是一个自定义的复杂的数据类型
3.栈区:运行速度快,体积小,保存基本类型
4.堆区:运行速度稍慢,体积大,保存复杂类型
5.实例化的过程就是分配内存空间的过程
6.对象保存在堆区,将堆区的地址保存到栈区

首先你看加载类
加载好了以后我是实例化

好
问大家一个问题呀
问大家一个问题

方法
我们上午讲的方法
调用执行吗


 
上午我们讲的方法
不调用不执行
那个方法嘞

我们叫普通方法
不调用不执行的

那么在 php中
有一个方法
非常非常的特殊
就是你不调用他也执行

当然了前面提到过 用来监听外部事件
从而自动 做出反应

这也是面向对象的精髓

那么这个方法就叫构造方法

构造方法也就构造函数
他是自动执行的
就是不需要

你调用啊

不需要你调用啊
他是自动执行的

那什么时候他自动执行的呢

当对象创建的时候自动执行
那么他的语法是
function __construct(){}

两个下划线


//开始
class StudentStudent
{
    function __construct()
    {
        echo '<br>这是类对象创建既执行的构造函数方法';
    }
}

new StudentStudent;//这是类对象创建既执行的构造函数方法
new StudentStudent;//这是类对象创建既执行的构造函数方法
//结束

每通过类实例化对象一次
就会执行一次类里面的构造函数

tip: 在其他语言里面构造方法一般就是 
      类的名字作为方法名
      而不是 __construct

虽然PHP中   function StudentStudent()
这么写也能运行 但是php会告诉你 已弃用

那么构造方法他用来
干什么的呢

一般是用来初始化成员的

//示例 开始
class StudentStudent
{
    private $a;
    private $b;
    function __construct($o, $p)
    {
        $this->a = $o;
        $this->b = $p;
    }
}

$c = new StudentStudent(1, 2);
print_r($c);
// StudentStudent Object
// (
//     [a:StudentStudent:private] => 1
//     [b:StudentStudent:private] => 2
// )
//示例 结束

构造函数里是不能return 
会报错

我们上午讲的函数不调用
方法不执行

当对象创建的时候他就执行
这个方法就叫构造方法
构造方法也叫构造函数
它名字必须是 __construct(){}
双下滑线


当我实例化的时候自动调用这个方法
其主要作用就是初始化 
类对象里的
变量的


刚才我说有一个方法
非常非常特殊

你不调用他都执行
什么时候呢

当他实例化的时候
当这个对象产生的时候

他就调用
同学们一个对象
有两个状态特别重要

一个是生 一个死

生的时候自动调用一个方法
死的时候也会自动调用一个方法

这倒是有点像vue生命周期函数

那他死的时候调用的方法
就叫什么呢

析构方法
就当被类创建的对象被销毁之前
他会自动调用

//析构函数 样例 开始
class StudentS
{
    private $a;
    private $b;
    //构造方法
    public function __construct($o, $p)
    {
        $this->a = $o;
        $this->b = $p;
        echo "<br>类的对象即将创建<br>";
    }
    //析构方法 无法携带参数
    public function __destruct()
    {
        echo '<br>类的对象创建完成 实例化die后 本函数即将被调用';
    }
}

$c = new StudentS(1, 2);
print_r($c);
// 类的对象出生了
// StudentS Object
// (
//     [a:StudentS:private] => 1
//     [b:StudentS:private] => 2
// )

// 类的对象即将被销毁

//析构函数 样例 结束

题目;在上面的基础上
$c1 = new StudentS(1, 2);
$c2 = new StudentS(3, 4);
$c3 = new StudentS(5, 6);

销毁过程是怎样的
$c1 实例化  
$c2 实例化
$c3 实例化
$c3 结束实例化(销毁)
$c2 结束实例化(销毁)
$c1 结束实例化(销毁)

为什么会这样呢
下面我给大家讲一讲
计算机的内存管理


我们下面说计算机的内存是怎么管理的

它决定了我是怎么个销毁方法
计算机内存管理有两种
一种是
先进先出
还一种是
先进后出

先进先出一般用到哪呢
秒杀 ,购票 等等

比如我先进来的
我先登录的

应该先把东西给我

这种先进先出我们画一个示意图


类似于管子从左往右 先进先出

除了这个以外
还有一种

他不是 类似于一根管道

先进先出

他是个茶杯 烧杯

理所当然自然是 
先进后出  
因为是茶杯嘛

计算机的默认内存管理
就是先进后出的
茶杯形式

函数也是先进后出的

一般自己写逻辑的内存管理就是先进先出


计算机的默认管理全部都是先进后出
(杯子式容器)

先进的东西垫在最底下了


手动 unset()函数可提前手动销毁 类对象
(类似于从杯子空间 中间提前穿过杯子 
手动拿取 类对象 出来 并销毁)


然后 计算机默认 先进后出的循序继续销毁



题目;在上面的基础上
new StudentS(1, 2);
new StudentS(3, 4);
new StudentS(5, 6);

销毁过程是怎样的

1, 2 实例化  
1, 2 结束实例化(销毁)

3, 4 实例化
3, 4 结束实例化(销毁)

5, 6 实例化
5, 6结束实例化(销毁)

 
由于 new StudentS()
没有变量引用它
所以 计算机当做垃圾回收了
所以 刚实例化完就销毁了



题目3;继续 在上面的基础上
$a=new StudentS(1, 2);
$a=new StudentS(3, 4);
$a=new StudentS(5, 6);

销毁过程是怎样的

1, 2 实例化  

3, 4 实例化
1, 2 结束实例化(销毁)

5, 6 实例化
3, 4 结束实例化(销毁)

5, 6结束实例化(销毁)

也很简单 
3,4 的实例化导致 
1,2没有被变量引用
因为共用一个变量
所以 1,2会被计算机 当垃圾回收
所以是以上的结果


这是析构函数的3道题

下面我们说继承

继承这个东西跟我们
现实中的继承的一样的

          动物
  食肉            食草
狮子 老虎        牛  羊

动物(类)的特性
其子类
会被继承下来

动物有个方法叫吃饭
它所有底下子类都有吃饭

所以 说继承
子类就会继承了父类的特性

他有什么好处啊
好处就是 代码可重用性

直接写一遍就不用写了
直接继承下来了

那么
我问你啊
大家问一个问题

对于动物(父类)和
食肉动物(子类)
 
狮子老虎又是
食肉动物的子类

食肉动物又是
狮子老虎的父类

所以没有一个绝对的
父类和子类

都是相对的

那么继承呢

继承使代码更有层次性
而且子类继承了父类的属性和方法

那么怎么实现继承呢
我们一般 都是通过 extend 
实现 类与类之间的继承

样例
class a{}
class b extends a{}

那么下面我给大家写一道题

// class Person
// {
//     public function show()
//     {
//         echo '这是人类<br>';
//     }
// }
// //子类
// class Stude251nt extends Person
// {
// }
// // 测试
// $stu = new Stude251nt;
// $stu->show(); //这是人类

/** 但是这里面内部执行大家千万不要以为
 * 哦 继承就是 从上面拷贝 往下面一放
 * 不是的
 * 第一步:在 Stude251nt中查找 show(),如果找到就调用.
 *         找不到就到父类(Person类)查找
 * 第二步:在 Person类 中查询show()
 * 
 * 好 这个是继承
 * 下面小结一下
 * 继承使得代码更有层次性
 * 
 * 子类继承了父类的代码
 * 实现了代码的可重用性
 * 
 * 使用 extends关键字实现继承
 * 
 * 那么继承就有父类和子类了
 * 父类和子类是相对的
 * 
 * 这里面有个父类
 * extends 其实就是父类
 * 成这样了以后
 * 那么他就
 * 一实例化就会调用show()
 * 调用show()并不是说
 * 把父类的show()
 * 放到子类来
 * 并不是这样子的
 * 
 * 好 下面啊
 * 我们说在
 * 子类中如何调用父类的成员
 * 就是在子类的里面
 * 怎么调用父类的成员
 *  */
// class Person
// {
//     public function show()
//     {
//         echo '这是人类<br>';
//     }
// }
// //子类
// class Stude251nt extends Person
// {
//     public function test()
//     {
//         //方法1:
//         // $person = new Person();
//         // $person->show();

//         //方法2:(推荐)
//         $this->show();
//         //这里没有 show() 会自动去父类找
//     }
// }
// // 测试
// $stu = new Stude251nt;
// $stu->test(); //这是人类

/**
 * protected(受保护的)
 * 与
 * public(公有的) 
 * private(私有的)
 * 同类
 * 
 * protected(受保护的)
 * 是在继承里面使用的
 */

class A
{
    // private $num = 10;//报错 私有的
    // protected $num = 10; //正常 受保护的 只能在 类 和子类 中访问
    // public $num = 10; //正常 公有的
    public function getNum()
    {
        echo $this->num;
    }
}
class B extends A
{
    protected $num = 10; //正常 受保护的
    //可以在 在类 和 子类(继承链) 随意访问  也就是
    //父类函数 可以 通过 $this->name 访问 子类的变量

}
//测试
$obj = new B(); //10
// $obj = new A(); //Undefined
//Notice: Undefined property: A::$num in D:\phpStudy\PHPTutorial\WWW\创建类.php on line 369
$obj->getNum();
/**
 * 最终总结下
 * protected 受保护的是在整个继承链子上修饰
 * -----------------------------------------------------
 * 
 * 好
 * 我们下面再说这个关于继承中的构造函数
 * 
 * 在子类中关于子类中怎么去
 * 构造函数
 * 是个什么样子的
 */
class Person
{
    public function __construct()
    {
        echo '这是父类';
    }
}
class Student extends Person
{
    public function __construct()
    {
        echo '这是子类';
        Person::__construct(); //这是子类这是父类
        parent::__construct(); //这是子类这是父类 parent 代表父类的名字
        //两者 是同一个意思
        //但是 parent 可以不知道 父类的名字具体是谁
    }
}
new Student(); //此实例化过程
//会自动调用 子类的 构造函数 __construct() '这是子类
//如果子类没有才
//会自动调用 父类的 构造函数 __construct() 这是父类

//那如果我要想调用 两类的 构造函数 呢
//在子类 构造函数 添加
// Person::__construct();

/**
 * 耦合性高 联系比较紧密
 * 耦合性低 联系没那么紧密
 * 
 * 编程时是耦合性 低 好
 * 我这处改了
 * 其它地方别动
 * 
 * 如何降低  父子类的 耦合性
 * 在子类 构造函数 添加
 * 继续插入 parent::__construct();
 * 
 * 下面为大家小结一下
 * 其实构造函数有个规则
 * 
 * 说如果 
 * 子类有构造函数就调用子类的
 * 子类没有
 * 父类有构造函数就调用父类的
 * 
 * 都有
 * 那调用完子类默认不再调用父类了
 * 
 * 那么说我就想调用父类的
 * 可以在子类构造函数中插入
 * parent::__construct();
 * 
 * parent 指代父类
 * 
 * 那么有什么作用呢
 * 例题 写一个例题
 * 给父类传递参数
 */
class Persson
{
    protected $name; //受保护的
    protected $sex;
    public function __construct($name, $sex)
    {
        // $this->sex = $sex;
        // $this->name = $name;
    }
}
class Studesnt extends Persson
{
    private $score; //私有的 私有的在父类时子类读取会报错
    public function __construct($name, $sex, $score)
    { //构造函数 就是初始化数据用的
        $this->score = $score;
        // parent::__construct($name, $sex);
        $this->sex = $sex;
        $this->name = $name;
    }
}
//测试
$stu = new Studesnt('tom', '男', 88);
echo '<br><pre>';
print_r($stu);
//搞不懂为什么要通过  双构造函数传值
//明明可以直接在子类$this->name 赋值 到父类 上去

/**Studesnt Object
(
    [score:Studesnt:private] => 88
    [name:protected] => tom
    [sex:protected] => 男
) */

/**
 * 老师并未做解释
 * 加下来讲$this 
 * $this 表示对当前对象的引用
 * 当前对象的引用是什么
 * 也就是说
 * $this 保存的是当前对象的地址
 * 
 * 同js的this 谁调用this this就指向谁所在的环境
 * 
 * 
 * 接下来讲多重继承的关系
 * 上面讲的是继承一个 单继承
 * 
 * php 不允许多重继承
 * 好耶
 * 可以少学一个
 * 
 * 因为容易产生二异性
 * 
 * 只有c++ 和go 语言是允许多重继承的
 * 但是就算有 也建议不要使用
 * 
 * 那如果我就要 将a 和 b 继承到 c上呢
 * s
 * 这时候就要形成继承链
 * a > b > c 逐一继承
 * 这样就不会产生二异性了
 */
继承链很常用啊
基本上都用 3个


/**
 * 今日 2021 4 15
 * 下面我们讲一个概念叫
 * 多态:多种形态
 * 多态分为两种
 * 一种是方法重写
 * 一种是方法重载
 * 
 * 那么什么是方法重写呢
 * 同学们我写一道题啊
 */
class Per爱读书son
{
    public function show()
    {
        echo '这是父类';
    }
}
class Stude阿萨德nt extends Per爱读书son
{
    //子类重写了父类的同名方法
    //这个就叫方法重写
    public function show()
    {
        echo '这是子类';
    }
}
//测试
$stu = new Stude阿萨德nt;
$stu->show(); //这是子类
var_dump($stu);
/**
 * 子承父类 中父子 拥有同名 普通函数
 * 这种名字相同的普通函数就叫多态
 * 
 * 子类重写了父类的同名方法
 * 这个就叫方法重写
 * 
 * 实际上计算机里面有两个show()
 * 
 * 但是就结果而言 
 * 给人的感觉就是
 * 子类把父类的 show() 给覆盖了
 * 重写了
 * 子 把 父重新实现了一遍
 * 
 * 所以说起术语的名字叫方法重写
 * 
 * 假设参数个数不一样呢
 * 
 * 父类中
 *  public function show($a)
 * 
 * 子类中
 * public function show()
 * 
 * 会警告
 * 两者 函数 无法兼容
 * 
 * 也就是说
 * 重写的方法中的参数个数要一致
 * 
 * 再来 案例 
 * 父类中
 * protected function show()
 * 
 * 子类中
 * public function show()
 * 
 * private 私有的                        更严格
 * protected 受保护的 公有的(仅在父子上 ) 更宽松
 * public 公有的(所有地方公有)            最宽松
 * 
 * 当 父类中
 * 使用 比子类 更严格 的 修饰符 
 * 修饰(在子类中 的同名) 普通 函数
 * 才不会报错
 * 
 * 换种说法
 * 子类重写父类 不能使用更严格的修饰符
 *              但可以使用更宽松的修饰符
 * 
 * 
 * 方法重写说完了
 * 下面我们说方法重载
 * 在同一个类中 有多个同名的方法
 * 
 */
// class S阿萨德tudent
// {
//     public function show()
//     {
//     }
//     public function show($a)
//     {
//     }
//     public function show($a, $b)
//     {
//     }
// }
/**
 * 调用 show的时候 
 * 根据传入参数的不同数量
 * 调用 3种不同形式的 show()方法
 * 这就是方法重载
 * 
 * 但是php 不支持方法重载
 * 但是php可以通过其它方法来
 * 模拟方法重载
 * 
 * 有同学可能要讲了
 * 讲半天不支持
 * 你讲给我听干嘛
 * 
 * 我讲的是面向对象的语法
 * 这个语言不支持
 * 不代表
 * 其他语言不支持
 * 
 * 其他语言是支持的
 * 
 * 我讲的面向对象是个思想
 * 语言是个载体
 * 
 * 通过这个语言可以实现这个思想
 * 
 * 我通过那个语言也能实现
 * 这个思想
 * 
 * 支持的有 java cshpy(洗刷扑)
 * 
 * 后面 来模拟一下方法重载
 * 
 * 到此为止
 * 
 * 面向对象的 3 大特性就讲完了
 * 1.封装  修饰符 (唯一:public修饰函数可省略)
 *    private 私有的                        更严格
 *    protected 受保护的 公有的(仅在父子上 ) 更宽松
 *    public 公有的(所有地方公有)            最宽松
 * 
 * 2.继承 class 子类 extends 父类 {写子类}
 *   protected 受保护的 公有的(仅在父子上 ) 更宽松
 *   可以使用在子中 $this->父类 变量/函数
 *      使用父类的变量 或 调用弗雷德函数
 *      
 *   周期函数 生   __construct  构造函数
 *            死   __destruct   析构函数
 *          
 *    在子类中调用 父类的  生/死函数 并传参(与$this->父类的变量 等效)
 *         父类::__construct($name, $sex);
 *     或          
 *         parent::__construct($name, $sex);
 * 3.多态 
 *   方法重写
 *    父子类 具有同名普通函数
 *    子类(同名普通函数的修饰符)不能比父类更严格
 * 
 *   方法重载(php不支持 但可以模拟 后面讲)
 * 
 * 
 * 好 
 * 下满来一个绝的啊
 * 是今天
 * 最有意思的
 * 之前
 * 
 * 今天所有知识点里面这个知识点
 * 最有意思
 * 
 * 老师: 哎呀我就指望这个活着呢 呵呵呵
 * 可好玩了
 * 
 * 下面来看
 * 同学们 昨天我们是不是将继承啊
 * 
 * 子类继承了父类 的 成员 属性和方法
 * 但是私有的 能不能继承呢
 * 我们好像没说 昨天
 * 
 * 没说吗
 * 说了啊 
 * 私有 不能调用 会报错
 * 需要使用 受保护的修饰 protected 才能
 * 在父子类之间以私有的形式调用
 * 
 */
class A阿萨德
{
    private $name = 'PHP';
}
class B阿萨德 extends A阿萨德
{
    private $name = 'Java';
}
$obj = new B阿萨德();
echo '<br><br><br><br><br><br>';
// echo $obj->name;
// Java
var_dump($obj);
/**
 * object(B阿萨德)#5 (1) {
 *   ["name"]=>
 *  string(4) "Java"
 *}
 * 父子类共用同一个 $name 
 * 
 * 子类再次赋值 $name 
 * 相当于给 父类 $name 重新赋值(重写(专业术语))
 * 
 * 父类改为
 * private $name = 'PHP';
 * 子类改为
 * private $name = 'Java';
 * 
 * object(B阿萨德)#2 (2) {
 * ["name":"B阿萨德":private]=>
 * string(4) "Java"
 * ["name":"A阿萨德":private]=>
 * string(3) "PHP"
 *}
 *
 *  由此可见
 * 私有属性也是可以继承的
 * ......
 * 为什么 前面理解错了
 * 可以继承
 * 
 * 前面学的有点混乱
 * 的确 老师没有细讲
 * 而 只是我的推断 (为了大脑方便理解从而自身想出合理的解释)
 * 
 * 其实是错误的 片面的
 * 
 * 现在重新来看这个知识点
 * 
 * private
 * 私有属性可以继承但不能被重写
 * 在私有属性中
 * 谁(类对象)调用$this.name 并不会指向哪个类的私有name
 * 
 * 而是只能调用 函数所在类 自己私有的 name
 *    调用不到  this指向的另一个类 及其私有 name
 *    
 *    也就是说 子类 this有两个 父子name 但是父类中函数
 *            只有访问 自己(父类)name的权限 即使this指向的是子类
 * 
 * 
 * public
 * 公有属性可以被重写但不能被继承
 *  当为 public 公有是 重写了
 *   所以父子类中都只有 一个 name
 *   
 *   父元素 私有 变量$name
 *   子元素 公有 变量$name
 *   子元素 继承了私有变量 父$name
 *        自己也有  公有 变量$name
 * 
 *   父类对象 调用时优先 访问 自己私有的$name
 *   子类对象 不能访问父私有的$name 只能访问自己的$name
 * 
 * 
 * 
 * 好了下面我们来说
 * 
 * 方法修饰符(3个)
 * 
 * static 静态的 变量/函数
 * 
 * 静态成员加载类的时候分配空间
 * 程序执行完毕后销毁
 * 静态成员 在内存中就一份
 *                 (加载类的时候就已经加载好了*(占用好了空间 分配好了))
 *                  在new 类的时候 实例化 不会再占用内存空间了
 *                  因为之前已经 占用好了
 *             
 *                 如果不写  static  加载类的时候是不会分配空间的
 *                   new 的时候(实例化) 才分配
 *                     但是new 多次 也只分配第一次 
 *                       后面不会也没必要 重复分配
 * 
 * 如果是静态的方法
 */
class Pers阿萨德on
{
    public static $add = '北京';
    static  public  function show()
    {
        echo '这是一个静态的方法';
    }
}
echo Pers阿萨德on::$add; //北京
Pers阿萨德on::show(); //北京

/**
 * 同样 不需要new 实例化 类对象
 * 就可以调用 内存中的静态方法 
 *  Pers阿萨德on::$add
 * 
 * 原来的调用方式
 * $a= new 类对象
 * $a->add();
 * 
 *  修饰符
 *   美丽的漂亮的小明
 * 和漂亮的美丽的小明
 * 
 * 有没有区别 没有
 * 修饰符是没有顺序的
 * 
 * static  public  function show()
 * public  static  function show()
 * 是一样的 没什么区别
 * 
 * 小结:
 * static 在内存中就一份 (好像都是一份吧 谁能存两份? 
 * 主要是类创建时就 存了 一般都是  new 实例化 存进变量里的)
 * 
 *
 * 
 * 下面我们写一道例题啊
 * 就是统计在线人数
 * 
 * 就是 来一个人 我就+1 来一个人我就+1
 * 
 */
class Studen阿诗丹顿t
{
    private static $num = 0;
    public function __construct() //构造函数 生 de是死析构函数
    {
        self::$num++;
        //self 指代当前类的名字
    }
    public function __destruct() //死析构函数
    {
        self::$num--;
        //self 指代当前类的名字
    }
    public function show()
    {
        echo '<br><br><br><br>总人数是' .  self::$num;
        //self 指代当前类的名字
    }
}
$stu = new Studen阿诗丹顿t;
$stu2 = new Studen阿诗丹顿t;
$stu3 = new Studen阿诗丹顿t;

$stu2->show(); //3
unset($stu2); //垃圾创建既销毁  有变量保存的 类对象不是垃圾 不会被销毁 需要手动销毁
$stu3->show(); //2 被手动销毁了一个
/**
 * 按道理来说是两个人 为什么会是 1 呢
 *  private $num = 0;
 * 因为第二次 重新赋值了啊
 * 
 * 因此我们要使用 静态$num 
 * 如果用了静态就不能用  $this->num
 * 必须使用  静态$num所在的 类 的名::$变量名;
 *                 Studen阿诗丹顿t::$num; 
 * 
 * 成功记录创建 new 类对象的次数
 *  为 2
 * 
 * 同学们问一下 啊
 * 
 * 假如 $num所在的 类 名字 Studen阿诗丹顿t 被改了
 * 
 * Studen阿诗丹顿t::$num; 
 * 这里的 名字要不要改
 * 要吧
 * 
 * 那怎么 有什么方法不改
 * 使用降低耦合性的 parent::__construct($name, $sex); ?
 * 
 * 不不不  parent代表父类的名字
 * 
 * 今天我们再学一个 
 * (关键字)   self 代表当前类的名字
 * self::$num; 
 * 
 * 降低耦合性 (专业术语)
 * 
 * 
 * 
 * 
 * 延时绑定
 * 
 */
class Per这是人类son
{
    public static $address = "中区";
    public static function show()
    {
        echo  '这是人类';
    }
}
//继承
class Stu1dent extends Per这是人类son
{
} //测试
echo Stu1dent::$address; //中区 子类会继承 父类的静态变量

Stu1dent::show(); //这是人类             以及静态函数

/**
 * 好 
 * 这是静态成员可以被继承
 * 
 * 下面再讲一个
 * 关于 static
 * 的最后一个知识点
 * 叫 延时绑定
 * 
 * 这是什么意思呢
 * 我写一个
 */
class Per9son
{
    public static $address = "<br><br><br>谁在调用show()1";
    public static function show1()
    {
        echo  static::$address;
        //这么写的话 
        //这个static 代表什么 你猜一猜
    }
}
/**
 * :: 是静态调用
 * 你只要看到 :: 的
 * 前面基本是类名
 * 
 * static::$address
 * 
 * static 那static 类名代表什么意思呢
 * 当前对象
 * 所属的类
 * 
 * 那个对象调用  static 就指向 对象 的源 类
 * 
 */
$obj = new Per9son();
$obj->show1();
/**
 * 请问 谁在调用 show1()
 * 
 * 是  Per9son 的类 对象 在调用 
 * 那么   static 就指向 对象 所属的 类(Per9son)
 * 
 * 这道题没有意思
 * 我改一改让这道题更有意思
 * 
 * 
 */
echo '<hr>';
class Pers人类on
{
    public static $type = '人类';
    public function show1()
    {

        echo static::$type;
        echo self::$type; //self 当前的类  从 子类 调用 当前类$type被重写为 '学生'
        // 不不不 公有的 且无修饰符 static 会被重写
        //使用了  static  的修饰符只会被 写入一遍(在类加载时 类对象未new实例化之前)
        // 所以 public static $type = '人类'; 没有被重写
        //还是 当前 类的  $type = '人类'
        var_dump($this); //object(stud学生ent)#10 (0) {}
    }
}
class stud学生ent extends Pers人类on
{
    public static $type = '学生';
    public function show2()
    {
        echo static::$type;
        echo self::$type; //self 当前的类  '学生'
        var_dump($this); //object(stud学生ent)#10 (0) {}
    }
}

$obj = new stud学生ent();
$obj->show1(); //学生 指向   stud学生ent 类
$obj->show2(); //学生 指向   stud学生ent 类
/**
 * 因为是公有属性  $type 被重写了 '学生'
 * 
 * 其实这里我觉得 不管指向谁都是 只有 一个$type '学生'
 * 错了 没有被 重写 
 * 
 *  
 *   使用了  static  的修饰符只会被 写入一遍(在类加载时 类对象未new实例化之前)
 * 所以 public static $type = '人类'; 没有被重写
 * 
 *  echo static::$type;
 * 
 * 这里 的 static 有点像this
 * 不调用 你是 不知道  static指向那个类的
 * 
 * 调用后才知道 
 * 因此也被称为 延时调用
 * 
 * 静态延时绑定(专业术语)
 * 
 * 小结一下
 * 1.static在内存中就一份,在类加载的时候分配空间 这也是不会被重写的原因
 * 2.如果有多个修饰符,修饰符之间是没有顺序的
 * 3.self当前类的类名
 * 4.static表示当前对象所属的类
 * 
 * static 有俩个作用 
 * 1.表示静态的 在内存中就一份 不会被重写
 * 2.当前对象 所属的类
 * 
 * 好这是  static
 */

/**
 * 今日 2021 4 16
 * 
 * 下面我们说第二个
 *  final (php) 最终的
 * 
 * 在js 里面 有 finally 是最终的意思
 * 
 *  final 修饰的方法不能被重写
 *  final 修饰的类不能被继承 
 *  
 * 
 */
// class Pers最终的函数不能被重写on
// {
//     final public function show()
//     //final 修饰的方法不能被重写
//     {
//     }
// }
// class Stud最终的ent extends Pers最终的函数不能被重写on
// {
//     public function show() //重写时报错
//       //报错:Cannot override final method
//     {
//     }
// }

// // final 修饰的类
// final class Per最终的类不能被继承son
// {
// }
// class stu继承时报错dent extends Per最终的类不能被继承son
// {
//     //报错:may not inherit from final class
// }

/**
 * final 最终修饰符的作用
 * 
 * 1.如果一个类 确定不被继承 
 *   用 final 修饰 这个类  
 *   可以提高执行效率
 * 
 * 2.如果一个方法不允许被其他类重写
 *   用 final 修饰 这个方法(函数) 
 *   可以提高执行效率
 * 
 *  
 * 有的方法就是不允许你重写
 * 有的啊
 *  
 * 必须用我(父类)的 不能让子类重写我的方法
 * 但是 子类可以用 我(父类)的方法
 * 
 * 应用场景有两个
 * final 修饰符 给类   防止 类   被继承
 * final 修饰符 给方法 防止 方法 被重写
 * 
 * 小结:
 * final 修饰的方法 不能被重写
 * final 修饰的类   不能被继承
 * 
 * 
 * 
 * 接下来我们讲 adstract [抽象的]
 * 
 *  abstract 
 *  修饰的 方法 是抽象 方法
 *  修饰的 类   是抽象 类
 * 
 *  只有方法的 声明
 *  没有方法的 实现 
 *   为抽象方法
 *  
 * 举个例子
 */
abstract class pe抽象修饰符rson
{
    public abstract function setInfo(); //方法的声明
    // 请看
    //这是不是 只有方法的声明
    //没有方法的 实现 
    //但是这样写报错了
    //得加 abstract 修饰符

    //public abstract function setInfo(); 
    //这就是 抽象方法

    //但是 vscode 还是 提示语法有问题
    // 只要 类里面有一个 抽象 方法

    //这个方法就是抽象 类
    // 需要使用  abstract 修饰
    // OK 现在不报错了



    //加了 {}才是方法的实现
    public function getInfo()
    {
        echo '获取信息<br>';
    }
}
// $obj = new pe抽象修饰符rson;
// //抽象类 不能new  会报语法错误
// // 报错: Cannot instantiate abstract class
/**
 * 抽象类 不允许实例化
 * 那么 其作用是什么 ???
 * 
 * 如果有 一个子类 继承了 抽象类
 * 在 子类中 必须把 抽象方法全部实现
 * 
 * 如果只声明 不实现
 * 光是语法就通不过  也不能new 实例化 也就没有任何用处
 * 
 * 
 * 下面是实现抽象父类的 子类样例
 */
abstract class Stu继承抽象类的子类dent extends pe抽象修饰符rson
{
    public abstract function setInfo(); //方法的声明
    // {
    //     //实现 未完成的 抽象类
    //如果我再 抽象 遗留到 下个类来 实现 再能否实例化 呢
    // 答案是可以  遗留 如下
    // 那么我猜测 应该可以 一直遗留
    // 这就像 昨天的工作 留在今天 也可以留在 后天
    // 只要不急 可以一直留 下去
    // }
}
class Stu继承抽象类的抽象类子类dent extends Stu继承抽象类的子类dent
{
    public  function setInfo()
    {
        echo '<br><br><br>实行 抽象 时声明的方法';
    }
}
$stu = new Stu继承抽象类的抽象类子类dent;
$stu->setInfo(); //实行 抽象 时声明的方法
$stu->getInfo(); //获取信息

/**
 * 这就是抽象类的所有的特点
 * 
 * 那么下面呢
 * 同学们肯定想
 * 有什么用
 * 
 * 我来说个场景啊
 * 
 * 我说完以后 你来给我总结
 * 有什么用
 * 我不说
 * 
 * 你自己给我总结
 * 
 * 在多人开发时
 * 项目经理会写好 基础抽象类
 * 里面预设了 函数功能的名字
 * 
 * 所有小组成员从  基础抽象类 里面继承
 * 这样 一样的功能 函数名 一定会一致
 * 
 * 
 * 否则就会语法报错
 * 
 * 所以抽象方法 最大的作用
 * 就是来定义 命名规范
 *            分工合作时 统一命名
 * 
 * 
 * 刚才说了
 * 只要这个类里面有一个抽象方法
 * 那么这个类就是抽象类
 * 
 * 下面的问题是
 * 如果一个抽象
 * 方法都没有
 * 都是普通方法
 * 那你 能不能
 * 
 * 声明 这个类 为  abstract 抽象类呢
 * 
 * 并不报错
 * 但是抽象类是不允许被实例化的
 * 所以这个类 它没作用
 * 
 * 硬要说的话 阻止实例化 new 也算是一个作用吧
 * 
 * 同学们肯定想 我好好的 干嘛要阻止实例化?
 * 在 哪些情况下需要阻止实例化?
 * 
 * 分析: 实例化是最消耗资源的 费时间
 * 
 *      假设一个类里面全是静态的变量 
 *       类加载时 就分配好了 内存
 *     
 *      的确 此时这个类不需要实例化
 *         也不需要分配 内存
 *     
 *       ?? 但是 这个类 此时也没啥要分配的了吧
 *       静态资源 只会分配一次地址
 *       也就是说 实例化 也仅消耗了一个 空壳 的内存而已
 *      
 *  不对 但是实例化 存了 静态资源的地址 不只是一个空壳
 *      所以的确 消耗了资源 白费了时间
 *         因为不实例化也能调用
 *            调用方式 echo Pers阿萨德on::$add; //北京
 *                      Pers阿萨德on::show(); //北京
 * 
 * 因此 如果一个类 全是静态资源
 *  那么 就可以写一个 abstract 防止别人 实例化 浪费性能
 * 
 * 
 * 好 下面小结一下
 * 一个 类 里面 只要 有一个 方法 是 抽象方法
 * 那么 这个类 必须是 抽象类 否则语法报错
 * 
 * 抽象类 无法实例化
 * 子类想要 继承并实例化 需要补完声明的 抽象方法
 * 
 * 如果一个类 它里面的方法
 * 全部是 普通方法
 * 也可以 声明 抽象类 不会报错
 * 
 * 但是 这样做 意义何在
 * 一般 当类的内容 全是 静态  时 阻止实例化 以提高性能
 * 
 * 好 这个就是抽象的
 * 
 * 
 * 
 * 下面我们说 
 * 类 常量 啊
 * 
 * 类里面有属性 有方法
 * 有常量
 * 是不是
 * 
 * 那么上面属性和方法我们都讲完了
 * 
 * 下面开始讲常量了
 * 那么类常量 就是
 * const 常量
 * 
 * 接下来 展示样例
 * 
 */
class Stud常量ent
{
    public const ADD = '<br><br><br><br>地址不详'; //常量没有 $ 符
    // 访问/静态 修饰符 7.1以后都能加 本来就该如此

}
// $obj = new Stud常量ent;
// $obj->ADD;//错误 应该使用 静态修饰符的访问方法
echo Stud常量ent::ADD;
//意思 是常量 和 static静态修饰过的 访问方式一致
/**
 * 类常量 就是 const 常量
 * 
 * 那么现在我问你 啊
 * 
 * define 和 const 定义的常量有什么区别
 * 
 * 区别差异
 * 1. const可以做类成员  define 不能
 * 2.下一堂课讲
 * 
 * 
 * 常量 const a=''; 和 静态变量 static $a='';
 * 相同: 都在加载类时分配空间
 * 不同: 常量不能更改  静态变量可以重新赋值
 * 
 * 到此 类的3个成员就讲完了
 * 
 * 1.属性 2.方法 3.常量
 * 
 * 请问常量是属于方法还是属于类
 * 
 * 常量是属于类吧
 * 所以说对象里面 对象是由什么组成的
 *                   属性和方法
 * 
 * 类里面类成员有几个 
 * 类成员 有3个
 * 1.属性 2.方法 3.常量
 * 
 * 但是 new实例化 的时候
 * 常量会不会 放到对象里面去
 * 不会
 * 
 * 只有属性和方法 直接放到类对象里了
 * 常量 在类里
 * 
 * 所以类对象是由 属性和方法组成的
 * 而 类 里面有 属性方法 和 常量
 * 
 * 但是实例化的时候
 * 类是不是 模具 模型啊
 * 按照类的样子创造 类对象
 * 
 * 但是常量 不需要 为什么
 * 因为 常量是属于类的
 * 
 * 所以 类对象里面只有属性和方法
 *           方法 其实你看不见 因为太大 只能看见 方法的 地址
 *           只能看见 属性 方法的地址 你也是看不见的
 * 
 * 
 * 
 * 
 * 好了 下面 我们说接口
 *  我先声明一下
 * 我现在说的 这个 接口
 * 和你
 * 将来 调的什么支付宝接口
 * 
 * 那是两码事
 * 
 * 这个接口 是语法
 * 那个接口是 url 地址
 * 是两码事
 * 
 * 现在我讲的这个接口
 * 它属于 面向对象的语法
 * 
 * 将来的接口是属于 url 地址
 * 
 * 
 * 现在开始
 * 
 * 一个类 里面只要有一个抽象方法
 * 那就是 抽象类 是吧
 * 
 * 比如说是这个 
 * 
 */
abstract class Stude接口nt
{
    public abstract function fun1();
    public abstract function fun2();
    public abstract function fun3();
    public abstract function fun4();
    public abstract function fun5();
}
/**
 * 这个类里面的 方法如果全部是抽象方法
 * 就可以使用 简化 写法
 * 
 * 注意 接口是一个特殊的抽象类
 * 它特殊在什么地方
 * 因为这里面(指接口里面) 的方法
 * 全部都是抽象方法
 * 如果一个类里面 他的方法 都是抽象方法
 * 
 * 那么这个类就可以设成接口
 * 接口用 interface
 * 
 * 写法为
 * 
 */
interface Stude简化接口nt
{
    const ADD = '接口抽象类的常量';
    public function fun1();
    // 抽象化 类的 fun 必定得重新实现
    // 接口中的抽象方法 只能是public
    // 因此可省略  不写默认就是 public
    function fun2();
    // function fun3();
    // function fun4();
    // function fun5();
}
/***
 * 如果一个类中所有
 * 方法都是抽象方法
 * 那么这个 类 可以声明为抽象接口 
 *      interface Stude简化接口nt
 *      interface 顶替了 class 
 * 
 * 接口是一个特殊的抽象类
 * 成为接口后 只能放 抽象方法(只声明的函数)
 *                   和常量
 *            不能放 变量        
 * 
 * 接口中的抽象方法 只能是public
 * 可省略  不写默认就是 public
 * 
 * 接口已经是 抽象的 里面的函数如果再
 * 使用  abstract 抽象 修饰 (没人会这么做吧)
 * 直接报错
 * 
 * 也就是 说 接口已经是抽象了
 * 你还用 abstract 抽象它干嘛?
 * 
 * 也不能用 final 最终的 来修饰
 * 因为 接口里抽象的声明方法 将来一定会重写
 * 
 * 你用 final 将来就不能重写了
 * 
 * 好了
 * 那么接口 抽象 类
 * 怎么实现呢 补完 声明的抽象类
 * 
 */
class stu实现接口的抽象类dent implements Stude简化接口nt
{
    //成为接口后 性质和普通类的继承方式不一样了
    //它已经不是 类了 类可以继承
    //但是它现在已经不是类了
    //现在是接口 接口不能叫继承
    //叫实现  
    //使用 implements 代替 extends
    //           接口实现
    function fun1()
    {
        echo '接口实现implements 代替extends 继承 接口抽象类';
    }
    function fun2()
    {
        echo '接口实现implements 代替extends 继承 接口抽象类';
    }
}
$a = new stu实现接口的抽象类dent;
$a->fun1();
//接口实现 implements 代替extends 继承 接口抽象类
$a->fun2();
//接口实现 implements 代替extends 继承 接口抽象类

echo Stude简化接口nt::ADD; //访问接口中的常量
//接口抽象类接口抽象类的常量
//接口 访问常量与 普通类 没什么区别

/**
 * 下面我为大家小结一下
 * 成为接口
 * 的关键字是 interface 代替class
 * 
 * 继承接口 
 * 的关键字是 implements 代替 extends
 * 
 * 一个类中所有的成员(只能有 抽象方法和常量)
 * 那么可以成为 接口 你也可以不成为
 *  接口只是一种 含全部抽象方法类的简写 形式
 * 
 * echo Stude简化接口nt::ADD;//访问接口中的常量
 * 
 * 好了
 * 
 * 类是不允许多重继承的
 * 但是 接口(抽象函数 和常量)
 * 是可以多重实现(继承)的
 * 
 * 就是一个类 可以实现两个规范
 * 
 * 就是 我们之前学的 类 全部都只能 被一个子类继承?
 * 但是 现在成为接口的抽象类 可以被 多个子类继承(实现)?
 * 
 */
class A0
{
    public $a = 1;
}
class A1 extends A0
{
    public $a1 = 2;
}
class A2 extends A0
{
    public $a2 = 3;
}
$stu1 = new A1;
$stu2 = new A2;
echo $stu1->a;
echo $stu2->a;
/**
 * 可以啊 怎么不可以啊 普通类当然能有 多个子类继承
 * 但是 一个 子类不能有 多个父类
 * 
 * 那么 类 不可以多重继承 指的到底是什么
 * 
 * 多重继承: 是指 一个子类 继承多个父类
 * 
 * 接口抽象声明的类 允许多重 实现/继承
 *  
 */
interface Ipic1
{ //抽象声明类的 简化写法 接口
    function fun1(); //只声明
}
interface Ipic2
{
    function fun2();
}
class Ipic3 implements Ipic1, Ipic2
{ // 继承/实现 多个 抽象声明类的 简化写法 接口
    //多重继承
    function fun1()
    {
    }
    function fun2()
    {
    }
}
//不报错 可以的 具体就不测试了 省时间 最近时间紧

/**
 * 如果两个接口里面的函数同名
 * 比如 Ipic1 和Ipic2 里面都只声明 一个函数 fun1()
 * 该怎么办
 * 
 * 同名 子类 实现/继承 时
 * 只需要 补完抽象声明的 fun1(){} 一次就可以了
 * 
 * 不会有任何影响和问题
 * 
 * 在接口的多种实现中
 * 如果有同名的方法
 * 只要 补完抽象声明一次 即可
 * 
 * 小结一下:
 *    类是不允许多重继承的
 *    但是接口允许多重实现
 *    
 *     在接口的多种实现中
 *     如果有同名的方法
 *     只要 补完抽象声明一次 即可
 *    
 * 类可以继承的时候 
 * 同时实现接口
 * 
 * 这是什么意思呢
 * 继承 别人的类 然后把自己变成接口?
 */
class Per假想父类son
{
}
class Stud类可以继承时实现接口ent extends Per假想父类son implements Ipic1, Ipic2
{
    function fun1()
    {
    }
    function fun2()
    {
    }
}
/**
 * 普通子类虽然
 * 不能多重 继承 
 * 继承几个普通父类
 * 但是能在继承 一个父类的同时 
 * 继承/实现 其它多个 接口(抽象声明类)
 * 
 * 实际应用情况非常少
 * 
 * 一定要先继承 再实现 不能写反
 * 
 * 好这是接口的多重实现
 */

/**
 * 下面我们说
 * 一个匿名类
 * 匿名类这个
 *  是了解的内容大家了解一下就够了
 * 
 * 下面还是写上例子吧
 * 
 */
$a = new class
{
    //这个类没有名字
    public $name = 'tom';
    public function __construct() //生函数
    {
        echo '构造函数<br>'; //构造函数

    }
};
echo $a->name; // tom
unset($a); //如果这个类只用一次
//销毁了节省空间
/**
 * 我这语法就通不过 不知道为什么 少了个 分号 ; 
 * $a = new class 是赋值 要写分号
 * 
 * 这么写有什么用
 * 有什么好处呢
 * 实例化结束后 方便销毁 类对象
 * 实例化 后 立马释放 无名类 的空间 只有类对象保留了下来
 * 
 * 如果是 普通 有名类 类对象消不消毁 有名类的空间一直在 占内存
 * 
 * 
 * 这样做可以 减少 资源消耗
 * php 7.0 后支持
 * 
 * 
 * 好这是匿名类
 */

/**
 * 下面我们再了解一个内容
 * 方法绑定
 * 方法绑定是什么意思呢
 * php7.0 以上才支持
 * 
 * 下面面举一个场景的例子
 */
class Stud方法绑定ent
{
    public function fun1()
    {
    }
    public function fun2()
    {
    }
    public function fun3()
    {
    }
    public function fun4()
    {
    }
}
$stu = new Stud方法绑定ent;
$stu->fun1();
//如果我只用到 一种函数 fun1();
//其它函数 就浪费了 空间 因为有名类是不会被任何东西销毁的

//现在我有个需求
//我用你的方法 你就加载 其它没用到的不加载

/**
 * 方法绑定就是将类的方法 绑定在类对象上
 * 
 * 那怎么绑定呢 
 * 用的是这个 语法:
 *                  闭包->call(类对象)
 * 
 * 闭包 在php里面什么叫
 * 闭包呢
 * 就是匿名函数
 * 
 * 在php 里面匿名函数就叫闭包
 * 
 * 在 js 里面 什么是闭包? 差不多吧 递归不就是闭包?
 * 
 * php里面比较宽松
 * 
 * php 匿名函数 称为 闭包
 * 
 * 闭包->call(类对象):将闭包绑定到对象上
 * 
 * 我们来写一下闭包
 * 
 * 
 */
$lang = '非ch';
//普通类
class Stu闭包dent
{
}
if ($lang == 'ch') {
    $fun = function () {
        echo '这里匿名函数 闭包了';
    };
} else {
    $fun = function () {
        echo '这里也是匿名函数 闭包了';
    };
}
//匿名函数 绑定 普通类
$stu = new Stu闭包dent; //通过类 实例化出来的 类对象
$fun->call($stu); //将函数绑定在 类对象上 并执行
//$lang = 'ch';  这里匿名函数 闭包了
//$lang = '非ch';  这里也是匿名函数 闭包了

/**
 * 就是你每次 只加载了一个函数
 * 这里匿名函数 闭包了 
 * 或
 * 这里也是匿名函数 闭包了
 * 
 * 类是空的 
 * 
 * $fun->call($stu);//绑定匿名函数并执行一次
 * 
 * 这种行为相当于 把函数(匿名) 
 * 后添加到 类对象 里面并进行 回调(call) 执行
 * 匿名函数执行一次就会销毁
 * 
 * 这样 类为空
 * 类 对象为空 
 * 节省了 运行资源 if也是执行一遍就消失了
 * 
 * 极大 省出了 内存空间
 * 
 * 这样就做到了 需要调用的 加载
 * 不需要调用的不加载
 * 
 * 好
 * 
 * 
 * 下面呢 我们说一下
 * 这个异常处理
 * 
 * 异常处理是什么呢
 * 就是说我写了
 * 好多 很多 代码 啊
 * 
 * 但是代码里面可能有异常
 * 发生错误啊
 * 
 * 我们把错误一起处理了 统一处理
 * 就是说不要一个一个处理
 * 
 * 一个一个处理麻烦
 * 比如说
 * 
 * 写一行 出错了
 * 处理一下
 * 又写一行 出错了
 * 又处理一下
 * 
 * 太麻烦了 我能够
 * 处理的
 * 把他们统一处理了
 * 
 * 我们需要 使用到
 * 关键字
 * 
 * try :检测代码块
 * catch:捕获异常
 * throw:抛出异常
 * finally:无论有无异常都会执行,可以省略
 * Exception:要捕获异常的什么数据类型(strinf / int / sss /...)
 * class sss{}  sss 类型
 * 
 * 语法结构
 * try{
 * 普通代码
 * }catch(Exception $ex){
 *  错误是 有错执行这里 也可以什么都不做
 * }
 * finally{
 *   有没有错都会执行这里 也可以什么都不做
 * }
 * 
 * 实例
 */
?>
<?php
// if (isset($_POST['button'])) { //如果点击了提交
//     try {
//         $age = $_POST['age'];
//         if ($age == '') {
//             throw new Exception('年龄不能为空', 1001); //抛出 Exception 类型错误    message   ,  错误码
//         };
//         if (!($age >= 10 && $age <= 30)) {
//             throw new Exception('年龄必须在10-30之间', 1002); //抛出 Exceptio
//         };
//         echo '您的年龄合适';
//     } catch (Exception $ex) { //捕获 Exception
//         //这里 PHP提供了 众多 内置函数
//         echo '错误信息: ' . $ex->getMessage(), '<br>';
//         //这 获取方式 仿佛 抛出的错误
//         //全到了  $ex 这个对象里 
//         //而且 php 还分配了对应获取 函数
//         echo '错误吗: ' . $ex->getCode(), '<br>';
//         echo '文件地址: ' . $ex->getFile(), '<br>';
//         echo '错误行号: ' . $ex->getLine(), '<br>';
//         //还分封装了 获取当前行 之类的函数

//         //有异常 的话 不用处理
//         //然后在 有错误异常时 
//         //捕获 分析 错误

//         // 与js的 try 还是不太一样的
//         // js是 写在 try 里的错误代码不会报错
//         //然后 捕获 啥也不做
//     } finally {
//         echo '无论是否有异常都会执行这句话';
//     };
// }
/**输入 0
 * 错误信息: 年龄必须在10-30之间
 * 错误吗: 1002
 * 文件地址: D:\phpStudy\PHPTutorial\WWW\创建类.php
 * 错误行号: 1801 
 * 
 * 输入12
 * 您的年龄合适
 */
?>
<html>
<!-- <form action="" method="post">
    年龄:<input type="text" name="age"><br />
    <input type="submit" name="button" value="提交"><br />
</form>
 -->

</html>
<?php
/**
 * 我下面说个场景
 * 就是异常可能会很多啊
 * 
 * 在上面 抛出三个 异常 但是共用一个处理
 * 
 * 但是下面有个问题了
 * 比如说我有很多很多异常
 * 
 * 我想要分类处理
 * 比如分 3类 
 * 第一类
 * 1.轻微的 记录下来到日志当中就可以了
 * 第二类
 * 2.稍微级别 高一点的 就给他发电子邮件
 * 第三类
 * 3.第三个是级别最高的 立马给你发信息
 * 
 * 自定义三种 异常即可
 * 所有异常类的父类是 Exception
 * 
 * Exception中的方法无法重写(加了final 最终修饰符的)
 * 
 */
?>
<?php
//定义三种错误 类  从 Exception 类 中 继承
class 自定义空异常类 extends Exception
{ //写这就够了 什么都不用写
    // 因为方法都继承下来了
    //你只要定义一个类 就行了
}
class 自定义类型异常 extends Exception
{
}
class 自定义范围异常 extends Exception
{
}


if (isset($_POST['button'])) { //如果点击了提交
    try {
        $name = $_POST['name'];
        $age = $_POST['age'];

        if ($name == '') {
            throw new 自定义空异常类('姓名不能为空', 1001); //抛出 自定义空异常类 类型错误    message   ,  错误码
        };
        if ($age == '') {
            throw new 自定义空异常类('年龄不能为空', 1002); //也是抛出 自定义空异常类 类型错误    message   ,  错误码
        };

        if (!(is_numeric($age))) { //如果 不是个  整型/字符 数字
            throw new 自定义类型异常('年龄不是个数字', 1003); //抛出 自定义类型异常
        };
        if (!($age >= 10 && $age <= 30)) {
            throw new 自定义范围异常('年龄必须在10-30之间', 1004); //抛出 自定义范围异常
        };
        echo '姓名:', $name, '<br>';
        echo '年龄:', $age;
    } catch (自定义空异常类 $ex) { //捕获 自定义空异常类
        //这里 PHP提供了 众多 内置函数
        echo '错误信息: ' . $ex->getMessage(), '<br>';

        echo '错误吗: ' . $ex->getCode(), '<br>';
        echo '文件地址: ' . $ex->getFile(), '<br>';
        echo '错误行号: ' . $ex->getLine(), '<br>';
        echo '错误记录在日志中: ';
        //只是 写了句话 并没有记录在日记中 啊喂
    } catch (自定义类型异常 $ex) { //捕获 自定义类型异常
        //这里 PHP提供了 众多 内置函数
        echo '错误信息: ' . $ex->getMessage(), '<br>';

        echo '错误吗: ' . $ex->getCode(), '<br>';
        echo '文件地址: ' . $ex->getFile(), '<br>';
        echo '错误行号: ' . $ex->getLine(), '<br>';
        echo '这个错误的级别重要程度为发送邮件 ';
    } catch (自定义范围异常 $ex) { //捕获 Exception
        //这里 PHP提供了 众多 内置函数
        echo '错误信息: ' . $ex->getMessage(), '<br>';

        echo '错误吗: ' . $ex->getCode(), '<br>';
        echo '文件地址: ' . $ex->getFile(), '<br>';
        echo '错误行号: ' . $ex->getLine(), '<br>';
        echo '这个错误的级别重要程度为给管理员打电话 ';
        //别问我 我也不知道 打电话怎么用php实现的
    } finally {
        echo '无论是否有异常都会执行这句话';
    };
}

?>
<html>
<form action="" method="post">
    姓名:<input type="text" name="name"><br />
    年龄:<input type="text" name="age"><br />
    <input type="submit" name="button" value="提交"><br />
</form>


</html>
<?php
/**
 * 好了 
 * 小结一下啊
 * 自定义异常
 * 
 * 就是自己定义自己类别的异常
 * 那么异常类的父类
 * 所有的自定义异常类
 * 都必须要继承  Exception 父类
 * 
 * Exception 类中的方法是不允许被重写的
 * 自定义异常 以后
 * 你就可以抛出 自定义的类异常 别
 * 可以写 多个 catch 分别捕获 这些类的异常
 * 如上面例子所示
 * 
 * 好这个是自定义异常
 */

/**
 * 今日 2021 4 17
 * 
 * 接下来把这个作业题给做了
 * 
 *              o
 *            o o o
 *          o o o o o
 *        o o o o o o o
 *      o o o o o o o o o
 *        o o o o o o o
 *          o o o o o
 *            o o o
 *              o
 * 
 * 看好了  共 9 行  9列
 * 
 *   第 6 行和 第4行 是一样的  =10
 *   第 7 行和 第3行 是一样的  =10
 *   第 8 行和 第2行 是一样的  =10
 *   第 9 行和 第1行 是一样的  =10
 * 
 * 先实现  1 2 3 4 5 4 3 2 1
 * 把每行的数字打印出来
 * 
 */

for ($i = 1; $i <= 9; $i++) {
    if ($i <= 5) {
        echo $i;

        for ($k = 1; $k <= 5 - $i; $k++) {
            echo ' '; //前面的空格
        }
        for ($j = 1; $j <= $i * 2 - 1; $j++) {

            echo 'o';
        }
        echo '<br>';
    }
    if ($i > 5) {
        echo (10 - $i);
        $i2 = 10 - $i;
        for ($k = 1; $k <= 5 - $i2; $k++) {
            echo ' '; //前面的空格
        }
        for ($j = 1; $j <= $i2 * 2 - 1; $j++) {

            echo 'o';
        }
        echo '<br>';
    }
    //这个出来就好办了
    // 1 是 1个点  2*n-1 数学算法(仅用于 局限性并没有大用)
    // 2 是 3个点
    // 3 是 5个点
    // 4 是 7个点
    // 5 是 9个点
}
/**
 * 老师使用的body样式 居中
 * 不是使用逻辑
 * 我使用的逻辑 
 * 
 * 做出来且没有bug就完了 不管实现方法
 * 
 * 
 * 好了下一道题目 
 * 
 *    o o o o o o o o o o
 *    o o o o o o o o o o
 *    o o             o o
 *    o o             o o
 *    o o             o o
 *    o o             o o
 *    o o             o o
 *    o o             o o
 *    o o o o o o o o o o
 *    o o o o o o o o o o
 * 
 * 这是 10行 10列
 *  
 * 行大于 2 小于 9
 * 且 
 * 列大于 2 小于 9
 * 时 为空 其余为 o
 * 
 */
for ($i = 1; $i <= 10; $i++) {
    for ($j = 1; $j <= 10; $j++) {
        echo (($i > 2 && $i < 9) && ($j > 2 && $j < 9)) ? ' ' :  'o';
    }
    echo '<br>';
}
/**
 * 老师输出的使用了 <span></span>
 * 包起来 
 * 
 * 且使用了 样式控制 美化
 * 
 */

/**
 *   下面我带同学们讲自动加载类这么一个技术
 * 
 * 在项目开发中
 * 我们以后一个 .php文件里面写一个 类
 * 
 */
class Goods
{
}
class Book extends Goods
{
}
/**
 * 我们以前是这么写的 
 * 这么写是不行的
 * 
 * 以后一个文件
 * 只能写一个类
 * 
 * 这个时候肯定是多个文件了
 * 是不是
 * 
 * 再往下讲
 * 因为一个文件只能写一个类
 * 
 * 并且在类的执行的过程中
 * 会有很多类的参与
 * 
 * 如果一个一个的加载很麻烦
 *  
 *  这就需要有一个机制
 *  来实现 php 的自动加载类
 * 
 *   接下来我写一个样例
 * 
 */

//在 ./父抽象类.class.php 中
// abstract class 父抽象类
// {
//     protected $name; //受保护的 继承变量
//     final public  function setName($n)
//     // 不可被 重写 的函数  setName()
//     {
//         $this->name = $n;
//         //传入 参数 至 当前 抽象类 里的 变量 $name
//     }
//     public abstract function getName();
//     //声明 公共 抽象 函数 
//     //所在类 必须同时 改为 抽象类才能容纳 此声明 函数

// }

//在 ./子类.class.php 中
// class 子类 extends 父抽象类
// {
//     public function getName()
//     {
//         echo  $this->name;
//         //打印 父抽象类 里的 变量 $name
//     }
// }


//在 ./子类2.class.php 中
// class 子类2 extends 父抽象类
// {
//     public function getName()
//     {
//         echo  $this->name;
//         //打印 父抽象类 里的 变量 $name
//     }
// }

// 在 本文件.php 中
// require './父抽象类.class.php'; //包含
// require './子类.class.php'; //包含
// require './子类2.class.php'; //包含
// $stu = new 子类;
// $stu->setName('我是子类 传入的名字');
// $stu->getName(); //打印传入的名字

// $stu = new 子类2;
// $stu->setName('我是子类2传入的名字');
// $stu->getName(); //打印传入的名字

/**
 * 过去我们是这么写的
 * 但是新的开发标准
 * 
 * 就是这个东西啊 这样写
 * 在上课 讲讲知识点行 
 *  做实验 demo 案例行 
 * 
 * 一旦到了实际工作中没人这么写
 * 你这么写非常不利于 维护
 * 
 * 那应该怎么写呢
 * 一般来说
 * 
 * 一个文件里面写一个类
 * 那这是不是把 类 单独 
 * 写在一个 .php 里面
 * 独立开来了
 * 
 * 文件名 必须和 其中仅有的一个类名 一模一样 
 * 
 * 父抽象类.php 
 * 父抽象类.class.php (非必须)
 * 
 * .class 是为了 区分 
 * 类.php  和 普通.php 
 * 
 * 现在好多框架默认不加
 * 以前的好多框架默认的是加的
 * 
 * 框架里可以配置的 加不加都可
 * 
 *  ........ 无语了
 * 搞半天 我以为有新技术
 * 这不就是纯粹的包含 嘛
 * 
 * 说好的自动加载类呢 
 * 在哪儿呀???
 * 
 *  来了 
 *  同学们你们那这样一个一个加载
 * 是不是很麻烦
 * 
 * 这个时候我们需要一个机制
 * 自动的 来加载
 * 
 * 上面刚讲的方法是 
 * 手动加载
 * 
 * 那么现在我们就自动加载啊
 * 有几种方法
 * 方法一 :利用 __autoload() 函数
 *            双下划线
 * 只要当你缺少 类了
 * php会控制计算机 自动帮你
 *  调用 __autoload() 这个函数
 * 
 * 自动的啊
 * 同学们有没有发现
 * 一个现象
 *  双下滑线 开头的好像都是 
 * 自动调用的函数
 * 
 * 而且自动将 缺少的 类名
 * 作为 参数 然后调用自身
 * 
 * 
 * 
 */

function __autoload($缺号少的类名)
{
    echo $缺号少的类名; //每次只会输出当前缺的
    //而不是 所有缺的类

    //当前缺少 子类      
    //当前缺少 父抽象类
    //当前缺少 子类2
    require "./$缺号少的类名.class.php";
    //这样 就会自动包含所有 类名 的 文件名
    //因为 只要缺一个类名 __autoload()函数
    //就会被调用一次

    // 这个好 会自动引入 当前路径下 用到的类


}

$stu = new 子类;
$stu->setName('我是子类 传入的名字');
$stu->getName(); //打印传入的名字

$stu = new 子类2;
$stu->setName('我是子类2传入的名字');
$stu->getName(); //打印传入的名字

/**
 * 但是 老师说 __autoload()
 * 这个函数在
 * php 7.2 以后就不支持了
 * 
 * 我现在是
 * php 7.2.10
 * 支持啊
 * 
 * 
 * 因为有一个更强大的 函数
 * 下面我们说
 * 
 * 说第二种
 * 
 * 是因为 7.2 里面有一个更强大的东西
 * 
 * spl autoload register()
 * 
 *  
 */

/**
 * 今日 2021 4 19
 * 
 * 
 * 有人说 
 * 老师 干什么的
 * 我说一个场景你就知道了
 * 请问
 * 如果一个项目很大
 * 
 * 我说一个场景
 * 将来我这个类很多
 * 有几万个 类 文件
 * 
 * function __autoload($缺号少的类名)
 *{
 *   echo $缺号少的类名;
 *   require "./1类/$缺号少的类名.class.php";
 *   require "./2类/$缺号少的类名.class.php";
 *}
 * 
 * 在这种情况下加载类就不能用一个文件写了 
 * 
 * 在上面的基础上可能有一部分保存在
 * 1类文件夹下
 * 有一部分保存在
 * 2类文件夹下
 * 
 * 假设 我有一个 loadNews 新闻的类
 * function loadNews(){
 * 自动在这里
 * 专门加载新闻的类
 * }
 * function loadGoods(){
 * 自动在这里
 * 专门加载商品的类
 * }
 * 
 * 但是你想  __autoload() 函数是不是只有一个
 * 
 * __autoload() 无法在 不同的函数里运行
 * 
 * 一个页面只能写 一个 __autoload() 函数 
 * 
 * 现在功能也不一样了
 * 
 * 我想要 在 __autoload() {中再 调用自己写的1类函数
 * 但是 不触发 2类函数 
 * } 
 *
 *      很显然这是做不到的
 *   __autoload($缺号少的类名) 是根据缺的类来调用的
 *   除非手动写 $缺号少的类名的判断
 * 
 * 这个时候该怎么做 ?
 * 计算机内存里面有一个栈
 * 叫 __autoload 栈
 * 这个栈里面
 * 
 * 有函数 叫 __autoload
 * 
 * 当你缺少类的时候他会自动
 * 的跑到这里面来调用
 * 
 * 接下来我写个function
 * 叫 loadClass()
 * 
 * 那么现在我做什么事呢
 * 注册到 这个区域(__autoload 栈)里面
 * 
 * 把这个 function loadClass(){} 函数
 * 
 * 注册到 __autoload 栈 区域里面来
 * 
 * 当我把function loadClass(){} 函数
 * 注册到
 *  __autoload 栈 区域 
 * 之后
 * 
 * function loadClass(){} 函数 是我自己定义的
 * 
 * 当我一注册 缺少类的时候
 * 他就跑哪 来了
 * 跑 function loadClass(){} 函数来了
 * 
 * 而且我告诉你 可以注册多个函数
 * 那么这里面用什么来注册呢
 * 用的一个方法
 * 叫 spl__autoload_register()
 * 
 * 将函数注册到
 *   __autoload 栈 区域 
 * 当中
 * 
 * 好了那么下面
 * 我写给大家看啊
 * 
 * 
 */
// function loadClass($class_name) //加载类函数 方法一
// {
//     require "./$class_name.class.php";
// }
/**
 * 缺少类的时候 会自动调用这个函数吗
 * 不会
 * 因为这是我自定义的
 * 肯定不行
 * 那我要把他注册一下
 * 
 */
// spl_autoload_register('loadClass'); //注册加载类函数  方法一

/**
 * 当我一注册
 * 以后只要 一缺少类
 * 他就跑
 * function loadClass($class_name)//加载类函数
 * 这来了
 * 
 * 方法二 
 * 还可以用匿名函数
 */
// spl_autoload_register(function ($class_name) {
//     require "./$class_name.class.php";
// }); //注册加载类函数 方法二
/**一样的
 * 第一个传入 有名的函数
 * 第二个 直接传没名字的函数
 * 
 * 实际逻辑上一模一样
 * 
 * 匿名函数 又叫 回调函数
 * 什么是回调函数?
 * 
 * 把 函数 作为参数的这个
 *   函数本身
 * 就叫回调函数
 * 
 * 回调函数 是相对而言的
 * 
 * 那么有人说
 * 我可以注册多个函数吗
 * 当然可以
 * 我可以注册多个函数的
 * 
 * spl_autoload_register('load1'); 
 * spl_autoload_register('load2'); 
 * spl_autoload_register('load3'); 
 * 就这样注册就可以了
 * 
 * 那我问你
 * 你注册的这三个是队列
 * 先进先出  还是
 * 就是
 * 你先注册 load1 再注册 load2 再注册 load3
 * 到时候查找的时候
 * 
 * 到时候级将来查找的时候
 * 是先查找什么
 * 
 *     先查找 load3 
 * 还是先查找 load1 呢
 * 
 * 有先进先出 和先进后出嘛
 * 
 * 我先加载了个load1
 * 再加载了个 load2
 * 再加载 load3
 * 
 * 三块
 * 那么将来是什么呢
 * 你有可能是这样子的
 * 
 * 有可能是查找的时候
 * 先加载的 1
 * 再加载的 2
 * 再加载的 3
 * 
 * 请问 他是茶杯式 先进后出 用的时候 321
 * 还是 管道式     先进先出 用的时候 123
 * 
 * 是 管道式 先进先出的 
 * 
 * 在官方文档 spl_autoload_register函数 中 
 * 说明为添加到 队列
 * 
 * 一般而言 只要看到队列两个字就是先进先出
 * 这个函数 php5.1 以后就支持了
 * 
 * 从 php 5.1 到 7.2 这两个函数是
 * 支持的
 * 但是 7.2 以后 
 * __autoload  就不支持了 我现在是 7.2.10
 * (还是支持的啊)
 * 
 * 只剩下 注册函数了
 * 
 * 
 * 
 * 
 */


// $stu = new 子类;
// $stu->setName('我是子类 传入的名字');
// $stu->getName(); //打印传入的名字

// $stu = new 子类2;
// $stu->setName('我是子类2传入的名字');
// $stu->getName(); //打印传入的名字


/**
 * 同学们我们来思考一个问题啊
 * 刚才我写的这个类
 * 存储 规不规则?
 * 不规则
 * 
 * 瞎说的 放到一块(需要的类都在当前文件目录 没有东放西放) 
 * 还不规则
 * 那要咋样才规则
 * 
 * 存储肯定规则的
 * 哪不规则
 * 
 * 刚才我讲的这个类 存储的类
 * 都是规则的
 * 
 * 你说有没有 
 * 在我加载的过程中
 * 出现类不规则的情况
 * 
 * 有可能的是吧
 * 那咱们举一个例子
 * 
 * 在当前目录下
 * 我新建一个 
 * 名称为 aa 
 * 的文件夹 
 * 
 * 又新建一个 
 * 名称为 bb 
 * 的文件夹 
 * 
 * 又新建一个 
 * 名称为 cc 
 * 的文件夹 
 * 
 * 你看 原来需要引入的  3个 类 文件
 * 都放在 当前目录下下
 * 我们是不是说他规则的
 * 
 * 现在 我把他们分别放进
 * aa bb 和 cc 里面
 * 
 * 3个不同的地方(文件夹)
 * 规则吗
 * 
 * 不规则
 * 那么下面的问题来了
 * 怎么办?
 * 什么怎么办 
 * 怎么在这加载 
 * 这三个不同的地方的类
 * 
 * 
 * 
 */
// spl_autoload_register(function ($class_name) {
//     require "./$class_name.class.php";
// });
/**
 * 有没有 什么绝招啊
 * 遍历 
 * 遍历是指 循环当前文件夹的 名字?
 * 
 * 我为什么要写 自动加载
 * 就是为了少些代码吧
 * 结果你说
 * 你要遍历一下
 * 
 * 那算了 还是手动加载
 * 来的更简单
 * 
 * 好了 
 * 我说下他的解决方法
 * 如果只要出现
 * 不规则的 
 * 那么就把不规则的
 * 单独的 拎出来
 * 
 *只要出现
 * 不规则的 
 * 那么就把不规则的
 * 单独的 拎出来
 * 
 * 把他的文件 名字 类名作为一个
 * 作为一个 数组
 * 
 * 比如说我写一个
 * 
 */
spl_autoload_register(function ($class_name) {
    $map = array( //类名和文件地址映射成一个关联数组
        '父抽象类' => './aa/父抽象类.class.php',
        '子类' => './bb/子类.class.php',
        '子类2' => './cc/子类2.class.php'
    );
    // require "./$class_name.class.php";
    if ($map[$class_name]) { //如果传入的是 不规则类名
        require $map[$class_name]; //引入 这个不规则类名
        //在 $map 的地址


    }
});
/**
 * 你想想一个代码 是规则的
 * 存储多
 * 还是不规则
 * 的存储多
 * 
 * 肯定是规则的存储多啊
 * 不规则的
 * 就那么几个
 * 
 * 你把不规则的拎出来
 * 剩下的是不是全部是
 * 规则的
 * 
 */
$stu = new 子类;
$stu->setName('我是子类 传入的名字');
$stu->getName(); //打印传入的名字

$stu = new 子类2;
$stu->setName('我是子类2传入的名字');
$stu->getName(); //打印传入的名字

/**
 * 好那下面我来小结一下
 * 如果 类文件存储不规则怎么办
 * 因为不规则的比较少
 * 因为绝大部分都是规则的
 * 只有少部分不规则
 * 
 * 我们可以把少部分 
 * 类名 和 文件的地址
 * 做一个 映射(保存到创建的数组中)
 * 
 *  映射成一个关联数组
 *  将 类名和文件地址作为映射
 * 
 * 映射成一个数组
 * 然后你缺少哪个类你到里面找
 * 如果你找到了
 * 如果你找到
 * 
 * 那你就把那个地址啊
 * 使用 require 地址;
 * 包含进来就可以了
 * 
 * 我说名一下
 * 在项目中
 * 不规则的少
 * 
 * 规则存储是绝大部分
 * 
 * 绝大部分都是规则存储
 * 不规则的少
 * 正因为 不规则的少你才能这么做
 * 
 * 如果你有 500个文件全部不规则的
 * 那你把人全累死了
 * 
 * 一般不规则的很少
 * 绝大部分 是规则的
 * 
 * 如果一个项目不规则 存储多了
 * 那说明 设计的
 * 项目的人就有问题了
 * 
 * 你到这个公司里面
 * 你发现这个 存储都是乱的
 * 说明这个设计的人肯定有问题
 * 
 * 瞎写
 * 只有极少部分 这可以理解
 * 但是绝大部分 不可能不规则
 * 
 * 
 * 好
 * 同学们下面呢我讲 一个指令
 * 
 * 就是 克隆指令
 * 
 * 讲一个克隆指令
 * 这个
 * 首先
 * 我问大家一个问题
 * 同学们
 * 创建一个对象的方式有哪些呢
 * 
 * 我们知道的就一种
 * 面向对象 
 * 通过
 * new 类(实例化) 来创建对象
 * 
 * 就知道一种
 * 你要说第二种的话
 * 
 * 你现在就知道一种
 * 你哪能说出第二种
 * 
 * 
 * 
 */
class 一个普通类
{
}
$一个普通对象 = new 一个普通类; //object(一个普通类)#22 (0) {}
$一个普通对象2 = new 一个普通类; //object(一个普通类)#23 (0) {}

// 下面我学一个指令叫克隆
// $一个普通对象 = new 一个普通类; //object(一个普通类)#22 (0) {}
// $一个普通对象2 = clone $一个普通对象; //object(一个普通类)#23 (0) {}

var_dump($一个普通对象, $一个普通对象2);

/**
 * 所以得出一个结论
 * 创建对象的方式有哪些
 * 
 * 第一个 : new 类 (实例化)
 * 第二个 :  clone 对象(克隆)
 * 
 * 下面要讲一个 双下划线 __clone()
 * 唉
 * 我们发现啊
 * 哼!~哼!~哼! ~
 * 自动克隆是吧
 * 自动克隆(笑)
 * 啊~ 不是自动克隆
 * 
 * 哈哈哈哈哈哈哈 呵呵呵
 * 啊 `
 * 是
 */
class 一个很普通的类
{
    public function __clone()
    {
        echo '正在克隆';
    }
}
$一个普通对象 = new 一个很普通的类;
$一个普通对象2 = clone $一个普通对象; //正在克隆
/**
 * 当你 clone $一个普通对象 的时候
 * 他会自动调用
 * 一个很普通的类 的对象
 * $一个普通对象 中
 * 继承的
 * public function __clone()
 *   {
 *  }
 *函数
 *
 * 什么时候 自动调
 * 当调用 clone 指令的时候自动调用
 * 
 * 好了 
 * 同学们
 * 现在我不克隆了
 * 
 * 注释掉 $一个普通对象2 = clone $一个普通对象; //正在克隆
 * 那么  __clone() 是不会执行的
 * 
 * 下面我给大家小结一下
 * 说第一个
 * 那个
 * clone 克隆是这个创建对象的方法之一
 * clone 克隆是这个创建对象的方法之一
 * 第二个 
 * 当执行克隆指令的时候
 * 会自动的调用 
 * 被克隆对象中(如果有)
 * 继承类的 
 * __clone() 函数方法
 * 
 * 好了 
 * 那么这个 
 * clone 
 * 和 
 * __clone
 * 
 * 就讲完了
 * 
 * 同学们讲到这的时候同学们会感觉
 * 唉?
 * 说老师
 * 前面讲的是自动加载类
 * 
 * 冷不丁的冒出来个克隆
 * 好像之间没什么关联呐
 * 
 * 唉 我告诉你
 * 讲克隆
 * 是为了讲
 * 
 * 下面的即将要学的设计模式 做铺垫的
 * 
 * 下面我再带同学们学一个设计模式
 * 叫
 * 单例模式
 * 
 * 什么叫
 * 单利模式
 * 就是一个类
 * 不管你怎么样
 * 
 * 你只能创建
 * 一个 对象
 * 
 * 一个类
 * 你只能创建
 * 一个 对象
 * 
 * 随你怎么弄
 * 他只能创建一个对象
 * 
 * 那么有人说老师
 * 那他用了
 * 
 * 什么地方的
 * 呃 用的
 * 就是说  呃
 * 这样说吧
 * 
 * 这个 这个
 * 我们留到最后来说
 * 
 * 我先告诉你这个需求吧
 * 就是单例模式
 * 
 * 单例模式就是
 * 一个类 
 * 就是只能有一个对象
 * 
 * 好了
 * 那么下面
 * 我怎么让一个类
 * 只有一个对象呢
 * 
 * 比如 连接数据库
 * 我怎么让你一个类
 * 只有一个对象呢
 * 
 * 唉 你说我问你啊
 * 
 */
// class DB
// {
// }
// $obj = new DB; //我实例化一次是不是一个对象
// $obj2 = new DB; //再实例化一次是不是又一个对象
/**
 * 所以为了让你有单例
 * 
 * 我 ...
 * 不让你
 * 实例化
 * 
 * 阻止你的
 * 外部 实例化
 * 
 * 怎么样阻止你的
 * 外部实例化呢
 * 
 * 有人说用抽象方法
 * 用抽象方法不行
 * 
 * 抽象方法是
 * 不但
 * 不让你
 * 在外部实例化
 * 
 * 连内部实例化
 * 都不让你
 * 
 * 单例模式
 * 单例模式
 * 至少还有一个对象呢
 * 
 * 单例
 * 单例
 * 是不是
 * 至少
 * 还有一个对象
 * 
 * 如果是 
 * 连一次
 * 都不实例化
 * 连一个对象都没有
 * 
 * 所以说
 * 我干什么
 * 我不能让你
 * 在外部实例化
 * 但是
 * 我可以调用
 * 一个方法
 * 我写一个公有的方法
 * 
 * 
 */
// class DB
// {
//     public function getInstance() //从内部得到当前类 的实例化对象
//     {
//     }
// }
// $obj = new DB; //我实例化一次是不是一个对象
// $obj2 = new DB; //再实例化一次是不是又一个对象
/**
 * 我不让你在外部实例化
 * 我通过一个
 * 公有方法 getInstance()
 * 来得到一个
 * 当前类的
 * 实例化的对象
 * 
 * 说我怎么样
 * 不让你在外部实例化
 * 
 * 有没有办法
 * 好 
 * 我给大家
 * 说一下
 * 
 * 我给大家
 * 说一下
 * 
 * 当你实例化的时候 
 * 要不要调用 构造方法 生 __construct()
 * 
 * 哦哦哦
 * 我想到了
 * 在构造方法 中return 空
 * 
 * 不管实例化多少次
 * 实例化的对象 
 * 就
 * 永远是个 
 * return 的 
 * 空 
 * 
 * 
 */
// class DB
// {
//     public function __construct() //构造函数 生
//     {
//         return ''; //return 空 ;
//     }

//     public function getInstance() //从内部得到当前类 的实例化对象
//     {
//     }
// }
// $obj = new DB; //我实例化一次是不是一个对象 
// //现在 $obj 是 空

// $obj2 = new DB; //再实例化一次是不是又一个对象
// //现在 $obj2 也是 空
// var_dump(1, $obj, $obj2, 1);
// int(1)
// object(DB)#26 (0) {
// }
// object(DB)#27 (0) {
// }
// int(1)

/**
 * 不管用
 * 不管用
 * 不管用
 * 为什么
 * 为什么
 * 为什么
 * 
 * 我只要将 __construct 
 * 改为私有的
 */
// class DB
// {
//     //私有的构造方法阻止在类的外部 实例化类
//     private function __construct() //构造函数 生
//     {
//     }

//     public function getInstance() //从内部得到当前类 的实例化对象
//     {
//     }
// }
// $obj = new DB; //我实例化一次是不是一个对象 
// //报错 因为实例化时调用了 私有的  __construct

// $obj2 = new DB; //再实例化一次是不是又一个对象
// //报错 因为实例化时调用了 私有的  __construct
// var_dump(1, $obj, $obj2, 1);
//

/**
 * 因为报错
 * 从而阻止了
 * 外部实例化
 * 方法  new DB
 * 
 * 那你不能在外部实例化了
 * 那你干什么呢
 * 
 * 当你还要得到一个单例啊
 * 调用公有函数 在公有函数内 实例化当前类啊
 * 
 * 注意不让你实例化 
 * getInstance()
 * 这个方法只能是静态的
 * 
 */
// class DB
// {
//     private static $instance;
//     private function __construct() //构造函数 生
//     {
//     }

//     public static function getInstance() //从内部得到当前类 的实例化对象
//     {
//         //如果未曾被实例化 才实例化 防止重复实例化
//         if (!self::$instance instanceof self) {
//             //当前 self::$instance 的实例化出来的类对象 是否属于 当前类
//             self::$instance = new self; //实例化
//             //self 当前类 DB 的替代 称谓
//             //self 当前类的类名
//             //static 表示当前对象所属的类
//         }

//         return self::$instance;
//         //如果已经被实例化 直接返回 以保存的实例化 类对象
//     }
// }
// $db = DB::getInstance(); //单例 
// $db2 = DB::getInstance(); //单例 
// $db3 = DB::getInstance(); //单例 
// var_dump(1, $db, $db2, $db3, 1); //都是同一个 实例 类对象
// int(1)
// object(DB)#9 (0) {
// }
// object(DB)#9 (0) {
// }
// object(DB)#9 (0) {
// }
// int(1)

/***
 * 同学们
 * 不管你
 * $db = DB::getInstance();
 * 调用多少次
 * 
 * 实例化出来的 单例都不能消失
 * private static $Instance;
 * 因此使用 static静态修饰符 进行修饰
 * 
 * 这样写
 * 行不行呢
 * 这样写
 * 是可以的
 * 但是这样写
 * 也有一个问题
 * 
 * 在判断这里
 * if (self::$instance == null) {
 * 不等于空
 * 不等于空
 * 难道
 * self::$instance
 * 一定就是 就是当前的单例吗
 * 
 * 此处判断还是不严谨
 * 的确
 * 问题是这是你写的
 * 又不是我写的
 * 你不严谨 那直接
 * 判断是不是对象不就完了?
 * 
 *  if (self::$instance instanceof self) {
 * 当前 self::$instance 的实例化出来的类对象 是否属于 当前类
 * 
 * 真的没有方法再 
 * 在外部
 * 获得对象吗
 * 
 * 
 */
// $db = DB::getInstance(); //单例 
// $db2 = clone $db; //还是可以克隆的

// var_dump(1, $db, $db2, 1); //都是同一个 实例 类对象

// int(1)
// object(DB)#26 (0) {
// }
// object(DB)#27 (0) {
// }
// int(1)

/**
 * 也因此 
 * 我们要阻止外部克隆
 * 
 * 前面是 
 * 私有化 构造函数
 * 现在是
 * 私有化 克隆 ?
 * 
 * 
 * 
 */
class DB
{
    private static $instance;
    private function __clone() //私有化克隆函数 
    { //阻止外部克隆
    }
    private function __construct() //私有化构造函数 生
    { //阻止外部实例化new 当前类对象
    }
    public static function getInstance() //从内部得到当前类 的实例化对象
    {
        //如果未曾被实例化 才实例化 防止重复实例化
        if (!self::$instance instanceof self) {
            self::$instance = new self; //实例化
            //self 当前类的类名
            //static 表示当前对象所属的类
        }
        return self::$instance;
        //如果已经被实例化 直接返回 以保存的实例化 类对象
    }
}
$db = DB::getInstance(); //单例 
// $db2 = clone $db; //还是可以克隆的 X
// 变成 报错了 和 $db3 = new DB; 一样

//现在 既 无法外部克隆 也无法外部实例化了
var_dump(1, $db, $db2, $db3, 1); //都是同一个 实例 类对象
// int(1)
// object(DB)#26 (0) {
// }
// NULL
// NULL
// int(1)

//真的成了 单例 了
//只能 通过
// $db = DB::getInstance(); //单例 
//调用一次 实例化

/**
 * 下面我再说一遍
 * 什么叫做
 * 单例
 * 就是这个类
 * 只有一个对象
 * 
 * 怎么能保证呢
 * 首先
 * 如果外部实例化了
 * 实例化一次
 * 产生一个类对象
 * 是不是
 * 
 * 干什么
 * 所以我要阻止你
 * 用了 私有的
 * 把 构造函数变成私有的
 * 
 * 有同学说 用抽象修饰类
 * 抽象的不行
 * 抽象的
 * 一个都没有
 * 
 * 但是我 
 * 单例单例
 * 还得有一个呢
 * 
 * 所以你肯定是不能用抽象的
 * 那怎么办呢
 * 我用一个私有的构造函数方法
 * 
 * 能不能再内部实例化呢
 * 唉
 * 私有的函数能不能在内部实例化
 * 
 * 可以的
 * 他只是不允许在外部实例化
 * 但是 还是可以在
 * 内部实例化的
 * 
 * 我上面那个例子
 * 实例化不就在内部吗
 * 
 * 然后呢
 * 我要 实例化
 * 我要写一个函数方法
 * 来获取之后
 * 
 * 那 我肯定需要一个静态变量
 * 来保存  当前类  new 实例化的 类对象
 * 
 * 为什么要静态的 
 * 变量
 * 不是要普通的
 * 变量
 * 呢
 * 
 * 
 * 为什么要静态的 
 * 变量
 * 不是要普通的
 * 变量
 * 
 * 因为你 通过从外部 
 * 调用当前类
 * 的 私有 函数 
 * 实例化 
 * 当前类对象 以后
 * 
 * 这个执行完以后
 * 普通的
 * 变量
 * 就会怎么样
 * 是不是要销毁了
 * 
 * 因为普通的
 * 变量
 * 
 * 只能
 * 给自己 
 * 的类对象 
 * 访问
 * 
 * 换
 * 做 类
 * 就不能了
 * 
 * 如果类不能访问这个 
 * 当前类(自己) 实例化
 * 出来的类对象
 * 
 * 将无法重复 阻止 外部 new 实例化
 * 因为 无法 读取到 普通变量
 * 是否 已经 保存 过 当前 类 的 实例化 
 * 的 类对象 了
 * 
 * 普通变量消失
 * 意味着
 * 外部 可以 无限 调用 类的公有函数 进行 实例化
 * 
 * 这就不叫单例了
 * 
 * 而且
 * 每个 实例化 在保存到 普通变量后
 * 由于 普通变量 是 私有的
 * 只有 类对象 自己能 调取 
 * 也就成了 无用 储存的实例化的变量
 * 
 * 自己 调取 自己 
 * 显然 
 * 自己 不能 举起自己
 * 
 * 这是逻辑问题
 * 
 * 如果
 * 通过外部 调用 
 * 类的 公有函数 
 * 进行 私有 变量 的实例化 中
 * 保存
 *  类 的 类对象
 * 那么 这个变量
 * 必须为 静态私有变量
 * 否则 连类 自己无法调用
 * 那么 实例化 将没有任何意义
 * 
 * 小结:
 * 一个类只能有
 * 一个对象
 * 应用场景
 * 多次请求 数据库
 * 只需要一个连接对象
 * 
 * 实现 : 三私 一公
 * 1.私有的静态属性用来保存对象的单例
 * 2.私有的构造方法用来阻止在类的外部实例化
 * 3.私有的__clone阻止在类的外部clone对象
 * 4.公有的静态方法用来获取对象的单例
 * 
 * 那这个单例模式
 * 干什么呢
 * 他用到什么地方呢
 * 
 * 1.用户浏览器
 * 2.php服务器
 * 3.mysql数据库
 * 
 * 请问 php服务器在执行的过程中
 * 有没有 多次访问 mysql数据库 的可能
 * 
 * insert update select
 * 插入    更新   查询
 * 增      改      查
 * 
 * 
 * 以上 分别 访问了 3次数据库
 * 
 * 也就是说 浏览器向服务器
 * 发送请求
 * 服务器有可能多次向 mysql 发送请求
 * 
 * 好了那么下面来一个问题了
 * 多次访问数据库
 * 
 * 你看啊
 * 服务器 第一次 往 mysql 数据库去了
 * 
 * 第一次 mysql 要开门
 *  比喻: 你去你的仓库里 拿东西
 *         可能要拿三次
 * 
 *   比喻: 你到京东去买东西
 *         你请求京东买了三个商品
 *          京东的工作人员是不是要到仓库里帮你取
 *          三个商品
 *          
 *          关于 取商品
 *          1.拿钥匙开门拿了第一个商品 关门 销毁钥匙
 *          2.又造了把钥匙  拿第二个商品 关门又 销毁了
 *          3.再造了把钥匙  拿第三个商品 关门再 销毁了
 * 
 * 造了三把钥匙
 * 其实你想一想
 * 你到仓库里拿东西只要几把钥匙 
 * 就够了
 * 一把就够了
 * 
 * 所以像这种情况是不是就单例啊
 * 
 * 那么
 * 我们链接 数据库
 * 其实就是单例啊
 * 
 * 你不管你 
 * PHP服务器
 * 去
 * 数据库
 * 取几次
 * 几把钥匙就够了
 * 一把钥匙
 * 就够了
 * 连一次就行了
 * 
 * 所以链接数据库
 * 用单例
 * 就可以了
 * 
 * 你实例化一次
 * 再实例化一次
 * 造了好几把钥匙有什么用呢
 * 浪费时间
 * 
 * 这种情况下一把钥匙就可以了
 * 所以
 * 
 * 这就是单例的应用场景:
 *                    多次请求数据库
 *                   只需要一个连接对象
 * 
 * 
 * 这有道题啊
 * 我说一下
 * 今天有三个同学
 * 问我这个问题了
 * 今天我就说一下
 * 
 * spl_autoload_register() 可以注册多个自动加载函数
 * 
 * 比如说我有三个规则不同的
 * 前面已经讲得很清楚了
 * 
 * 
 */

/**
 * 今日 2021 4 20
 * 那么下面我们继续往下
 * 上午我们已经讲了
 * 单例模式
 * 
 * 现在我们讲第二种模式
 * 工厂模式
 * 
 * 工厂模式
 * 就是顾名思义
 * 就是生产呗
 * 
 * 用不同的原材料
 * 我给你不同的原材料
 * 
 * 你就给我生产不同的产品出来
 * 这个叫工厂模式
 * 那么他的特点
 * 
 * 用一句话来总结
 * 是什么呢
 * 传递不同的参数
 * 给 工厂
 * 获取工厂返回的不同的对象
 * 
 * 这个怎么写呢
 * 你只要
 * 能够实现传递不同的参数
 * 获取不同的
 * 对象就可以了
 * 
 * 实例:
 * 
 * */
class 生产A
{
}
class 生产B
{
}
class 工厂
{
    public function 创建($创建所需参数)
    {
        switch ($创建所需参数) {
            case 1: //如果 参数 是 1
                return new 生产A; //创建 生产A的类对象 并返回

            case 2: //如果 参数 是 2
                return new 生产B; //创建 生产B的类对象 并返回

            default: //都不是
                return null; //返回 null
        }
    }
}
// 测试
$工厂 = new 工厂;
$对象1 = $工厂->创建(1);
$对象2 = $工厂->创建(2);
var_dump(2, $对象1, $对象2, 2);
// int(2)
// object(生产A)#12 (0) {
// }
// object(生产B)#13 (0) {
// }
// int(2)

/**
 * 条件:
 * 传递不同的参数
 * 获取不同的对象
 * 
 * 只要满足这个条件的
 * 都是工厂模式
 * 
 * 
 * 啊
 * 下面我们说策略模式
 * 
 * 策略就是方法啦
 * 就是传递不同的参数
 * 
 * 调用不同的策略
 * 调用不同的方法
 * 
 * 
 * 
 */
class Walk
{
    public function way()
    {
        echo '走着去';
    }
}
class Bus
{
    public function way()
    {
        echo '坐公交车去';
    }
}
//策略模式
class 学生
{
    public function play($obj)
    {
        $obj->way();
    }
}
//测试
$stu = new 学生;
$stu->play(new Walk()); //走着去
$stu->play(new Bus()); //坐公交车去

/**
 * 调用传入的
 * 对象
 * 里面的方法
 * 
 * 传入什么 调用什么
 * 
 * 传入 的参数
 * 对象里有什么 函数
 * 调用指定函数
 * 
 * 小结一下
 * 策略模式讲了三个
 * 
 * 单例模式
 *      就是一个类只能有一个对象
 *      实现条件: 三私一公
 * 
 * 工厂模式
 *      传递不同的参数
 *      获取不同的对象
 * 
 * 策略模式时方法
 *         传递不同的参数
 *         调用不同参数里的同名方法
 * 
 * 
 * 同学们接下来我们说
 * 这个序列化
 * 与反序列化
 * 
 * 我说一个场景
 * 请问一个 
 * 字符串
 * 你能不能
 * 把他保存起来
 * 
 * 可以吧 
 * 用 
 * 前面学过保存在文件里
 * file_put_contents('./txt', '要保存的字符串');
 * 
 * 那么
 * 请问你
 * 一个
 * 数组
 * 能保存吗
 * 
 * $stu=['tom','berry','ketty'];
 * 你没法保存的
 * 因为他是
 * 内存的结构
 * 他是个
 * 复杂的结构
 * 
 * 你能保存的基本类型
 * 字符串
 * file_put_contents('./txt', '要保存的字符串');
 * 
 * 强行保存 $stu=['tom','berry','ketty'];
 * 至 ./txt 中
 * 保存结果
 * 为
 * tomberryketty 
 * 的字符串
 * 
 * 当你读出来是无法反编译的
 * 这时候
 * 凡是 数组/对象
 * 对象 也不行
 * 
 * 那就是我们要讲的序列化
 * 就是什么叫
 * 序列化
 * 
 * 把他变成一个序列
 * 
 * 怎么把他变成一个序列呢
 * 非常的容易
 * 用 一个单词就可以了
 * 
 */
$stu = ['tom', 'berry', 'ketty'];
echo $stu = serialize($stu); //数组的序列化
//a:3:{i:0;s:3:"tom";i:1;s:5:"berry";i:2;s:5:"ketty";}
//a:3 数组长度为3
//i:0; 键 数组 3 个中的第一个键
//s:3: 值 当前键 对应的的 值的 长度

file_put_contents('./txt', $stu);
// 现在就可以把他写进去了
/**
 * 在 ./txt中 表现为
 * a:3:{i:0;s:3:"tom";i:1;s:5:"berry";i:2;    
 * s:5:"ketty";}
 * 
 * 这个东西写进来
 * 不是你的目的啊
 * 
 * 你的目的是将来
 * 服务器电脑关机了以后
 * 
 * 重启以后
 * 还能把他变成数组再读出来
 * 
 * 那么序列化是吧一个数组变成序列
 * 对吧
 * 
 * 那么你说我想把序列化的东西反过来反编译
 * 
 * 叫什么啊
 * 
 * 数组的反序列化
 */
echo $str = file_get_contents('./txt');
//a:3:{i:0;s:3:"tom";i:1;s:5:"berry";i:2;s:5:"ketty";}
$stu = unserialize($str); //反序列化
print_r($stu);

// Array
// (
//     [0] => tom
//     [1] => berry
//     [2] => ketty
// )
/**
 * 同学们
 * 刚才说的是数组的
 * 序列化与
 * 反序列化
 * 
 * 就是把 数组再变成一个序列
 * 然后把数组再反过来
 * 
 * 那么对象呢
 * 我们用类 创造一个对象来试试
 */
class Yangli
{
    public $name;
    protected $sex;
    private $add;
    public function __construct($name, $sex, $add)
    {
        $this->name = $name;
        $this->sex = $sex;
        $this->add = $add;
    }
}
//测试
$stu = new Yangli('tom', '男', '北京');
// file_put_contents('./stu.txt', $stu);
/**
 * 在 ./stu中 为空
 * 只能序列化
 */
echo '<hr>', $str = serialize($stu); //序列化

file_put_contents('./stu.txt', $str); //保存文件
/**
 * O:6:"Yangli":3:{s:4:"name";s:6:"北京";s:6:"*sex";N;
 * s:11:"Yangliadd";N;}
 * 
 */




//读取 文件反序列化
echo '<hr>', $str = file_get_contents('./stu.txt'), '<hr>';
//
//O:6:"Yangli":3:{s:4:"name";s:6:"北京";s:6:"*sex";N;
//s:11:"Yangliadd";N;}
$stu = unserialize($str); //反序列化
print_r($stu);
// Yangli Object                        
// (
//     [name] => tom
//     [sex:protected] => 男
//     [add:Yangli:private] => 北京
// )
var_dump($stu);
// object(Yangli)#34 (3) {
//     ["name"]=>
//     string(3) "tom"
//     ["sex":protected]=>
//     string(3) "男"
//     ["add":"Yangli":private]=>
//     string(6) "北京"
//   }
/**
 * 老师出现了
 * __PHP_Incomplete_Class 
 * __PHP_Incomplete_Class_Name
 * 
 * object(php未知类)#34 (3) {
 * object(Yangli)#34 (3) {
 * 
 * 而我这明显是 没有出现未知类
 * 
 * 原来是 老师保存后把 原来的类注释了
 * 
 * 意思就是
 * 反编译时 也需要原来的类参与
 * 
 * 在类的反序列化的时候
 * 必须要有
 * 类的参与
 * 
 * 下面我给大家小结一下
 * 我们讲了 序列化与反序列化
 * 说 为什么
 * 需要将 
 * 序列化        js    对象数组 转为 json字符串
 * 和反序列化    js  json字符串 转为 对象数组
 * 呢
 * 说在
 * php 中
 * 数组和对象是没有办法保存的
 * 那么如果你想要
 * 保存数组或对象
 * 
 * 那就要把他转成一个序列
 * 那么这个过程
 * 我把他称为序列化
 * 
 * 序列化 就是将
 * 数组 或 对象
 * 转为一个对象
 * 用的是 serialize(类对象)    序列化
 * 
 * 反序列化
 * 就是将
 * 序列化的那个字符串
 * 再反过来
 * 转成数组或对象
 * 用的是 unserialize('字符串') 反序列化
 * 
 * 那么数组的序列化
 * 和反序列化
 * 直接调用 serialize(类对象)    序列化
 * 和  unserialize('字符串')  反序列化
 * 就可以了
 * 就可以了
 * 
 * 那么
 * 对象的序列化
 * 要注意一下
 * 对象的序列化
 * 也没什么可说的
 * 
 * 但是对象的反序列化
 * 必须要注意
 * 必须要有 本来类的参与
 * 否则的话
 * 它
 * 如果没有参与的话
 * 
 * 它无法确定是哪个类
 * 所以反序列化的时候必须要有
 * 类的参与
 * 那么类的参与以后
 * 
 * 他从里面 他就发现
 * 哦 Yangli 这个类了
 * 
 * 对象的序列化与反序列化
 * 
 * 好
 * 下面我们
 * 来学习
 * 这个
 * 魔术方法
 * 
 * 魔术方法(函数)
 * 都是
 * 计算机
 * 自动执行的
 * 函数方法
 * 
 * 都是 
 * 两个
 * 下滑线
 * 开头
 * 那么
 * 我们
 * 已经
 * 学过的
 * 魔术方法
 * 有哪些呢
 * 有
 * __construct() 生 构造函数
 * __destruc()  死 析构函数
 * __clone() 克隆 类对象被克隆时 自动启动
 * 
 * 下面
 * 我们
 * 再来学
 * 学什么呢
 * __toString()
 * 和
 * __invoke()
 * 那么
 * 这
 * 两个
 * 有什么
 * 作用
 * 我
 * 不说
 * 我来
 * 写
 * 写完了
 * 你给我总结
 * 
 * 什么
 * 时候
 * (这两个函数)
 * 自动执行 
 * 
 * 
 */
class 魔术方法
{
    public function __toString()
    {
        return '这是一个对象不是字符串';
    }
    public function __invoke()
    {
        echo '这是一个对象不是函数';
    }
}
$stu = new 魔术方法;
echo '<hr>';
print_r($stu);
// 魔术方法 Object
// (
// )
echo $stu, '<hr>'; //本来 echo 是无法输出 对象的
//此刻结果却为: 这是一个对象不是字符串
/**
 * __toString()
 * 当 使用echo 输出类对象 时自动调用
 * 类对象里的 __toString()函数方法
 * 
 * 把
 * 类对象
 * 当做
 * 字符串的
 * 时候
 * 自动执行
 * 
 */
$stu(); //这是一个对象不是函数
/**
 * __invoke()
 * 把
 * 类对象
 * 当做
 * 函数的
 * 时候
 * 自动执行
 * 
 * 
 * 
 * 那么
 * 下面呢
 * 我讲一讲
 * 这个
 * __set()  给无法访问的属性赋值的时候 $stu->name = 'tom'; 自动执行
 * __get()  获取无法访问的属性值的时候 echo $stu->name; 自动执行
 * __isset() 判断无法访问的属性存不存在 isset($stu->name) 自动执行
 * __unset() 要销毁无法访问的属性的时候 unset($stu->age); 自动执行
 * 
 * 
 */
class asfasd
//
{

    private $name;
    private $sex;
    private $age;
    public  function __set($a, $b)
    {
        echo "<hr>";
        var_dump($a, $b); //name=>tom 
        //     要写入对象的   键=>值
        echo '__set()  给无法访问的属性赋值的时候 自动执行';
        $this->$a = $b;
        //只要是 没有权限访问的
        //都要经过这给他赋值
    }
    public  function __get($a)
    {
        echo "<hr>";
        var_dump($a); //name 
        // 要读取对象的   键 
        echo '__get()  获取无法访问的属性值的时候 自动执行';
        return $this->$a;
        //只要是 没有权限访问的
        //都要经过这给他返回值
    }
    public  function __isset($a)
    {
        echo "<hr>";
        var_dump($a); //name 
        // 要读取对象的   键 
        echo '__get()  判断无法访问的属性存不存在时 自动执行';
        return $this->$a;
        //只要是 没有权限访问的
        //都要经过这给他返回值
    }
    public  function __unset($a)
    {
        echo "<hr>";
        var_dump($a); //age 
        // 要读取对象的   键 
        echo '__unset()  要销毁无法访问的属性时 自动执行';
        unset($this->$a);
        //只要是 没有权限访问的
        //都要经过这 销毁
    }
}
//测试
$stu = new asfasd;
$stu->name = 'tom'; //给 asfasd 类的 私有变量赋值成功
$stu->sex = '男'; //给 asfasd 类的 私有变量赋值成功
$stu->age = 18; //给 asfasd 类的 私有变量赋值成功
print_r($stu);
// asfasd Object
// (
// [name:asfasd:private] => tom
// [sex:asfasd:private] => 男
// [age:asfasd:private] => 18
// )


// echo '<hr>', $stu->name;//报错

//虽然给 私有变量 赋值成功
//但是却无法调用 私有变量
/**
 * 这时候我们又学下一种
 * 获取方法
 * __get()
 * 
 */
echo '<hr>', $stu->name; //tom



// var_dump(isset($stu->name)); //bool(false)
//存不存在   $stu->name 这个属性
//它说不存在
//怎么可能不存在呢
//他是 因为无法访问找不到
//所以这时候
//我们又要学一个
//__isset() 就是判断无法访问的属性存不存在
var_dump(isset($stu->name)); //bool(true)

echo '<hr>';
// unset($stu->age);//当我销毁 add 的时候
//它销毁掉了吗  没有
// 读取不到 报错
// 私有的让你销毁
unset($stu->age); //通过 __unset配合 成功销毁了私有变量
print_r($stu); //销毁age成功
// asfasd Object
// (
//     [name:asfasd:private] => tom
//     [sex:asfasd:private] => 男
// )
/**
 * 小结
 * __set()  给无法访问的属性赋值的时候 $stu->name = 'tom'; 自动执行
 * __get()  获取无法访问的属性值的时候 echo $stu->name; 自动执行
 * __isset() 判断无法访问的属性存不存在 isset($stu->name) 自动执行
 * __unset() 要销毁无法访问的属性的时候 unset($stu->age); 自动执行
 * 
 * 
 * 同学们说
 * 这个
 * 是
 * 干嘛
 * 用的
 * 呢
 * 你写了
 * 半天
 * 哪有什么用呢
 * 
 * 一般是用来做这个属性
 * 这个属性比如说是
 * 有的属性是 只读 属性
 * 有的属性是 只写 属性
 * 有的属性是 读写 属性
 * 
 * 那么
 * 你比如说
 * 上面例子中
 * 
 * $name  读写 属性
 * $sex   只读 属性
 * $age   只写 属性
 * 
 * 在上面中
 * 
 *  *   public  function __set($a, $b)
 *  {
 *后添加判断
 *   if(in_arry($a,arry('name','age'))){
 *    $this->$a=$b}}
 * 
 * 意思是 只有 $name和$age 是可写的
 * 是 $sex 时则不会写入
 * 
 * in_arry('name',arry('name','age'))
 *        name 是否 在 arry('name','age') 中
 * 是返回 true 否 返回false
 * 
 * 其余 
 * 读  get
 * 判断是否存在  isset
 * 销毁 unset
 * 
 * 均可以自行加入判断
 * 规定哪些可以
 * 哪些不可以
 * 
 * 这是设置权限的
 * 应该是这个意思 
 * 
 * 后台管理权限设置有
 * 可能是用这个
 * 
 * 应用:设置变量的读写属性
 * 
 * 这是前面几个魔术方法的应用
 * 
 * 好
 * 下面我再加讲一个啊
 * 
 * __call()       :调用无法访问的    方法时 自动执行
 * __callstatic() :调用无法访问的静态方法时 自动执行
 */
class asffgdsfs
{
    public function __call($a, $b)
    { //$a  被调用的方法名 
        //$b  被调用的方法名的参数
        echo '<hr>';
        var_dump($a, $b);
    }

    public static function __callstatic($a, $b) //本身就该是 静态的
    { //$a  被调用的方法名 
        //$b  被调用的方法名的参数
        echo '<hr>';
        var_dump($a, $b);
    }
}
$stu = new asffgdsfs;
$stu->show(10, 20);
// string(4) "show",
// array(2) {
//   [0]=>
//   int(10)
//   [1]=>
//   int(20)
// }
asffgdsfs::show(30, 40);
// string(4) "show"
// array(2) {
//   [0]=>
//   int(30)
//   [1]=>
//   int(40)
// }

/**
 * 好 
 * __sleep(): 当类对象被序列化时自动调用
 * 
 * __sleep 睡着了的意思 
 * 他的反义词是
 *  __wakeup  醒来了
 * 
 * __wakeup(): 当类对象参与反序列化时自动调用
 * 
 * 试一下
 */
class hlufab
{
    private $name;
    private $sex;
    public $add = '中区';
    public function __construct($name, $sex)
    {
        $this->name = $name;
        $this->sex = $sex;
    }


    public function __sleep()
    {    //大家都是 中区
        //在序列化 时把中区 去掉 减少内存 存太多中区
        return array('name', "sex");
        //需要 序列化的字段名 

        // O:6:"hlufab":2:{s:12:"hlufabname";s:3:"tom";
        //     s:11:"hlufabsex";s:3:"男";}

        //$add = '中区' 不在 序列化之内
    }
    public function __wakeup()
    {   //在反序列化时 即使不加上中区 也不会造成任何影响
        //会自动加入  $add = '中区';

        //我们也可以 追加 额外 字段
        $this->type = "学生";
        // 反序列时 每个类对象都会多个 
        //type = "学生"; 属性

        //结果展示:
        // object(hlufab)#39 (4) {
        //     ["name":"hlufab":private]=>
        //     string(3) "tom"
        //     ["sex":"hlufab":private]=>
        //     string(3) "男"
        //     ["add"]=>
        //     string(6) "中区"
        //     ["type"]=>
        //     string(6) "学生"
        //   }
    }
}
//测试
$stu = new hlufab('tom', '男');
//下面我来序列化
$str = serialize($stu); //序列化
echo '<hr>', $str;
//O:6:"hlufab":3:
//{s:12:"hlufabname";
// s:3:"tom";s:11:"hlufabsex";s:3:"男";
// s:3:"add";s:6:"中区";}

$stu = unserialize($str); //反序列化
var_dump($stu);
// object(hlufab)#14 (3) {
//     ["name":"hlufab":private]=>
//     string(3) "tom"
//     ["sex":"hlufab":private]=>
//     string(3) "男"
//     ["add"]=>
//     string(6) "中区"
//   }

/**
 * 好了
 * 这是序列化
 * 与反序列化的调用
 * 
 * 现在小结一下
 *  __sleep() :当序列化的时候 自动执行
 *             他的作用是什么呢
 *             指定 要序列化的 字段 以数组的形式
 *             return array('name','sex');
 * 
 * __wakeup() 当反序列化的时候 自动执行
 *             可以初始化值
 *              我也可以不管你原来是什么值
 *              我重新给你赋值也可以
 *             也可以额外的添加一个字段
 * 
 * 注意 未曾被序列化的 变量='有值的'
 *      不需要 在 __wakeup()  手动加回来
 *       由于 反序列化 需要  原本类对象 的参与
 *      所以 
 *       未曾被序列化的 $变量='有值的';
 *      会自动 反序列化 回来
 *       或者说 反序列化 回来
 *       自己就有  $变量='有值的';了
 * 
 * 
 * 好
 *  这是序列化
 * 与
 * 反序列化
 * 
 * 那么到此
 * 咱们的魔术方法
 * 就全部讲完了
 * 
 * 魔术方法
 * 我们来一起回顾一下
 * 
 * __construct() 生 构造函数 new 实例化的时候 自动执行
 * __destruct() 死 析造函数 类对象即将被销毁的时候 自动执行
 * __call() 在外部调用了类对象的私有函数  自动执行
 * __callStatic() 在外部调用了类对象的私有静态函数  自动执行
 * __get() 在外部获取了类对象的私有变量  自动执行
 * __set() 在外部设置了类对象的私有变量  自动执行
 * __isset() 在外部使用了isset()判断了了类对象的私有变量  自动执行
 * __unset() 在外部使用了销毁了类对象的私有变量  自动执行
 * __sleep() 在外部使用了 序列化 类对象的时候  自动执行
 * __wakeup() 在外部使用了 反序列化 类对象的时候  自动执行
 * __toString() 在外部把类对象当做 字符串 使用的时候  自动执行
 * __invoke() 在外部把类对象当做 函数 使用的时候  自动执行
 * __clone() 在外部 使用了 clone()函数 克隆类对象的时候  自动执行
 * 
 *         
 * 那么下面我来问一个问题
 * 好像少一个 __autoload($缺少的类名) 呀
 * 就是缺少类 自动调用 并传入$缺少的类名 的那个
 * 
 * 没错是吧
 *  __autoload() 是魔术方法吗
 * 
 * 魔术方法 包不包含 __autoload()
 * 
 * 不包含 因为 __autoload() 不在 类/类对象 里面
 * 
 * 以上所有魔术方法全部 写在 类/类对象 里面
 * 
 *  __autoload()不属于魔术方法
 * 
 * 有人说 __autoload() 不也是自动执行的吗
 * 
 * 你别忘了
 * 魔术方法 的方法
 * 一定写哪才叫方法?
 * 
 * 对一定要写在 类/类对象 里面才叫方法
 * 写在外面的一律叫函数
 * 虽然本质 是一类 东西
 * 
 * __autoload() 函数
 * 是直接写到 页面中的
 * 
 * 
 * 好的啊
 * 
 * 前面我们说过
 * 多态
 * 
 * 有两种形式
 * 一种是方法重写
 * 一种是方法重载
 * 
 * 但是我们说
 * php 啊
 * 他不支持
 * 方法重载
 * 但是我们说过
 * 
 * 我们可以通过
 * 魔术方法
 * 来模拟它(方法重载)
 * 
 * 那么
 * 下面
 * 我怎么
 * 通过
 * 魔术方法
 * 模拟
 * 方法重载呢
 * 
 * 你看
 * 我这么来写
 * 
 */
class Math
{
    public function __call($fn_name, $fn_args)
    //                     函数名     函数的参数
    { //$fn_args 是个数组 包含了所有参数
        // 我们可以遍历出每个数组 键的值 然后累计相加
        //就可做到 不同参数的 求和
        $sum = 0;
        foreach ($fn_args as $item) {
            $sum += $item;
        }
        echo  implode(',', $fn_args) . '和是' . $sum;
        //结果展示
        // 10,20和是3010,20,30和是6010,20,30,40和是100

        //foreach([数组] as $item){}
        // foreach(数组 as $index=>$item){}

        // implode(',', [数组]); /以  item,item,item 形式展示


    }
}
$math = new Math;
$math->call(10, 20); //我想要求两数之和
// 10,20和是30
$math->call(10, 20, 30); //我想要求三数之和
//10,20,30和是60
$math->call(10, 20, 30, 40); //我想要求4数之和
// 10,20,30,40和是100

/**
 * 同学们
 * 来看
 * 这里面(指 Math 类 里面)
 * 你感觉写了几个 call 啊
 * 
 * 应该是 3个 同名call 但不同 参 的 公有函数
 * 但是这样写 虽然能够实现
 * 但是 很复杂 不够简便
 * 我们利用
 *  魔术方法中的 
 * __call(){} 
 * 在外部调用了类对象没有权限的函数  自动执行
 * 
 * 
 * 
 * 下面呢我来
 * 展示
 * 一个
 * 遍历对象
 * 的
 * 例子
 * 
 */
class asfsd
{
    public $name = 'tom';
    protected $sex = '男';
    private $age = 22;
    public function show()
    {
        foreach ($this as $index => $item) {
            echo "<hr>", $index, $item;
            //            name ,tom
        }
    }
}
$stu = new asfsd;
// foreach ($stu as $index => $item) {
//     echo "<hr>", $index, $item;
//     //            name ,tom
// }
//因为在外部我只有 公有的 public $name = 'tom';
//能访问
$stu->show();
// name,tom
// sex,男
// age,22

// 虽然类里面有 函数show
//也因此 类对象 里面也有 函数show()
//但是为什么 没有遍历出来呢

//因为 前面提到过 函数是个 大家伙
//是作为 地址 存在 类对象里的
//因此 类对象里面 没有函数
//只有函数的地址 地址有什么用
//用途 是只供调用 
//因此 类对象里面是没有函数本体的

// foreach (数组/类对象 as $index => $item) {}
//foreach 即是遍历 数组  的方法
//    也是遍历 类对象 的方法

/**
 * 那么得出一个结论
 * 遍历类对象
 * 遍历的是
 * 当前
 * 位置
 * 所能
 * 访问到的 类 属性变量
 *  
 */


/**
 * 今日 2021 4 21
 * 下面
 * 我
 * 带大家
 * 做
 * 一套
 * 综合练习
 * 就是
 * 封装
 * Mysql的
 * 一个
 * 单例
 * 
 * 那么
 * 这里面
 * 我们准备做什么呢
 * 
 * 就是
 * 我们现在已经讲过
 * 单例模式了
 * 是吧
 * 
 * 我们是不是也讲过
 * mysqli 操作了
 * (指 与 mysqli相关的一系列 mysqli开头的函数)
 * 
 * mysqli 操作
 * 然后
 * 对
 * 数据库
 * 做
 * 增删改查
 * 
 * 总共算下来
 * 就
 * 三件事
 * 1.单例
 * 2.连接数据库
 * 3.对数据进行操作
 * 
 * 那么我们一步步来啊
 * 
 * 单例模式的实现
 * mysql数据库 实现
 * 
 * 我们先把单例写出来
 * 
 * 
 */
echo '<hr>';

class MySQLDB
{
    private $host; //接受 外部的 主机   参数
    private $port; //接受 外部的 端口号 参数
    private $user; //接受 外部的 用户名 参数
    private $pwd;  //接受 外部的 密码   参数
    private $dbname; //接受 外部的 数据库名 参数
    private $charset; //接受 外部的 字符集 参数
    private $link; //接受 内部的 连接成功返回的 mysql对象


    private function initParam($param)
    { //初始化成员 成员(被内部调用)
        $this->host = $param['host'] ?? '127.0.0.1'; // ?? 等于 js的 ||
        $this->port = $param['port'] ?? 3306;
        $this->user = $param['user'] ?? '';
        $this->pwd = $param['pwd'] ?? '';
        $this->dbname = $param['dbname'] ?? '';
        $this->charset = $param['charset'] ?? '';
        $this->charset = $param['charset'] ?? 'utf8';
    }
    private function initConnect()
    { // 初始化数据库 (被内部调用)
        $this->link = @mysqli_connect($this->host, $this->user, $this->pwd, $this->dbname, $this->port)  or die('<br>数据库连接失败<br>错误信息 : ' . mysqli_connect_error() . '<br>错误码: ' . mysqli_connect_errno());
        //1.连接 mysql 如果出错 显示错误信息

        mysqli_query($this->link, "SET NAMES {$this->charset}");;
        //set names utf8指定了客户端和服务器之间传递字符的编码规则为UTF8。
    }



    private static $instance;
    //私有的静态属性 将会保存当前单例 的 唯一一个 类对象

    private function __construct($param)
    { //私有的构造函数 将会阻止外部的 实例化 类对象 

        $this->initParam($param); //抽离 并调用 初始化成员 赋值  为 函数 

        $this->initConnect(); //抽离 并调用 连接 mysql 为 函数 

        // 只要内部静态函数方法 一实例化 触发 构造函数 生
        // 就开始 连接数据库
    }
    private function __clone()
    { //私有的克隆函数 将会阻止外部 克隆 类对象 
    }
    public static function getInstance($param = array())
    { //公有的静态方法 将只能在此处获取当前的 单例

        //如果未曾被实例化 才实例化 防止重复实例化
        if (!self::$instance instanceof self) {
            // 当前 self::$instance 的实例化出来的类对象
            // 是否属于 当前类 的 class 数据类型 这里指  MySQLDB 的 数据类型

            self::$instance = new self($param); //实例化 并初始化成员
            //self 当前类的类名
            //static 表示当前对象所属的类
        }
        return self::$instance;
        //如果已经被实例化 直接返回 以保存的实例化 类对象
    }

    //连接 数据库 封装完毕
    //接下来还要写执行 数据库的 语句
    //封装 1.增2.删3.改4.查 
    //4种功能都要用的 mysqli_query语句
    private function execute($sql)
    {
        //外部  mysqli_query(函数 所有 sql语句入口)
        $rs = mysqli_query($this->link, $sql);
        if ($rs != false) {
            echo  '<br>SQL语句执行成功<br>';
            return $rs;
        } else {
            echo '<br>SQL语句执行失败<br>'
                . '错误码' . mysqli_errno($this->link) . '<br>'
                . '错误的SQL语句' . $sql . '<br>'
                . '错误信息' . mysqli_error($this->link);
            exit; //代码终止
        }
    }
    //增删改(无返回结果集的写到一起)
    public function exec($sql)
    { //增:insert   删:delete  改:updete
        // 通过截取 $sql 的字符串进行判断 是哪种行为
        //由于 三个单词长度  都是 6个
        $key = substr($sql, 0, 6); // insert / delete /updete
        if (in_array($key, array("insert", "INSERT", "delete", "DELETE", "update", "UPDATE"))) {
            return $this->execute($sql);
            //返回的 是 execute 返回的$rs/echo错误信息
        } else {
            echo '非法访问<br>';
            exit;
        }
    }

    //增加 成功时返回的 添加成功 id 为
    public function getLastInsertId()
    {
        return mysqli_insert_id($this->link);
    }

    //查询(3种) 成功时返回的 结果集为
    private function query($sql)
    {   //查结果集                       :select   
        // 查看索引 包括 主键 外键 唯一   :show  
        // 查看表头(列)约束              :desc
        if (
            substr($sql, 0, 6) == 'select'
            || substr($sql, 0, 6) == 'SELECT'
            || substr($sql, 0, 4) == 'show'
            || substr($sql, 0, 4) == 'SHOW'
            || substr($sql, 0, 4) == 'desc'
            || substr($sql, 0, 4) == 'DESC'
        ) {
            return $this->execute($sql);
        } else {
            echo "非法访问<br>";
            exit;
        }
    }

    //查询返回多维数组
    //查询语句 一次只返回一个对象 这是不合理的
    //我们需要在进行一次封装
    public function fetchAll($sql, $type = MYSQLI_ASSOC)
    {
        if (
            $type != MYSQLI_ASSOC
            && $type != MYSQLI_NUM
            && $type != MYSQLI_BOTH
        ) {
            echo
            "'{$type}' 类型错误
            请输入正确的类型
            <br>类型必须是一下三种
            <br>MYSQLI_ASSOC (获取全部关联数组)
            <br>MYSQLI_NUM (获取全部索引数组(默认))
            <br>MYSQLI_BOTH (获取全部 索引和关联 数组 )
            <br>不输入默认
            <br>MYSQLI_ASSOC 获取全部关联数组";
            exit;
        };
        $rs = $this->query($sql); //调用 内部 查询函数 返回的单行对象数据
        return mysqli_fetch_all($rs, $type); //获取全部索引数组(默认)

        // print_r(mysqli_fetch_all($b4, MYSQLI_NUM)); 
        //获取全部索引数组(默认)
        // print_r(mysqli_fetch_all($b4, MYSQLI_ASSOC)); 
        //获取全部关联数组
        // print_r(mysqli_fetch_all($b4, MYSQLI_BOTH));
        //获取全部 索引和关联 数组 
    }

    //但是上面 查询where id=1 只有一条结果返回也是个 二维数组
    //此时 我们应该 返回个 一维数组就够了
    public function fetchRow($sql, $type = MYSQLI_ASSOC)
    {
        $list = $this->fetchAll($sql, $type);
        if (!empty($list)) { //如果 查询二维数组不为空
            return $list[0];  //返回 二维数组中的第一个
        } else {
            return array(); //否则返回空
        }
    }


    //又但是 select count(*) from news 只需要返回一行一列
    public function fetchColumn($sql)
    {
        $list = $this->fetchAll($sql, MYSQLI_NUM);
        if (!empty($list)) { //如果  查询二维数组不为空
            return  $list[0];  //返回 二维数组中的第一个
        } else {
            return array(); //否则返回空
        }
    }
}

//测试
//配置 mysql 登录 参数
$param = array(
    'user' => 'root',
    'pwd' => 'root',
    'dbname' => 'xinWen'
);
//得到一个供mysql使用的单例
$db =  MySQLDB::getInstance($param);

// 增
// if ($db->exec("INSERT INTO nes VALUES(NULL,'方','萨',UNIX_TIMESTAMP());")) {
//     //新增一条数据 成功返回id 
//     echo  '<br>mysql语句 执行新增成功 新插入行自增id为' . $db->getLastInsertId();
// }

//抛弃(多余)
//  else {不需要错误提示 增删改的 错误统一 返回了 并且SQL语句有错时 代码会exit 终止
//     //失败提示错误
//     echo 'mysql语句 执行失败';
// }

//删
// $db->exec("DELETE FROM news where id=6; ");
//删除 id 6  的 那一行数据

//改
// $db->exec("update news set title1='青草' where id=2;");
//修改 id 2  的 title1 为 青草

//查
// $list = $db->fetchAll("SELECT * from news ;"); //查询多结果 二维数组
// $list = $db->fetchRow("SELECT * from news where id=1 ;"); //查询单结果
//返回 1维数组 去掉了一个空维

$list = $db->fetchColumn("select count(*) from news ;"); //查询一行一列的计数结果 其实就是 把关联数组 变成了索引数组 对于上面的 fetchRow 是一样的

// SQL语句执行成功 fetchRow
// array(1) {
//   ["count(*)"]=>
//   string(2) "42"
// }

// array(1) { fetchColumn
//     [0]=>
//     string(2) "42"
//   }

// 写了这么多   增伤改 一种  查询 3种 
// 流出了 四个 外部 函数 要我我也行

//我一开始还以为 一共就封装 两个 函数 
// 查询函数里面写判断逻辑
//没想到 留了这么多 外部函数入口




var_dump($list);




// var_dump($db);
// age22object(MySQLDB)#14 (7) {
//     ["host":"MySQLDB":private]=>
//     string(9) "127.0.0.1"
//     ["port":"MySQLDB":private]=>
//     int(3306)
//     ["user":"MySQLDB":private]=>
//     string(4) "root"
//     ["pwd":"MySQLDB":private]=>
//     string(4) "root"
//     ["dbname":"MySQLDB":private]=>
//     string(6) "xinWen"
//     ["charset":"MySQLDB":private]=>
//     string(4) "utf8"
//     ["link":"MySQLDB":private]=>
//     NULL
//   }


/**
 * 1.单例 准备完成
 * 
 * (类对象 instanceof 类) 的意思是
 * 类对象 是否是      类  的类型 
 * 是返回 true
 * 否返回 false
 * 
 * 光有
 * 空壳
 * 单例
 * 是
 * 没用的
 * 
 * 啥都没干
 * 下面
 * 我们要
 * 连接
 * 数据库啊
 * 是不是
 * 
 * 好了 我们休息一下
 * 
 * 好了
 * 刚才呢
 * 单例
 * 已经做好了
 * 单例
 * 已经做好了
 * 接下来
 * 是不是要连接数据库啦
 * 
 * 那连接数据库同学们来看
 * 我们来分析啊
 * 
 * 在我一实例化的时候就该 连数据库
 * 
 * 
 * 总结:
 *    这里封装那里封装
 *    这么麻烦
 *   我说个场景
 * 你就明白了
 * 
 * 请问 
 * 我写了这么多
 * 是不是
 * 全部写在
 * 类里面
 * 将来呀
 * 有一个
 * 
 * 好人
 * 这个好人
 * 把这个类
 * 封装好了
 * 你工作的时候只要写下面这一点
 * 就够了
 * 你觉得
 * 哪个好哇
 * 
 * 还是 别人帮封装了的好
 * 是我冒失了 考虑不周 没想全面
 * 
 * 这个好吧
 * 我告诉你
 * 将来
 * 你们用的框架
 * 好人啊
 * 都把这些类
 * 全部封装好了
 * 
 * 好人太好啦
 * 
 * 我们
 * 将来
 * 开发的
 * 时候
 * 只要做下面的调用就可以了
 * 
 * 但是我给你们讲的
 * 不是 告诉你
 * 怎么调用
 * 
 * 我现在要你们
 * 后边
 * 跟着我
 * 学着写框架
 * 
 * 我现在要你们
 * 先在后边
 * 跟着我
 * 学着写框架
 * 
 * 你们的主要精力
 * 现在是 封装类 这里面的东西
 * 
 * 等到以后
 * 这一部分
 * 你写6了
 * 
 * 那我们以后就不写了
 * 我们就写
 * 
 * 我们以后做工作的时候
 * 就写下面的调用就够了
 * 
 * 
 * 
 * 
 */




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 《数据库系统工程师五天修练》是一本关于数据库系统工程师职业技能培训的书籍,通过五天的修炼,帮助读者系统地学习和掌握数据库系统工程师的知识和技能。 这本书的主要内容包括数据库系统的基础知识、数据库管理和维护、性能优化、数据备份与恢复等方面。通过分析实际案例和练习,读者可以深入了解数据库系统的原理和实践应用。 首先,第一天的修炼主要介绍数据库系统的基础知识,包括数据库概念、数据库模型和范式、SQL语言基础等。读者可以通过学习这些知识,了解数据库系统的基本构成和运作方式。 接下来,第二天的修炼聚焦在数据库管理和维护方面。读者将学习如何创建和管理数据库,包括表的创建和修改、索引的使用和优化等。此外,还将讨论数据库的安全性和权限管理,以及常见的故障处理和故障恢复方法。 第三天的修炼着重介绍数据库的性能优化。通过学习索引的设计和使用、查询语句的优化、数据库参数的调整等技术,读者可以提高数据库系统的性能和响应速度。 第四天的修炼关注数据备份与恢复。读者将学习数据库备份和恢复的方法和策略,以确保数据库的安全性和可恢复性。此外,还将介绍常见的数据迁移和数据同步技术。 最后,第五天的修炼将通过实际案例和练习,帮助读者将所学的知识应用到实际工作中。通过解决真实问题,读者可以加深对数据库系统工程师职业技能的理解和应用。 总之,《数据库系统工程师五天修练》是一本有助于数据库系统工程师提升职业技能的实用指南。通过系统的学习和实践,读者可以全面掌握数据库系统的原理和实践技术,提高自己在数据库管理和维护、性能优化、数据备份与恢复等方面的能力。 ### 回答2: 《数据库系统工程师五天修炼.pdf》是一本关于数据库系统工程师修炼的书籍。在这本书中,作者介绍了数据库系统工程师的基本知识和技能,并提供了一系列的实践案例和习题,帮助读者在五天内快速提升自己的数据库系统工程师能力。 首先,该书从数据库的基本概念和原理讲起,介绍了关系型数据库、非关系型数据库以及分布式数据库等不同类型的数据库系统。读者可以通过学习这些基本知识,了解数据库的结构和运作原理。 其次,该书还介绍了数据库的设计和优化技巧。数据库的设计是数据库系统工程师的核心工作之一,它涉及到表结构设计、数据模型设计等方面,而优化则是提高数据库性能和效率的重要手段。通过学习这些技巧,读者可以学会如何设计出高效的数据库系统。 此外,该书还介绍了SQL语言和存储过程的使用。SQL是数据库系统工程师必备的编程语言,它可以用来操作和管理数据库。而存储过程是数据库中一组预定义的SQL语句集合,可以用于实现复杂的业务逻辑。通过学习SQL语言和存储过程的使用,读者可以掌握数据库的操作和管理技能。 最后,该书还介绍了数据库系统的安全和备份恢复策略。保证数据库系统的安全性是数据库系统工程师的重要职责之一,而备份恢复策略则是防止数据丢失和业务中断的关键措施。通过学习这些策略,读者可以了解如何保障数据库系统的安全性和可靠性。 总的来说,《数据库系统工程师五天修炼.pdf》是一本系统全面介绍数据库系统工程师知识和技能的书籍。通过学习这本书,读者可以快速提升自己的数据库系统工程师能力,成为一名优秀的数据库系统工程师。 ### 回答3: 《数据库系统工程师五天修练.pdf》是一本专门面向数据库系统工程师的修炼指南。此书内容涵盖数据库系统的各个方面,包括数据库设计、数据模型、数据管理、数据安全等。通过学习这本书,数据库系统工程师能够提高自己的专业能力,更好地应对工作中遇到的问题。 首先,该书从数据库设计入手,介绍了关系型数据库的基本概念和范式理论,教会读者如何规划数据库的结构和关系,使其能够快速高效地存储和检索数据。同时,该书也对数据库的物理设计进行了详细探讨,让读者能够了解磁盘存储、数据索引等方面的知识,以提升数据库的性能和可靠性。 其次,该书还涉及到数据模型的建立和使用,包括关系模型、面向对象模型等。读者可以通过深入学习这些模型,了解其特点和适用场景,并能够根据实际需求选择合适的模型进行数据库设计,以最大限度地满足业务需求。 此外,该书还介绍了数据库管理的相关知识,包括数据备份和恢复、性能优化、容灾和高可用等方面。读者能够学会如何规划数据库的备份策略,以保证数据的安全性和可恢复性;同时,还能学习到如何通过调优数据库的配置和参数,提升数据库的性能和响应速度。 最后,该书也对数据库系统的安全进行了全面介绍。读者能够了解到数据库系统面临的各种安全威胁和攻击手段,学习到如何设置用户权限、加密敏感数据以及监控数据库的安全漏洞,以确保数据库系统的安全性和可信度。 综上所述,《数据库系统工程师五天修练.pdf》是一本对于数据库系统工程师而言非常有价值的修炼指南。通过学习这本书,数据库系统工程师能够全面提升自己的专业能力,更加熟练地应对各类数据库系统相关的工作挑战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qwer22215

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值