클래스와 객체 기초

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 selfnew 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된 예외가 바람직합니다.