上一篇文章补充:console-demo
一个简单的console-demo项目,包含了psr-4(注意看composer.json文件哟),命名空间配合psr-4,symfony/console组件单独使用(如果需要配合数据库,自行安装自己是熟悉的组件,model和service目录都准备好了的,自己慢慢研究吧)
请慢慢食用:https://github.com/lirui310/demo-console
很少写的性状(trait)
开发中很多人遇到过trait,比如laravel的auth,路由中间件,在一个class中非常耀眼,居然在class中用use,用归用,恐怕自己也很少写。trait,是PHP5.4.0引入的概念,既像类,又像接口。性状到底是类还是接口呢?哪个都不是。
性状是类的部分实现(即常量,属性和方法),可以混入一个或多个现有的PHP类中(性状不能单独实例化,还必须得靠其他类使用)。性状有两个作用:表明类可以做什么(像是接口);提供模块化实现(像是类)。
为什么使用性状?
一句话概括:类实现"多继承",为啥要引号来表示这个多继承呢?PHP本来就是单继承,性状这个多继承只是用法感觉像,实际没有多继承哟!很简单的例子:定义一个Persion类,然后我们的Man类或者Woman类都继承Persion,为啥这样继承,因为他们都属于Persion类,不管你是男人还是女人都是人,没毛病吧,这是我们经典的面向对象,java,php等面向对象的语言大家都能理解。现在有一个类叫FacePainting(化妆),这个类只有Woman才用,Man不用(人妖,伪娘请滚开,你不属于Man),那么问题来了,Woman已经继承了Persion,如果把FacePainting类取消,放入到Persion不合适吧,而且FacePainting(化妆)这个类还可能在其他类中也使用,比如Cosplay这个类也要用到FacePainting(化妆),哈哈哈,很好理解了吧,把FacePainting(化妆)定义成性状,就可以为我们解决了这个问题,当Woman类和Cosplay类都使用FacePainting(trait),那么Woman类和Cosplay类都拥有了FacePainting(trait)里面的方法了。
如何创建性状?
关键字:trait
先来回想我们如何写一个类:
class Test {
// 属性
// 方法
}
其实当你写性状的时候就和写类一样的,只是把class关键字变成trait关键字
trait Test {
// 属性
// 方法
}
非常简单吧,而且性状(trait)也可以使用命名空间,也能psr-4自动加载,所以,我的理解就是一个被弱化的class,而且这个弱化的class还不能直接实例化,哈哈哈哈,对,trait是不能实例化的(不能new)!
官方原文(其实很好理解):
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
如何使用性状?
我们知道了trait是不能被实例化的,那么就很容易理解,为啥在一个类里面使用use来使用trait
class Test {
use Test; // 使用Test这个trait 非常简单吧
// 属性
// 方法
}
有些class里面没有一个方法,只有一个use Trait; 但是实例化后又很多方法可以用,小伙伴们现在知道为啥了吗?对,方法都在这个trait中,但是也有可能在这个class继承的父类中哟,反正现在IDE都能帮你直接定位到,哈哈哈,我估摸着忙着做业务逻辑(早下班,好约会)都没注意看到底是父类的方法还是trait的方法。
来几个官方例子,加深下理解(这个东西确实不难,看看就能理解了):
Example #1 Trait 示例
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
Example #2 优先顺序示例
从基类继承的成员被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆盖。其行为 MyHelloWorld 类中定义的方法一致。优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
结果是:
Hello World!
Example #3 另一个优先级顺序的例子
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
?>
结果是:
Hello Universe!
Example #4 多个 trait 的用法
通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World';
}
}
class MyHelloWorld {
use Hello, World;
public function sayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
结果是:
Hello World!
以上基本就是我们trait常用的基础知识,还有更多的比如:如果两个 trait 都插入了一个同名的方法,一个class用了这两个trait,需要如何解决冲突?如何修改trait的方法访问控制?还有很多问题大家可以直接去php官方手册查看,我反正看了都会忘,因为这个东西确实用的少,更别说那些问题了,哈哈哈哈,比较懒,了解这些基础的知识点,基本足够你在项目中使用了,但是你会用吗?很多人情愿定义一个类,多实例化一个类,也难得用trait吧,这也是为啥我们实际使用中trait用的那么少的原因,如果你是追求高质量代码的phper,那么trait绝对会帮助你,普通玩家还是赶紧写个class,直接new了开始用吧,早写完,早下班,好和妹子吃饭呢,哈哈哈哈。
附上trait官方链接供高玩食用:https://www.php.net/manual/zh/language.oop5.traits.php
后续会继续更新,感兴趣的小伙伴可以收藏下哟!
欢迎有问题或者项目开发有问题的小伙伴(妹子优先)添加微信:wxmm686800,共同成长!不好意思可以邮箱联系:lirui310@aliyun.com