Looking to hire Laravel developers? Try LaraJobs

laravel-helpers maintained by gsebastiao

Description
Auto-discover de funções helper para Laravel: cria app/Helpers, carrega tudo automaticamente (com subpastas) e cacheia para produção.
Author
Gerson Sebastiao Cossa
Last update
2026/07/11 06:55 (dev-main)
License
Links
Downloads
1

Comments
comments powered by Disqus

Laravel Helpers

Pacote Composer para auto-discover de funções helper em projetos Laravel. Cria uma pasta app/Helpers, carrega automaticamente qualquer ficheiro .php dentro dela (incluindo subpastas), e oferece cache opcional para produção.

Pensado para não exigir nenhum passo manual complicado — funciona logo após composer require, mesmo antes de correr qualquer comando.


Índice


Instalação

composer require gsebastiao/laravel-helpers

O Laravel deteta o pacote automaticamente via package discovery — não precisas de registar nada em config/app.php.

Depois, cria a pasta de helpers:

php artisan helpers:install

Isto cria app/Helpers/ com um ficheiro de exemplo (ExampleHelper.php), só para confirmares que está tudo a funcionar. Se a pasta já existir, o comando não altera nada.

Nota: mesmo sem correres helpers:install, se criares a pasta app/Helpers/ manualmente, o pacote já a deteta sozinho. O comando existe só por conveniência.


Uso básico

Cria qualquer ficheiro .php dentro de app/Helpers/:

<?php
// app/Helpers/StringHelper.php

if (! function_exists('slug_amigavel')) {
    function slug_amigavel(string $texto): string
    {
        return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $texto), '-'));
    }
}

E usa em qualquer lugar da tua aplicação, sem nenhum use ou import:

echo slug_amigavel('Olá Mundo!'); // ola-mundo

Não precisas de correr composer dump-autoload, nem editar composer.json, nem registar nada. Basta criar o ficheiro e recarregar a página (ou correr o teu comando/teste).

Boa prática: sempre envolve a tua função em if (! function_exists(...)). Isto evita erros fatais de "Cannot redeclare function" caso dois ficheiros definam a mesma função por engano, e é o padrão usado pelo próprio Laravel nos seus ficheiros de helpers.


Subpastas

Podes organizar os teus helpers como quiseres — tudo dentro de app/Helpers/ é varrido recursivamente:

app/Helpers/
├── ExampleHelper.php
├── Strings/
│   ├── Slug.php
│   └── Mascara.php
├── Arrays/
│   └── Agrupamento.php
└── Money/
    └── Formatacao.php

Todas as funções declaradas em qualquer nível ficam disponíveis globalmente, do mesmo jeito.


Cache em produção

Em cada request, o pacote varre a pasta app/Helpers à procura de ficheiros .php. Para uma dúzia de ficheiros isto é irrelevante, mas se preferires eliminar essa varredura em produção, gera um cache compilado:

php artisan helpers:cache

Isto cria bootstrap/cache/helpers.php com a lista de ficheiros já descoberta. A partir daí, o pacote passa a ler diretamente desse cache, sem tocar no sistema de ficheiros para descobrir nada.

Se adicionares, remover ou mover ficheiros depois de cachear, precisas de correr helpers:cache novamente para atualizar a lista — ou simplesmente:

php artisan helpers:clear

Isto remove o cache e volta ao modo de descoberta automática em tempo real. Isto é importante: nunca ficas "preso". Se esqueceres de cachear de novo depois de adicionar um ficheiro, o pior cenário é a tua nova função não aparecer até correres helpers:cache — a aplicação não quebra, porque o pacote sempre verifica se cada ficheiro do cache ainda existe antes de o carregar.

Sugestão: adiciona php artisan helpers:cache ao teu pipeline de deploy, junto com config:cache e route:cache. Segue exatamente a mesma convenção.


Configuração

Por padrão não precisas de configurar nada. Mas se quiseres mudar o nome da pasta (por exemplo, se já tens uma pasta app/Helpers a fazer outra coisa), publica o ficheiro de configuração:

php artisan vendor:publish --tag=helpers-config

Isto cria config/helpers.php:

<?php

return [
    'path' => 'Helpers',              // relativo a app_path()
    'cache_path' => 'cache/helpers.php', // relativo a bootstrap_path()
];

Podes, por exemplo, mudar 'path' => 'Helpers' para 'path' => 'Support/Functions', e o pacote passa a varrer app/Support/Functions em vez de app/Helpers.


Comandos disponíveis

Comando O que faz
php artisan helpers:install Cria app/Helpers/ com um ficheiro de exemplo. Não faz nada se a pasta já existir.
php artisan helpers:cache Compila a lista de helpers descobertos num ficheiro de cache, para produção.
php artisan helpers:clear Remove o cache e volta à descoberta automática em tempo real.

Como funciona por dentro

Para quem quiser entender (ou contribuir), aqui está o raciocínio das principais decisões de arquitetura:

1. O carregamento acontece em register(), não em boot()

O Laravel corre o método register() de todos os Service Providers primeiro — sequencialmente, um a um — e só depois de todos terminarem é que começa a correr boot() de qualquer um deles. São duas passagens completas e separadas, não intercaladas.

Se este pacote carregasse os helpers em boot(), qualquer código dentro do register() de outro provider da tua aplicação (por exemplo, o AppServiceProvider que o próprio Laravel já traz por padrão) que dependesse de uma das tuas funções helper causaria um erro fatal Call to undefined function, independentemente da posição deste pacote na lista de providers — porque a fase de register() inteira termina antes de qualquer boot() começar.

A convenção geral de "só resolva coisas do container dentro do boot()" existe para evitar depender de bindings que outros providers ainda não registaram. Mas carregar um ficheiro .php com require_once é I/O puro — não resolve nada do container — então essa convenção não se aplica aqui. Por isso é seguro (e necessário) fazer isto em register().

Limite conhecido: isto não cobre helpers usados dentro de config/*.php ou bootstrap/app.php, porque esses ficheiros são lidos antes de qualquer Service Provider ser sequer instanciado. Ver Limitações conhecidas.

2. Por que não usar glob() com GLOB_BRACE para recursividade

GLOB_BRACE não está garantida em todos os sistemas operativos (não faz parte do padrão POSIX, e falta nalgumas builds fora do universo GNU/Linux). Para um pacote que se propõe a "simplesmente funcionar" em qualquer ambiente, preferimos RecursiveDirectoryIterator + RecursiveIteratorIterator, que são parte do SPL do PHP e funcionam de forma consistente em qualquer sistema operativo suportado pelo PHP.

3. Por que o cache guarda uma lista de caminhos, e não o conteúdo dos ficheiros

O custo real da descoberta automática está na varredura de diretórios (percorrer o sistema de ficheiros à procura de .php), não nos require_once em si. Para um punhado de ficheiros pequenos, os require_once são triviais. Por isso o cache serve só para eliminar a varredura — guardamos a lista de caminhos já encontrados (via var_export), e cada caminho continua a ser carregado individualmente com require_once.

4. Por que cada require_once (mesmo vindo do cache) tem uma verificação file_exists()

Um cache pode ficar desatualizado: se cacheaste a lista e depois apagaste um ficheiro sem correr helpers:clear ou helpers:cache de novo, a lista em cache ainda aponta para um caminho que já não existe. Sem a verificação file_exists(), isto causaria um erro fatal (Failed to open stream: No such file or directory) logo na inicialização da aplicação — o pior cenário possível para um pacote cujo objetivo é nunca causar dor de cabeça. Com a verificação, esse ficheiro é simplesmente ignorado silenciosamente, e a aplicação continua a funcionar normalmente (só sem essa função específica).

5. Por que a lógica de descoberta está isolada numa classe própria (HelperFileFinder)

Tanto o HelpersServiceProvider (em runtime, quando não há cache) quanto o comando helpers:cache (para gerar o cache) precisam exatamente da mesma lógica de varredura. Se essa lógica estivesse duplicada nos dois lugares, haveria risco de um dia alguém corrigir um bug ou adicionar uma funcionalidade num só, deixando o outro dessincronizado. Centralizar numa única classe elimina esse risco por construção.


Limitações conhecidas

  • Helpers usados dentro de config/*.php ou bootstrap/app.php não são cobertos. Esses ficheiros são lidos pelo Laravel antes de qualquer Service Provider ser instanciado, então nenhuma abordagem baseada em ServiceProvider (como esta) consegue intervir a tempo. Isto é raro na prática — a esmagadora maioria dos casos de uso (controllers, models, views, outros services, etc.) já corre depois dos providers, e portanto funciona sem qualquer problema. Se precisares mesmo de um helper disponível nesse estágio inicial, a alternativa é declarar autoload.files diretamente no composer.json da tua aplicação (fora do escopo deste pacote).

  • Duas funções com o mesmo nome em ficheiros diferentes causam erro fatal. Isto é uma limitação do próprio PHP (não é possível ter duas funções globais com o mesmo nome), não deste pacote. A prática de envolver toda função em if (! function_exists(...)) (ver Uso básico) evita que isto aconteça por acidente ao recarregar o mesmo ficheiro, mas não protege contra dois ficheiros diferentes declararem a mesma função — isso precisa de disciplina de nomenclatura da tua parte.


Testes

O pacote inclui uma suite de testes de integração usando Orchestra Testbench, em tests/HelpersDiscoveryTest.php, cobrindo:

  • Carregamento de helper no topo da pasta
  • Carregamento recursivo em subpastas
  • Comportamento seguro quando a pasta não existe
  • Comportamento dos três comandos artisan
  • Comportamento do cache, incluindo cache desatualizado

Para correr localmente:

composer install
vendor/bin/phpunit

Nota de transparência: estes testes foram escritos e revistos manualmente com cuidado (sintaxe, lógica, balanceamento de chaves, imports), mas não foram executados durante o desenvolvimento deste pacote, porque o ambiente onde foi criado não tinha um interpretador PHP disponível para correr composer install e phpunit. Recomendo fortemente correr vendor/bin/phpunit localmente antes de publicar ou usar este pacote em produção, para confirmar que tudo passa como esperado no teu ambiente real.

Nota sobre o suporte ao Laravel 13: o composer.json já declara illuminate/support e illuminate/console com constraint ^13.0 incluído, porque o pacote não usa nenhuma API que se espere quebrar numa major release (apenas ServiceProvider, Command, mergeConfigFrom e publishes, todas estáveis há várias versões). No entanto, à data de criação deste pacote o Laravel 13 ainda não tinha sido lançado, e por isso o orchestra/testbench (usado só para os testes automatizados do próprio pacote, em require-dev) ainda não tem uma versão publicada que o suporte oficialmente. Isto não afeta quem instala o pacote — só significa que, quando o Laravel 13 sair, vale a pena atualizar o require-dev do Testbench e correr a suite de testes novamente contra ele antes de confiar cegamente na tag ^13.0.


FAQ

Preciso de correr composer dump-autoload depois de adicionar um novo helper? Não. O carregamento acontece em runtime (dentro do register() do Service Provider), não via autoload PSR-4 do Composer. Basta criar o ficheiro.

Funciona com Laravel Octane / servidores de longa duração? Sim, com uma ressalva: como o Octane mantém o processo PHP vivo entre requests, a varredura de diretórios (quando não há cache) só corre uma vez no arranque do worker, não a cada request. Isto é uma vantagem de performance, mas significa que adicionar um novo ficheiro helper exige reiniciar os workers do Octane para ser detetado — recomenda-se usar helpers:cache e reiniciar o Octane no deploy, tal como farias com route:cache.

Posso usar isto fora de um projeto Laravel? Não diretamente — o pacote depende de illuminate/support e illuminate/console, e usa helpers do próprio Laravel como app_path() e config(). Foi desenhado especificamente para o ecossistema Laravel.

O que acontece se eu apagar app/Helpers inteira? Nada de errado. O ServiceProvider verifica se a pasta existe antes de tentar varrê-la; se não existir, simplesmente não carrega nada, sem erros.


Licença

MIT.