只是一个快速说明,可以同时声明多个属性的可见性,方法是用逗号隔开它们。
例如
<?php
class a
{
protected $a, $b;
public $c, $d;
private $e, $f;
}
?>
属性、方法或(从 PHP 7.1.0 开始)常量的可见性可以通过在声明前添加关键字 public
、protected
或 private
来定义。声明为 public 的类成员可以在任何地方访问。声明为 protected 的成员只能在类本身和继承类以及父类中访问。声明为 private 的成员只能被定义该成员的类访问。
类属性可以定义为 public、private 或 protected。没有显式可见性关键字的属性定义为 public。
示例 #1 属性声明
<?php
/**
* 定义 MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // 可行
echo $obj->protected; // 致命错误
echo $obj->private; // 致命错误
$obj->printHello(); // 显示 Public、Protected 和 Private
/**
* 定义 MyClass2
*/
class MyClass2 extends MyClass
{
// 我们可以重新声明 public 和 protected 属性,但不能声明 private
public $public = 'Public2';
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // 可行
echo $obj2->protected; // 致命错误
echo $obj2->private; // 未定义
$obj2->printHello(); // 显示 Public2、Protected2、未定义
?>
类方法可以定义为 public、private 或 protected。没有显式可见性关键字的方法定义为 public。
示例 #2 方法声明
<?php
/**
* 定义 MyClass
*/
class MyClass
{
// 声明一个 public 构造函数
public function __construct() { }
// 声明一个 public 方法
public function MyPublic() { }
// 声明一个 protected 方法
protected function MyProtected() { }
// 声明一个 private 方法
private function MyPrivate() { }
// 这是 public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}
$myclass = new MyClass;
$myclass->MyPublic(); // 可行
$myclass->MyProtected(); // 致命错误
$myclass->MyPrivate(); // 致命错误
$myclass->Foo(); // Public、Protected 和 Private 可行
/**
* 定义 MyClass2
*/
class MyClass2 extends MyClass
{
// 这是 public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // 致命错误
}
}
$myclass2 = new MyClass2;
$myclass2->MyPublic(); // 可行
$myclass2->Foo2(); // Public 和 Protected 可行,Private 不行
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new Foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
?>
从 PHP 7.1.0 开始,类常量可以定义为 public、private 或 protected。没有显式可见性关键字的常量定义为 public。
示例 #3 从 PHP 7.1.0 开始的常量声明
<?php
/**
* 定义 MyClass
*/
class MyClass
{
// 声明一个公有常量
public const MY_PUBLIC = 'public';
// 声明一个受保护的常量
protected const MY_PROTECTED = 'protected';
// 声明一个私有常量
private const MY_PRIVATE = 'private';
public function foo()
{
echo self::MY_PUBLIC;
echo self::MY_PROTECTED;
echo self::MY_PRIVATE;
}
}
$myclass = new MyClass();
MyClass::MY_PUBLIC; // 可用
MyClass::MY_PROTECTED; // 致命错误
MyClass::MY_PRIVATE; // 致命错误
$myclass->foo(); // 公有、受保护和私有都可用
/**
* 定义 MyClass2
*/
class MyClass2 extends MyClass
{
// 这是公有的
function foo2()
{
echo self::MY_PUBLIC;
echo self::MY_PROTECTED;
echo self::MY_PRIVATE; // 致命错误
}
}
$myclass2 = new MyClass2;
echo MyClass2::MY_PUBLIC; // 可用
$myclass2->foo2(); // 公有和受保护可用,私有不可用
?>
同一类型的对象可以访问彼此的私有和受保护成员,即使它们不是同一个实例。这是因为在这些对象内部,实现细节已经知道。
示例 #4 访问同一对象类型的私有成员
<?php
class Test
{
private $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
private function bar()
{
echo '访问了私有方法。';
}
public function baz(Test $other)
{
// 我们可以更改私有属性:
$other->foo = 'hello';
var_dump($other->foo);
// 我们也可以调用私有方法:
$other->bar();
}
}
$test = new Test('test');
$test->baz(new Test('other'));
?>
上面的示例将输出
string(5) "hello" Accessed the private method.
只是一个快速说明,可以同时声明多个属性的可见性,方法是用逗号隔开它们。
例如
<?php
class a
{
protected $a, $b;
public $c, $d;
private $e, $f;
}
?>
注意:可见性在每个类基础上工作,并不阻止同一类的实例访问彼此的属性!
<?php
class Foo
{
private $bar;
public function debugBar(Foo $object)
{
// 这不违反可见性,尽管 $bar 是私有的
echo $object->bar, "\n";
}
public function setBar($value)
{
// 必要的 method,因为 $bar 在类外部是不可见的
$this->bar = $value;
}
public function setForeignBar(Foo $object, $value)
{
// 这不违反可见性!
$object->bar = $value;
}
}
$a = new Foo();
$b = new Foo();
$a->setBar(1);
$b->setBar(2);
$a->debugBar($b); // 2
$b->debugBar($a); // 1
$a->setForeignBar($b, 3);
$b->setForeignBar($a, 4);
$a->debugBar($b); // 3
$b->debugBar($a); // 4
?>
动态属性是“公有的”。
<?php
class MyClass {
public function setProperty($value) {
$this->dynamicProperty = $value;
}
}
$obj = new MyClass();
$obj->setProperty('Hello World');
echo $obj->dynamicProperty; // 输出 "Hello World"
?>
此用法也是相同的
<?php
class MyClass {
}
$obj = new MyClass();
$obj->dynamicProperty = 'Hello World';
echo $obj->dynamicProperty; // 输出 "Hello World"
?>
如果未被覆盖,子类中的 self::$foo 实际上是指向父类的 self::$foo
<?php
class one
{
protected static $foo = "bar";
public function change_foo($value)
{
self::$foo = $value;
}
}
class two extends one
{
public function tell_me()
{
echo self::$foo;
}
}
$first = new one;
$second = new two;
$second->tell_me(); // bar
$first->change_foo("restaurant");
$second->tell_me(); // restaurant
?>
我在任何地方都找不到关于这一点的文档,但你可以像预期的那样访问同一类的不同实例中的受保护和私有成员变量
即
<?php
类 A
{
protected $prot;
private $priv;
public function __construct($a, $b)
{
$this->prot = $a;
$this->priv = $b;
}
public function print_other(A $other)
{
echo $other->prot;
echo $other->priv;
}
}
类 B extends A
{
}
$a = new A("a_protected", "a_private");
$other_a = new A("other_a_protected", "other_a_private");
$b = new B("b_protected", "ba_private");
$other_a->print_other($a); // 输出 a_protected 和 a_private
$other_a->print_other($b); // 输出 b_protected 和 ba_private
$b->print_other($a); // 输出 a_protected 和 a_private
?>
> 声明为 protected 的成员只能在类本身和继承类中访问。声明为 private 的成员只能由定义该成员的类访问。
>
>
>
这并不完全正确。对象外部的代码可以获取和设置私有和受保护成员
<?php
类 Sealed { private $value = 'foo'; }
$sealed = new Sealed;
var_dump($sealed); // private $value => string(3) "foo"
call_user_func(\Closure::bind(
function () use ($sealed) { $sealed->value = 'BAZ'; },
null,
$sealed
));
var_dump($sealed); // private $value => string(3) "BAZ"
?>
神奇之处在于 \Closure::bind,它允许匿名函数绑定到特定类作用域。\Closure::bind 的文档中提到
> 如果提供了对象,则将使用该对象的类型
> 而不是。这将确定绑定对象中受保护和
> 私有方法的可见性。
因此,实际上,我们为 $sealed 添加了一个运行时设置器,然后调用该设置器。这可以扩展为可以强制设置和强制获取对象成员的通用函数
<?php
function force_set($object, $property, $value) {
call_user_func(\Closure::bind(
function () use ($object, $property, $value) {
$object->{$property} = $value;
},
null,
$object
));
}
function force_get($object, $property) {
return call_user_func(\Closure::bind(
function () use ($object, $property) {
return $object->{$property};
},
null,
$object
));
}
force_set($sealed, 'value', 'quux');
var_dump(force_get($sealed, 'value')); // 'quux'
?>
您可能不应该在生产环境中依赖此功能,但对于调试和测试来说,拥有此功能非常方便。
我看到我们可以将私有属性重新声明到子类中
<?php
类 A{
private int $private_prop = 4;
protected int $protected_prop = 8;
}
类 B extends A{
private int $private_prop = 7; // 我们可以重新声明私有属性!!!
public function printAll() {
echo $this->private_prop;
echo $this->protected_prop;
}
}
$b = new B;
$b->printAll(); // 显示 78
}
?>