php类中的访问控制
对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。
- 被定义为公有的类成员可以在任何地方被访问。
- 被定义为受保护的类成员则可以被其自身以及其子类和父类访问(不可以被实例对象访问)。
- 被定义为私有的类成员则只能被其定义所在的类访问(不可以被实例对象访问)。
在没有任何访问控制关键字的情况下,默认声明为 public。
类的内部是指:
class Test {
// 大括号内就是类的内部
// protected 和 private 定义的类成员只能在类的内部访问/调用
}
属性的访问控制
例子1:
class FatherClass {
public $public = 'father public ';
protected $protected = 'father protected';
private $private = 'father private';
}
$father = new FatherClass();
echo $father->public; // 输出 father public
echo '<br/>';
echo $father->protected; // 报错 Fatal error: Cannot access protected property FatherClass::$protected
echo '<br/>';
echo $father->private; // 报错 Fatal error: Cannot access private property FatherClass::$private
例子2:
class FatherClass {
public $public = 'father public ';
protected $protected = 'father protected';
private $private = 'father private';
}
class ChildClass extends FatherClass {
public function getProtected() {
echo $this->protected;
}
public function getPrivate() {
echo $this->private;
}
}
$child = new ChildClass();
echo $child->public; // father public
echo '<br/>';
echo $child->getProtected(); // father protected
echo '<br/>';
echo $child->getPrivate(); // 报错 Notice: Undefined property: ChildClass::$private
/**
* 因为 ChildClass中没有定义private属性,而FatherClass类中的private属性是私有属性不能被子类继承。
*/
方法的访问控制
例子1:
class FatherClass {
public $public = 'father public ';
protected $protected = 'father protected';
private $private = 'father private';
public function test() {
$this->testProtected();
$this->testPrivate();
}
public function testPublic() {
echo 'father testPublic';
echo '<br/>';
}
protected function testProtected() {
echo 'father testProtected';
echo '<br/>';
}
private function testPrivate() {
echo 'father testPrivate';
echo '<br/>';
}
}
$father = new FatherClass();
$father->testPublic(); // father testPublic
// protected 和 private 定义的类成员只能在类的内部访问/调用
$father->testProtected(); // 报错 Fatal error: Call to protected method FatherClass::testProtected() from context ''
$father->testPrivate(); // 报错 Fatal error: Call to private method FatherClass::testPrivate() from context ''
$father->test(); // 输出 father testProtected 和 father testPrivate
例子2:
// 这个例子很有意思
class FatherClass {
public $public = 'father public ';
protected $protected = 'father protected';
private $private = 'father private';
public function test() {
$this->testProtected();
$this->testPrivate();
}
public function testPublic() {
echo 'father testPublic';
echo '<br/>';
}
protected function testProtected() {
echo 'father testProtected';
echo '<br/>';
}
private function testPrivate() {
echo 'father testPrivate';
echo '<br/>';
}
}
class ChildClass extends FatherClass {
public function getProtected() {
echo $this->protected;
}
public function getPrivate() {
echo $this->private;
}
public function testPublic() {
echo 'child testPublic';
echo '<br/>';
}
protected function testProtected() {
echo 'child testProtected';
echo '<br/>';
}
private function testPrivate() {
echo 'child testPrivate';
echo '<br/>';
}
}
$child = new ChildClass();
$child->test(); // 输出 child testProtected 和 father testPrivate
/**
* 为什么输出father testPrivate?
* 只能强行解释:
* 因为test函数是继承自父类FatherClass,在父类中定义的,test函数中又调了私有方法testProvate,而私有方法只能在类自身内部调用,所以调用的是父类的testPrivate。
* 官方的原话是:由于 $this-> 会在同一作用范围内尝试调用私有方法 (出处: https://www.php.net/manual/zh/language.oop5.late-static-bindings.php)
*/
类常量
可以把在类中始终保持不变的值定义为 常量 。 类常量的默认可见性是 public 。
自 PHP 7.1.0 起,类的常量可以定义为 public、private 或 protected。如果没有设置这些关键字,则该常量默认为 public。
接口(interface)中也可以定义常量。
可以用一个变量来动态调用类。但该变量的值不能为关键字(如 self , parent 或 static)。
注意,类常量只为每个类分配一次,而不是为每个类的实例分配。换句话说类常量是存在于类上的,不管通过类实例化多少个对象,各个对象在访问类常量的时候访问的是同一个常量。
例子:
class MyClass
{
const CONSTANT = 'constant value';
function showConstant() {
echo self::CONSTANT . "<br/>";
}
}
echo MyClass::CONSTANT . "<br/>";
$classname = "MyClass";
echo $classname::CONSTANT . "<br/>";
$class = new MyClass();
$class->showConstant();
echo $class::CONSTANT."<br/>";
类常量是通过::
操作符访问的。
类常量可以通过子类重新定义。PHP 8.1.0 起,如果类常量定义为 final,则不能被子类重新定义。
final 关键字
只有类,方法,常量(php 8.0新增)可以被定义为final。属性不能定义为final。
从 PHP 8.0.0 起,除了构造函数之外,私有方法也不能声明为 final 。
定义方法和常量时在其前面加上 final 关键字来防止被子类覆盖。
如果一个类被声明为 final,则不能被继承。
例子1:
// 子类不能覆盖父类加了final关键字的方法
class BaseClass {
public function test() {
echo "BaseClass::test() called\n";
}
final public function moreTesting() {
echo "BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "ChildClass::moreTesting() called\n";
}
}
// 产生 Fatal error: Cannot override final method BaseClass::moreTesting()
例子2:
// 定义为final的类不能被继承
final class BaseClass {
public function test() {
echo "BaseClass::test() called\n";
}
// 由于类已经是 final,所以 final 关键字是多余的
final public function moreTesting() {
echo "BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass {
}
// 产生 Fatal error: Class ChildClass may not inherit from final class (BaseClass)
范围解析操作符(::)
范围解析操作符是一对冒号::
,可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。
当在类定义之外引用到这些项目时,要使用类名。
引用类常量:
class MyClass {
const CONST_VALUE = 'A constant value';
}
$classname = 'MyClass';
echo $classname::CONST_VALUE; // 输出 A constant value
echo MyClass::CONST_VALUE; // 输出 A constant value
self
,parent
和 static
这三个特殊的关键字是用于在类的内部对其属性或方法进行访问的。例子:
class MyClass {
const CONST_VALUE = 'A constant value';
}
class OtherClass extends MyClass
{
public static $my_static = 'static var';
public static function doubleColon() {
echo parent::CONST_VALUE . "\n"; // 输出 A constant value
echo self::$my_static . "\n"; // 输出 static var
}
}
$classname = 'OtherClass';
$classname::doubleColon();
OtherClass::doubleColon();
当一个子类覆盖其父类中的方法时,PHP 不会调用父类中已被覆盖的方法。是否调用父类的方法取决于子类。例子:
class MyClass
{
protected function myFunc() {
echo "MyClass::myFunc()\n";
}
}
class OtherClass extends MyClass
{
// 覆盖了父类的定义
public function myFunc()
{
// 但还是可以调用父类中被覆盖的方法
parent::myFunc();
echo "OtherClass::myFunc()\n";
}
}
$class = new OtherClass();
$class->myFunc();