가시성
가시성 (Visibility)
프로퍼티, 메서드, 그리고 PHP 7.1.0부터는 상수까지 — 그 가시성은 선언 앞에 public, protected, private 키워드를 붙여서 정해요. public으로 선언한 클래스 멤버는 어디서든 접근할 수 있고요. protected로 선언한 멤버는 해당 클래스 자신과, 이를 상속한 자식 클래스, 그리고 부모 클래스 안에서만 접근할 수 있어요. private로 선언한 멤버는 그 멤버를 정의한 클래스 안에서만 접근할 수 있죠.
프로퍼티 가시성 (Property Visibility)
클래스 프로퍼티를 public, private, protected 중 하나로 정의할 수 있어요. 가시성 키워드를 명시하지 않고 선언한 프로퍼티는 기본적으로 public으로 정의돼요.
예제 #1 프로퍼티 선언 (Property declaration)
<?php
/**
* Define 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; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// We can redeclare the public and protected properties, but not private
public $public = 'Public2';
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->protected; // Fatal Error
echo $obj2->private; // Undefined
$obj2->printHello(); // Shows Public2, Protected2, Undefined
?>
비대칭 프로퍼티 가시성 (Asymmetric Property Visibility)
PHP 8.4부터는 객체 프로퍼티의 가시성을 비대칭으로 설정할 수도 있어요. 읽기(get)와 쓰기(set)에 서로 다른 유효 범위를 지정하는 방식이죠. 구체적으로는 set 가시성을 따로 지정할 수 있는데, 단 이때 기본 가시성보다 더 관대해지면 안 돼요.
예제 #2 비대칭 프로퍼티 가시성 (Asymmetric Property visibility)
<?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; // Fatal Error
}
}
$b = new Book('How to PHP', 'Peter H. Peterson', 2024);
echo $b->title; // Works
echo $b->author; // Works
echo $b->pubYear; // Fatal Error
$b->title = 'How not to PHP'; // Fatal Error
$b->author = 'Pedro H. Peterson'; // Fatal Error
$b->pubYear = 2023; // Fatal Error
?>
PHP 8.5부터는 클래스의 정적 프로퍼티에도 set 가시성을 적용할 수 있어요.
예제 #3 비대칭 정적 프로퍼티 가시성 (Asymmetric Static Property Visibility)
<?php
class Manager
{
public private(set) static int $calls = 0;
public function doAThing(): string
{
self::$calls++;
// Do other stuff.
return "some string";
}
}
$m = new Manager();
$m->doAThing(); // Works
echo Manager::$calls; // Works
Manager::$calls = 5; // Fatal error
?>
위 예제를 실행하면 다음과 같이 출력돼요.
1
Fatal error: Uncaught Error: Cannot modify private(set) property Manager::$calls from global scope in /some/file.php
비대칭 가시성과 관련해 몇 가지 주의할 점이 있어요.
-
set가시성을 따로 가질 수 있는 것은 타입이 지정된 프로퍼티뿐이에요. -
set가시성은get과 같거나 더 제한적이어야 해요. 즉public protected(set)과protected protected(set)은 허용되지만,protected public(set)은 문법 오류가 나요. -
프로퍼티가
public이라면 기본 가시성은 생략할 수 있어요. 즉public private(set)과private(set)은 같은 결과를 내요. -
private(set)가시성을 가진 프로퍼티는 자동으로final이 되어서, 자식 클래스에서 다시 선언할 수 없어요. -
프로퍼티의 참조(reference)를 얻는 동작은
get이 아니라set가시성을 따라요. 참조는 프로퍼티 값을 바꾸는 데 쓰일 수 있기 때문이에요. -
비슷하게, 배열 프로퍼티에 쓰기를 시도하면 내부적으로
get과set이 둘 다 일어나요. 그래서 항상 더 제한적인 쪽인set가시성을 따르게 돼요.
참고: set-가시성 선언에는 공백을 넣을 수 없어요.
private(set)가 맞고,private( set )처럼 쓰면 파싱 오류가 나요.
클래스가 다른 클래스를 상속하면, 자식 클래스는 final이 아닌 프로퍼티는 다시 정의할 수 있어요. 이때 기본 가시성이나 set 가시성을 넓힐 수 있지만, 넓힌 결과의 가시성은 부모 클래스와 같거나 더 넓어야 해요. 다만 주의할 점이 하나 있어요. private 프로퍼티를 오버라이드하면 실제로는 부모의 프로퍼티를 바꾸는 게 아니라, 내부 이름이 다른 새 프로퍼티를 만드는 것이에요.
예제 #4 비대칭 프로퍼티 상속 (Asymmetric Property inheritance)
<?php
class Book
{
protected string $title;
public protected(set) string $author;
protected private(set) int $pubYear;
}
class SpecialBook extends Book
{
public protected(set) string $title; // OK, as reading is wider and writing the same.
public string $author; // OK, as reading is the same and writing is wider.
public protected(set) int $pubYear; // Fatal Error. private(set) properties are final.
}
?>
메서드 가시성 (Method Visibility)
클래스 메서드를 public, private, protected 중 하나로 정의할 수 있어요. 가시성 키워드를 명시하지 않고 선언한 메서드는 기본적으로 public으로 정의돼요.
예제 #5 메서드 선언 (Method Declaration)
<?php
/**
* Define MyClass
*/
class MyClass
{
// Declare a public constructor
public function __construct() { }
// Declare a public method
public function MyPublic() { }
// Declare a protected method
protected function MyProtected() { }
// Declare a private method
private function MyPrivate() { }
// This is public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}
$myclass = new MyClass;
$myclass->MyPublic(); // Works
$myclass->MyProtected(); // Fatal Error
$myclass->MyPrivate(); // Fatal Error
$myclass->Foo(); // Public, Protected and Private work
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Fatal Error
}
}
$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Works
$myclass2->Foo2(); // Public and Protected work, not 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
?>
상수 가시성 (Constant Visibility)
PHP 7.1.0부터 클래스 상수를 public, private, protected 중 하나로 정의할 수 있어요. 가시성 키워드를 명시하지 않고 선언한 상수는 기본적으로 public으로 정의돼요.
예제 #6 상수 선언 (PHP 7.1.0부터)
<?php
/**
* Define MyClass
*/
class MyClass
{
// Declare a public constant
public const MY_PUBLIC = 'public';
// Declare a protected constant
protected const MY_PROTECTED = 'protected';
// Declare a private constant
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; // Works
MyClass::MY_PROTECTED; // Fatal Error
MyClass::MY_PRIVATE; // Fatal Error
$myclass->foo(); // Public, Protected and Private work
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function foo2()
{
echo self::MY_PUBLIC;
echo self::MY_PROTECTED;
echo self::MY_PRIVATE; // Fatal Error
}
}
$myclass2 = new MyClass2;
echo MyClass2::MY_PUBLIC; // Works
$myclass2->foo2(); // Public and Protected work, not Private
?>
다른 객체에서의 가시성 (Visibility from other objects)
같은 타입의 객체라면 인스턴스가 서로 달라도 서로의 private 멤버와 protected 멤버에 접근할 수 있어요. 그 객체 안에 들어가 보면 구현에 대한 구체적인 내용이 이미 알려져 있기 때문이죠.
예제 #7 같은 객체 타입의 private 멤버 접근 (Accessing private members of the same object type)
<?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)
{
// We can change the private property:
$other->foo = 'hello';
var_dump($other->foo);
// We can also call the private method:
$other->bar();
}
}
$test = new Test('test');
$test->baz(new Test('other'));
?>
위 예제를 실행하면 다음과 같이 출력돼요.
string(5) "hello"
Accessed the private method.