PHP 8.0: Visão geral completa das notícias (1/4)
A versão 8.0 do PHP está sendo lançada agora mesmo. Ela está cheia de coisas novas como nenhuma outra versão antes. Sua introdução merecia quatro artigos separados. No primeiro, vamos dar uma olhada no que ele traz no nível do idioma.
Antes de mergulharmos no PHP, saiba que a versão atual da Nette está totalmente preparada para a oitava versão. Além disso, como um presente, um Nette 2.4 totalmente compatível foi lançado, portanto do ponto de vista da estrutura não há nada que o impeça de usá-lo.
Argumentos nomeados
Vamos começar imediatamente com uma bomba, que poderia ser ousadamente designada como um jogo de mudança. Os argumentos podem agora ser passados a funções e métodos não apenas posicionados, mas de acordo com seu nome. O que é absolutamente legal no caso de um método ter realmente parâmetros demais:
class Response implements IResponse
{
public function setCookie(
string $name,
string $value,
string|DateInterface|null $time,
string $path = null,
string $domain = null,
bool $secure = null,
bool $httpOnly = null,
string $sameSite = null
) {
...
}
}
Os dois primeiros argumentos são passados em posição, outros pelo nome: (os nomes devem seguir os posicionais)
$response->setCookie('lang', $lang, sameSite: 'None');
// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Antes da introdução deste recurso, havia planos para criar uma nova API
para envio de cookies na Nette, porque o número de argumentos para
setCookie()
realmente cresceu e a notação posicional era confusa.
Isso não é mais necessário, porque os argumentos nomeados são, neste caso, a
API mais conveniente. A IDE vai insinuá-los e há um tipo de segurança.
Eles são ideais mesmo para explicar argumentos lógicos, onde seu uso não
é necessário, mas um simples true
ou false
não
o corta:
// before
$db = $container->getService(Database::class, true);
// now
$db = $container->getService(Database::class, need: true);
Os nomes dos argumentos agora fazem parte do API público. Não é mais possível alterá-los à vontade. Por causa disso, até mesmo Nette está passando por uma auditoria determinando se todos os argumentos têm um nome adequado.
Os argumentos nomeados também podem ser usados em combinação com os variadíssimos:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]
A matriz $args
pode agora conter até mesmo chaves não
numéricas, o que é uma espécie de quebra BC. O mesmo se aplica ao
comportamento de call_user_func_array($func, $args)
, onde as chaves
da matriz $args
desempenham agora um papel muito mais
significativo. Pelo contrário, as funções da família func_*()
estão protegidas de argumentos nomeados.
Os argumentos indicados estão intimamente relacionados ao fato de que
o operador de splat ...
pode agora expandir as matrizes
associativas:
variadics(...['b' => 2, 'c' => 3]);
Surpreendentemente, não funciona dentro de matrizes no momento:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
A combinação de argumentos nomeados e variadíssimos dá a opção de
finalmente ter uma sintaxe fixa, por exemplo, para o método
link()
, ao qual podemos passar argumentos nomeados, bem como
argumentos posicionais:
// before
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // had to be an array
// now
$presenter->link('Product:detail', $id, page: 1);
A sintaxe dos argumentos nomeados é muito mais sexy do que escrever arrays,
portanto Latte
adotou-o imediatamente, onde pode ser usado, por exemplo, nas tags
{include}
e {link}
:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Voltaremos aos argumentos nomeados na terceira parte da série em relação aos atributos.
Uma expressão pode lançar uma exceção
Lançar uma exceção é agora uma expressão. Você pode embrulhá-la entre
parênteses e acrescentá-la a uma condição if
. Hmmm, isso não
soa muito prático. No entanto, isto é muito mais interessante:
// before
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('value not set');
}
$value = $arr['value'];
// now, when throw is an expression
$value = $arr['value'] ?? throw new \InvalidArgumentException('value not set');
Como até agora as funções de seta só podem conter uma expressão, agora elas podem lançar exceções graças a esta característica:
// only single expression
$fn = fn() => throw new \Exception('oops');
Expressão de jogo
A declaração switch-case
tem dois vícios principais:
- ela usa uma comparação não restrita
==
em vez de===
- você tem que ter cuidado para não esquecer
break
por engano
Por causa disso, o PHP vem com uma alternativa na forma de uma nova
expressão match
, que usa uma comparação rigorosa e,
inversamente, não usa break
.
switch
exemplo de código:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('ok');
break;
case 400:
$message = $this->formatMessage('not found');
break;
case 500:
$message = $this->formatMessage('server error');
break;
default:
$message = 'unknown status code';
break;
}
E o mesmo (apenas com uma comparação rigorosa) escrito usando
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('not found'),
500 => $this->formatMessage('server error'),
default => 'unknown status code',
};
Note que match
não é uma estrutura de controle como
switch
, mas uma expressão. No exemplo, nós atribuímos seu valor
resultante a uma variável. Ao mesmo tempo, as “opções” individuais
também são expressões, portanto não é possível escrever mais passos, como
no caso do switch
.
Em caso de não haver correspondência (e não há cláusula padrão), a
exceção UnhandledMatchError
é lançada.
A propósito, há também {switch}
, {case}
e
{default}
tags em Latte. Sua função corresponde exatamente ao
novo match
. Eles utilizam comparação rigorosa, não exigem
break
e é possível especificar múltiplos valores separados por
vírgulas em case
.
Operador Nullsafe
O encadeamento opcional permite escrever uma expressão, cuja avaliação
cessa se encontrar nula. Isso é graças ao novo operador ?->
.
Ela substitui muitos códigos que, de outra forma, teriam que ser repetidamente
checados por nulos:
$user?->getAddress()?->street
// approximately translates to
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Por que “aproximadamente”? Porque na realidade a expressão está sendo
avaliada de forma mais engenhosa para que nenhum passo seja repetido. Por
exemplo, $user->getAddress()
é chamado apenas uma vez, portanto
o problema causado pelo método que devolve algo diferente pela primeira e
segunda vez não pode ser encontrado.
Esta característica foi trazida pelo Latte há um ano. Agora o próprio PHP está adotando-o. Ótimo.
Promoção da propriedade dos construtores
Açúcar sintático que economiza a escrita do tipo duas vezes e a variável quatro vezes. É uma pena que não tenha vindo numa época em que não tínhamos idéias tão inteligentes que hoje o escrevemos para nós 🙂
class Facade
{
private Nette\Database\Connection $db;
private Nette\Mail\Mailer $mailer;
public function __construct(Nette\Database\Connection $db, Nette\Mail\Mailer $mailer)
{
$this->db = $db;
$this->mailer = $mailer;
}
}
class Facade
{
public function __construct(
private Nette\Database\Connection $db,
private Nette\Mail\Mailer $mailer,
) {}
}
Ele funciona com Nette DI, você pode começar a usá-lo.
Verificações mais rígidas para operadores aritméticos e bitwise
O que uma vez ajudou as linguagens dinâmicas de script a se destacar se tornou seu ponto mais fraco. Uma vez, o PHP se livrou de “citações mágicas”, o registro global de variáveis e agora o comportamento relaxado está sendo substituído pelo rigor. O tempo, quando em PHP você podia adicionar, multiplicar, etc. quase qualquer tipo de dado para o qual não fazia sentido, já se foi há muito tempo. A partir da versão 7.0, o PHP está se tornando cada vez mais rigoroso e desde a versão 8.0, uma tentativa de usar qualquer operador aritmético/bitwise em arrays, objetos ou recursos termina com o TypeError. A exceção são as adições de arrays.
// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Comparações entre números de cordel a cordel saner
Ou tornar o operador solto ótimo novamente.
Parece que não há mais espaço para o operador solto ==
, que
é apenas uma gralha ao escrever ===
, mas esta mudança a devolve
de volta ao mapa novamente. Se já o temos, deixe-o comportar-se
razoavelmente. Como resultado da comparação anterior “irracional”, por
exemplo, in_array()
poderia te derrubar de forma desagradável:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false
A mudança de comportamento de ==
diz respeito à comparação
de números e cordas “numéricas” e é mostrada na tabela a seguir:
Comparação | Antes do | PHP 8.0 |
---|---|---|
0 == "0" |
true | true |
0 == "0.0" |
true | true |
0 == "foo" |
true | false |
0 == "" |
true | false |
42 == " 42" |
true | true |
42 == "42 " |
true | true |
42 == "42foo" |
true | false |
42 == "abc42" |
false | false |
"42" == " 42" |
true | true |
"42" == "42 " |
false | true |
Surpreendentemente, é uma pausa BC no cerne da linguagem, que foi aprovada sem qualquer resistência. E isso é bom. O JavaScript poderia ser muito invejoso a este respeito.
Relatório de erros
Muitas funções internas agora acionam o TypeError e ValueError ao invés
de avisos que eram fáceis de ignorar. Uma série de avisos do kernel foram
reclassificados. O operador de desligamento @
agora não silencia
erros fatais. E a DOP lança exceções por padrão.
A Nette sempre tentou resolver essas coisas de alguma forma. Tracy modificou o comportamento do operador de desligamento, banco de dados trocou o comportamento das DOPs, Utils contêm substituições para funções padrão, que lançam exceções ao invés de avisos fáceis de serem ignorados, etc. É bom ver que a direção estrita que a Nette tem em seu DNA se torna a direção nativa do idioma.
Incrementos chave de matriz negativa
$arr[-5] = 'first';
$arr[] = 'second';
Qual será a chave do segundo elemento? Costumava ser 0
, since
PHP 8 it’s -4
.
Vírgula de fuga
O último lugar, onde a vírgula de fuga não podia estar, era a definição de argumentos de função. Isto é uma coisa do passado:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // trailing comma
) {
....
}
$object::class
A constante mágica ::class
também funciona com objetos
$object::class
, substituindo completamente a função
get_class()
.
Apanhar exceções apenas por tipo
E finalmente: não é necessário especificar uma variável para a exceção na cláusula de captura:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
- Nas próximas partes, veremos grandes inovações em relação aos tipos de dados, mostraremos o que são atributos, que novas funções e classes apareceram no PHP e apresentaremos o Compilador Just in Time.*
Para enviar um comentário, faça o login