Коли використовувати self над $ this?


Відповіді:


1727

Коротка відповідь

Використовуйте $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


330
Ця відповідь надмірно спрощена. Як вказувалося в інших відповідях, selfвикористовується для використання оператора роздільної здатності ::для позначення поточного класу; це можна зробити як в статичному, так і в нестатичному контекстах. Крім того, це абсолютно законно використовувати $thisдля виклику статичних методів (але не до посилальних полів).
Артефакто

50
Також ви можете використовувати static :: замість :: self, якщо ви на 5.3+. Інакше це може викликати у вас непересічні головні болі, дивіться мою відповідь нижче, чому.
Sqoo

25
-1. Ця відповідь вводить в оману, прочитайте інші відповіді для отримання додаткової інформації.
Pacerier

6
Це може бути надто спрощеним, але воно відповіло на моє основне питання, не змушуючи мою голову вибухнути. Я отримав ще трохи інформації, яка мені здалася корисною для подальшого зниження, але поки що я просто намагалася з'ясувати, чому я потрапила в свої атрибути класу $ this-> attrib і константи класу з self :: Constant. Це допомогло мені зрозуміти це краще
MydKnight

Про що $this::?
Джеймс

742

Ключове слово " НЕ" посилається лише на "поточний клас", принаймні не таким чином, що обмежує вас статичними членами. У контексті нестатичного члена selfтакож передбачений спосіб обходу vtable ( див. Wiki на 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вказівника в межах викликаних функцій.


3
Ця відповідь була б навіть кращою, якби ви почали із загального правила, а не з винятку. Це питання стилю, а не технічної експертизи. Це найкращий приклад, який я коли-небудь бачив про різницю між self :: і $ this->, але прикро приховувати це, спершу спростуючи поняття.
adjwilli

3
@adjwilli: Чому такий поганий стиль? Чи не піднімає це свідомість, якщо сподівання (теза) про ОП спочатку не схвалюється (антитеза), а потім пояснення дається як синтез?
хакре

1
Я вважаю "поточний клас" дійсно проблематичним. Оскільки словосполучення можна розуміти як "клас, де selfзнаходиться" / "визначення класу, це буквальна частина", так і "клас об'єкта" (що насправді було б static).
Джакумі

Про що $this::?
Джеймс

1
@James - немає вагомих причин для використання $this::; всі можливі випадки вже охоплені більш часто використовуваними синтаксисами. В залежності від того, що ви маєте в виду, використання $this->, self::або static::.
ToolmakerSteve

461

НЕ ВИКОРИСТОВУЙТЕ 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 вам доведеться знайти хитрості для цього.

Див. Документацію PHP

Тож відповісти на питання не так, як задали ...

$this->посилається на поточний об'єкт (екземпляр класу), тоді як static::посилається на клас


6
А як щодо констант класу?
Кевін Бонд

53
"Закликаючи померлого :: status (), ми очікували б побачити" Особа померла "". Ні. Це статичний виклик функції, тому немає поліморфізму.
cquezel

2
З усіх вад PHP, я, по-моєму, не думаю, що це зовсім божевільно. Як інакше вони дозволять кодерам призначити методи для поточного класу (на відміну від пошуку їх у vtable)? Якби вони назвали її по-іншому (можливо, з провідними підкресленнями), то люди, які хочуть цю функцію, критикували б її за некрасивість. Інакше, хоч якою б розумною назвою вони не користувалися, здається, завжди було б легко заплутати людей, які б критикували це за "шалену" поведінку, ймовірно, не звертаючи уваги на те, як працює навіть метод відправки.
TNE

2
Приклад мені здається заплутаним: я бачу getStatusметод як такий, який я б назвав для екземпляра класу, а не для класу.
Jānis Elmeris

1
@Sqoo - приказка "НЕ ВИКОРИСТОВУЙТЕ САМО ::, використовуйте статичний ::" - це дивна річ - це свідомо не одна і та ж операція. Я думаю, що ви дійсно задумаєтесь про те, що "зрозуміліше, якщо ви використовуєте фактичну назву класу" MyClass :: ", а не" self :: " . Тобто, якщо ви хочете поведінки self::, ви можете отримати це, менше змішання, використовуючи певне ім'я класу, наприклад MyClass::.
ToolmakerSteve

248

Щоб дійсно зрозуміти, про що ми говоримо, коли ми говоримо 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;
    }

Таким чином, ми зберігаємо інформацію про контейнер класу та об'єкта, використовуючи "підказки" про статичні, які визначають, поділяється інформація (і, отже, статична) чи ні (і, отже, динамічна).

Стан та методи

Всередині методу екземпляр об'єкта представлений $thisзмінною. Поточний стан цього об’єкта є, і його зміна (зміна) будь-якого властивості призведе до зміни цього екземпляра (але не до інших).

Якщо метод називається статично, $thisзмінна не визначається . Це тому, що жоден екземпляр, пов'язаний зі статичним викликом, не існує.

Цікавим тут є те, як здійснюються статичні дзвінки. Тож давайте поговоримо про те, як ми отримуємо доступ до держави:

Доступ до держави

Отже, тепер, коли ми зберегли цей стан, нам потрібно отримати доступ до нього. Це може стати дещо складним (або набагато більше), тому давайте розділимо це на дві точки зору: ззовні екземпляра / класу (скажімо, із звичайного виклику функції або з глобальної області) та зсередини екземпляра / клас (зсередини методу на об'єкті).

Ззовні екземпляра / класу

Ззовні екземпляра / класу наші правила досить прості та передбачувані. У нас є два оператори, і кожен негайно повідомляє нам, чи маємо ми справу з екземпляром або класом статики:

  • ->- об’єкт-оператор - Це завжди використовується, коли ми звертаємось до екземпляра.

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

    Важливо зазначити, що дзвонити Person->fooне має сенсу (оскільки Personце клас, а не екземпляр). Отже, це помилка розбору.

  • ::- Оператор-роздільна здатність - Це завжди використовується для доступу до статичного властивості або методу Class.

    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() екземпляра кожен раз. Але ми використовуємо це 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замість self. Але зауважте, що це не одне й те саме, так що приказка "завжди використовувати staticзамість цього self- це дійсно недалекоглядне. Натомість зупиніться та подумайте про виклик, який ви хочете здійснити, і подумайте, чи хочете ви, щоб дочірні класи змогли перекрити цю статичну розв'язку" дзвінок.

TL / DR

Шкода, поверніться і прочитайте. Це може бути занадто довго, але це так довго, тому що це складна тема

TL / DR №2

Добре, гаразд. Коротше кажучи, selfвикористовується для посилання на ім'я поточного класу в межах класу, де $thisпосилається на поточний екземпляр об'єкта . Зауважте, що selfце скорочення від копіювання / вставки. Ви можете сміливо замінити його на ім’я свого класу, і воно буде добре працювати. Але $thisце динамічна змінна, яку неможливо визначити достроково (а може навіть не бути вашим класом).

TL / DR №3

Якщо використовується об’єкт-оператор ( ->), то ви завжди знаєте, що маєте справу з екземпляром. Якщо використовується оператор діапазону роздільної здатності ( ::), вам потрібна додаткова інформація про контекст (ми вже є в об'єктному контексті? Ми знаходимось поза об'єктом? Тощо).


1
Слово застереження №1: $ це не буде визначено при виклику статичного методу: 3v4l.org/9kr0e
Марк Ахіе

Ну ... $thisне буде визначено, якщо ви будете дотримуватися "Строгих стандартів" і не називати методи статично, які не визначені як статичні. Я бачу результат, який ви пояснили тут: 3v4l.org/WeHVM Погодився, дійсно дивно.
Марк Achée

2
Повністю прочитавши довгий опис, я лінувався знову прокрутити вище, щоб підтвердити його. Жартую, я це схвалив: D. Спасибі це дуже корисно.
Mr_Green

3
було б добре додати чітке пояснення про різницю між self :: $ property та self :: property; Я думаю, що це теж заплутано
Томмазо Барбуглі

1
WoC №1 поводиться інакше, оскільки PHP 7. Як Foo::isFoo()називається статично, $thisне визначатиметься. На мою думку, це більш інтуїтивна поведінка. - Ще один різний результат дається, якщо Barслід продовжити від Foo. Тоді виклик Foo::isFoo()був би фактично в контексті екземпляра (не специфічно для PHP7).
Kontrollfreak

117

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

Оскільки thisє об'єктом, ви використовуєте його так:$this->member

Оскільки selfце не об'єкт, це в основному тип, який автоматично посилається на поточний клас, ви використовуєте його так:self::member


97

$this-> використовується для позначення конкретного примірника змінних класу (змінних членів) або методів.

Example: 
$derek = new Person();

$ derek зараз є специфічним екземпляром Person. У кожної людини є ім’я та прізвище, але $ derek має конкретне ім'я та прізвище (Дерек Мартін). Всередині екземпляра $ derek ми можемо позначати такі, як $ this-> first_name та $ this-> last_name

ClassName :: використовується для позначення цього типу класу та його статичних змінних, статичних методів. Якщо це допоможе, ви можете подумки замінити слово "статичний" на "спільний". Оскільки вони є спільними, вони не можуть посилатися на $ this, що посилається на певний екземпляр (не поділяється). Статичні змінні (тобто статичні $ db_connection) можуть бути спільними для всіх примірників типу об’єкта. Наприклад, всі об'єкти бази даних поділяють одне з'єднання (статичне $ з'єднання).

Приклад статичних змінних: Прикиньтесь, що у нас є клас бази даних із змінною одного члена: статичний $ 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).

Статичні методи (тобто загальнодоступний статичний вигляд :: format_phone_number ($ цифри)) можна використовувати БЕЗ спочатку інстанціювання одного з цих об'єктів (тобто вони не посилаються на $ 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 (), це зовсім не проблема. Конфлікту немає. Так, статичні методи!

SELF :: Якщо ви кодуєте за межами об'єкта, який має статичний метод, до якого ви хочете посилатися, ви повинні викликати його, використовуючи ім'я об'єкта View :: format_phone_number ($ phone_number); Якщо ви кодування всередині об'єкта , який має статичний метод , який ви хочете звернутися, ви можете або використовувати ім'я View :: format_phone_number об'єкта ($ Pn), або ви можете використовувати автоматичний :: format_phone_number ($ Pn) ярлик

Те саме стосується статичних змінних: Приклад: Перегляд :: templates_path проти self :: templates_path

Всередині класу DB, якщо ми мали на увазі статичний метод якогось іншого об'єкта, ми використовували б ім'я об'єкта: Приклад: Session :: getUsersOnline ();

Але якщо клас DB хотів звернутися до власної статичної змінної, він би просто сказав self: Приклад: self :: connection;

Сподіваюся, що це допоможе зрозуміти речі :)


Чудова відповідь. Я просто хочу зазначити, при посиланні на статичний атрибут потрібно використовувати $знак. Наприкладself::$templates_path
Генрірайт

30

З цієї публікації в блозі :

  • self відноситься до поточного класу
  • self може використовуватися для виклику статичних функцій та опорних змінних статичних членів
  • self може використовуватися всередині статичних функцій
  • self також може вимкнути поліморфну ​​поведінку, минаючи vtable
  • $this відноситься до поточного об'єкта
  • $this може використовуватися для виклику статичних функцій
  • $thisне слід використовувати для виклику статичних змінних членів. Використовуйте selfзамість цього.
  • $this не можна використовувати всередині статичних функцій

26

У 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()завжди буде виводитися "батьків", тоді як $this->who()залежатиме від того, який клас має об'єкт.

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

Отже, ви повинні використовувати self лише тоді, коли $thisвін недоступний або коли ви не хочете дозволяти класам нащадків перезаписувати поточний метод.


22

Всередині визначення класу $thisпосилається на поточний об'єкт, а selfна поточний клас.

Потрібно посилатися на елемент класу за допомогою selfта посилатися на елемент об'єкта з використанням $this.

self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable  

21

Ось приклад правильного використання $ 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();
?> 

21

Згідно з http://www.php.net/manual/en/language.oop5.static.php немає $self. Є лише $thisдля посилання на поточний екземпляр класу (об'єкта) та self, який може бути використаний для позначення статичних членів класу. Тут грає різниця між об'єктом об'єкта та класом.


9
Рекомендація: Прочитайте цю відповідь, коли натрапляєте на кислоту.
a20

16

Вважаю, питання було не в тому, чи можна зателефонувати статичному члену класу ClassName::staticMember. Питання було в чому різниця між використанням self::classmemberта $this->classmember.

Наприклад, обидва наступні приклади працюють без будь-яких помилок, незалежно від того, використовуєте ви self::чи$this->

class Person{
    private $name;
    private $address;

    public function __construct($new_name,$new_address){
        $this->name = $new_name;
        $this->address = $new_address;
    }
}

class Person{
    private $name;
    private $address;
    public function __construct($new_name,$new_address){
        self::$name = $new_name;
        self::$address = $new_address;
    }
}

Особливо смішно, що ви починаєте свою відповідь з "Я вважаю, що питання полягало не в тому, чи можна викликати статичний член класу, зателефонувавши ClassName :: staticMember. Питання полягало в тому, яка різниця між використанням self :: classmember та $ this-> classmember" і тоді ви продовжуєте проявляти відмінності зовсім. Насправді ви показуєте приклад, коли обидва варіанти працюють однаково. -1
Буттер Буткус

Проте корисно. Обсяг стосувався роздільної здатності, і ця частина не зрозуміла в керівництві PHP. Я все ще вважаю це корисним
перегляньте

2
Fatal error: Access to undeclared static property: Person::$name in D:\LAMP\www\test.php on line 16
K-Gun

16

self посилається на поточний клас (у якому він називається),

$thisпосилається на поточний об'єкт. Ви можете використовувати статичні замість self. Дивіться приклад:

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

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

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

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

Вихід: батьківська дитина


16
  • Вказівник об'єкта $thisпосилається на поточний об'єкт.
  • Значення класу staticвідноситься до поточного об'єкта.
  • Значення класу selfвідноситься до точного класу, в якому він був визначений.
  • Значення класу parentвідноситься до батьківського рівня саме того класу, в якому він був визначений.

Дивіться наступний приклад, який показує перевантаження.

<?php

class A {

    public static function newStaticClass()
    {
        return new static;
    }

    public static function newSelfClass()
    {
        return new self;
    }

    public function newThisClass()
    {
        return new $this;
    }
}

class B extends A
{
    public function newParentClass()
    {
        return new parent;
    }
}


$b = new B;

var_dump($b::newStaticClass()); // B
var_dump($b::newSelfClass()); // A because self belongs to "A"
var_dump($b->newThisClass()); // B
var_dump($b->newParentClass()); // A


class C extends B
{
    public static function newSelfClass()
    {
        return new self;
    }
}


$c = new C;

var_dump($c::newStaticClass()); // C
var_dump($c::newSelfClass()); // C because self now points to "C" class
var_dump($c->newThisClass()); // C
var_dump($b->newParentClass()); // A because parent was defined *way back* in class "B"

Більшу частину часу ви хочете віднести до поточного класу, саме тому ви використовуєте staticабо $this. Однак бувають випадки, коли вам це потрібно, self тому що ви хочете оригінальний клас незалежно від того, що його розширює. (Дуже, дуже рідко)


14

Оскільки тут ніхто не говорив про виступи, ось невеликий орієнтир, який я зробив (5.6):

 Name     | Time    | Percent  
----------|---------|---------  
 $this->  | 0.99163 | 106.23%  
 self::   | 0.96912 | 103.82%  
 static:: | 0.93348 | 100%

Це результати за 2 000 000 пробіжок, і ось код, який я використав:

<?php

require '../vendor/autoload.php';

// My small class to do benchmarks
// All it does is looping over every test x times and record the
//   time it takes using `microtime(true)`
// Then, the percentage is calculated, with 100% being the quickest
// Times are being rouned for outputting only, not to calculate the percentages
$b = new Tleb\Benchmark\Benchmark(2000000);

class Foo
{
    public function calling_this()
    {
        $this->called();
    }

    public function calling_self()
    {
        self::called();
    }

    public function calling_static()
    {
        static::called();
    }

    public static function called()
    {
    }
}

$b->add('$this->',  function () { $foo = new Foo; $foo->calling_this(); });
$b->add('self::',   function () { $foo = new Foo; $foo->calling_self(); });
$b->add('static::', function () { $foo = new Foo; $foo->calling_static(); });

$b->run();

1
Функція безреактивного виклику в 2 000 000 разів триває 1 с. Треба любити PHP.
rr-

Старий добрий PHP. :) Але дзвінок = 0,001мс. Це так погано?
понеділок

Я вважаю, що це (і подібні речі), тому такі речі, як ORM, відчувають себе повільно, якщо ви не кешуєте речі, а статичні генератори сайтів - це не річ.
rr-

2
Теоретично він повинен зайняти 1 тактовий цикл процесора, який становить 1 / 2e9 s = 0.5 nsці дні
Бадді,

Просто перечитайте мою відповідь. Будьте уважні: це створює і клас . Я не знаю, чому я не використовував useключове слово tbh, але у мене вже немає PHP, щоб повторити показник, і я не відчуваю, як перевстановити його.
tleb

13

При selfвикористанні з ::оператором він посилається на поточний клас, який можна робити як в статичному, так і в нестатичному контекстах. $thisвідноситься до самого об'єкта. Крім того, це цілком законно використовувати $thisдля виклику статичних методів (але не для посилань на поля).


8

Я зіткнувся з тим же запитанням і проста відповідь:

  • $this вимагає екземпляр класу
  • self:: не робить

Кожного разу, коли ви використовуєте статичні методи або статичні атрибути і хочете викликати їх, не маючи примірника класу, вам потрібно використовувати їх self:для виклику, тому що $thisзавжди потрібно створити об'єкт.


7

$thisвідноситься до об'єкта поточного класу, selfпосилається на поточний клас (Not object). Клас - це креслення об’єкта. Отже, ви визначаєте клас, але конструюєте об'єкти.

Так іншими словами, використовуйте self for staticіthis for none-static members or methods .

також у сценарії «дитина / батьки» self / parentв основному використовується для виявлення членів та методів класу дитини та батьків.


7

Крім того, поки $this::ще не було обговорено.

Тільки в інформаційних цілях, як на PHP 5.3 при роботі з об'єктами, що створюють екземпляр, щоб отримати поточне значення обсягу, на відміну від використання static::, можна альтернативно використовувати $this::так.

http://ideone.com/7etRHy

class Foo
{
    const NAME = 'Foo';

    //Always Foo::NAME (Foo) due to self
    protected static $staticName = self::NAME;

    public function __construct()
    {
        echo $this::NAME;
    }

    public function getStaticName()
    {
       echo $this::$staticName;
    }
}

class Bar extends Foo
{
    const NAME = 'FooBar';

    /**
     * override getStaticName to output Bar::NAME
     */
    public function getStaticName()
    {
        $this::$staticName = $this::NAME;
        parent::getStaticName();
    }
}

$foo = new Foo; //outputs Foo
$bar = new Bar; //outputs FooBar
$foo->getStaticName(); //outputs Foo
$bar->getStaticName(); //outputs FooBar
$foo->getStaticName(); //outputs FooBar

Використання наведеного вище коду не є звичайною чи рекомендованою практикою, а просто проілюструвати його використання та має діяти як більшу частину "Чи знали ви?" посилаючись на питання оригінального плаката.

Він також представляє використання, $object::CONSTANTнаприклад echo $foo::NAME;, на відміну від$this::NAME;


5

Використовуйте, selfякщо ви хочете викликати метод класу, не створюючи об'єкт / примірник цього класу, зберігаючи таким чином оперативну пам'ять (іноді використовуйте self для цієї мети). Іншими словами, він фактично викликає метод статично. Використовувати thisдля перспективи об'єкта.


2

Випадок 1: Використання self можна використовувати для констант класу

 клас класA { 
     const FIXED_NUMBER = 4; 
     self :: POUNDS_TO_KILOGRAMS
}

Якщо ви хочете викликати його поза класом, використовуйте classA::POUNDS_TO_KILOGRAMSдля доступу до констант

Випадок 2: для статичних властивостей

class classC {
     публічна функція __construct () { 
     self :: $ _ counter ++; $ this-> num = self :: $ _ лічильник;
   }
}

1

За php.net є три спеціальні ключові слова в даному контексті: self, parentі static. Вони використовуються для доступу до властивостей або методів з визначення всередині класу.

$thisз іншого боку, використовується для виклику екземпляра та методів будь-якого класу, доки цей клас доступний.


-1

self ::  ключове слово, що використовується для поточного класу, і в основному воно використовується для доступу до статичних членів, методів та констант. Але у випадку $ цього ви не можете викликати статичний член, метод та функції.

Ви можете використовувати ключове слово self :: в іншому класі та отримати доступ до статичних членів, методу та констант. Коли він буде поширюватися з батьківського класу і такий самий у разі $ цього ключового слова. Ви можете отримати доступ до нестатичних членів, методу та функції в іншому класі, коли він буде поширюватися з батьківського класу.

Код, наведений нижче, є прикладом ключового слова self :: і $ . Просто скопіюйте та вставте код у свій файл коду та подивіться вихід.

class cars{
    var $doors=4;   
    static $car_wheel=4;

  public function car_features(){
    echo $this->doors." Doors <br>";
    echo self::$car_wheel." Wheels <br>"; 
  }
}

class spec extends cars{
    function car_spec(){
        print(self::$car_wheel." Doors <br>");
        print($this->doors." Wheels <br>");
    }
}

/********Parent class output*********/

$car = new cars;
print_r($car->car_features());

echo "------------------------<br>";

/********Extend class from another class output**********/


$car_spec_show=new spec;

print($car_spec_show->car_spec());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.