PHP 8.0: Novas funções, classes e JIT (4/4)
A versão 8.0 do PHP foi lançada. Está tão cheia de novidades como nenhuma versão anterior. A apresentação delas exigiu quatro artigos separados. Neste último, veremos as novas funções e classes e apresentaremos o Just in Time Compiler.

Novas funções
A biblioteca padrão do PHP possui centenas de funções e na versão 8.0 apareceram seis novas. Parece pouco, mas a maioria delas preenche lacunas da linguagem. O que corresponde bem ao tom de toda a versão 8.0, que aprimora e consolida o PHP como nenhuma versão anterior. Você pode encontrar uma visão geral de todas as novas funções e métodos no guia de migração.
str_contains()
str_starts_with()
str_ends_with()
Funções para verificar se uma string começa, termina ou contém uma substring.
if (str_contains('Nette', 'te')) {
...
}
Juntamente com a chegada deste trio, o PHP define como lidar com a string vazia ao pesquisar, o que também orienta todas as outras funções relacionadas, de forma que a string vazia é encontrada em todos os lugares:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (anteriormente false)
Graças a isso, o comportamento do trio é completamente idêntico aos análogos no Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Por que essas funções são tão importantes? As bibliotecas padrão de
todas as linguagens são sempre sobrecarregadas pelo desenvolvimento histórico
e é impossível evitar o surgimento de inconsistências e erros. Mas, ao mesmo
tempo, é o cartão de visita de cada linguagem. É surpreendente que no PHP,
com 25 anos de idade, faltem funções para operações tão básicas como
retornar o primeiro ou o último elemento de um array, escapar HTML sem
truques (htmlspecialchars
não escapa apóstrofo) ou justamente
pesquisar uma string dentro de outra. Que isso possa ser contornado de
alguma forma não se sustenta, porque o resultado não é um código
legível e compreensível. É uma lição para todos os autores de API. Quando
você vê que uma parte considerável da documentação de uma função é
ocupada pela explicação de suas peculiaridades (como os valores de retorno de
strpos
), é um sinal claro para ajustar a biblioteca e adicionar
justamente str_contains
.
get_debug_type()
Substitui o já obsoleto get_type()
. Em vez de tipos longos
como integer
, retorna o int
usado hoje; no caso de
objetos, retorna diretamente o tipo:
Valor | gettype() | get_debug_type() |
---|---|---|
'abc' |
string |
string |
[1, 2] |
array |
array |
231 |
integer |
int |
3.14 |
double |
float |
true |
boolean |
bool |
null |
NULL |
null |
new stdClass |
object |
stdClass |
new Foo\Bar |
object |
Foo\Bar |
function() {} |
object |
Closure |
new class {} |
object |
class@anonymous |
new class extends Foo {} |
object |
Foo@anonymous |
curl_init() |
resource |
resource (curl) |
curl_close($ch) |
resource (closed) |
resource (closed) |
Objetificação de recursos
Valores do tipo resource vêm dos tempos em que o PHP ainda não tinha objetos, mas na verdade precisava deles. Assim surgiram os resources. Hoje temos objetos e, em comparação com os resources, eles funcionam muito melhor com o garbage collector, então o plano é substituí-los gradualmente por objetos.
A partir do PHP 8.0, os resources de imagens, conexões curl, openssl, xml, etc. são alterados para objetos. No PHP 8.1, será a vez das conexões FTP, etc.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - Quebra de BC
Esses objetos ainda não têm métodos, nem você pode criar instâncias
deles diretamente. Por enquanto, trata-se realmente apenas de tirar os resources
obsoletos do PHP sem alterar a API. E isso é bom, porque criar uma boa API é
uma tarefa separada e exigente. Ninguém deseja que surjam no PHP outras classes
como SplFileObject
com métodos nomeados fgetc()
ou
fgets()
.
PhpToken
O tokenizador também está se movendo para objetos, e, portanto, as
funções em torno de token_get_all
. Desta vez, não se trata de se
livrar de resources, mas obtemos um objeto completo que representa um
token PHP.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // instância PhpToken
echo $token->id; // T_OPEN_TAG
echo $token->text; // '<?php'
echo $token->line; // 1
echo $token->getTokenName(); // 'T_OPEN_TAG'
echo $token->is(T_STRING); // false
echo $token->isIgnorable(); // true
O método isIgnorable()
retorna true para os tokens
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
e
T_OPEN_TAG
.
Weak maps
Weak maps estão relacionados ao garbage collector, que libera da memória todos os objetos e valores que não estão mais em uso (ou seja, não há nenhuma variável ou propriedade em uso que os contenha). Como a vida de uma thread PHP é efêmera e hoje temos memória suficiente disponível nos servidores, geralmente não nos preocupamos com questões relacionadas à liberação eficiente de memória. Mas para scripts de execução mais longa, elas são cruciais.
O objeto WeakMap
é semelhante ao
SplObjectStorage
. Em ambos, objetos são usados como chaves e
permitem o armazenamento de quaisquer valores sob eles. A diferença é que
WeakMap
não impede que o objeto seja liberado pelo garbage
collector. Ou seja, se o único lugar onde o objeto ainda existe for a chave
em um weak map, ele será removido do mapa e da memória.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Para que isso é bom? Por exemplo, para cache. Tenhamos um método
loadComments()
, ao qual passamos um artigo de blog e ele retorna
todos os seus comentários. Como o método é chamado repetidamente com
o mesmo artigo, criaremos também getComments()
, que armazenará
em cache o resultado do primeiro método:
class Comments
{
private WeakMap $cache;
public function __construct()
{
$this->cache = new WeakMap;
}
public function getComments(Article $article): ?array
{
$this->cache[$article] ??= $this->loadComments($article);
return $this->cache[$article]
}
...
}
O truque é que, no momento em que o objeto $article
for
liberado (por exemplo, a aplicação começar a trabalhar com outro artigo), sua
entrada no cache também será liberada.
PHP JIT (Just in Time Compiler)
Talvez você saiba que o PHP é compilado para o chamado opcode, que são instruções de baixo nível que você pode ver, por exemplo, aqui e que são executadas pela máquina virtual do PHP. E o que é JIT? O JIT pode compilar transparentemente o PHP diretamente para código de máquina, que é executado diretamente pelo processador, evitando assim a execução mais lenta pela máquina virtual.
O JIT, portanto, visa acelerar o PHP.
A tentativa de implementar JIT no PHP remonta a 2011 e é obra de Dmitry Stogov. Desde então, ele experimentou 3 implementações diferentes, mas nenhuma delas chegou ao PHP estável pelos seguintes motivos: o resultado nunca foi um aumento substancial de desempenho para aplicações web típicas; complica a manutenção do PHP (ou seja, ninguém além de Dmitry entende 😉); existiam outras maneiras de melhorar o desempenho sem precisar usar JIT.
O salto de desempenho do PHP na versão 7 foi um subproduto do trabalho no JIT, embora paradoxalmente ele não tenha sido implantado. Isso só acontece no PHP 8. Mas vou frear imediatamente as expectativas exageradas: provavelmente você não notará nenhuma aceleração.
Então, por que o JIT entra no PHP? Em primeiro lugar, outras formas de melhorar o desempenho estão se esgotando lentamente e o JIT está simplesmente na vez. Embora não traga aceleração em aplicações web comuns, ele acelera significativamente, por exemplo, cálculos matemáticos. Abre-se assim a possibilidade de começar a escrever essas coisas em PHP. E, na verdade, seria possível implementar diretamente em PHP funções que até agora exigiam implementação direta em C por causa da velocidade.
O JIT faz parte da extensão opcache
e é ativado junto com ela
no php.ini (leia a documentação
sobre aqueles quatro dígitos):
zend_extension=php_opcache.dll
opcache.jit=1205 ; configuração usando o quarteto OTRC
opcache.enable_cli=1 ; para funcionar também no CLI
opcache.jit_buffer_size=128M ; memória reservada para o código compilado
Que o JIT está em execução, você saberá, por exemplo, no painel de informações na Tracy Bar.
O JIT funciona muito bem quando todas as variáveis têm tipos claramente
definidos e não podem mudar durante chamadas repetidas do mesmo código.
Portanto, estou curioso para saber se um dia declararemos tipos também para
variáveis no
PHP: string $s = 'Olá, esta é a conclusão da série';
Faça o login para enviar um comentário