Como Usar PHP Moderno em Produção Já leu

Estrutura de Projeto e Padrões Modernos PHP moderno em produção começa com uma arquitetura sólida. A maioria dos projetos sérios utiliza PSR-4 para autoloading, namespaces e o padrão MVC ou Clean Architecture. Isso não é opcional — é a base para manutenibilidade e escalabilidade. Usamos Composer como gerenciador de dependências, que padroniza a forma como importamos bibliotecas. A estrutura de diretórios é essencial. Separe src/ (código), tests/ (testes), config/ (configurações) e public/ (entrada web). Nunca coloque lógica no public; use um único entry point. Segurança e Boas Práticas de Código Segurança em produção não é "algo a mais" — é fundamental. SQL injection, XSS, CSRF e validação inadequada continuam sendo os principais culpados de vazamentos. Use prepared statements sempre, valide inputs agressivamente e implemente rate limiting. Use type hints agressivamente (PHP 8.1+). Eles não são apenas boas práticas — são proteção contra bugs. Implemente early returns para reduzir complexidade ciclomática e sempre trate erros com exceções bem definidas. Testes, Logs

Estrutura de Projeto e Padrões Modernos

PHP moderno em produção começa com uma arquitetura sólida. A maioria dos projetos sérios utiliza PSR-4 para autoloading, namespaces e o padrão MVC ou Clean Architecture. Isso não é opcional — é a base para manutenibilidade e escalabilidade. Usamos Composer como gerenciador de dependências, que padroniza a forma como importamos bibliotecas.

<?php

// composer.json
{
    "name": "meu-projeto/app",
    "require": {
        "php": ">=8.1",
        "symfony/http-foundation": "^6.0",
        "doctrine/orm": "^2.14"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

// src/User/UserRepository.php
namespace App\User;

class UserRepository
{
    public function findById(int $id): ?User
    {
        // Implementação
        return null;
    }
}

// src/User/User.php
namespace App\User;

class User
{
    public function __construct(
        private int $id,
        private string $email,
        private string $password
    ) {}

    public function getId(): int
    {
        return $this->id;
    }
}

A estrutura de diretórios é essencial. Separe src/ (código), tests/ (testes), config/ (configurações) e public/ (entrada web). Nunca coloque lógica no public; use um único entry point.

Segurança e Boas Práticas de Código

Segurança em produção não é "algo a mais" — é fundamental. SQL injection, XSS, CSRF e validação inadequada continuam sendo os principais culpados de vazamentos. Use prepared statements sempre, valide inputs agressivamente e implemente rate limiting.

<?php

// Errado: vulnerável a SQL injection
$user = $pdo->query("SELECT * FROM users WHERE email = '{$_POST['email']}'");

// Correto: prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();

// Validação robusta com filter_var
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
    throw new InvalidArgumentException('Email inválido');
}

// Proteção CSRF com tokens
session_start();
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Verificar no formulário
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    throw new RuntimeException('Token CSRF inválido');
}

// Hashing seguro de senhas
$hash = password_hash($_POST['password'], PASSWORD_ARGON2ID, ['memory_cost' => 65536]);
if (!password_verify($input_password, $hash)) {
    throw new RuntimeException('Senha incorreta');
}

// Headers de segurança
header('Content-Security-Policy: default-src \'self\'');
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

Use type hints agressivamente (PHP 8.1+). Eles não são apenas boas práticas — são proteção contra bugs. Implemente early returns para reduzir complexidade ciclomática e sempre trate erros com exceções bem definidas.

Testes, Logs e Monitoramento

Código em produção sem testes é código quebrado que você ainda não descobriu. Implemente testes unitários, de integração e end-to-end. Use PHPUnit para testes estruturados e mantenha cobertura acima de 80%. Logs estruturados com Monolog permitem rastrear problemas em produção; use níveis apropriados (DEBUG, INFO, WARNING, ERROR, CRITICAL).

<?php

// tests/User/UserRepositoryTest.php
namespace Tests\User;

use App\User\UserRepository;
use PHPUnit\Framework\TestCase;

class UserRepositoryTest extends TestCase
{
    private UserRepository $repository;

    protected function setUp(): void
    {
        $this->repository = new UserRepository();
    }

    public function testFindByIdReturnsNullWhenNotFound(): void
    {
        $result = $this->repository->findById(999);
        $this->assertNull($result);
    }

    public function testFindByIdReturnsUserInstance(): void
    {
        $user = $this->repository->findById(1);
        $this->assertInstanceOf(User::class, $user);
        $this->assertEquals(1, $user->getId());
    }
}

// src/Logger/ApplicationLogger.php
namespace App\Logger;

use Monolog\Logger;
use Monolog\Handlers\StreamHandler;

class ApplicationLogger
{
    private Logger $logger;

    public function __construct()
    {
        $this->logger = new Logger('app');
        $this->logger->pushHandler(
            new StreamHandler('var/logs/app.log', Logger::DEBUG)
        );
    }

    public function logUserLogin(int $userId): void
    {
        $this->logger->info('User login', ['user_id' => $userId]);
    }

    public function logException(\Throwable $e): void
    {
        $this->logger->error('Exception occurred', [
            'message' => $e->getMessage(),
            'trace' => $e->getTraceAsString()
        ]);
    }
}

Configure APM (Application Performance Monitoring) como Sentry ou NewRelic. Logs locais são insuficientes em produção — você precisa visibilidade em tempo real sobre performance, erros e comportamento do usuário.

Deploy, Variáveis de Ambiente e Performance

Nunca commite credenciais ou configurações no Git. Use dotenv para gerenciar variáveis de ambiente. Separe claramente configuração (que muda entre ambientes) de código.

<?php

// .env.example (commitar no repositório)
DATABASE_URL=mysql://user:pass@localhost/dbname
REDIS_URL=redis://localhost:6379
LOG_LEVEL=DEBUG
APP_ENV=development

// .env (NÃO commitar — apenas local/produção)
DATABASE_URL=mysql://prod_user:prod_pass@prod-db:3306/prod_db
REDIS_URL=redis://prod-cache:6379
LOG_LEVEL=WARNING
APP_ENV=production

// config/Database.php
namespace App\Config;

use Dotenv\Dotenv;

class Database
{
    public static function connect(): \PDO
    {
        $dotenv = Dotenv::createImmutable(__DIR__ . '/..');
        $dotenv->load();

        $url = $_ENV['DATABASE_URL'];
        return new \PDO($url);
    }
}

Para performance, implemente caching em múltiplas camadas: Redis para sessão e dados quentes, APCu para dados estáticos, e HTTP caching via headers. Use lazy loading, evite N+1 queries com eager loading em ORMs, e perfil seu código com Xdebug ou Blackfire.

<?php

// Cache com Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$cacheKey = 'user:' . $userId;
$user = $redis->get($cacheKey);

if ($user === false) {
    $user = $repository->findById($userId); // Query ao BD
    $redis->setex($cacheKey, 3600, serialize($user)); // Cache por 1h
}

// Eager loading para evitar N+1
$users = $repository->findAllWithPosts(); // JOINs no SQL

Conclusão

Dominar PHP moderno em produção exige três pilares: (1) Arquitetura sólida com padrões, namespaces e estrutura clara; (2) Segurança rigorosa com validação, prepared statements e headers apropriados; (3) Observabilidade via testes, logs estruturados e monitoramento real-time. Não pule essas etapas por "ganho de tempo" — código inseguro ou difícil de debugar custará muito mais depois.

Referências


Artigos relacionados