trait
概念
为类似PHP的单继承语言而准备的一种代码复用机制
- trait可以使得单继承语言摆脱为了复用而不得不继承的尴尬,让面对象变得更加纯粹
- trait 提供公共代码让具有共性类引入
- trait的结构类似于类:可以有属性和方法,但不能有常量
trait是为类提供公共内容的,因此是需要在类中引入 trait ```php class name{trait 名字{
# 属性(包含静态)
# 方法(包含静态,抽象)
}
}use trait 名字;
可以使用多个
class name{ use trait 名字1,trait 名字2,trait 名字3; }
**步骤**<br />1、不同类之间有公共代码,但是类彼此关系不存在继承关系.<br />
2、将公共代码抽离出来存储到trait中.
<br />3、在需要使用的类中引入 trait,实现公共代码复用.
<br />4、如果存在抽象方法,那么引入的类要么是抽象类,要么要实现抽象方法.
```php
trait Eat{
public $time;
protrcted $how;
public function showTime(){}
}
$s = new Eat() # 错误 不能实例化
class human{
use Eat;
}
$s = new human();
s->showTime();
同名
trait同名:一个类中可能需要引入多个trait,而不同trait中可能出现同名。
trait别名不会改变原来方法的存在,而相当于复制了一个别名方法。
■不允许同名出现,使用替代方法:确定要用的那个
use name1,name2{
name1::way insteadof name2;
}
■不允许同名出现,使用别名,让两个都可以用(使用别名前需要先替代)
use name1,name2{
name1::way insteadof name2;
name2::way as name3;
}
$s->name3();
$s->name1();
控制权
trait控制权:trait在引入类后,可以根据实际类的需求修改trat中对应方法的控制权
- as 修改
- 别名来实现(只针对别名,相当于复制了一个再更改权限) ```php trait Eat{ public $time; protrcted $how; public function showTime(){} private function pee(){} }
class human{
use Eat{
showTime as private;
pee as public;
}
public function show(){
$this->showTime();
$this->pee();
}
}
class human{ use Eat{ showTime as private s; pee as public a; } }
$s = new human(); $s->s(); # 已经私有化 $s->showTime(); # 原有正常
<a name="Ev49U"></a>
### 优先级
trait优先级 :trait在引入的过程中可能存在类本身或者父类拥有同名的成员
- trait同名属性必须保证同名同值,否则报错
- trait同名方法系统认定为重写,优先级关系为:子类>trdt>父类
- 如果子类、trai和父类都有同名方法:子类中使用 parent只能访问到父类
```php
trait Eat{
public functionEat(){}
}
class Human{
public functionEat(){}
}
class Man extends Human{
use Eat;
echo parent::eat();//用parent访问
public functionEat(){} //相当于重写 如果确实需要使用 则用别名
}
$s = new Man();
$s->eat();
总结
- trait是一种类似class结构关键字,trait不能被实例化,可以拥有所有类成员结构(类常量不行)
- trait是用来实现代码复用的,为其他类提供公共代码(方法),其他类如果使用trait用use关键字引入
- 在类中use具体trait就相当于将trait内的所有代码在类中写了一遍
- 一个类可以使用多个trait,但是要注意同名问题
- 同名方法可以使用insteadof来实现替代:一个trait中的同名方法替代另外一个,类就访问替代的那个
- 同名方法可以在被替代之后使用as制作方法别名:类就可以拥有两个方法
- 类中在引入trait后,要注意与trait中的同名成员问题
- 同名属性:不予许(类中不允许)
- 同名方法:允许,类中的方法会覆盖trait中的方法
- 如果类在使用trait的同时也继承了其他类,那么trait中出现的同名方法会覆盖基类的同名方法:类自己 > trait > 基类
- 类在使用trait时可以修改trait方法的控制级别:更严或者更宽松都可以,注意修改控制级别时使用的是别名机制,一定需要改成别名:[trait名::]方法名 as 访问修饰限定符 别名;(原来trait中的方法依然可以正常使用)
- trait中可以使用抽象方法,那么使用该trait的类就必须本身为抽象类或者将抽象方法实现
- trait使用机制
- 有公共代码要实现(方法),而这些方法可能在很多类中会用到
- 公共代码不是属于某一类事务特有,而是很多事务都有(不符合继承)
接口
接口: interface,与类类似,专门用来规范一些共性类必须实现的方法
- 接口不是类,但是与类有类似的结构
接口不能实例化,类可以实现接口
interface 接口名字{}
class 类名 implements 接口名字{}
接口是用来规范项目体系,提供一些必须的行为规范的
接口成员
接口成员
1.接口常量和公有抽象方法(没有方法体)
2.类实现接口的时候,必须实现接口中所有的抽象方法(或者抽象类实现)
3.接口中的成员不能被实现接口的类进行重写(接口常量)和权限更小(受保护或者私有化接口方法)
4.接口成员(方法)规定了实现接口的类必须实现所有方法,从而从最上层规范整体架构
- 大型开发团队中,必要保证类的实现可以让其他类使用成员可知(命名规范)
- 核心结构类必须实现接口,并实现接口中规范的方法(结构规范)
接口继承
小结重载
定义:重载overload,本意指在一个类中可以出现多个同名方法,彼此之间的参数个数和类型不一样。但是PHP中不支持同名方法,而且也不区分数据类型(弱类型语言),所以PHP不支持传统重载。PHP中的重载指的是当某些不允许操作发生时,会自动调用的一种内部机制,即自动调用相关的魔术方法。
- 魔术方法:指系统为类中预先设计好的,只需要开发者实现的方法,魔术方法以双下划线
__
开始。对象在某些特定情况下会自动调用的方法。构造方法、析构方法和克隆方法就是魔术方法
class Man{
public function __construct(){}
}
new Man(); //实例化后对象自动调用(触发时机:对象被实例化)
- PHP重载是指某些容错处理(也可以理解为为了某些特殊情况而自动调用),在访问没有权限或者不存在的属性或者方法的时候,会自动触发的魔术方法。
- 属性重载
-
属性重载
属性重载:当PHP对象访问不存在的或者没有权限访问的属性的时候会自动调用的方法
__get($key):读属性的时候触发
- __set($key,$value):写属性的时候触发
- __isset($key):外部调用isset()函数或者empty()函数时自动触发
- __unset($key):外部调用unset()结构删除对象属性时自动触发
- __toString():对象被当做普通变量输出或者连接时自动调用
class Man{
private $age = 10;
//读取重载
public function __get($key){
echo $key,__METHOD__,'<br/>';
}
//写重载
public function __set($key,$value){
echo $key . ' : ' . $value . '<br/>';
}
//查是否存在重载
public function __isset($key){
echo $key,__METHOD__,'<br/>'
}
//删除属性重载
public function __unset($key){
echo $key,__METHOD__,'<br/>';
}
//对象字符串化
public function __toString(){
echo __METHOD__,'<br/>';
}
}
//实例化
$m = new Man();
$m->age; //类外访问私有属性:原本不允许
$m->age = 100; //设置
isset($m->age); //判定
unset($m->age); //删除
- 属性重载的目的:一方面为了不让程序运行出错,另一方面可以在类内部由我们自己控制内容的访问
class Man{
private $age = 10;
//读取重载
public function __get($key){
//echo $key,'<br/>';
//定义一个允许访问列表:假设有很多私有属性
$allow = array('age');
//判定是否在列表内:在就允许访问,不在就返回NULL或者false
if(in_array($key,$allow)){
return $this->$key; //可变属性:$key是外部访问的目标,最终为$this->age
}
//不允许访问
return false;
}
//写重载(该方法没有返回值)
public function __set($key,$value){
//echo $key . ' : ' . $value . '<br/>';
//与__get理论类似:允许的设置,不允许的不设置(什么都不做即可)
}
//判定重载
public function __isset($key){
//给出内部判定结果
return isset($this->$key);
}
//对象重载
public function __toString(){
//返回一个指定字符串(一般是当类有属性保存某些信息时,输出某个属性)
return __METHOD__;
}
}
1、PHP中属性重载是为了保证数据的安全性,不让代码出现一些操作性错误
2、PHP重载机制get和set也可以理解为统一为属性设置访问和设置方法(便捷)
3、属性重载也能有效保证对象结构,不会让用户无限增加对象属性
4、一般情况下系统内部使用的类如果不是特殊情况不需要使用到属性重载,如果类是对外提供操作的那么应该增
加相应的重载机制以防出错
方法重载
方法重载
5. 方法重载:当PHP对象访问不存在的方法或者不允许访问的方法时自动调用的方法(抑或是谋者特殊情况,如构造方法)
- __call($function_name,$args):对象调用不可调用方法时触发
- __callStatic($function_name,$args):类访问不可调用静态方法时触发
class Man{
private function show(){
echo __METHOD__,'<br/>';
}
private static function staticShow(){
echo __METHOD__,'<br/>';
}
//普通方法重载
public function __call($name){
echo $name,__METHOD__,'<br/>';
}
//静态方法重载
public static function __callStatic($name){
echo $name,__METHOD__,'<br/>';
}
}
//访问不可访问的方法
Man::staticShow();
$m = new Man();
$m->show();
- 方法重载的主要目的:不让外部访问出错。当然,如果必要时也可以进行内部访问
class Man{
private function show(){
echo __METHOD__,'<br/>';
}
private static function staticShow(){
echo __METHOD__,'<br/>';
}
//方法重载
public function __call($name,$arg){
//允许访问列表
$allow = array('show');
//判定是否在列表中
if(in_array($name,$allow)) return $this->$name($arg);
//其他情况
return false;
}
public static function __callStatic($name){
//不允许访问
return false;
}
}
总结
- PHP重载不是指同名方法,而是指对象或者类在访问一些不允许或者不存在的属性或者方法的时候自动调用的魔术方法
- PHP重载分为属性重载和方法重载
- PHP重载的目的是为了保护程序的正确运行而提供的一种容错机制
- 并非所有类都需要实现这些重载,只是如果有类需要对外提供访问使用的时候才有必要采取