4.8.10 类的静态成员、类常量
静态成员、类常量是属于类本身,不需要实例化就可以被访问到,直接使用类名访问,减少实例化对象所占内存。
static 标识静态成员(默认为 public,可以不写)
const 类常量(不能用 define ),通常在类内部使用,不建议在类外部使用
在类内部,使用 self::访问静态成员(需要加$)、类常量(不需要加$),(不能使用 $this)
__callStatic 当调用不存在或者没有权限访问的静态方法时,此魔术方法会被调用
:: 范围解析符 self::用于类常量 静态成员的访问
- 类外部访问:className::静态成员(需要加$),className::类常量(不需要加$)
class Teacher
{
// 类常量
public const CATES = ['前端', '后端', '全栈'];
// 静态属性
public static $uname;
private static $salary;
// 不加访问控制,默认为 public
static $siteName = 'php中文网';
static $count = 0;
// 构造方法,创建对象时初始化属性
public function __construct($uname, $salary)
{
//静态成员与类的实例无关 不能使用$this来访问 使用self::访问静态成员 类常量
self::$salary = $salary;
self::$uname = $uname;
self::$count++;
}
static function getBK()
{
// 非静态成员是对象级别的存在,静态成员是类级别的存在,随着类的加载而加载,优先于对象存在的,在静态方法中无法访问非静态成员(此时还不存在对象)
return self::$uname . '来自' . self::$siteName . '可以胜任' . join(',', self::CATES) . '的相关课程';
}
}
$mj = new Teacher('灭绝师妹', 2000);
echo Teacher::$uname; // 灭绝师妹
// 可以使用对象来访问,但不建议
echo $mj::$uname; // 灭绝师妹
$mj1 = new Teacher('灭绝老师', 5000);
// 实例化对象会改变静态属性的值,值为最后一次实例化的值,也会改变之前实例化的对象
echo Teacher::$uname; // 灭绝老师
echo $mj::$uname; // 灭绝老师
//类常量不建议在类外访问
print_r(Teacher::CATES); // Array ( [0] => 前端 [1] => 后端 [2] => 全栈 )
// 访问静态方法
echo Teacher::getBK(); // 灭绝老师来自php中文网可以胜任前端,后端,全栈的相关课程
// 访问次数
echo Teacher::$count; // 2
4.8.11 接口、抽象类、trait
4.8.11.1 接口、抽象类
接口(interface)是定义,类(class)是实现。
- 接口(interface):定义方法(只有声明,没有实现),不能被实例化
- 抽象类(abstract):继承接口(interface),重写并实现部分方法,不能被实例化,可以继承多个接口
- 类(class):继承抽象类,实现全部方法,可以实例化,只能继承一个父类(抽象类)
// 接口(interface)
interface iDemo
{
// 所有成员必须是公开的(public)
public const gender = 'MALE';
// 接口(interface)所有方法都是抽象方法 只有声明 没有实现
public function sum($a, $b);
public function sub($a, $b);
public function mul($a, $b);
public function div($a, $b);
}
interface test
{
public const T = 20;
}
// 抽象类(abstract):
// 抽象类 可以存在 抽象方法和工作方法 不能被实例化
// 抽象类可以继承多个接口
// 实现部分方法,剩余方法由类(class)完成
abstract class aDemo implements iDemo, test
{
// 重写接口 iDemo 中的 sum 方法
public function sum($a, $b)
{
return $a + $b;
}
// 重写接口 iDemo 中的 sub 方法
public function sub($a, $b)
{
return $a - $b;
}
}
// 类只能继承一个父类(抽象类)
// 类(class):实现全部方法,可以实例化
class Work extends aDemo
{
// 重写接口 iDemo 中的 mul 方法
public function mul($a, $b)
{
// 调用接口 test 中的常量 T
echo self::T;
return $a * $b;
}
// 重写接口 iDemo 中的 div 方法
public function div($a, $b)
{
return $a / $b;
}
}
$obj = new Work;
$obj->mul(5,6); // 20 ( echo self::T; )
echo $obj->sum(10, 20); // 30
4.8.11.2 trait
trait是基于类的语法,但是和接口一样,不能被实例化 ,是对类功能的横向扩展。
php oop 默认是单继承 只能继承一个父类->高耦合
- 一个类可以实现多个接口,多态
- trait 功能组合式地实现多继承
// 接上例的接口和抽象类
// 定义 trait,扩展类的功能
// 断点打印 t1
trait t1
{
public function dd($data)
{
var_dump($data);
die;
}
}
// 扩展 sum() 功能
trait t2
{
public function sum($a, $b, ...$args)
{
return $a + $b + array_sum($args);
}
}
class Work extends aDemo
{
// 同名方法优先级 类同名方法(重写)>trait>继承成员方法
//trait 功能组合式地实现多继承
use t1, t2;
// 重写接口 iDemo 中的 mul 方法
public function mul($a, $b)
{
// 调用接口 test 中的常量 T
echo self::T;
return $a * $b;
}
// 重写接口 iDemo 中的 div 方法
public function div($a, $b)
{
return $a / $b;
}
// 类同名方法(重写),优先级最大
// public function sum($a, $b)
// {
// return 'hello';
// }
}
echo $obj->sum(10, 20, 30, 40, 200); // 300 (trait t2)
$obj->dd('你好'); // 你好(trait t1)
4.8.12 后期静态绑定
self:: 对当前类的静态引用是由限制的,self::取决于定义当前方法所在的类, 定义类与调用类(实例化的对象)不能动态绑定
class Car
{
// 私有静态方法 getName()
private static function getName(): string
{
return 'car';
}
public static function run()
{
// self:: 指向当前类,实例化的对象中依然指向当前类 Car
return self::getName();
}
}
class Benz extends Car
{
// 重写父类的静态方法 getName()
public static function getName()
{
return 'E300';
}
}
echo Car::run(); // car
// 调用类中 self::还是指向父类 Car,而不是指向调用类 Benz
echo Benz::run(); // car
使用后期静态绑定 static::,不再被解析为定义当前方法所在的类,而是在调用类。
class Car
{
private static function getName(): string
{
return 'car';
}
public static function run()
{
// self:: 改为 static::
return static::getName();
}
}
class Benz extends Car
{
public static function getName()
{
return 'E300';
}
}
echo Car::run(); // car
// 后期静态绑定 static::,不再指向父类 Car,而是指向调用类 Benz
echo Benz::run(); // E300
4.8.13 命名空间
全局成员: 常量、命名函数、类(接口),不受作用域限制,重名的话会冲突
命名空间:可以解决全局成员命名冲突的问题
namespace ns1;
class test
{
public static function show()
{
// __FUNCTION__ 只是返回方法(或者函数)的名字
// __METHOD__ 返回类名和方法的名字(包括命名空间,如有)
return __METHOD__;
}
}
namespace ns2;
class test
{
public static function show()
{
return __METHOD__;
}
}
// 当前在命名空间 ns2,可以直接调用本空间下的类
echo test::show(); // ns2\test::show
// call_user_func() 回调,是异步,脱离了当前线程,不能直接调用,需要加上命名空间
echo call_user_func(['test', 'show']); // 报错找不到test类
echo call_user_func(['ns2\test', 'show']); // ns2\test::show
// 从非全局命名空间(当前为 ns2)访问别的空间中的类 先回到root: \
// 注意:这里的反斜杠(\)是空间分隔符,不是目录分隔符
echo \ns1\test::show(); // ns1\test::show
命名空间下的函数,优先级高于系统同名函数 (系统函数默认在全局命名空间)
namespace demo;
function var_dump($data)
{
echo 'var_dump' . $data;
}
var_dump('你好'); // var_dump你好
4.8.14 use 在命名空间中的使用
目录结构:
- tp6
- app
- controller
- Index.php
- Login.php
- controller
- autoload.php
- index.php
- app
app\controller 下面的两个类(命名空间设置为类所在的目录)
Index.php
namespace app\controller;
class Index
{
static function index()
{
return __METHOD__ . "<br />";
}
}
Login.php
namespace app\controller;
class Login
{
static function index()
{
return __METHOD__ . "<br />";
}
}
autoload.php:类的自动加载器
spl_autoload_register(function ($className) {
// 自动加载器引入的类名 $className 是带有命名空间的(app\controller\Index)
// 命名空间名称与目录树对应,可以参与类文件所在位置的拼接
// 命名空间的反斜杠(\)是空间分隔符,不是目录分隔符,需要转换为目录分割符
// "\\":第一个 \ 是转义符,第二个 \ 是空间分隔符
$file = str_replace("\\", DIRECTORY_SEPARATOR, $className) . '.php';
if (is_file($file) && file_exists($file)) require $file;
});
index.php:入口文件,使用 use 引入其他命名空间中的类
// 入口文件
// 注意:入口文件不能放在类所在的目录,否则无法引入
require __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php';
// 1. 直接调用其他命名空间中的类,需加上命名空间
// 无需 use app\controller;
// 当前为全局命名空间,无需加 root:\
echo app\controller\Index::index(); // app\controller\Index::index
// 如果需要给命名空间起别名,则需要 use as
use app\controller as ac;
echo ac\login::index(); // app\controller\Login::index
// 2. use 引入其他命名空间中的类,然后可以直接调用改类
use app\controller\Index;
use app\controller\Login;
echo Index::index(); // app\controller\Index::index
echo Login::index(); // app\controller\Login::index
// 给引入的类起别名: use as
// 不起别名,默认为Index
use app\controller\Index as in;
echo in::index(); // app\controller\Index::index