Desafio PHP
Calculadora com tipagem estrita e union types
Enunciado
Crie uma calculadora que aceita dois números (int ou float) e uma operação (string). A função calcular deve ter parâmetros tipados como int|float e string, e retorno int|float. Ela deve suportar as operações soma, subtracao, multiplicacao e divisao. Se a operação for desconhecida, lance InvalidArgumentException. Se houver divisão por zero, lance DivisionByZeroError. Use declare(strict_types=1);. Teste com as quatro operações e imprima cada resultado. Trate as exceções com try/catch para exibir mensagens amigáveis.
Requisitos
- A função
calculardeve ter parâmetrosint|float $a,int|float $b,string $operacaoe retornoint|float. - Deve lançar
InvalidArgumentExceptionpara operação inválida eDivisionByZeroErrorpara divisão por zero. - O programa deve capturar as exceções e imprimir mensagens claras.
- A saída deve mostrar os resultados das quatro operações e pelo menos um erro.
Código inicial
<?php
declare(strict_types=1);
function calcular(int|float $a, int|float $b, string $operacao): int|float {
// Implemente
}
// Testes
Saída esperada
Soma: 10
Subtração: 4
Multiplicação: 21
Divisão: 2.5
Erro: Operação desconhecida: potencia
Erro: Divisão por zero não permitida
Ver dica
Use match ou switch para escolher a operação. Para divisão, verifique se $b == 0 antes de dividir e lance DivisionByZeroError. Lembre-se de que o retorno pode ser int ou float, então não force conversão.
Mostrar solução
<?php
declare(strict_types=1);
function calcular(int|float $a, int|float $b, string $operacao): int|float {
return match ($operacao) {
'soma' => $a + $b,
'subtracao' => $a - $b,
'multiplicacao' => $a * $b,
'divisao' => $b == 0
? throw new DivisionByZeroError("Divisão por zero não permitida")
: $a / $b,
default => throw new InvalidArgumentException("Operação desconhecida: $operacao"),
};
}
// Testes
try {
echo "Soma: ", calcular(7, 3, 'soma'), PHP_EOL;
echo "Subtração: ", calcular(7, 3, 'subtracao'), PHP_EOL;
echo "Multiplicação: ", calcular(7, 3, 'multiplicacao'), PHP_EOL;
echo "Divisão: ", calcular(5, 2, 'divisao'), PHP_EOL;
} catch (DivisionByZeroError | InvalidArgumentException $e) {
echo "Erro: ", $e->getMessage(), PHP_EOL;
}
try {
echo calcular(10, 2, 'potencia'), PHP_EOL;
} catch (InvalidArgumentException $e) {
echo "Erro: ", $e->getMessage(), PHP_EOL;
}
try {
echo calcular(10, 0, 'divisao'), PHP_EOL;
} catch (DivisionByZeroError $e) {
echo "Erro: ", $e->getMessage(), PHP_EOL;
}
Passo a passo
declare(strict_types=1);garante que os tipos sejam verificados sem conversão.- A função
calcularaceita dois parâmetrosint|floate umstring, e retornaint|float. - Usamos
matchpara mapear a operação para a expressão correspondente. - Na divisão, verificamos se
$b == 0; se for, lançamosDivisionByZeroErrorcom uma mensagem. - Para operação desconhecida, o
defaultlançaInvalidArgumentException. - O primeiro bloco
tryexecuta as quatro operações válidas e imprime os resultados. - O segundo
trytesta uma operação inválida e captura a exceção. - O terceiro
trytesta divisão por zero e captura a exceção.
Por que funciona
A tipagem estrita garante que apenas int ou float sejam passados para $a e $b, e que a operação seja string. O union type no retorno permite que a função devolva tanto inteiros (ex.: soma de inteiros) quanto floats (ex.: divisão). O uso de exceções específicas (InvalidArgumentException e DivisionByZeroError) torna o tratamento de erros claro e idiomático. O match é uma expressão que retorna um valor ou lança exceção, mantendo o código conciso.
Erros comuns
- Não verificar divisão por zero e deixar o PHP emitir um warning:
$a / 0geraDivisionByZeroErrornativamente em PHP 8, mas a mensagem pode não ser amigável. Melhor lançar explicitamente. - Usar
==em vez de===para comparar a operação:'soma' == 0pode ser verdadeiro em versões antigas, mas commatcha comparação é estrita. - Esquecer de declarar o retorno como
int|floate usar apenasfloat: isso forçaria a conversão de inteiros para float, alterando a saída esperada. - Capturar
Exceptiongenérica em vez das exceções específicas: funciona, mas perde a semântica e pode mascarar outros erros.
Outra forma de resolver
Usar switch em vez de match:
switch ($operacao) {
case 'soma': return $a + $b;
case 'subtracao': return $a - $b;
case 'multiplicacao': return $a * $b;
case 'divisao':
if ($b == 0) throw new DivisionByZeroError("Divisão por zero não permitida");
return $a / $b;
default:
throw new InvalidArgumentException("Operação desconhecida: $operacao");
}
O switch é preferível quando se precisa de lógica mais complexa em cada caso, mas o match é mais moderno e expressivo.
Saída esperada
Soma: 10
Subtração: 4
Multiplicação: 21
Divisão: 2.5
Erro: Operação desconhecida: potencia
Erro: Divisão por zero não permitida