O PHP 7 trouxe novidades com relação a tipos de declaração de parâmetros, possibilitando que estes sejam declarados explicitamente com os tipos bool, float, int e string. Seguindo esta linha de pensamento, incluiu-se no PHP 7.1 a capacidade de configurar estes parâmetros tipados com valores null, tanto para tipos básicos como complexos.

Antes do Novo Recurso

O exemplo abaixo apresenta um código-fonte para a classe Foo\Bar que possui um método simples de encapsulamento.

<?php

namespace Foo;

/**
 * Classe de Exemplo
 */
class Bar
{
    /**
     * Adaptador
     * @type AdapterInterface
     */
    protected $adapter;

    /**
     * Configura o Adaptador
     *
     * @param  $adapter AdapterInterface Valor para Configuração
     * @return self     Próprio Objeto para Encadeamento
     */
    public function setAdapter(AdapterInterface $adapter) : self
    {
        // Configuração
        $this->adapter = $adapter;
        // Encadeamento
        return $this;
    }

    /**
     * Apresenta o Adaptador
     *
     * @return AdapterInterface Valor Configurado
     */
    public function getAdapter() : AdapterInterface
    {
        // Apresentação
        return $this->adapter;
    }
}

No caso anterior, após a configuração do adaptador com um objeto do tipo Foo\AdapterInterface, através do método Foo\Bar::setAdapter, não há a possibilidade de desconfiguração do atributo, podendo fazer com que o mesmo volte ao estado em tempo de construção. Para isto, pode-se adicionar um valor padrão null ao parâmetro $adapter do método em questão.

<?php
    // ...

    public function setAdapter(AdapterInterface $adapter = null) : self
    {
        // Configuração
        $this->adapter = $adapter;
        // Encadeamento
        return $this;
    }

    // ...

Ou, ainda, adicionar um novo método, responsável pela desconfiguração do atributo.

<?php
    // ...

    public function unsetAdapter() : self
    {
        // Configuração
        $this->adapter = null;
        // Encadeamento
        return $this;
    }

    // ...

Ambos os casos possuem problemas de capacidade de escrita. O primeiro caso define que existe um valor padrão que, se ignorado durante a utilização do método, deve ser utilizado. Todavia, a chamada do método se torna pouco compreensível quando sem parâmetros, sendo que encapsulamentos do tipo set possuem a lógica de recebimento de valores em seus parâmetros.

<?php

// Construção
$element = new Foo\Bar();
// Desconfigura Adaptador
$element->setAdapter();

var_dump($element);

/*
object(Foo\Bar)#1 (1) {
  ["adapter":protected]=>
  NULL
}
*/

Já o segundo, adiciona um método que executa nenhuma funcionalidade importante e que, dependendo da quantidade de atributos que devem usufruir desta necessidade, quebra-se facilmente a regra ExcessivePublicCount do PHPMD.

<?php

// Construção
$element = new Foo\Bar();
// Desconfigura Adaptador
$element->unsetAdapter();

var_dump($element);

/*
object(Foo\Bar)#1 (1) {
  ["adapter":protected]=>
  NULL
}
*/

Por fim, caso o adaptador não esteja configurado, o acesso ao método Foo\Bar::getAdapter apresenta um erro, pois o retorno foi configurado com tipo explícito.

<?php

// Construção
$element = new Foo\Bar();
// Acesso
$element->getAdapter();

/*
Fatal error:  Uncaught TypeError: Return value of Foo\Bar::getAdapter()
  must be an instance of Foo\AdapterInterface, null returned in [...][...]:38
Stack trace:
#0 [...][...](6): Foo\Bar->getAdapter()
#1 {main}
  thrown in [...][...] on line 38
*/

Aplicando Nullable Types

Com a utilização dos nullable types (tipos anuláveis, em tradução livre) disponíveis no PHP 7.1, adiciona-se o caractere ? antes do tipo explícido, informando à linguagem de programação que o valor passado pode ser aquele específico ou null. O exemplo abaixo apresenta a classe Foo\Bar com sua estrutura modificada, usufruindo dos tipos anuláveis.

<?php

namespace Foo;

/**
 * Classe de Exemplo
 */
class Bar
{
    /**
     * Adaptador
     * @type AdapterInterface|null
     */
    protected $adapter;

    /**
     * Configura o Adaptador
     *
     * @param  $adapter AdapterInterface|null Valor para Configuração
     * @return self     Próprio Objeto para Encadeamento
     */
    public function setAdapter(?AdapterInterface $adapter) : self
    {
        // Configuração
        $this->adapter = $adapter;
        // Encadeamento
        return $this;
    }

    /**
     * Apresenta o Adaptador
     *
     * @return AdapterInterface|null Valor Configurado
     */
    public function getAdapter() : ?AdapterInterface
    {
        // Apresentação
        return $this->adapter;
    }
}

Os tipos anuláveis, contrariando o valor padrão apresentado anteriormente no primeiro caso, obrigam que seja informado o valor do parâmetro definido no método, mesmo que este seja null. Ainda, o método de acesso ao encapsulamento não apresentará erros, pois há a informação de que o seu retorno pode ser nula.

<?php

// Construção
$element = new Foo\Bar();
// Desconfigura Adaptador
$element->setAdapter(null);

var_dump($element->getAdapter());

/*
NULL
*/

Conclusão

A disponibilização da versão 7 do PHP traz mudanças importantes para os desenvolvedores que usufruem da linguagem. A própria inclusão da declaração de tipos básicos significa um grande avanço com relação à tipagem.

Com a inclusão dos tipos anuláveis na linguagem, os códigos-fonte tendem a reduzir o tamanho e aumentar a confiabilidade. Todavia, a legibilidade do caractere ? pode apresentar problemas, pois, o autor deste texto desconhece outra linguagem de programação que utiliza a mesma estrutura.