Semântica dos branches
Um branch é um desfecho de uma decisão: a entrada de uma função, o corpo de if, elseif, else,
de um laço, de case, catch e finally, o operando direito de &&, ||, and, or e ??,
os braços de ?: e match, e o valor não nulo de ?->.
Modo estrito
O modo estrito é o padrão; --no-strict desliga a primeira e a quarta regras abaixo (o else
sintetizado e o lado nulo de ?->).
-
Corpo vazio é desfecho. Um corpo vazio de
if,elseif,elsee de laços é um desfecho como qualquer outro: ganha uma sonda sem linha, e conta como branch.if ($order->isPaid()) {// nothing to do: this outcome still has to be reached by a test} -
Arrow functions viram closures.
fn&(...) => exprvira a closure que abrevia,function &(...) use (...) { probe; return expr; }, com as variáveis da expressão capturadas por valor como a arrow function faz;&get => exprvira&get { probe; return expr; }. A referência devolvida fica intacta. -
Todo
ifprecisa doelse. Todoifsemelseganha umelsesintetizado: os dois desfechos precisam ser testados. O Xdebug conta blocos básicos, então umifsemelsecuja condição é sempre verdadeira dá 100% lá e não no Sonda.public function total(): int{if ($this->discount !== null) { // a test with a discount, and one withoutreturn $this->subtotal - $this->discount;}return $this->subtotal;} -
?->conta os dois lados. O desfecho nulo e o não nulo de toda chamada nullsafe.$name = $order->customer?->name(); // a test with a customer, and one with null -
O curto-circuito de
&&e||. O operando esquerdo de&&,||,andeortem de decidir sozinho ao menos uma vez (o curto-circuito, quando o direito nem roda), como o Xdebug conta.if ($user->isAdmin() || $user->owns($order)) { // one test where isAdmin() is true, so owns() never runsNão no
??: o lado esquerdo dele pode ser uma chave indefinida, que só esse operador lê sem aviso, então só o operando direito do??é um branch.$limit = $options['limit'] ?? 10; // only the fallback (right side) needs a test
O que o Sonda deixa de fora de propósito
-
O braço implícito do
match. O Sonda não conta o branch implícito de "nenhum braço casou" domatch, que o Xdebug conta e nenhum teste alcança quando os braços cobrem todos os casos de um enum.return match ($status) {Status::Open => 'open',Status::Closed => 'closed',}; // the arms are the branches; the UnhandledMatchError path is not one -
O desvio de fallback de uma função frameless. O Sonda não conta o desvio de fallback que o Xdebug vê numa chamada a uma função nativa frameless (
in_array,max,preg_replace,str_contains...) feita dentro de um namespace semuse function: é um detalhe do opcode, não uma decisão do código, e umuse functionsó para calar a cobertura deixa de ser necessário.namespace App;$found = in_array($id, $ids, true); // no "branch" here for Sonda; Xdebug sees the fallback to the global function -
First-class callables creditam o método. Um método chamado como first-class callable (
$this->method(...)) aparece no Xdebug como uma função solta com o nome do método, com as execuções, enquanto o método da classe fica em zero; o Sonda credita o método. Otools/compare.phpfunde as duas entradas antes de comparar.
A fidelidade dessas regras contra o Xdebug, medida numa suíte inteira, está em Comparação com o Xdebug.