# Container System - Framework OfficeGest

> **Nível**: Intermédio a Avançado  
> **Pré-requisitos**: Conhecimento de PHP, OOP e conceitos de Dependency Injection  
> **Tempo de leitura**: ~18 minutos

---

## 📋 Índice

1. [Introdução](#introdução)
2. [Quick Start](#quick-start)
3. [Arquitetura e Estrutura](#arquitetura-e-estrutura)
4. [O Container](#o-container)
5. [Bindings e Resolução](#bindings-e-resolução)
6. [Service Providers](#service-providers)
7. [Facades](#facades)
8. [Gestão de Ambiente](#gestão-de-ambiente)
9. [Ciclo de Vida](#ciclo-de-vida)
10. [Exemplos Práticos](#exemplos-práticos)
11. [Boas Práticas](#boas-práticas)
12. [Troubleshooting](#troubleshooting)
13. [Referência de API](#referência-de-api)

---

## Introdução

### O que é Dependency Injection?

**Dependency Injection (DI)** é um padrão de design onde as dependências de uma classe são "injectadas" em vez de criadas internamente. Isto torna o código mais testável, flexível e desacoplado.

```php
// SEM DI - dependência criada internamente
class UserService {
    public function __construct() {
        $this->database = new DatabaseConnection(); // ❌ Acoplado
    }
}

// COM DI - dependência injectada
class UserService {
    public function __construct(DatabaseConnection $database) {
        $this->database = $database; // ✅ Desacoplado
    }
}
```

### O que é um Container?

Um **Container de DI** é responsável por:
- **Registar** como criar instâncias de classes
- **Resolver** dependências automaticamente
- **Gerir** o ciclo de vida (singletons, scoped, etc.)

### Container no OfficeGest

O Container do OfficeGest é baseado no **Laravel Container** e serve como ponte entre:
- 🆕 Arquitectura moderna (`Modules/`)
- 🏛️ Sistema legacy (variáveis globais `$u`, `$a`, etc.)

---

## Quick Start

### 🚀 Para Começar em 2 Minutos

```php
// 1. OBTER o container
$container = app();  // Helper global

// 2. RESOLVER uma dependência
$cache = app('cache');
$request = app(Request::class);

// 3. VERIFICAR se existe
if (app()->has('database')) {
    $db = app('database');
}

// 4. REGISTAR um serviço (em Service Provider)
app()->singleton('meu-servico', function() {
    return new MeuServico();
});

// 5. USAR Facades (recomendado)
use Og\Modules\Common\Facades\Cache;
Cache::put('key', 'value', 60);
```

### Helper `app()`

O helper mais usado para aceder ao container:

```php
// Obter o container
$container = app();

// Resolver serviço específico
$service = app('nome-do-servico');
$service = app(MinhaClasse::class);

// Com parâmetros
$service = app()->make(MinhaClasse::class, ['param' => $valor]);
```

---

## Arquitetura e Estrutura

### Estrutura de Ficheiros

```
Modules/Common/Container/
├── Container.php                    # Classe principal (singleton)
├── BindingResolutionException.php   # Excepção PSR-11
└── HandleEnvironment.php            # Trait para gestão de ambiente
```

### Diagrama de Componentes

```mermaid
graph TB
    A[Aplicação] -->|app| B[Container]
    B --> C[Laravel Container]
    B --> D[ServiceProviderManager]
    D --> E[16 Service Providers]
    
    subgraph "Service Providers"
        E --> F[QueueServiceProvider]
        E --> G[RedisServiceProvider]
        E --> H[CacheServiceProvider]
        E --> I[GlobalServiceProvider]
        E --> J[...]
    end
    
    B --> K{Resolver}
    K -->|make| L[Instância]
    K -->|singleton| M[Instância Única]
    K -->|bind| N[Nova Instância]
```

### Padrões Utilizados

| Padrão | Aplicação |
|--------|-----------|
| **Singleton** | Container global único |
| **Decorator** | Wrapper do Laravel Container |
| **Service Provider** | Registo modular de serviços |
| **Facade** | Interface estática simplificada |

---

## O Container

### Localização

`Modules/Common/Container/Container.php`

### Características Principais

```php
class Container
{
    use HandleEnvironment;  // Trait para ambiente
    
    private static ?Container $instance = null;       // Singleton
    private LaravelContainer $container;              // Laravel Container interno
    private ServiceProviderManager $providerManager;  // Gestor de providers
    private bool $booted = false;                     // Estado de boot
}
```

### Obter Instância

```php
use Og\Modules\Common\Container\Container;

// Via helper (recomendado)
$container = app();

// Via método estático
$container = Container::getInstance();
```

### Métodos Principais

#### Resolução de Dependências

```php
// make() - Resolver com parâmetros opcionais
$service = app()->make('service');
$service = app()->make(MyClass::class, ['param' => $value]);

// get() - Resolver ou obter container
$service = app()->get('service');
$container = app()->get();  // Sem argumento retorna o Laravel Container

// call() - Chamar método com DI
$result = app()->call([$object, 'method'], ['param' => $value]);
$result = app()->call(function(Service $service) {
    return $service->doSomething();
});
```

#### Registo de Serviços

```php
// bind() - Nova instância a cada resolução
app()->bind('service', MyService::class);
app()->bind('service', function() {
    return new MyService($config);
});

// singleton() - Mesma instância sempre
app()->singleton('database', function() {
    return new DatabaseConnection();
});

// instance() - Registar instância existente
app()->instance('current-user', $user);

// alias() - Nome alternativo
app()->alias(MyService::class, 'my-service');

// scoped() - Singleton por request/job
app()->scoped('request-data', function() {
    return new RequestData();
});
```

#### Verificação

```php
// has() / bound() - Verificar se existe
if (app()->has('service')) { ... }
if (app()->bound('service')) { ... }

// isBooted() - Verificar se já fez boot
if (app()->isBooted()) { ... }
```

---

## Bindings e Resolução

### Bindings Automáticos

O Container regista automaticamente estes serviços:

```php
// HTTP
Request::class    → Request::capture()
'request'         → alias para Request::class
Response::class   → new Response()
'response'        → alias para Response::class

// Routing
'router-collector' → RouterCollector::getInstance()

// Ambiente
'env' → 'production', 'devel', ou 'testing'

// Config
Config::class → Configuração com cache
'config'      → alias para Config::class
```

### Auto-Wiring

O container resolve automaticamente dependências via reflection:

```php
class UserService
{
    // O container resolve automaticamente $repository
    public function __construct(
        private UserRepository $repository,  // Resolvido via DI
        private string $config = 'default'   // Parâmetro com default
    ) {}
}

// Uso:
$service = app(UserService::class);  // UserRepository injectado automaticamente!
```

### Hooks de Resolução

```php
// Antes da resolução
app()->resolving('service', function($service, $container) {
    // Configurar antes
    $service->configure();
});

// Após resolução
app()->afterResolving('service', function($service, $container) {
    // Configurar depois
    $service->initialize();
});
```

---

## Service Providers

### O que são?

Service Providers são classes que registam serviços no container. Seguem o padrão:

```php
class MeuServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Fase 1: Registar bindings
        $this->app->singleton('meu-servico', function() {
            return new MeuServico();
        });
    }
    
    public function boot(): void
    {
        // Fase 2: Configurar após todos os providers registados
        $servico = $this->app->make('meu-servico');
        $servico->configure();
    }
}
```

### Providers Registados (16 providers)

O Container regista automaticamente estes providers na ordem:

| # | Provider | Responsabilidade |
|---|----------|------------------|
| 1 | `EventServiceProvider` | Sistema de eventos |
| 2 | `HelperServiceProvider` | Funções auxiliares |
| 3 | `ConfigServiceProvider` | Configuração |
| 4 | `QueueServiceProvider` | Sistema de filas |
| 5 | `RedisServiceProvider` | Cliente Redis |
| 6 | `CacheServiceProvider` | Sistema de cache |
| 7 | `HttpClientServiceProvider` | Cliente HTTP |
| 8 | `SaftPtServiceProvider` | Integração SAFT PT |
| 9 | `SaftAoServiceProvider` | Integração SAFT AO |
| 10 | `ValidationServiceProvider` | Sistema de validação |
| 11 | `GlobalServiceProvider` | Bridge legacy ($u, $a, etc.) |
| 12 | `ViewServiceProvider` | Engine de templates |
| 13 | `NotificationServiceProvider` | Sistema de notificações |
| 14 | `RoutingServiceProvider` | Sistema de routing |
| 15 | `GuardServiceProvider` | Autenticação |
| 16 | `QueueMasterServiceProvider` | Queue Master |

### Criar um Service Provider

```php
namespace Og\Modules\MeuModulo\Providers;

use Og\Modules\Common\Providers\ServiceProvider;

class MeuModuloServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Registar repositórios
        $this->app->singleton(UserRepository::class, function() {
            return new EloquentUserRepository($this->app->get('database'));
        });
        
        // Registar serviços
        $this->app->singleton('user-service', function($app) {
            return new UserService(
                $app->make(UserRepository::class),
                $app->make('config')->get('users')
            );
        });
    }
    
    public function boot(): void
    {
        // Registar eventos
        $events = $this->app->make('events');
        $events->listen(UserCreated::class, SendWelcomeEmail::class);
    }
}
```

### Reload de Providers

```php
// Reload de um provider específico (útil em desenvolvimento)
app()->reloadServiceProvider(MeuServiceProvider::class);
```

---

## Facades

### O que são?

Facades fornecem uma interface estática para serviços do container, tornando o código mais limpo:

```php
// Sem Facade
$cache = app('cache');
$cache->put('key', 'value', 60);

// Com Facade (mais limpo)
Cache::put('key', 'value', 60);
```

### Facades Disponíveis

| Facade | Accessor | Serviço |
|--------|----------|---------|
| `Auth` | `'auth'` | AuthManager |
| `Cache` | `'cache'` | CacheManager |
| `File` | `'files'` | Filesystem |
| `Notification` | `'notification'` | NotificationService |
| `Queue` | `'queue'` | QueueManager |
| `Request` | `'request'` | Request |
| `Response` | `'response'` | Response |
| `Route` | `'router'` | Router |
| `View` | `'view'` | ViewFactory |

### Criar uma Facade

```php
namespace Og\Modules\Common\Facades;

use Og\Modules\Common\Utils\Facade;

class MeuServico extends Facade
{
    public static function getFacadeAccessor(): string
    {
        return 'meu-servico';  // Nome no container
    }
}

// Uso:
MeuServico::metodo();  // Chama app('meu-servico')->metodo()
```

---

## Gestão de Ambiente

### Trait HandleEnvironment

O Container usa o trait `HandleEnvironment` para gestão de ambiente:

```php
// Verificar se está em testes
if (app()->runningUnitTests()) {
    // Ambiente de testes
}

// Obter ambiente atual
$env = app()->environment();  // 'production', 'devel', ou 'testing'

// Verificar se está em CLI
if (app()->runningInConsole()) {
    // Executando via terminal
}
```

### Detecção Automática de Ambiente

```php
// Container.php:79-89
$this->singleton('env', function () {
    $env = $_ENV['APP_ENV'] ?? getenv('APP_ENV');
    $uri = URL::make()->fromServerGlobals()->toString();
    
    if (!$uri) {
        return 'production';
    }
    if (!$env && !$this->runningUnitTests() && !str_contains($uri, 'https')) {
        return 'devel';
    }
    return $_ENV['APP_ENV'] ?? getenv('APP_ENV') ?? 'production';
});
```

---

## Ciclo de Vida

### Fases do Container

```mermaid
sequenceDiagram
    participant App as Aplicação
    participant C as Container
    participant PM as ProviderManager
    participant SP as Service Providers
    
    App->>C: getInstance()
    C->>C: __construct()
    C->>C: registerGlobalConfig()
    C->>C: registerBindings()
    C->>PM: register(providers)
    PM->>SP: register() em cada provider
    
    App->>C: boot()
    C->>PM: boot()
    PM->>SP: boot() em cada provider
    C->>C: booted = true
```

### Métodos de Gestão de Ciclo

```php
// Boot - executa boot() em todos os providers
app()->boot();

// Reload completo (só antes do boot)
app()->reload();

// Reload de provider específico
app()->reloadServiceProvider(MyProvider::class);

// Limpar instâncias (para workers)
app()->clearInstances();

// Terminar (cleanup completo)
app()->terminate();
```

### Para Queue Workers

```php
// Reload do container entre jobs
app()->reloadContainer();

// Limpar estado da base de dados
app()->cleanDatabaseState();
```

---

## Exemplos Práticos

### 🎯 Exemplo 1: Serviço com Dependências

```php
// Service Provider
class PaymentServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Registar gateway
        $this->app->singleton(PaymentGateway::class, function($app) {
            $config = $app->make('config')->get('payment');
            return new StripeGateway($config['api_key']);
        });
        
        // Registar serviço
        $this->app->singleton(PaymentService::class, function($app) {
            return new PaymentService(
                $app->make(PaymentGateway::class),
                $app->make(OrderRepository::class)
            );
        });
    }
}

// Uso
$paymentService = app(PaymentService::class);
$paymentService->processPayment($order);
```

### 🎯 Exemplo 2: Binding Contextual

```php
// Diferentes implementações por contexto
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // PDF para web
        $this->app->when(WebController::class)
            ->needs(ReportGenerator::class)
            ->give(PdfReportGenerator::class);
        
        // Excel para API
        $this->app->when(ApiController::class)
            ->needs(ReportGenerator::class)
            ->give(ExcelReportGenerator::class);
    }
}
```

### 🎯 Exemplo 3: Integração com Legacy

```php
// GlobalServiceProvider - Bridge para variáveis globais
class GlobalServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Expor $u global via container
        $this->app->singleton('user', function() {
            if (!empty($GLOBALS['u'])) {
                return $GLOBALS['u'];
            }
            return new User();
        });
        
        // Expor $a global via container
        $this->app->singleton('app', function() {
            if (isset($GLOBALS['a'])) {
                return $GLOBALS['a'];
            }
            return new Aplication();
        });
    }
}

// Agora pode usar ambos:
$user = $GLOBALS['u'];     // Legacy
$user = app('user');       // Moderno
```

### 🎯 Exemplo 4: Abort com HTTP Exceptions

```php
// No controller/action
public function show(int $id): Response
{
    $item = $this->repository->find($id);
    
    if (!$item) {
        app()->abort(404, 'Item não encontrado');
        // Lança NotFoundException
    }
    
    if (!$this->canAccess($item)) {
        app()->abort(403, 'Acesso negado');
        // Lança AuthorizationException
    }
    
    return response()->json($item);
}
```

---

## Boas Práticas

### ✅ Use Service Providers para Registo

```php
// BOM - centralizado no provider
class MyServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(MyService::class, ...);
    }
}

// MAU - registo espalhado pelo código
app()->singleton('service', ...);  // Em qualquer lugar
```

### ✅ Prefira Interfaces a Implementações

```php
// BOM - depende de interface
class OrderService
{
    public function __construct(
        private PaymentGatewayInterface $gateway  // Interface
    ) {}
}

// No provider:
$this->app->bind(PaymentGatewayInterface::class, StripeGateway::class);

// MAU - acoplado à implementação
class OrderService
{
    public function __construct(
        private StripeGateway $gateway  // Implementação concreta
    ) {}
}
```

### ✅ Use Facades com Moderação

```php
// BOM - em controllers/views (código de aplicação)
Cache::put('key', $value, 60);

// EVITAR - em classes de domínio (use DI)
class UserService
{
    public function __construct(
        private CacheInterface $cache  // ✅ Injectado
    ) {}
    
    public function getUser(int $id): User
    {
        // ❌ Não use Cache:: aqui, use $this->cache
        return $this->cache->remember(...);
    }
}
```

### ✅ Singleton para Serviços Stateless

```php
// Singleton - mesmo serviço, sem estado
$this->app->singleton(EmailService::class, ...);

// Bind - nova instância por request (com estado)
$this->app->bind(ShoppingCart::class, ...);
```

---

## Troubleshooting

### Problema: BindingResolutionException

```
BindingResolutionException: Target [MyInterface] is not instantiable
```

**Causa:** Interface sem binding concreto.

**Solução:**
```php
// No Service Provider
$this->app->bind(MyInterface::class, MyImplementation::class);
```

### Problema: Circular Dependency

```
Circular dependency detected while resolving [ServiceA]
```

**Causa:** ServiceA depende de ServiceB que depende de ServiceA.

**Solução:**
```php
// Usar lazy loading ou reestruturar
$this->app->singleton(ServiceA::class, function($app) {
    return new ServiceA(fn() => $app->make(ServiceB::class));
});
```

### Problema: Serviço não encontrado

```php
app('meu-servico');  // Retorna erro
```

**Soluções:**
```php
// 1. Verificar se Provider está registado no Container.php
// 2. Verificar se o nome está correcto
if (!app()->has('meu-servico')) {
    dd('Serviço não registado!');
}

// 3. Verificar se Provider foi carregado
dd(app()->isBooted());
```

### Debug do Container

```php
// Ver todos os bindings
dd(app()->get()->getBindings());

// Ver se está booted
dd(app()->isBooted());

// Ver ambiente
dd(app()->environment());
```

---

## Referência de API

### Container

```php
class Container
{
    // Singleton
    public static function getInstance(): self;
    
    // Resolução
    public function make($abstract, array $parameters = []): mixed;
    public function get(string $abstract = null): mixed;
    public function call($callback, array $parameters = [], $defaultMethod = null): mixed;
    
    // Registo
    public function bind(string $abstract, $concrete = null, bool $shared = false): void;
    public function singleton(string $abstract, $concrete = null): void;
    public function instance(string $abstract, $instance): void;
    public function alias(string $abstract, string $alias): void;
    public function scoped(string $abstract, \Closure $callback): void;
    
    // Verificação
    public function has(string $abstract): bool;
    public function bound(string $string): bool;
    public function isBooted(): bool;
    
    // Hooks
    public function resolving(string $string, \Closure $param): void;
    public function afterResolving(string $string, \Closure $param): void;
    
    // Ciclo de vida
    public function boot(): void;
    public function reload(): void;
    public function reloadServiceProvider(string $providerClass): void;
    public function reloadContainer(): void;
    public function terminate(): void;
    public function clearInstances(): void;
    public function forgetInstance(string $abstract): void;
    public function cleanDatabaseState(): void;
    
    // Ambiente (via HandleEnvironment trait)
    public function runningUnitTests(): bool;
    public function environment(): string;
    public function runningInConsole(): bool;
    
    // HTTP Exceptions
    public function abort($code, $message = '', array $headers = []): never;
}
```

### ServiceProvider

```php
abstract class ServiceProvider
{
    protected Container $app;
    
    abstract public function register(): void;
    public function boot(): void;
}
```

---

## Próximos Passos

- 📖 [Service Providers](./service-providers-system.md) - Criar providers personalizados
- 📖 [Facades](./facades-system.md) - Sistema de facades
- 📖 [Helpers](./helpers-system.md) - Funções auxiliares como `app()`

---

*Última atualização: Dezembro 2024*
