heliomarpm / linq-php
Manipulação fluida de arrays em PHP inspirada no LINQ: filtros, joins relacionais, agrupamentos e projeções com alta performance e zero dependências.
Fund package maintenance!
Requires
- php: >=8.1
Requires (Dev)
- laravel/pint: ^1.32
- phpbench/phpbench: ^1.7
- phpstan/phpstan: *
- phpunit/phpunit: ^13.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
LinqPHP é uma biblioteca leve, de alta performance e sem dependências externas para manipulação de coleções de dados (arrays) em PHP, inspirada na sintaxe do LINQ (Language Integrated Query).
Permite filtrar, agrupar, ordenar e unir arrays multidimensionais de forma fluida e elegante, sendo ideal para processamento de relatórios, formatação de dados para APIs e refatoração de sistemas legados.
Benchmark
Requisitos
- PHP 8.1 ou superior.
Instalação
Você pode instalar o pacote via Composer:
composer require heliomarpm/linq-php
Como usar
A classe LinqPHP utiliza uma interface fluida (method chaining). Todas as operações retornam a própria instância (exceto os métodos de saída toArray e toObject), permitindo encadear múltiplas transformações.
Inicialização
require 'vendor/autoload.php'; use HeliomarPM\LinqPHP\LinqPHP; $data = [ ['id' => 1, 'name' => 'John', 'age' => 25, 'status' => 'active', 'city' => 'London'], ['id' => 2, 'name' => 'Jane', 'age' => 30, 'status' => 'pending', 'city' => 'New York'], ['id' => 3, 'name' => 'John', 'age' => 35, 'status' => 'inactive', 'city' => 'Paris'], ]; $linq = LinqPHP::from($data);
where() - Filtragem de Dados
Aceita arrays de condições simples ou funções (closures) para filtros complexos.
// Filtro simples $result = LinqPHP::from($data) ->where(['age', '>', 25]) ->toArray(); // Múltiplos filtros e operadores (startswith, endswith, contains, in) $result = LinqPHP::from($data) ->where([ ['age', '>=', 25], ['status', 'in', ['active', 'pending']], ['name', 'startswith', 'J'] ]) ->toArray(); // Usando Closures (funções anônimas) $result = LinqPHP::from($data) ->where(fn($item) => $item['id'] % 2 !== 0) ->toArray();
select() e distinct() - Projeção de Dados
Selecione apenas as colunas desejadas, renomeie campos utilizando aliases ou misture ambos com a projeção híbrida.
// 1. Seleção simples de colunas $result = LinqPHP::from($data) ->select(['name', 'status']) ->toArray(); // 2. Seleção com Aliases (renomeação de colunas) $result = LinqPHP::from($data) ->select([ 'id' => 'codigo', 'name' => 'nome_completo', 'city' => 'cidade' ]) ->toArray(); // 3. Projeção Híbrida (mantém alguns campos originais e renomeia outros) $result = LinqPHP::from($data) ->select([ 'id', // mantém como 'id' 'name' => 'nome_completo', // renomeia para 'nome_completo' 'status' // mantém como 'status' ]) ->toArray(); // 4. Retorna apenas os registros únicos $result = LinqPHP::from($data) ->select(['name']) ->distinct() ->toArray();
Tip
Performance e Modo Estrito (strict):
- Zero Performance Penalty: Em seleções tradicionais indexadas numericamente (
['name', 'status']), o LinqPHP utiliza internamentearray_is_list()nativo do PHP 8.1+ e executa o caminho direto em C comarray_intersect_key, garantindo zero degradação de performance. - Preservação de Ordem: As colunas no array de saída seguem rigorosamente a ordem em que foram declaradas no
select(). - Modo Estrito: Por padrão (
strict: false), campos inexistentes na fonte são preenchidos comnull. Ao usarselect(['campo1', 'campo2'], strict: true), umaInvalidArgumentExceptionserá lançada caso alguma chave não exista no registro.
join() - União Relacional
Faça uniões relacionais entre arrays multidimensionais como se estivesse em um banco de dados relacional. Suporta os 4 tipos de join via enum nativo JoinType:
JoinType::INNER: Retorna apenas os registros com correspondência em ambas as coleções.JoinType::LEFT: Retorna todos os registros da esquerda e os correspondentes da direita (ounull).JoinType::RIGHT: Retorna todos os registros da direita e os correspondentes da esquerda (ounull).JoinType::FULL: Retorna todos os registros de ambos os lados, preenchendo lacunas comnull.
use HeliomarPM\LinqPHP\JoinType; $users = [ ['id' => 1, 'name' => 'John', 'course_id' => 10], ['id' => 2, 'name' => 'Jane', 'course_id' => 20], ]; $courses = [ ['id_course' => 10, 'course_name' => 'PHP 8'], ['id_course' => 20, 'course_name' => 'Clean Code'], ]; // Inner Join simples $result = LinqPHP::from($users) ->join($courses, JoinType::INNER, ['course_id' => 'id_course']) ->toArray(); // Joins Múltiplos encadeados (Join Triplo) $result = LinqPHP::from($vendas) ->join($produtos, JoinType::INNER, ['produto_id' => 'id_produto']) ->join($clientes, JoinType::INNER, ['cliente_id' => 'id_cliente']) ->toArray();
unionAll() - Concatenação de Coleções
Consolida múltiplos arrays em uma única coleção, preenchendo automaticamente com null colunas ausentes em registros heterogêneos.
$produtosRegulares = [ ['id' => 1, 'nome' => 'Filamento PLA', 'preco' => 90.0] ]; $produtosOutlet = [ ['id' => 2, 'nome' => 'Resina 3D', 'desconto' => 0.2] // coluna 'preco' ausente ]; $result = LinqPHP::from($produtosRegulares) ->unionAll($produtosOutlet) ->toArray();
groupBy() - Agrupamento e Agregação
Agrupe dados e realize cálculos automáticos (sum, max, min, count, avg).
$vendas = [ ['vendedor' => 'João', 'valor' => 100], ['vendedor' => 'Maria', 'valor' => 200], ['vendedor' => 'João', 'valor' => 150], ]; // Agrupa por vendedor e soma os valores $result = LinqPHP::from($vendas) ->groupBy(['vendedor'], ['sum' => ['valor']]) ->toArray();
orderBy() e orderByKey() - Ordenação
Ordene por uma chave simples (orderByKey) ou por múltiplos critérios com direções independentes (orderBy).
// Ordena por uma chave simples $result = LinqPHP::from($data) ->orderByKey('age', 'desc') ->toArray(); // Ordenação múltipla com direções independentes $result = LinqPHP::from($data) ->orderBy(['name' => 'asc', 'age' => 'desc']) ->toArray();
Important
Ciclo de Vida em Memória e Encadeamento com select() e Aliases:
O LinqPHP opera por mutação in-memory a cada chamada da cadeia fluente. Quando você utiliza o select() renomeando colunas para aliases, os nomes originais são substituídos nos dados internos:
-
Boa Prática — Ordenar antes da projeção: Ordene seus dados pela chave física de origem antes de renomeá-los para a saída:
$result = LinqPHP::from($vendas) ->where(['status', '=', 'concluido']) ->orderByKey('id_venda', 'asc') // ✅ Ordena pela chave original ->select(['id_venda' => 'Código Venda', 'valor' => 'Total']) ->toArray();
-
Ordenando após a projeção: Se chamar o
orderByapós oselect(), você deve referenciar o alias gerado, pois o nome antigo não existe mais no registro:$result = LinqPHP::from($vendas) ->select(['id_venda' => 'Código Venda', 'valor' => 'Total']) ->orderByKey('Código Venda', 'asc') // ✅ Ordena pelo alias criado no select ->toArray();
Caso tente ordenar por uma chave inexistente (como tentar usar 'id_venda' após ele ter sido transformado em 'Código Venda'), o LinqPHP disparará uma InvalidArgumentException("This field does not exist for sorting.").
Extração de Resultados (toArray e toObject)
No final da cadeia fluente, invoque um método de saída terminal para extrair os dados processados.
$linq = LinqPHP::from($data)->where(['age', '>', 20]); // Retorna o array tradicional processado $array = $linq->toArray(); // Retorna um stdClass contendo os dados e métricas de telemetria embutida $obj = $linq->toObject(); echo $obj->count; // Quantidade de registros resultantes echo $obj->elapsedTime; // Tempo total gasto na operação em segundos echo $obj->memoryUsed; // Consumo de memória formatado (ex: 18.00 MB) print_r($obj->data); // Coleção de dados processados
🧪 Laboratório Interativo (Demo)
O projeto inclui uma interface web completa para experimentação e simulação de queries LINQ em tempo real, com dados de e-commerce e editor interativo de código:
composer demo
# Acesse http://localhost:8000 no seu navegador
Na demo interativa você pode simular cenários avançados como Join Triplo, Union All, agregações com GroupBy e testar expressões customizadas com telemetria instantânea de memória e tempo de execução.
🛠️ Desenvolvimento e Testes
O código-fonte principal encontra-se na pasta src/. O projeto utiliza ferramentas de qualidade estrita (PHPStan e Laravel Pint) e suíte completa de testes unitários com PHPUnit 13.
Comandos disponíveis via Composer:
# Executa a suíte de testes unitários composer test # Executa testes com saída detalhada composer test:v # Executa benchmarks de performance (até 200.000 registros) composer bench # Executa análise estática de tipos (PHPStan nível 5) composer lint # Valida conformidade com PSR-12 (Laravel Pint) composer format:test # Corrige automaticamente estilo de código composer format:fix # Executa verificação completa de QA (Testes + Benchmarks + PHPStan) composer qa
🤝 Contribuições e suporte
Existem várias formas de contribuir:
- ⭐ Marcar o repositório com uma estrela
- 🐞 Relatar bugs
- 💡 Sugerir funcionalidades ou novas stacks
- 🧾 Melhorar a documentação
- 📢 Compartilhar o projeto
Para propor uma melhoria ou contribuição, veja o Guia de Contribuição.
Se precisar de ajuda ou encontrou um problema, consulte o Suporte ou abra uma Issue. Leia também o Código de Conduta.
Obrigado a todos que já contribuíram:
Criado com contrib.nn
💵 Apoie o Projeto
Se você gosta do projeto, considere fazer uma doação ao desenvolvedor via GitHub Sponsors, Ko-fi, PayPal ou Liberapay — a escolha é sua. 😉
📝 Licença
Distribuído sob a licença MIT © Heliomar P. Marques.
Sinta-se livre para utilizar, customizar e estender estas habilidades nos seus ambientes de desenvolvimento. 🔝

