BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos PHP 7 - Melhorias na biblioteca padrão

PHP 7 - Melhorias na biblioteca padrão

Favoritos

Pontos Principais

  • Para criar constantes em array nomeadas em tempo de execução, use a nova função define();
  • Para vincular um escopo de objeto a uma variável e invocá-lo, use a nova função Closure::call();

  • Para usar uma expressão e/ou um AssertionError personalizado com o tradicional assert(), use as expectations;

  • O PHP 7 suporta o retorno de um valor de funções geradoras;

  • O PHP 7 suporta delegação de uma função de gerador para outra;

  • Para divisão inteira, use a nova função chamada intdiv();

  • Para substituir as definições de configuração da sessão em php.ini, use a nova função session_start;

  • Para realizar uma busca e substituição de expressão regular usando callbacks, use a nova função preg_replace_callback_array();

  • O PHP 7 é capaz de gerar inteiros e bytes criptograficamente seguros;

  • O PHP 7.1 suporta a conversão de Callables para Closures;

  • As funções seta (=>) fornecem uma sintaxe concisa para funções anônimas.

O PHP 7.x traz várias melhorias e novos recursos que tocam todos os aspectos da linguagem, incluindo um suporte melhor para programação orientada a objetos, extensões para classes e interfaces, melhorias no sistema de tipos, tratamento de erros e muito mais. Nesta série de artigos, discutiremos os novos recursos nas várias versões do PHP 7.x.

No artigo anterior desta série sobre o PHP 7, discutimos novos recursos no sistema de tipos do PHP. Neste artigo, iremos explorar as melhorias nas funções do PHP 7.x

O PHP suporta vários tipos de funções, incluindo funções definidas pelo usuário, funções internas, funções de variáveis e funções anônimas.

Nova função para definir constantes em matrizes

Uma nova função chamada define() foi adicionada ao PHP 7.0 para definir constantes de array nomeadas em tempo de execução. A função define() tem a seguinte sintaxe:

bool define ( string $name , mixed $value [, bool $case_insensitive = FALSE ] )

Os parâmetros da função são discutidos na Tabela 1.

Tabela 1. Parâmetros de função define()

Parâmetro

Descrição

name

O nome da constante. O nome pode ser reservado, mas não é recomendado.

value

O valor da constante. O valor deve ser um valor escalar (inteiro, float, string, Boolean ou NULL) ou até mesmo uma matriz.

case_insensitive

Se a constante não diferencia maiúsculas de minúsculas, sendo o padrão sensível a maiúsculas e minúsculas. Constantes que não fazem distinção entre maiúsculas e minúsculas estão obsoletas no PHP 7.3.0.

Crie um script PHP chamado constant.php e defina uma constante chamada CONSTANT como descrito abaixo.

define("CONSTANT", "Hello PHP");

É possível também usar a palavra-chave const para definir uma constante:

const CONSTANT_2 = 'Hello php';
const Constant = 'HELLO PHP';

A função define() pode ser usada para definir uma constante matriz:

define("Catalog", ['Oracle Magazine','Java Magazine']);

Os valores constantes da matriz podem ser produzidos usando o acesso ao elemento da matriz.

echo Catalog[0]
echo Catalog[1]

O arquivo constant.php está descrito abaixo:

<?php
define("CONSTANT", "Hello PHP");
echo CONSTANT."<br>";  
const CONSTANT_2 = 'Hello php';
echo CONSTANT_2."<br>";
const Constant = 'HELLO PHP';
echo Constant."<br>";
define("Catalog", ['Oracle Magazine','Java Magazine']);
echo Catalog[0]."<br>";
echo Catalog[1]
?>

Execute o script para gerar os valores constantes que foram definidos.

Hello PHP
Hello php
HELLO PHP
Oracle Magazine
Java Magazine

Uma constante definida globalmente, como TRUE ou FALSE, não pode ser redefinida. Para demonstrar isso, crie um script chamado const.php que define uma constante TRUE configurando o valor para 20:

<?php
define('TRUE', 20);
echo TRUE;
?>

Se executar o script, verá o valor 1, que é o valor definido globalmente para o TRUE.

Nova função para chamar o escopo do objeto com o Closure

Um Closure é uma classe usada para representar uma função anônima. O PHP 7.0 introduziu uma nova função Closure::call() como sendo uma forma abreviada de chamar temporariamente um escopo de objeto a um closure e invocá-lo. Para demonstrar isso, crie um script chamado closure.php e copie o seguinte código:

<?php
class Hello {
private function getMsg() {
                  echo "Hello";
          	}
}
 $getMsg = function() {return $this->getMsg();};
echo $getMsg->call(new Hello);
?>

No script acima, o Hello é uma classe com a função getMsg(). A função Closure::call() é usada para criar uma instância Hello e vincular seu escopo a um closure que invoca o método getMsg. Execute o script e a mensagem Hello aparecerá na tela.

Expectations do PHP

Antes de discutir expectations, vamos revisar as afirmações tradicionais. O método tradicional assert() é definido como segue. Ele irá verificar se um código expectation (que também é chamado de asserção) é FALSE, e se o resultado for FALSE imprime a mensagem de descrição e aborta o programa por padrão. Se uma asserção não for FALSE, o assert() não terá efeito.

bool assert ( mixed $assertion [, string $description ] )

Asserções são projetadas para serem usadas para depuração durante o desenvolvimento e teste, e não para operações em tempo de execução. O comportamento do assert() é configurado com assert_options() ou com as configurações do arquivo .ini.

Tradicionalmente, o primeiro parâmetro do assert() deve ser uma string a ser avaliada ou uma condição booleana a ser testada como uma asserção. Se uma string for fornecida, será avaliada como código PHP. Se uma condição booleana for fornecida, será convertida em uma string antes de ser passada para a função de retorno de chamada de asserção, se houver, definida em assert_options().

As asserções foram completamente reformuladas no PHP 7 e agora são chamadas de expectations e assert() é uma construção de linguagem no PHP 7. As expectations foram adicionadas como um aprimoramento de assert(), com a seguinte sintaxe:

bool assert ( mixed $assertion [, Throwable $exception ] )

Com as expectations, o primeiro parâmetro assert() pode ser uma expressão que retorna um valor, ao invés de uma string de código PHP a ser avaliada ou um boolean a ser testado. A expressão é avaliada e o resultado usado para verificar se a afirmação foi bem-sucedida. O uso de uma string como primeiro argumento está obsoleto no PHP 7. O assert_options() ainda são suportados com expectations, mas não são recomendados. Ao invés disso, duas novas diretivas de configuração php.ini devem ser usadas, conforme detalhado na tabela 2.

Tabela 2. Configuração e diretivas para as expectations

Configuração e diretiva

Tipo

Descrição

Valores suportados

Valor padrão

zend.assertions

integer

Configura se o código de asserção deve ser gerado e executado.

1: Gerar e executar código de asserção (modo de desenvolvimento)

0: Gera código de asserção, mas não o executa em tempo de execução

-1: Não gere código de asserção (modo de produção). Use esta configuração para usar as expectations.

1

assert.exception

 

Lança uma exceção AssertionError ou Custom para declaração com falha

1: lançar quando a asserção falhar, lançando o objeto fornecido como exceção ou lançando um novo objeto AssertionError se nenhuma exceção foi fornecida. Use esta configuração para usar as expectations.

0: use ou gere um Throwable conforme descrito acima, mas apenas gere um aviso em vez de lançar uma exceção ou AssertionError

0

Com as expectations, o segundo argumento pode ser um objeto Throwable ao invés de uma descrição de string. Para que o objeto ou exceção Throwable seja ativado quando uma asserção falhar, a diretiva assert.exception deve ser definida como 1.

As expectations têm as seguintes vantagens sobre as afirmações tradicionais, que ainda são compatíveis para compatibilidade com versões anteriores:

  • Uma expressão pode ser usada para avaliar uma asserção, ao invés de uma string ou um boolean;
  • Usando as configurações do php.ini, o código de asserção pode ser ignorado em tempo de execução, mesmo que seja gerado. Ou o código de asserção pode nem mesmo ser gerado, o que é recomendado para uso em produção.
  • Exceções personalizadas podem ser lançadas. Uma nova classe, AssertionError, foi adicionada no PHP 7.

Como um exemplo de como usar as expectations, crie um script chamado expectation.php e copie o código abaixo. O script define ambas as diretivas de configuração como 1. A construção de linguagem assert tem o primeiro argumento definido como true e o segundo argumento definido como AssertionError personalizado.

<?php
ini_set('assert.exception', 1);
ini_set('zend.assertions', 1);
assert(true, new AssertionError('Assertion failed.'));
?>

Se você executar o script, nenhum AssertionError será executado.

Em seguida, defina o primeiro arg como false.

assert(false, new AssertionError('Assertion failed.'));

Se você executar o script novamente, a expectation falha e lança um AssertonError.

Uncaught AssertionError: Assertion failed

Como um exemplo de uso de expectations com uma expressão e AssertionError customizado em assert(), comece com um uso tradicional do assert() que testa uma asserção de que uma variável tem um valor numérico. As strings são usadas para testar a variável e enviar uma mensagem se a asserção falhar.

$numValue = '123string';
assert('is_numeric_Value($numValue)' , "Assertion that $numValue is a number failed." );

Em seguida, use uma expressão como o primeiro argumento para assert(). E use um objeto AssertionError para lançar uma mensagem de erro personalizada.

$num = 10;
assert($num > 50 , new AssertionError("Assertion that $num is greater than 50 failed.") );

Expressões de retorno para geradores

Funções geradoras são funções que retornam um objeto iterável, como o foreach. As funções geradoras são iteradores simples, pois não requerem a classe para implementar a interface do Iterator. A sintaxe para funções geradoras é a mesma de uma função normal, exceto que incluem uma ou mais declarações de yield, com cada um produzindo um valor quando o objeto iterador retornado pela função geradora é iterado. Uma função geradora deve ser chamada como uma função normal, fornecendo todos os argumentos necessários.

O PHP 7.0 adicionou um novo recurso à função geradora, permitindo especificar uma instrução de retorno após todas as instruções de yield. O valor retornado por uma função geradora pode ser acessado com a função getReturn() do objeto retornado pela função geradora. O objeto iterador retornado por uma função geradora não deve ser confundido com o valor retornado pela função. O objeto iterador retornado não inclui o valor de retorno, apenas inclui os valores produzidos. Ser capaz de retornar um valor é útil se uma função geradora precisar executar alguns cálculos e retornar um valor final. Os geradores só podem declarar um tipo de retorno: Generator, Iterator, Traversable, ou Iterable.

Para demonstrar como usar esse novo recurso, crie um script chamado gen_return.php definindo uma função de gerador com várias declarações de rendimento e invoque a função geradora passando 1 como um argumento. Use o foreach para iterar o valor de retorno e mostrar os valores produzidos. Finalmente, produza o valor de retorno usando getReturn(). O script gen_return.php está descrito abaixo.

<?php
$gen_return = (function($var) {
	$x=$var+2;
	yield $var;
	yield $x;
	$y=$x+2;
	return $y;
})(1);
foreach ($gen_return as $value) {
	echo $value, PHP_EOL;
}
echo $gen_return->getReturn(), PHP_EOL;
?>

Se executar o script, deverá receber a seguinte saída:

1 3 5

Delegação de Geradores

O PHP 7.0 adicionou suporte para delegação de geradores, o que implica que um gerador pode delegar para outro gerador, objeto Traversable ou para um array usando o yeld da palavra-chave. Para demonstrar a delegação de geradores, crie um script chamado gen_yield_from.php e defina duas funções geradoras gen($var) e gen2($var), onde gen($var) delega para gen2($var) com a seguinte instrução:

yield from gen2($var);

Depois, itere sobre o objeto iterador retornado por gen($var) em um único loop foreach. O script gen_yield_from.php está descrito abaixo:

<?php
 function gen($var)
{
	yield $var;
	$x=$var+2;
	yield $x;
	yield from gen2($var);
}
 function gen2($var)
{
	$y=$var+1;
	yield $var;
	yield $y;
}
foreach (gen(1) as $val)
{
	echo $val, PHP_EOL;
} 
?>

Execute o script para gerar todos os valores produzidos de ambas as funções do gerador:

1 3 1 2

Nova função para divisão inteira

PHP 7.0 adicionou uma nova função intdiv() para divisões de números inteiros. A função retorna o quociente inteiro da divisão entre dois inteiros e tem a seguinte sintaxe.

int intdiv ( int $dividend , int $divisor )

Crie um script com nome de int_div.php para usar a função intdiv(). Adicione alguns exemplos de divisão de inteiros. Constantes do PHP como PHP_INT_MAX e PHP_INT_MIN podem ser usadas como argumentos para a função.

<?php
var_dump(intdiv(4, 2));
var_dump(intdiv(5, 3));
var_dump(intdiv(-4, 2));
var_dump(intdiv(-7, 3));
var_dump(intdiv(4, -2));
var_dump(intdiv(5, -3));
var_dump(intdiv(-4, -2));
var_dump(intdiv(-5, -2));
var_dump(intdiv(PHP_INT_MAX, PHP_INT_MAX));
var_dump(intdiv(PHP_INT_MIN, PHP_INT_MIN));
?>

Se executar o script, obterá o seguinte em sua tela:

nt(2) int(1) int(-2) int(-2) int(-2) int(-1) int(2) int(2) int(1) int(1)

ArithmeticErrors, se houver, são produzidos no navegador. Para demonstrar isso, inclua a seguinte chamada de função e execute o script novamente.

var_dump(intdiv(PHP_INT_MIN, -1));

Nesse caso, um ArithmeticError é gerado indicando que a divisão de PHP_INT_MIN por -1 não é um número inteiro:

Uncaught ArithmeticError: Division of PHP_INT_MIN by -1 is not an integer

Adicione a seguinte chamada de função ao script int_div.php e execute-o novamente.

var_dump(intdiv(1, 0));

Desta vez, um DivisionByZeroError é informado:

Uncaught ArithmeticError: Division of PHP_INT_MIN by -1 is not an integer

Novas Opções de Sessão

A função session_start é usada para iniciar uma nova sessão ou retomar uma sessão existente. O PHP 7.0 adicionou suporte para um novo parâmetro chamado options, que é um array associativo de opções que sobrescreve as configurações das diretivas de configuração de sessão no php.ini. Essas diretivas de configuração de sessão começam com session no php.ini, mas o prefixo session deve ser omitido na matriz de parâmetros de opções com o session_start fornecida como um argumento de função. Além das diretivas de configuração da sessão, foi adicionada uma nova opção read_and_close, que, se configurada como TRUE, fecha a sessão após ser lida, já que manter a sessão aberta pode ser desnecessário. Como exemplo, crie um script chamado session_start.php e copie o código abaixo. A matriz associativa no script de exemplo tem uma chamada de função session_start(options) que define algumas diretivas de configuração:

<?php
session_start([
	'name' => 'PHPSESSID',
    'cache_limiter' => 'private',
	'use_cookies' => '0'
]);

Quando executar este script, as opções de configuração de sessão especificadas nele substituem as diretivas de configuração de sessão definidas no php.ini, caso haja alguma. O script não irá gerar nenhuma saída.

Com o PHP 7.1, o session_start() retorna FALSE e não inicializa o $_SESSION quando falha ao iniciar a sessão.

Nova função para realizar busca e substituição de expressão regular usando callbacks

Uma nova função preg_replace_callback_array() foi adicionada no PHP 7.0 para realizar uma busca e substituição de expressão regular usando callbacks. A função é semelhante à função preg_replace_callback(), com exceção que os callbacks são invocados por padrão. A função tem a seguinte sintaxe:

mixed preg_replace_callback_array ( array $patterns_and_callbacks , mixed $subject [, int $limit = -1 [, int &$count ]] )

Ela retorna uma matriz de strings se o parâmetro $subject for uma matriz e uma única string se o $subject for uma string. A matriz ou string retornada é o novo $subject se qualquer correspondência for encontrada ou o $subject inalterado se nenhuma correspondência for encontrada. Os parâmetros da função são discutidos na Tabela 3.

Tabela 3. Parâmetros de função para preg_replace_callback_array

Parâmetro

Tipo

Descrição

$patterns_and_callbacks

array

Especifica padrões de mapeamento de matriz associativa (chaves) para retornos de chamada (valores)

$subject

mixed

Especifica a string ou array de string para pesquisar e substituir

$limit

int

Especifica um limite de substituições máximas para cada padrão em cada string do $subject. O padrão é -1, que significa nenhum limite.

/$count

int

O número de substituições concluídas é armazenado na variável $count.

Para demonstrar essa nova funcionalidade, crie um script de exemplo chamado prereg.php e copie o seguinte código nele. O subject ou a string de exemplo a ser pesquisada é definida como 'AAaaaaa Bbbbb'. O argumento patterns_and_callbacks é definido para encontrar o número de correspondências para 'A' e 'b'.

<?php
$subject = 'AAaaaaa Bbbbb';
preg_replace_callback_array(
	[
    	'~[A]+~i' => function ($match) {
        	echo strlen($match[0]), ' matches for "A" found', PHP_EOL;
    	},
    	'~[b]+~i' => function ($match) {
        	echo strlen($match[0]), ' matches for "b" found', PHP_EOL;
    	}
	],
	$subject
);
?>

Se executar o script, o número de correspondências para 'A' e 'b' será impresso:

7 matches for "A" found 5 matches for "b" found

Novas funções para gerar inteiros e bytes criptograficamente seguros

Duas novas funções foram adicionadas para gerar inteiros e bytes criptograficamente seguros. Essas funções são discutidas na Tabela 4.

Tabela 4. Novas funções criptográficas

Função

Sintaxe

Parâmetro(s)

Valor de retorno

Descrição

random_bytes()

string random_bytes(int $length)

$length, do tipo int, é o comprimento da string aleatória a ser retornada como bytes.

Retorna uma string contendo o número solicitado de bytes aleatórios criptograficamente seguros.

Gera e retorna uma string arbitrária contendo bytes criptográficos aleatórios.

random_int()

int random_int(int $min, int $max)

O parâmetro $min especifica o limite inferior para o valor a ser retornado, que deve ser PHP_INT_MIN ou superior. O parâmetro $max especifica o limite superior para o valor a ser retornado, que deve ser PHP_INT_MAX ou inferior.

Um número inteiro criptograficamente seguro entre $min e $max.

Gera e retorna números inteiros aleatórios criptográficos.

Vamos executar um exemplo, criando um script chamado random_int.php para gerar números inteiros aleatórios criptograficamente. Primeiro, gere um número inteiro no intervalo de 1 e 99 e, em seguida, gere outro número inteiro no intervalo de -100 e 0.

<?php
var_dump(random_int(1, 99));
var_dump(random_int(-100, 0));
?>

Se executar o script, dois inteiros serão mostrados.

int(98) int(-84)

Os inteiros gerados são aleatórios e, se o mesmo script for executado novamente, provavelmente serão gerados valores diferentes.

A seguir, crie um script de exemplo chamado random_bytes.php para gerar bytes criptográficos aleatórios com comprimento de 10 bytes. Em seguida, use a função bin2hex para converter bytes aleatórios em uma string ASCII contendo representação hexadecimal da string de bytes retornada. Copie o seguinte código para o script:

<?php
$bytes = random_bytes(10);
var_dump(bin2hex($bytes));
?>

Execute o script para gerar os bytes aleatórios:

string(20) "ab9ad4234e7c6ceeb70d"

Modificações da função list()

A função list() é usada para atribuir uma lista de variáveis como se fossem um array. O PHP 7.0 e 7.1 trouxeram várias mudanças na função list(). A função no PHP 7 não pode descompactar strings como as versões anteriores e retorna NULL se uma string for descompactada.

No PHP 5.x, descompactar uma string com list() atribui uma variável no list() com um valor de uma string. Vamos primeiro demonstrar como descompactar uma string com list() usando PHP 5.x. Crie um script chamado list.php e copie o seguinte código para o script.

<?php
$str = "aString";
 list($elem) = $str;
  var_dump($elem);

O script descompacta um valor de $str para um elemento de lista. Execute o script com o PHP 5.x e o valor $elem é gerado como sendo 'a'. Se executar o mesmo script com o PHP 7.0, obterá o valor NULL. Outro exemplo da função list() não sendo capaz de descompactar uma string com PHP 7 é a seguinte:

<?php
list($catalog) = "Oracle Magazine";
var_dump($catalog);
?>

Se executar o script, obterá um valor de saída NULL, como no script anterior.

Na verdade, no PHP 7.x, se um list() receber valores de uma string, a função str_split deve ser usada:

<?php
$str = "aString";
list($elem) = str_split($str);
var_dump($elem);

Se executar o exemplo anterior, o elemento da lista receberá o valor 'a', conforme o esperado. Com o PHP 7.0, uma expressão list() não pode estar completamente vazia. Veja o exemplo seguinte, executando o código no list.php.

<?php
$info = array('Oracle Magazine', 'January-February 2018', 'Oracle Publishing');
list(, , ) = $info;
echo " \n";

Desta vez, uma mensagem de erro é emitida indicando que "Não é possível usar a lista vazia...". Uma lista pode ter alguns dos elementos vazios. Na lista a seguir, um dos elementos list() está vazio:

<?php
$info = array('Oracle Magazine', 'January-February 2018', 'Oracle Publishing');
list($name, $edition,) = $info;
echo "$name  latest edition is $edition.\n";
?>

Se executar o script, nenhum erro será gerado e uma lista com 2 elementos não nulos e um elemento vazio será criada.

Oracle Magazine latest edition is January-February 2018.

Outra modificação no list() é que agora ele atribui valores às variáveis na ordem em que são definidas. Anteriormente, os valores eram atribuídos às variáveis na ordem inversa em que foram definidas. Para demonstrar o comportamento anterior, execute o seguinte código no script list.php com PHP 5.x:

<?php
list($a[], $a[], $a[]) = ['A', 2, 3];
var_dump($a);
?>

Como pode ver, inspecionando o resultado do script (mostrado na Figura 1), os valores são atribuídos às variáveis na ordem inversa daquela em que foram definidas.

Figura 1. Os valores são atribuídos na ordem inversa

Execute o mesmo script novamente com PHP 7.0 e verá o list() atribuir valores a variáveis na mesma ordem em que foram definidas:

array(3) { [0]=> string(1) "A" [1]=> int(2) [2]=> int(3) }

O PHP 7.1.0 habilita a especificação de chaves em lista para indicar a ordem numérica de atribuição. Como exemplo, crie um script chamado list.php com o seguinte código. Observe que todas as chaves são numéricas neste exemplo:

<?php
list(0 => $journal, 1=> $publisher, 2 => $edition) = ['Oracle Magazine', 'Oracle Publishing', 'January February 2018'];
echo "$journal, $publisher, $edition. \n";
?>

Se executar o script, obterá a seguinte lista de valores:

Oracle Magazine, Oracle Publishing, January February 2018.

A ordem numérica de atribuição pode ser embaralhada conforme mostrado na lista a seguir, onde a chave do índice 0 é listada após o índice 1.

<?php
list(1 => $journal, 0=> $publisher, 2=>$edition) = ['Oracle Magazine', 'Oracle
Publishing', 'January February 2018'];
echo "$journal, $publisher,$edition \n";
?>

Se executar o script, o resultado indica que as variáveis são atribuídas a um valor com base na chave numérica e não na ordem em que as chaves são listadas.

Oracle Publishing, Oracle Magazine,January February 2018

O índice da chave pode ser colocado entre aspas simples ou duplas, conforme mostrado abaixo:

<?php
list('1' => $journal, '0'=> $publisher, '2'=>$edition) = ['Oracle Magazine', 'Oracle
Publishing', 'January February 2018'];
echo "$journal, $publisher,$edition \n";
?>

O script anterior gera a mesma saída. Se um elemento do list() for uma chave, todos os seus elementos deverão ser de uma chave. Por exemplo, crie uma atribuição de lista com alguns elementos com chaves e outros sem.

<?php
list(0 => $journal, 1=> $publisher, 2) = ['Oracle Magazine', 'Oracle Publishing', 'January February 2018'];
echo "$journal, $publisher. \n";
?>

Se executar o script, receberá uma mensagem de erro indicando que as entradas de matriz com e sem chave nas atribuições não podem ser misturadas:

Cannot mix keyed and unkeyed array entries in assignments 

Offsets para negativas

A partir do PHP 7.1, funções de manipulação de string, como strpos e substr, suportam deslocamentos negativos, que são tratados como deslocamentos do final da string. A indexação de string com [] e {} também oferece suporte a deslocamentos negativos. Por exemplo, "ABC" [-2] retornaria a letra 'B'. Agora, crie um script chamado str-negative-offset.php e copie o código abaixo no script:

<pre>
<?php
echo "ABCDEF"[-1];
echo "<br/>";
echo strpos("aabbcc", "a", -6);
echo "<br/>";
echo strpos("abcdef", "c", -1);
echo "<br/>";
echo strpos("abcdef", "c", -5);
echo "<br/>";
echo substr("ABCDEF", -1); 
echo "<br/>"; 
echo substr("ABCDEF", -2, 5); 	
echo "<br/>";
echo substr("ABCDEF", -7);
echo "<br/>";
echo substr("ABCDEF", -6);
echo "<br/>";
echo substr("ABCDEF", -5);
echo "<br/>";
echo substr("ABCDEF", 6);
echo "<br/>";
echo substr("abcdef", 1, -3); 
echo "<br/>";
echo substr("abcdef", 3, -2); 
echo "<br/>"; 
echo substr("abcdef", 4, -1);
echo "<br/>";  
echo substr("abcdef", -5, -2); 
?>

O script fornece vários exemplos de uso de deslocamentos negativos com diferentes funções de manipulação de string e []. Se executar o script, será gerado o seguinte:

F
0
 
2
F
EF
ABCDEF
ABCDEF
BCDEF
 
bc
d
e
bcd

Nova função para converter Callables em Closures

Os Callables são úteis para passar funções (definidas pelo usuário e internas, exceto os construções da linguagem) e métodos como variáveis string. Por exemplo, a função hello() pode ser passada para outra função como um argumento ou retornada de uma função usando o nome da função como string, ou seja, 'hello', se o tipo de parâmetro ou o tipo de retorno puder ser chamado. Crie um script chamado callable.php e declare a função hello() que gera uma mensagem 'hello'. Declare outra função com tipo de parâmetro como sendo callable.

function callFunc(callable $callback) {
	$callback();
}

A função callFunc(callable) pode invocar hello() usando o nome como uma string:

callFunc("hello");

Alternativamente, a função com o mixed integrado call_user_func(callable $callback [, mixed $ ...]), que tem um callable como primeiro parâmetro, pode ser usado para invocar a função hello() pelo nome:

call_user_func('hello');

O script callable.php pode ser analisado abaixo:

<?php
function hello() {
	echo 'hello';
} 
call_user_func('hello');
function callFunc(callable $callback) {
	$callback();
}
echo '<br/>';
callFunc("hello");

Se executar o script, a função hello() é invocada pelo nome fornecido como uma string.

hello
hello

Um closure é uma representação do objeto de uma função anônima. Por que converter um callable em um closure? Por vários motivos, um deles é o desempenho. O tipo callable é relativamente lento devido ao custo de descobrir se uma função pode ser callable ou não.

Outra desvantagem de usar um callable é que apenas funções públicas podem ser usadas como callables. Ao invés disso, converter para um closure dentro de uma classe não requer que a função seja pública, ela pode ser uma função privada, por exemplo. Crie um script chamado hello.php e declare uma classe Hello com um método getCallback() que retorna uma função que retorna um callback:

public function getCallback() {
    	return [$this, 'hello_callback_function'];
	}

A função de retorno do callback é declarada pública.

public function hello_callback_function($name) { var_dump($name);  }

Crie uma instância da classe para obter e invocar a função de retorno do callback. O script hello.php está mostrado abaixo:

<?php
class Hello {
	public function getCallback() {
    	return [$this, 'hello_callback_function'];
	}
	public function hello_callback_function($name) { var_dump($name);  }
}
$hello = new Hello();
$callback = $hello-> getCallback();
$callback('Deepak');

Se executar o script, teremos o seguinte retorno:

string(6) "Deepak"

Em seguida, use o método estático Closure::fromCallable para converter a função de retorno de chamada privada em um closure.

<?php
class Hello {
	public function getClosure() {
    	return Closure::fromCallable([$this, 'hello_callback_function']);
	}
	private function hello_callback_function($name) { var_dump($name);  }
}
$hello = new Hello();
$closure = $hello-> getClosure();
$closure('Deepak');

Se executar o script, obterá o mesmo resultado:

string(6) "Deepak"

Outro motivo para a conversão para um encerramento é a detecção de erros em uma fase inicial, e não no tempo de execução. Considere o mesmo exemplo de antes, mas desta vez, com o nome da função do callback incorreta:

public function getCallback() {
    	return [$this, 'hello_callback_functio'];
	}

Se executar o script, uma chamada de erro ao método indefinido Hello::hello_callback_functio() será lançada quando o retorno da chamada for realmente usado na seguinte linha:

$callback('Deepak');

Ao invés disso, se convertermos o callable em um closure, o erro Failed to create closure from callable: class 'Hello' does not have a method 'hello_callback_function' é detectado na seguinte linha:

return Closure::fromCallable([$this, 'hello_callback_functio']);

Flag JSON_THROW_ON_ERROR

O tratamento de erros para funções JSON json_encode() e json_decode() é mínimo nas versões do PHP pré-7.3, contendo assim as seguintes deficiências:

  • O json_decode() retorna nulo em caso de erro, mas este mesmo resultado pode ser o valor de resultado válido, por exemplo, se JSON "nulo" for decodificado. O único método para descobrir se ocorreu um erro é a partir do estado de erro global com json_last_error() ou json_last_error_msg(). O json_encode() tem um valor de retorno de erro;
  • A execução do programa não é interrompido em caso de erro e nem mesmo um aviso é informado.

O PHP 7.3 adicionou suporte de flag JSON_THROW_ON_ERROR no json_encode() e no json_decode(). Uma nova subclasse de exceção chamada JsonException também foi adicionada para descrever uma exceção resultante para codificação/decodificação do JSON. Se o sinalizador JSON_THROW_ON_ERROR for fornecido para o json_encode() e para o json_decode() e uma JsonException for lançada, o estado de erro global não será modificado. Para demonstrar o novo JSON_THROW_ON_ERROR e a nova JsonException, crie um script chamado json.php e tente usar o json_decode para decodificar uma matriz contendo um erro ao usar JSON_THROW_ON_ERROR:

<?php
try {
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
    json_decode("{",false,1,JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
	echo $exception->getMessage(); // Imprime "Syntax error"
}
?>

Se executar o script, obterá a seguinte JsonException:

Maximum stack depth exceeded

Como um exemplo de uso de JSON_THROW_ON_ERROR com json_encode(), codifique uma matriz com NAN como um valor de elemento como no seguinte script:

<?php
try {
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => NAN);
echo json_encode($arr,JSON_THROW_ON_ERROR);
}
catch (JsonException $exception) {
	echo $exception->getMessage(); 
}
?>

Quando o script é executado, a seguinte mensagem será gerada:

Inf and NaN cannot be JSON encoded

Novas funções para obter o primeiro/último valor-chave de uma matriz

Obter a primeira e última chave de um array é uma operação comum e o PHP 7.3 adicionou duas novas funções especializadas:

$key = array_key_first($array);
$key = array_key_last($array);

Um exemplo de como usá-los com uma matriz associativa e até mesmo uma matriz vazia é fornecido no seguinte script:

<?php
// Uso de uma matriz associativa
$array = ['a' => 'A', 2 => 'B', 'c' => 'C'];
$firstKey =array_key_first($array);
$lastKey = array_key_last($array);
echo assert($firstKey === 'a');
echo "<br/>";
 echo $firstKey;
echo "<br/>";
 echo $lastKey;
echo "<br/>";
// Uso de uma matriz vazia
$array = [];
$firstKey = array_key_first($array);
$lastKey = array_key_last($array);
 echo "<br/>";
echo assert($firstKey === null);
echo "<br/>";
echo assert($lastKey === null);
?>

O resultado do script é o seguinte:

1
a
c
 
1
1

Função compact que relata variáveis indefinidas

A função compact() possui um novo recurso no PHP 7.3 para relatar variáveis indefinidas. Para demonstrar isso, execute o seguinte script que inclui algumas dessas variáveis:

<?php
$array1=['a','b','c',];
$var1="var 1";
var_dump(compact($array1,$array2,'var1','var2'));
?>

A seguinte mensagem será mostrada na tela:

Notice: Undefined variable: array2  on line 9
Notice: compact(): Undefined variable: a  on line 9
Notice: compact(): Undefined variable: b  on line 9
Notice: compact(): Undefined variable: c   on line 9
Notice: compact(): Undefined variable: var2  on line 9

Vírgulas finais em chamadas de função

PHP 7.3 adicionou suporte para "vírgulas finais" em chamadas de função. As vírgulas finais são úteis em vários contextos nos quais os argumentos são acrescentados com frequência, como em funções variáveis (array_merge, compact, sprintf). As construções de linguagem unset() e isset() também permitem vírgulas no final. Um exemplo do uso de uma vírgula final em uma chamada de função não definida é fornecido abaixo.

<?php
function funcA($A,$B,$C)
{
    unset($A,$B,$C,);
	echo $A;
	echo $B;
   echo $C;
 	
}
$A = 'variable A';
$B = 'variable B';
$C = 'variable C';
funcA($A,$B,$C,);
?>

Quando o script é executado, o resultado é o seguinte:

Notice: Undefined variable: A 
Notice: Undefined variable: B 
Notice: Undefined variable: C 

A função array_merge() é outro exemplo em que uma vírgula final pode facilitar o acréscimo de valores. O script a seguir usa uma vírgula final em uma chamada de função para o array_merge():

<?php
$array1=[1,2,3];
$array2=['A','B','C'];
$array = array_merge(
	$array1,
	$array2,
	['4', '5'],
);
?>

Métodos de call e closures também permitem vírgulas finais. Um método é uma função dentro de uma classe. Um closure é um objeto que representa uma função anônima. Vírgulas à direita só são permitidas em função call e não em/como declarações de função. Vírgulas autônomas, vírgulas iniciais e várias vírgulas finais também são proibidas na linguagem.

Função matemática bcscale.php

A função bcscale com sintaxe int bcscale([int $scale]) é usada para definir o fator de escala padrão para todas as chamadas de função matemática bc subsequentes. As funções matemáticas BC, como bcadd(), bcdiv() e bcsqrt(), são usadas com números de precisão arbitrária. O PHP 7.3 adicionou suporte para obter o fator de escala atual com bcscale. Chamar o bcscale retorna o fator de escala antigo. Anteriormente, o scale era obrigatório e o bcscale() sempre retornava TRUE. Como exemplo disso, o script a seguir define o fator de escala padrão para 3 e, subsequentemente, gera o fator de escala atual:

<?php
bcscale(3);
echo bcscale();
?>

A saída do script anterior é 3.

Nova função is_countable

O PHP 7.3 adicionou suporte para uma nova função is_countable que retorna true se o argumento da função for um tipo de array ou uma instância de Countable.

bool is_countable(mixed $var)

Por exemplo, is_countable() pode ser usado para descobrir se um determinado argumento é um array.

echo is_countable(['A', 'B', 3]);

Uma instância do ArrayIterator acaba sendo contável e is_countable gera TRUE, uma vez que ArrayIterator implementa a interface Countable.

echo is_countable(new ArrayIterator());

O is_countable pode ser usado com if() para garantir que um argumento seja contável antes de executar qualquer código. No seguinte trecho, uma instância de uma classe A é testada quanto à capacidade de contagem:

class A{}
if (!is_countable(new A())) {
	echo "Not countable";
}

O resultado do trecho de código anterior é FALSE, pois a classe A não implementa o Countable. Se o argumento para is_countable for uma matriz, ele retornará TRUE como mostrado no seguinte trecho de código:

$array=['A', 'B', 3];
if (is_countable($array)) {
    var_dump(count($array)); 
}

Todos os fragmentos de código nesta seção foram coletados do script is_countable.php.

<?php
class A{}
echo is_countable(['A', 'B', 3]);
echo "<br/>";	
echo is_countable(new ArrayIterator());
echo "<br/>"; 
if (!is_countable(new A())) {
	echo "Not countable";
}
echo "<br/>";
$array=['A', 'B', 3];
if (is_countable($array)) {
	var_dump(count($array)); 
}
?>

Execute o script e o resultado é o seguinte.

1
1
Not countable
int(3)

Funções de seta

O PHP 7.4 apresenta funções de seta para uma sintaxe mais concisa para funções anônimas. As funções de seta têm o seguinte formato:

fn(parameter_list) => expr

As funções de seta têm a precedência mais baixa, o que implica que a expressão à direita da seta => é aplicada antes da função. Por exemplo, a função de seta fn ($x) => $x + $y é equivalente a fn ($x) => ($x + $y) e não a (fn ($x) => $x) + $y. Uma variável usada na expressão declarada no escopo é capturada implicitamente como sendo valor. Como exemplo, considere o seguinte script que declara uma função de seta:

$fn1 = fn($msg) => $msg.' '.$name;

A variável $name é capturada do escopo automaticamente e a função de seta é equivalente a:

$fn2 = function ($msg) use ($name) {
    return $msg.' '.$name;
};

A vinculação por valor para $x é equivalente a executar o use($x) para cada ocorrência $x dentro de uma função de seta. A função de seta também declara um parâmetro $msg. No próximo exemplo, o var_export chama a função de seta com um argumento fornecido e produz o valor 'Hello John':

<?php
$name = "John";
$fn1 = fn($msg) => $msg.' '.$name;
var_export($fn1("Hello"));//'Hello John'
?>

As funções de seta podem ser aninhadas, conforme demonstrado pelo script a seguir. A função de seta externa captura a variável $name por valor do escopo delimitador e a função de seta interna captura $name da função externa:

<?php
$name = "John";
$fn = fn($msg1) => fn($msg2) => $msg1.' '.$msg2.' '.$name;
var_export($fn("Hello")("Hi")); 
?>

Quando o script é executado, o resultado mostrado na Figura 2 é exibido.

Figura 2. Funções de seta podem ser aninhadas

Como as funções de seta usam vinculação de variável por valor, modificar o valor de uma variável em uma função de seta não tem efeito na variável declarada no escopo delimitador. Para demonstrar isso, a função de seta no script a seguir diminui o valor da variável $x do bloco, mas não tem efeito no valor da variável $x, que ainda é o mesmo valor 1:

<?php
$x = 1
$fn = fn() => x--; // Não tem efeito
$fn();
var_export($x);   //Retorna 1
?>

A função de seta suporta assinaturas de função arbitrárias que podem incluir parâmetros e tipos de retorno, valores padrão, variadics e passagem e retorno de variável por referência. O script a seguir demonstra o uso de diferentes formas de assinaturas de função de seta. A descrição da assinatura e a saída do script são mostradas com comentários //.

<?php
$name = "John";
$x = 1; 
//Função de seta inclui tipos de parâmetros, retorna o tipo e o valor padrão
$fn = fn(string $msg='Hi'): string => $msg.' '.$name;
//A função de seta inclui um variadic. 
$fn2 = fn(...$x) => $x;
//A função de seta inclui passagem de variável por referência
$fn3=fn(&$x) => $x++;
$fn3($x);
echo $x; // 2
var_export($fn("Hello"));//'Hello John'
var_export($fn());//'Hi John'
var_export($fn2(1,2,3)); //array ( 0 => 1, 1 => 2, 2 => 3, ) 
?>

O $this pode ser usado em uma função de seta no contexto do objeto. Se usado com uma função de seta prefixada com estático, o $this não pode ser utilizado. Para demonstrar isso, considere o seguinte script que usa o $this no contexto do objeto e no contexto da classe. Uma mensagem de erro é emitida quando não é usada no contexto do objeto.

<?php
class A {
    public function fn1() {
        $fn = fn() => var_dump($this);
        $fn(); //  object(A)#1 (0) { }
        $fn = static fn() => var_dump($this);
        $fn(); //Uncaught Error: Using $this when not in object context 
    }
}
$a=new A();
$a->fn1();

Resumo

Neste quarto e penúltimo artigo da série sobre novos recursos no PHP 7, apresentamos os novos recursos relacionados às funções do PHP.

No próximo e último artigo da série, discutiremos alguns novos recursos relacionados a matrizes, operadores, constantes e tratamento de exceções.

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