Novidades do PHP 7.1 – Parte 01 – Tipos nulos

php_logoBoa tarde pessoal,

Estou preparando um material para falar sobre as novidades do PHP 7.1, onde cada parte representará uma novidade diferente.

Na parte 01, a primeira que estou postando, estou falando sobre os tipos nulos, onde diferente do implementado no PHP 7.0, permitirá que seu método possa retornar valores nulos sem que ocorra fatal error.

Tipos escaláveis no PHP 7.0

Para quem não sabe, o PHP 7.0 implementou uma grande mudança de paradigmas em relação as versões anteriores do PHP, conhecida como “tipos escaláveis”. Basicamente, este recurso permite que o desenvolvedor possa declarar tipos primitivos(string, float, int, bool, array) e objetos nas variáveis de argumentos dos métodos e inclusive como tipos de retornos.

Lembrando que a declaração do tipo primitivo “array” nas variáveis de argumentos dos métodos está disponível desde a versão 5.1 do PHP, assim como referência a objetos está disponível desde a versão 5.0 do mesmo.

Exemplo de declaração de tipos primitivos nos argumentos dos métodos

class Exemplo {

    /**
     * @var int Atributo de valor inteiro
     */
    private $atributo;

    /**
     * @param int $atributo Argumento "$atributo" com reconhecimento do tipo primitivo "int"(inteiro).
     */
    public function setAtributo(int $atributo)
    {
        $this->atributo = $atributo;
    }

}

$exemplo = new Exemplo();
$exemplo->setAtributo('texto'); //TypeError - Estamos passando uma string onde o argumento deveria ser um número inteiro
$exemplo->setAtributo(false); //TypeError - Estamos passando um valor booleano onde o argumento deveria ser um número inteiro
$exemplo->setAtributo(22.9); //TypeError - Estamos passando um valor real onde o argumento deveria ser um número inteiro
$exemplo->setAtributo(23); //OK - Estamos passando um número inteiro 
$exemplo->setAtributo(null); //TypeError - Estamos passando nulo onde o argumento deveria ser um número inteiro

Note que no exemplo acima, conseguimos definir somente um valor cujo o mesmo seja um número inteiro. Mas e se quisermos definir que esse atributo não terá mais um valor, em outras palavras, que esse atributo seja nulo por inexistência?

Lidando com tipos primitivos, você poderá recorrer para valores válidos, como no exemplo abaixo.

    // ...
    $exemplo->setAtributo(0); //Caso o argumento tenha que receber um número inteiro ou real
    $exemplo->setAtributo(''); //Caso o argumento tenha que receber uma string
    $exemplo->setAtributo(false); //Caso o argumento tenha que receber um valor booleano

Porem, quando fazemos referência a objetos, muitas vezes haverá somente um meio de contornar a situação, permitindo a entrada de valores nulos.

    //...

    /**
     * @var \DateTime Atributo de objeto \DateTime
     */
    private $atributo2;

    /**
     * @param \DateTime|null $atributo2 Argumento "$atributo2" com reconhecimento do tipo objeto "\DateTime".
     */
    public function setAtributo2(\DateTime $atributo2 = null)
    {
        $this->atributo2 = $atributo2;
    }

    //...

No exemplo acima, definimos que o argumento “setAtributo2” poderá receber um valor nulo caso o mesmo não seja do tipo objeto “\DateTime”.

//...
$objeto = new \DateTime();
$exemplo->setAtributo2('texto'); //TypeError - Estamos passando uma string onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2(false); //TypeError - Estamos passando um valor booleano onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2(22.9); //TypeError - Estamos passando um valor real onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2(23); //TypeError - Estamos passando um número inteiro onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2($objeto); //OK - Estamos passando um objeto "\DateTime"
$exemplo->setAtributo2(null); //OK - Estamos passando nulo

Note que, se não colocássemos o termo “= null” no final da declaração, quando tentássemos definir valor nulo também ocorreria o TypeError.

Agora falando dos tipos de retornos, o mesmo funciona de uma forma bem similar, porem não suporta retorno de valores nulos no PHP 7.0(calma, já falaremos sobre como contornar esse comportamento no PHP 7.0 e o que mudará no PHP 7.1).

Exemplo de declaração

class Exemplo {

    /**
     * @var int Atributo de valor inteiro
     */
    private $atributo;

    /**
     * @var \DateTime Atributo de objeto \DateTime
     */
    private $atributo2;

    /**
     * Este método sempre retornará um número inteiro.
     *
     * @return int Número inteiro
     */
    public function getAtributo() : int
    {
        return $this->atributo;
    }

    /**
     * @param int $atributo Argumento "$atributo" com reconhecimento do tipo primitivo "int"(inteiro).
     */
    public function setAtributo(int $atributo)
    {
        $this->atributo = $atributo;
    }

    /**
     * Este método sempre retornará um objeto "\DateTime".
     *
     * @return \DateTime Objeto "\DateTime"
     */
    public function getAtributo2() : \DateTime
    {
        return $this->atributo2;
    }

    /**
     * @param \DateTime|null $atributo2 Argumento "$atributo2" com reconhecimento do tipo objeto "\DateTime".
     */
    public function setAtributo2(\DateTime $atributo2 = null)
    {
        $this->atributo2 = $atributo2;
    }

}

$exemplo = new Exemplo();
// Usando exemplo de tipos primitivos
$var = $exemplo->getAtributo(); //TypeError - Estamos tentando retornar um atributo cujo não possui um valor definido
$exemplo->setAtributo(23); //OK - Estamos passando um número inteiro 
$var = $exemplo->getAtributo(); //OK - Neste caso, "$var" receberá um número inteiro
$exemplo->setAtributo(null); //TypeError - Estamos passando nulo onde o argumento deveria ser um número inteiro
$exemplo->setAtributo(0); //OK - Estamos passando um número inteiro 
$var = $exemplo->getAtributo(); //OK - Neste caso, "$var" receberá um número inteiro

// Usando exemplo de tipos de objetos
$objeto = new \DateTime();
$var = $exemplo->getAtributo2(); //TypeError - Estamos tentando retornar um atributo cujo não possui um valor definido
$exemplo->setAtributo2($objeto); //OK - Estamos passando um objeto "\DateTime"
$var = $exemplo->getAtributo2(); //OK - Neste caso, "$var" receberá um objeto "\DateTime"
$exemplo->setAtributo2('texto'); //TypeError - Estamos passando uma string onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2(null); //OK - Estamos passando nulo
$var = $exemplo->getAtributo2(); //TypeError - Estamos tentando retornar um atributo cujo não possui um valor definido(nulo)

Note que no exemplo acima, com um tipo primitivo conseguimos contornar a possibilidade de recuperar um valor teoricamente nulo, definindo o número “0” no lugar de “null”, porem com tipo objeto “\DateTime”, não há como definirmos um valor cujo represente nulo, exceto o próprio valor “null”. Neste caso, sempre ocorrerá o TypeError.

Como contornar tipos de retornos nulos no PHP 7.0

Existem duas maneiras de tratar essa situação no PHP 7.0, tratando TypeError como exceção ou simplesmente não utilizando tipos de retornos.

Tratando TypeError como exceção

try {
    $var = $exemplo->getAtributo2(); //Neste caso, $var sempre possuirá um valor definido nesta linha quando o método puder retornar um objeto "\DateTime"
} catch (\TypeError $e) {
    //Aqui definimos o que nosso algoritmo fará quando o atributo acima não retornar um objeto "\DateTime"
    $var = null;
}

Não utilizando tipos de retornos

//...
    public function getAtributo2()
    {
        return $this->atributo2;
    }
//...

$var = $exemplo->getAtributo2(); //Neste caso, o método sempre retornará um valor independente do seu tipo, porem como definimos no método "setAtributo2" que ele só poderá ser nulo ou um objeto "\DateTime", sabemos que o valor da variável $var será sempre um de tais tipos.
if (!$var instanceof \DateTime) {
    //Rotina para quando $var for nulo
}

Tipos nulos no PHP 7.1

Já levantei alguns debates sobre a forma de como foi implementado os tipos de retornos no PHP 7.0 e me deparei com uma fragmentação da comunidade de desenvolvedores, onde alguns apoiam a decisão da equipe que implementou o recurso e outros não apoiam, justamente pelo fato ter se tornado a única linguagem com esse comportamento ao retornar tipos nulos(Java, C#, Ruby e outras linguagens permitem retorno de valores nulos mesmo que seus devidos tipos tenham sido definidos).

Por este motivo, a equipe do PHP resolveu implementar no PHP 7.1 a possibilidade de retornar valores nulos e modificar a forma na qual definimos que um argumento de um método poderá receber um valor nulo. Para isso, devemos implementar um carácter de interrogação antes de escrevermos o tipo.

Exemplo

class Exemplo {

    /**
     * @var int Atributo de valor inteiro
     */
    private $atributo;

    /**
     * @var \DateTime Atributo de objeto \DateTime
     */
    private $atributo2;

    /**
     * Este método sempre retornará um número inteiro.
     *
     * @return int Número inteiro
     */
    public function getAtributo() : int
    {
        return $this->atributo;
    }

    /**
     * @param int $atributo Argumento "$atributo" com reconhecimento do tipo primitivo "int"(inteiro).
     */
    public function setAtributo(int $atributo)
    {
        $this->atributo = $atributo;
    }

    /**
     * Este método sempre retornará um objeto "\DateTime".
     *
     * @return \DateTime Objeto "\DateTime"
     */
    public function getAtributo2() : ?\DateTime
    {
        return $this->atributo2;
    }

    /**
     * @param \DateTime|null $atributo2 Argumento "$atributo2" com reconhecimento do tipo objeto "\DateTime".
     */
    public function setAtributo2(?\DateTime $atributo2)
    {
        $this->atributo2 = $atributo2;
    }

}

$exemplo = new Exemplo();
// Usando exemplo de tipos primitivos
$var = $exemplo->getAtributo(); //TypeError - Estamos tentando retornar um atributo cujo não possui um valor definido
$exemplo->setAtributo(23); //OK - Estamos passando um número inteiro 
$var = $exemplo->getAtributo(); //OK - Neste caso, "$var" receberá um número inteiro
$exemplo->setAtributo(null); //TypeError - Estamos passando nulo onde o argumento deveria ser um número inteiro
$exemplo->setAtributo(0); //OK - Estamos passando um número inteiro 
$var = $exemplo->getAtributo(); //OK - Neste caso, "$var" receberá um número inteiro

// Usando exemplo de tipos de objetos
$objeto = new \DateTime();
$var = $exemplo->getAtributo2(); //TypeError - Estamos tentando retornar um atributo cujo não possui um valor definido
$exemplo->setAtributo2($objeto); //OK - Estamos passando um objeto "\DateTime"
$var = $exemplo->getAtributo2(); //OK - Neste caso, "$var" receberá um objeto "\DateTime"
$exemplo->setAtributo2('texto'); //TypeError - Estamos passando uma string onde o argumento deveria ser um objeto "\DateTime"
$exemplo->setAtributo2(null); //OK - Estamos passando nulo
$var = $exemplo->getAtributo2(); //OK - Neste caso, "$var" receberá um valor nulo

Note que no exemplo acima eu utilizei a nova sintaxe somente no tipo de objeto “\DateTime”, isso porque embora tipos primitivos também possa utilizar este recurso, seu uso não é obrigatório. Caso deseje utilizar da forma que foi implementado no PHP 7.0, você poderá utilizar sem problemas e a linguagem se comportará com os exemplos mostrados anteriormente.

Abaixo estou deixando o link oficial do RFC que está documentado essa mudança. Qualquer dúvida, não deixe de comentar.

Espero que tenham gostado. Até a próxima pessoal!

PHP 7.1 – RFC: Nullable Types

Anúncios

Um comentário em “Novidades do PHP 7.1 – Parte 01 – Tipos nulos

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s