¿Cuándo debo usar 'self' en lugar de '$this'?
En PHP 5, ¿cuál es la diferencia entre usar self
y $this
?
¿Cuándo es apropiado cada uno?
Respuesta corta
Úselo
$this
para referirse al objeto actual. Úseloself
para referirse a la clase actual. En otras palabras, utilícelo$this->member
para miembros no estáticos y utilíceloself::$member
para miembros estáticos.
Respuesta completa
A continuación se muestra un ejemplo del uso correcto$this
de y self
para 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 self
para 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 $this
funciones 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 self
de 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 lafoo()
función miembro de cualquiera que sea el tipo exacto del objeto actual. Si el objeto es detype X
, llama aX::foo()
. Si el objeto es detype Y
, llama aY::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
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, self
tambié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 $this
puntero, 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 $this
puntero dentro de las funciones llamadas.
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.
Para entender realmente de qué estamos hablando cuando hablamos de self
versus $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 $name
y 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 new
operador.
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 instanceof
una clase particular: $bob instanceof Person
lo que devuelve un valor booleano si la $bob
instancia se creó usando la Person
clase 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:
Propiedades : piense en ellas como variables que contendrá cada instancia.
class Foo { public $bar = 1; }
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; }
Métodos : son funciones que contendrá cada instancia (y operarán en las instancias).
class Foo { public function bar() {} }
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() {} }
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 $this
variable. 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 $this
variable 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->foo
no tiene sentido (ya quePerson
es 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 thebaz()
method statically, and hence$this
will not be populated. It's worth noting that in recent versions of PHP (5.3+) this will trigger anE_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 asstatic
, 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 returntrue
, 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. Soself::baz()
is the same asFoo::baz()
within theFoo
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 usingstatic
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).