首页
留言
友情链接
标签页
Search
1
如何使用JavaScript获取和设置CSS root变量值
1,072 阅读
2
中国历史朝代顺序图
676 阅读
3
那些顶级不落俗套的“美好祝福”
552 阅读
4
春和 《江海共余生》
529 阅读
5
hyperf常用命令
410 阅读
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
故事
诗词
生活
学习
学科
语文
数学
英语
物理
化学
生物
政治
历史
地理
自然
其他
抖音
快手
小视频
随笔
易经
书摘
登录
/
注册
Search
标签搜索
一年级语文
sunshine
累计撰写
162
篇文章
累计收到
15
条评论
首页
栏目
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
故事
诗词
生活
学习
学科
语文
数学
英语
物理
化学
生物
政治
历史
地理
自然
其他
抖音
快手
小视频
随笔
易经
书摘
页面
留言
友情链接
标签页
搜索到
161
篇与
的结果
2022-10-12
MySQL数据库(一)
1.1 目标掌握数据库的作用;能够通俗的解释什么是关系型数据库;能够至少说出三种关系型数据库;掌握MySQL客户端登录和登出MySQL服务器;理解数据库具体数据的存储逻辑;掌握创建、查看和删除数据库;了解MySQL数据库创建与删除指令对应的文件效果掌握数据表的增删改查操作;掌握数据的增删改查操作;1.2 数据库介绍1.2.1 作用数据库是用来存放数据的仓库数据库中存放的是表,表中存放的是数据。1.2.2 数据库的发展史萌芽阶段:文件系统最初始的数据库是用磁盘来存储数据的。文件就是最早的数据库。第一代数据库:层次模型优点:这是导航结构优点:结构清晰,分类查询方便缺点:有可能造成数据无效第一代数据库:网状模型网状模型解决了层次模型的数据不一致的问题,但没有解决导航问题。导航结构在查询中有时候效率低下,比如查询整个公司的四月的营业额。第二阶段:关系模型特点:1、每个表都是独立的2、通过关系字段将两个表连接起来3、关系:两个表的公共字段4、关系型数据库中多表联合查询效率低下。多学一招:为了解决关系型数据库多表查询效率的问题,项目中使用了NoSQL(非关系型数据库,Redis、mongodb等等),在数据库中按照键值对来存储,它是关系型数据库的补充。1.2.3 SQLStructured Query Language(结构化查询语言),是用来操作关系型数据库的一门语言。这是一个关系型数据库的通用操作语言,也成为标准SQL,也叫SQL-92。脚下留心:数据库的生产厂商为了占有市场份额,都会在标准SQL的基础上扩展一些自己的东西以吸引用户。1.2.4 常用的关系型数据库关系型数据库开发公司使用语言access微软公司SQLSQL Server微软公司T-SQLOracle甲骨文公司PL/SQLMySQL被甲骨文公司收购MySQL思考:已知标准SQL可以在所有的关系型数据库上运行,在Oracle上编写的PL/SQL能否在MySQL上运行?答:不可以,只能运行标准SQL1.3 连接服务器数据库是CS模式的软件,所以要连接数据库必须要有客户端软件。MySQL数据库默认端口号是33061.3.1 window界面连接服务器1、Navicat2、MySQL-Front1.3.2 通过web窗体连接主要有浏览器就可以访问数据库1.3.3 命令行连接host -h 主机 port -P 端口号 (大写) user -u 用户名 password -p 密码 (小写)例题-- 连接数据库 F:\wamp\PHPTutorial\MySQL\bin>mysql -h127.0.0.1 -P3306 -uroot -proot -- 明文 -- 如果连接本地数据库 -h可以省略 如果服务器端口是3306,-P端口号也可以省略 F:\wamp\PHPTutorial\MySQL\bin>mysql -uroot -proot -- 明文 -- 密文 F:\wamp\PHPTutorial\MySQL\bin>mysql -uroot -p Enter password: ****1.3.4 退出登录mysql> exit -- 方法一 mysql> quit -- 方法二 mysql> \q -- 方法三1.4 数据库基本概念1.4.1 数据库、表相关数据库:数据库中存放的是表,一个数据库中可以存放多个表表:表是用来存放数据的。关系:两个表的公共字段行:也称记录,也称实体列:也称字段,也称属性脚下留心:就表结构而言,表分为行和列;就表数据而言,表分为记录和字段;就面向对象而言,一个记录就是一个实体,一个字段就是一个属性。1.4.2 数据相关1、数据冗余:相同的数据存储在不同的地方冗余只能减少,不能杜绝。 减少冗余的方法是分表2、数据完整性:正确性+准确性=数据完整性正确性:数据类型正确 准确性:数据范围要准确思考:学生的年龄是整型,输入1000岁,正确性和准确性如何?答:正确的,但不准确。失去了数据完整性。1.4.3 数据库执行过程1.5 数据库的操作1.5.1 创建数据库语法:create database [if not exists] 数据名 [选项]例题-- 创建数据库 mysql> create database stu; Query OK, 1 row affected (0.06 sec) -- 创建数据库时,如果数据库已经存在就要报错 mysql> create database stu; # ERROR 1007 (HY000): Can't create database 'stu'; database exists -- 在创建数据库时候,判断数据库是否存在,不存在就创建 mysql> create database if not exists stu; Query OK, 1 row affected, 1 warning (0.00 sec) -- 特殊字符、关键字做数据库名,使用反引号将数据库名括起来 mysql> create database `create`; Query OK, 1 row affected (0.04 sec) mysql> create database `%$`; Query OK, 1 row affected (0.05 sec) -- 创建数据库时指定存储的字符编码 mysql> create database emp charset=gbk; Query OK, 1 row affected (0.00 sec) # 如果不指定编码,数据库默认使用安装数据库时指定的编码MySQL数据库的目录数据库保存的路径在安装MySQL的时候就配置好。 也可以在my.ini配置文件中更改数据库的保存地址。(datadir="F:/wamp/PHPTutorial/MySQL/data/") 一个数据库就对应一个文件夹,在文件夹中有一个db.opt文件。在此文件中设置数据库的字符集和校对集**小结:1、如果创建的数据库已存在,就会报错。解决方法:创建数据库的时候判断一下数据库是否存在,如果不存在再创建2、如果数据库名是关键字和特殊字符要报错。解决:在特殊字符、关键字行加上反引号3、创建数据库的时候可以指定字符编码脚下留心:创建数据库如果不指定字符编码,默认和MySQL服务器的字符编码是一致的。1.5.2 显示所有数据库语法:show databases例题mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | test | +--------------------+ 4 rows in set (0.00 sec)注意:数据库安装后,只带上面四个数据库1.5.3 删除数据库语法:drop database [if exists] 数据库名例题mysql> drop database `create`; Query OK, 0 rows affected (0.00 sec) mysql> drop database `%$`; Query OK, 0 rows affected (0.00 sec) -- 判断数据库是否存在,如果存在就删除 mysql> drop database if exists stu; Query OK, 0 rows affected (0.00 sec)小结:1、如果删除的数据库不存在,会报错解决:删除之前判断一下,如果存在就删除1.5.4 显示创建数据库的语句语法:show create database 数据库名例题:mysql> show create database emp; +----------+-------------------------------------------------------------+ | Database | Create Database | +----------+-------------------------------------------------------------+ | emp | CREATE DATABASE `emp` /*!40100 DEFAULT CHARACTER SET gbk */ | +----------+-------------------------------------------------------------+ 1 row in set (0.00 sec)1.5.5 修改数据库只能修改数据库选项,数据库的选项只有字符编码语法:alter database 数据库名 charset=字符编码例题:mysql> alter database emp charset=utf8; Query OK, 1 row affected (0.00 sec)小结:1、修改数据库只能修改数据库的字符编码2、在MySQL中utf字符编码之间没有横杆 utf81.5.6 选择数据库语法:use 数据库名例题mysql> use emp; Database changed1.6 表的操作mysql> create database data; Query OK, 1 row affected (0.00 sec) mysql> use data; Database changed1.6.1 创建表语法:create table [if not exists] `表名`( `字段名` 数据类型 [null|not null] [default] [auto_increment] [primary key] [comment], `字段名 数据类型 … )[engine=存储引擎] [charset=字符编码] null|not null 是否为空 default: 默认值 auto_increment 自动增长,默认从1开始,每次递增1 primary key 主键,主键的值不能重复,不能为空,每个表必须只能有一个主键 comment: 备注 engine 引擎决定了数据的存储和查找 myisam、innodb 脚下留心:表名和字段名如果用了关键字,要用反引号引起来。例题: -- 设置客户端和服务器通讯的编码 mysql> set names gbk; Query OK, 0 rows affected (0.00 sec) -- 创建简单的表 mysql> create table stu1( -> id int auto_increment primary key, -> name varchar(20) not null -> )engine=innodb charset=gbk; Query OK, 0 rows affected (0.11 sec) -- 创建复杂的表 mysql> create table stu2( -> id int auto_increment primary key comment '主键', -> name varchar(20) not null comment '姓名', -> `add` varchar(50) not null default '地址不详' comment '地址', -> score int comment '成绩,可以为空' -> )engine=myisam; Query OK, 0 rows affected (0.06 sec)小结:1、如果不指定引擎,默认是innodb2、如果不指定字符编码,默认和数据库编码一致3、varchar(20) 表示长度是20个字符数据表的文件一个数据库对应一个文件夹 一个表对应一个或多个文件 引擎是myisam,一个表对应三个文件 .frm :存储的是表结构 .myd :存储的是表数据 .myi :存储的表数据的索引 引擎是innodb,一个表对应一个表结构文件,innodb的都有表的数据都保存在ibdata1文件中,如果数据量很大,会自动的创建ibdata2,ibdata3...innodb和myisam的区别引擎 myisam1、查询速度快2、容易产生碎片3、不能约束数据innodb1、以前没有myisam查询速度快,现在已经提速了2、不产生碎片3、可以约束数据脚下留心:推荐使用innodb。1.6.2 显示所有表语法show tables;例题:mysql> show tables; Empty set (0.00 sec)1.6.3 显示创建表的语句语法show create table; -- 结果横着排列 show create table \G -- 将结果竖着排列例题1.6.4 查看表结构语法desc[ribe] 表名例题-- 方法一 mysql> describe stu2; +-------+-------------+------+-----+----------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+----------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | add | varchar(50) | NO | | 地址不详 | | | score | int(11) | YES | | NULL | | +-------+-------------+------+-----+----------+----------------+ 4 rows in set (0.05 sec) -- 方法二 mysql> desc stu2; +-------+-------------+------+-----+----------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+----------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | add | varchar(50) | NO | | 地址不详 | | | score | int(11) | YES | | NULL | | +-------+-------------+------+-----+----------+----------------+ 4 rows in set (0.00 sec)1.6.5 复制表语法一:create table 新表 select 字段 from 旧表特点:不能复制父表的键,能够复制父表的数据语法二:create table 新表 like 旧表特点:只能复制表结构,不能复制表数据小结:*表示所有字段1.6.6 删除表语法:drop table [if exists] 表1,表2,… 例题:-- 删除表 mysql> drop table stu4; Query OK, 0 rows affected (0.06 sec) -- 如果表存在就删除 mysql> drop table if exists stu4; Query OK, 0 rows affected, 1 warning (0.00 sec) -- 一次删除多个表 mysql> drop table stu2,stu3; Query OK, 0 rows affected (0.03 sec)1.6.7 修改表语法:alter table 表名 创建初始表mysql> create table stu( -> id int, -> name varchar(20) -> ); Query OK, 0 rows affected (0.00 sec)1、添加字段:alter table 表名add [column] 字段名 数据类型 [位置]mysql> alter table stu add `add` varchar(20); -- 默认添加字段放在最后 Query OK, 0 rows affected (0.05 sec) mysql> alter table stu add sex char(1) after name; -- 在name之后添加sex字段 Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table stu add age int first; -- age放在最前面 Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc stu; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | age | int(11) | YES | | NULL | | | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | sex | char(1) | YES | | NULL | | | add | varchar(20) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 5 rows in set (0.00 sec)2、删除字段:alter table 表 drop [column] 字段名mysql> alter table stu drop age; -- 删除age字段 Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 03、修改字段(改名):alter table 表 change [column] 原字段名 新字段名 数据类型 …-- 将name字段更改为stuname varchar(10) mysql> alter table stu change name stuname varchar(10); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc stu; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | stuname | varchar(10) | YES | | NULL | | | sex | char(1) | YES | | NULL | | | add | varchar(20) | YES | | NULL | | +---------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec)4、修改字段(不改名):alter table 表 modify 字段名 字段属性…-- 将sex数据类型更改为varchar(20) mysql> alter table stu modify sex varchar(20); Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 -- 将add字段更改为varchar(20) 默认值是‘地址不详’ mysql> alter table stu modify `add` varchar(20) default '地址不详'; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 05、修改引擎:alter table 表名 engine=引擎名mysql> alter table stu engine=myisam; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 06、修改表名:alter table 表名 rename to 新表名-- 将stu表名改成student mysql> alter table stu rename to student; Query OK, 0 rows affected (0.00 sec)7、将表移动到其他数据库-- 将当前数据库中的student表移动到php74数据库中改名为stu mysql> alter table student rename to php74.stu; Query OK, 0 rows affected (0.00 sec)1.7 数据操作1.7.1 插入数据语法:insert into 表名 (字段名, 字段名,…) values (值1, 值1,…)1、插入所有字段-- 插入所有字段 mysql> insert into stu (id,stuname,sex,`add`) values (1,'tom','男','北京'); Query OK, 1 row affected (0.00 sec) -- 插入部分字段 mysql> insert into stu(id,stuname) values (2,'berry'); -- 插入的字段和表的字段可以顺序不一致。但是插入字段名和插入的值一定要一一对应 mysql> insert into stu(sex,`add`,id,stuname) values ('女','上海',3,'ketty'); Query OK, 1 row affected (0.00 sec) -- 插入字段名可以省略 mysql> insert into stu values(4,'rose','女','重庆'); Query OK, 1 row affected (0.00 sec)小结:1、插入字段名的顺序和数据表中字段名的顺序可以不一致 2、插入值的个数、顺序必须和插入字段名的个数、顺序要一致。 3、如果插入的值的顺序和个数与表字段的顺序个数一致,插入字段可以省略。2、插入默认值和空值mysql> insert into stu values (5,'jake',null,default); Query OK, 1 row affected (0.05 sec)小结:default关键字用来插入默认值,null用来插入空值.3、插入多条数据mysql> insert into stu values (6,'李白','男','四川'),(7,'杜甫','男','湖北'); Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 01.7.2 更新数据语法:update 表名 set 字段=值 [where 条件]-- 将berry性别改为女 mysql> update stu set sex='女' where stuname='berry'; Query OK, 1 row affected (0.06 sec) -- 将编号是1号的学生性别改成女,地址改为上海。 mysql> update stu set sex='女',`add`='上海' where id=1; Query OK, 1 row affected (0.00 sec)1.7.3 删除数据语法:delete from 表名 [where 条件]-- 删除1号学生 mysql> delete from stu where id=1; -- 删除名字是berry的学生 mysql> delete from stu where stuname='berry'; Query OK, 1 row affected (0.00 sec) -- 删除所有数据 mysql> delete from stu; Query OK, 5 rows affected (0.00 sec)多学一招:delete from 表和truncate table 表区别?1、delete from 表:遍历表记录,一条一条的删除 2、truncate table:将原表销毁,再创建一个同结构的新表。就清空表而言,这种方法效率高。1.7.4 查询数据语法:select 列名 from 表名例题-- 查询id字段的值 mysql> select id from stu; -- 查询id,stuname字段的值 mysql> select id,stuname from stu;、 -- 查询所有字段的值 mysql> select * from stu;1.7.5 数据传输时使用字符集发现:在插入数据的时候,如果有中文会报错(或者中文无法插入)分析:1、查看客户端发送的编码2、查看服务器接受,返回的编码更改接受客户端指令的编码mysql> set character_set_client=gbk; Query OK, 0 rows affected (0.05 sec)原因:返回编码是utf8,客户端是gbk;测试:成功可以通过set names一次性设置小结:1、设置什么编码取决于客户端的编码2、通过set names 设置编码1.8 补充知识每次执行指令要进入相应的目录中,麻烦,可以通过环境变量简化操作。1.8.1 环境变量配置我的电脑右键——属性——高级将mysql指令目录地址添加到环境变量的Path值中这时候就可以在任意目录下使用mysql指令原理:1、输入指令后,首先在当前目录下查找,如果当前目录下找不到,就到环境变量的Path中查找2、Path中有很多目录,从前往后查找1.8.2 校对集1、概念:在某种字符集下,字符之间的比较关系,比如a和B的大小关系,如果区分大小写a>B,如果不区分大小写则a<B。比如赵钱孙李大小关系,不同的标准关系不一样2、校对集依赖与字符集,不同的字符集的的比较规则不一样,如果字符集更改,校对集也重新定义。3、不同的校对集对同一字符序列比较的结果是不一致的。4、 可以在定义字符集的同时定义校对集、 语法: collate = 校对集例题:定义两个表,相同字符集不同校对集mysql> create table stu1( -> name char(1) -> )charset=utf8 collate=utf8_general_ci; Query OK, 0 rows affected (0.05 sec) mysql> create table stu2( -> name char(1) -> )charset=utf8 collate=utf8_bin; Query OK, 0 rows affected (0.05 sec) mysql> insert into stu1 values ('a'),('B'); Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> insert into stu2 values ('a'),('B'); Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0测试:两个表的数据都是有小到大排序mysql> select * from stu1 order by name; -- 不区分大小写 +------+ | name | +------+ | a | | B | +------+ 2 rows in set (0.08 sec) mysql> select * from stu2 order by name; -- 区分大小写 +------+ | name | +------+ | B | | a | +------+ 2 rows in set (0.00 sec) 小结校对集规则:_bin:按二进制编码比较,区别大小写_ci:不区分大小写
2022年10月12日
86 阅读
0 评论
0 点赞
2022-10-12
PHP Smarty模板技术
1.1 目标掌握Smarty模板技术的实际运用;掌握Smarty模板技术的基础配置;掌握常用的smarty内置函数应用;掌握模板变量的使用以及常见的保留变量的使用;了解smarty配置文件的使用规范;掌握Smarty中内置函数:if分支、foreach和section循环的使用掌握Smarty在类中引入实现子类便捷使用的方式1.4 Smarty简介1.4.1 Smarty的引入1、为了分工合作,模板页面中最好不要出现PHP的代码。2、需要将表现和内容相分离1.2.2 Smarty介绍1.5 自定义Smarty1.3.1 演化一:(smarty生成混编文件)在模板中不能出现PHP定界符,标准写法如下:1、html代码<body> {$title} </body>2、PHP代码<?php $title='锄禾'; require './1-demo.html';运行结果不能解析的原因是:PHP不能识别 { 和 }解决:将大括号替换成PHP的定界符代码实现<?php $title='锄禾'; $str=file_get_contents('./index.html'); $str=str_replace('{','<?php echo ',$str); //替换左大括号 $str=str_replace('}',';?>',$str); //替换右大括号 file_put_contents('./index.html.php', $str); //写入混编文件 require './index.html.php'; //包含混编文件运行1.3.2 演化二:(smarty封装)由于每个页面都要替换定界符,所以需要将替换定界符的代码封装起来由于封装在类中,所有访问的方法需要通过面向对象的方式来访问1、创建Smarty.class.php<?php class Smarty{ private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用:编译模板 *@param $tpl string 模板的路径 */ public function compile($tpl){ $com_file=$tpl.'.php'; //混编文件地址 $str=file_get_contents($tpl); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } }2、在index.php<?php require './Smarty.class.php'; $smarty=new Smarty(); $smarty->assign('title','锄禾'); $smarty->compile('./index.html');小结:1、需要将外部的变量赋值到对象的内部2、要通过面向对象的方式访问1.3.3 演化三:(有条件的生成混编文件)混编文件存在并且是最新的就直接包含,否则就重新生成模板文件修改时间<混编文件修改时间 => 混编文件是最新的Smarty类中的代码编译代码如下<?php class Smarty{ private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用:编译模板 *@param $tpl string 模板的路径 */ public function compile($tpl){ $com_file=$tpl.'.php'; //混编文件地址 //文件存在,并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl)<filemtime($com_file)) require $com_file; else{ $str=file_get_contents($tpl); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } } }小结:生成混编文件的条件1、混编不存在2、模板修改了, 模板文件修改时间>混编文件修改时间,说明模板修改过了。1.3.4 演化四:文件分类存放模板文件:view混编文件:viewcSmarty文件:smarty.class.phpSmarty.class.php代码如下:<?php class Smarty{ public $template_dir='./templates/'; //默认模板目录 public $templatec_dir='./templates_c/'; //默认混编目录 private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用:编译模板 *@param $tpl string 模板的名字 */ public function compile($tpl){ $tpl_file=$this->template_dir.$tpl; //拼接模板地址 $com_file=$this->templatec_dir.$tpl.'.php'; //混编文件地址 //文件存在,并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl_file)<filemtime($com_file)) require $com_file; else{ $str=file_get_contents($tpl_file); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } } }index.php代码如下<?php require './Smarty/Smarty.class.php'; $smarty=new Smarty(); $smarty->template_dir='./view/'; //更改模板目录 $smarty->templatec_dir='./viewc/'; //更改混编目录 $smarty->assign('title','锄禾'); $smarty->compile('index.html');1.3.5 演化五:封装编译方法编译的方法是smarty的核心方法,核心方法一般是不可以直接调用,需要进行二次封装smarty.class.php<?php class Smarty{ ... public function display($tpl){ require $this->compile($tpl); } /* *作用:编译模板 *@param $tpl string 模板的名字 */ private function compile($tpl){ .. //文件存在,并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl_file)<filemtime($com_file)) return $com_file; //返回混编地址 else{ $str=file_get_contents($tpl_file); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 return $com_file; //返回混编地址 } } }index.php<?php ... $smarty->assign('title','锄禾'); $smarty->display('index.html'); //传递文件名1.6 官方Smarty介绍1.6.1 smarty目录结构到www.smarty.net网站下载最新的smarty版本解压libs目录结构需要掌握的smarty的属性和方法public $left_delimiter = "{"; //左界定 public $right_delimiter = "}"; //右界定 protected $template_dir = array('./templates/'); //默认模板目录 protected $compile_dir = './templates_c/'; //默认混编目录 protected $config_dir = array('./configs/'); //默认配置目录 protected $cache_dir = './cache/'; //默认缓存目录 public function setTemplateDir(){} //设置模板文件夹 public function setConfigDir(){} //设置配置文件夹 public function setCompileDir(){} //设置混编文件夹 public function setCacheDir(){} //设置缓存文件夹练习:以下关于Smarty配置描述正确的是(ABCD) A: 使用left_delimiter属性可以修改Smarty左定界符; B: 使用right_delimiter属性可以修改Smarty右定界符; C: 使用setTemplateDir()方法可以重新指定默认模板工作目录; D: 使用setCompileDir()方法可以重新指定默认编译文件工作目录。 1.6.2 smarty简单的操作1、将libs目录拷贝到站点下,改名为smarty2、创建模板目录templates3、创建混编目录templates_c4、在站点下创建1-demo.php<?php require './Smarty/Smarty.class.php'; $smarty=new Smarty(); $smarty->assign('title','锄禾'); $smarty->left_delimiter='{{'; //更改左界定 $smarty->right_delimiter='}}'; //更改右界定 $smarty->setTemplateDir('./view/'); //设置模板目录 $smarty->setCompileDir('./viewc/'); //设置混编目录 $smarty->display('1-demo.html');在templates下创建demo1.html<body> {{$title}} </body>1.6.3 注释语法:{ }注意:smarty注释在源码中看不见。思考:已知smarty的定界符是{ 和 },那么它的注释是什么?答:{ }1.2 变量smarty中变量有3中,普通变量、配置变量、保留变量1、普通变量普通变量就是我们自己定义的变量方法一:在PHP中定义$smarty->assign('name','tom');方法二:可以在模板定义语法:{assign var='变量名' value='值'} 例如:{assign var='sex' value='男'}简化写法:{$sex='男'}例题:php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('name','tom'); //给变量赋值 $smarty->display('1-demo.html');HTML代码<body> 姓名:{$name} <br> {assign var='age' value=20} 年龄:{$age}<br> {$add='北京'} 地址:{$add} </body>运行结果2、保留变量Smarty中有一个特殊的保留变量(内置变量),类似于PHP中的所有的超全局变量、常量、时间等信息表达式描述{$smarty.get.name}获取get提交的name的值{$smarty.post.name}获取post提交的name的值{$smarty.request.name}获取get和post提交的name的值{$smarty.cookies.name}获取cookie中的name的值{$smarty.session.name}获取session中的name的值{$smarty.const.name}获取常量name{$smarty.server.DOCUMENT_ROOT}获取服务器的虚拟目录地址{$smarty.config.name}获取配置文件中的值{$smarty.now}时间戳{$smarty.ldelim}获取左界定{$smarty.rdelim}获取右界定例题PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); define('name', '常量name'); setcookie('name','cookie的值'); $_SESSION['name']='session的值'; $smarty->display('1-demo.html');HTML代码<body> get提交:{$smarty.get.name}<br> post提交:{$smarty.post.name}<br> request提交:{$smarty.request.name}<br> 常量:{$smarty.const.name}<br> cookie的值:{$smarty.cookies.name}<br> session:{$smarty.session.name}<br> 时间戳:{$smarty.now}<br> 版本号:{$smarty.version}<br> 根目录:{$smarty.server.DOCUMENT_ROOT}<br> 左界定:{$smarty.ldelim}<br> 右界定:{$smarty.rdelim} </body>运行结果3、配置变量从配置文件中获取变量值,配置文件默认的文件夹是configs1、在站点下创建配置文件夹configs2、在configs目录下创建smarty.conf文件color='#FF0000'; size='15px';3、PHP页面<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('1-demo.html');4、HTML页面{config_load file='smarty.conf'} <!--引入配置文件--> <style> body{ color:{#color#}; font-size: {$smarty.config.size} } </style>小结:1、要使用配置文件中的值,首先必须引入配置文件,通过{config_load}标签引入2、获取配置文件中的值的方法有两种 第一:{#变量名#} 第二:{$smarty.config.变量名}多学一招:配置文件中的节在配置文件中,‘[ ]’表示配置文件的段落例题:配置文件color=#FF0000 size=30px [spring] # 配置文件中的段落 color=#009900; size=20px; [winter] color=#000000; size=5px;注意:1、全局的一定要写在节的前面2、配置文件中[]表示节3、配置文件中的注释是 #HTML页面{config_load file='smarty.conf' section='winter'} -- 通过section引入配置文件中的段落 <style> body{ color:{#color#}; font-size: {$smarty.config.size} } </style>1.3 运算符smary中的运算符是PHP是一样的。除此以外,smarty还支持如下的运算符。运算符描述eqequal 相等neqnot equal 不等于gtgreater than 大于ltless than 小于lteless than or equal 小于等于gtegreat than or equal 大于等于is even是偶数is odd是奇数is not even不是偶数is not odd不是奇数not非mod求模取余div by被整除is [not] div by能否被某数整除,例如:{if $smarty.get.age is div by 3}...{/if}is [not] even by商的结果是否为偶数is [not] odd by商的结果是否为奇数1.4 判断语法:{if 条件} {elseif 条件} {else} {/if}例题:php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('1-demo.html');html代码<body> {if is_numeric($smarty.get.score)} {#判断是否是数字#} {if $smarty.get.score gte 90} A {elseif $smarty.get.score gte 80} B {else} C {/if} {else} 不是数字 {/if} <hr> {if $smarty.get.score is even} 是偶数 {elseif $smarty.get.score is odd} 是奇数 {/if} </body>运行结果小结:在判断中是可以使用PHP的函数的1.5 数组smarty中访问数组的方式有两种数组[下标] 数组.下标PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $stu=array('tom','berry'); //索引数组 $emp=array('name'=>'rose','sex'=>'女'); //关联数组 $goods=array( array('name'=>'手机','price'=>22), array('name'=>'钢笔','price'=>10) ); $smarty->assign('stu',$stu); $smarty->assign('emp',$emp); $smarty->assign('goods',$goods); $smarty->display('2-demo.html');HTML代码<body> 学生:{$stu[0]}-{$stu.1} <br> 雇员:{$emp['name']}-{$emp.sex}<br> 商品: <ul> <li>{$goods[0]['name']}</li> <li>{$goods[0].price}</li> <li>{$goods.1['name']}</li> <li>{$goods.1.price}</li> </ul> </body>运行结果1.6 循环smarty中支持的循环有:{for}、{while}、{foreach}、{section}。对于开发来说用的最多就是{foreach}循环1.6.1 for语法:{for 初始值 to 结束值 [step 步长]} {/for} 默认步长是1例题<body> {for $i=1 to 5} {$i}:锄禾日当午<br> {/for} <hr> {for $i=1 to 5 step 2} {$i}:锄禾日当午<br> {/for} </body>运行结果1.6.2 while语法{while 条件} {/while}例题(输出5句):<body> {$i=1} {while $i<=5} {$i++}:锄禾日当午<br> {/while} </body>1.6.3 foreach既能遍历关联数组也能遍历索引数组语法:{foreach 数组 as $k=>$v} {foreachelse} 没有数组输出 {/foreach}foreach的属性@index:从0开始的索引 @iteration:从1开始的编号 @first:是否是第一个元素 @last:是否是最后一个元素PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('stu',array('first'=>'tom','second'=>'berry','third'=>'ketty','forth'=>'rose')); $smarty->display('3-demo.html');html代码<table border='1' bordercolor='#000' width='780'> <tr> <th>是否是第一个元素</th> <th>索引</th> <th>编号</th> <th>键</th> <th>值</th> <th>是否是最后一个元素</th> </tr> {foreach $stu as $k=>$v} <tr> <td>{$v@first}</td> <td>{$v@index}</td> <td>{$v@iteration}</td> <td>{$k}</td> <td>{$v}</td> <td>{$v@last}</td> </tr> {foreachelse} 没有输出 {/foreach} </table>运行结果1.6.4 sectionsection不支持关联数组,只能遍历索引数组语法:{section name=自定义名字 loop=数组} {/section}例题:php<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('stu',array('tom','berry')); $smarty->display('4-demo.html');html代码<table border='1' bordercolor='#000' width='780'> <tr> <th>是否是第一个元素</th> <th>索引</th> <th>编号</th> <th>值</th> <th>是否是最后一个元素</th> </tr> {section name=s loop=$stu} <tr> <td>{$smarty.section.s.first}</td> <td>{$smarty.section.s.index}</td> <td>{$smarty.section.s.iteration}</td> <td>{$stu[s]}</td> <td>{$smarty.section.s.last}</td> </tr> {sectionelse} 没有输出 {/section} </table>1.7 函数函数有两种,自定义函数和内置函数smarty的内置函数就是封装的PHP的关键字1.8 变量修饰器1.8.1 变量修饰器变量修饰器的本质就是PHP函数,用来转换数据php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('5-demo.html');html代码<body> 转成大写:{'abc'|upper} <br> 转成小写:{'ABC'|lower} <br> 默认值:{$add|default:'地址不详'}<br> 去除标签:{'<b>你好吗</b>'|strip_tags}<br> 实体转换:{'<b>你好吗</b>'|escape}<br> 日期:{$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'} 多个管道连续使用:{'<b>boy</b>'|strip_tags|upper}<br> </body>运行结果注意:1、将PHP的关键字或函数封装成标签称为函数,将PHP关键字封装成smarty关键字称为修饰器。内部的本质都是、PHP函数或PHP关键字。2、|称为管道运算符,将前面的参数传递后后面的修饰器使用1.8.2 自定义变量修饰器变量修饰器存放在plugins目录中规则:文件的命名规则:modifier.变量修饰器名称.php文件内方法命名规则:smarty_modifier_变量修饰器名称(形参...){}例题1、在plugins目录中创建modifier.cal.php页面<?php function smarty_modifier_cal($num1,$num2,$num3){ return $num1+$num2+$num3; }2、在模板中调用{10|cal:20:30} 10作为第一个参数传递 参数之间用冒号分隔1.9 避免Smarty解析smarty的定界符和css、js中的大括号产生冲突的时候,css、js中的大括号不要被smarty解析方法一:更换定界符方法二:左大括号后面添加空白字符方法三:{literal} {/literal}smarty不解析{literal} {/literal}中的内容<style> {literal} body{color: #FF0000;} {/literal} </style>1.10 缓存缓存:页面缓存、空间缓存、数据缓存。smarty中的缓存就是页面缓存smarty的缓存是页面缓存。1.10. 1 开启缓存$smarty->caching=true|1; //开启缓存1.10.2 缓存的更新方法一:删除缓存,系统会重新生成新的缓存文件方法二:更新了模板文件,配置文件,缓存自动更新方法三:过了缓存的生命周期,默认是3600秒方法四:强制更新PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=true; //开启缓存 if(date('H')>=9) $smarty->force_cache=true; //强制更新缓存 $smarty->display('6-demo.html');1.10.3 缓存的生命周期$smarty->cache_lifetime=-1 | 0 | N -1:永远不过期 0:立即过期 N:有效期是N秒,默认是3600秒PHP代码$smarty->cache_lifetime=3; //缓存的生命周期1.10.4 局部不缓存局部不缓存有两种方法1、变量不缓存 {$变量名 nocache} 2、整个块不缓存 {nocache} {/nocache}代码不缓存:{$smarty.now nocache} <br> 不缓存:{nocache} {$smarty.now}<br> {/nocache}1.10.5 缓存分页通过$smarty->display(模板,识别id)。通过识别id来缓存分页、集合PHP页面<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=1; $smarty->display('7-demo.html',$_GET['pageno']);html页面<body> 这是第{$smarty.get.pageno}页 </body>运行结果1.10.6 缓存集合每个组合都会产生缓存PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=1; $color=$_GET['color']; $size=$_GET['size']; $smarty->display('7-demo.html',"$color|$size");HTML代码<body> 颜色:{$smarty.get.color}<br> 大小:{$smarty.get.size} </body>运行结果1.10.7 清除缓存$smarty->clearCache(模板,[识别id]) $smarty->clearAllCache(); //清除所有缓存代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); //$smarty->clearCache('7-demo.html',1); //$smarty->clearCache('7-demo.html','red|10'); //$smarty->clearCache('7-demo.html'); $smarty->clearAllCache(); //清除所有缓存1.11 将smarty集成到项目中1、将smarty拷贝到Lib目录下2、实现smarty类的自动加载 private static function initAutoLoad(){ spl_autoload_register(function($class_name){ //Smarty类存储不规则,所以将类名和地址做一个映射 $map=array( 'Smarty' => LIB_PATH.'Smarty'.DS.'Smarty.class.php' ); ... elseif(isset($map[$class_name])) $path=$map[$class_name]; else //控制器 $path=__URL__.$class_name.'.class.php'; if(file_exists($path) && is_file($path)) require $path; }); }3、创建混编目录,并且定义混编目录地址private static function initRoutes(){ ... define('__VIEW__',VIEW_PATH.$p.DS); //当前视图的目录地址 define('__VIEWC__', APP_PATH.'Viewc'.DS.$p.DS); //混编目录 }4、由于前后台都要启动模板,所以应该在基础控制器中实例化smarty<?php //基础控制器 namespace Core; class Controller{ protected $smarty; use \Traits\Jump; public function __construct() { $this->initSession(); $this->initSmarty(); } //初始化session private function initSession(){ new \Lib\Session(); } //初始化Smarty private function initSmarty(){ $this->smarty=new \Smarty(); $this->smarty->setTemplateDir(__VIEW__); //设置模板目录 $this->smarty->setCompileDir(__VIEWC__); //设置混编目录 } }5、在控制器中使用smartyclass ProductsController extends BaseController{ //获取商品列表 public function listAction() { //实例化模型 $model=new \Model\ProductsModel(); $list=$model->select(); //加载视图 //require __VIEW__.'products_list.html'; $this->smarty->assign('list',$list); $this->smarty->display('products_list.html'); }6、在模板中更改如下:{foreach $list as $rows} <tr> <td>{$rows['proID']}</td> <td>{$rows['proname']}</td> <td>{$rows['proprice']}</td> <td><a href="index.php?p=Admin&c=Products&a=edit&proid={$rows['proID']}">修改</a></td> <td><a href="javascript:void(0)" onclick="if(confirm('确定要删除吗')){ location.href='index.php?p=Admin&c=Products&a=del&proid={$rows['proID']}'}">删除</a></td> </tr> {/foreach}
2022年10月12日
81 阅读
0 评论
1 点赞
2022-10-12
PHP 类封装文件上传
1.1 目标更好的理解类的封装特性;理解代码根据业务和功能的分类管理思想;理解公共控制器的作用,掌握公共控制器的封装;掌握PHP面向对象中继承的核心应用;1.2 文件上传1.2.1 封装文件上传类1、在Lib目录下创建Upload.class.php<?php namespace Lib; class Upload{ private $path; //上传的路径 private $size; //上传的大小 private $type; //允许上传的类型 private $error; //保存错误信息 public function __construct($path,$size,$type) { $this->path=$path; $this->size=$size; $this->type=$type; } //返回错误信息 public function getError(){ return $this->error; } /* * 文件上传 * @param $files array $_FILES[] * @return bool|string 成功返回文件路径,失败返回false */ public function uploadOne($files){ if($this->checkError($files)){ //没有错误就上传 $foldername=date('Y-m-d'); //文件夹名称 $folderpath= $this->path.$foldername; //文件夹路径 if(!is_dir($folderpath)) mkdir($folderpath); $filename=uniqid('',true).strrchr($files['name'],'.');//文件名 $filepath="$folderpath/$filename"; //文件路径 if(move_uploaded_file($files['tmp_name'],$filepath)) return "{$foldername}/{$filename}"; else{ $this->error='上传失败<br>'; return false; } } return false; } //验证上传是否有误 private function checkError($files){ //1、验证错误号 if($files['error']!=0){ switch($files['error']) { case 1: $this->error='文件大小超过了php.ini中允许的最大值,最大值是:'.ini_get('upload_max_filesize'); return false; case 2: $this->error='文件大小超过了表单允许的最大值'; return false; case 3: $this->error='只有部分文件上传'; return false; case 4: $this->error='没有文件上传'; return false; case 6: $this->error='找不到临时文件'; return false; case 7: $this->error='文件写入失败'; return false; default: $this->error= '未知错误'; return false; } } //2、验证格式 $info=finfo_open(FILEINFO_MIME_TYPE); $mime=finfo_file($info,$files['tmp_name']); if(!in_array($mime, $this->type)){ $this->error='只能上传'.implode(',', $this->type).'格式'; return false; } //3、验证大小 if($files['size']> $this->size){ $this->error='文件大小不能超过'.number_format($this->size/1024,1).'K'; return false; } //4、验证是否是http上传 if(!is_uploaded_file($files['tmp_name'])){ $this->error='文件不是HTTP POST上传的<br>'; return false; } return true; } }1.2.2 封装缩略图类在Lib目录下创建Image.class.php<?php namespace Lib; class Image{ /* * 制作缩略图 * @param $src_path 源图的路径 */ public function thumb($src_path,$prefix='small_',$w=200,$h=200){ $dst_img=imagecreatetruecolor($w,$h); //目标图 $src_img=imagecreatefromjpeg($src_path); //源图 $src_w=imagesx($src_img); $src_h=imagesy($src_img); imagecopyresampled($dst_img,$src_img,0,0,0,0,$w,$h,$src_w,$src_h); $filename=basename($src_path); //文件名 $foldername=substr(dirname($src_path),-10); //目录名 $save_path= dirname($src_path).'/'.$prefix.$filename; imagejpeg($dst_img,$save_path); return "{$foldername}/{$prefix}{$filename}"; } }1.2.3 实现文件上传1、register.html <form action="" method="post" enctype="multipart/form-data"> ...2、更改注册控制器public function registerAction(){ //第二步:执行注册逻辑 if(!empty($_POST)){ //文件上传 $path=$GLOBALS['config']['app']['path']; $size=$GLOBALS['config']['app']['size']; $type=$GLOBALS['config']['app']['type']; $upload=new \Lib\Upload($path, $size, $type); if($filepath=$upload->uploadOne($_FILES['face'])){ //生成缩略图 $image=new \Lib\Image(); $data['user_face']=$image->thumb($path.$filepath,'s1_'); }else{ $this->error('index.php?p=Admin&c=Login&a=register', $upload->getError()); } //文件上传结束 ...3、配置文件 'app' =>array( 'path' => './Public/Uploads/', 'size' => 1234567, 'type' => ['image/png','image/jpeg','image/gif'],1.3 登录模块1.3.1 记住密码登录成功后,如果需要记录用户名和密码,则将用户名和密码记录在cookie中打开登录页面的时候,获取cookie的值在视图页面(login.html)页面显示cookie的信息<input type="text" class="input" name="username" placeholder="登录账号" value="<?=$name?>" /> ... <input type="password" class="input" name="password" placeholder="登录密码" value="<?=$pwd?>" />运行结果1.3.2 安全退出退出:退出的时候不销毁令牌安全退出:退出的时候销毁了令牌top.html<a class="button button-little bg-yellow" href="index.php?p=Admin&c=Login&a=logout" target="_top">安全退出</a> _top:表示在最顶端的窗口中打开控制器(LoginController)public function logoutAction(){ session_destroy(); header('location:index.php?p=Admin&c=Login&a=login'); }
2022年10月12日
96 阅读
0 评论
0 点赞
2022-10-12
PHP GD库
1.1 目标理解PHP扩展的加载;了解GD库能做什么;掌握GD库实现验证码功能;掌握GD库实现缩略图功能;掌握GD库实现水印图功能;掌握验证码的验证原理;1.2 开启GD扩展GD库是用来处理图片的。使用GD库,首先在php.ini中开启gd扩展extension=php_gd2.dll开启以后就可以使用image开头的函数了。1.3 创建最简单的图片步骤1、创建画布2、给画布填充颜色(给画布分配的第一个颜色自动填充成背景色)3、显示图片<?php $img=imagecreate(200,100); //创建图片 //var_dump($img); //resource(2) of type (gd) imagecolorallocate($img,255,0,0); //给图片分配第一个颜色,默认是背景色 //操作一:显示图片 /* //告知浏览器用jpg格式显示 header('content-type:image/jpeg'); //显示图片 imagejpeg($img); //用jpg格式显示图片 */ //操作二:保存图片(不需要设置header头) imagejpeg($img,'./tu.jpg');多学一招imagepng():将图片输出为png格式 imagegif():将图片输出为gif格式小结:1、第一个分配的颜色是背景色2、要在浏览器显示画布,需要设置header()头3、保存画布,不需要设置header()头1.4 填充颜色给图片分配的第一个颜色自动填充成背景色,如果要更换背景色需要手动的填充颜色。<?php $img=imagecreate(200,100); //创建图片资源 $color=imagecolorallocate($img,200,200,200); //更改背景色 switch(rand(1,100)%3) { case 0: $color=imagecolorallocate($img,255,0,0); //颜色的索引编号 break; case 1: $color=imagecolorallocate($img,0,255,0); break; default: $color=imagecolorallocate($img,0,0,255); } //填充颜色 imagefill($img,0,0,$color); //显示图片 header('content-type:image/png'); imagepng($img);1.5 验证码1.5.1 验证码的作用防止暴力破解1.5.2 原理创建一个图片,在图片上写上一串随机字符串实现步骤:第一步:生成随机字符串第二步:创建画布第三步:将字符串写到画布上imagestring(图片资源,内置字体,起始点x,起始点y,字符串,颜色编号)难点:字符串居中1.5.3 代码实现<?php //第一步:创建随机字符串 //1.1 创建字符数组 $all_array=array_merge(range('a','z'),range('A','Z'),range(0,9)); //所有字符数组 $div_array=['1','l','0','o','O','I']; //去除容易混淆的字符 $array=array_diff($all_array,$div_array); //剩余的字符数组 unset($all_array,$div_array); //销毁不需要使用的数组 //1.2 随机获取4个字符 $index=array_rand($array,4); //随机取4个字符,返回字符下标,按先后顺序排列 shuffle($index); //打乱字符 //1.3 通过下标拼接字符串 $code=''; foreach($index as $i){ $code.=$array[$i]; } //第二步:创建画布 $img=imagecreate(150,30); imagecolorallocate($img,255,0,0); //分配背景色 $color=imagecolorallocate($img,255,255,255); //分配前景色 //第三步:将字符串写到画布上 $font=5; //内置5号字体 $x=(imagesx($img)-imagefontwidth($font)*strlen($code))/2; $y=(imagesy($img)-imagefontheight($font))/2; imagestring($img,$font,$x,$y,$code,$color); //显示验证码 header('content-type:image/gif'); imagegif($img);小结range():生成指定范围的数组array_merge():合并数组array_diff():计算数组的差集array_rand():随机获取数组元素shuffle():打乱数组去除容易混淆的字符数组要打乱起始点x=(图片宽度-字符串宽度)/2 字符串宽度=字符的宽度*字符的个数起始点y=(图片高度-字符高度)/2运行结果1.6 打开图片创建验证码步骤:1、生成随机字符串2、打开图片3、将字符串写到图片上代码实现<?php //第一步:生成随机字符串 $codeSet='2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY'; $code=''; $max=strlen($codeSet); for($i=1;$i<=4;$i++){ $index=rand(0,$max-1); $code.=$codeSet[$index]; } //第二步:打开图片 $path='./captcha/captcha_bg'.rand(1,5).'.jpg'; $img=imagecreatefromjpeg($path); //第三步:将字符串写到图片上 $font=5; //内置5号字体 $x=(imagesx($img)-imagefontwidth($font)*strlen($code))/2; $y=(imagesy($img)-imagefontheight($font))/2; //随机前景色 $color=imagecolorallocate($img,255,255,255); //设置背景色 if(rand(1,100)%2) $color=imagecolorallocate($img,255,0,0); //设置背景色 imagestring($img,$font,$x,$y,$code,$color); //显示验证码 header('content-type:image/gif'); imagegif($img);运行结果多学一招:captcha1.7 中文验证码1.7.1 步骤与思考思考1、中文验证码需要引入字体文件,内置字体不支持中文2、使用imagettftext(图片资源,字号大小,角度,起始x坐标,起始y坐标,颜色,字体文件地址,字符串)写入中文3、字体保存在C:\Windows\Fonts目录下4、用imagettfbbox()测定中文字符串的宽高,此函数返回8个值,4个角的坐标步骤1、生成随机字符串2、创建画布3、将字符串写到画布上1.7.2 代码实现将黑体拷贝到站点的ttf目录下代码实现<?php //第一步:生成随机字符串 $codeSet='们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来'; $max=mb_strlen($codeSet)-1; //中文字符的最大索引号 $code=''; for($i=0; $i<4; $i++) { $start=rand(0,$max); $code.=mb_substr($codeSet,$start,1); } //第二步:创建画布 $img=imagecreate(150,40); imagecolorallocate($img,255,0,0); //第三步:将字符串写到画布上 //3.1 指定字符串的参数 $color=imagecolorallocate($img,255,255,255); $size=15; //字号 $angle=0; //角度 $fontfile='./ttf/simhei.ttf'; //字体路径 //3.2 测定字符串的范围 $info=imagettfbbox($size,$angle,$fontfile,$code); $code_w=$info[4]-$info[6]; //字符串的宽度 $code_h=$info[1]-$info[7]; //字符串的高度 $x=(imagesx($img)-$code_w)/2; //起始点的$x $y=(imagesy($img)+$code_h)/2; //起始点的$y //3.3 将中文字符串写到画布上 imagettftext($img,$size,$angle,$x,$y,$color,$fontfile,$code); //将文字写到画布上 //显示验证码 header('content-type:image/jpeg'); imagejpeg($img);小结1、中文处理需要使用多字节处理2、使用多字节处理函数需要开启相应的扩展extension=php_mbstring.dll3、使用imagettfbbox 测定中文字符串的范围4、使用imagettftext将中文写到画布上1.8 水印1.8.1 文字水印在图片上添加文字或图片,目的:宣传,防止盗图水印有文字水印和图片水印.文字水印实现原理和中文验证码是一样的步骤1、打开图片2、将文字写到图片上3、输出图片(另存图片)实现<?php //第一步:打开图片 $img=imagecreatefromjpeg('./face.jpg'); //第二步:将文字写到图片上 $color=imagecolorallocate($img,255,0,0); $size=35; //字号 $angle=0; //角度 $fontfile='./ttf/simhei.ttf'; //字体路径 $code='传智播客黑马程序员'; $info=imagettfbbox($size,$angle,$fontfile,$code); $code_w=$info[4]-$info[6]; //字符串的宽度 $code_h=$info[1]-$info[7]; //字符串的高度 $x=imagesx($img)-$code_w; //起始点的$x $y=imagesy($img)-$code_h; //起始点的$y //将中文字符串写到画布上 imagettftext($img,$size,$angle,$x,$y,$color,$fontfile,$code); //将文字写到画布上 //第三步:保存图片 imagejpeg($img,'./face.jpg');1.8.2 图片水印原理:将水印图片拷贝复制到目标图片上。步骤:1、打开源图2、打开目标图3、复制源图拷贝到目标图上实现<?php //第一步:打开源图 $src_img=imagecreatefromjpeg('./water.jpg'); //第二步:打开目标图 $dst_img=imagecreatefromjpeg('./face.jpg'); //第三步:将源图复制到目标图上 $dst_x=imagesx($dst_img)-imagesx($src_img); //开始粘贴的x $dst_y=imagesy($dst_img)-imagesy($src_img); //开始粘贴的y $src_w=imagesx($src_img); $src_h=imagesy($src_img); imagecopy($dst_img,$src_img,$dst_x,$dst_y,0,0,$src_w,$src_h); //显示水印图 header('content-type:image/jpeg'); imagejpeg($dst_img);运行结果1.9 缩略图上传图片后,将图片变成统一的大小的缩略图。原理:将源图复制拷贝到目标图上,并缩放大小。步骤1、创建目标图2、打开源图3、复制源图,拷贝到目标图上代码实现<?php //第一步:创建目标图 $dst_img=imagecreatetruecolor(200,200); //第二步:打开源图 $src_img=imagecreatefromjpeg('./face.jpg'); //第三步:复制源图拷贝到目标图上,并缩放大小 $src_w=imagesx($src_img); $src_h=imagesy($src_img); imagecopyresampled($dst_img,$src_img,0,0,0,0,200,200,$src_w,$src_h); //第四步:保存缩略图 //header('content-type:image/jpeg'); imagejpeg($dst_img,'./face1.jpg');注意:imagecreate()和imagecreatetruecolor()的区别imagecreate():创建支持256种颜色的画布 imagecreatetruecolor():创建真彩色画布,支持256*256*256种颜色1.10 验证码改错验证码错误不会报具体的错误信息第一招:注释header注释掉header后,错误信息就出来了第二招:如果没有报错,就留心一下图片代码前有无字符串输出,图片前面是不允许有任何字符串输出的第三招:查看源码,图片代码前是否有空白字符第四招:如果上面的三招无效,在header()前添加ob_clean();1.11 在项目中实现验证码1.11.1 封装验证码类1、验证码封装Lib类库中2、在Lib目录下创建Captcha.class.php页面<?php namespace Lib; class Captcha{ private $width; private $height; public function __construct($width=80,$height=32) { $this->width=$width; $this->height=$height; } //生成随机字符串 private function generalCode(){ $all_array=array_merge(range('a','z'),range('A','Z'),range(0,9)); //所有字符数组 $div_array=['1','l','0','o','O','I']; //去除容易混淆的字符 $array=array_diff($all_array,$div_array); //剩余的字符数组 unset($all_array,$div_array); //销毁不需要使用的数组 $index=array_rand($array,4); //随机取4个字符,返回字符下标,按先后顺序排列 shuffle($index); //打乱字符 $code=''; foreach($index as $i) $code.=$array[$i]; $_SESSION['code']=$code; //保存到会话中 return $code; } //创建验证码 public function entry(){ $code=$this->generalCode(); $img=imagecreate($this->width, $this->height); imagecolorallocate($img,255,0,0); //分配背景色 $color=imagecolorallocate($img,255,255,255); //分配前景色 $font=5; //内置5号字体 $x=(imagesx($img)-imagefontwidth($font)*strlen($code))/2; $y=(imagesy($img)-imagefontheight($font))/2; imagestring($img,$font,$x,$y,$code,$color); //显示验证码 header('content-type:image/gif'); imagegif($img); } //验证码比较 public function check($code){ return strtoupper($code)== strtoupper($_SESSION['code']); } }1.11.2 使用验证码1、在控制器中调用验证码类(LoginController)public function verifyAction(){ $captcha=new \Lib\Captcha(); $captcha->entry(); }2、在视图页面显示验证码<img src="index.php?p=Admin&c=Login&a=verify" width="80" height="32" class="passcode" onclick='this.src="index.php?p=Admin&c=Login&a=verify&"+Math.random()' />添加随机数的原因是为了让URL地址变得唯一,防止浏览器缓存。3、校验输入的验证码public function loginAction(){ //第二步:执行登陆逻辑 if(!empty($_POST)){ //校验验证码 $captcha=new \Lib\Captcha(); if(!$captcha->check($_POST['passcode'])) $this->error ('index.php?p=Admin&c=Login&a=login', '验证码错误'); ...运行结果1.12 使用的函数imagecreate():创建画布 imagecreatetruecolor():创建支持真彩色的画布 imagecolorallocate():给画布分配颜色 imagejpeg():将图片以jpeg格式输出 imagegif():将图片以gif格式输出 imagepng():将图片以png格式输出 imagefill():填充颜色 imagesx():获取图片宽度 imagesy():获取图片高度 imagefontwidth():获取内置字体宽度 imagefontheight():获取内置字体高度 imagestring():将字符串写到图片上 imagecreatefromjpeg():打开jpg创建图片资源 imagecreatefrompng():打开png创建图片资源 imagecreatefromgif():打开gif创建图片资源 imagettfbbox():测定中文字体的范围 imagettftext():将中文字体写到图片上 imagecopy():图片拷贝 imagedestroy():销毁图片资源 imagecopyresampled():拷贝图片并缩放大小
2022年10月12日
106 阅读
0 评论
1 点赞
2022-10-12
PHP Session技术
1.1 目标理解会话技术的概念;理解会话技术产生的原因和工作原理;理解session的工作原理;理解session与cookie的关系;掌握session与cookie的区别;了解session的配置与作用;理解session入库的作用;掌握session入库的原理;能够封装session入库的类;1.2 Session(会话)1.2.1 原理1、session是服务器端的技术2、session是基于cookie技术的1.2.2 session操作1、默认情况下,会话不会自动开启,通过session_start()开启会话2、通过session_id()获取会话的编号3、通过$_SESSION操作会话4、会话可以保存除了资源以外的所有类型。5、重复开启会话会报错,一般出现在包含文件中。<?php session_start(); //开启会话 @session_start(); //重复开启会话会报错,可以通过错误抑制符来屏蔽错误 $_SESSION['name']='tom'; //保存会话 $_SESSION['age']=20; echo $_SESSION['name'],'<br>'; echo $_SESSION['age'],'<br>'; echo '会话编号:'.session_id(); //获取会话编号session_start()作用1、没有会话空间就创建一个空间 2、有会话空间就打开空间1.2.3 与会话有关的配置重要:1、session.save_path="F:\wamp\tmp\" session保存的地址 2、session.auto_start = 1 session自动开启,默认不自动开启 3、session.save_handler = files 会话以文件的形式保存 4、session.gc_maxlifetime = 1440 会话的生命周期是1440秒了解:session.name = PHPSESSID session.cookie_lifetime = 0 会话编号的过期时间 session.cookie_path = / 会话编号整站有效 session.cookie_domain = 会话编号在当前域名下有效1.2.4 销毁会话通过session_destroy()销毁会话销毁会话就是删除自己的会话文件。<?php session_start(); session_destroy(); //销毁会话1.2.5 垃圾回收1、会话文件超过了生命周期是垃圾文件。2、PHP自动进行垃圾回收3、垃圾回收的概率默认是1/1000session.gc_probability = 1 session.gc_divisor = 10001.2.6 session和cookie的区别 cookiesession保存位置客户端服务器端数据大小小(4K)大数据类型字符串除了资源以外的所有类型安全性不安全安全1.2.7 禁用cookie对session的影响session是基于cookie的,如果禁用cookie,session无法使用。解决:默认情况下,session只依赖于cookie,session的编号只能通过cookie传输可以设置为session不仅仅依赖于cookiesession.use_only_cookies = 0 // session不仅仅依赖于cookie session.use_trans_sid = 1 //允许通过其他方式传递session_id设置后,php自动添加get和post传递session_id1.3 session入库session默认情况下存储到文件中,我们可以将session存储到数据库中1.3.1 创建sess表-- 如果用了text类型就不能使用memory引擎 drop table if exists sess; create table sess( sess_id varchar(50) primary key comment '会话编号', sess_value text comment '会话值', sess_time int unsigned not null comment '会话产生时间' )engine=innodb charset=utf8 comment '会话表' -- memory引擎数据存储在内存中 drop table if exists sess; create table sess( sess_id varchar(50) primary key comment '会话编号', sess_value varchar(2000) comment '会话值', sess_time int unsigned not null comment '会话产生时间' )engine=memory charset=utf8 comment '会话表'memory引擎的注意事项1、memory引擎数据存储在内存中,速度快,但是重启服务后数据清空2、memory引擎中的字段不可以是text类型1.3.2 更改会话存储(session入库)1、知识点a)通过session_set_save_handler()更改存储 b)session_set_save_handler()必须在session_start()之前 c)有6个回调函数,open,close,read,write,destroy,gc。 4)read必须返回字符串,其他函数返回bool值 6个回调函数执行的时间: open():开启会话执行 close():关闭会话执行 read():打开会话后就执行 write():更改会话会话的值和关闭会话之前执行,如果调用了session_destroy()就不会调用write() destroy():调用session_destroy()的时候自动执行 gc():垃圾回收的时候自动执行。2、代码实现<?php //打开会话 function open() { global $link; $link=mysqli_connect('localhost','root','root','data'); mysqli_set_charset($link,'utf8'); return true; } //关闭会话 function close() { return true; } //读取会话 function read($sess_id) { global $link; $sql="select sess_value from sess where sess_id='$sess_id'"; $rs=mysqli_query($link,$sql); $rows=mysqli_fetch_row($rs); return (string)$rows[0]; } //写入会话 function write($sess_id,$sess_value) { global $link; $sql="insert into sess values ('$sess_id','$sess_value',unix_timestamp()) on duplicate key update sess_value='$sess_value',sess_time=unix_timestamp()"; return mysqli_query($link,$sql); } //销毁会话 function destroy($sess_id) { global $link; $sql="delete from sess where sess_id='$sess_id'"; return mysqli_query($link,$sql); } //垃圾回收 function gc($lifetime) { global $link; $expires=time()-$lifetime; //过期时间点 $sql="delete from sess where sess_time<$expires"; return mysqli_query($link,$sql); } //更改会话存储 session_set_save_handler('open','close','read','write','destroy','gc'); //开启会话 session_start(); //session_destroy();1.3.3 项目封装1、在Lib目录下创建Session.class.php页面<?php namespace Lib; class Session{ private $mypdo; public function __construct() { session_set_save_handler( [$this,'open'], [$this,'close'], [$this,'read'], [$this,'write'], [$this,'destroy'], [$this,'gc'] ); session_start(); } public function open() { $this->mypdo= \Core\MyPDO::getInstance($GLOBALS['config']['database']); return true; } //关闭会话 public function close() { return true; } //读取会话 public function read($sess_id) { $sql="select sess_value from sess where sess_id='$sess_id'"; return (string)$this->mypdo->fetchColumn($sql); } //写入会话 public function write($sess_id,$sess_value) { $sql="insert into sess values ('$sess_id','$sess_value',unix_timestamp()) on duplicate key update sess_value='$sess_value',sess_time=unix_timestamp()"; return $this->mypdo->exec($sql)!==false; } //销毁会话 public function destroy($sess_id) { $sql="delete from sess where sess_id='$sess_id'"; return $this->mypdo->exec($sql)!==false; } //垃圾回收 public function gc($lifetime) { $expires=time()-$lifetime; //过期时间点 $sql="delete from sess where sess_time<$expires"; return $this->mypdo->exec($sql)!==false; } }2、由于需要启动项目的时候就开启会话存储,将session入库的调用放在基础控制器中。在Core目录下创建Controller.class.php(基础控制器)<?php //基础控制器 namespace Core; class Controller{ public function __construct() { $this->initSession(); } //初始化session private function initSession(){ new \Lib\Session(); } }3、所有的控制器都继承基础控制器<?php namespace Controller\Admin; //商品模块 class ProductsController extends \Core\Controller{ ...测试结果:只要访问控制器就会启动session入库。1.4 登录模块1.4.1 创建用户表drop table if exists `user`; create table `user`( user_id smallint unsigned auto_increment primary key comment '主键', user_name varchar(20) not null comment '用户名', user_pwd char(32) not null comment '密码', user_face varchar(50) comment '头像', user_login_ip int comment '最后登录的IP', user_login_time int unsigned comment '最后登录的时间', user_login_count smallint unsigned default 0 comment '登录次数', is_admin tinyint default 0 comment '超级管理员' )engine=innodb charset=utf8 comment '用户表';1.4.2 显示界面1、将HTML模板页面拷贝到View\Admin目录下2、将images、css 拷贝到Public\Admin目录下显示登录、注册界面1、在Controller\Admin目录下创建LoginController.class.php<?php namespace Controller\Admin; use Core\Controller; //引入基础控制器 class LoginController extends Controller{ //登录 public function loginAction(){ require __VIEW__.'login.html'; } //注册 public function registerAction(){ require __VIEW__.'register.html'; } }2、更改login.html、register.html页面中的静态资源路径 <link rel="stylesheet" href="/Public/Admin/css/pintuer.css"> <link rel="stylesheet" href="/Public/Admin/css/admin.css">注意:在HTML中路径要使用绝对路径,从根目录开始匹配 在CSS页面图片的路径要使用相对路径,相对于当前页面本身。3、将login.html、register.html页面联通起来-- login.html跳转到register.html <input type="button" value="用户注册" class="button button-block bg-main text-big" onClick="location.href='index.php?p=Admin&c=Login&a=register'" /> -- register.html跳转到login.html <input type="button" class="button button-block bg-main text-big" value="返回" onClick="location.href='index.php?p=Admin&c=Login&a=login'" />显示后台管理界面1、在Controller\Admin目录下创建AdminController.class.php<?php namespace Controller\Admin; class AdminController extends \Core\Controller{ public function adminAction(){ require __VIEW__.'admin.html'; } public function topAction(){ require __VIEW__.'top.html'; } public function menuAction(){ require __VIEW__.'menu.html'; } public function mainAction(){ require __VIEW__.'main.html'; } }2、在admin.html中,更改框架集中的路径<frameset rows="95,*" cols="*" frameborder="no" border="0" framespacing="0"> <frame src="index.php?p=Admin&c=Admin&a=top" name="topFrame" scrolling="no" noresize="noresize" id="topFrame" /> <frameset rows="*" cols="180,*" framespacing="0" frameborder="no" border="0"> <frame src="index.php?p=Admin&c=Admin&a=menu" name="leftFrame" scrolling="no" noresize="noresize" id="leftFrame" /> <frame src="index.php?p=Admin&c=Admin&a=main" name="mainFrame" id="mainFrame" /> </frameset> </frameset>3、更改top.html、menu.html、main.html的静态资源测试1.4.3 用户注册配置文件'app' =>array( 'key' => 'itcast', //加密秘钥控制器(LoginController)public function registerAction(){ //第二步:执行注册逻辑 if(!empty($_POST)){ $data['user_name']=$_POST['username']; $data['user_pwd']=md5(md5($_POST['password']).$GLOBALS['config']['app']['key']); $model=new \Core\Model('user'); if($model->insert($data)) $this->success ('index.php?p=Admin&c=Login&a=login', '注册成功,您可以去登陆了'); else $this->error ('index.php?p=Admin&c=Login&a=register', '注册失败,请重新注册'); } //第一步:显示注册界面 require __VIEW__.'register.html'; } 多学一招:md5的单向加密,md5解密使用的是数据字典实现。 #### 1.4.4 完善注册功能 用户名是不能重复的,但输入用户名以后,通过异步判断一下此用户名是否存在。 1、ajax代码 window.onload=function(){ var req=new XMLHttpRequest(); //创建ajax对象 document.getElementById('username').onblur=function(){ document.getElementById('msg').innerHTML=''; req.open('get','/index.php?p=admin&c=Login&a=checkUser&username='+this.value); req.onreadystatechange=function(){ if(req.readyState==4 && req.status==200){ if(req.responseText=='1'){ document.getElementById('msg').innerHTML='用户名已经存在'; } } } req.send(); } } ... 2、控制器(LoginController) public function checkUserAction(){$model=new \Model\UserModel(); echo $model->isExists($_GET['username']);} 3、模型(UserModel) <?phpnamespace Model;class UserModel extends \Core\Model{//用户存在返回1,否则返回0 public function isExists($name){ $info=$this->select(['user_name'=>$name]); return empty($info)?0:1; }}  #### 1.4.5 用户登陆 原理:通过用户名和密码找到对应的用户就是登陆成功 控制器(LoginController) namespace Controller\Admin;use Core\Controller; //引入基础控制器class LoginController extends Controller{//登录 public function loginAction(){ //第二步:执行登陆逻辑 if(!empty($_POST)){ $model=new \Model\UserModel(); if($info=$model->getUserByNameAndPwd($_POST['username'], $_POST['password'])){ $this->success('index.php?p=Admin&c=Admin&a=admin', '登陆成功'); }else{ $this->error('index.php?p=Admin&c=Login&a=login', '登陆失败,请重新登陆'); } } //第一步:显示登陆界面 require __VIEW__.'login.html'; } ... 模型(UserModel) //通过用户名和密码获取用户的信息public function getUserByNameAndPwd($name,$pwd){//条件数组 $cond=array( 'user_name' => $name, 'user_pwd' => md5(md5($pwd).$GLOBALS['config']['app']['key']) ); //通过条件数组查询用户 $info=$this->select($cond); if(!empty($info)) return $info[0]; //返回用户信息 return array();} 视图(login.html) #### 1.4.6 防止SQL注入 通过输入的字符串和SQL语句拼接成具有其他含义的语句,以达到攻击的目的  原理  防范措施: 1、给特殊字符添加转义 2、将单引号替换为空 //单引号添加转义字符echo addslashes("aa'bb'"),''; //aa\'bb\'//字符串替换echo str_replace("'",'',"aa'bb'"); //aabb 3、md5加密 4、预处理 5、如果确定传递的参数是整数,就需要进行强制类型转换。  #### 1.4.7 防止翻墙 翻墙:通过直接在地址栏输入URL地址进入模板页面 解决:用户登录成功以后,给用户一个令牌(session),在整个访问的过程中,令牌不消失。  代码实现: 1、登录成功以后,将用户信息保存到会话中 public function loginAction(){//第二步:执行登陆逻辑 if(!empty($_POST)){ $model=new \Model\UserModel(); if($info=$model->getUserByNameAndPwd($_POST['username'], $_POST['password'])){ $_SESSION['user']=$info; //将用户信息保存到会话中 $this->success('index.php?p=Admin&c=Admin&a=admin', '登陆成功'); .... 2、在Controller\Admin目录下创建后台基础控制器(BaseController) <?php//后台基础控制器namespace Controller\Admin;class BaseController extends \Core\Controller{public function __construct() { parent::__construct(); $this->checkLogin(); } //验证是否登录 private function checkLogin(){ if(CONTROLLER_NAME=='Login') //登录控制器不需要验证 return; if(empty($_SESSION['user'])){ $this->error('index.php?p=Admin&c=Login&a=login', '您没有登录'); } }} 3、所有的后台控制器都继承后台基础控制器 namespace Controller\Admin;class AdminController extends BaseController{....其他控制器也要继承BaseController #### 1.4.8 更新登陆信息 登陆成功后,更新登陆信息 代码实现: 控制器 public function loginAction(){//第二步:执行登陆逻辑 if(!empty($_POST)){ $model=new \Model\UserModel(); if($info=$model->getUserByNameAndPwd($_POST['username'], $_POST['password'])){ $_SESSION['user']=$info; //将用户信息保存到会话中 $model->updateLoginInfo(); //更新登陆信息 ... 模型(UserModel) //更新登陆信息public function updateLoginInfo(){//更新的信息 $_SESSION['user']['user_login_ip']= ip2long($_SERVER['REMOTE_ADDR']); $_SESSION['user']['user_login_time']=time(); $_SESSION['user']['user_login_count']=++$_SESSION['user']['user_login_count'] ; //实例化模型 $model=new \Core\Model('user'); //更新 return (bool)$model->update($_SESSION['user']);} 小结: 1、ip2long:IP转换成整数 2、long2ip:整数转换成IP 3、addslashes:添加转义字符 4、$_SERVER['REMOTE_ADDR']:获取客户端地址
2022年10月12日
96 阅读
0 评论
1 点赞
2022-10-12
PHP Cookie技术
1. 目标掌握cookie技术的实现方式;掌握cookie结束的应用场景;理解和控制cookie的生命周期;理解和控制cookie的作用范围;理解和控制cookie的跨域访问;1.4 Cookie思考:A页面中的变量如果提供给B页面访问方法一:包含文件方法二:get或post提交方法三:cookie,cookie就是保存在客户端的信息文件1.4.1 原理cookie是保存在客户端的信息包(一个文件)通过header()、setcookie()操作响应头语法格式:header(键:值)<?php header('content-type:charset=gbk'); header('name:tom');setcookie()作用:将值放到响应头中发送到客户端,并保存到客户端。1.4.2 设置cookie<?php setcookie('name','tom'); //将name=tom放到响应头中在响应头中可以看到cookie的信息客户端有cookei信息后,每次请求服务器,cookie的信息都会自动的放到请求头中带到服务器。1.4.3 获取cookie的值<?php echo $_COOKIE['name']; //从请求头中获取名字是name的cookie注意:1、关闭浏览器后,cookie消失。这种cookie称为临时性cookie2、cookie的信息不可以在不同的浏览器中共享,不可以跨浏览器。思考:如下代码为什么第一次执行报错,第二次执行正常<?php setcookie('name','tom'); echo $_COOKIE['name']; //在请求头中获取name的cookie因为:第一次访问请求头中没有cookie的值所以获取不到,第二次访问由于第一次已经设置了将cookie设置到响应头中,第二次访问就会自动将cookie的信息放到请求头中,所以第二次访问就能获取cookie的值了1.4.4 永久性cookie说明:关闭浏览器后cookie的值不消失应用场景:语法:给cookie添加过期时间就形成了永久性cookie,过期时间是时间类型是时间戳$time=time()+3600; setcookie('name','tom',$time); //cookie的有效时间是3600秒1.4.5 cookie的有效目录cookie默认在当前目录及子目录中有效cookie一般要设置在整站有效setcookie('name','tom',0,'/'); // /表示根目录1.4.6 支持子域名场景:每个域名代码一个网站,网站之间的cookie是不可以相互访问的。问题:百度下有多个二级域名的网站,他们自己的cookie是要共享的,如何实现?<?php setcookie('name','tom',0,'/','baidu.com'); //在baidu.com域名下都有效 ?> <a href="http://www.bb.baidu.com/bb.php">跳转</a>1.4.7 是否安全传输安全传输就是https传输。默认情况下https和http都可以传输cookiesetcookie('name','tom',0,'/','',true); // true表示只能是https传输1.4.8 是否安全访问默认情况下,PHP和JS都可以访问cookie安全访问:PHP可以访问,JS不可以 默认是false。php代码<?php setcookie('name','tom',0,'/','',false,true); ?> <a href="/5-demo2.php">跳转</a> html代码<?php echo $_COOKIE['name'],'<br>'; //PHP获取cookie ?> <script type="text/javascript"> document.write(document.cookie); //js获取cookie </script>1.4.9 删除cookie注意:cookie中只能保存数字和字符串。<?php //setcookie('name',false); //删除cookie方法一 //setcookie('name'); //删除cookie方法二 setcookie('name','tom',time()-1); //删除cookie方法三1.4.10 cookie的缺点1、因为在浏览器中可以看到cookie 的值,所以安全性低2、因为只能保存字符串和数字,所以可控性差3、因为数据放在请求头中传输,增加了请求时候的数据负载。4、因为数据存储在浏览器中,但浏览器存储空间是有吸限制的,一般是4K。
2022年10月12日
89 阅读
0 评论
0 点赞
2022-10-12
PHP MVC设计模式二
1.1 目标了解基于MVC思想的项目单一入口概念;掌握项目单一入口的实际应用;掌握框架的核心架构;理解DAO类的作用,掌握DAO的封装;理解trait的作用;理解公共模型的作用,掌握公共模型的封装;理解MVC框架设计的目录结构和各个目录的作用;理解配置文件的作用;1.2 框架目录1.2.1 创建目录结构1.2.2 文件分类存放将上一讲的文件分类存放到不同的目录中将文件存放到不同的目录以后,由于类文件地址发生了变化,所以无法完成自动加载类,那么今天的主要任务就是围绕如何实现类的自动加载展开。由于每次都请求入口文件,所以”.“表示入口文件所在的目录1.3 添加命名空间通过文件目录地址做命名空间,这样获取了命名空间就能知道文件存放的地址。Model.class.phpnamespace Core; class Model { ...MyPDO.class.phpnamespace Core; class MyPDO{ ...ProductsModel.class.php<?php namespace Model; //products模型用来操作products表 class ProductsModel extends Model{ ...ProductsController.class.php<?php namespace Controller\Admin; //商品模块 class ProductsController { ...1.4 框架类实现1.4.1 定义路径常量由于文件路径使用频率很高,而且路径比较长,所以将固定不变的路径定义成路径常量知识点1、getcwd():入口文件的绝对路径 2、windows下默认的目录分隔符是`\`,Linux下默认的目录分隔符是`/`。DIRECTORY_SEPARATOR常量根据不同的操作系统返回不同的目录分隔符。代码实现在Core文件夹下创建Framework.class.phpprivate static function initConst(){ define('DS', DIRECTORY_SEPARATOR); //定义目录分隔符 define('ROOT_PATH', getcwd().DS); //入口文件所在的目录 define('APP_PATH', ROOT_PATH.'Application'.DS); //application目录 define('CONFIG_PATH', APP_PATH.'Config'.DS); define('CONTROLLER_PATH', APP_PATH.'Controller'.DS); define('MODEL_PATH', APP_PATH.'Model'.DS); define('VIEW_PATH', APP_PATH.'View'.DS); define('FRAMEWORK_PATH', ROOT_PATH.'Framework'.DS); define('CORE_PATH', FRAMEWORK_PATH.'Core'.DS); define('LIB_PATH', FRAMEWORK_PATH.'Lib'.DS); define('TRAITS_PATH', ROOT_PATH.'Traits'.DS); }1.4.2 引入配置文件1、在config目录下创建config.php<?php return array( //数据库配置 'database'=>array(), //应用程序配置 'app' =>array( 'dp' => 'Admin', //默认平台 'dc' => 'Products', //默认控制器 'da' => 'list' //默认方法 ), );2、在框架类中引入配置文件private static function initConfig(){ $GLOBALS['config']=require CONFIG_PATH.'config.php'; }思考:配置文件为什么不保存在常量中?答:因为7.0之前,常量不能保存数组和对象。1.4.3 确定路由p:【platform】平台c:【controller】控制器a:【action】方法private static function initRoutes(){ $p=$_GET['p']??$GLOBALS['config']['app']['dp']; $c=$_GET['c']??$GLOBALS['config']['app']['dc']; $a=$_GET['a']??$GLOBALS['config']['app']['da']; $p=ucfirst(strtolower($p)); $c=ucfirst(strtolower($c)); //首字母大写 $a=strtolower($a); //转成小写 define('PLATFROM_NAME', $p); //平台名常量 define('CONTROLLER_NAME', $c); //控制器名常量 define('ACTION_NAME', $a); //方法名常量 define('__URL__', CONTROLLER_PATH.$p.DS); //当前请求控制器的目录地址 define('__VIEW__',VIEW_PATH.$p.DS); //当前视图的目录地址 }1.4.4 自动加载类private static function initAutoLoad(){ spl_autoload_register(function($class_name){ $namespace= dirname($class_name); //命名空间 $class_name= basename($class_name); //类名 if(in_array($namespace, array('Core','Lib'))) //命名空间在Core和Lib下 $path= FRAMEWORK_PATH.$namespace.DS.$class_name.'.class.php'; elseif($namespace=='Model') //文件在Model下 $path=MODEL_PATH.$class_name.'.class.php'; elseif($namespace=='Traits') //文件在Traits下 $path=TRAITS_PATH.$class_name.'.class.php'; else //控制器 $path=CONTROLLER_PATH.PLATFROM_NAME.DS.$class_name.'.class.php'; if(file_exists($path) && is_file($path)) require $path; }); }1.4.5 请求分发private static function initDispatch(){ $controller_name='\Controller\\'.PLATFROM_NAME.'\\'.CONTROLLER_NAME.'Controller'; //拼接控制器类名 $action_name=ACTION_NAME.'Action'; //拼接方法名 $obj=new $controller_name(); $obj->$action_name(); } 1.4.6 封装run()方法class Framework{ //启动框架 public static function run(){ self::initConst(); self::initConfig(); self::initRoutes(); self::initAutoLoad(); self::initDispatch(); } ...1.4.7 在入口中调用run()方法<?php require './Framework/Core/Framework.class.php'; Framework::run();run()方法调用后就启动了框架。1.5 运行项目1、连接数据库的参数从配置文件中获取class Model { ... //连接数据库 private function initMyPDO() { $this->mypdo= MyPDO::getInstance($GLOBALS['config']['database']); } }2、更改ProductsControleller控制器<?php namespace Controller\Admin; //商品模块 class ProductsController { //获取商品列表 public function listAction() { //实例化模型 $model=new \Model\ProductsModel(); $list=$model->getList(); //加载视图 require __VIEW__.'products_list.html'; } //删除商品 public function delAction() { ... $model=new \Model\ProductsModel(); ... } }3、更改ProductsModel类<?php namespace Model; class ProductsModel extends \Core\Model{ }4、更改MyPDO类... private function fetchType($type){ switch ($type){ case 'num': return \PDO::FETCH_NUM; case 'both': return \PDO::FETCH_BOTH; case 'obj': return \PDO::FETCH_OBJ; default: return \PDO::FETCH_ASSOC; } } ... //所有的内置类都在公共的命名空间下。测试:成功1.6 traits代码复用有的控制器操作完毕后要跳转,有的不需要,解决:将跳转的方法封装到traits中。代码实现1、将准备好的图片拷贝到Public目录下2、在Traits目录中创建Jump.class.php<?php //跳转的插件 namespace Traits; trait Jump{ //封装成功的跳转 public function success($url,$info='',$time=1){ $this->redirect($url, $info, $time, 'success'); } //封装失败跳转 public function error($url,$info='',$time=3){ $this->redirect($url, $info, $time, 'error'); } /* * 作用:跳转的方法 * @param $url string 跳转的地址 * @param $info string 显示信息 * @param $time int 停留时间 * @param $flag string 显示模式 success|error */ private function redirect($url,$info,$time,$flag){ if($info=='') header ("location:{$url}"); else{ echo <<<str <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <!-- <meta http-equiv="refresh" content="3;http://www.php.com"/> --> <title>Document</title> <style> body{ text-align: center; font-family: '微软雅黑'; font-size: 18px; } #success,#error{ font-size: 36px; margin: 10px auto; } #success{ color: #090; } #error{ color: #F00; } </style> </head> <body> <img src="/Public/https://img.sunshine966.com/images/{$flag}.fw.png"> <div id='{$flag}'>{$info}</div> <div><span id='t'>{$time}</span>秒以后跳转</div> </body> </html> <script> window.onload=function(){ var t={$time}; setInterval(function(){ document.getElementById('t').innerHTML=--t; if(t==0) location.href='index.php'; },1000) } </script> str; exit; } } }在ProductsController控制器中使用原型namespace Controller\Admin; //商品模块 class ProductsController{ use \Traits\Jump; //复用代码 ...1.7 删除功能入口<a href="index.php?p=Admin&c=Products&a=del&proid=<?=$rows['proID']?>" onclick="return confirm('确定要删除吗')">删除</a>控制器(ProductsController)public function delAction() { $id=(int)$_GET['proid']; //如果参数明确是整数,要强制转成整形 $model=new \Model\ProductsModel(); if($model->del($id)) $this->success('index.php?p=Admin&c=Products&a=list', '删除成功'); else $this->error('index.php?p=admin&c=Products&a=list', '删除失败'); }
2022年10月12日
88 阅读
0 评论
0 点赞
2022-10-12
PHP MVC设计模式一
1.1 目标了解MVC思想的概念;理解MVC中每个组件所处理的业务;理解MVC思想的优缺点;掌握MVC思想的设计思路;了解基于MVC思想的单一入口概念;掌握MVC的实际应用;1.2 MVC介绍1、MVC是一个编程思想,是一种设计模式2、思想:将一个功能分解成3个部分,M V CModel(模型):处理与数据有关的逻辑View(视图):显示页面Controller(控制器):处理业务逻辑小结:1、控制器用来接收请求2、以后不能直接请求模型和视图1.3 MVC演化1.3.1 显示商品1、导入products表的数据2、将上一讲的MyPDO类拷贝到站点下,改名为MyPDO.class.php,这个文件中只存放MyPDO类3、在站点下创建index.php,代码如下<?php //自动加载类 spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //连接数据库 $param=array( 'user' => 'root', 'pwd' => 'root' ); $mypdo= MyPDO::getInstance($param); //获取商品数据 $list=$mypdo->fetchAll('select * from products'); ?> <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> </head> <body> <table border='1' width='980' bordercolor='#000'> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> <?php foreach($list as $rows):?> <tr> <td><?=$rows['proID']?></td> <td><?=$rows['proname']?></td> <td><?=$rows['proprice']?></td> <td><a href="">删除</a></td> </tr> <?php endforeach;?> </table> </body> </html>运行结果1.3.2 演化一:分离视图1、创建products_list.html页面(视图页面),将显示部分的代码拷贝到视图页面上<!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> </head> <body> <table border='1' width='980' bordercolor='#000'> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> <?php foreach($list as $rows):?> <tr> <td><?=$rows['proID']?></td> <td><?=$rows['proname']?></td> <td><?=$rows['proprice']?></td> <td><a href="">删除</a></td> </tr> <?php endforeach;?> </table> </body> </html>2、在index.php页面上加载视图<?php //自动加载类 spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //连接数据库 $param=array( 'user' => 'root', 'pwd' => 'root' ); $mypdo= MyPDO::getInstance($param); //获取商品数据 $list=$mypdo->fetchAll('select * from products'); //加载视图 require './products_list.html';1.3.3 演化二:分离模型模型的规则1、一个表对应一个模型,表名和模型名一致(必须的)2、模型以Model结尾(不是必须的)代码实现:1、在站点下创建ProductsModel.class.php页面<?php //products模型用来操作products表 class ProductsModel { //获取products表的数据 public function getList() { //连接数据库 $param=array( 'user' => 'root', 'pwd' => 'root' ); $mypdo= MyPDO::getInstance($param); //获取商品数据 return $mypdo->fetchAll('select * from products'); } }2、在index.php页面中调用模型的getList()<?php //自动加载类 spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //实例化模型 $model=new ProductsModel(); $list=$model->getList(); //加载视图 require './products_list.html';1.3.4 演化三:分离基础模型连接数据库的代码每个模型都要使用,所有我们需要将连接数据库的代码封装到基础模型类中(Model)第一步:在站点下创建Model.class.php页面(基础模型)<?php //基础模型 class Model { protected $mypdo; public function __construct() { $this->initMyPDO(); } //连接数据库 private function initMyPDO() { $param=array( 'user' => 'root', 'pwd' => 'root' ); $this->mypdo= MyPDO::getInstance($param); } }第二步:ProductsModel继承基础模型类<?php //products模型用来操作products表 class ProductsModel extends Model{ //获取products表的数据 public function getList() { return $this->mypdo->fetchAll('select * from products'); } }1.3.5 演化四:分离控制器控制器代码放在index.php页面中是不合理的,因为项目中的控制器会很多,而index.php只有一个。所以需要将控制器分离开来控制器的规则:1、一个模块对应一个控制器(必须的)2、控制器以Controller结尾(不是必须的)3、控制器中的方法以Action结尾(不是必须的),目的防止方法名是PHP关键字创建ProductsController.class.php<?php //商品模块 class ProductsController { //获取商品列表 public function listAction() { //实例化模型 $model=new ProductsModel(); $list=$model->getList(); //加载视图 require './products_list.html'; } }index.php页面<?php //自动加载类 spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //确定路由 $c=$_GET['c']??'Products'; //控制器 $a=$_GET['a']??'list'; //方法 $c=ucfirst(strtolower($c)); //首字母大写 $a=strtolower($a); //转成小写 $controller_name=$c.'Controller'; //拼接控制器类名 $action_name=$a.'Action'; //拼接方法名 //请求分发 $obj=new $controller_name(); $obj->$action_name();通过在url地址上传递参数来寻址。c:控制器a:方法注意:每次请求都要从index.php进入。所以index.php又叫入口文件。小结:1.4 删除商品入口(products_list.html)<td><a href="index.php?c=Products&a=del&proid=<?=$rows['proID']?>" onclick="return confirm('确定要删除吗')">删除</a></td>控制器(ProductsController)<?php //商品模块 class ProductsController { .. //删除商品 public function delAction() { $id=(int)$_GET['proid']; //如果参数明确是整数,要强制转成整形 $model=new ProductsModel(); if($model->del($id)) header('location:index.php?c=Products&a=list'); else { echo '删除失败'; exit; } } } 模型(ProductsModel)<?php //products模型用来操作products表 class ProductsModel extends Model{ ... //删除商品 public function del($proid) { return $this->mypdo->exec("delete from products where proid=$proid"); } }
2022年10月12日
80 阅读
0 评论
0 点赞
2022-10-11
PHP面向对象(4)
1.1 目标掌握trait的作用以及实际应用;了解预定义接口Iterator对foreach的修改;掌握命名空间的基本语法;掌握命名空间的成员控制;了解子空间的概念;掌握命名空间的三种访问方式及其区别;掌握空间引入的使用;掌握全局空间的概念;掌握全局空间与其他空间进行引入时的区别;了解PSR-1基础编码规范;了解不产生副作用和只产生副作用的概念能够按照PSR-1规范进行文件分类了解PSR-2编码规范1.2 命名空间1.2.1 介绍在一个大的项目中,可能会遇到同名的类、函数、常量,为了区分这些元素,我们可以将这些元素分别存放到不同的命名空间中。1、命名空间就是包,用来存放项目中的类、函数、常量2、通过namespace关键字来声明命名空间1.2.2 声明命名空间<?php namespace China; //定义命名空间 function getInfo() { echo '我是中国人<br>'; } namespace USA; //定义命名空间 function getInfo() { echo 'I am a America<br>'; } //调用 getInfo(); //I am a America \USA\getInfo(); //I am a America \China\getInfo(); //我是中国人注意:\表示公共空间1.2.3 多级命名空间命名空间的名字可以是多级的(子级命名空间)。<?php namespace China\Beijing\Shunyi; class Student { } namespace USA\Washington; class Student { } //测试: $stu1=new Student(); //相对路径 $stu2=new \USA\Washington\Student(); //绝对路径 $stu3=new \China\Beijing\Shunyi\Student(); //绝对路径 var_dump($stu1,$stu2,$stu3); //object(USA\Washington\Student)#1 (0) { } //object(USA\Washington\Student)#2 (0) { } //object(China\Beijing\Shunyi\Student)#3 (0) { } 总结:如果将相对路径转成绝对路径公共空间+命名空间+空间元素 公共空间 命名空间 空间元素 \ China\Shanghai\PuDong\ Student1.2.4 访问空间元素的三种方式1、非限定名称访问2、完全限定名称访问3、限定名称访问<?php namespace China\Beijing\Shunyi; function getInfo() { echo '顺义...<br>'; } namespace China\Beijing; function getInfo() { echo '北京...<br>'; } //访问空间元素的三种方式 getInfo(); //非限定名称访问 北京... \China\Beijing\getInfo(); //完全限定名称访问 北京... Shunyi\getInfo(); //限定名称访问 顺义... //转成绝对路径如下: // \China\Beijing\Shunyi\getInfo();练习:将下面的相对路径转成绝对路径例题一: namespace A\B\C; fun(); => \A\B\C\fun(); a\fun(); => \A\B\C\a\fun() C\fun(); => \A\B\C\C\fun();1.2.5 引入命名空间完全限定名称访问元素路径太长,可以将其他空间引入到当前空间来通过use引入命名空间<?php namespace China\Beijing\Shunyi; function getInfo() { echo '李白<br>'; } namespace USA; function getInfo() { echo 'Lincoln<br>'; } //引入命名空间 use China\Beijing\Shunyi; //测试 getInfo(); //Lincoln Shunyi\getInfo(); //李白 /* 分析: 第一步:通过当前空间拼接成绝对路径:\USA\Shunyi\getInfo(),这个地址没有对应的空间元素 第二步:通过引入的空间拼接绝对路径:\China\Beijing\Shunyi+Shunyi\getInfo(),Shunyi是公共部分,只需要取一个,最后拼接的地址是:\China\Beijing\Shunyi\getInfo(),这个地址可以找到对应的元素 */引入命名空间的拼接规则公共空间+引入空间+(去除公共部分,公共部分只能有一级)空间元素 比如: namespace A\B\C; function getInfo(){} namespace D\E; use A\B\C; //引入命名空间 C\getInfo(); //正确 \A\B\C\getInfo(); B\C\getInfo(); //错误 \A\B\C\B\C\getInfo();1.2.6 引入空间元素引入类:use引入函数:use function [php7.0以后支持]引入常量:use const [php7.0以后支持]<?php namespace China\Beijing\Shunyi; class Student { } function getInfo() { echo '李白<br>'; } const TYPE='学生'; namespace USA; //引入类 use China\Beijing\Shunyi\Student; //引入函数 use function China\Beijing\Shunyi\getInfo; //引入常量 use const China\Beijing\Shunyi\TYPE; //测试 $stu=new Student; var_dump($stu); echo '<br>'; getInfo(); echo TYPE;1.2.7 给类、函数取别名如果引入的类和函数与当前空间的类和函数名称相同,需要给引入的类和函数取别名。通过as取别名<?php namespace China\Beijing\Shunyi; class Student { } function getInfo() { echo '李白<br>'; } namespace USA\Washington; class Student { } function getInfo() { echo 'Lincoln<br>'; } //引入类取别名 use China\Beijing\Shunyi\Student as ChinaStudent; //引入函数 use function China\Beijing\Shunyi\getInfo as info1; //测试 $stu=new ChinaStudent; var_dump($stu); getInfo(); //Lincoln info1(); //李白1.2.8 公共空间如果一个页面没有namespace声明空间,这个页面的元素在公共空间下公共空间用\表示<?php function getInfo() { echo '李白<br>'; } \getInfo(); //李白1.2.9 命名空间注意事项1、命名空间只能存放类、函数、const常量2、第一个namespace前面不能有任何的代码,空白字符、header()也不行。3、包含文件不影响当前的命名空间1.3 trait(原型)trait 为了减少单继承语言的限制,可以在不同层次结构内独立的类中复用类的方法集。<?php //原型 trait A{ public function getInfo() { echo '锄禾日当午<br>'; } } //使用原型 class Student { use A; //代码复用 } //测试 $stu=new Student; $stu->getInfo(); //锄禾日当午引入多个 trait<?php //原型 trait A{ public function getInfo1() { echo '锄禾日当午<br>'; } } trait B{ public function getInfo2() { echo '床前明月光<br>'; } } //使用原型 class Student { use A,B; //引入A、B trait } //测试 $stu=new Student; $stu->getInfo1(); //锄禾日当午 $stu->getInfo2(); //床前明月光trait和继承结合<?php trait A{ public function getInfo() { echo '这是trait原型<br>'; } } class Person { public function getInfo() { echo '这是Person类<br>'; } } //继承类同时代码复用 class Student extends Person { use A; //继承了getInfo,有被A中getInfo覆盖 } //测试 $stu=new Student; $stu->getInfo(); //这是trait原型解决同名冲突<?php //原型 trait A{ public function getInfo() { echo '锄禾日当午<br>'; } } trait B{ public function getInfo() { echo '床前明月光<br>'; } } //使用原型 class Student { use A,B{ //引入A和B的trait,同时解决名称冲突 //方法一:方法替换 //A::getInfo insteadof B; //将A中的getInfo替换掉B中的getInfo //B::getInfo insteadof A; //将B中的getInfo替换到A中的getInfo //方法二:改名 A::getInfo insteadof B; B::getInfo as show; //将B的getInfo改名为show } } //测试 $stu=new Student; $stu->getInfo(); //锄禾日当午 $stu->show(); //床前明月光 /* 同名冲突的解决方法有两个: 第一:方法替换 第二:方法改名更改权限<?php trait A{ private function show() { echo '锄禾日当午<br>'; } } class Student { use A{ //show as public; //将show方法权限设为public; show as public show2; //将show方法设置public,并改名为show2 } } $stu=new Student; //$stu->show(); $stu->show2(); 多学一招:具体参见手册1、 多个trait可以组成一个trait2、 trait可以定义抽象成员3、 trait可以定义静态成员4、 trait可以定义属性1.4 迭代器1.4.1 遍历数组手动遍历数组步骤:1、复位数组指针 reset()2、检查指针是否合法 获取当前指针,如果不为null就是合法的3、获取当前值 current()4、获取当前键 key()5、指针下移 next()代码实现<?php $stu=['tom','berry','ketty','rose']; reset($stu); //复位指针 while(key($stu)!==null){//键合法 echo key($stu),'-',current($stu),'<br>';//获取键、值 next($stu); //指针下移 } /* 0-tom 1-berry 2-ketty 3-rose */1.4.2 迭代器迭代器是PHP内置的接口场景:遍历对象,获取的是对象中属性保存的数组<?php //定义类实现迭代器接口 class MyClass implements Iterator{ //$list属性用来保存学生数组 private $list=array(); //添加学生 public function addStu($name) { $this->list[]=$name; } //实现接口中的复位方法 public function rewind() { reset($this->list); } //验证当前指针是否合法 public function valid() { return key($this->list)!==null; } //获取值 public function current() { return current($this->list); } //获取键 public function key() { return key($this->list); } //指针下移 public function next() { next($this->list); } } //创建班级 $class=new MyClass(); //添加学生 $class->addStu('tom'); $class->addStu('berry'); $class->addStu('ketty'); //遍历班级 foreach($class as $k=>$v){ echo "{$k}-{$v}<br>"; } /* 0-tom 1-berry 2-ketty */1.5 PSR编码规范1.5.1 概述PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。目前已表决通过了 6 套标准,已经得到大部分 PHP 框架的支持和认可。网址:http://psr.phphub.org/1.5.2 PSR-1 基础编码规范1、PHP代码文件 必须 以 <?php 或 <?= 标签开始 2、类的命名大写开头的驼峰命名规范 3、类中的常量所有字母都必须大写,单词间用下划线分隔 4、方法名称必须符合小写开头驼峰命名规范5、副作用:(side effects),一个文件只做一件事情,如果做了其他事情就是产生了副作用不产生副作用:一个文件只做一件事产生副作用:一个文件做了多件事情1.5.3 PSR-2 编码风格规范代码 必须 使用 4 个空格符而不是「Tab 键」进行缩进 每个 namespace 命名空间声明语句和 use 声明语句块后面,必须 插入一个空白行 类的开始花括号({) 必须 写在类声明后自成一行,结束花括号(})也 必须 写在类主体后自成一行 方法的开始花括号({) 必须 写在函数声明后自成一行,结束花括号(})也 必须 写在函数主体后自成一行。 类的属性和方法 必须 添加访问修饰符(private、protected 以及 public),abstract 以及 final 必须 声明在访问修饰符之前,而 static 必须 声明在访问修饰符之后。1.6 分页1.6.1 分析-- 1、获取当前页码的数据 页码 SQL语句 1 select * from products limit 0,10 2 select * from products limit 10,10 3 select * from products limit 20,10 结论: $pageno:页码 $startno:起始位置 $pagesize=10:页面大小 公式:$startno=($pageno-1)*$pagesize; -- 2、如何获取页码 用户点击页面底端页码,传递当前的页面 -- 3、如何获取总页码 记录数 页数 计算 60 6 60/10=6 51 6 ceil(51/10)=6 结论: $rowcount:总记录数 $pagecount:总页数 公式:$pagecount=ceil($rowcount/$pagesize) -- 4、如何获取总记录数 select count(*) from products;1.6.2 步骤第一步:获取总记录数第二步:求出总页数第三步:循环显示页码第四步:通过当前页面,求出起始位置第五步:获取当前页面数据,并遍历显示1.6.3 代码实现将上一讲的MySQLDB类拷贝到站点下,将测试代码删除,只留下类代码,文件名改为MySQLDB.class.php分页页面代码如下:<?php //自动加载类 spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //获取单例 $param=array( 'user' => 'root', 'pwd' => 'root', 'dbname' => 'data' ); //获取单例 $db=MySQLDB::getInstance($param); ?> <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style type="text/css"> table{ width:780px; border:solid #000 1px; } td,th{ border:solid #000 1px; } </style> </head> <body> <?php $pagesize=10; //页面大小 //第一步:获取总记录数 $rowcount=$db->fetchColumn('select count(*) from products'); //第二步:求出总页数 $pagecount=ceil($rowcount/$pagesize); //第四步:通过当前页面,求出起始位置 //$pageno=isset($_GET['pageno'])?$_GET['pageno']:1; $pageno=$_GET['pageno']??1; $pageno=$pageno<1?1:$pageno; $pageno=$pageno>$pagecount?$pagecount:$pageno; $startno=($pageno-1)*$pagesize; //第五步:获取当前页面数据,并遍历显示 $sql="select * from products limit $startno,$pagesize"; $rs=$db->fetchAll($sql); ?> <table> <tr> <th>编号</th> <th>商品名称</th> <th>规格</th> <th>价格</th> </tr> <?php foreach($rs as $row):?> <tr> <td><?=$row['proID']?></td> <td><?=$row['proname']?></td> <td><?=$row['proguige']?></td> <td><?=$row['proprice']?></td> </tr> <?php endforeach;?> </table> <!--第三步:循环显示页码--> 一共有<?=$rowcount?>条记录,每页放<?=$pagesize?>条记录,当前是<?=$pageno?>页 <br /> 【<a href="?pageno=1">首页</a>】 【<a href="?pageno=<?=$pageno-1?>">上一页</a>】 <?php for($i=1; $i<=$pagecount; $i++):?> <a href="?pageno=<?=$i?>"><?=$i?></a> <?php endfor;?> 【<a href="?pageno=<?=$pageno+1?>">下一页</a>】 【<a href="?pageno=<?=$pagecount?>">末页</a>】 </body> </html>运行结果1.6.4 分页优化在上面的分页代码中,虽然SQL语句比较经典,但是每次都要获取不需要的数据,浪费资源$sql="select * from products limit $startno,$pagesize";优化$sql="select * from products where proid>=(select proid from products limit $startno,1) limit $pagesize";1.6.5 练习通过异步实现分页
2022年10月11日
66 阅读
0 评论
0 点赞
2022-10-11
PHP面向对象(3)
1.1 目标掌握类的自动加载原理和实际应用;了解对象的克隆意义;掌握PHP重载的意义以及具体重载的应用;了解对象的序列化和反序列化操作以及注意事项掌握foreach对对象进行遍历;掌握Mysqli的类封装以及应用;1.2 自动加载类在项目开发中,因为一个文件中只能写一个类,并且在执行过程中会有很多的类参与,如果一个一个的加载很麻烦,所以,就需要一个机制实现在PHP执行过程中自动加载需要的类。1.2.1 类的规则一个文件中只能放一个类(必须)文件名和类名同名(必须)类文件以.class.php结尾(不是必须)1.2.2 手动加载类1、创建Goods.class.php页面<?php //商品类 abstract class Goods { protected $name; final public function setName($name) { $this->name=$name; } public abstract function getName(); }2、创建Book.class.php页面<?php //图书类 class Book extends Goods { public function getName() { echo "《{$this->name}》<br>"; } }3、创建Phone.class.php页面<?php //电话类 class Phone extends Goods { public function getName() { echo $this->name,'<br>'; } }4、在PHP页面上加载类文件<?php require './Goods.class.php'; //手动加载类文件 require './Book.class.php'; //手动加载类文件 require './Phone.class.php'; //手动加载类文件 //测试 $book=new Book(); $book->setName('面向对象编程'); $phone=new Phone(); $phone->setName('苹果6s'); $book->getName(); $phone->getName();运行结果1.2.3 自动加载类当缺少类的时候自动的调用__autoload()函数,并且将缺少的类名作为参数传递给__autoload()。<?php /* *作用:自动加载类 *@param $class_name string 缺少的类名 */ function __autoload($class_name) { require "./{$class_name}.class.php"; } //测试 $book=new Book(); $book->setName('面向对象编程'); $phone=new Phone(); $phone->setName('苹果6s'); $book->getName(); $phone->getName();注意:__autoload()函数在PHP7.2以后就不支持了。1.2.4 注册加载类通过spl_autoload_register()注册__autoload()函数<?php //方法一: /* //加载类函数 function loadClass($class_name) { require "./{$class_name}.class.php"; } //注册加载类函数 spl_autoload_register('loadClass'); */ //方法二: spl_autoload_register(function($class_name){ require "./{$class_name}.class.php"; }); //测试 $book=new Book(); $book->setName('面向对象编程'); $phone=new Phone(); $phone->setName('苹果6s'); $book->getName(); $phone->getName();1、spl_autoload_register()可以注册多个自动加载函数<?php function load1($class) { require "./{$class}.class.php"; } function load2($class) { require "./{$class}.php"; } function load3($class) { require "./{$class}.fun.php"; } spl_autoload_register('load1'); spl_autoload_register('load2'); spl_autoload_register('load3');2、PHP5.1以后就开始支持此函数。1.2.5 类文件存储不规则的加载方法将类名和文件地址做一个映射,组成一个关联数组。$map=array( //类名 => 类文件地址 'Goods' => './aa/Goods.class.php', 'Book' => './bb/Book.class.php', 'Phone' => './cc/Phone.class.php' );代码如下<?php spl_autoload_register(function($class_name){ //类名和文件地址映射成一个关联数组 $map=array( 'Goods' => './aa/Goods.class.php', 'Book' => './bb/Book.class.php', 'Phone' => './cc/Phone.class.php' ); //在映射数组中找到就包含 if(isset($map[$class_name])) require $map[$class_name]; }); //测试 $book=new Book(); $book->setName('面向对象编程'); $phone=new Phone(); $phone->setName('苹果6s'); $book->getName(); $phone->getName();在项目中,绝大部分都是规则存储的,不规则的比较少。1.3 clone和__clone()思考:创建对象的方式有哪些?方法一:实例化 方法二:克隆例题<?php class Student { //执行clone指令的时候自动执行 public function __clone() { echo '正在克隆对象<br>'; } } $stu1=new Student; $stu2=clone $stu1; //克隆对象 var_dump($stu1,$stu2); //object(Student)#1 (0) { } object(Student)#2 (0) { } 小结:1、clone的创建对象的方法之一2、当执行clone指令的时候,会自动的调用__clone()方法1.4 设计模式1.4.1 单例模式一个类只能有一个对象应用场景:多次请求数据库只需要一个连接对象。实现:三私一公1、私有的静态属性用来保存对象的单例 2、私有的构造方法用来阻止在类的外部实例化 3、私有的__clone阻止在类的外部clone对象 4、公有的静态方法用来获取对象的单例代码<?php //三私一公 class DB { //静态的属性用来保存对象的单例 private static $instance; //私有的构造方法阻止在类的外部实例化 private function __construct() { } //私有的__clone()阻止在类的外部clone对象 private function __clone() { } public static function getInstance() { //保存的值不属于DB类的类型就实例化 if(!self::$instance instanceof self) self::$instance=new self(); return self::$instance; } } //测试 $db1=DB::getInstance(); $db2=DB::getInstance(); var_dump($db1,$db2); //object(DB)#1 (0) { } object(DB)#1 (0) { } 1.4.2 工厂模式特点:传递不同的参数获取不同的对象<?php class ProductsA { } class ProductsB { } //工厂模式 class ProductsFactory { public function create($num) { switch($num) { case 1: return new ProductsA; case 2: return new ProductsB; default: return null; } } } //测试 $factory=new ProductsFactory(); $obj1=$factory->create(1); $obj2=$factory->create(2); var_dump($obj1,$obj2); //object(ProductsA)#2 (0) { } object(ProductsB)#3 (0) { } 1.4.3 策略模式特点:传递不同的参数调用不同的策略(方法)<?php class Walk { public function way() { echo '走着去<br>'; } } class Bus { public function way() { echo '坐车去<br>'; } } //策略模式 class Student { public function play($obj) { $obj->way(); } } //测试 $stu=new Student; $stu->play(new Walk()); //走着去 $stu->play(new Bus()); //坐车去1.5 序列化与反序列化在PHP中,数组和对象无法保存,如果需要保存就要将数组或对象转换成一个序列。序列化:将数组或对象转换成一个序列(serialize)反序列化:将序列化的字符串转换成数组或对象。(unserialize)1.5.1 数组的序列化与反序列化<?php //数组的序列化 /* $stu=['tom','berry','ketty']; $str=serialize($stu); //序列化 file_put_contents('./stu.txt',$str); */ //数组的反序列化 $str=file_get_contents('./stu.txt'); $stu=unserialize($str); //反序列化 print_r($stu); //Array ( [0] => tom [1] => berry [2] => ketty ) 1.5.2 对象的序列化与反序列化注意:对象的反序列化需要有类的参与,如果没有类在反序列化时候无法确定类代码<?php class Student { public $name; protected $sex; private $add; public function __construct($name,$sex,$add) { $this->name=$name; $this->sex=$sex; $this->add=$add; } } /* //测试 $stu=new Student('tom','男','北京'); //序列化 $str=serialize($stu); file_put_contents('./stu.txt',$str); */ //反序列化,类的反序列化必须要有类的参与 $str=file_get_contents('./stu.txt'); $stu=unserialize($str); echo '<pre>'; var_dump($stu);运行结果1.6 魔术方法已经学习的魔术方法__construct() __destruct() __clone()1.6.1 __tostring()、__invoke()__tostring():将对象当成字符串使用的时候自动调用__invoke():将对象当成函数使用的时候自动调用<?php class Student { //把对象当成字符串使用的时候自动执行 public function __tostring() { return '这是一个对象,不是字符串<br>'; } //把对象当成函数使用的时候自动执行 public function __invoke() { echo '这是一个对象,不是函数<br>'; } } $stu=new Student; echo $stu; //当成字符串使用 $stu(); //当成函数使用1.6.2 __set()、__get()、__isset()、__unset()__set($k,$v):给无法访问的属性赋值的时候自动执行 __get($k):获取无法访问的属性值的时候自动调用 __isset($k):判断无法访问的属性是否存在自动调用 __unset($k):销毁无法访问的属性的时候自动执行例题<?php class Student { private $name; private $sex; private $age; //给无法访问的属性赋值的时候自动执行 public function __set($k,$v) { $this->$k=$v; } //获取无法访问的属性值的时候自动调用 public function __get($k) { return $this->$k; } //判断无法访问的属性是否存在自动调用 public function __isset($k) { return isset($this->$k); } //销毁无法访问的属性的时候自动执行 public function __unset($k) { unset($this->$k); } } //测试 $stu=new Student; //1、给私有属性赋值 $stu->name='tom'; $stu->sex='男'; $stu->age=22; //2、获取私有属性的值 //echo $stu->name; //3、判断私有属性是否存在 //var_dump(isset($stu->name)); //4、销毁私有属性 unset($stu->age); print_r($stu);应用:设置读写属性<?php class Student { private $name; //读写属性 private $add='中国'; //只读属性 private $age; //只写属性 public function __set($k,$v) { if(in_array($k,array('name','age'))) $this->$k=$v; else echo "{$k}属性是只读属性<br>"; } public function __get($k) { if(in_array($k,array('name','add'))) return $this->$k; else echo "{$k}是只写属性<br>"; } } //测试 $stu=new Student; $stu->name='tom'; $stu->age=22; echo '姓名:'.$stu->name,'<br>'; echo '地址:'.$stu->add,'<br>';1.6.3 __call()、__callstatic()__call():调用无法访问的方法时自动执行 __callstatic():调用无法访问的静态方法时自动执行例题:<?php class Student { /** *作用:调用无法访问的方法时自动执行 *@param $fn_name string 方法名 *@param $fn_args array 参数数组 */ public function __call($fn_name,$fn_args) { echo "{$fn_name}不存在<br>"; } //调用无法访问的静态方法时自动执行 public static function __callstatic($fn_name,$fn_args) { echo "{$fn_name}静态方法不存在<br>"; } } //测试 $stu=new Student; $stu->show(10,20); Student::show();1.6.4 __sleep()、__wakeup()__sleep():当序列化的时候自动调用 __wakeup():当反序列化的时候自动调用例题<?php class Student { private $name; private $sex; private $add='中国'; public function __construct($name,$sex) { $this->name=$name; $this->sex=$sex; } /** *序列化的时候自动调用 *@return array 序列化的属性名 */ public function __sleep() { return array('name','sex'); } //反序列化的时候自动调用 public function __wakeup() { $this->type='学生'; } } //测试 $stu=new Student('tom','男'); $str=serialize($stu); //序列化 $stu=unserialize($str); //反序列化 print_r($stu);1.7 模拟方法重载通过魔术方法模拟方法重载<?php class Math { public function __call($fn_name,$fn_args) { $sum=0; foreach($fn_args as $v) { $sum+=$v; } echo implode(',',$fn_args).'的和是:'.$sum,'<br>'; } } //利用魔术方法模拟方法重载 $math=new Math(); $math->call(10,20); $math->call(10,20,30); $math->call(10,20,30,40);1.8 遍历对象通过foreach遍历对象<?php class Student { public $name='tom'; protected $sex='男'; private $age=22; public function show() { foreach($this as $k=>$v) { echo "{$k}-{$v}<br>"; } } } //测试 $stu=new Student; foreach($stu as $k=>$v) { echo "{$k}-{$v}<br>"; } echo '<hr>'; $stu->show();结论:遍历到当前位置所能访问到属性1.9 封装MySQL的单例1.8.1 分析1、实现单例2、连接数据库3、对数据进行操作1.8.2 步骤第一步:实现单例第二步:初始化参数第三步:连接数据库第四步:操作数据1、执行数据操作语句(增、删、改)2、执行数据查询语句 a) 返回二维数组 b) 返回一维数组 c)返回一行一列1.8.3 代码实现第一步:实现单例<?php class MySQLDB { private static $instance; private function __construct() { } private function __clone() { } public static function getInstance() { if(!self::$instance instanceof self) self::$instance=new self(); return self::$instance; } } //测试 $db=MySQLDB::getInstance(); var_dump($db);注意:A instanceof B,表示A是否是B的类型,返回bool值第二步:初始化参数<?php //封装MySQL单例 class MySQLDB { private $host; //主机地址 private $port; //端口号 private $user; //用户名 private $pwd; //密码 private $dbname; //数据接名 private $charset; //字符集 private $link; //连接对象 private static $instance; private function __construct($param) { $this->initParam($param); } private function __clone() { } //获取单例 public static function getInstance($param=array()) { if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param) { $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->user=$param['user']??''; $this->pwd=$param['pwd']??''; $this->dbname=$param['dbname']??''; $this->charset=$param['charset']??'utf8'; } } //测试 //配置参数 $param=array( 'user' => 'root', 'pwd' => 'root', 'dbname' => 'data' ); //获取单例 $db=MySQLDB::getInstance($param); var_dump($db);第三步:连接数据库<?php //封装MySQL单例 class MySQLDB { private $host; //主机地址 private $port; //端口号 private $user; //用户名 private $pwd; //密码 private $dbname; //数据接名 private $charset; //字符集 private $link; //连接对象 private static $instance; private function __construct($param) { $this->initParam($param); $this->initConnect(); } private function __clone() { } //获取单例 public static function getInstance($param=array()) { if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param) { $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->user=$param['user']??''; $this->pwd=$param['pwd']??''; $this->dbname=$param['dbname']??''; $this->charset=$param['charset']??'utf8'; } //连接数据库 private function initConnect() { $this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname); if(mysqli_connect_error()){ echo '数据库连接失败<br>'; echo '错误信息:'.mysqli_connect_error(),'<br>'; echo '错误码:'.mysqli_connect_errno(),'<br>'; exit; } mysqli_set_charset($this->link,$this->charset); } } //测试 //配置参数 $param=array( 'user' => 'root', 'pwd' => 'root', 'dbname' => 'data' ); //获取单例 $db=MySQLDB::getInstance($param); var_dump($db);第四步:数据操作的功能1、执行增、删、改操作<?php //封装MySQL单例 class MySQLDB { private $host; //主机地址 private $port; //端口号 private $user; //用户名 private $pwd; //密码 private $dbname; //数据接名 private $charset; //字符集 private $link; //连接对象 private static $instance; private function __construct($param) { $this->initParam($param); $this->initConnect(); } private function __clone() { } //获取单例 public static function getInstance($param=array()) { if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param) { $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->user=$param['user']??''; $this->pwd=$param['pwd']??''; $this->dbname=$param['dbname']??''; $this->charset=$param['charset']??'utf8'; } //连接数据库 private function initConnect() { $this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname); if(mysqli_connect_error()){ echo '数据库连接失败<br>'; echo '错误信息:'.mysqli_connect_error(),'<br>'; echo '错误码:'.mysqli_connect_errno(),'<br>'; exit; } mysqli_set_charset($this->link,$this->charset); } //执行数据库的增、删、改、查 private function execute($sql) { if(!$rs=mysqli_query($this->link,$sql)){ echo 'SQL语句执行失败<br>'; echo '错误信息:'.mysqli_error($this->link),'<br>'; echo '错误码:'.mysqli_errno($this->link),'<br>'; echo '错误的SQL语句:'.$sql,'<br>'; exit; } return $rs; } /** *执行增、删、改 *@return bool 成功返回true,失败返回false */ public function exec($sql) { $key=substr($sql,0,6); if(in_array($key,array('insert','update','delete'))) return $this->execute($sql); else{ echo '非法访问<br>'; exit; } } //获取自动增长的编号 public function getLastInsertId() { return mysqli_insert_id($this->link); } } //测试 //配置参数 $param=array( 'user' => 'root', 'pwd' => 'root', 'dbname' => 'data' ); //获取单例 $db=MySQLDB::getInstance($param); //更新 //$db->exec("update news set title='青草' where id=2"); //插入 if($db->exec("insert into news values (null,'aa','bb',unix_timestamp())")) echo '编号是:'.$db->getLastInsertId();2、查询结果<?php //封装MySQL单例 class MySQLDB { private $host; //主机地址 private $port; //端口号 private $user; //用户名 private $pwd; //密码 private $dbname; //数据接名 private $charset; //字符集 private $link; //连接对象 private static $instance; private function __construct($param) { $this->initParam($param); $this->initConnect(); } private function __clone() { } //获取单例 public static function getInstance($param=array()) { if(!self::$instance instanceof self) self::$instance=new self($param); return self::$instance; } //初始化参数 private function initParam($param) { $this->host=$param['host']??'127.0.0.1'; $this->port=$param['port']??'3306'; $this->user=$param['user']??''; $this->pwd=$param['pwd']??''; $this->dbname=$param['dbname']??''; $this->charset=$param['charset']??'utf8'; } //连接数据库 private function initConnect() { $this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname); if(mysqli_connect_error()){ echo '数据库连接失败<br>'; echo '错误信息:'.mysqli_connect_error(),'<br>'; echo '错误码:'.mysqli_connect_errno(),'<br>'; exit; } mysqli_set_charset($this->link,$this->charset); } //执行数据库的增、删、改、查 private function execute($sql) { if(!$rs=mysqli_query($this->link,$sql)){ echo 'SQL语句执行失败<br>'; echo '错误信息:'.mysqli_error($this->link),'<br>'; echo '错误码:'.mysqli_errno($this->link),'<br>'; echo '错误的SQL语句:'.$sql,'<br>'; exit; } return $rs; } /** *执行增、删、改 *@return bool 成功返回true,失败返回false */ public function exec($sql) { $key=substr($sql,0,6); if(in_array($key,array('insert','update','delete'))) return $this->execute($sql); else{ echo '非法访问<br>'; exit; } } //获取自动增长的编号 public function getLastInsertId() { return mysqli_insert_id($this->link); } //执行查询语句 private function query($sql) { if(substr($sql,0,6)=='select' || substr($sql,0,4)=='show' || substr($sql,0,4)=='desc'){ return $this->execute($sql); }else{ echo '非法访问<br>'; exit; } } /** *执行查询语句,返回二维数组 *@$sql string 查询sql语句 *@type string assoc|num|both */ public function fetchAll($sql,$type='assoc') { $rs=$this->query($sql); $type=$this->getType($type); return mysqli_fetch_all($rs,$type); } //匹配一维数组 public function fetchRow($sql,$type='assoc') { $list=$this->fetchAll($sql,$type); if(!empty($list)) return $list[0]; return array(); } //匹配一行一列 public function fetchColumn($sql) { $list=$this->fetchRow($sql,'num'); if(!empty($list)) return $list[0]; return null; } //获取匹配类型 private function getType($type) { switch($type){ case 'num': return MYSQLI_NUM; case 'both': return MYSQLI_BOTH; default: return MYSQLI_ASSOC; } } } //测试 //配置参数 $param=array( 'user' => 'root', 'pwd' => 'root', 'dbname' => 'data' ); //获取单例 $db=MySQLDB::getInstance($param); //更新 //$db->exec("update news set title='青草' where id=2"); //插入 /* if($db->exec("insert into news values (null,'aa','bb',unix_timestamp())")) echo '编号是:'.$db->getLastInsertId(); */ //查询 //$list=$db->fetchAll('select * from news','aa'); //$list=$db->fetchRow('select * from news where id=1','aa'); $list=$db->fetchColumn('select count(*) from news'); echo '<pre>'; var_dump($list); 小结:1、instanceof 用来判断对象是否属于某个类2、参数必须从外部传递到内部,不能写死到类的内部。3、为了保证代码的可重用性,一个方法只实现一个功能,所以初始化参数和连接数据库分到两个方法中。
2022年10月11日
38 阅读
0 评论
0 点赞
1
...
14
15
16
17