PHP 8.4.2 Released!

アクセス権

プロパティ、メソッドまたは (PHP 7.1.0 以降) 定数のアクセス権 (visibility) は、 キーワード: 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; // Fatal エラー
echo $obj->private; // Fatal エラー
$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; // Fatal エラー
echo $obj2->private; // 未定義です
$obj2->printHello(); // Public2, Protected2, Undefined を表示します

?>

非対称可視性プロパティ

PHP 8.4 から、プロパティは非対称に アクセス権を設定できるようになりました。 読み取り(get)と書き込み(set)に 異なるスコープを設定できます。具体的には、set のアクセス権が メインのアクセス権より広くならない限り、 別々に指定できます。

例2 非対称可視性プロパティ

<?php
class Book
{
public function
__construct(
public private(
set) string $title,
public protected(
set) string $author,
protected private(
set) int $pubYear,
) {}
}

class
SpecialBook extends Book
{
public function
update(string $author, int $year): void
{
$this->author = $author; // OK
$this->pubYear = $year; // 致命的なエラー
}
}

$b = new Book('How to PHP', 'Peter H. Peterson', 2024);

echo
$b->title; // OK
echo $b->author; // OK
echo $b->pubYear; // 致命的なエラー

$b->title = 'How not to PHP'; // 致命的なエラー
$b->author = 'Pedro H. Peterson'; // 致命的なエラー
$b->pubYear = 2023; // 致命的なエラー
?>

非対称可視性プロパティにはいくつかの注意点があります:

  • 型付きプロパティのみが、set のアクセス権を個別に指定できます。
  • set のアクセス権は、get と同じか、 より厳しくなければなりません。つまり、 public protected(set) および protected protected(set) は許可されますが、protected public(set) は構文エラーになります。
  • プロパティが public の場合、メインのアクセス権は 省略できます。つまり、public private(set)private(set) は 同じ結果になります。
  • private(set) のアクセス権を持つプロパティは 自動的に final となり、子クラスで再宣言できません。
  • プロパティへのリファレンスを取得する場合、get ではなく set のアクセス権に従います。 これは、リファレンスを使用してプロパティの値を変更できるためです。
  • 同様に、配列プロパティへの書き込みを試みると、内部的には getset の両方の操作が行われます。そのため、常により厳しい set のアクセス権に従います。

クラスが別のクラスを継承するとき、子クラスは final でない任意のプロパティを再定義できます。その際、 新しいアクセス権が親クラスと同じか広い場合に限り、 メインのアクセス権または set のアクセス権を広げることができます。ただし、 private プロパティがオーバーライドされると、 実際には親のプロパティを変更するのではなく、 異なる内部名を持つ新しいプロパティを作成することに注意してください。

例3 非対称可視性プロパティの継承

<?php
class Book
{
protected
string $title;
public protected(
set) string $author;
protected private(
set) int $pubYear;
}

class
SpecialBook extends Book
{
public protected(
set) $title; // OK。読み取りのアクセス権が広く、書き込みは同じだから。
public string $author; // OK。読み取りのアクセス権は同じで、書き込みが広いから。
public protected(set) int $pubYear; // 致命的なエラー。private(set) プロパティは final です。
}
?>

メソッドのアクセス権

クラスメソッドは、public, private, または protected として定義します。アクセス権を明示せずに宣言したメソッドは、 public となります。

例4 メソッドの宣言

<?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(); // Fatal エラー
$myclass->MyPrivate(); // Fatal エラー
$myclass->Foo(); // Public、Protected および Private が動作します


/**
* MyClass2 の定義
*/
class MyClass2 extends MyClass
{
// これは public となります
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Fatal エラー
}
}

$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 として定義されます。

例5 PHP 7.1.0 以降での定数の宣言

<?php
/**
* MyClass を定義する
*/
class MyClass
{
// public な定数を宣言する
public const MY_PUBLIC = 'public';

// protected な定数を宣言する
protected const MY_PROTECTED = 'protected';

// private な定数を宣言する
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; // Fatal エラー
MyClass::MY_PRIVATE; // Fatal エラー
$myclass->foo(); // Public, Protected, Private のいずれでも動作します


/**
* MyClass2 を定義する
*/
class MyClass2 extends MyClass
{
// これは public
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Fatal エラー
}
}

$myclass2 = new MyClass2;
echo
MyClass2::MY_PUBLIC; // 動作します
$myclass2->foo2(); // Public, Protected では動作しますが、Private では動作しません
?>

他のオブジェクトからのアクセス権

同じ型のオブジェクト間では、たとえ同一のインスタンスでなくても お互いの private メンバーや protected メンバーにアクセスすることができます。 これは、そのオブジェクトの内部ではオブジェクトの実装の詳細が既知であるからです。

例6 同じ型のオブジェクトの private メンバーへのアクセス

<?php
class Test
{
private
$foo;

public function
__construct($foo)
{
$this->foo = $foo;
}

private function
bar()
{
echo
'Accessed the private method.';
}

public function
baz(Test $other)
{
// private プロパティを変更することができます
$other->foo = 'hello';
var_dump($other->foo);

// private メソッドをコールすることもできます
$other->bar();
}
}

$test = new Test('test');

$test->baz(new Test('other'));
?>

上の例の出力は以下となります。

string(5) "hello"
Accessed the private method.
add a note

User Contributed Notes 6 notes

up
62
pgl at yoyo dot org
9 years ago
Just a quick note that it's possible to declare visibility for multiple properties at the same time, by separating them by commas.

eg:

<?php
class a
{
protected
$a, $b;

public
$c, $d;

private
$e, $f;
}
?>
up
4
alperenberatdurmus at gmail dot com
1 year ago
Dynamic properties are "public".
<?php
class MyClass {
public function
setProperty($value) {
$this->dynamicProperty = $value;
}
}
$obj = new MyClass();
$obj->setProperty('Hello World');
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>

This usage is the same as well:
<?php
class MyClass {
}
$obj = new MyClass();
$obj->dynamicProperty = 'Hello World';
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>
up
13
jc dot flash at gmail dot com
12 years ago
if not overwritten, self::$foo in a subclass actually refers to parent's 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
?>
up
7
bishop at php dot net
8 years ago
> Members declared protected can be accessed only within
> the class itself and by inherited classes. Members declared
> as private may only be accessed by the class that defines
> the member.

This is not strictly true. Code outside the object can get and set private and protected members:

<?php
class 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"

?>

The magic lay in \Closure::bind, which allows an anonymous function to bind to a particular class scope. The documentation on \Closure::bind says:

> If an object is given, the type of the object will be used
> instead. This determines the visibility of protected and
> private methods of the bound object.

So, effectively, we're adding a run-time setter to $sealed, then calling that setter. This can be elaborated to generic functions that can force set and force get object members:

<?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'

?>

You should probably not rely on this ability for production quality code, but having this ability for debugging and testing is handy.
up
8
Joshua Watt
17 years ago
I couldn't find this documented anywhere, but you can access protected and private member varaibles in different instance of the same class, just as you would expect

i.e.

<?php
class 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;
}
}

class
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); //echoes a_protected and a_private
$other_a->print_other($b); //echoes b_protected and ba_private

$b->print_other($a); //echoes a_protected and a_private
?>
up
1
kostya at eltexsoft dot com
3 years ago
I see we can redeclare private properties into child class
<?php
class A{
private
int $private_prop = 4;
protected
int $protected_prop = 8;
}

class
B extends A{
private
int $private_prop = 7; // we can redeclare private property!!!
public function printAll() {
echo
$this->private_prop;
echo
$this->protected_prop;
}
}

$b = new B;
$b->printAll(); // show 78
}
?>
To Top