Membros estáticos
Esta aula aborda membros estáticos em PHP, incluindo propriedades e métodos static, a diferença entre self e static (late static binding), constantes de classe e o padrão Singleton. Exemplos práticos e exercícios consolidam o aprendizado.
Na programação orientada a objetos, membros estáticos pertencem à classe em si, e não a instâncias individuais. Eles são compartilhados entre todas as instâncias e podem ser acessados sem criar um objeto. Nesta aula, exploraremos propriedades e métodos static, o uso de self vs static (late static binding), constantes de classe e o padrão Singleton, que utiliza membros estáticos para garantir uma única instância.
Esses conceitos são fundamentais para escrever código mais eficiente e organizado, especialmente quando você precisa de valores globais ou comportamentos que não dependem de estado de objeto.
Propriedades e métodos static
Propriedades e métodos declarados como static podem ser acessados diretamente pela classe, sem instância. Propriedades estáticas são úteis para armazenar valores compartilhados, como contadores ou configurações. Métodos estáticos são frequentemente usados para funções utilitárias ou fábricas.
Para acessar um membro estático dentro da classe, use self::; fora da classe, use NomeDaClasse::. Exemplo:
<?php
class Math {
public static float $pi = 3.14159;
public static function circleArea(float $radius): float {
return self::$pi * $radius * $radius;
}
}
echo Math::$pi; // 3.14159
echo Math::circleArea(5); // 78.53975
?>self vs static (late static binding)
Quando você usa self:: em um método estático, ele se refere à classe onde o método foi definido, independentemente da herança. Já static:: usa late static binding: ele resolve o nome da classe no momento da execução, permitindo que subclasses substituam membros estáticos.
Isso é útil para polimorfismo com métodos estáticos. Exemplo:
<?php
class ParentClass {
public static function who() {
echo __CLASS__;
}
public static function testSelf() {
self::who();
}
public static function testStatic() {
static::who();
}
}
class ChildClass extends ParentClass {
public static function who() {
echo __CLASS__;
}
}
ChildClass::testSelf(); // ParentClass
ChildClass::testStatic(); // ChildClass
?>No exemplo, self::who() chama o método de ParentClass porque foi definido lá, enquanto static::who() respeita a classe que chamou (ChildClass).
Constantes de classe
Constantes de classe são valores imutáveis associados à classe. Elas são declaradas com const e, por padrão, são públicas. Diferente de propriedades estáticas, constantes não têm o cifrão e não podem ser alteradas após a definição. São acessadas com self:: ou NomeDaClasse::.
Exemplo:
<?php
class Config {
const APP_NAME = 'Meu App';
const VERSION = '1.0.0';
public static function getAppInfo(): string {
return self::APP_NAME . ' v' . self::VERSION;
}
}
echo Config::APP_NAME; // Meu App
echo Config::getAppInfo(); // Meu App v1.0.0
?>Constantes são úteis para valores fixos como nomes, versões, limites, etc. A partir do PHP 8.1, constantes de classe podem ser tipadas e ter visibilidade (public, protected, private).
Singleton
O padrão Singleton garante que uma classe tenha apenas uma instância e fornece um ponto global de acesso a ela. Geralmente usa um método estático getInstance() que cria a instância na primeira chamada e a retorna nas chamadas subsequentes. O construtor é privado para evitar criação externa.
Exemplo:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {}
private function __clone() {}
public function __wakeup() {
throw new \Exception("Cannot unserialize singleton");
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query(string $sql): string {
return "Executing: $sql";
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
var_dump($db1 === $db2); // true
?>O Singleton é útil para conexões de banco de dados, logs, configurações globais, etc. Mas deve ser usado com moderação, pois pode dificultar testes e criar acoplamento.
Boas práticas e observações
Evite usar membros estáticos em excesso, pois eles podem tornar o código menos testável e mais acoplado. Prefira injeção de dependência quando possível. Use constantes para valores fixos e imutáveis. Lembre-se de que métodos estáticos não podem acessar membros não estáticos diretamente.
Referências
- PHP Manual: Static Keyword
- PHP Manual: Late Static Bindings
- PHP Manual: Class Constants
- PHP Manual: Design Patterns (Singleton)
- Refactoring Guru: Singleton Pattern
Exercícios
- Crie uma classe
Countercom uma propriedade estática$counte um método estáticoincrement()que aumenta o contador. Crie também um métodogetCount()para retornar o valor. Teste chamandoCounter::increment()várias vezes e exiba o resultado.✓ Resposta:<?php class Counter { private static int $count = 0; public static function increment(): void { self::$count++; } public static function getCount(): int { return self::$count; } } Counter::increment(); Counter::increment(); Counter::increment(); echo Counter::getCount(); // 3 ?> - Explique a diferença entre
self::estatic::em herança. Dê um exemplo onde o uso destatic::é necessário para obter o comportamento polimórfico esperado.✓ Resposta:self::refere-se à classe onde o método foi definido, enquantostatic::resolve a classe chamadora em tempo de execução (late static binding). Exemplo:<?php class Animal { public static function who() { echo 'Animal'; } public static function testSelf() { self::who(); } public static function testStatic() { static::who(); } } class Cachorro extends Animal { public static function who() { echo 'Cachorro'; } } Cachorro::testSelf(); // Animal Cachorro::testStatic(); // Cachorro ?> - Crie uma classe
MathConstantscom constantesPIeE(valor de Euler). Em seguida, crie um método estático que retorna a soma dessas constantes. Use a classe para exibir o resultado.✓ Resposta:<?php class MathConstants { const PI = 3.14159; const E = 2.71828; public static function sum(): float { return self::PI + self::E; } } echo MathConstants::sum(); // 5.85987 ?> - Implemente um Singleton para uma classe
Loggerque possui um métodolog(string $message)que imprime a mensagem com um timestamp. Garanta que apenas uma instância exista e que ela não possa ser clonada ou desserializada.✓ Resposta:<?php class Logger { private static ?Logger $instance = null; private function __construct() {} private function __clone() {} public function __wakeup() { throw new \Exception("Cannot unserialize singleton"); } public static function getInstance(): Logger { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function log(string $message): void { echo "[" . date('Y-m-d H:i:s') . "] $message\n"; } } $logger = Logger::getInstance(); $logger->log("Teste de log"); ?> - Considere a classe
ParentClasscom um método estáticowhoAmI()que retorna o nome da classe usandostatic::class. Crie uma subclasseChildClassque não sobrescreve o método. ChameChildClass::whoAmI()e explique o resultado.✓ Resposta:<?php class ParentClass { public static function whoAmI(): string { return static::class; } } class ChildClass extends ParentClass {} echo ChildClass::whoAmI(); // ChildClass ?>O resultado é 'ChildClass' porque
static::classusa late static binding e retorna o nome da classe chamadora (ChildClass), mesmo que o método não tenha sido sobrescrito.