Option
Modele valores opcionais sem espalhar checagens de null. Some/None com map, flatMap, filter e unwrap seguro.
Ler Option
Option, Result, Schema e DTO para PHP 7.4+. Adote uma fronteira por vez, sem reescrita.
composer require gabrielalmir/maybe$result = Schema::shape([
'email' => Schema::string()->trimmed(),
])->safeParse($payload)
->andThen(fn (array $data) => saveCustomer($data));
$result->match(
fn (Customer $customer) => "Ok: saved",
fn (mixed $error) => "Err: fix input"
);Um e-mail de confirmação de pedido falha. Ou o checkout inteiro quebra por causa de um efeito colateral não crítico, ou a falha é engolida e ninguém descobre que o cliente nunca foi avisado.
// Silently invisible:
@mail($to, $subject, $body);
// "Handled", but the outcome is thrown away:
try {
$mailer->send($to, $subject, $body);
} catch (\Exception $e) {
error_log($e->getMessage());
}Quem chamou não tem como saber se o cliente foi avisado, nem como distinguir um endereço malformado de um relay que deu timeout.
$emailResult = $emailSchema->safeParse($message)
->andThen(static fn (array $valid): Result
=> sendWithFallback($valid));
$emailResult->match(
static fn (string $ref): string => "sent ({$ref})",
static fn (array $error): string => $error['retryable']
? "queued for retry"
: "rejected: fix the input"
);O payload de erro mantém `retryable` explícito. Um endereço malformado e um relay SMTP instável são problemas diferentes: um pede correção de dado, o outro uma fila de retry. O tipo impede que sejam tratados igual por acidente.
Os blocos foram desenhados para compor entre si, em uma única dependência que instala no PHP 7.4.
SchemaResult
safeParse() nunca lança. Devolve um Result carregando um ValidationErrorBag.
DTOSchema
Um schema, um objeto tipado e imutável, construído a partir de input bruto.
OptionResult
okOr() transforma um valor ausente em um erro nomeado na fronteira.
O nicho
Não é reescrita, não é framework. Uma dependência pequena que instala no PHP que você já tem.
Sem enums, sem promotion, sem readonly. Testado na CI contra 7.4, 8.2, 8.3, 8.4 e 8.5.
Async usa proc_open, não pcntl. Um job de CI dedicado prova isso no Windows.
Helpers globais e uma library carregável. Adote um controller por vez.
Concorrência por processos, deliberadamente não um event loop. Precisa de um? Use amphp ou reactphp.
Em produção
Um e-mail de confirmação falha. Ou o checkout inteiro quebra por um efeito colateral não crítico, ou a falha é silenciosamente engolida.
O SAP falha por razões estruturadas: documento duplicado, centro de custo ausente, sessão expirada, timeout. Código legado colapsa tudo na mesma não-resposta.
Validação espalhada por um controller deixa um contrato ser salvo pela metade em estado inválido, com erros desestruturados demais para uma tela de revisão apontar o campo culpado.
Um llms.txt publicado com nomes e assinaturas exatos, incluindo os getters que deliberadamente não existem, para o assistente parar de inventá-los.
Combinado com o AGENTS.md do repositório, para quem contribui usando um assistente.
Honestidade primeiro. Procure outra coisa quando:
Sem acoplamento a framework e sem reescrita. Valide o input, modele uma operação falível e decida o resultado na borda.
composer require gabrielalmir/maybeNovato por aqui? Leia Por que Maybe? para entender os trade-offs, siga o Tutorial para construir um fluxo validado de ponta a ponta, ou mantenha a Referência de API aberta enquanto trabalha.