클래스와 객체 기초
class
기본 클래스 정의는 class
키워드로 시작하고, 그 뒤에 클래스 이름이 오고, 그 뒤에 클래스에 속하는 속성 및 메서드의 정의를 묶는 한 쌍의 중괄호가 옵니다.
클래스 이름은 PHP 예약어가 아닌 경우 모든 유효한 레이블이 될 수 있습니다. 유효한 클래스 이름은 문자 또는 밑줄로 시작하고 그 뒤에 임의의 수의 문자, 숫자 또는 밑줄이 옵니다. 정규식으로 다음과 같이 표현됩니다. ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
.
클래스에는 자체 상수, 변수("속성"이라고 함) 및 함수("메서드"라고 함)가 포함될 수 있습니다.
예제 #1 단순 클래스 정의
<?php
class SimpleClass
{
// property declaration
public $var = 'a default value';
// method declaration
public function displayVar() {
echo $this->var;
}
}
?>
의사 변수 $this는 객체 컨텍스트 내에서 메서드가 호출될 때 사용할 수 있습니다. $this는 호출 객체의 값입니다.
경고 비정적 메서드를 호출하면 정적으로 Error가 발생합니다. PHP 8.0.0 이전에는 사용 중단 알림이 생성되고 $this는 정의되지 않았습니다.
예제 #2 $this 의사 변수의 몇 가지 예
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this is not defined.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
PHP 7에서 위 예제의 출력:
$this is defined (A) Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this is not defined. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this is not defined. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this is not defined.
PHP 8에서 위 예제의 출력:
$this is defined (A) Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 {main} thrown in %s on line 27
new
클래스의 인스턴스를 생성하려면 new
키워드를 사용해야 합니다. 객체에 오류 발생 시 exception를 발생시키는 생성자가 정의되어 있지 않으면 객체가 항상 생성됩니다. 클래스는 인스턴스화 전에 정의되어야 합니다(어떤 경우에는 이것이 요구사항임).
클래스 이름을 포함하는 문자열이 new
와 함께 사용되면 해당 클래스의 새 인스턴스가 생성됩니다. 클래스가 네임스페이스에 있는 경우 이 작업을 수행할 때 정규화된 이름을 사용해야 합니다.
메모: 클래스 생성자에 전달할 인수가 없으면 클래스 이름 뒤의 괄호를 생략할 수 있습니다.
예제 #3 인스턴스 만들기
<?php
$instance = new SimpleClass();
// This can also be done with a variable:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>
클래스 컨텍스트에서 new self
와 new parent
에 의해 새로운 객체를 생성하는 것이 가능합니다.
클래스의 이미 생성된 인스턴스를 새 변수에 할당할 때 새 변수는 할당된 개체와 동일한 인스턴스에 액세스합니다. 이 동작은 인스턴스를 함수에 전달할 때도 동일합니다. 복제를 통해 이미 생성된 개체의 복사본을 만들 수 있습니다.
예제 #4 개체 할당
<?php
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance and $reference become null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
위의 예는 다음을 출력합니다.
NULL NULL object(SimpleClass)#1 (1) { ["var"]=> string(30) "$assigned will have this value" }
다음과 같은 몇 가지 방법으로 개체의 인스턴스를 만들 수 있습니다.
예제 #5 개체 할당
<?php
class Test
{
static public function getNew()
{
return new static;
}
}
class Child extends Test
{}
$obj1 = new Test();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);
$obj3 = Test::getNew();
var_dump($obj3 instanceof Test);
$obj4 = Child::getNew();
var_dump($obj4 instanceof Child);
?>
위의 예는 다음을 출력합니다.
bool(true) bool(true) bool(true)
단일 표현식에서 새로 생성된 객체의 멤버에 액세스할 수 있습니다.
예제 #6 새로 생성된 객체의 접근 멤버
<?php
echo (new DateTime())->format('Y');
?>
위의 예는 다음과 유사한 결과를 출력합니다.
2016
참고: PHP 7.1 이전에는 생성자 함수가 정의되지 않은 경우 인수가 평가되지 않았습니다.
속성 및 메서드
클래스 속성과 메서드는 별도의 "네임스페이스"에 있으므로 동일한 이름을 가진 속성과 메서드를 가질 수 있습니다. 속성과 메서드를 모두 참조하는 것은 동일한 표기법을 사용하며 속성에 액세스할지 아니면 메서드를 호출할지 여부는 컨텍스트, 즉 용도가 변수 액세스인지 함수 호출인지에 따라 다릅니다.
예제 #7 속성 액세스 대 메서드 호출
<?php
class Foo
{
public $bar = 'property';
public function bar() {
return 'method';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
위의 예는 다음을 출력합니다.
property method
즉, 속성에 할당된 익명 함수를 직접 호출하는 것은 불가능합니다. 예를 들어 속성을 먼저 변수에 할당해야 합니다. 이러한 속성을 괄호로 묶어 직접 호출할 수 있습니다.
예제 #8 속성에 저장된 익명 함수 호출
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
위의 예는 다음을 출력합니다.
42
extends
클래스는 클래스 선언에서 extends
키워드를 사용하여 다른 클래스의 상수, 메서드 및 속성을 상속할 수 있습니다. 여러 클래스를 확장하는 것은 불가능합니다. 클래스는 하나의 기본 클래스에서만 상속할 수 있습니다.
상속된 상수, 메서드 및 속성은 부모 클래스에 정의된 동일한 이름으로 다시 선언하여 재정의할 수 있습니다. 그러나 부모 클래스가 메서드나 상수를 final로 정의한 경우 재정의할 수 없습니다. parent::로 참조하여 재정의된 메서드 또는 정적 속성에 액세스할 수 있습니다.
참고: PHP 8.1.0부터 상수는 final로 선언될 수 있습니다.
예제 #9 단순 클래스 상속
<?php
class ExtendClass extends SimpleClass
{
// Redefine the parent method
function displayVar()
{
echo "Extending class\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
위의 예는 다음을 출력합니다.
Extending class a default value
서명 호환성 규칙
메서드를 재정의할 때 해당 서명은 부모 메서드와 호환되어야 합니다. 그렇지 않으면 치명적인 오류가 발생하거나 PHP 8.0.0 이전에는 E_WARNING
수준 오류가 생성됩니다. 서명은 분산 규칙을 준수하고 필수 매개변수를 선택사항으로 만들고 새 매개변수가 선택사항인 경우 호환됩니다. 이것은 Liskov 치환 원리 또는 줄여서 LSP로 알려져 있습니다. 생성자 및 private
메서드는 이러한 서명 호환성 규칙에서 제외되므로 서명이 일치하지 않는 경우 치명적인 오류가 발생하지 않습니다.
예제 #10 호환되는 자식 메서드
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
위의 예는 다음을 출력합니다.
Valid Valid
다음 예는 매개변수를 제거하거나 선택적 매개변수를 필수로 만드는 자식 메서드가 부모 메서드와 호환되지 않음을 보여줍니다.
예제 #11 자식 메서드가 매개변수를 제거하면 치명적인 오류가 발생합니다.
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
PHP 8에서 위 예제의 출력은 다음과 유사합니다.
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
예제 #12 자식 메서드가 선택적 매개변수를 필수로 만들 때 치명적인 오류가 발생합니다.
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
PHP 8에서 위 예제의 출력은 다음과 유사합니다.
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
경고 자식 클래스에서 메서드 매개 변수의 이름을 바꾸는 것은 서명 비호환성이 아닙니다. 그러나 명명된 인수가 사용되는 경우 런타임 오류가 발생하므로 권장하지 않습니다.
예제 #13 명명된 인수를 사용할 때 오류가 발생하고 매개변수의 이름이 하위 클래스에서 변경되었습니다.
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Pass parameters according to A::test() contract
$obj->test(foo: "foo", bar: "bar"); // ERROR!
위의 예는 다음과 유사한 결과를 출력합니다.
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 {main} thrown in /in/XaaeN on line 14
::class
class
키워드는 클래스 이름 확인에도 사용됩니다. ClassName
클래스의 완전한 이름을 얻으려면 ClassName::class
를 사용하십시오. 이것은 네임스페이스 클래스에서 특히 유용합니다.
예제 #14 클래스 이름 확인
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
위의 예는 다음을 출력합니다.
NS\ClassName
메모: ::class
를 사용한 클래스 이름 확인은 컴파일 시간 변환입니다. 즉, 클래스 이름 문자열이 생성될 때 아직 자동 로드가 발생하지 않았습니다. 따라서 클래스가 존재하지 않더라도 클래스 이름이 확장됩니다. 이 경우 오류가 발생하지 않습니다.
예제 #15 클래스 이름 확인 누락
<?php
print Does\Not\Exist::class;
?>
위의 예는 다음을 출력합니다.
Does\Not\Exist
PHP 8.0.0부터 ::class
상수는 객체에도 사용할 수 있습니다. 이 해결은 컴파일 시간이 아니라 런타임에 발생합니다. 그 효과는 객체에 대해 get_class()를 호출하는 것과 같습니다.
예제 #16 개체 이름 확인
<?php
namespace NS {
class ClassName {
}
}
$c = new ClassName();
print $c::class;
?>
위의 예는 다음을 출력합니다.
NS\ClassName
Nullsafe 메서드 및 속성
PHP 8.0.0부터 속성과 메서드는 ?->
대신 "nullsafe" 연산자를 사용하여 액세스할 수도 있습니다. nullsafe 연산자는 역참조되는 개체가 null
인 경우 예외가 throw되지 않고 null
이 반환된다는 점을 제외하고는 위와 같은 속성 또는 메서드 액세스와 동일하게 작동합니다. 역참조가 체인의 일부인 경우 체인의 나머지 부분은 건너뜁니다.
효과는 is_null() 검사에서 각 액세스를 먼저 래핑하는 것과 유사하지만 더 간결합니다.
예제 #17 Nullsafe Operator
<?php
// As of PHP 8.0.0, this line:
$result = $repository?->getUser(5)?->name;
// Is equivalent to the following code block:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
메모: nullsafe 연산자는 null이 속성 또는 메서드 반환에 대해 유효하고 예상 가능한 값으로 간주될 때 가장 잘 사용됩니다. 오류를 나타내기 위해서는 throw된 예외가 바람직합니다.