[php] $ this 이상으로 언제 자기를 사용해야합니까?

PHP 5에서 사용하는 것의 차이점은 무엇입니까? self$this? 입니까?

언제 각각 적절한가요?



답변

짧은 답변

$this현재 객체를 참조하는 데 사용 합니다. self현재 클래스를 참조하는 데 사용 합니다. 즉,
$this->member비 정적 멤버에 사용 self::$member하고 정적 멤버에 사용하십시오.

전체 답변

비 정적 및 정적 멤버 변수 를 올바르게 사용 $this하고 사용 하는 예는 다음과 같습니다 self.

<?php
class X {
    private $non_static_member = 1;
    private static $static_member = 2;

    function __construct() {
        echo $this->non_static_member . ' '
           . self::$static_member;
    }
}

new X();
?>

비 정적 및 정적 멤버 변수 를 잘못 사용 $this하고 사용 하는 예는 다음과 같습니다 self.

<?php
class X {
    private $non_static_member = 1;
    private static $static_member = 2;

    function __construct() {
        echo self::$non_static_member . ' '
           . $this->static_member;
    }
}

new X();
?>

다음의 예는 다형성$this멤버 함수는 :

<?php
class X {
    function foo() {
        echo 'X::foo()';
    }

    function bar() {
        $this->foo();
    }
}

class Y extends X {
    function foo() {
        echo 'Y::foo()';
    }
}

$x = new Y();
$x->bar();
?>

다음은 멤버 함수 를 사용하여 다형성 동작억제 하는 예입니다 self.

<?php
class X {
    function foo() {
        echo 'X::foo()';
    }

    function bar() {
        self::foo();
    }
}

class Y extends X {
    function foo() {
        echo 'Y::foo()';
    }
}

$x = new Y();
$x->bar();
?>

아이디어는 현재 객체의 정확한 유형이 무엇이든 멤버 함수 를 $this->foo()호출 한다는 것입니다 foo(). 객체가 객체 type X인 경우를 호출합니다 X::foo(). 객체가의 type Y경우를 호출합니다 Y::foo(). 그러나 self :: foo ()를 사용 X::foo()하면 항상 호출됩니다.

에서 http://www.phpbuilder.com/board/showthread.php?t=10354489 :

으로 http://board.phpbuilder.com/member.php?145249-laserlight


답변

self 키워드는 정적 멤버로 제한하는 방식이 아닌 ‘현재 클래스’를 가리키는 것이 아닙니다 . 정적이 아닌 멤버의 컨텍스트 내에서 현재 객체에 대한 selfvtable을 무시하는 방법 (vtable의 wiki 참조) 도 제공 합니다. parent::methodName()부모 버전의 함수를 호출하는 데 사용할 수있는 것처럼 self::methodName()메서드의 현재 클래스 구현을 호출 하기 위해 호출 할 수 있습니다 .

class Person {
    private $name;

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

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();

출력됩니다 :

안녕하세요, 저는 Ludwig에서 온 괴짜
안녕 Ludwig입니다

sayHello()$this포인터를 사용 하므로 vtable이 호출되도록 호출 Geek::getTitle()됩니다.
sayGoodbye()를 사용 self::getTitle()하므로 vtable이 사용되지 않고 Person::getTitle()호출됩니다. 두 경우 모두 인스턴스화 된 객체의 메서드를 다루며 $this호출 된 함수 내의 포인터에 액세스 할 수 있습니다.


답변

DO NOT USE self::사용static::

자기의 또 다른 측면이 있습니다 : 그것은 언급 할 가치가 있습니다. 성가신 self::것은 실행 시점이 아닌 정의 시점의 범위를 나타냅니다 . 다음 두 가지 방법으로이 간단한 클래스를 고려하십시오.

class Person
{

    public static function status()
    {
        self::getStatus();
    }

    protected static function getStatus()
    {
        echo "Person is alive";
    }

}

전화 Person::status()하면 “사람이 살아있다”는 메시지가 나타납니다. 이제 이것을 상속받은 클래스를 만들 때 어떤 일이 발생하는지 고려하십시오.

class Deceased extends Person
{

    protected static function getStatus()
    {
        echo "Person is deceased";
    }

}

호출 Deceased::status()할 때 “사람이 사망 했음”이 표시 될 것으로 예상되지만, 호출 할 때 범위에 원래 메소드 정의가 포함되어 있으므로 “사람이 살아 남았습니다” self::getStatus().

PHP 5.3에는 해결책이 있습니다. static::해상도 연산자 구현은 호출 클래스의 범위에 바인딩 된 것을 말하는 멋진 방법이다 “늦게 정적 바인딩”. 에 줄을 변경 status()하는 static::getStatus()결과는 무엇을 기대합니다. 이전 버전의 PHP에서는이를 수행하기 위해 kludge를 찾아야합니다.

PHP 문서를 참조하십시오

그래서 질문대로 대답하지 않기 위해 …

$this->현재 객체 (클래스의 인스턴스)를 static::참조하는 반면 , 클래스는


답변

정말 우리가 우리가 이야기 할 때 무슨 말을하는지 이해하는 데 self비해 $this, 우리는 실제로 개념 및 실제적인 수준에 무슨 일이 일어나고 있는지에 파고해야합니다. 나는 실제로 어떤 대답도 적절하게 수행한다고 생각하지 않으므로 여기에 내 시도가 있습니다.

클래스객체 가 무엇인지 이야기하면서 시작하겠습니다 .

개념적으로 클래스와 객체

그럼, 이다 클래스는 ? 많은 사람들이 그것을 청사진 이나 객체 의 템플릿 으로 정의 합니다. 실제로, PHP 클래스에 대한 정보는 여기를 참조하십시오 . 그리고 그것은 어느 정도 그것이 진짜입니다. 수업을 봅시다 :

class Person {
    public $name = 'my name';
    public function sayHello() {
        echo "Hello";
    }
}

알 수 있듯이 해당 클래스에는이라는 속성 $name과이라는 메서드 (함수)가 sayHello()있습니다.

그것은이다 매우 하는 것이 중요 클래스는 정적 구조입니다. 즉 Person, 일단 정의 된 클래스 는 보는 곳마다 항상 동일합니다.

반면에 객체 는 클래스 의 인스턴스 입니다. 즉, 클래스의 “청사진”을 가져 와서 동적 사본을 만드는 데 사용합니다. 이 복사본은 이제 저장된 변수에 연결됩니다. 따라서 인스턴스의 모든 변경 사항은 해당 인스턴스 에 로컬입니다.

$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"

연산자를 사용하여 클래스의 새 인스턴스 를 만듭니다 new.

따라서 클래스는 전역 구조이고 객체는 로컬 구조라고합니다. 이 재미있는 ->구문 에 대해 걱정하지 마십시오 .

우리가에 대한 이야기해야 또 한가지는 우리가 할 수 있다는 것입니다 확인 인스턴스가있는 경우 instanceof: 특정 클래스 $bob instanceof Person경우 부울을 반환 $bob인스턴스가 사용되었다 Person클래스를, 또는 의 아이가 Person.

상태 정의

클래스에 실제로 포함 된 내용을 조금 살펴 보겠습니다. 클래스에는 5 가지 유형의 “사물”이 있습니다.

  1. 속성 -각 인스턴스에 포함될 변수로 생각하십시오.

    class Foo {
        public $bar = 1;
    }
  2. 정적 속성 -클래스 수준에서 공유되는 변수로 생각하십시오. 각 인스턴스에 의해 복사되지 않음을 의미합니다.

    class Foo {
        public static $bar = 1;
    }
  3. 메소드 -각 인스턴스가 포함하고 인스턴스에서 작동하는 함수입니다.

    class Foo {
        public function bar() {}
    }
  4. 정적 메소드 -전체 클래스에서 공유되는 함수입니다. 인스턴스에서는 작동 하지 않지만 정적 속성에서만 작동합니다.

    class Foo {
        public static function bar() {}
    }
  5. 상수 -클래스 해석 상수. 더 깊이 들어 가지 않고 완전성을 추가하십시오.

    class Foo {
        const BAR = 1;
    }

따라서 기본적으로 우리는 정보가 공유되는지 (따라서 정적 인) 아닌지 (그리고 동적)를 식별하는 static 에 대한 “힌트”를 사용하여 클래스와 객체 컨테이너에 정보를 저장합니다 .

상태 및 방법

메소드 내에서 객체의 인스턴스는 $this변수 로 표시됩니다 . 해당 객체의 현재 상태가 있으며 속성을 변경 (변경)하면 해당 인스턴스가 변경되지만 다른 인스턴스는 변경되지 않습니다.

메소드가 정적으로 호출되면 $this변수 가 정의되지 않습니다 . 정적 호출과 연결된 인스턴스가 없기 때문입니다.

여기서 흥미로운 점은 정적 호출 방법입니다. 우리가 상태에 접근하는 방법에 대해 이야기 해 봅시다 :

접근 상태

이제 해당 상태를 저장 했으므로 해당 상태에 액세스해야합니다. 이 비트를 얻을 수있는 까다로운 (또는 방법으로 더 많은 비트 이상), 그래서 두 가지 관점으로이 분할하자 : 인스턴스 / 클래스의 외부에서 (일반 함수 호출에서, 또는 글로벌 범위에서 말하는), 그리고 인스턴스의 내부 / class (객체의 메소드 내에서).

인스턴스 / 클래스 외부에서

인스턴스 / 클래스 외부에서 우리의 규칙은 매우 간단하고 예측 가능합니다. 우리는 두 개의 연산자를 가지고 있으며 각각 인스턴스 또는 클래스 정적을 처리하고 있는지 즉시 알려줍니다.

  • ->객체 연산자 – 우리는 인스턴스를 액세스하는 경우는 항상 사용됩니다.

    $bob = new Person;
    echo $bob->name;

    호출 Person->foo은 의미가 없습니다 ( Person인스턴스가 아닌 클래스이므로). 따라서 구문 분석 오류입니다.

  • ::범위 해상도 연산자 -이 항상 액세스 클래스의 정적 속성 또는 메서드를 사용합니다.

    echo Foo::bar()

    또한 동일한 방법으로 객체에서 정적 메서드를 호출 할 수 있습니다.

    echo $foo::bar()

    그것은이다 매우 우리는이 작업을 수행 할 때주의하는 것이 중요 외부에서 , 객체의 인스턴스가 숨겨져 bar()방법. 실행과 정확히 동일하다는 것을 의미합니다.

    $class = get_class($foo);
    $class::bar();

따라서 $this정적 호출에 정의되지 않았습니다.

인스턴스 / 클래스 내부에서

여기서 조금 변합니다. 동일한 연산자가 사용되지만 의미가 크게 흐리게 표시됩니다.

객체 연산자는 -> 여전히 객체의 인스턴스 상태에 대한 호출을 만드는 데 사용됩니다.

class Foo {
    public $a = 1;
    public function bar() {
        return $this->a;
    }
}

object-operator를 사용하여 (의 인스턴스 ) 에서 bar()메소드 호출 :$fooFoo$foo->bar() 인스턴스의 버전이 $a됩니다.

이것이 우리가 기대하는 방식입니다.

::연산자 의 의미는 변경됩니다. 현재 함수에 대한 호출 컨텍스트에 따라 다릅니다.

  • 정적 컨텍스트 내

    정적 컨텍스트에서 사용하는 모든 호출 ::도 정적입니다. 예를 보자.

    class Foo {
        public function bar() {
            return Foo::baz();
        }
        public function baz() {
            return isset($this);
        }
    }

    호출 Foo::bar()부르는 것이다 baz()정적 방법을, 따라서 $this것입니다 하지 채워. E_STRICT정적이 아닌 메소드를 정적으로 호출하기 때문에 최신 버전의 PHP (5.3+)에서는 오류가 발생할 수 있습니다.

  • 인스턴스 컨텍스트 내

    반면 인스턴스 컨텍스트 내에서 :: 의 수신자 (우리가 호출하는 방법)에 따라 다릅니다. 메소드가로 정의 static되면 정적 호출을 사용합니다. 그렇지 않으면 인스턴스 정보를 전달합니다.

    따라서 위의 코드를 보면 “정적”호출이 인스턴스 컨텍스트 내에서 발생하기 때문에 호출 $foo->bar()은을 반환 true합니다.

말이 되나요? 그렇게 생각하지 않았다. 혼란 스럽습니다.

짧은 키워드

클래스 이름을 사용하여 모든 것을 함께 묶는 것은 다소 더럽 기 때문에 PHP는 3 가지 기본 “단축”키워드를 제공하여 범위를 쉽게 해결할 수 있습니다.

  • self-현재 클래스 이름을 나타냅니다. 그래서 self::baz()과 동일 Foo::baz()내에서 Foo클래스 (거기에 어떤 방법).

  • parent -이것은 현재 클래스의 부모를 나타냅니다.

  • static-이것은 호출 된 클래스를 나타냅니다. 상속 덕분에 자식 클래스는 메서드와 정적 속성을 재정의 할 수 있습니다. 따라서 static클래스 이름 대신 사용하여 호출 하면 현재 수준이 아니라 호출이 어디서 왔는지 확인할 수 있습니다.

이를 이해하는 가장 쉬운 방법은 몇 가지 예를 살펴 보는 것입니다. 수업을 고르자 :

class Person {
    public static $number = 0;
    public $id = 0;
    public function __construct() {
        self::$number++;
        $this->id = self::$number;
    }
    public $name = "";
    public function getName() {
        return $this->name;
    }
    public function getId() {
        return $this->id;
    }
}

class Child extends Person {
    public $age = 0;
    public function __construct($age) {
        $this->age = $age;
        parent::__construct();
    }
    public function getName() {
        return 'child: ' . parent::getName();
    }
}

이제 우리는 또한 상속을보고 있습니다. 이것이 잘못된 객체 모델이라는 것을 잠시 무시하십시오.

$bob = new Person;
$bob->name = "Bob";
$adam = new Person;
$adam->name = "Adam";
$billy = new Child;
$billy->name = "Billy";
var_dump($bob->getId()); // 1
var_dump($adam->getId()); // 2
var_dump($billy->getId()); // 3

따라서 ID 카운터는 인스턴스와 하위에서 공유 self됩니다 (액세스에 사용 하고 있기 때문에 사용 합니다. static하위 클래스에서 재정의 할 수 있음).

var_dump($bob->getName()); // Bob
var_dump($adam->getName()); // Adam
var_dump($billy->getName()); // child: Billy

우리는 매번 Person::getName() 인스턴스 메소드를 실행합니다 . 그러나 우리는 parent::getName()사례 중 하나 (자식 사례) 에서이 작업을 수행하고 있습니다. 이것이이 접근 방식을 강력하게 만듭니다.

주의의 말 # 1

호출 컨텍스트는 인스턴스 사용 여부를 결정합니다. 따라서:

class Foo {
    public function isFoo() {
        return $this instanceof Foo;
    }
}

항상 사실 은 아닙니다 .

class Bar {
    public function doSomething() {
        return Foo::isFoo();
    }
}
$b = new Bar;
var_dump($b->doSomething()); // bool(false)

이제는 정말 이상합니다. 우리는 다른 클래스를 호출하고 있지만 메소드에 $this전달되는 Foo::isFoo()것은의 인스턴스입니다 $bar.

이로 인해 모든 종류의 버그와 개념적 WTF-ery가 발생할 수 있습니다. 나는 높은 피하는 게 좋을 것 그래서 ::그 세 가상 “바로 가기”키워드를 제외하고 아무것도 인스턴스 메서드 내에서 연산자 ( static, self, 및 parent).

주의의 말 # 2

정적 메서드와 속성은 모든 사람이 공유합니다. 기본적으로 전역 변수가됩니다. 전 세계와 동일한 문제가 있습니다. 따라서 전 세계에 익숙하지 않다면 정적 메서드 / 속성에 정보를 저장하는 것이 정말 주저합니다.

주의 사항 # 3

일반적으로을 static대신 하여 Late-Static-Binding이라는 것을 사용하고 싶을 것입니다 self. 그러나 그것들은 같은 것이 아니므로 “항상 static대신에 항상 사용 하는 self것은 근시안적입니다. “라고 말하고 , 대신하고 싶은 호출에 대해 생각하고 자식 클래스가 정적 해결 을 재정의 할 수 있도록하려면 생각하십시오. 요구.

TL / DR

너무 나쁘고 돌아가서 읽어보십시오. 너무 길지만 복잡한 주제이기 때문에 너무 깁니다.

TL / DR # 2

알았어 괜찮아. 즉, 클래스에서 현재 클래스 이름self 을 참조 하는 데 사용됩니다 . 여기서 as $this는 현재 객체 인스턴스 를 나타냅니다 . 참고 self카피는 / 바로 가기를 붙여 넣습니다. 클래스 이름으로 안전하게 바꿀 수 있으며 정상적으로 작동합니다. 그러나$this 미리 결정할 수없는 동적 변수입니다 (클래스가 아닐 수도 있습니다).

TL / DR # 3

객체 연산자가 사용 된 경우 ( ->) 항상 인스턴스를 처리하고 있다는 것을 알고 있습니다. scope-resolution-operator가 사용되는 경우 ( ::) 컨텍스트에 대한 추가 정보가 필요합니다 (이미 오브젝트 컨텍스트에 있습니까? 오브젝트 외부에 있습니까? 등).


답변

self($ self가 아님)은 클래스 의 유형$this나타내며 , 여기서 클래스 의 현재 인스턴스 를 나타냅니다 . self정적 멤버 함수에 사용하여 정적 멤버 변수에 액세스 할 수 있습니다. $this비 정적 멤버 함수에 사용되며 멤버 함수가 호출 된 클래스의 인스턴스에 대한 참조입니다.

때문에 this개체입니다, 당신은 그것을처럼 사용$this->member

self객체가 아니기 때문에 기본적으로 현재 클래스를 자동으로 참조하는 유형입니다.self::member


답변

$this-> 클래스 변수 (멤버 변수) 또는 메서드의 특정 인스턴스를 나타내는 데 사용됩니다.

Example:
$derek = new Person();

$ derek은 이제 Person의 특정 인스턴스입니다. 모든 Person은 first_name과 last_name을 가지고 있지만 $ derek은 특정한 first_name과 last_name을 가지고 있습니다 (Derek Martin). $ derek 인스턴스 내에서 $ this-> first_name 및 $ this-> last_name으로 참조 할 수 있습니다.

ClassName ::은 해당 유형의 클래스와 정적 변수 인 정적 메서드를 나타내는 데 사용됩니다. 도움이되면 정신적으로 “정적”이라는 단어를 “공유”로 바꿀 수 있습니다. 공유되어 있기 때문에 특정 인스턴스 (공유되지 않음)를 나타내는 $ this를 참조 할 수 없습니다. 정적 변수 (정적 $ db_connection)는 객체 유형의 모든 인스턴스간에 공유 될 수 있습니다. 예를 들어 모든 데이터베이스 개체는 단일 연결 (정적 $ connection)을 공유합니다.

정적 변수 예 :
단일 멤버 변수가있는 데이터베이스 클래스가 있다고 가정합니다. static $ num_connections; 이제 이것을 생성자에 넣습니다.

function __construct()
{
    if(!isset $num_connections || $num_connections==null)
    {
        $num_connections=0;
    }
    else
    {
        $num_connections++;
    }
}

객체에 생성자가있는 것처럼 객체도 죽거나 설정이 해제 될 때 실행되는 소멸자가 있습니다.

function __destruct()
{
    $num_connections--;
}

새 인스턴스를 만들 때마다 연결 카운터가 하나씩 증가합니다. 인스턴스 사용을 중단하거나 중단 할 때마다 연결 카운터가 하나씩 줄어 듭니다. 이런 식으로 우리는 우리가 사용하고있는 데이터베이스 객체의 인스턴스 수를 모니터링 할 수 있습니다 :

echo DB::num_connections;

$ num_connections는 정적 (공유)이므로 총 활성 데이터베이스 개체 수를 반영합니다. 데이터베이스 클래스의 모든 인스턴스간에 데이터베이스 연결을 공유하는 데이 기술이 사용 된 것을 보았을 것입니다. 데이터베이스 연결을 만드는 데 시간이 오래 걸리기 때문에이 작업을 수행하므로 하나만 만들어 공유하는 것이 가장 좋습니다 (싱글 톤 패턴이라고 함).

정적 메소드 (예 : public static View :: format_phone_number ($ digits))는 먼저 해당 오브젝트 중 하나를 인스턴스화하지 않고 사용할 수 있습니다 (즉, 내부적으로 $ this를 참조하지 않음).

정적 방법 예 :

public static function prettyName($first_name, $last_name)
{
    echo ucfirst($first_name).' '.ucfirst($last_name);
}

echo Person::prettyName($derek->first_name, $derek->last_name);

보다시피 public static 함수 prettyName은 객체에 대해 아무것도 모른다. 객체의 일부가 아닌 일반 함수처럼 전달하는 매개 변수로 작업하고 있습니다. 그렇다면 왜 우리가 물건의 일부로 그것을 가질 수 없다면 왜 귀찮게합니까?

  1. 먼저, 객체에 기능을 연결하면 객체를 정리하는 데 도움이되므로 찾을 위치를 알 수 있습니다.
  2. 둘째, 이름 충돌을 방지합니다. 큰 프로젝트에서는 두 명의 개발자가 getName () 함수를 작성하게 할 것입니다. 하나는 ClassName1 :: getName ()을 작성하고 다른 하나는 ClassName2 :: getName ()을 작성해도 전혀 문제가되지 않습니다. 충돌이 없습니다. 예 정적 방법!

SELF :: 참조하려는 정적 메소드가있는 오브젝트 외부 에서
코딩 하는 경우 오브젝트 이름을 사용하여 호출해야합니다. View :: format_phone_number ($ phone_number); 당신이 코딩하는 경우 안에 당신이 있습니다, 참조 할 정적 메서드가있는 객체 중 하나를 오브젝트의 이름보기 :: format_phone_number ($ PN)를 사용, 또는 셀프 :: format_phone_number ($ PN)를 사용할 수 있습니다를 바로 가기

정적 변수도 마찬가지입니다.
예 : View :: templates_path와 self :: templates_path

DB 클래스 내에서 다른 객체의 정적 메서드를 참조하는 경우 객체 이름을 사용합니다.
예 : Session :: getUsersOnline ();

그러나 DB 클래스가 자체 정적 변수를 참조하려면 self라고 말하면됩니다.
예 : self :: connection;

희망을 정리하는 데 도움이되기를 바랍니다 🙂


답변

에서 이 블로그 게시물 :

  • self 현재 클래스를 참조
  • self 정적 함수를 호출하고 정적 멤버 변수를 참조하는 데 사용할 수 있습니다.
  • self 정적 함수 내에서 사용할 수 있습니다
  • self vtable을 우회하여 다형성 동작을 끌 수도 있습니다.
  • $this 현재 객체를 참조
  • $this 정적 함수를 호출하는 데 사용할 수 있습니다
  • $this정적 멤버 변수를 호출하는 데 사용해서는 안됩니다. self대신 사용하십시오 .
  • $this 정적 함수 내에서 사용할 수 없습니다