首页
留言
友情链接
Search
1
如何使用JavaScript获取和设置CSS root变量值
830 阅读
2
春和 《江海共余生》
325 阅读
3
清除浮动,单行多行超出用...
295 阅读
4
Centos7 下编译安装php8.2
285 阅读
5
中国历史朝代顺序图
228 阅读
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
历史
故事
诗词
生活
学习
其他
抖音
快手
小视频
随笔
易经
书摘
登录
/
注册
Search
标签搜索
诗词
sunshine
累计撰写
142
篇文章
累计收到
14
条评论
首页
栏目
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
历史
故事
诗词
生活
学习
其他
抖音
快手
小视频
随笔
易经
书摘
页面
留言
友情链接
搜索到
23
篇与
的结果
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; }} ![1561716144848](https://img.sunshine966.com/images/1561716144848.png) #### 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语句拼接成具有其他含义的语句,以达到攻击的目的 ![1561719384318](https://img.sunshine966.com/images/1561719384318.png) 原理 ![1561719631643](https://img.sunshine966.com/images/1561719631643.png) 防范措施: 1、给特殊字符添加转义 2、将单引号替换为空 //单引号添加转义字符echo addslashes("aa'bb'"),''; //aa\'bb\'//字符串替换echo str_replace("'",'',"aa'bb'"); //aabb 3、md5加密 4、预处理 5、如果确定传递的参数是整数,就需要进行强制类型转换。 ![1561720464857](https://img.sunshine966.com/images/1561720464857.png) #### 1.4.7 防止翻墙 翻墙:通过直接在地址栏输入URL地址进入模板页面 解决:用户登录成功以后,给用户一个令牌(session),在整个访问的过程中,令牌不消失。 ![1561721185763](https://img.sunshine966.com/images/1561721185763.png) 代码实现: 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日
42 阅读
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日
40 阅读
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日
27 阅读
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日
23 阅读
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日
10 阅读
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日
6 阅读
0 评论
0 点赞
2022-10-11
PHP面向对象(2)
1.1 目标掌握静态成员的意义和应用;掌握self关键字的实际运用;了解面向对象的三大特性:封装、继承和多态;掌握PHP中继承机制和基本语法;掌握访问修饰限定符protected的原理和实际运用;掌握重写override的作用和实际应用;了解PHP中继承的特点;了解静态延迟绑定的作用和使用;掌握最终类的作用以及具体的使用方式;掌握抽象类的作用以及具体的使用方式;掌握接口的作用以及具体的使用方式;1.2 多态多态:多种形态。多态分为两种:方法重写和方法重载1.2.1 方法重写子类重写了父类的同名的方法<?php //父类 class Person { public function show() { echo '这是父类<br>'; } } //子类 class Student extends Person { //子类重写了父类的同名方法 public function show() { echo '这是子类<br>'; } } //测试 $stu=new Student; $stu->show(); //这是子类注意事项:子类的方法必须和父类的方法同名参数个数要一致子类修饰的不能比父类更加严格1.2.2 方法重载在同一个类中,有多个同名的函数,通过参数的不同来区分不同的方法,称为方法重载注意:PHP不支持方法重载,但是PHP可以通过其他方法来模拟方法重载。1.3 面向对象三大特性封装继承多态1.4 私有属性继承和重写私有属性可以继承但不能重写。<?php class A { private $name='PHP'; public function showA() { //var_dump($this); //object(B)#1 (2) { ["name":"B":private]=> string(4) "Java" ["name":"A":private]=> string(3) "PHP" } echo $this->name,'<br>'; //PHP } } class B extends A { private $name='Java'; public function showB() { //var_dump($this); //object(B)#1 (2) { ["name":"B":private]=> string(4) "Java" ["name":"A":private]=> string(3) "PHP" } echo $this->name,'<br>'; //Java } } $obj=new B(); $obj->showA(); $obj->showB(); /*分析: showA()和showB()中的$this都表示B的对象,B中继承了A的私有属性,所以B中有两个$name. 在showA()中只能访问A中的$name,不能访问B中的$name 在showB()中只能访问B中的$name,不能访问A中的$name */练习一<?php class A { protected $name='tom'; public function showA() { echo $this->name,'<br>'; } } class B extends A { public $name='berry'; public function showB() { echo $this->name,'<br>'; } } //测试 $obj=new B(); $obj->showA(); //berry $obj->showB(); //berry /* 分析:B中将A的$name重写,所以$obj中只有一个$name,($name='berry'),不管$this在哪个方法中访问,就只能访问这个$name */练习二<?php class A { private $name='tom'; public function showA() { echo $this->name,'<br>'; } } class B extends A { public $name='berry'; public function showB() { echo $this->name,'<br>'; } } //测试 $obj=new B(); $obj->showA(); //tom $obj->showB(); //berry /* 分析: $obj中有两个$name,一个是私有的,一个是公有的 在showA()中既能访问私有的$name,也能访问公有的$name,但是私有的比公有的权限高,所以输出tom 在showB()中不能访问私有的$name,只能访问公有的$name,所以输出berry */1.5 方法修饰符方法修饰符有:static、final、abstract1.5.1 static【静态的】static修饰的属性叫静态属性、static修饰的方法叫静态方法静态成员加载类的时候分配空间,程序执行完毕后销毁静态成员在内存中就一份。调用语法 类名::属性 类名::方法名()<?php class Person { public static $add='北京'; // 修饰符之间没有顺序 static public function show() { echo '这是一个静态的方法<br>'; } } echo Person::$add,'<br>'; //北京 Person::show(); //这是一个静态的方法练习:统计在线人数<?php class Student { private static $num=0; //静态变量,在内存中就一份 public function __construct() { self::$num++; //self表示所在类的类名 } public function __destruct() { self::$num--; } public function show() { echo '总人数是:'.self::$num,'<br>'; } } //测试 $stu1=new Student; $stu2=new Student; $stu3=new Student; $stu2->show(); //总人数是:3 unset($stu2); $stu3->show(); //总人数是:2注意:self表示所在类的类名,使用self降低耦合性静态成员也可以被继承<?php class Person { public static $add='中国'; public static function show() { echo '这是人类<br>'; } } //继承 class Student extends Person { } //测试 echo Student::$add,'<br>'; //中国 通过子类名称访问父类的静态成员 Student::show(); //这是人类静态延时绑定static表示当前对象所属的类<?php class Person { public static $type='人类'; public function show1() { //var_dump($this); //object(Student)#1 (0) { } //echo self::$type,'<br>'; //人类 echo static::$type,'<br>'; //学生 延时绑定 } } class Student extends Person { public static $type='学生'; public function show2() { //var_dump($this); //object(Student)#1 (0) { } //echo self::$type,'<br>'; //学生 echo static::$type,'<br>'; //学生 } } //测试 $obj=new Student(); $obj->show1(); $obj->show2();小结:1、static在内存中就一份,在类加载的时候分配空间2、如果有多个修饰符,修饰符之间是没有顺序的3、self表示所在类的类名4、static表示当前对象所属的类5、static有两个作用,第一表示静态的,第二表示类名1.5.2 final【最终的】final修饰的方法不能被重写final修饰的类不能被继承作用1、如果一个类确定不被继承,一个方法确定不会被重写,用final修饰可以提高执行效率。2、如果一个方法不允许被其他类重写,可以用final修饰。1.5.3 abstract【抽象的】abstract修饰的方法是抽象方法,修饰的类是抽象类只有方法的声明没有方法的实现称为抽象方法一个类中只要有一个方法是抽象方法,这个类必须是抽象类。抽象类的特点是不能被实例化子类继承了抽象类,就必须重新实现父类的所有的抽象方法,否则不允许实例化类中没有抽象方法也可以声明成抽象类,用来阻止类的实例化例题<?php //抽象类 abstract class Person { public abstract function setInfo(); //抽象方法 public function getInfo() { echo '获取信息<br>'; } } //继承 class Student extends Person { //重写实现父类的抽象方法 public function setInfo() { echo '重新实现父类的抽象方法<br>'; } } //测试 $stu=new Student; $stu->setInfo(); //重新实现父类的抽象方法 $stu->getInfo(); //获取信息抽象类的作用:1定义命名规范2、阻止实例化,如果一个类中所有的方法都是静态方法,这时候没有必要去实例化,可以通过abstract来阻止来的实例化。1.6 类常量类常量是const常量<?php class Student { //public const ADD; //7.1以后才支持访问修饰符 const ADD='地址不详'; } echo Student::ADD;问题:define常量和const常量的区别?答:const常量可以做类成员,define常量不可以做类成员。问题:常量和静态的属性的区别?答:相同点:都在加载类的时候分配空间 不同点:常量的值不可以更改,静态属性的值可以更改1.7 接口(interface)1.7.1 接口如果一个类中所有的方法是都是抽象方法,那么这个抽象类可以声明成接口接口是一个特殊的抽象类,接口中只能有抽象方法和常量接口中的抽象方法只能是public,可以省略,默认也是public的通过implements关键字来实现接口不能使用abstract和final来修饰接口中的抽象方法。<?php //声明接口 interface IPerson { const ADD='中国'; function fun1(); function fun2(); } //接口实现 class Student implements IPerson { public function fun1() { } public function fun2() { } } //访问接口中的常量 echo IPerson::ADD;1.7.2 接口的多重实现类不允许多重继承,但是接口允许多重实现。<?php interface IPic1 { function fun1(); } interface IPic2 { function fun2(); } //接口允许多重实现 class Student implements IPic1,IPic2 { public function fun1() { } public function fun2() { } }注意:1、在接口的多重实现中,如果有同名的方法,只要实现一次即可2、类可以继承的同时实现接口class Student extends Person implements IPIc1,IPic1{ }1.8 匿名类这是了解的内容,PHP7.0支持<?php $stu=new class { public $name='tom'; public function __construct() { echo '构造函数<br>'; } }; echo $stu->name; /*运行结果; 构造函数 tom */小结:1、如果类只被实例化一次就可以使用匿名类2、好处,在执行的过程中,类不占用空间1.9 方法绑定这是了解的内容,PHP7.0支持作用:将方法绑定到对象上,并调用语法:闭包->call(对象):将闭包绑定到对象上,并调用在PHP中匿名函数称为闭包例题<?php $lang='en'; //类 class Student{ } //匿名函数 if($lang=='ch'){ $fun=function(){ echo '我是一名学生'; }; }else{ $fun=function(){ echo 'i am a studnet'; }; } //绑定 $stu=new Student; $fun->call($stu); //i am a studnet1.10 异常处理集中处理在代码块中发生的异常。在代码块中发生了异常直接抛出,代码块中不处理异常,将异常集中起来一起处理。1.10.1 使用的关键字try:监测代码块 catch:捕获异常 throw:抛出异常 finally:无论有无异常都会执行,可以省略 Exception:异常类语法结构try{ //检测代码 }catch(Exception $ex){ //捕获异常 } finally{ //不论是否有异常,都要执行,finally可以省略 }例题:<?php if(isset($_POST['button'])) { try{ $age=$_POST['age']; if($age=='') throw new Exception('年龄不能为空',1001); //抛出异常 if(!is_numeric($age)) throw new Exception('年龄必须是数字',1001); //抛出异常 if(!($age>=10 && $age<=30)) throw new Exception('年龄必须在10-30之间',1002); //抛出异常 echo '您的年龄合适'; }catch(Exception $ex){ //捕获异常 echo '错误信息:'.$ex->getMessage(),'<br>'; echo '错误码:'.$ex->getCode(),'<br>'; echo '文件地址:'.$ex->getFile(),'<br>'; echo '错误行号:'.$ex->getLine(),'<br>'; } finally{ echo '关闭数据库连接'; //不管是否有异常,finally都要执行 } } ?> <form method="post" action=""> 年龄: <input type="text" name="age"> <br /> <input type="submit" name="button" value="提交"> </form>注意:抛出异常后,try块终止执行,执行权限交给catch块.运行结果1.8.2 自定义异常场景:如果实现异常的分类处理?比如异常有三个级别异常对应三种处理方式自定义三种异常即可所有异常类的父类是Exception,Exception中的方法不允许重写<?php //自定义空异常类 class MyNullException extends Exception { } //自定义类型异常 class MyTypeException extends Exception { } //自定义范围异常 class MyRangeException extends Exception { } //逻辑代码 if(isset($_POST['button'])) { try{ $name=$_POST['name']; $age=$_POST['age']; if($name=='') throw new MyNullException('姓名不能为空'); if($age=='') throw new MyNullException('年龄不能为空'); if(!is_numeric($age)) throw new MyTypeException('年龄不是数字'); if($age<10 || $age>30) throw new MyRangeException('年龄必须在10-30之间'); echo '姓名:'.$name,'<br>'; echo '年龄:'.$age; }catch(MyNullException $ex){ echo $ex->getMessage(),'<br>'; echo '错误记录在日志中'; }catch(MyTypeException $ex){ echo $ex->getMessage(),'<br>'; echo '发送电子邮件'; }catch(MyRangeException $ex){ echo $ex->getMessage(),'<br>'; echo '给管理员打电话'; } } ?> <form method="post" action=""> 姓名: <input type="text" name="name"> <br /> 年龄: <input type="text" name="age"> <br /> <input type="submit" name="button" value="提交"> </form>1.9 练习1、打印图像<body> <style type="text/css"> body{ text-align:center; font-size:25px; } span{ width:30px; height:10px; display:inline-block; } </style> <?php for($i=1;$i<=9;$i++){ $n=$i>5?(10-$i):$i; $k=2*$n-1; //星星的个数 for($j=1;$j<=$k;$j++){ echo '<span>*</span>'; } echo '<br>'; } ?> </body>2、打印回形<body> <style type="text/css"> body{ text-align:center; font-size:25px; } span{ width:30px; height:10px; display:inline-block; } </style> <?php for($i=1; $i<=10; $i++) { for($j=1; $j<=10; $j++) { if($i>=3 && $i<=8 && $j>=3 && $j<=8) echo '<span></span>'; else echo '<span>*</span>'; } echo '<br>'; } ?> </body>
2022年10月11日
75 阅读
0 评论
1 点赞
2022-10-11
PHP面向对象(1)
1.1 目标了解面向对象的基本概念;了解面向对象和面向过程的区别;掌握面向对象的基本语法:封装类;掌握类成员的基本使用:定义和访问;掌握访问修饰限定符的作用和实际运用;掌握$this的概念和使用;掌握构造方法的和析构方法的目的和触发时间;了解对象存储和访问的内存原理;了解对象比较的方式;掌握对象与类在内存在依赖关系;1.2 面向对象介绍1.2.1 介绍面向对象是一个编程思想。编程思想有面向过程和面向对象面向过程:编程思路集中的是过程上面向对象:编程思路集中在参与的对象以去饭馆吃饭为例: 面向过程:点菜——做菜——上菜——吃饭——结账——收拾 面向对象:服务员,厨师,客人1.2.2 面向对象的好处多人合作方便减少代码冗余,灵活性高代码的可重用性发挥到极致可扩展性强多学一招: OOP:面向对象编程(Object Oriented Programming,面向对象编程) OOA: 面向对象分析(Object-Oriented Analysis,OOA) OOD: 面向对象设计(Object-Oriented Design,OOD)1.3 类和对象1、对象是具体存在的事物,对象是由属性(变量)和方法(函数)组成的 2、类是具有相同属性和行为的一组对象的集合分析:做菜动作——厨师对象——厨师类结论:我们在开发的时候,先写类,通过类创建对象,然后调用对象的属性和方法实现功能。 类——对象——调用成员注意:一个类可以创建多个对象小结:1、对象是由属性和方法组成的2、类是所有对象的相同属性和方法的集合3、在开发的时候先写类,通过类创建对象,通过对象调用方法和属性4、一个类可以创建多个对象1.4 在PHP中实现类和对象1.4.1 创建类语法:class 类名{ //属性 //方法 //常量 } 类是由属性、方法、常量组成的,也可以说 类成员有:属性、方法、常量 类名的命名规则:以字母、下划线开头,后面跟的是字母、数字、下划线不能用PHP关键字做类名类名不区分大小写(变量名区分,关键字、类名不区分大小写)类名用帕斯卡命名法(大驼峰 单词的首字母大写)<?php class Student { }1.4.2 对象实例化通过new关键字来实例化对象。<?php //定义类 class Student { } //实例化对象 $stu1=new Student(); $stu2=new Student; //小括号可以省略 var_dump($stu1,$stu2); //object(Student)#1 (0) { } object(Student)#2 (0) { } 1.4.3 对象的比较注意:对象的传递是地址传递相等:结构和保存的值一样就相等全等:指向同一个对象才是全等。<?php //定义类 class Student { } //实例化对象 $stu1=new Student(); $stu2=new Student; $stu3=$stu2; //对象传递的是地址 //var_dump($stu1,$stu2,$stu3); //object(Student)#1 (0) { } object(Student)#2 (0) { } object(Student)#2 (0) { } //对象比较 var_dump($stu1==$stu2); //bool(true) ,比较对象的结构 echo '<br>'; var_dump($stu1===$stu2); //bool(false) $stu1和$stu2是否是同一个对象 echo '<br>'; var_dump($stu2===$stu3); //bool(true) $stu2和$stu3是同一个对象1.5 属性属性本质就是变量通过->调用对象的成员 对象名->属性名 对象名->方法名()<?php //定义类 class Student { public $name; //属性 public $add='地址不详'; //属性 } //实例化对象 $stu=new Student(); //print_r($stu); //Student Object ( [name] => [add] => 地址不详 ) //操作属性 //1、给属性赋值 $stu->name='tom'; $stu->add='北京'; //2、获取属性的值 echo '姓名:'.$stu->name,'<br>'; //姓名:tom echo '地址:'.$stu->add,'<br>'; //地址:北京 //3、添加属性 $stu->age=20; print_r($stu); //Student Object ( [name] => tom [add] => 北京 [age] => 20 ) echo '<br>'; //4、删除属性 unset($stu->add); print_r($stu); //Student Object ( [name] => tom [age] => 20 ) 1.6 方法方法的本质就是函数<?php class Student { //定义方法 public function show() { echo '这是show方法<br>'; } //public可以省略,如果省略,默认就是public function test() { echo '这是test方法<br>'; } } $stu=new Student; $stu->show(); //调用方法 $stu->test();多学一招:1、方法前面public是可以省略的,如果省略,默认就是public的。2、属性前面的public不能省略1.7 访问修饰符用来控制成员的访问权限修饰符描述public(公有的)在类的内部和外部都能访问private(私有的)只能在类的内部访问protected(受保护的)在整个继承链上访问多学一招:一般来说,属性都用私有的,通过公有的方法对私有的属性进行赋值和取值。作用:保证数据的合法性<?php //访问修饰符 class Student { private $name; //私有属性 private $sex; //私有属性 //通过公有的方法对私有的属性进行赋值 public function setInfo($name,$sex) { if($sex!='男' && $sex!='女'){ echo '性别必须是男或女'; exit; } $this->name=$name; //$this表示当前对象 $this->sex=$sex; } //显示信息 public function getInfo() { echo '姓名:'.$this->name,'<br>'; echo '性别:'.$this->sex,'<br>'; } } //实例化 $stu=new Student; $stu->setInfo('tom','男'); $stu->getInfo(); echo '<hr>'; $stu2=new Student; $stu2->setInfo('berry','女'); $stu2->getInfo();提示:$this表示调用当前方法的对象运行结果1.8 类和对象在内存中的分布对象的本质是一个复杂的变量类的本质是一个自定义的复杂数据类型栈区:运行速度快,体积小,保存基本类型堆区:运行速度稍慢,体积大,保存复杂类型实例化的过程就是分配内存空间的过程对象保存在堆区,将堆区的地址保存到栈区。分析如下代码的结构<?php class Student { public $name; public $sex; public function show() { } } $stu1=new Student; $stu2=new Student; $stu1->show();示意图1.9 封装封装就是有选择性的提供数据通过访问修饰符来实现封装1.10 构造方法1.10.1 介绍构造方法也叫构造函数,当实例化对象的时候自动执行。语法:function __construct(){ } 注意:前面是两个下划线例题<?php class Student { public function __construct() { echo '这是构造方法<br>'; } } new Student(); //这是构造方法 new Student(); //这是构造方法注意:在其他语言里,与类名同名的函数是构造函数,在PHP中不允许这种写法。class Student { //和类名同名的方法是构造方法,PHP中不建议使用 public function Student() { echo '这是构造方法<br>'; } } /* Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; Student has a deprecated constructor in F:\wamp\www\6-demo.php on line 2 这是构造方法 */1.10.2 构造函数作用:初始化成员变量<?php class Student { private $name; private $sex; //构造函数初始化成员变量 public function __construct($name,$sex) { $this->name=$name; $this->sex=$sex; } //显示信息 public function show() { echo "姓名:{$this->name}<br>"; echo "性别:{$this->sex}<br>"; } } //实例化 $stu=new Student('tom','男'); $stu->show(); //运行结果 /* 姓名:tom 性别:男 */注意:构造函数可以带参数,但不能有return。1.11 析构方法1.11.1 介绍当对象销毁的时候自动调用语法function __destruct(){ }脚下留心:析构函数不可以带参数例题<?php class Student { private $name; //构造方法 public function __construct($name) { $this->name=$name; echo "{$name}出生了<br>"; } //析构方法 public function __destruct() { echo "{$this->name}销毁了<br>"; } } //测试 $stu1=new Student('tom'); $stu2=new Student('berry'); $stu3=new Student('ketty'); echo '<hr>';运行结果1.11.2 计算机的内存管理计算机内存管理方式:先进先出,先进后出先进先出的内存管理方式一般用在业务逻辑中,比如秒杀、购票等等先进后出是计算机的默认内存管理方式1.11.3 思考题思考题1<?php class Student { private $name; //构造方法 public function __construct($name) { $this->name=$name; echo "{$name}出生了<br>"; } //析构方法 public function __destruct() { echo "{$this->name}销毁了<br>"; } } //测试 $stu1=new Student('tom'); $stu2=new Student('berry'); $stu3=new Student('ketty'); unset($stu2); echo '<hr>'; /* tom出生了 berry出生了 ketty出生了 berry销毁了 ketty销毁了 tom销毁了 */思考题2<?php class Student { private $name; //构造方法 public function __construct($name) { $this->name=$name; echo "{$name}出生了<br>"; } //析构方法 public function __destruct() { echo "{$this->name}销毁了<br>"; } } //测试 new Student('tom'); new Student('berry'); new Student('ketty'); /* tom出生了 tom销毁了 berry出生了 berry销毁了 ketty出生了 ketty销毁了 */思考题3<?php class Student { private $name; //构造方法 public function __construct($name) { $this->name=$name; echo "{$name}出生了<br>"; } //析构方法 public function __destruct() { echo "{$this->name}销毁了<br>"; } } //测试 $stu=new Student('tom'); $stu=new Student('berry'); $stu=new Student('ketty'); /* tom出生了 berry出生了 tom销毁了 ketty出生了 berry销毁了 ketty销毁了 */1.12 继承1.12.1 继承介绍继承使得代码具有层次结构子类继承了父类的属性和方法,实现了代码的可重用性。使用extends关键字实现继承父类和子类是相对的语法class 子类 extends 父类{ }例题<?php //父类 class Person { public function show() { echo '这是人类<br>'; } } //子类继承父类 class Student extends Person { } //测试 $stu=new Student; $stu->show(); //这是人类执行过程:第一步:在Student类中查找show(),如果找到就调用,找不到就到父类中查找第二步:在Person类中查询show()1.12.2 子类中调用父类成员<?php //父类 class Person { public function show() { echo '这是人类<br>'; } } //子类 class Student extends Person { public function test() { //方法一; /* $person=new Person(); $person->show(); //这是人类 */ //方法二 $this->show(); //这是人类 } } //测试 $stu=new Student; $stu->test();小结:1、方法一:通过实例化父类调用父类的成员2、方法二:通过$this关键字调用父类的成员1.12.3 protectedprotected:受保护的,在整个继承链上使用例题://例题一: <?php class A { protected $num=10; //在整个继承链上访问 } class B extends A { public function getNum() { echo $this->num; } } //测试 $obj=new B(); //整个继承链上有A和B $obj->getNum(); //10 //例题二: <?php class A { public function getNum() { echo $this->num; } } class B extends A { protected $num=10; } //测试 $obj=new B(); //整个继承链上有A和B $obj->getNum(); //10 //例题三: <?php class A { public function getNum() { echo $this->num; } } class B extends A { protected $num=10; } //测试 $obj=new A(); //整个继承链上只有A $obj->getNum(); //Notice: Undefined property: A::$num 1.12.4 继承中的构造函数规则:1、如果子类有构造函数就调用子类的,如果子类没有就调用父类的构造函数。 2、子类的构造函数调用后,默认不再调用父类的构造函数通过类名调用父类的构造函数类名::__construct()例题<?php class Person { //父类的构造函数 public function __construct() { echo '这是父类<br>'; } } class Student extends Person { //子类的构造函数 public function __construct() { Person::__construct(); //通过父类的名字调用父类的构造函数 parent::__construct(); //parent表示父类的名字 echo '这是子类<br>'; } } //测试 new Student();注意:parent关键字表示父类的名字,可以降低程序的耦合性例题:给父类传递参数<?php class Person { protected $name; protected $sex; //父类的构造函数 public function __construct($name,$sex) { $this->name=$name; $this->sex=$sex; } } class Student extends Person { private $score; //子类的构造函数 public function __construct($name,$sex,$score) { parent::__construct($name,$sex); //调用父类构造函数并传递参数 $this->score=$score; } //显示信息 public function getInfo() { echo "姓名:{$this->name}<br>"; echo "性别:{$this->sex}<br>"; echo "成绩:{$this->score}"; } } //测试 $stu=new Student('tom','男',88); $stu->getInfo(); /* 姓名:tom 性别:男 成绩:88 */1.12.5 $this详解$this表示当前对象的引用,也就是是或$this保存的当前对象的地址<?php class A { public function __construct() { var_dump($this); } } class B extends A { } new A(); //object(A)#1 (0) { } echo '<br>'; new B(); //object(B)#1 (0) { } 1.12.6 多重继承PHP不允许多重继承,因为多重继承容易产生二义性如何实现C继承A和B,使用继承链
2022年10月11日
29 阅读
0 评论
1 点赞
2022-10-11
PHP基础(5)
1.1 目标了解文件编程的作用;掌握指定目录的文件遍历;掌握文件内容的读写操作;掌握表单传值方式GET和POST的区别;掌握PHP接收表单传递数据的方式;理解复选框表单的设计思路;掌握PHP实现文件上传的方案;掌握PHP单文件上传函数的封装;1.2 文件操作1、将字符串写入文件<?php $str="床前明月光,\r\n疑是地上霜。\r\n举头望明月,\r\n低头思故乡。"; file_put_contents('./test.txt',$str); //将字符串写到文本中小结:1、 所有的“写”操作都是清空重写2、在文本中换行是\r\n\r:回车 光标移动到当前行的最前面 \n:换行 将光标下移动一行 按键盘的回车键做了两步,第一步将光标移动到当前行的最前面,第二步下移一行。3、\r\n是特殊字符,必须放在双引号内2、将整个文件读入一个字符串//方法一: echo file_get_contents('./test.txt'); //将整个文件读入一个字符串 //方法二: readfile('./test.txt'); //读取输出文件内容 //注意:echo file_get_contents()==readfile()3、打开文件并操作fopen(地址,模式) 打开文件 模式: r:读 read w:写 write a:追加 append例题://3.1、打开文件写入 /* $fp=fopen('./test.txt','w'); //打开文件返回文件指针(文件地址) //var_dump($fp); //resource(3) of type (stream) for($i=1;$i<=10;$i++) fputs($fp,'关关雎鸠'."\r\n"); //写一行 fclose($fp); //关闭文件 */ //3.2 打开文件读取 /* $fp=fopen('./test.txt','r'); //打开文件读取 while($line=fgets($fp)){ echo $line,'<br>'; } */ //3.3 打开文件追加 $fp=fopen('./test.txt','a'); //打开文件追加 fputs($fp,'在河之洲'); //在文件末尾追加小结:1、打开文件,返回文件指针(文件指针就是文件地址),资源类型2、打开文件写、追加操作,如果文件不存在,就创建新的文件3、打开文件读操作,文件不存在就报错4、fputs()写一行,fgets()读一行,fclose()关闭文件5、追加是在文件的末尾追加4、是否是文件【is_file()】echo is_file('./test.txt')?'是文件':'不是文件';5、判断文件或文件夹是否存在【file_exists()】echo file_exists('./test.txt')?'文件存在':'文件不存在';6、删除文件【unlink】$path='./test.txt'; if(file_exists($path)){ //文件存在 if(is_dir($path)) //如果是文件夹用rmdir()删除 rmdir($path); elseif(is_file($Path)) //如果是文件用unlink()删除 unlink($path); }else{ echo '文件夹或文件不存在'; }7、二进制读取【fread(文件指针,文件大小)】文件的存储有两种:字符流和二进制流二进制流的读取按文件大小来读的。$path='./face.jpg'; $fp=fopen($path,'r'); header('content-type:image/jpeg'); //告知浏览器下面的代码通过jpg图片方式解析 echo fread($fp,filesize($path)); //二进制读取多学一招:file_get_contents()也可以进行二进制读取header('content-type:image/jpeg'); echo file_get_contents('./face.jpg');小结:1、文本流有明确的结束符,二进制流没有明确的结束符,通过文件大小判断文件是否读取完毕2、file_get_contents()既可以进行字符流读取,也可以进行二进制读取。1.3 表单提交数据的两种方式1.3.1 两种方式1、get2、post<form method="post" action=""></form> <form method="get" action=""></form>1.3.2 区别1、外观上看 get提交在地址上可以看到参数 post提交在地址栏上看不到参数2、安全性 get不安全 post安全3、提交原理 get提交是参数一个一个的提交 post提交是所有参数作为一个整体一起提交4、提交数据大小 get提交一般不超过255个字节 post提交的大小取决于服务器// 在php.ini中,可以配置post提交的大小 post_max_size = 8M5、灵活性 get很灵活,只要有页面的跳转就可以传递参数 post不灵活,post提交需要有表单的参与1、 html跳转 <a href="index.php?name=tom&age=20">跳转</a> 2、JS跳转 <script type="text/javascript"> location.href='index.php?name=tom&age=20'; location.assign('index.php?name=tom&age=20'); location.replace('index.php?name=tom&age=20'); </script> 3、PHP跳转 header('location:index.php?name=tom&age=22')小结: GETPOST外观上在地址上看到传递的参数和值地址栏上看不到数据提交数据大小提交少量数据,不同的浏览器最大值不一样,IE是255个字符提交大量数据,可以通过更改php.ini配置文件来设置post提交数据的最大值安全性低高提交原理提交的数据和数据之间在独立的将提交的数据变成XML格式提交灵活性很灵活,只要有页面的跳转就可以get传递数据不灵活1.4 服务器接受数据的三种方式通过名字获取名字对应的值$_POST:数组类型,保存的POST提交的值 $_GET:数组类型,保存的GET提交的值 $_REQUEST:数组类型,保存的GET和POST提交的值例题:HTML页面<body> <!--表单提交数据--> <form method="get" action="./2-demo2.php"> 语文: <input type="text" name="ch"> <br /> 数学: <input type="text" name="math"> <br /> <input type="submit" name="button" value="提交"> <br><br> </form> <!--超链接提交数据--> <a href="2-demo2.php?ch=77&math=88">跳转</a> <br><br> <!--js提交数据--> <input type="button" value="点击" onclick="location.href='2-demo2.php?ch=66&math=55'"> <br><br> <input type="button" value="点击" onclick="location.assign('2-demo2.php?ch=11&math=22')"> </body>PHP页面<?php //post数组中不为空 if(!empty($_POST)) { echo '这是post提交的数据<br>'; echo '语文:'.$_POST['ch'],'<br>'; echo '数学:'.$_POST['math'],'<br>'; } echo '<hr>'; //获取get提交的数据 if(!empty($_GET)){ echo '这是get提交的数据<br>'; echo '语文:'.$_GET['ch'],'<br>'; echo '数学:'.$_GET['math'],'<br>'; } echo '<hr>'; //既能获取get又能获取post提交的数据 echo $_REQUEST['ch'],'<br>'; echo $_REQUEST['math'];思考题在一个请求中,既有get又有post,get和post传递的名字是一样的,这时候通过$_REQUET获取的数据是什么?答:结果取决于配置文件request_order = "GP" # 先获取GET,在获取POST值例题<?php if(!empty($_POST)){ echo '姓名:'.$_REQUEST['username'],'<br>'; } ?> <form method="post" action="?username=berry"> 姓名: <input type="text" name="username"><br /> <input type="submit" name="button" value="提交"> </form> 分析:先获取GET的username,再获取post的username,后面的将前面的值覆盖小结:1、在开发的时候,如果明确是post提交就使用$_POST获取,如果明确get提交就用$_GET获取2、request获取效率低,尽可能不要使用,除非提交的类型不确定的情况下才使用。1.5 参数传递1.5.1 复选框值的传递复选框的命名要注意带'[]'。<body> <?php if(isset($_POST['button'])) { print_r($_POST['hobby']); } ?> <form method="post" action=""> 爱好: <input type="checkbox" name="hobby[]" value='爬山'>爬山 <input type="checkbox" name="hobby[]" value='抽烟'>抽烟 <input type="checkbox" name="hobby[]" value='喝酒'>喝酒 <input type="checkbox" name="hobby[]" value='烫头'>烫头 <input type="submit" name="button" value="提交"> </form> </body>小结:1、表单提交到本页面需要判断一下是否有post提交2、数组的提交表单元素的名字必须带有[]。1.5.2 例题<body> <?php if(isset($_POST['button'])) { echo '姓名:'.$_POST['username'].'<br>'; echo '密码:'.$_POST['pwd'].'<br>'; echo '性别:'.$_POST['sex'].'<br>'; echo '爱好:',isset($_POST['hobby'])?implode(',',$_POST['hobby']):'没有爱好','<br>'; echo '籍贯:'.$_POST['jiguan'],'<br>'; echo '留言:'.$_POST['words']; } ?> <form method="post" action=""> 姓名: <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='0'>女 <br /> 爱好: <input type="checkbox" name="hobby[]" value='爬山'>爬山 <input type="checkbox" name="hobby[]" value='抽烟'>抽烟 <input type="checkbox" name="hobby[]" value='喝酒'>喝酒 <input type="checkbox" name="hobby[]" value='烫头'>烫头 <br /> 籍贯: <select name="jiguan"> <option value="021">上海</option> <option value="010">北京</option> </select> <br> 留言: <textarea name="words" rows="5" cols="30"></textarea> <br /> <input type="submit" name="button" value="提交"> </form> </body>运行结果1.6 文件上传开发中需要上传图片、音乐、视频等等,这种上传传递是二进制数据。1.6.1 客户端上传文件文件域<input type="file" name="image">表单的enctype属性 默认情况下,表单传递是字符流,不能传递二进制流,通过设置表单的enctype属性传递复合数据。enctype属性的值有:application/x-www-form-urlencoded:【默认】,表示传递的是带格式的文本数据。multipart/form-data:复合的表单数据(字符串,文件),文件上传必须设置此值text/plain:用于向服务器传递无格式的文本数据,主要用户电子邮件单词multipart:复合 form-data:表单数组1.6.2 服务器接受文件超全局变量$_FILES是一个二维数组,用来保存客户端上传到服务器的文件信息。二维数组的行是文件域的名称,列有5个。1、$_FILES[][‘name’]:上传的文件名2、$_FILES[][‘type]:上传的类型,这个类型是MIME类型(image/jpeg、image/gif、image/png)3、$_FILES[][‘size’]:文件的大小,以字节为单位4、$_FILES[][‘tmp_name’]:文件上传时的临时文件5、$_FILES[][‘error’]:错误编码(值有0、1、2、3、4、6、7)0表示正确$_FILES[][‘error’]详解值错误描述0正确1文件大小超过了php.ini中允许的最大值 upload_max_filesize = 2M2文件大小超过了表单允许的最大值3只有部分文件上传4没有文件上传6找不到临时文件7文件写入失败注意:MAX_FILE_SIZE必须在文件域的上面。只要掌握的错误号:0和41.6.3 将上传文件移动到指定位置函数:move_uploaded_file(临时地址,目标地址)代码<body> <?php if(!empty($_POST)) { if($_FILES['face']['error']==0){ //上传正确 //文件上传 move_uploaded_file($_FILES['face']['tmp_name'],'./'.$_FILES['face']['name']); }else{ echo '上传有误'; echo '错误码:'.$_FILES['face']['error']; } } ?> <form method="post" action="" enctype='multipart/form-data'> <input type="file" name="face"> <input type="submit" name="button" value="上传"> </form> </body>小结:上传的同名的文件要给覆盖1.6.4 与文件上传有关的配置post_max_size = 8M:表单允许的最大值upload_max_filesize = 2M:允许上传的文件大小upload_tmp_dir =F:\wamp\tmp:指定临时文件地址,如果不知道操作系统指定file_uploads = On:是否允许文件上传max_file_uploads = 20:允许同时上传20个文件1.7 优化文件上传1.7.1 更改文件名方法一:通过时间戳做文件名<?php $path='face.stu.jpg'; //echo strrchr($path,'.'); //从最后一个点开始截取,一直截取到最后 echo time().rand(100,999).strrchr($path,'.'); 方法二:通过uniqid()实现$path='face.stu.jpg'; echo uniqid().strrchr($path,'.'),'<br>'; //生成唯一的ID echo uniqid('goods_').strrchr($path,'.'),'<br>'; //带有前缀 echo uniqid('goods_',true).strrchr($path,'.'),'<br>'; //唯一ID+随机数1.7.2 验证文件格式方法一:判断文件的扩展名(不能识别文件伪装)操作思路:将文件的后缀和允许的后缀对比<body> <?php if(!empty($_POST)) { $allow=array('.jpg','.png','.gif'); //允许的扩展名 $ext=strrchr($_FILES['face']['name'],'.'); //上传文件扩展名 if(in_array($ext,$allow)) echo '允许上传'; else echo '文件不合法'; } ?> <form method="post" action="" enctype='multipart/form-data'> <input type="file" name="face"> <input type="submit" name="button" value="上传"> </form> </body>注意:比较扩展名不能防止文件伪装。方法二:通过$_FIELS[]['type']类型(不能识别文件伪装)<body> <?php if(!empty($_POST)) { $allow=array('image/jpeg','image/png','image/gif'); //允许的类别 $mime=$_FILES['face']['type']; //上传文件类型 if(in_array($mime,$allow)) echo '允许上传'; else echo '文件不合法'; } ?> <form method="post" action="" enctype='multipart/form-data'> <input type="file" name="face"> <input type="submit" name="button" value="上传"> </form> </body>注意:比较$_FIELS[]['type']不能防止文件伪装。方法三:php_fileinfo扩展(可以防止文件伪装) 在php.ini中开启fileinfo扩展extension=php_fileinfo.dll注意:开启fileinfo扩展以后,就可以使用finfo_*的函数了<body> <?php if(!empty($_POST)) { //第一步:创建finfo资源 $info=finfo_open(FILEINFO_MIME_TYPE); //var_dump($info); //resource(2) of type (file_info) //第二步:将finfo资源和文件做比较 $mime=finfo_file($info,$_FILES['face']['tmp_name']); //第三步,比较是否合法 $allow=array('image/jpeg','image/png','image/gif'); //允许的类别 echo in_array($mime,$allow)?'合法':'不合法'; } ?> <form method="post" action="" enctype='multipart/form-data'> <input type="file" name="face"> <input type="submit" name="button" value="上传"> </form> </body>小结:验证文件格式有三种方法1、可以验证扩展名(不可以防止文件伪装)2、通过$_FILES[]['type']验证(不可以防止文件伪装)3、通过file_info扩展(可以防止文件伪装)1.7.3 优化文件上传例题步骤第一步:验证是否有误第二步:验证格式第三步:验证大小第四步:验证是否是http上传第五步:上传实现<body> <?php /** *验证错误 *如果有错,就返回错误,如果没错,就返回null */ function check($file) { //1:验证是否有误 if($file['error']!=0){ switch($file['error']) { case 1: return '文件大小超过了php.ini中允许的最大值,最大值是:'.ini_get('upload_max_filesize'); case 2: return '文件大小超过了表单允许的最大值'; case 3: return '只有部分文件上传'; case 4: return '没有文件上传'; case 6: return '找不到临时文件'; case 7: return '文件写入失败'; default: return '未知错误'; } } //2、验证格式 $info=finfo_open(FILEINFO_MIME_TYPE); $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'; } //4、验证是否是http上传 if(!is_uploaded_file($file['tmp_name'])) return '文件不是HTTP POST上传的<br>'; return null; //没有错误 } //表单提交 if(!empty($_POST)) { //上传文件过程中有错误就显示错误 if($error=check($_FILES['face'])){ echo $error; }else{ //文件上传,上传的文件保存到当天的文件夹中 $foldername=date('Y-m-d'); //文件夹名称 $folderpath="./uploads/{$foldername}"; //文件夹路径 if(!is_dir($folderpath)) mkdir($folderpath); $filename=uniqid('',true).strrchr($_FILES['face']['name'],'.'); //文件名 $filepath="$folderpath/$filename"; //文件路径 if(move_uploaded_file($_FILES['face']['tmp_name'],$filepath)) echo "上传成功,路径是:{$foldername}/{$filename}"; else echo '上传失败<br>'; } } ?> <form method="post" action="" enctype='multipart/form-data'> <input type="file" name="face"> <input type="submit" name="button" value="上传"> </form> </body>运行结果小结:1、将时间戳转换格式echo date('Y-m-d H:i:s',1231346),'<br>'; //将时间戳转成年-月-日 小时:分钟:秒 echo date('Y-m-d H:i:s'),'<br>'; //将当前的时间转成年-月-日 小时:分钟:秒2、设置时区(php.ini)PRC:中华人民共和国3、PHP的执行可以不需要Apache的参与1.8 练习1、多文件上传1.9 练习讲解1、递归遍历文件夹<?php //获取文件夹的子级 function getFile($path) { $folder=opendir($path); //打开文件夹 echo '<ul>'; while($f=readdir($folder)){ //读取文件夹 if($f=='.' || $f=='..') continue; echo '<li>'.iconv('gbk','utf-8',$f).'</li>'; $subpath="{$path}/{$f}"; if(is_dir($subpath)) //如果子级还是文件夹,继续打开并读取 getFile($subpath); } echo '</ul>'; } //测试 getFile('./');运行结果2、一只猴子看守一堆桃子,第一天吃了一半后又多吃了1个,第二天一样,到第十天的时候就剩下一个桃子,请问原来有几个桃子?分析f(n)-(f(n)/2+1)=f(n+1) =>f(n)/2-1=f(n+1) =>f(n)=(f(n+1)+1)*2代码实现<?php function getTao($n) { if($n==10) return 1; return (getTao($n+1)+1)*2; } echo getTao(1); //1534
2022年10月11日
13 阅读
0 评论
1 点赞
2022-10-11
PHP基础(4)
1.1 目标掌握静态变量的工作原理;了解全局变量、局部变量和超全局变量的区别;了解两种错误处理机制;掌握错误相关信息的三种配置方式;掌握自定义函数实现错误处理;理解四种包含文件方式的区别;理解文件包含的运行原理;掌握文件包含的不同应用场景:1.2 作用域1.2.1 变量作用域1、全局变量:在函数外面2、局部变量:在函数里面,默认情况下,函数内部是不会去访问函数外部的变量3、超全局变量:可以在函数内部和函数外部访问<?php $num=10; function fun() { echo $num; //Notice: Undefined variable: num } fun(); //函数内部默认不能访问函数外部的值 --------------------- <?php $_POST['num']=10; //将值付给超全局变量 function fun() { echo $_POST['num']; //获取超全局的值 10 } fun(); ---------------------------- <?php function fun() { $_GET['num']=10; //将值付给超全局变量 } fun(); echo $_GET['num']; //打印超全局变量的值 10在函数内部访问全局变量<?php $num=10; //全局变量 function fun() { echo $GLOBALS['num']; //输出全局的$num } fun();练习:如下代码输出什么<?php function fun() { $GLOBALS['num']=10; //将值付给全局的$num } fun(); echo $num; //10global关键字<?php $num=10; function fun() { global $num; //将全局变量的$num的地址引入到函数内部 相当于$num=&GLOBALS['num'] echo $num; //10 $num=100; } fun(); echo '<br>'; echo $num; //100 ----------------------------------- <?php $num=10; function fun() { global $num; unset($num); //销毁的是引用,不是具体的值 } fun(); echo $num; //10 小结:1、$GLOBALS保存的是全局变量的所有的值<?php $a=10; $b=20; function show() { echo '<pre>'; var_dump($GLOBALS); //是一个数组,保存的是全局变量的所有的值 } show();2、global用于创建一个全局变量的引用注意:常量没有作用域的概念<?php /* define('PI',3.14); function fun() { echo PI; //3.14 } fun(); echo '<br>'; */ ------------------------------------- function fun() { define('PI',3.14); } fun(); echo PI; //3.141.2.2 静态变量(static)静态变量一般指的是静态局部变量。静态变量只初始化一次<?php function fun() { $num=10; //普通变量每调用一次初始化一次,调用完毕销毁 $num++; echo $num,'<br>'; } fun(); //11 fun(); //11 -------------------------------- <?php function fun() { static $num=10; //静态变量只初始化一次,调用完毕吧不销毁,第二次调用的时候就不再初始化 $num++; echo $num,'<br>'; } fun(); //11 fun(); //12常量和静态变量的区别1、常量和静态变量都是初始化一次2、常量不能改变值,静态变量可以改变值3、常量没有作用域,静态变量有作用域<?php function fun1() { define('num',10); } function fun2() { echo num; //10 } fun1(); fun2(); ------------------------------------------------------------ <?php function fun1() { static $num=10; } function fun2() { echo $num; //Notice: Undefined variable: num 因为静态变量是有作用域的 } fun1(); fun2();1.2.3 匿名函数use()默认情况下,函数内部不能访问函数外部的变量,但在匿名函数中,可以通过use将外部变量引入匿名函数中<?php $num=10; $fun=function() use($num) { //将$num引入到匿名函数中 echo $num; }; $fun(); //10思考:如何在函数内部访问函数外部变量1、使用超全局变量2、$GLOBALS3、global4、use将函数外部变量引入到匿名函数内部练习:如果代码输出什么<?php $num=10; function test() { $num=20; $fun=function() use($num) { //只能引入一层 echo $num; }; $fun(); } test(); //20多学一招:use可以引入值,也可以引入地址<?php $num=10; $fun=function()use(&$num){ //use可以传地址 $num=100; }; $fun(); echo $num; //1001.3 递归函数内部自己调用自己递归有两个元素,一个是递归点(从什么地方递归),第二递归出口例题1:输出9 8 7 6 .....<?php function printer($num) { echo $num,' '; if($num==1) //递归出口 return; printer($num-1); //递归点 } printer(9); //9 8 7 6 5 4 3 2 1 例题2:从1加到100function cal($num) { if($num==1) return 1; return $num+cal($num-1); } echo cal(100); //分析 /** 第$i次执行 结果 cal(100) 100+cal(99) = 100+99+cal(98) = 100+99+98+cal(97) = 100+99+98+++++cal(1) = 100+99+98++++1 */例题:打印前10个斐波那契数列//打印第5个斐波那契数 function fbnq($n) { if($n==1 || $n==2) return 1; return fbnq($n-1)+fbnq($n-2); //第n个斐波那契数等于前两个数之和 } echo fbnq(5),'<br>'; /** *分析: fbnq(5) =fbnq(4)+fbnq(3) =fbnq(3)*2+fbnq(2) =(fbnq(2)+fbnq(1))*2+fbnq(2) =(1+1)*2+1 =5 */ //打印前10个斐波那契数 for($i=1;$i<=10;$i++) echo fbnq($i),' '; //1 1 2 3 5 8 13 21 34 55 小结:递归尽量少用,因为递归需要用到现场保护,现场保护是需要消耗资源的1.4 包含文件场景:1.4.1 包含文件的方式1、require:包含多次2、include:包含多次3、require_once: 包含一次4、include_once: 包含一次小结:1、require遇到错误抛出error类别的错误,停止执行2、include遇到错误抛出warning类型的错误,继续执行3、require_once、include_once只能包含一次4、HTML类型的包含页面中存在PHP代码,如果包含到PHP中是可以被执行的5、包含文件相当于把包含文件中的代码拷贝到主文件中执行,魔术常量除外,魔术常量获取的是所在文件的信息。6、包含在编译时不执行、运行时加载到内存、独立编译包含文件1.4.2 包含文件的路径./ 当前目录 ../ 上一级目录区分如下包含:require './head.html'; //在当前目录下查找 require 'head.html'; //受include_path配置影响include_path的使用场景:如果包含文件的目录结构比较复杂,比如:在c:\aa\bb\cc\dd中有多个文件需要包含,可以将包含的路径设置成include_path,这样包含就只要写文件名就可以了<?php set_include_path('c:\aa\bb\cc\dd'); //设置include_path require 'head1.html'; //受include_path配置影响 require 'head2.html';include_path可以设置多个,路径之间用分号隔开set_include_path('c:\aa\bb\cc\dd;d:\\');多学一招:正斜(/) web中目录分隔用正斜 http://www.sina.com/index.php 反斜(\)物理地址的分隔用反斜,(windows中物理地址正斜和反斜都可以) c:\web1\aa1.5 错误处理1.5.1 错误的级别notice:提示warning:警告error:致命错误notice和warning报错后继续执行,error报错后停止执行1.5.2 错误的提示方法方法一:显示在浏览器上方法二:记录在日志中1.5.3 与错误处理有关的配置在php.ini中1. error_reporting = E_ALL:报告所有的错误 2. display_errors = On:将错误显示在浏览器上 3. log_errors = On:将错误记录在日志中 4. error_log=’地址’:错误日志保存的地址在项目开发过程中有两个模式,开发模式,运行模式开发模式:错误显示在浏览器上,不要记录在日志中 运行模式:错误不显示在浏览器上,记录是日志中例题<?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'); //错误日志保存的地址 } //测试 echo $num;提示:ini_set()设置PHP的配置参数1.5.4 自定义错误处理(了解)通过trigger_error产生一个用户级别的 error/warning/notice 信息<?php $age=100; if($age>80){ //trigger_error('年龄不能超过80岁'); //默认触发了notice级别的错误 //trigger_error('年龄不能超过80岁',E_USER_NOTICE); //触发notice级别的错误 //trigger_error('年龄不能超过80岁',E_USER_WARNING); trigger_error('年龄不能超过80岁',E_USER_ERROR); //错误用户error错误 }注意:用户级别的错误的常量名中一定要带有USER。定义错误处理函数function error() { echo '这是自定义错误处理'; } set_error_handler('error'); //注册错误处理函数,只要有错误就会自动的调用错误处理函数 echo $num;运行结果处理处理函数还可以带有参数/** *自定义错误处理函数 *@param $errno int 错误类别 *@param $errstr string 错误信息 *@param $errfile string 文件地址 *@param $errline int 错误行号 */ function error($errno,$errstr,$errfile,$errline) { switch($errno){ case E_NOTICE: case E_USER_NOTICE: echo '记录在日志中,上班后在处理<br>'; break; case E_WARNING: case E_USER_WARNING: echo '给管理员发邮件<br>'; break; case E_ERROR: case E_USER_ERROR: echo '给管理员打电话<br>'; break; } echo "错误信息:{$errstr}<br>"; echo "错误文件:{$errfile}<br>"; echo "错误行号:{$errline}<br>"; } set_error_handler('error'); echo $num; //运行结果 记录在日志中,上班后在处理 错误信息:Undefined variable: num 错误文件:F:\wamp\www\4-demo.php 错误行号:501.6 文件编程1.6.1 文件夹操作1 、创建文件夹【mkdir(路径,权限,是否递归创建)】make:创建 directory:目录,文件夹例题<?php //1、创建目录 //mkdir('./aa'); //创建aa文件夹 //mkdir('./aa/bb'); //在aa目录下创建bb(aa目录必须存在) mkdir('./aa/bb/cc/dd',0777,true); //递归创建小结:1、0777表示是文件夹的权限,在Linux中会详细讲解2、true表示递归创建,默认是false2、删除文件夹【rmdir()】//remove:移除 rmdir('./aa/bb/cc/dd'); //删除dd文件夹提醒:1、删除的文件夹必须是空的 2、PHP基于安全考虑,没有提供递归删除。3、重命名文件夹【rename(旧名字,新名字)】rename('./aa','./aaa'); //将aa改为aaa4、是否是文件夹【is_dir()】echo is_dir('./aaa')?'是文件夹':'不是文件夹';5、打开文件夹、读取文件夹、关闭文件夹$folder=opendir('./'); //打开目录 //var_dump($folder); //resource(3) of type (stream) while($f=readdir($folder)){ //读取文件夹 if($f=='.' || $f=='..') continue; echo iconv('gbk','utf-8',$f),'<br>'; //将gbk转成utf-8 } closedir($folder); //关闭文件夹小结:1、opendir()返回资源类型 2、每个文件夹中都有.和.. 3、iconv()用来做字符编码转换1.7 练习讲解1、 通过for循环将数组中值求和、求平均值<?php //1、求数组的和、平均值 $num=[1,20,53,23,14,12,15]; $sum=0; for($i=0,$n=count($num);$i<$n;$i++){ $sum+=$num[$i]; } echo '和是:'.$sum,'<br>'; //和是:138 echo '平均值:'.number_format($sum/count($num),1); //精确到小数点后面1位 平均值:19.7 echo '<hr>';2、数组翻转$stu=['tom','berry','ketty','rose','jake']; for($i=0,$j=count($stu)-1;$i<$j;$i++,$j--){ [$stu[$i],$stu[$j]]=[$stu[$j],$stu[$i]]; //元素交换 } print_r($stu); //Array ( [0] => jake [1] => rose [2] => ketty [3] => berry [4] => tom ) 3、遍历二维数组$stu=[ [1,2,3,4], [10,20,30,40] ]; for($i=0;$i<count($stu);$i++){ //循环第一列 for($j=0;$j<count($stu[$i]);$j++){ //循环第二列 echo $stu[$i][$j],' '; } echo '<br>'; } //运行结果 1 2 3 4 10 20 30 40 4、 循环输出1-100,其中3的倍数输出A,5的倍数输出B,15输出C。for($i=1; $i<=100; $i++) { if($i%15==0) //先写%15,,因为可以%15的值一定可以%3和%5 echo 'C'; elseif($i%3==0) echo 'A'; elseif($i%5==0) echo 'B'; else echo $i; echo ' '; }5、 打印水仙花数for($i=100;$i<=999;$i++){ $a=(int)($i/100); //百位数 $b=(int)(($i%100)/10); //十位数 $c=$i%10; //个位数 if($i==pow($a,3)+pow($b,3)+pow($c,3)) echo $i,'<br>'; } //pow($a,3) 表示$a的三次方 //运行结果 153 370 371 4076、 打印100以内的斐波那契数(迭代法)1 1 2 3 5 8 13 21 .....$num1=1; //第一个数 $num2=1; //第二个数 echo $num1,' ',$num2,' '; while(true){ $num3=$num1+$num2; //第三个数是前面两个数的和 if($num3>100) //超过100就终止循环 break; echo $num3,' '; $num1=$num2; //将$num2移给$num1 $num2=$num3; //将$num3移给$num2 } //1 1 2 3 5 8 13 21 34 55 89 1.8 练习1、一只猴子看守一堆桃子,第一天吃了一半后又多吃了1个,第二天一样,到第十天的时候就剩下一个桃子,请问原来有几个桃子?2、递归遍历整个文件夹
2022年10月11日
15 阅读
0 评论
0 点赞
1
2
3