学习任何一门学问,往往都是从起基本的概念学起。万丈高楼平地起,这些基本概念就是高楼的基石,必须做详尽的分析。我们知道,Yii2是一款脉络清晰的框架,理顺了基础的概念和基本功能,学习更高级和复杂的功能就容易多了。Yii2是一款纯面向对象的框架,它对类的功能做了扩充:PHP类的功能分为属性和方法,而Yii2定义了类的三个功能:属性(property),行为(behavior)和事件(event)。

为了更好的实现面向对象的编程,拿到一个现实的对象,要构造一个PHP对象与之对应,如果用Yii2框架去实现,那么首先要想到的是这个对象有哪些属性,哪些行为,哪些事件。

今天先来说说属性的概念。

我们举个例子,我们需要对$user对象的name属性做trim操作,那么我们首先想到这么做:

// $user is a instance of User
$user->name = trim($name);

然而,这样做有个问题,就是如果我们需要对所有地方的User实例的name属性都要进行trim操作,我们就需要改动多处;另外,假如哪一天我不仅要trim操作,还要首字母大写,那我还得改动很多地方。万一我遗漏了怎么办?

为了解决这个问题,Yii2引入了自己的属性概念。在yii\base\BaseObject中实现了。

注:Yii2.0.12之前是yii\base\Object,Yii2.0.13及以后,为了考虑到”object”将在PHP7.2版本成为受保留的专属名词,改为了yii\base\BaseObject

属性和成员变量

在 PHP 中,类的成员变量和属性既有区别,又有联系,从访问形式来看看,二者没有什么区别,但是,成员变量是就类的结构而言的概念,而属性是就类的功能逻辑而言的概念。

在具体实践中,常常会想用一个稍微特殊些的方法实现属性的读写。这就需要用到魔术方法读取器__getter(),和设定器__setter()。这两个方法提供了这样一种便利:对类的属性进行某种加工之后再写,对类的属性进行某种加工之后再读(预处理和后处理)

成员变量和属性的区别与联系在于:

  1. 成员变量是一个“内”概念,反映的是类的结构构成,与其相对应的是成员函数(类方法)。属性是一个“外”概念,反映的是类的逻辑意义
  2. 成员变量没有读写权限控制,而属性可以指定为只读或只写,或可读可写,这就有了权限这么一说
  3. 成员变量不对读出作任何后处理,不对写入作任何预处理,而属性则可以
  4. public成员变量可以视为一个可读可写、没有任何预处理或后处理的属性。 而private成员变量由于外部不可见,与属性“外”的特性不相符,所以不能视为属性。同样的,protect属性也不能视为属性
  5. 虽然大多数情况下,属性要由成员变量来实现,但是二者并没有必然的关系

在Yii中,由 yii\base\BaseObject 提供了对属性的支持,因此,如果要使你的类支持属性,只需要继承此类即可。

getter 和 setter 方法

属性是通过getter方法和setter方法来定义的。getter 方法是名称以 get 开头的方法,而 setter 方法名以 set 开头,分别是对魔术方法__getter()和__setter()的进一步封装。

getter方法

public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter();
} elseif (method_exists($this, 'set' . $name)) {
....
} ....
}

setter方法

public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) {
...
} else {
...
}
}

方法名中 get 或 set 后面的部分就定义了该属性的名字(get或者set后面的部分就是要加工的类的属性)。 如下面代码所示,getter 方法 getName() 和 setter 方法 setName() 操作的是 name 属性:

namespace app\components;
use yii\base\BaseObject ;
class User extend BaseObject
{
private $_name; public function getName() // 读操作
{
return $this->_name; // 读之前可以做后处理
} public function setName($value) // 写操作
{
$this->_name = trim($value); // 写之前可以做预处理
}
}

小编心得:这种对某种操作的“预处理”和“后处理”在框架中到处可见,虽说还算不上一种设计思想,但是作为一种技巧,可是大大提高了程序的可扩展性!

getter和setter方法创建了一个名为name的属性。在这个例子里,它指向了一个私有成员变量$_name。getter和setter定义的属性和类的成员变量一样。二者的的主要区别在于:当这种属性被读取时,对应的 getter 方法将被调用; 而当属性被赋值时,对应的 setter 方法就调用。如:

// 等效于$name = $user->getName();
$name = $user->name; // $user = $user->setName('Jason');
$user->name = 'Jason';

如果我们像这样去定义“属性”,而不是简单粗暴的定义一个名为publice $name的成员变量,那么就可以实现在任何地方,对User的实例的name属性进行trim操作,不用担心有漏网之鱼。同样,有了setter函数,我们如果还可以方便的再加上“首字母大写”的操作:

public function setName($value)
{
$this->_name = ucfirst(trim($value));
}

同样,在属性读之后的自定义处理:

public function getName()
{
return ucfirst($this->_name);
}

或者结合更多成员变量做更为复杂的处理:

public function getName()
{
return ucfirst($this->_firstname).' '.ucfirst($this->_lastname);
}

只读属性和只写属性

只定义了 getter 没有 setter 的属性是只读属性。 尝试赋值给这样的属性将导致 yii\base\InvalidCallException (无效调用)异常。 类似的,只有 setter 方法而没有 getter 方法定义的属性是只写属性,尝试读取这种属性也会触发相同异常,只不过只写属性的在现实应用中几乎没有。

通过 getter 和 setter 定义的属性也有一些特殊规则和限制:

  • 这类属性的名字是不区分大小写的。如$user->name和 $user->Name是同一个属性。 因为PHP方法名是不区分大小写的。
  • 如果此类属性名和类public成员变量相同,以后者为准。比如$user有age属性(即拥有setter或getter方法),同时也拥有public $age成员变量。那么无论在何种情况下只能用到成员变量public $age,不会用到作为属性的age。这很好理解,因为setter和getter都是魔术方法,只在被操作的成员变量不存在时调用,现在成员变量age存在了,那无论是$age->age的读操作还是$age->age = xxx的写操作都只会直接调用public $age了。
  • 属性不支持可见性(访问限制),无论setter还是getter方法都只能是public的。定义为private和protected有违初衷。
  • 这类属性的 getter 和 setter 方法只能定义为非静态的,若定义为静态方法(static)则不会以相同方式处理。
  • 既然Yii2属性已然不同于PHP的成员变量,那么property_exists()方法就不适宜用来判断yii\base\BaseObject及其子类的属性是否存在,而应该改用yii\base\BaseObject的hasProperty()/canGetProperty() /canSetProperty() 等方法。

属性的实现步骤

下面几步可以实现属性:

  1. 继承自 yii\base\BaseObject,如User
  2. 声明一个用于保存该属性的私有成员变量,如$_name,$_age
  3. 提供getter或setter函数,或两者都提供,用于访问、修改上面提到的私有成员变量。如果只提供了getter,那么该属性为只读属性,只提供了setter,则为只写属性。
class User extend  BaseObject // 1.继承yii\base\BaseObject
{
private $_name; // 2.声明一个用于保存该属性的私有成员变量 public function getName() // 3.提供一个getter或者setter
{
return $this->_name;
} public function setName($value)
{
$this->_name = trim($value);
}
}

小编心得:成员变量对外不可见是比较好的编程习惯。将成员变量的读写操作分开比合在一起方便。

BaseObject中属性的其他方法

hasProperty()

测试一个类是否存在某种属性:

public function hasProperty($name, $checkVars = true)
{
return $this->canGetProperty($name, $checkVars) || $this->canSetProperty($name, false);
}

即定义了getter或setter,如果$checkVars为true,那么类如有同名的成员变量(public/protected/private)也会任何属性存在。

canGetProperty()

测试某个属性是否可读:

public function canGetProperty($name, $checkVars = true)
{
return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name);
}

$checkVars意义同上。

canSetProperty()

测试一个属性是否可写:

public function canSetProperty($name, $checkVars = true)
{
return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name);
}

$checkVars意义同上,只要类定义了成员变量,不管是public还是private 还是 protected, 都认为是可写。

当然,__gettter()和__setter()方法是在遍历所有成员变量,找不到所需的时候才调用的,因此属性天生效率就要低一些。在一些类功能简单,表示数据结构,数据集合时且不需要读写控制时,可以考虑直接使用成员变量作为属性。 
另外,我们可以看到,在框架内部,几乎都是使用$response = $app->getResponse()来代替$response = $app->response; 用$app->setRequest(xxx)代替$app->request = xxx 的情况,几乎从来看不到直接对属性赋值的情况(如后者),从而避免对成员变量的遍历——框架自身还是格外地注重效率的,至于便利性,则留给了开发者啦!

Yii2基本概念之——属性(property)的更多相关文章

  1. Yii2基本概念之——行为(Behavior)

    使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充.通过将行为绑定到一个类,可以使得类具有行为本身所具有的属性和方法,就好像是类本来就具有的这些属性和功能一样. 好的代码设计, ...

  2. JavaScript之面向对象的概念,对象属性和对象属性的特性简介

    一.大家都知道,面向对象语言有一个标志,那就是他们都有类的概念,通过类我们可以创建任意多个具有相同属性和方法的对象.但ECMAScript(指定JavaScript标准的机构,也就是说JavaScri ...

  3. 理解特性attribute 和 属性property的区别 及相关DOM操作总结

    查一下英语单词解释,两个都可以表示属性.但attribute倾向于解释为特质,而property倾向于解释私有的.这个property的私有解释可以更方便我们下面的理解. 第一部分:区别点 第一点:  ...

  4. day26 python学习 对象的接口,封装,私用属性 property

    # 抽象类和接口类 #** #不崇尚接口类 #python本身支持多继承,没有接口专用的语法.但是我知道接口的概念 # 接口类:# 是规范子类的一个模板,只要接口类中定义的,就应该在子类中实现# 接口 ...

  5. 区分元素特性attribute和对象属性property

    × 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...

  6. 属性(@property)、@synthesize

    先前我们学的实例变量是这样的 { int _age; int _height; int age; } 后来学属性 @property int age; 看到@property 会自动编译生成某个成员变 ...

  7. Object的属性property详细解释(自动生成成员变量)

    类Class中的属性property: 在ios第一版中,我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: @interfac ...

  8. OC 实例变量(instance var)与属性(@property)的关系 isa指针

    实例变量(instance var)与属性(@property)的关系 Objective-C 2.0之后,声明一个@property name自动产生一个实例变量,名为_name,因此省去实例变量和 ...

  9. iOS中属性Property的常用关键字的使用说明

    属性关键字的作用 现在我们iOS开发中,基本都是使用ARC(自动引用计数)技术,来编写我们的代码.因此在属性property中我们经常使用的关键字有strong,weak,assign,copy,no ...

随机推荐

  1. scrapy爬虫框架setting模块解析

    平时写爬虫的时候并不需要设置setting里所有的参数,今天心血来潮,花了点时间查了一下setting模块创建后自动写入的所有参数的含义,记录一下. 模块相关说明信息 # -*- coding: ut ...

  2. iOS 轻松实现自定义TabBar

    自定义TabBar的案例网上不少,昨天受到开发小伙伴的影响,尝试了一下非大神的取巧思路:Demo 1.创建RootViewController,后面创建几个继承的VC,将这几个VC添加到TabBarC ...

  3. iOS动态性 运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

    借助前辈的力量综合一下资料. OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是 ...

  4. SQL Server 数据库引擎怎样记录完整备份后修改过的数据

    SQL Server 使用两个内部数据结构跟踪被大容量复制操作修改的区,以及自上次完整备份后修改的区.这些数据结构极大地加快了差异备份的速度.当数据库使用大容量日志恢复模式时,这些数据结构也可以加快将 ...

  5. C#创建对象时各种初始化属性、字段的方式的执行顺序

    创建对象代码如下: new FilterInfo(Student.CreateTimeProperty,"朱七",Express.Equals,Relationship.Or) { ...

  6. C++11新语法糖之尾置返回类型

    C++11的尾置返回类型初衷是为了方便复杂函数的声明和定义,但是当复杂度稍微提升一些的时候很明显能注意到这种设计的作用微乎其微. 首先考虑如下代码: C++ //返回指向数组的指针 auto func ...

  7. CentOS7.4 chrony时间同步服务器部署(替代NTPD)

    Chrony是一个开源的自由软件,它能保持系统时钟与时钟服务器(NTP)同步,让时间保持精确. 它由两个程序组成:chronyd和chronyc. chronyd是一个后台运行的守护进程,用于调整内核 ...

  8. iis部署php项目

    今天跟着学习了如何在IIS下部署php项目,操作很简单,记录一下步骤! 1.启动iis服务器 最后点击确定就完成了 2.打开iis 点击进入即可 3.创建网站 进入添加网站. 添加注意事项如图所示! ...

  9. KVM(一):KVM安装

    KVM通俗的说就是一台服务器当多台用,详细介绍去百度和谷歌. 首先查看服务器是否支持虚拟化 [root@KVM ~]# grep -E '(vmx|svm)' /proc/cpuinfo --colo ...

  10. Kotlin——最详细的抽象类(abstract)、内部类(嵌套类)详解

    如果您对Kotlin很有兴趣,或者很想学好这门语言,可以关注我的掘金,或者进入我的QQ群大家一起学习.进步. 欢迎各位大佬进群共同研究.探索QQ群号:497071402 进入正题 在前面几个章节中,详 ...