O básico
class
A definição de uma classe começa com a
palavra chave class
, seguida do nome da classe,
seguido de um par de chaves que englobam as definições
de propriedades e métodos pertencentes à classe.
O nome de uma classe tem de ser válido, que não seja
uma palavra reservada do PHP. Um nome de classe válido
começa com uma letra ou sublinhado, seguido de qualquer sequência de
letras, números e sublinhados. Como uma expressão regular,
pode ser expressada assim:
^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
.
Uma classe pode conter suas próprias constantes, variáveis (chamadas de "propriedades") e funções (chamadas de "métodos").
Example #1 Definição Simples de uma Classe
<?php
class SimpleClass
{
// declaração de propriedade
public $var = 'um valor padrão';
// declaração de método
public function displayVar() {
echo $this->var;
}
}
?>
A pseudo-variável $this está disponível quando um método é chamado a partir de um contexto de um objeto. $this é o valor do objeto chamado.
Chamar um método não estático de maneira estática lança um Error. Anteriormente ao PHP 8.0.0, isto iria gerar um aviso de descontinuação, e $this estaria indefinido.
Example #2 Alguns exemplos da pseudo variável $this
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this está definida (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this não está definida.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
Saída do exemplo acima no PHP 7:
$this está definida (A) Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this não está definida. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this não está definida. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this não está definida.
Saída do exemplo acima no PHP 8:
$this está definida (A) Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 {main} thrown in %s on line 27
Classes somente leitura
A partir do PHP 8.2.0, uma classe pode ser marcada com o modificador readonly. Marcar uma classe como readonly irá acrescentar o modificador readonly em cada propriedade declarada, e prevenir a criação de propriedades dinâmicas. Além disso será impossível de acrescentar suportes a propriedades dinâmicas utilizando o atributo AllowDynamicProperties. Acrescentar o atributo em classes somente leitura irá disparar um erro de compilação.
<?php
#[\AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
Como as propriedades estáticas ou sem tipo não podem ser marcadas com o
modificador readonly
, as classes somente leitura também não podem
declará-las:
<?php
readonly class Foo
{
public $bar;
}
// Fatal error: Readonly property Foo::$bar must have type
?>
<?php
readonly class Foo
{
public static int $bar;
}
// Fatal error: Readonly class Foo cannot declare static properties
?>
Uma classe readonly pode ser estendida se, e somente se, a classe filha também ser uma classe readonly.
new
Para criar uma instância de uma classe, a instrução new
deve
ser utilizada. Um objeto sempre será criado a não ser que a classe tenha um
construtor definido que dispare uma
exceção em caso de erro. Classes
devem ser definidas antes de instanciadas (e em alguns casos isso é
obrigatório).
Se uma variável contendo uma string com o nome da classe for utilizado com
new
, uma nova instância da classe será criada. Se
a classe estiver dentro de um namespace, seu nome qualificado completo deve ser utilizado
ao fazer isto.
Note:
Se não há argumentos a serem passados para o construtor da classe, os parênteses após o nome da classe podem ser omitidos.
Example #3 Criando uma instância
<?php
$instance = new SimpleClass();
// Também pode ser feito com uma variável:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>
A partir do PHP 8.0.0, usar new
com expressões arbitrárias
é suportado. Isso permite instanciação mais complexa se a expressão
produzir uma string. As expressões devem ser envolvidas em parênteses.
Example #4 Criando uma instância usando uma expressão arbitrária
No exemplo fornecido mostramos múltiplos exemplos de expressões arbitrárias válidas que produzem um nome de classe.
Isto mostra uma chamada para uma função, concatenação de string, e a constante ::class
.
<?php
class ClasseA extends \stdClass {}
class ClasseB extends \stdClass {}
class ClasseC extends ClasseB {}
class ClasseD extends ClasseA {}
function obterAlgumaClasse(): string
{
return 'ClasseA';
}
var_dump(new (obterAlgumaClasse()));
var_dump(new ('Classe' . 'B'));
var_dump(new ('Classe' . 'C'));
var_dump(new (ClasseD::class));
?>
Saída do exemplo acima no PHP 8:
object(ClasseA)#1 (0) { } object(ClasseB)#1 (0) { } object(ClasseC)#1 (0) { } object(ClasseD)#1 (0) { }
No contexto da classe, é possível criar um novo objeto com
new self
e new parent
.
Ao atribuir uma instância de uma classe já criada, a uma variável nova, a variável nova irá acessar a mesma instância do objeto que foi atribuído. Este comportamento se mantém ao se passar instâncias a uma função. Uma cópia de um objeto criado pode ser feita clonando o mesmo.
Example #5 Atribuição de Objetos
<?php
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned terá esse valor';
$instance = null; // $instance e $reference tornam-se nulos
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
O exemplo acima produzirá:
NULL NULL object(SimpleClass)#1 (1) { ["var"]=> string(30) "$assigned terá esse valor" }
É possível usar algumas formas de criar instâncias de um objeto:
Example #6 Criando novos objetos
<?php
class Test
{
public static function getNew()
{
return new static();
}
}
class Child extends Test {}
$obj1 = new Test(); // Usando o nome da classe
$obj2 = new $obj1(); // Usando a variável que contém o objeto
var_dump($obj1 !== $obj2);
$obj3 = Test::getNew(); // Usando o método da classe
var_dump($obj3 instanceof Test);
$obj4 = Child::getNew(); // Usando o método através da classe filha
var_dump($obj4 instanceof Child);
?>
O exemplo acima produzirá:
bool(true) bool(true) bool(true)
É possível acessar um membro do objeto recém criado em uma expressão simples:
Example #7 Acessando um membro de um novo objeto criado
<?php
echo (new DateTime())->format('Y');
?>
O exemplo acima produzirá algo semelhante a:
2016
Note: Anteriormente ao PHP 7.1, os argumentos não são avaliados se não houver um construtor definido.
Propriedades e métodos
Propriedades e métodos de classe vivem em "namespaces" separados, de forma que é possível ter uma propriedade e método com mesmos nomes. A referência a propriedades e métodos tem a mesma notação, e a decisão de se uma propriedade será acessada ou uma chamada a um método feita, depende somente do contexto, ou seja, se está tentando acessar uma variável ou chamar um método.
Example #8 Acesso a propriedade vs. chamar um método
<?php
class Foo
{
public $bar = 'propriedade';
public function bar() {
return 'métod';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
O exemplo acima produzirá:
propriedade método
Isto significa que chamar diretamente uma função anônima atribuída a uma propriedade não é possível. Em vez disso, por exemplo, a propriedade deve primeiro ser atribuída a uma variável. É possível chamar uma propriedade diretamente colocando-a entre parênteses.
Example #9 Chamando uma função anônima armazenada em uma propriedade
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
O exemplo acima produzirá:
42
extends
Uma classe pode herdar constantes, métodos e propriedades de outra classe usando a
palavra-chave extends
na declaração
da classe. Não é possível herdar múltiplas classes; uma
classe só pode herdar uma classe base.
Os métodos e propriedades herdados podem ser sobrescritos declarando-os com o mesmo nome definido na classe base. Entretanto, se a classe mãe definiu um método ou constante como final, eles não poderão ser sobrescritos. É possível acessar os métodos sobrescritos ou propriedades estáticas referenciado-os com parent::.
Note: A partir do PHP 8.1.0, constantes podem ser declaradas como finais.
Example #10 Herança simples de classe
<?php
class ExtendClass extends SimpleClass
{
// Redefine o método pai
function displayVar()
{
echo "Classe Herdeira\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
O exemplo acima produzirá:
Classe Herdeira um valor padrão
Regras de compatibilidade de assinaturas
Ao sobrescrever um método, sua assinatura precisa ser compatível com
a do método original. Caso contrário um erro fatal é emitido, ou, antes do PHP 8.0.0, um alerta
E_WARNING
seria gerado.
Uma assinatura é compatível se ela respeita as regras de
variância, se ela transforma um
parâmetro obrigatório em opcional, se ela adiciona novos parâmetros opcionais,
e se ela não restringe ou apenas aumenta a visibilidade.
Isso é conhecido como Princípio de Substituição de Liskov, ou LSP.
O construtor,
e membros private
não precisam seguir essas regras de
compatibilidade, e portanto não há emissão de erros fatais no caso
de assinaturas incompatíveis.
Example #11 Métodos compatíveis
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
O exemplo acima produzirá:
Valid Valid
Os exemplos a seguir demonstram que um método derivado, que remove um parâmetro ou transforma um parâmetro opcional em mandatório, não é compatível com o método original.
Example #12 Erro fatal quando um método derivado remove um parâmetro
<?php
class Base
{
public function foo(int $a = 5) {
echo "Válido?\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
A saída do exemplo acima no PHP 8 é semelhante a:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Example #13 Erro fatal quando um método derivado transforma um parâmetro opcional em mandatório
<?php
class Base
{
public function foo(int $a = 5) {
echo "Válido?\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
A saída do exemplo acima no PHP 8 é semelhante a:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Renomear um parâmetro em uma classe derivada não é uma quebra de assinatura. Entretanto isso é desencorajado porque ocasionará um Error se argumentos nomeados forem utilizados na chamada.
Example #14 Erro ao utilizar argumentos renomeados em uma classe derivada
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Passando parâmetros de acordo com o contrato de A::test()
$obj->test(foo: "foo", bar: "bar"); // ERROR!
O exemplo acima produzirá algo semelhante a:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 {main} thrown in /in/XaaeN on line 14
::class
A palavra-chave class
também pode ser utilizada para resolução
de nome de classes.
Pode-se obter o nome completo e qualificado da classe ClassName
utilizando ClassName::class
. Isso é particularmente útil em classes com
namespaces.
Example #15 Resolução de nome da classe
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
O exemplo acima produzirá:
NS\ClassName
Note:
A resolução do nome de classe através de
::class
é uma transformação em tempo de compilação. Isso significa que no momento em que o texto do nome da classe é criado, o auto carregamento ainda não ocorreu. Como consequência, nomes de classe são expandidos mesmo se a classe não existir. Não é emitido erro nestes casos.Example #16 Resolução de um nome de classe ausente
<?php print Does\Not\Exist::class; ?>
O exemplo acima produzirá:
Does\Not\Exist
A partir do PHP 8.0.0, a constante ::class
também pode ser utilizada em
objetos. Essa resolução acontece em tempo de execução, e não em tempo de compilação. O efeito é
o mesmo de chamar get_class() em um objeto.
Example #17 Resolução de nome de objeto
<?php
namespace NS {
class ClassName {
}
}
$c = new ClassName();
print $c::class;
?>
O exemplo acima produzirá:
NS\ClassName
Métodos e propriedades nullsafe
A partir do PHP 8.0.0, propriedades e métodos também podem ser acessados com o operador
"nullsafe": ?->
. O operador nullsafe
funciona da mesma forma que os acessos de métodos e variáveis, exceto que se
o objeto referenciado é null
, então null
será retornado ao invés de uma exceção ser lançada. Se uma derreferência for parte de uma
cadeia de chamadas, o resto da cadeia é ignorado.
O resultado é similar a encapsular cada acesso dentro de um teste is_null(), mas mais compacto.
Example #18 Operador nullsafe
<?php
// A partir do PHP 8.0.0, esta linha:
$result = $repository?->getUser(5)?->name;
// É o equivalente ao seguinte código:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
Note:
O operador nullsafe é melhor utilizado quando null é considerado um valor válido e esperado de uma propriedade ou retorno de método. Para indicar um erro, uma exceção lançada é preferível.