一、 PHP简介
php环境
php-cli command line interface php在命令行运行的接口,区分于在web服务器上运行的php环境(php-cgi,isapi),php不单可以写前台网页,也可以用来写后台的程序。swoole
认识PHP
PHP Hypertext Preprecessor 超文本预处理器 可以嵌入到html代码中的
PHP通用开源动态脚本语言,运行在服务端,适合高效快捷的进行web开发
通常用于创建动态生成的网页,支持常用的数据库 MySql, mariaDB ,Oracle, MongoDB
php发展史
PHP Personal Home Page 1994 Perl Rasmus Lerdorf 来统计个人网站访问量的程序,C重写后,1995 php1 php2 加入了对mysql
php5 2004-7 引入了面向对象编程 5.4 trait 5.6
php7 2015-12 性能是 php5.6提高了2倍
php7.4 提供了pre-loading预加载机制,实现了部分程序的常驻内存
4 2020-11 PHP8.0问世 高效的JIT just-in-time(即使编译)运算支持,PHP 已经应用到 websocket服务,高性能web,物联网,即时通讯,游戏,微服务等非web领域的系统开发
PHP 工作运行原理
二、 PHP语法第一课
PHP include request区别
(1)、include 遇到错误还继续,非常适合模版
(2)、request遇到错误就停止
include: 加载失败不中断 常用于加载模板或页面
require: 加载失败中断,常用于加载对象或数据
PHP 函数不区分大小写、php变量大小敏感
索引数组
关联数组
二维数组遍历
for
foreach
list()数组找那个的值赋给一组变量
extract
php 变量
1. 数据类型转换
转换方式 | 函数 |
---|---|
临时转换 | (int),(string),(array),(object)… |
自动转换 | 根据操作符对操作数的类型要求进行 |
强制转换 | settype() |
PHP的数据类型转换属于强制转换,允许转换的PHP数据类型有:
(int)、(integer):转换成整形
(float)、(double)、(real):转换成浮点型
(string):转换成字符串
(bool)、(boolean):转换成布尔类型
(array):转换成数组
(object):转换成对象
第一种转换方式: (int) (bool) (float) (string) (array) (object)
$num1=3.14;
$num2=(int)$num1;
var_dump($num1); //输出float(3.14)
var_dump($num2); //输出int(3)
?>
第二种转换方式: intval() floatval() strval()
$str=”123.9abc”;
$int=intval($str); //转换后数值:123
$float=floatval($str); //转换后数值:123.9
$str=strval($float); //转换后字符串:”123.9”
?>
第三种转换方式: settype();
$num4=12.8;
$flg=settype($num4,”int”);
var_dump($flg); //输出bool(true)
var_dump($num4); //输出int(12)
?>
2. 类型检测
转换方式 | 函数 |
---|---|
标量 | is_int(),is_string(),is_bool()… |
复合 | is_array(),is_object() |
特殊 | is_null(),is_resource() |
PHP有很多资源类型:数据库连接、结果、图像、PDF等
is_resource():判断一个变量是否是一个资源类型
null讲解
@var_dump 有错误可以忽略不在页面显示
null 类型不等于下面任何:
false(布尔值) 0(数字类型) ‘’(字符串)
如何能获得null 三种方法
1、直接赋值$var =nul
2、unset($fp)
3、var_dump($emp) $emp 不存在
3. 变量命名
- 仅允许使用: 英文字母(a-z,A-Z),数字(0-9),下划线(_)或他们的组合
- 不允许使用数字开头
- 必须使用”$”美元符为前缀
- 严格区分大小写
- 驼峰命名法
4. 常量
- define(): 函数
- const: 关键字
- 固定的值(圆周率), 常量不能重新被定义 或者取消定义
- 常量的命名前不能加 $
- 常量推荐全部大写,多个单词之间用下划线连接_
- 变量不属于全局成员, 常量、类、接口、函数属于全局成员 不受作用域限制
- 5、类常量只能用const定义
常量类型
系统常量 魔术常量 DIR
const APP_PATH = '/phpEnv/www/chloe.io/0302';
echo APP_PATH;
define('NATION', '中国');
echo NATION;
printf('国籍:%s', NATION);
class User
{
//类常量只能用const定义
const APP_PATH = '/phpEnv/www/chloe.io/0302';
}
// 常量必须初始化赋值
$height;
const HEIGHT = '183cm';
ob_clean();
// 系统常量
// print_r(get_defined_constants(true));
// 魔术常量
echo __FILE__; //当前执行PHP脚本的完整路径和文件名, 绝对路径
echo dirname(__FILE__); //当前执行PHP脚本的完整路径
echo __DIR__;//当前执行PHP脚本的完整路径
5. 全局变量 无法在函数内部直接调用
//系统预定义变量
// 超全局变量: 数组,任何地方都可以访问,不受作用域限制
// $GLOBALS: 引用全局作用域中可用的全部变量
// $_SERVER: 服务器和执行环境信息
// $_GET :通过 URL 参数(又叫 query string)传递给当前脚本的变量的数组
// $_POST:当 HTTP POST 请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 时,会将变量以关联数组形式传入当前脚本
// $_FILES:通过 HTTP POST 方式上传到当前脚本的项目的数组
// $_COOKIE:通过 HTTP Cookies 方式传递给当前脚本的变量的数组
// $_SESSION:当前脚本可用 SESSION 变量的数组
// $_REQUEST: 默认情况下包含了 $_GET,$_POST 和 $_COOKIE 的数组
// $_ENV: 通过环境方式传递给当前脚本的变量的数组
流程控制
1. 单一条件分支结构
if (1 == 1) {
echo 111;
}
2.双向条件分支结构 if…else
if(条件表达式)
{
#满足条件执行的代码块
} else{
不满足条件执行的代码块
}
3. 多向条件分支结构
$age = 85;
$gender = '女';
if ($gender == '女' && $age < 55) :
echo "您好女士,距离您退休还有" . (55 - $age) . "年<br>";
elseif ($gender == '女' && $age > 55) :
echo "您好女士,您已经退休" . ($age - 55) . "年了<br>";
elseif ($gender == '男' && $age < 60) :
echo "先生您好,距离您退休还有" . (60 - $age) . "年<br>";
elseif ($gender == '男' && $age > 60) :
echo "先生您好 ,您已经退休" . ($age - 60) . "年了<br>";
else :
echo '很抱歉,您输入的信息不正确';
endif;
ob_clean();
$d = date("w"); //0-6
switch ($d):
case 1:
echo '今天是工作日星期一';
break;
case 2:
echo '今天是工作日星期二';
break;
case 3:
echo '今天是工作日星期三';
break;
case 4:
echo '今天是工作日星期四';
break;
case 5:
echo '今天是工作日星期五';
break;
default:
echo "今天休息!";
endswitch;
// break 退出当前循环 continue 跳过此次循环,进入下一轮循环
php 模版
$nav = '';
for ($i = 0; $i < count($navs); $i++) {
$nav .= "<a href='{$navs[$i]['url']}' style='padding: 0 15px' >{$navs[$i]['name']}</a>";
}
js 模版
// 商品信息模板
const tr = `
<tr>
<td><input type="checkbox" name="" class="check" checked /></td>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.unit}</td>
<td class="price">${item.price}</td>
<td><input type="number" name="" value="${item.num}" min="1"></td>
<td class="money">${item.money}</td>
</tr>
`
运算符
<?php
/**
* ; php指令分隔符
* php 功能执行语句 必须以;结束
* 结构定义语句
*
*/
//! PHP运算符
function demo()
{
}
class demo
{
}
// 算数运算符 + - * / % 去余
// 能被4整除但不能被100整除的都是闰年,其余的年份均为平年
$year = date("Y");
// echo $year;
if ($year % 4 == 0 && $year % 100 !== 0) {
echo "{$year}是闰年";
} else {
echo "{$year}是平年";
}
// 控制随机数的范围 0-50
// echo mt_rand() % 50;
echo mt_rand(0, 50);
// 比较运算符 返回布尔型 用在流程控制语句
var_dump(11 > 10);
// 逻辑运算符 || && ! 返回布尔型 用在流程控制语句
/**
* || 或 两边的表达式只要一个为true 返回true
*
* && 且 两边的表达式全部为true 返回true
*
* !
* */
/**
* = 赋值运算符
* == 值比较
* === 严格比较 值 类型
*/
var_dump(1 > 0 || 4 > 5);
var_dump(0 === '0');
$username = 'admin';
if (isset($username) && $username === 'admin') {
echo "欢迎管理员{$username}回来";
}
$email = $_GET['email'];
if (isset($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo '邮箱验证通过';
} else {
echo '请输入有效的邮箱';
}
ob_clean();
//三元运算符 表达式 ? :
// $pwd = sha1('wwwphpcn');
$pwd = isset($pwd) ? $pwd : 'root';
// 检测页码
// $page = isset($_GET['p']) ? $_GET['p'] : 1;
// ?? null合并运算符 告别isset()
$page = @$_GET['p'] ?? 1;
echo $page;
以前的写法
$info = isset($_GET[’email’]) ? $_GET[’email’] : ‘noemail’;
现在直接这样写就可以了
$info = $_GET[’email’] ?? noemail;
循环
<?php
// ! 循环 while
$cities = ['合肥', '南京', '杭州', '苏州', '上海', '北京'];
next($cities);
next($cities);
next($cities);
next($cities);
reset($cities);
// echo current($cities);
// while循环 入口判断型循环
// while ($city = current($cities)) {
// echo $city . '<br>';
// next($cities);
// }
// while ($city = current($cities)) :
// echo $city . '<br>';
// next($cities);
// endwhile;
// do..while出口型判断循环
// $i = 0;
// do {
// echo $cities[$i];
// $i++;
// } while ($i < count($cities));
// for 计数型循环
// for ($i = 0; $i < count($cities); $i++) {
// // break 提前结束循环
// if ($i > 2) break;
// echo $cities[$i];
// }
// continue 跳出某一轮循环 选择性输出
for ($i = 0; $i < count($cities); $i++) {
if ($i == 2 || $i == 3) continue;
echo $cities[$i];
}
foreach ($cities as $k => $v) {
}
session cokioo
http: 无状态
cookie 储存用户信息在客户端
<img src="cookie.png">
cookie特点
- 保存在客户端,容易被篡改(删除、禁用)
- 保存的数据大小受限,本身最大4kb
session 储存用户信息在服务端
<img src="session.png">
- 会话 ID: 服务器设置,保存在客户端
cookie
3. php 会话流程
- 客户端->服务器: http 请求
- 服务器->客户端: 设置会话 ID(cookie)
- 客户端->服务器: http 请求(会话 ID)
- 服务器->客户端: 验证会话,状态保持
格式化输出
/**
* 格式化输出
* 1. printf(format,a,b,...): 打印(离散参数)
* 2. vprintf(format,array): 打印(数组参数)
* 3. sprintf(format,a,b,...): 只返回不打印
* 4. vsprintf(format,array): 只返回不打印
* 速记法:
* s: 只返回不打印
* v: 数组参数
*/
// printf('format',a,b,c);
printf('<div style="color:red">%s</div>', 'php中文网');
echo sprintf('<div style="color:red">%s</div>', 'php中文网');
函数相关
1. 函数
- 声明
- 调用
- 参数
- 返回值
```
function add($a, $b, $c)
{
return $a + $b + $c;
}
$num=[2, 3];
echo add(1, …$num); //6
function boo(…$args)
{
// var_dump(func_get_args());
// var_dump(func_num_args());
// var_dump(func_get_arg(3));
// if (func_num_args() >= 2) echo “第二个参数是” . func_get_arg(1);
return array_sum($args);
}
boo(15, 25, 57, 256, 89);
// 引用变量传递是地址
$d = &$c;
echo $d; //20;
$c = 30;
echo $d; //30
$d = 40;
ob_clean();
echo $c;
### 2. 特殊形式的函数
#### 1. 匿名函数
$loo = function ($i) {
echo "插入数据{$i}\n";
return true;
}
$loo(5)
#### 2. 回调函数
所谓的回调函数,就是指调用函数时并不是向函数中传递一个标准的变量作为参数,而是将另一个函数作为参数传递到调用的函数中,这个作为参数的函数就是回调函数。
通俗的来说,回调函数也是一个我们定义的函数,但是不是我们直接来调用的,而是通过另一个函数来调用的,这个函数通过接收回调函数的名字和参数来实现对它的调用。
当函数名或参数, 可能是由任意表达式生成或来自网络请求时非常有用, 例如类方法, 高阶函数,随机参数或参数来自一个web请求等的场景下, 别外, 如果需要对传参预处理时, 也比直接调用更简洁灵活
call_user_func()
call_user_func_array()
func_get_args()
func_num_args()
func_get_arg(3)
function boo(…$args)
{
// var_dump(func_get_args());
// var_dump(func_num_args());
// var_dump(func_get_arg(3));
// if (func_num_args() >= 2) echo “第二个参数是” . func_get_arg(1);
return array_sum($args);
}
boo(15, 25, 57, 256, 89);
/ //$b=’今天’;
// function boo (…$a) {
// //echo $a ;
// var_dump($a);
// };
// call_user_func(‘boo’,[15, 25, 57, 256, 89]); //展开
// echo call_user_func_array(‘boo’, [15, 25, 57, 256, 89]); //展开
#### 3. 递归函数
自己调用自己就是递归函数。老师说数状结构 文件夹访问就是
一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出
补充知识点
1. opendir($path) 打开目录句柄,如果成功则返回目录句柄的 resource, 或者在失败时返回 false。
2. readdir($dir_handle)从目录句柄中读取条目,目录句柄的 resource,之前由 opendir() 打开,成功则返回条目名 或者在失败时返回 false。
3. closedir($dir_handle) — 关闭目录句柄,没有返回值。
4. is_dir($filename)判断给定文件名是否是一个目录,如果文件名存在,并且是个目录,返回 true,否则返回false。
5. unlink($filename) — 删除文件,成功时返回 true, 或者在失败时返回 false。
6. rmdir($directory) — 尝试删除 directory 所指定的目录。 该目录必须是空的,而且要有相应的权限。 失败时会产生一个 E_WARNING 级别的错误。成功时返回 true, 或者在失败时返回 false。
// ! 递归函数
// 递归函数是指直接或间接的调用函数自身的函数
// ! 必须有一个终止处理或计算的准则;
function demo($a = 1)
{
if ($a <= 5) {
echo “第{$a}次执行<br>“;
$a++;
demo($a);
}
}
demo();
/**
- 函数是完成特定功能的代码块
*/
// 递归函数
/**
- delete_dir_file 删除指定目录
- params: 指定需要删除的目录路径
- return:返回布尔值 成功true 失败false
- */
if (!function_exists(‘delete_dir_file’)) {
function delete_dir_file($dir)
{
//声明一个初始状态 默认情况下缓存未被删除
$flag = false;
if (is_dir($dir)) {
if ($handle = opendir($dir)) {
// 遍历目录
while (($file = readdir($handle)) !== false) {
echo $file . ‘<br>‘;
if ($file != ‘.’ && $file != ‘..’) {
// var_dump(is_dir($dir . DIRECTORY_SEPARATOR . $file));
// die;
if (is_dir($dir . DIRECTORY_SEPARATOR . $file)) {
delete_dir_file($dir . DIRECTORY_SEPARATOR . $file);
} else {
unlink($dir . DIRECTORY_SEPARATOR . $file);
}
}
};
}
closedir($handle);
if (rmdir($dir)) {
$flag = true;
}
}
return $flag;
}
}
$delpath = _DIR . DIRECTORY_SEPARATOR . ‘0307’;
// echo $del_path;
if (delete_dir_file($del_path)) {
echo json_encode([‘msg’ => ‘缓存删除成功/‘, ‘code’ => 0], 320);
} else {
echo json_encode([‘msg’ => ‘缓存删除失败/‘, ‘code’ => 1], 256);
}
#### 4. 箭头函数
// 箭头函数 fn
//! 箭头函数会自动捕获父作用域变量进行值拷贝
$x = 1;
$fn = fn ($a, $b) => $a + $b + $x;
echo $fn(10, 20);
### 递归函数,删除指定目录
#### 字符串操作函数
1. 字符串拆分/合并 implode, explode
2. 字符串替换 str_replace
3. 字符串查找 substr()、mt_substr()
<?php
$arr = array(‘Hello’,’World!’,’I’,’love’,’Shanghai!’);
echo implode(“ “,$arr);
?>
//输出:
//Hello World! I love Shanghai!
<?php
$data = “foo:*:1023:1000::/home/foo:/bin/sh”;
list($user, $pass, $uid, $gid, $gecos, $home, $shell) = explode(“:”, $data);
echo $user; // foo
echo $pass; // 1023
?>
//输出:
//foo 1023
```
<?php
// !字符串操作函数
// 拆分 合并
// implode join 数组->字符串
$url = "https://chloe.io/0308/user/index/demo2.html";
// echo implode(",", ['html', 'css', 'uniapp', 'vue', 'electron']);
// explode 字符串 ->数组
$pathinfo = $_SERVER['DOCUMENT_URI'];
var_dump($pathinfo);
$arr = explode("/", $pathinfo);
var_dump($arr);
array_pop($arr);
var_dump($arr);
$action = array_pop($arr);
$controller = array_pop($arr);
echo $controller, $action;
list($localhost, $username, $password) = explode(",", 'localhost,root,wwwphpcn,utf8,3306');
echo "dsn=mysql:host={$localhost},username={$username},pwd={$password}";
die;
// 字符串替换函数 str_replace()
$search = ['交友', '广告', '转账', '直播', '带货', '陪聊', '异性'];
$flag = ['***', '----', '&&&', '00000', '+++++++', '@@@@@@'];
$news = '本公司承接各类广告代理,提供直播和带货教学,提供异性交友陪聊服务...';
// $news = 'php中文网';
str_replace($search, $flag, $news, $count);
// echo $count;
if ($count !== 0) {
echo "聊天内容包含敏感词,请保护好个人财产";
}
// 字符串查找函数 substr针对英文字符的截取 mb_substr针对中文的截取
// echo md5(rand());
// 生成随机颜色
$color = '#' . substr(md5(rand()), 0, 6);
?>
<p style="color:<?= $color ?>">你好</p>
<?php
echo mb_substr('你放弃了吗', 0, 2);
url函数
- parse_url
- http_build_query
- urlencode
- urldecode
- base64_encode
- base64_decode
parse_url 不会把地址转换为数组
$url = "https://www.php.net/manual/zh/ref.strings.php?email=9526678@qq.com&id=1&gender=男";
parse_url($url);
//输出 是个数组 ["scheme"]=> string(5) "https" ["host"]=> string(11) "www.php.net" ["path"]=> string(26) "/manual/zh/ref.strings.php" ["query"]=> string(36) "email=9526678@qq.com&id=1&gender=男" }
http_build_query
$params = [
'city' => '苏州', // 要查询的城市
'key' => 'uwehqwjuehjbsdsdhk232323'
];
$paramsString = http_build_query($params);
var_dump($paramsString);
//输出结果city=%E8%8B%8F%E5%B7%9E&key=uwehqwjuehjbsdsdhk232323
urlencode
urldecode
为了网址符合规范,网址中带有中文需要进行编码处理了
url网址只能是英文之类,中文等都算特殊符号,不能用在网址上,必须通过urlencode编码成符合 要求 网址形式 这个就这两编码的意义,具体使用如下
base64_encode
base64_decode
网页是不能显示二进制数据,可以base64_encod 把二进制转换成字符串,就可以在网页传播显示
上述处理过程主要用途有:
1、接口传输
主要适用于通过WEB接口将文件从一个站点向另一个站点传输,可以用于XML信息。
2、存入数据库
当然,将图片等文件信息保存到数据库中完全可以不用这么做,但这种方式依然适用。对于数据库新手来说这种方式更可接受。因为这完全是一个字符串。
3、文件加密
文件加密可大家用得比较少,举个例子,假如我们有一套PHP程序需要保护,必须有授权码的用户才能正常运行,那么我们可能使用授权码来对文件进行加密,即将上述编码后的字符串再次加工。运行过程需要授权码才可运行。
当然还有其它用途,根据各人的需要灵活使用
<?php
// 5. base64_encode 使用 mime base64 对 string 进行编码 设计此种编码是为了使二进制数据可以通过非纯 8-bit 的传输层传输,例如电子邮件的主体。在计算机中,所有颜色都可以映射到一个二进制值。存储图像时,图像上的每个点都有自己的颜色值。
// header('content-type:image/jpeg');
$file = file_get_contents("https://images.shobserver.com/news/690_390/2023/3/8/0e75818d99a949b5b763a504c62987cc.jpg");
//$file 是二进制数据,网页无法显示二进制数据,只能转换为base64编码,用特别方式才能显示
$img = base64_encode($file);
?>
<img src="data:image/jpeg;base64,<?= $img ?>" alt="模特">
OOP 类与对象
1.1 类的概念
- 物以类聚,把具有相似特性的对象对垒到一个类中,类定义了这些相似对象拥有的相同的属性和方法
- 类是相似对象的描述,成为类的定义,是该类对象的蓝图或者原型
- 类的对象称为一个类的实例(Instance)
- 类的属性和方法统称为类成员
1.2 类的实例化
- 类的实例化:通过类定义创建一个类的对象
- 类的定义属性值都是空或默认值,而对象的属性都有具体的值
1.3 类的定义
- 类的定义以关键字class开始,后面跟着这个类的名称。类的命名通常每个单词的第一个字母大写,以中括号开始和结束
- 类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对圆括号
- 对象中得成员属性和方法可以通过->符号来访问
2.1 面向对象
- 世间万物皆对象,抽象的也是对象,一切可见或不可见都是对象,面向对象是一种以对象为基础的编程思想(自下而上)
- 面向过程是一种以过程为中心的编程思想:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现(自上而下)
2.2 对象的基本组成
- 对象的组成元素:是对象的数据模型,用于描述对象的数据,又称为对象的属性,或者对象的成员变量.
- 对象的行为: 是对象的行为模型,用于描述对象能够做什么事情,又被称为对象的成员方法.
2.3 对象特点
- 每一个对象都是独一无二的
- 对象是一个特定的事物,他的职能是完成特定功能
- 对象是可以重复使用
2.4 OOP实质
-面向对象就是把生活中要解决的问题都用对象的方式进行存储—把所有的数据用属性、方法表现出来。对象之间的互动是通过方法的调用完成互动
3.1 对象的继承
父类:拥有部分相同的属性和方法
继承的好处:
- 父类里面定义的类成员可以不用在子类中重复定义,节约了编程的时间和代价;
- 同一个父类的子类拥有相同的父类定义的类成员,因此外部代码调用他们的时候可以一视同仁;
- 子类可以修改和调用父类定义的类成员我们称为重写(Overwrite), 一旦子类修改了,就按照子类修改之后的功能执行;
子类
:
- 子类可以通过$this访问父类的属性
- 子类的对象可以直接调用父类的方法和属性
- PHP的单继承特性:类不允许同时继承多个父类(extends后面只能跟一个父类名称)
3.2 访问控制
面向对象的三种访问权限:
- public是公有的类成员,可以在任何地方被访问,可以被类以及子类或者对象都可以访问;
- protected受保护的类成员,可以被其自身以及继承的子类访问,可以被子类继承,但是不能被对象访问,只能通过封装的方式让对象访问;
- private私有的类成员,只能被自身访问.不能被子类继承,也不能被对象访问,只能在自身通过封装让外界访问(例如在类里面定义一个公开方法来调用私有属性);
3.3 Static(静态)关键字
静态成员:定义时在访问控制关键字后添加static关键字即可(访问控制关键字:public. protected. private)
- 静态属性用于保存类的公有数据,可以在不同对象间共享
- 静态方法里面只能访问静态属性
- 静态成员不需要实例化对象就可以访问
- 类的内部可以通过 self:: 或 static:: 关键字访问自身静态成员,self::$属性 self::方法()
- 通过 parent:: 关键字访问父类的静态成员,也可以通过子类::父类静态成员
- 通过 类名:: 的方式在类的外部访问静态成员
3.4 重写和Final关键字
- 子类中编写跟父类完全一致的方法可以完成对父类方法的重写,方法参数最好有默认参数
- 对于不想被任何类继承的类可以在class之前添加final关键字
- 对于不想被子类重写(overwrite, 修改)的方法,可以在方法定义前面添加final关键字
3.5 数据访问
- parent关键字可以可用于调用父类中被子类重写了的方法
- self关键字可以用于访问类自身的成员方法,静态成员和类常量;不能用于访问类自身的属性!!!使用常量的时候不需要在常量const名称前面添加$符号
- static::关键字用于访问类自身定义的静态成员,访问静态属性时需要在属性前面添加$符号。
- 常量属性const不能使用对象访问,仅能使用类访问,在类本体内可以使用“self::常量名”,在类本体外可以使用“类名::常量名”
3.6 对象接口
接口就是把不同类的共同行为进行定义,然后在不同的类里面实现不同的功能
- interface定义接口,implements用于表示类实现某个接口
- 接口里面的方法没有具体的实现,无{}
- 实现了某个接口的类必须提供接口中定义的方法的具体实现
- 不能实例化接口,但是能够判断某个对象是否实现了某个接口。instanceof关键字判断某个对象是否实现了某个接口 $object instanceof interface
- 接口可以继承接口(interface extends interface)
- 接口中定义的所有方法都必须是公有,这是接口的特性
3.7 多态
因为接口的方法实现可以有很多,所以对于接口里面定义的方法的具体实现是多种多样的,这种特性我们称为多态
不需要知道对象属于哪个类,只要判断该对象的类是否实现接口,就能实现调用,相同代码实现不同结果
形象点说就是同一个接口,不同的对象实现,得出的结果不一样就是多态,如传入的是人类对象,得到的是人类吃苹果,传入的是猴子对象,得到的就是猴子吃香蕉。相同的一行代码,对于传入不同的接口的实现的对象的时候,表现是不同的。
3.8 抽象类
接口里面的方法都是没有实现的,而类里面的方法都是有实现的. 有没有一种形态,允许类里面一部分方法不实现呢?
当接口中的某些方法对于所有的实现类都是一样的实现方法,只有部分方法需要用到多态的特性
如人和动物吃东西是不同的,但是呼吸是相同的,不需要为人和动物分别实现呼吸的功能
- abstract关键字用于定义抽象类
- 在抽象方法前面添加abstract关键字可以标明这个方法是抽象方法不需要具体实现{}
- 抽象类中可以包含普通的方法,有方法的具体实现
- 继承抽象类的关键字是extends
- 继承抽象类的子类需要实现抽象类中定义的抽象方法
- 抽象类不能被实例化,当子类继承抽象类的时候,所有的抽象的方法都必须定义
4.1 魔术方法之_toString()和invoke()
- toString()当对象被当作String使用时,这个方法会被自动调用(需要在类中定义tostring()方法。调用 echo $object
- invoke()当对象被当作方法调用时,这个方法会被自动调用(需要在类中定义invoke()方法)。调用 $object($parameter)
4.2 魔术方法之call()和callStatic()
__call()方法:当对象访问不存在的方法名称时,此方法自动调用。
- 调用示例:public function __call($name,$argument){}
注意:访问控制关键字必须为public;必须有两个参数:对象访问的方法名称($name)、方法包含的参数($argument ==> 自动转换成数组)。
__callStatic()方法:当对象访问不存在的静态方法名称时,此方法自动调用。
- 调用示例:public static function callStatic($name,$argument){}
**注意:同call();此方法为静态方法(static)。**
这两种方法也被称为方法的重载(overloading)
- 注意区分重写(overwrite)
- 通过这两个方法,同一个方法的调用可以对应不同的方法的实现(同一个方法的静态调用、动态调用对应不同的方法实现)
总结
- class: 类声明
- new: 类实例化
- public: 公开成员,随处可见
- protected: 受保护成员,仅在当前类或子类中可见
- private: 私有成员, 仅在当前类可见
- spl_autoload_register(): 自动加载器
- extends: 类的扩展
- static: 声明类的静态成员
- $this: 实例引用
- self: 类的引用
- trait: 类功能横向扩展
我自己的总结
类
封装 继承 多态
多态:在面向对象的理论中,多态性的一般定义为:同一个操作作用于不同的类的实例,将产生不同的执行结果。
parent关键字可以可用于调用父类中被子类重写了的方法
- self关键字可以用于访问类自身的成员普通方法、静态成员方法和属性和类常量;不能用于访问类自身的普通属性!!!使用常量的时候不需要在常量const名称前面添加$符号
(静态方法中只能调用静态方法和静态属性)
eg: const DIZHI=’地球’;
- static::关键字用于访问类自身定义的静态成员,访问静态属性时需要在属性前面添加$符号。
- 常量属性const不能使用对象访问,仅能使用类访问,在类本体内可以使用“self::常量名”,在类本体外可以使用“类名::常量名”
autoload.php
//注册类的自动加载器
spl_autoload_register(function ($className) {
var_dump($className);
$classFile = __DIR__ . DIRECTORY_SEPARATOR . 'class' . DIRECTORY_SEPARATOR . $className . '.php';
if (is_file($classFile) && file_exists($classFile))
require $classFile;
});
<?php
// 客户端代码
// 引入类的自动加载器
require __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php';
$i = new Product("iphone 12 promax", 6000, 12);
//new时候会自动触发spl_autoload_register并把类名字Product 传送给
echo $i->show();
<?
header('content-Type:text/html;charset=utf-8');
class statica{
//静态成员可以是 类的属性 也可以是类的方法
//使用 static 关键词来声明 某个属性或方法是静态成员
//语法 static 可见性 $属性名称;
//语法 static 可见性 方法名称();
static public $name='0';
public $blog='dir321.com';
const DIZHI='地球';
static public function say(){
echo '我来自'.self::DIZHI;
//静态方法中只能调用静态方法和静态属性
}
public function ceshi(){
echo '我来自外星';
}
public function ceshi1(){
echo '我来自月亮';
echo self::DIZHI;
echo self::ceshi().'测试';
}
}
//现在我们来尝试访问以下 $name 这个属性
//访问 静态程序 不需要 实例化,静态成员与实例化无关。
//仔细看语法 和 访问类常量是一样的 但要注意 访问属性 是需要加$符号的。
echo statica::$name;
//成功输出了一个"0"
//好我们继续 来访问以下 静态方法
statica::say();
//成功输出 我来自地球,注意看 这里没有进行 实例化new,但是我们在类外成功 调用了say方
$a=new statica();
var_dump($a->ceshi1()) ;
?>
多态例子:同一个操作作用于不同的类的实例,将产生不同的执行结果
<?php
class painter{ //定义油漆工类
public function paintbrush(){ //定义油漆工动作
echo "油漆工正在刷漆!/n";
}
}
class typist{ //定义打字员类
public function typed(){ //定义打字员工作
echo "打字员正在打字!/n";
}
}
function printworking($obj){ //定义处理类
if($obj instanceof painter){ //若对象是油漆工类,则显示油漆工动作
$obj->paintbrush();
}elseif($obj instanceof typist){ //若对象是打字员类,则显示打字员动作
$obj->typed();
}else{ //若非以上类,则显示出错信息
echo "Error: 对象错误!";
}
}
printworking(new painter()); //显示员工工作
printworking(new typist()); //显示员工工作
?>
set get Call CallStatic 魔法函数,这个是系统函数只能系统调用
https://tieba.baidu.com/p/4838548831
模仿tp6链式调用
<script>
// console.dir(Navigator);
// document.write(Math.ceil());
</script>
<?php
//! ->php对象成员访问
//! => 数组的键值对
// ! . js对象成员的访问
/**
* 事件委托: 数据库查询构造器
* Db::table()->field()->where()->limit()->select();
*/
// new PDO($dsn, $username, $password);
// $dsn = 'mysql:host=localhost;dbname=phpcn';
// 被委托的类
class Query
{
public $db; //pdo连接对象
protected $table;
protected $field;
protected $limit;
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
function table($table)
{
$this->table = $table;
return $this;
}
function field($field)
{
$this->field = $field;
return $this;
}
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
public function getSql()
{
return sprintf('SELECT %s FROM %s LIMIT %d', $this->field, $this->table, $this->limit);
}
public function select()
{
return $this->db->query($this->getSql())->fetchAll(PDO::FETCH_ASSOC);
}
}
// 工作类
class Db
{
static function __callStatic($method, $args)
{
$dsn = 'mysql:host=localhost;dbname=phpcn';
$query = new Query($dsn, 'root', '');
// var_dump($query->db);
// die;
// 直接委托给Query类中的普通方法完成
//call_user_func可以回调对象中的成员方法,此时第一个参数是一个数组,数组第一个成员是对象,第二个是方法名
return call_user_func([$query, $method], ...$args);
}
}
$res = Db::table('oyk_course')
->field('id,title,f_title,old_price')
->limit(8)
->select();
var_dump($res);
<?php
/**
* 静态成员、类常量是属于类本身,不需要实例化,就可被访问到。
* 1. static 静态成员
* 2. self:: 类引用
* 3. const 类常量
* 4. __callStatic 当调用不存在的静态方法时
* 5. :: 范围解析符 用于类常量,静态成员的访问
*/
class Teacher
{
public const cates = ['前端', '后端', '全栈'];
// 静态属性
public static string $uname;
private static int $salary;
static int $count = 0;
public static string $siteName = 'php中文网';
public function __construct($uname, $salary)
{
self::$uname = $uname;
self::$salary = $salary;
self::$count++;
}
// 静态空间
public static function getCount()
{
//静态成员与类的实例无关 不能用$this来访问,使用self::访问
return sprintf(__CLASS__ . '类被实例化了%d次', self::$count);
// Cannot use '$this' in non-object
}
public static function getBK()
{
// 非静态成员是对象级别的存在,静态成员是类级别存在的,随着类的加载而加载, 优先于对象的存在=》,静态成员方法中无法访问非静态成员(此时还不存在对象)
return self::$uname . '来自' . self::$siteName . '可以胜任' . join(',', self::cates) . '的课程';
}
}
// print_r(Teacher::cates);//类常量不建议在类外部访问
$mj = new Teacher('灭绝', 20000);
$mj1 = new Teacher('灭绝', 20000);
// echo $mj->getCount();
echo Teacher::getCount();
echo Teacher::getBK();
// var_dump($mj, $mj1);
interface 需要implements来实现
interface iDemo
{
// 所有成员必须公开
public const gender = 'MALE';
// 所有方法全部抽象(只有声明,没有实现)
public function sum($a, $b);
public function sub($a, $b);
}
// 工作类
class aDemo implements iDemo
{
public function sum($a, $b)
{
return $a + $b;
}
public function sub($a, $b)
{
return $a - $b;
}
}
$a=new aDemo();
echo $a->sum(12, 16)
interface 定义接口 implements 实现接口 必须所有函数都实现才行,如果能实现需要用下列方法
// 接口 全抽象
// 抽象类 部分抽象
// 工作类 没有抽象类
/**
* 接口是定义,类是实现
* 1. interface 定义接口
* 2. implements 实现接口
* 3. abstract 抽象类 抽象方法
* 4. trait结构 组合式继承 解决php oop 单继承 高耦合的诟病
*/
interface iDemo
{
// 所有成员必须公开
public const gender = 'MALE';
// 所有方法全部抽象(只有声明,没有实现)
public function sum($a, $b);
public function sub($a, $b);
public function mul($a, $b);
public function div($a, $b);
}
// 抽象类 可以部分实现接口中的抽象方法
abstract class aDemo implements iDemo
{
public function sum($a, $b)
{
return $a + $b;
}
public function sub($a, $b)
{
return $a - $b;
}
// 没有实现的方法,在抽象类中可声明为抽象方法
abstract public function mul($a, $b);
abstract public function div($a, $b);
}
// !工作类
class Work extends aDemo
{
// 必须全部实现接口中的抽象方法
// 必须要把抽象类中的全部抽象方法实现了
public function mul($a, $b)
{
return $a * $b;
}
public function div($a, $b)
{
return $b !== 0 ? $a / $b : '除0错';
}
}
实现多个接口的实现
interface A
{
public const A = 1;
}
interface B
{
public const B = 2;
}
class User implements A, B
{
}
静态继承时,不要再用self::, 全部用static::
Example #1 self:: 用法
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
以上例程会输出:
A
后期静态绑定的用法
后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。
Example #2 static:: 简单用法
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // 后期静态绑定从这里开始
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
以上例程会输出:
B
<?php
//! 后期静态绑定 static::
// ! 静态继承时,不要再用self::, 全部用static::
class Car
{
private static function getName()
{
return 'car';
}
public static function run()
{
// ! php内核将类的继承实现放在了编译阶段,在编译阶段 就确定了self解析为car
// self:: 的限制 对当前类的静态引用,取决于定义当前方法所在的类,定义类与调用类不能绑定
// return self::getName();
// !static 后期静态绑定 static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。
return static::getName();
}
}
class Benz extends Car
{
public static function getName()
{
return 'E300';
}
}
echo Car::run() . PHP_EOL; //output CAR
echo Benz::run() . PHP_EOL;//output E300
三、 PHP项目实例
目录结构
config/common.php 存储各种复杂路径,都用常量表示
data存储是数据
template\public存储是网页头、网页底公共模版,都是用include 引用
static存储js css等
lib/user/check.php 用户登录界面后台验证
lib/user/check.php
<?php
// 获取用户登录数据 不能用传统的表单格式提交 ($_POST) , json
// json当成文本流原始数据来接收
$json = file_get_contents('php://input');
// echo $json;
// $json并非php能识别的数据类型,它只是json格式的字符串而已
// json -> php.array , true: 数组
$user = json_decode($json, true);
// var_dump($user);
echo json_encode($user);
config\common.php
<?php
// 根路径
// date() 格式化一个时间戳
// echo date("Y-m-d", time());
define('DAY', date('/md', time()));
define('ROOT_PATH', $_SERVER['DOCUMENT_ROOT'] . DAY . '/web');
// echo ROOT_PATH;
// 数据路径
define('DATA_PATH', ROOT_PATH . '/data');
// 模板路径
define('TMPL_PATH', ROOT_PATH . '/template');
// 公共模板路径
define('TMPL_PATH_PUBLIC', TMPL_PATH . '/public');
// 静态资源路径
define('STATIC_PATH', ROOT_PATH . '/static');
// css
define('STATIC_CSS_PATH', STATIC_PATH . '/css');
// js
define('STATIC_JS_PATH', STATIC_PATH . '/js');
// images
define('STATIC_IMG_PATH', STATIC_PATH . '/images');
login.php
<!-- 引入页眉 -->
<?php include 'template/public/header.php' ?>
<!-- 主体 -->
<main>
<!-- 用户登录 -->
<form class="login">
<table>
<caption>
用户登录
</caption>
<tbody>
<tr>
<td><label for="email">邮箱:</label></td>
<td><input type="email" name="email" id="email" /></td>
</tr>
<tr>
<td><label for="password">密码:</label></td>
<td><input type="password" name="password" id="password" /></td>
</tr>
<tr>
<td colspan="2"><button onclick="doLogin(this)" type="button">提交</button></td>
</tr>
</tbody>
</table>
</form>
<p>
<a href="register.php">没有帐号,请先注册</a>
</p>
</main>
<script>
async function doLogin(obj) {
// console.log(obj.form.email.value);
const email = obj.form.email.value;
const password = obj.form.password.value;
// 非空验证
if (email.length > 0 && password.length > 0) {
// 异步提交 fetch API
const response = await fetch('./lib/user/check.php', {
// 请求方法
method: 'POST',
// 请求头
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify({
email,
password
})
});
// 解析数据
const result = await response.json();
console.log(result);
} else {
alert('邮箱或密码不能为空');
}
}
</script>
<!-- 引入页脚 -->
<?php include 'template/public/footer.php' ?>
template\public 头部底部模板如下
头部模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<?
$arr = require 'data/data.php';
$navs = require 'data/nav.php';
?>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="<?= $arr['keywords'] ?>" />
<meta name="description" content="<?= $arr['des'] ?>" />
<link rel="stylesheet" href="static/css/style.css" />
<title><?= $arr['title'] ?></title>
</head>
<body>
<!-- 页眉 -->
<!-- 修改php.ini open_short_tag On-->
<header>
<nav>
<a href="index.php">首页</a>
<? foreach ($navs as $k => $v) : ?>
<a href="news.php"><?= $v['name'] ?></a>
<? endforeach; ?>
</nav>
</header>
<!-- 页脚 -->
<footer>
<div>php中文网©版权所有(2022-2025)</div>
</footer>
</body>
</html>
data存储是数组
return [
["id" => 1, 'name' => '新闻'],
["id" => 2, 'name' => '产品'],
["id" => 3, 'name' => '联系'],
["id" => 4, 'name' => '登录'],
];
return array("keywords" => "新闻,产品,合肥", "des" => "专业生产中高档办公家具,行业畅销20年", "title" => "首頁");
首页如下
<!-- 引入页眉 -->
<?php include 'template/public/header.php' ?>
<!-- 主体 -->
<main>
<!-- 新闻列表 -->
<div class="news">
<h3>新闻列表</h3>
<div class="list">
<a href="">切实做好高风险岗位从业人员疫情防控。</a>
<a href="">切实做好高风险岗位从业人员疫情防控。</a>
<a href="">切实做好高风险岗位从业人员疫情防控。</a>
<a href="">切实做好高风险岗位从业人员疫情防控。</a>
<a href="">切实做好高风险岗位从业人员疫情防控。</a>
</div>
</div>
<!-- 产品列表 -->
<div class="items">
<h3>产品列表</h3>
<div class="list">
<div class="item">
<img src="static/images/item1.jpeg" alt="" />
<a href="">最新产品1</a>
</div>
<div class="item">
<img src="static/images/item2.jpeg" alt="" />
<a href="">最新产品2</a>
</div>
<div class="item">
<a href=""><img src="static/images/item3.jpeg" alt="" /></a>
<a href="">最新产品3</a>
</div>
<div class="item">
<img src="static/images/item4.jpeg" alt="" />
<a href="">最新产品4</a>
</div>
</div>
</div>
</main>
<!-- 引入页脚 -->
<?php include 'template/public/footer.php' ?>
重点例子代码
fetch的使用 注意await async
<form class="login">
<table>
<caption>
用户登录
</caption>
<tbody>
<tr>
<td><label for="email">邮箱:</label></td>
<td><input type="email" name="email" id="email" /></td>
</tr>
<tr>
<td><label for="password">密码:</label></td>
<td><input type="password" name="password" id="password" /></td>
</tr>
<tr>
<td colspan="2"><button onclick="doLogin(this)" type="button">提交</button></td>
</tr>
</tbody>
</table>
</form>
<script>
async function doLogin(obj) {
// console.log(obj.form.email.value);
const email = obj.form.email.value;
const password = obj.form.password.value;
// 非空验证
if (email.length > 0 && password.length > 0) {
// 异步提交 fetch API
const response = await fetch('./lib/user/check.php', {
// 请求方法
method: 'POST',
// 请求头
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify({
email,
password
})
});
// 解析数据
const result = await response.json();
console.log(result);
} else {
alert('邮箱或密码不能为空');
}
}
</script>
后端代码
<?php
// 获取用户登录数据 不能用传统的表单格式提交 ($_POST) , json
// json当成文本流原始数据来接收
$json = file_get_contents('php://input');
// echo $json;
// $json并非php能识别的数据类型,它只是json格式的字符串而已
// json -> php.array , true: 数组
$user = json_decode($json, true);
// var_dump($user);
echo json_encode($user);
登录判断使用 登录退出 全部写入到一个函数中
/ 获取对用户的操作动作
$action = strtolower($_GET['action']);
$prompt = false;
switch ($action):
case 'login':
$json = file_get_contents('php://input');
$user = json_decode($json, true);
$email = $user['email'];
$salt = 'wwwphpcn';
$password = md5($user['password'] . $salt);
$result = array_filter($users, function ($user) use ($email, $password) {
return $user['email'] === $email && $user['password'] === $password;
});
$result = array_values($result);
if (count($result) === 1) {
$prompt = true;
$_SESSION['user']['email'] = $result[0]['email'];
$_SESSION['user']['name'] = $result[0]['name'];
}
break;
case 'logout':
if (session_destroy()) {
$prompt = true;
}
case 'register':
<?php if (!isset($_SESSION['user'])) : ?>
<a href="login.php">登录</a>
<?php else : ?>
<a href="javascript:;" onclick="logout()"><?= $_SESSION['user']['name'] ?>退出</a>
<?php endif ?>
</nav>
</header>
<script>
async function logout() {
if (confirm('是否退出')) {
const response = await fetch('./lib/userHandle.php?action=logout')
const result = await response.json();
if (result) {
location.href = 'index.php';
} else {
alert('退出失败');
}
}
}
</script>