IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Introducció

En programació orientada a objectes les propietats i els mètodes d'una classe són per defecte dinàmics. El contrari de dinàmic és estàtic. Però que implica que un mètode o una propietat siguin estàtics? Ho explico als següents apartats.

En certa forma en programació orientada a objectes l'ús d'static permet treballar en certs casos de forma procedimental i no pas de forma orientada a objectes.

PHP

Mètodes estàtics

Un mètode estàtic (i també una propietat estàtica) d'una classe pot ser utilitzat sense tenir una instància de la classe (el que anomenem objecte).

Vegem un exemple:

class Math {
    public function add() {
        return array_sum(func_get_args());
    }
}

$match = new Math;
var_dump($math->add(1,2,3));

En aquest cas podem convertit el mètode a static i podem aleshores accedir al mètode amb l'Scope Resolution Operator (::):

class Math {
    public static function add() {
        return array_sum(func_get_args());
    }
}

var_dump(Math::add(1,2,3));

S'acostumen a utilitzar per definir una espècie de funcions globals amb prefix (el prefix és la classe que conté el mètode/funció global).

Static methods are nothing more than namespaced global functions. Namespacing, I think we can all agree on, is great. As for global functions: We use those all the time. The native functions in 
PHP form our basic building blocks [1]

Vegeu també:

Resources:

Propietats estàtiques

L'objectiu de definir una propietat estàtica (compte no confondre amb una constant la qual no pot canviar de valor!) és definir una propietat que es compartida (shared) per totes les instàncies (objectes) d'una classe. Vegem un exemple de com NO utilitzar una propietat estàtica:

class Person {
    publi static $age = 1;
}

Person::$age

En aquest cas cada objecte (persona) compartirà la mateixa edat! Raro ja que al canviar la edat d'una persona (per exemple pq compleix anys) canviarem l'edat a tots els objectes! Estem trencant la encapsulació.

IMPORTANT: Fireu-vos que cal posar $ per diferenciar age d'un mètode o una propietat

Constants

IMPORTANT: Oco que a diferència de molts altres llenguatges no s'utilitza public static final per declarar constants

Una constant és una variable que un cop definida no canvia i no es pot canviar de valor. En PHP s'utilitza l'operador const:

<?php
class MyClass {
    const CONST_VALUE = 'Un valor constante';
}

$classname = 'MyClass';
echo $classname::CONST_VALUE; // A partir de PHP 5.3.0

echo MyClass::CONST_VALUE;
?>

No és obligatori però les constants se solen definir amb majúscules.

Laracast

Scope Resolution operator

Disponible des de la versió 4 de PHP el operador :: (doble dos punts) s'utilitza per accedir a propietats o mètodes d'una classe directament sense la existència d'un objecte d'aquella classe. aka T_PAAMAYIM_NEKUDOTAYIM (Crec que està escrit en hebreu). En anglès s'anomena double two colons.

Té multitud de usos i s'utilitza en programació orientada a objectes de PHP per diferènciar (en certa manera a mode d'indicar un prefix i espai de noms) a quin atribut o mètode ens referim. Normalment abans dels dos punts posem el nom d'una classe però també és bastant habitual utilitzar pseudo-classes com parent.

Exemples:

Sense Scope Resolution operator:

sayhello()

Cridarà una funció disponible a l'scope al que estem en aquell moment o d'scope global. En canvi:

Person:sayhello()

Cridarà al mètode/funció sayhello() de la classe Person. Un dels codis més habituals on és veu el operador :::

parent::__construct()

Per cridar al constructor de la classe pare. Altres llenguatges com C++ o Ruby també l'utilitzen

Resources:

Quan utilitzar mètodes estàtics?

En resum:

When they are stateless.

El problema a considerar és shared global state (estat global compartit).

vegem un exemple d'un mètode sense estat:

<?php
$time = Time::from("11:45");

és un mètode que no té efectes col·laterals i es completament predictible no depèn del context ni de cap informació emmagatzemada o passada. Es pot cridar exactament a la mateixa funció amb el mateix argument i sempre donarà el mateix resultat (una objecte amb la hora de les 11:45). Un altre exemple:

$sum = Calculator::sum(1, 2);

La suma proveeix un servei que és sense estat, que no recorda cap mena d'informació i que la seva sortida/resultat no pot ser influenciat per res més que els arguments que passem a la funció. A més aquest servei de sumar no serà mai polimòrfic ni tindrà implementacions diferents. Retornar res que sigui diferent a 3 quan els arguments són 1 i 2 trenca el contracte. Evidentment poden haver diferents implementacions més o menys eficients però els clients/usuaris de la funció això no els afecta.

En canvi vegeu el següent exemple:

<?php
Counter::increment(1);
$count = Counter::getCount();

En aquesta cas si dos desenvolupadors utilitzen la mateixa funció en parts diferents del codi individualment cadascú farà les seves proves i tot anirà correctament però un els codi es provi de forma conjunta els resultats seran erràtics perquè tots dos comparteixen el mateix estat global (un contador global). La solució en aquest cas és treballar amb objectes:

<?php
$myCounter = new Counter;
$myCounter->increment(1);
$count = $myCounter->getCount();

Vegeu com amb Singleton podríem solucionar/implementar l'anterior exemple de comptador a Singleton#Comptador_Singleton

Resources:

Vegeu més cassos més concrets en els següents apartats

Named constructors

Vegeu Named constructors

Singleton

Vegeu Singleton

Track number of instances

Mantenir un comptador que calculi el nombre d'objectes.

class CountMe {
    public static $instances = 0;
    public function __construct() {
        CountMe::$instances++;
    }
    public function __destruct() {
        CountMe::$instances--;
    }
}
$a = new CountMe();
$b = new CountMe();
echo CountMe::$instances; // outputs 2

Anti-pattern

Static vs self

Quan s'utilitza self:: es fa referència a la classe actual (exactament igual que el mètode get_class) i quan s'utilitza static:: fem referència a la classe que s'ha cridat exactament igual que al mètode get_called_class

Igualment cal tenir en compte que $this fa referència a l'objecte actual (no és pot utilitzar per exemple en mètodes estàtics) i en canvi self fa referència a la classe i permet accedir a membres estàtics de la classe:

self::$member for static members.

Són doncs igual self i static? No sempre. Si no hi ha herència pel mig solen tornar el mateix resultat però amb static a partir de PHP 5.3 permetem el que s'anomena Late static bindings ([2] o Enllaç estàtic en temps d'execució [3]) per cobrir les limitacions de $self (o __CLASS__) en temes d'herència estàtica:

De forma más precisa, un enlace estático en tiempo de ejecución para funcionar almacena el nombre de clase de la última llamada que no tenga "propagación". En el caso de las llamadas a métodos estáticos, se trata de la clase a la que se llamó explícitamente (normalmente, la que precede al operador ::); en los casos de llamadas a métodos que no son estáticos, se resolvería a la clase del objeto. Una "llamada con propagación" es una llamada estática que está precedida por self::, parent::, static::, o, si seguimos la jerarquía de clases, forward_static_call(). La función get_called_class() puede utilizarse para obtener un string con el nombre de la clase que realiza la llamada, y static:: revela cuál es su alcance.

Se le ha llamado "enlace estático en tiempo de ejecución" teniendo en cuenta un punto de vista interno. "Enlace en tiempo de ejecución" viene del hecho de que static:: ya resuelve a la clase en la 
que se definió el método, sino que en su lugar se resolverá utilizando información en tiempo de ejecución debido a que se puede utilizar (entre otras cosas) para las llamadas de métodos estáticos,  
se le llamó también "enlace estático".

Un exemples on es veuen les diferències ([4]):

<?php
  
class Animal
{
    public static $name = "animal";
  
    // Return the class that is represented by "self::"
    public function getSelfClass()
    {
        return get_class();
    }
  
    // Return the class that is represented by "static::"
    public function getStaticClass()
    {
        return get_called_class();
    }
  
    public function selfVar()
    {
        return self::$name;
    }
  
    public function staticVar()
    {
        return static::$name;
    }
  
    public function selfMethod()
    {
        return self::getName();
    }
  
    public function staticMethod()
    {
        return static::getName();
    }
  
    protected function getName()
    {
        return "animal";
    }
  
}
  
class Penguin extends Animal
{
    public static $name = "penguin";
  
    protected function getName()
    {
        return "penguin";
    }
}
  
var_dump(Penguin::selfVar());
var_dump(Penguin::staticVar());
var_dump(Penguin::selfMethod());
var_dump(Penguin::staticMethod());
var_dump(Penguin::getSelfClass());
var_dump(Penguin::getStaticClass());

Resources:

Vegeu també

Enllaços externs