BT

Início Artigos PHP 7 - Melhorias em Classes e Interfaces

PHP 7 - Melhorias em Classes e Interfaces

Favoritos

Pontos Principais

  • O PHP 7 adicionou classes anônimas para objetos pontuais. Exemplos sendo um objeto de valor e um objeto que implementa uma interface para injeção de dependência;

  • As classes anônimas são projetadas para uso único e não requerem definição completa de classe;

  • As classes anônimas são como classes de comuns e podem estender outras classes, implementar interfaces, definir argumentos do construtor, e muito mais.

  • O PHP 7 introduziu a classe IntlChar para acessar informações sobre caracteres Unicode.

  • O PHP 7 descontinuou alguns recursos, como os construtores do PHP 4.

Nesta série de artigos, estamos explorando os novos recursos no PHP 7. No primeiro artigo, preparamos o ambiente e introduzimos o PHP 7, depois discutimos os novos recursos relacionados à programação orientada a objetos. Neste artigo, discutiremos as melhorias feitas nas classes e interfaces do PHP.

Classes anônimas

Geralmente, usadas em momentos específicos, as classes anônimas são objetos descartáveis que podem ser usados no lugar de instâncias de classes comuns.

O PHP 7.0 adicionou suporte para classes anônimas, que são convenientes para instanciar, mesmo para uso único. Classes anônimas são exatamente como classes normais, pois podem estender outras classes, implementar interfaces, definir argumentos construtores, entre outras coisas.

Como exemplo, criaremos uma classe anônima para manipular mensagens de log de um servidor. Crie um script chamado anonymous.php e defina uma interface LogMsg contendo a função setMsg(string $msg) que permite definir uma mensagem no log. Além disso, crie uma classe ServerLog com os métodos getter/setter getLogMsg(): LogMsg e setLogMsg (LogMsg $logMsg) para definir uma mensagem no log.

<?php
interface LogMsg {
	public function setMsg(string $msg);
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

Crie uma instância da classe ServerLog e chame a função setLogMsg(LogMsg $logMsg) com o argumento fornecido como uma classe anônima.

$serverLog = new ServerLog;
$serverLog->setLogMsg(new class implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
});

Se executar o script, o var_dump produzirá uma referência ao objeto de classe anônimo que passamos para SetLogMsg.

object(class@anonymous)#2 (0) { }

Se não tivéssemos usado uma classe anônima no exemplo anterior, precisaríamos fornecer uma definição de classe completa implementando a interface LogMsg. O mesmo exemplo sem usar classes anônimas fica mais ou menos assim.

<?php
interface LogMsg {
	public function setMsg(string $msg);
}
class ServerLogMsg implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new ServerLogMsg());
var_dump($serverLog->getLogMsg());
?>

Todos os objetos instanciados da mesma declaração de classe anônima são instâncias dessa mesma classe e idênticos entre si. A comparação idêntica é realizada com o operador === e implica que os objetos que estão sendo comparados são iguais e do mesmo tipo. A comparação de identidade usando === pode parecer confusa no início. Vamos começar com o operador de igualdade ==. Duas instâncias de objeto são iguais, quando comparadas com o operador ==, se tiverem os mesmos atributos e valores e forem instâncias da mesma classe. Com o operador de comparação de identidade (===), dois objetos são idênticos se, e somente se, referem-se à mesma instância da mesma classe.

Para entender isso, crie um script anonymous-class-objects.php e defina uma função que retorne um objeto de classe anônimo. Agora, usando a função get_class, obtenha o nome da classe de dois objetos anônimos diferentes instanciados, chamando a função duas vezes e comparando esses nomes. Como mostramos abaixo, os dois nomes serão iguais.

<?php
function a_class()
{
	return new class {};
}
 
if(get_class(a_class())===get_class(a_class())){
echo "Os objetos são instâncias da mesma classe ".get_class(a_class());
}else{
echo "Os objetos são instâncias de classes diferentes";
}
echo "</br>";
var_dump(get_class(a_class()));
echo "</br>";
var_dump(get_class(a_class()));
?>

A execução do script anonymous-class-objects.php produzirá uma mensagem indicando que os objetos são instâncias da mesma classe, cujo nome começa com class@anonymous. A mesma instância class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031 é retornada, o que indica que as duas classes são idênticas. O nome da classe anônima é designado pelo engine do PHP e depende da implementação.

Os objetos são instâncias da mesma classe class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031
string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"
string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"

Uma classe anônima também pode estender outra classe usando extends.

Para demonstrar isso, vamos criar um script chamado anonymous-extend-class.php e definir uma classe LogMsg com um campo $msg e e as funções get/set deste campo. Agora, vamos criar uma classe ServerLog com as funções getLogMsg(): LogMsg e setLogMsg(LogMsg $logMsg). Por fim, vamos criar uma instância da classe ServerLog e vamos chamar a função setLogMsg(LogMsg $logMsg) com o argumento LogMsg fornecido como uma classe anônima que estende outra classe LogMsg:

$serverLog = new ServerLog;
$serverLog->setLogMsg(new class extends LogMsg {
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});

O script anonymous-extend-class.php é listado da seguinte maneira:

<?php
class LogMsg {
private $msg;
	public function getMsg() {
        return  $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	    return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class extends LogMsg {
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

Execute o script e verifique o resultado. Veremos que o campo msg do tipo LogMsg está definido como NULL.

object(class@anonymous)#2 (1) { ["msg":"LogMsg":private]=> NULL }

Como era de se esperar, um argumento pode ser passado para um construtor de classe anônima.

Para demonstrar isso, vamos criar um script anonymous-extend-class-add-constructor.php e definir as classes LogMsg e ServerLog como no exemplo anterior. A única diferença é que um argumento será passado para o construtor de classe anônima:

$serverLog->setLogMsg(new class('Log Message') extends LogMsg {
	public function __construct($msg)
	{
        $this->msg = $msg;
	}
…
}

O script anonymous-extend-class-add-constructor.php fica assim:

<?php
class LogMsg {
private $msg;
	public function getMsg() {
        return  $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class('Log Message') extends LogMsg {
	public function __construct($msg)
	{
        $this->msg = $msg;
	}
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

Ao executar o script podemos verificar que a mensagem de log passada para o construtor de classe anônima é retornada por getLogMsg().

object(class@anonymous)#2 (2) { ["msg":"LogMsg":private]=> NULL ["msg"]=> string(11) "Log Message" }

Uma classe anônima pode estar aninhada em outra classe, porém não pode usar as funções ou propriedades protegidas ou privadas da classe externa. Para usar as propriedades privadas da classe externa na classe anônima interna, passe as propriedades como argumentos para o construtor da classe anônima, conforme mostrado no exemplo anterior.

Para demonstrar isso, vamos criar um script inner-class-private.php e definir uma classe externa Outer com uma propriedade privada. Além disso, vamos adicionar uma função inner() que retorne um objeto de classe anônima. A propriedade privada da classe Outer é passada para o construtor da classe anônima e configurada como uma propriedade privada da classe anônima. Agora, usando uma função declarada na classe anônima, podemos retornar o valor da propriedade privada passada da classe Outer para a classe anônima interna:

return new class($this->a) extends Outer {
             private $a;
            public function __construct($a)
        	{
                $this->a = $a;
        	}
            public function getFromOuter()
        	{
                echo $this->a;
        	}
    	};

Para exibir o valor da propriedade privada passada de Outer para a classe anônima interna, vamos criar uma instância da classe Outer e chamar a função inner(), que cria a classe anônima e depois chamar a função de classe anônima que retorna o valor da propriedade privada:

echo (new Outer)->inner()->getFromOuter();

O script inner-class-private.php fica assim:

<?php
class Outer
{
	private $a = 1;
	public function inner()
	{
    	  return new class($this->a) {
             private $a;
            public function __construct($a)
        	{
                $this->a = $a;
        	}
        	            public function getFromOuter()
        	{
          	  echo $this->a;	
        	}
    	  };
	}
}
echo (new Outer)->inner()->getFromOuter();
?>

Ao executar o script podemos verificar que o valor da propriedade privada (1) passado do Outer para a classe interna é exibido no navegador.

A seguir, demonstraremos como uma função protegida de uma classe Outer pode ser chamada em uma classe anônima. Para invocar uma das funções protegidas definidas na classe externa, a classe anônima interna precisa estender a primeira.

Para observarmos esta propriedade, vamos criar um script inner-class-protected.php e definir uma classe externa chamada Outer com um campo e uma função protegida. Agora, vamos definir outra função que cria uma classe anônima que estenda a classe Outer e faça com que essa classe anônima defina uma função que chama a função protegida da classe externa que definimos em primeiro lugar. Como a classe anônima estende a classe Outer, ela herda os campos e funções protegidos, o que implica que a função e o campo protegidos possam ser acessados ​​usando this. A função de classe anônima é invocada como antes, criando primeiro uma instância da classe Outer:

echo (new Outer)->inner()->getFromOuter();

Abaixo o script inner-class-protected.php.

<?php
class Outer
{
	protected $a = 1;
	protected function getValue()
	{
    	return 2;
	}
	public function inner()
	{
    	return new class extends Outer {
            public function getFromOuter()
        	{
                echo $this->a;
                echo "<br/>";
                echo $this->getValue();
        	}
    	};
	}
}
echo (new Outer)->inner()->getFromOuter();
?>

Execute o script e verifique o valor retornado pelo campo protegido pela classe Outer e pelas funções da classe Outer que são exibidos.

1
2

Usamos dois exemplos, um para demonstrar como chamar campos privados da classe externa e o outro para chamar campos e funções protegidas da classe externa em uma classe anônima aninhada. Os dois exemplos podem ser combinados, fazendo com que a classe anônima aninhada estenda a classe externa para herdar os campos e funções protegidos da classe externa, bem como passar os campos privados da classe externa para o construtor da classe anônima da seguinte maneira.

return new class($this->prop) extends Outer {
…
}

Para demonstrar isso, vamos criar um script inner-class.php.

<?php
class Outer
{
	private $prop = 1;
	protected $prop2 = 2;
	protected function func1()
	{
    	return 3;
	}
	public function func2()
	{
    	return new class($this->prop) extends Outer {
            private $prop3;
            public function __construct($prop)
        	{
                $this->prop3 = $prop;
        	}
            public function func3()
        	{
                return $this->prop2 + $this->prop3 + $this->func1();
        	}
    	};
	}
}
echo (new Outer)->func2()->func3();
?>

A execução do script produzirá o valor 6, obtido pela chamada dos campos e funções da classe externa.

Nova classe IntlChar para caracteres unicode

O PHP 7.0 introduziu uma nova classe chamada IntlChar com vários métodos úteis para acessar informações sobre caracteres unicode. Observe que a extensão Intl precisa ser instalada para usar a classe IntlChar, o que é possível descomentando a seguinte linha do arquivo de configuração no php.ini:

extension=intl

Alguns dos métodos da classe IntlChar estão descritos na Tabela 2.

Tabela 2. Métodos da classe IntlChar

Método

Descrição

IntlChar::charFromName

Encontra um caractere unicode pelo nome e retorna o ponto do valor

IntlChar::charName

Retorna o nome de um caractere unicode

IntlChar::charType

Retorna o valor geral da categoria para o ponto do código unicode. Por exemplo, para a categoria de letra maiúscula, usamos IntlChar::CHAR_CATEGORY_TITLECASE_LETTER. Para número decimal, a categoria que iremos usar é IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER. Se o caractere não estiver em nenhuma das categorias definidas, a categoria será IntlChar::CHAR_CATEGORY_UNASSIGNED.

IntlChar::chr

Retorna o caractere unicode pelo valor do ponto do código

IntlChar::getNumericValue

Retorna o valor numérico para um ponto de código unicode.

IntlChar::isdefined

Retorna um valor booleano para indicar se um caractere está definido ou não.

Vamos criar um script Intlchar.php para testar alguns desses métodos. No exemplo a seguir, vamos produzir a versão unicode com constante IntlChar::UNICODE_VERSION, para descobrir qual é o ponto do código unicode da LETRA CAPITAL LATINA B; e para descobrir se \u{00C6} está definido. O script Intlchar.php está descrito abaixo.

<?php
printf('Versão do unicode : ');
echo "<br/>";
echo IntlChar::UNICODE_VERSION;
echo "<br/>";
echo IntlChar::charFromName("LATIN CAPITAL LETTER B");
echo "<br/>";
var_dump(IntlChar::isdefined("\u{00C6}"));
?>

A execução do script produzirá o seguinte: 

Unicode Version :
12.1
66
bool(true)

Recursos obsoletos

O PHP 7 também depreciou vários recursos.

Entre os recursos descontinuados no PHP 7.0.x está os construtores do estilo "antigo" que foram incluídos no PHP 4, ou seja, os métodos de construtor com o mesmo nome da classe.

Por exemplo, vamos criar um script constructor.php e copiar o seguinte código:

<?php
class Catalog {
	function Catalog() {
	}
}
?>

O script declara uma classe Catalog com um método usando o mesmo nome Catalog. Se executarmos o script, a seguinte mensagem será exibida:

Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; Catalog has a deprecated constructor

Traduzindo...

Descontinuado: Métodos com o mesmo nome da classe não serão construtores em uma versão futura do PHP; O “Catalog” possui um construtor obsoleto.

Além disso, chamadas estáticas para métodos não estáticos foram descontinuados no PHP 7.0.0. Para demonstrar isso, podemos criar um script static.php e copiar o seguinte código, que declara uma classe com uma função não estática getTitle() para tentarmos fazer uma chamada estática para essa função:

<?php
class Catalog {
	function getTitle() {
	}
}
Catalog::getTitle();
?>

A execução desse script produzirá a seguinte mensagem:

Deprecated: Non-static method Catalog::getTitle() should not be called statically

Traduzindo...

Descontinuado: O método não estático Catalog::getTitle() não deve ser chamado estaticamente

No PHP 7.1.x, a extensão mcrypt foi descontinuada. Os recursos depreciados no PHP 7.2 incluem unquoted strings, o método __autoload(), o create_function(), convertido em unset, usar parse_str() sem um segundo argumento, a função gmp_random() e each, assert() com um argumento string e a função read_exif_data(). Os recursos obsoletos no PHP 7.3 incluem as constantes que não diferenciam maiúsculas de minúsculas e a declaração assert() em um namespace.

Como um exemplo de descontinuação das constantes sem distinção entre maiúsculas e minúsculas, podemos executar o seguinte script no qual a função define() é chamada com o parâmetro case_insensitive definido como true:

<?php
define('CONST_1', 10, true); 
var_dump(CONST_1); 
var_dump(const_1);
?>

Isso fornecerá as seguintes mensagens: 

Deprecated: define(): Declaration of case-insensitive constants is deprecated on line 2

int(10)

Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "CONST_1" on line 4

Traduzindo...

Descontinuado: define(): A declaração de constantes que não diferenciam maiúsculas de minúsculas está descontinuada na linha 2

int (10)

Descontinuado: Constantes que não diferenciam maiúsculas de minúsculas foram descontinuadas. O correto para esta constante é "CONST_1" na linha 4

Resumo

Neste segundo artigo da série sobre o PHP 7, exploramos os novos recursos para as classes e as interfaces. O novo recurso mais notável é o suporte para as classes anônimas. O unicode também recebe um impulso com a nova classe IntlChar, que pode ser usada para obter informações sobre as caracteres unicode.

No próximo artigo da série, iremos explorar os novos recursos no sistema de tipos do PHP.

Sobre o autor

Deepak Vohra é um programador Java com as certificações Sun Certified Java Programmer and Sun Certified Web Component Developer. Deepak publicou artigos técnicos relacionados a Java e Java EE no WebLogic Developer's Journal, XML Journal, ONJava, java.net, IBM developerWorks, Java Developer's Journal, Oracle Magazine e devx. Além disso, publicou cinco livros no Docker e é um dos mentores desta tecnologia. Deepak também publicou vários artigos sobre PHP e um livro sobre Ruby on Rails para desenvolvedores de PHP e Java.

Avalie esse artigo

Relevância
Estilo/Redação

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Comentários da comunidade

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

BT

Seu cadastro no InfoQ está atualizado? Poderia rever suas informações?

Nota: se você alterar seu email, receberá uma mensagem de confirmação

Nome da empresa:
Cargo/papel na empresa:
Tamanho da empresa:
País:
Estado:
Você vai receber um email para validação do novo endereço. Esta janela pop-up fechará em instantes.