Propiedades

En PHP, las variables miembro de clases también llamadas propiedades, son parte de la definición de la clase, y se usan para representar el estado de una instancia de la clase (ej. para diferenciar una instancia de clase de otra). A la práctica, a menudo, se puede querer gestionar la lectura o escritura de las propiedades de algunos momentos. Por ejemplo, se puede querer eliminar los espacios en blanco (trim) de una cadena de texto cada vez que esta se asigne a una propiedad de tipo label. Se podría usar el siguiente código para realizar esta tarea:

$object->label = trim($label);

La desventaja del código anterior es que se tendría que ejecutar trim() en todas las partes del código que pudieran establecer la propiedad label. Si en el futuro, la propiedad label tiene que seguir otro funcionamiento, como por ejemplo que la primera letra tiene que estar en mayúsculas, se tendrán que modificar todas las secciones de código que asignen el valor a la propiedad label. La repetición de código conlleva a bugs, y es una practica que se tiene que evitar en la medida de lo posible.

Para solventar este problema, Yii introduce la clase base llamada [[yii\base\Object]] que da soporte a la definición de propiedades basada en los métodos de clase getter y setter. Si una clase necesita más funcionalidad, debe extender a la clase [[yii\base\Object]] o a alguna de sus hijas.

Información: Casi todas las clases del núcleo (core) en el framework Yii extienden a [[yii\base\Object]] o a una de sus clases hijas. Esto significa que siempre que se encuentre un getter o un setter en una clase del núcleo, se puede utilizar como una propiedad.

Un método getter es un método cuyo nombre empieza por la palabra get: un metodo setter empieza por set. El nombre añadido detrás del prefijo get o set define el nombre de la propiedad. Por ejemplo, un getter getLabel() y/o un setter setLabel() definen la propiedad label, como se muestra a continuación:

namespace app\components;

use yii\base\Object;

class Foo extends Object
{
    private $_label;

    public function getLabel()
    {
        return $this->_label;
    }

    public function setLabel($value)
    {
        $this->_label = trim($value);
    }
}

(Para ser claros, los métodos getter y setter crean la propiedad label, que en este caso hace una referencia interna al nombre de atributo privado _label.)

Las propiedades definidas por los getter y los setters se pueden usar como variables de clase miembro. La principal diferencia radica en que cuando esta propiedad se lea, se ejecutará su correspondiente método getter; cuando se asigne un valor a la propiedad, se ejecutará el correspondiente método setter. Por ejemplo:

// equivalente a $label = $object->getLabel();
$label = $object->label;

// equivalente a $object->setLabel('abc');
$object->label = 'abc';

Una propiedad definida por un getter sin un setter es de tipo sólo lectura. Si se intenta asignar un valor a esta propiedad se producirá una excepción de tipo [[yii\base\InvalidCallException|InvalidCallException]]. Del mismo modo que una propiedad definida con un setter pero sin getter será de tipo sólo escritura, cualquier intento de lectura de esta propiedad producirá una excepción. No es común tener variables de tipo sólo escritura.

Hay varias reglas especiales y limitaciones en las propiedades definidas mediante getters y setters:

  • Los nombres de estas propiedades son case-insensitive. Por ejemplo, $object->label y $object->Label son la misma. Esto se debe a que los nombres de los métodos en PHP son case-insensitive.
  • Si el nombre de una propiedad de este tipo es igual al de una variable miembro de la clase, la segunda tendrá prioridad. Por ejemplo, si la anterior clase Foo tiene la variable miembro label, entonces la asignación $object->label = 'abc' afectará a la variable miembro 'label'; no se ejecutará el método setter setLabel().
  • Estas variables no soportan la visibilidad. No hay diferencia en definir los métodos getter o setter en una propiedad public, protected, o private.
  • Las propiedades sólo se pueden definir por getters y setters no estáticos. Los métodos estáticos no se tratarán de la misma manera.

Volviendo de nuevo al problema descrito al principio de la guía, en lugar de ejecutar trim() cada vez que se asigne un valor a label, ahora trim() sólo necesita ser invocado dentro del setter setLabel(). I si se tiene que añadir un nuevo requerimiento, para que label empiece con una letra mayúscula, se puede modificar rápidamente el método setLabel() sin tener que modificar más secciones de código. El cambio afectará a cada asignación de label.