¿Cuándo debo usar 'self' en lugar de '$this'?

Resuelto Casey Watson asked hace 54 años • 24 respuestas

En PHP 5, ¿cuál es la diferencia entre usar selfy $this?

¿Cuándo es apropiado cada uno?

Casey Watson avatar Jan 01 '70 08:01 Casey Watson
Aceptado

Respuesta corta

Úselo $thispara referirse al objeto actual. Úselo selfpara referirse a la clase actual. En otras palabras, utilícelo $this->memberpara miembros no estáticos y utilícelo self::$memberpara miembros estáticos.

Respuesta completa

A continuación se muestra un ejemplo del uso correcto$this de y selfpara variables miembro estáticas y no estáticas:

<?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();
?>

A continuación se muestra un ejemplo de uso incorrecto$this de y selfpara variables miembro estáticas y no estáticas:

<?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();
?>

Aquí hay un ejemplo de polimorfismo con $thisfunciones miembro:

<?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();
?>

A continuación se muestra un ejemplo de cómo suprimir el comportamiento polimórfico mediante el uso selfde funciones miembro:

<?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();
?>

La idea es $this->foo()llamar a la foo()función miembro de cualquiera que sea el tipo exacto del objeto actual. Si el objeto es de type X, llama a X::foo(). Si el objeto es de type Y, llama a Y::foo(). Pero con self::foo(), X::foo()siempre se llama.

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

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

John Millikin avatar Sep 30 '2008 06:09 John Millikin

La palabra clave self NO se refiere simplemente a la 'clase actual', al menos no de una manera que lo restrinja a miembros estáticos. Dentro del contexto de un miembro no estático, selftambién proporciona una forma de omitir vtable ( consulte wiki sobre vtable ) para el objeto actual. Así como puede usar parent::methodName()para llamar a la versión principal de una función, también puede llamar self::methodName()para llamar a la implementación de clases actual de un método.

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();

Esto generará:

Hola, soy Ludwig el friki.
Adiós de parte de Ludwig el hombre.

sayHello()usa el $thispuntero, por lo que se invoca vtable para llamar Geek::getTitle(). sayGoodbye()usa self::getTitle(), por lo que vtable no se usa y Person::getTitle()se llama. En ambos casos, estamos tratando con el método de un objeto instanciado y tenemos acceso al $thispuntero dentro de las funciones llamadas.

nbeagle avatar Jul 27 '2009 18:07 nbeagle

No utilice self:: . Usarstatic:: *

Hay otro aspecto del yo que vale la pena mencionar. Molesto, self::se refiere al alcance en el punto de definición, no en el punto de ejecución . Considere esta clase simple con dos métodos:

class Person
{

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

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

}

Si llamamos Person::status()veremos "La persona está viva". Ahora considere lo que sucede cuando creamos una clase que hereda de esto:

class Deceased extends Person
{

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

}

Al llamar Deceased::status()esperaríamos ver "La persona ha fallecido". Sin embargo, vemos "La persona está viva" ya que el alcance contiene la definición del método original cuando self::getStatus()se definió la llamada.

PHP 5.3 tiene una solución. El static::operador de resolución implementa un "enlace estático tardío", que es una forma elegante de decir que está vinculado al alcance de la clase llamada. Cambie la línea status()a static::getStatus()y los resultados serán los esperados. En versiones anteriores de PHP tendrás que encontrar un truco para hacer esto.

Ver documentación PHP

Entonces, para responder la pregunta no como se hizo...

$this->se refiere al objeto actual (una instancia de una clase), mientras que static::se refiere a una clase.

Sqoo avatar Jul 24 '2011 15:07 Sqoo

Para entender realmente de qué estamos hablando cuando hablamos de selfversus $this, necesitamos profundizar en lo que está sucediendo a nivel conceptual y práctico. Realmente no creo que ninguna de las respuestas haga esto de manera apropiada, así que aquí está mi intento.

Comencemos hablando de qué es una clase y un objeto .

Clases y objetos, conceptualmente

Entonces, ¿qué es una clase ? Mucha gente lo define como un modelo o plantilla para un objeto. De hecho, puedes leer más sobre clases en PHP aquí . Y hasta cierto punto eso es lo que realmente es. Veamos una clase:

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

Como puede ver, hay una propiedad en esa clase llamada $namey un método (función) llamado sayHello().

Es muy importante tener en cuenta que la clase es una estructura estática. Lo que significa que la clase Person, una vez definida, es siempre la misma dondequiera que la mires.

Un objeto, por otro lado, es lo que se llama una instancia de una Clase. Lo que eso significa es que tomamos el "modelo" de la clase y lo usamos para hacer una copia dinámica. Esta copia ahora está vinculada específicamente a la variable en la que está almacenada. Por lo tanto, cualquier cambio en una instancia es local para esa instancia.

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

Creamos nuevas instancias de una clase usando el newoperador.

Por tanto, decimos que una Clase es una estructura global y un Objeto es una estructura local. No te preocupes por esa graciosa ->sintaxis, hablaremos de eso más adelante.

Otra cosa de la que deberíamos hablar es que podemos verificar si una instancia es instanceofuna clase particular: $bob instanceof Personlo que devuelve un valor booleano si la $bobinstancia se creó usando la Personclase o un hijo de Person.

Definición de estado

Así que profundicemos un poco en lo que realmente contiene una clase. Hay 5 tipos de "cosas" que contiene una clase:

  1. Propiedades : piense en ellas como variables que contendrá cada instancia.

    class Foo {
        public $bar = 1;
    }
    
  2. Propiedades estáticas : piense en ellas como variables que se comparten a nivel de clase. Lo que significa que nunca son copiados por cada instancia.

    class Foo {
        public static $bar = 1;
    }
    
  3. Métodos : son funciones que contendrá cada instancia (y operarán en las instancias).

    class Foo {
        public function bar() {}
    }
    
  4. Métodos estáticos : son funciones que se comparten en toda la clase. No operan en instancias, sino únicamente en propiedades estáticas.

    class Foo {
        public static function bar() {}
    }
    
  5. Constantes : constantes resueltas por clase. No profundizaré más aquí, pero agregaré para completar:

    class Foo {
        const BAR = 1;
    }
    

Básicamente, estamos almacenando información en el contenedor de clases y objetos usando "sugerencias" sobre estática que identifican si la información es compartida (y por lo tanto estática) o no (y por lo tanto dinámica).

Estado y métodos

Dentro de un método, la instancia de un objeto está representada por la $thisvariable. El estado actual de ese objeto está ahí, y mutar (cambiar) cualquier propiedad dará como resultado un cambio en esa instancia (pero no en otras).

Si un método se llama estáticamente, la $thisvariable no está definida . Esto se debe a que no hay ninguna instancia asociada con una llamada estática.

Lo interesante aquí es cómo se realizan las llamadas estáticas. Entonces, hablemos de cómo accedemos al estado:

Estado de acceso

Ahora que hemos almacenado ese estado, necesitamos acceder a él. Esto puede resultar un poco complicado (o mucho más que un poco), así que dividámoslo en dos puntos de vista: desde fuera de una instancia/clase (por ejemplo, desde una llamada de función normal o desde el alcance global) y dentro de una instancia. /clase (desde dentro de un método en el objeto).

Desde fuera de una instancia/clase

Desde el exterior de una instancia/clase, nuestras reglas son bastante simples y predecibles. Tenemos dos operadores, y cada uno nos dice inmediatamente si estamos tratando con una instancia o una clase estática:

  • ->- operador de objeto : esto siempre se usa cuando accedemos a una instancia.

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

    Es importante tener en cuenta que llamar Person->foono tiene sentido (ya que Persones una clase, no una instancia). Por lo tanto, se trata de un error de análisis.

  • :: - scope-resolution-operator - This is always used to access a Class static property or method.

    echo Foo::bar()
    

    Additionally, we can call a static method on an object in the same way:

    echo $foo::bar()
    

    It's extremely important to note that when we do this from outside, the object's instance is hidden from the bar() method. Meaning that it's the exact same as running:

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

Therefore, $this is not defined in the static call.

From Inside Of An Instance/Class

Things change a bit here. The same operators are used, but their meaning becomes significantly blurred.

The object-operator -> is still used to make calls to the object's instance state.

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

Calling the bar() method on $foo (an instance of Foo) using the object-operator: $foo->bar() will result in the instance's version of $a.

So that's how we expect.

The meaning of the :: operator though changes. It depends on the context of the call to the current function:

  • Within a static context

    Within a static context, any calls made using :: will also be static. Let's look at an example:

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

    Calling Foo::bar() will call the baz() method statically, and hence $this will not be populated. It's worth noting that in recent versions of PHP (5.3+) this will trigger an E_STRICT error, because we're calling non-static methods statically.

  • Within an instance context

    Within an instance context on the other hand, calls made using :: depend on the receiver of the call (the method we're calling). If the method is defined as static, then it will use a static call. If it's not, it will forward the instance information.

    So, looking at the above code, calling $foo->bar() will return true, since the "static" call happens inside of an instance context.

Make sense? Didn't think so. It's confusing.

Short-Cut Keywords

Because tying everything together using class names is rather dirty, PHP provides 3 basic "shortcut" keywords to make scope resolving easier.

  • self - This refers to the current class name. So self::baz() is the same as Foo::baz() within the Foo class (any method on it).

  • parent - This refers to the parent of the current class.

  • static - This refers to the called class. Thanks to inheritance, child classes can override methods and static properties. So calling them using static instead of a class name allows us to resolve where the call came from, rather than the current level.

Examples

The easiest way to understand this is to start looking at some examples. Let's pick a class:

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();
    }
}

Now, we're also looking at inheritance here. Ignore for a moment that this is a bad object model, but let's look at what happens when we play with this:

$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

So the ID counter is shared across both instances and the children (because we're using self to access it. If we used static, we could override it in a child class).

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

Note that we're executing the Person::getName() instance method every time. But we're using the parent::getName() to do it in one of the cases (the child case). This is what makes this approach powerful.

Word Of Caution #1

Note that the calling context is what determines if an instance is used. Therefore:

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

Is not always true.

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

Now it is really weird here. We're calling a different class, but the $this that gets passed to the Foo::isFoo() method is the instance of $bar.

This can cause all sorts of bugs and conceptual WTF-ery. So I'd highly suggest avoiding the :: operator from within instance methods on anything except those three virtual "short-cut" keywords (static, self, and parent).

Word Of Caution #2

Note that static methods and properties are shared by everyone. That makes them basically global variables. With all the same problems that come with globals. So I would be really hesitant to store information in static methods/properties unless you're comfortable with it being truly global.

Word Of Caution #3

In general you'll want to use what's known as Late-Static-Binding by using static instead of self. But note that they are not the same thing, so saying "always use static instead of self is really short-sighted. Instead, stop and think about the call you want to make and think if you want child classes to be able to override that static resolved call.

TL/DR

Too bad, go back and read it. It may be too long, but it's that long because this is a complex topic

TL/DR #2

Ok, fine. In short, self is used to reference the current class name within a class, where as $this refers to the current object instance. Note that self is a copy/paste short-cut. You can safely replace it with your class name, and it'll work fine. But $this is a dynamic variable that can't be determined ahead of time (and may not even be your class).

TL/DR #3

If the object-operator is used (->), then you always know you're dealing with an instance. If the scope-resolution-operator is used (::), you need more information about the context (are we in an object-context already? Are we outside of an object? etc).

ircmaxell avatar Jun 10 '2013 15:06 ircmaxell