Когда использовать себя сверх $ this? - PullRequest
1891 голосов
/ 30 сентября 2008

В PHP 5, в чем разница между использованием self и $this?

Когда каждый уместен?

Ответы [ 22 ]

1619 голосов
/ 30 сентября 2008

Короткий ответ

Используйте $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

726 голосов
/ 27 июля 2009

Ключевое слово self NOT относится только к «текущему классу», по крайней мере не таким образом, чтобы ограничивать вас статическими членами. В контексте нестатического члена self также предоставляет способ обхода vtable ( см. Вики по vtable ) для текущего объекта. Так же, как вы можете использовать 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();

Будет выведено:

Привет, я Людвиг, выродок
Прощай, Людвиг, человек

sayHello() использует указатель $this, поэтому vtable вызывается для вызова Geek::getTitle(). sayGoodbye() использует self::getTitle(), поэтому vtable не используется и вызывается Person::getTitle(). В обоих случаях мы имеем дело с методом экземпляра объекта и имеем доступ к указателю $this в вызываемых функциях.

444 голосов
/ 24 июля 2011

НЕ ИСПОЛЬЗУЙТЕ 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:: относится к классу

238 голосов
/ 10 июня 2013

Чтобы по-настоящему понять, о чем мы говорим, когда говорим о self против $this, нам нужно действительно разобраться в том, что происходит на концептуальном и практическом уровне. Я действительно не чувствую, что какой-либо из ответов делает это должным образом, поэтому вот моя попытка.

Давайте начнем с разговора о том, что такое класс и объект .

Классы и объекты, концептуально

Итак, что есть a класс ? Многие люди определяют его как проект или шаблон для объекта. Более того, вы можете прочитать о классах в 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 не определена . Это потому, что нет никакого экземпляра, связанного со статическим вызовом.

Интересно то, как выполняются статические вызовы. Итак, давайте поговорим о том, как мы получаем доступ к состоянию:

Состояние доступа

Итак, теперь, когда мы сохранили это состояние, нам нужно получить к нему доступ. Это может быть немного сложно (или way больше, чем немного), поэтому давайте разделим это на две точки зрения: снаружи экземпляра / класса (скажем, из обычного вызова функции или из глобальной области видимости) и внутри экземпляра / класса (изнутри метода объекта).

Снаружи экземпляра / класса

Снаружи экземпляра / класса наши правила довольно просты и предсказуемы. У нас есть два оператора, и каждый сразу сообщает нам, имеем ли мы дело с экземпляром или классом static:

  • -> - объект-оператор - Это всегда используется, когда мы обращаемся к экземпляру.

    $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;
    }
}

Вызов метода bar() для $foo (экземпляр Foo) с использованием объекта-оператора: $foo->bar() приведет к версии экземпляра $a.

Так мы и ожидаем.

Значение оператора :: хотя и меняется. Это зависит от контекста вызова текущей функции:

  • В статическом контексте

    В статическом контексте любые вызовы, сделанные с использованием ::, также будут статическими. Давайте посмотрим на пример:

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

    Вызов Foo::bar() вызовет метод baz() статически, и поэтому $this будет не заполняться. Стоит отметить, что в последних версиях PHP (5.3+) это вызовет ошибку E_STRICT, потому что мы вызываем нестатические методы статически.

  • В контексте экземпляра

    С другой стороны, в контексте экземпляра вызовы, сделанные с использованием ::, зависят от получателя вызова (метода, который мы вызываем). Если метод определен как 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

Таким образом, счетчик идентификаторов является общим для обоих экземпляров и дочерних элементов (поскольку мы используем self для доступа к нему. Если мы использовали static, мы могли бы переопределить его в дочернем классе).

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

Обратите внимание, что мы каждый раз выполняем метод Person::getName() instance . Но мы используем 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-ошибки. Поэтому я настоятельно рекомендую избегать использования оператора :: из методов экземпляра для чего-либо, кроме этих трех виртуальных «сокращенных» ключевых слов (static, self и parent).

Слово предостережения № 2

Обратите внимание, что статические методы и свойства доступны всем. Это делает их в основном глобальными переменными. Со всеми теми же проблемами, которые идут с глобалами. Поэтому я бы не решался хранить информацию в статических методах / свойствах, если вы не уверены, что она действительно глобальная.

Слово предостережения # 3

Как правило, вы хотите использовать то, что известно как Late-Static-Binding, используя static вместо self. Но обратите внимание, что это не одно и то же, поэтому выражение «всегда использовать static вместо self действительно недальновидно. Вместо этого остановитесь и подумайте о вызове, который вы хотите сделать, и подумайте, хотите ли вы, чтобы дочерние классы были возможность переопределить этот статический разрешенный вызов.

TL / DR

Жаль, вернись и прочитай это. Это может быть слишком долго, но это так долго, потому что это сложная тема

TL / DR # 2

Хорошо, хорошо. Вкратце, self используется для ссылки на имя текущего класса внутри класса, где $this относится к текущему объекту экземпляр . Обратите внимание, что self является ярлыком копирования / вставки. Вы можете смело заменить его своим именем класса, и оно будет работать нормально. Но $this - это динамическая переменная, которая не может быть определена заранее (и даже не может быть вашим классом).

TL / DR # 3

Если используется объект-оператор (->), то вы всегда знаете, что имеете дело с экземпляром. Если используется оператор scope-resolution-operator (::), вам потребуется дополнительная информация о контексте (мы уже в объект-контексте? Мы вне объекта? И т. Д.).

115 голосов
/ 30 сентября 2008

self (не $ self) относится к типу класса, где $this относится к текущему экземпляру класса. self предназначен для использования в статических функциях-членах, чтобы позволить вам получить доступ к статическим переменным-членам. $this используется в нестатических функциях-членах и является ссылкой на экземпляр класса, для которого была вызвана функция-член.

Поскольку this является объектом, вы используете его как: $this->member

Поскольку self не является объектом, это в основном тип, который автоматически ссылается на текущий класс, вы используете его следующим образом: self::member

96 голосов
/ 22 октября 2008

$this-> используется для ссылки на конкретный экземпляр переменных класса (переменных-членов) или методов.

Example: 
$derek = new Person();

$ derek теперь является конкретным экземпляром Person. У каждого персонажа есть имя и фамилия, но у $ derek есть конкретные имя и фамилия (Дерек Мартин). Внутри экземпляра $ derek мы можем ссылаться на них как $ this-> first_name и $ this-> last_name

ClassName :: используется для ссылки на этот тип класса и его статические переменные, статические методы. Если это поможет, вы можете мысленно заменить слово «статический» на «общий». Поскольку они являются общими, они не могут ссылаться на $ this, что относится к конкретному экземпляру (не общему). Статические переменные (то есть static $ db_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 является статическим (общим), оно будет отражать общее количество активных объектов базы данных. Возможно, вы видели этот метод, используемый для совместного использования соединений с базой данных среди всех экземпляров класса базы данных. Это сделано потому, что создание соединения с базой данных занимает много времени, поэтому лучше всего создать только одно и поделиться им (это называется Singleton Pattern).

Статические методы (то есть 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);

Как видите, публичная статическая функция prettyName ничего не знает об объекте. Он просто работает с параметрами, которые вы передаете, как обычная функция, которая не является частью объекта. Зачем тогда беспокоиться, если бы мы могли иметь это не как часть объекта?

  1. Во-первых, прикрепление функций к объектам помогает вам упорядочить вещи, чтобы вы знали, где их найти.
  2. Во-вторых, это предотвращает конфликты имен. В большом проекте вам, скорее всего, придется два разработчика, создающих функции getName (). Если один создает ClassName1 :: getName (), а другой создает ClassName2 :: getName (), это не проблема. Нет конфликта Yay статические методы!

SELF :: Если вы кодируете вне объекта, который имеет статический метод, на который вы хотите сослаться, вы должны вызвать его, используя имя объекта View :: format_phone_number ($ phone_number); Если вы кодируете внутри объекта, который имеет статический метод, к которому вы хотите обратиться, вы можете либо использовать имя объекта View :: format_phone_number ($ pn), ИЛИ вы можете использовать ярлык self :: format_phone_number ($ pn)

То же самое касается статических переменных: Пример: Просмотр :: templates_path и self :: templates_path

Внутри класса DB, если бы мы ссылались на статический метод какого-то другого объекта, мы бы использовали имя объекта: Пример: Session :: getUsersOnline ();

Но если бы класс DB хотел ссылаться на свою статическую переменную, он просто сказал бы: Пример: self :: connection;

Надеюсь, это поможет прояснить ситуацию:)

29 голосов
/ 10 мая 2013

С этого блога :

  • self относится к текущему классу
  • self может использоваться для вызова статических функций и ссылки на статические переменные-члены
  • self может использоваться внутри статических функций
  • self также может отключить полиморфное поведение, минуя vtable
  • $this относится к текущему объекту
  • $this может использоваться для вызова статических функций
  • $this не должен использоваться для вызова статических переменных-членов. Вместо этого используйте self.
  • $this нельзя использовать внутри статических функций
25 голосов
/ 29 декабря 2014

В PHP вы используете ключевое слово self для доступа к статическим свойствам и методам.

Проблема в том, что вы можете заменить $this->method() на self::method() где угодно, независимо от того, объявлено ли method() статическим или нет. Так какой из них вы должны использовать?

Рассмотрим этот код:

class ParentClass {
    function test() {
        self::who();    // will output 'parent'
        $this->who();   // will output 'child'
    }

    function who() {
        echo 'parent';
    }
}

class ChildClass extends ParentClass {
    function who() {
        echo 'child';
    }
}

$obj = new ChildClass();
$obj->test();

В этом примере self::who() всегда будет выводить «parent», тогда как $this->who() будет зависеть от того, к какому классу относится объект.

Теперь мы можем видеть, что self относится к классу, в котором оно вызывается, а $this относится к классу текущего объекта .

Таким образом, вы должны использовать self только в том случае, если $this недоступен или если вы не хотите, чтобы классы-потомки перезаписывали текущий метод.

21 голосов
/ 08 мая 2013

Внутри определения класса $ это относится к текущему объекту, а self ссылается на текущий класс.

Необходимо сослаться на элемент класса, используя self, и сослаться на элемент объекта, используя $ this.

self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable  
20 голосов
/ 06 декабря 2011

Вот пример правильного использования $ 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();
?> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...