AgentSkillsCN

legacy-modernizer

对老旧 PHP 代码进行现代化改造,使其符合当前主流标准(PHP 7.4–8.3)。通过将过程式代码重构为面向对象编程、升级已废弃的语法、引入类型提示、用对象或集合替代数组、将回调函数转换为闭包或箭头函数、以依赖注入取代全局变量等方式,助力代码迈向更现代、更高效的开发范式,并为后续框架迁移做好充分准备。

SKILL.md
--- frontmatter
name: legacy-modernizer
description: Modernização de código PHP legado para padrões atuais (PHP 7.4-8.3). Usar para converter código procedural para OOP, atualizar sintaxe deprecated, implementar type hints, substituir arrays por objetos/collections, modernizar callbacks para closures/arrow functions, converter globals para dependency injection, e preparar código para migração de frameworks.

Legacy Modernizer

Skill para transformação de código PHP legado em código moderno e maintainable.

Checklist de Modernização

  1. Sintaxe PHP 8.x - Type hints, named arguments, match, enums
  2. OOP - Classes, interfaces, traits, composition
  3. SOLID - Single responsibility, dependency injection
  4. PSR - Autoload, coding style, interfaces
  5. Error Handling - Exceptions em vez de error codes
  6. Testing - Código testável e desacoplado

Transformações Comuns

1. Sintaxe Moderna

php
// ❌ ANTES: PHP 5.x
function getUser($id) {
    global $db;
    $result = mysql_query("SELECT * FROM users WHERE id = " . $id);
    if ($result) {
        return mysql_fetch_assoc($result);
    }
    return false;
}

// ✅ DEPOIS: PHP 8.x
function getUser(int $id): ?array
{
    return DB::table('users')->find($id)?->toArray();
}

2. Type Declarations

php
// ❌ ANTES
function calcularTotal($items, $desconto) {
    $total = 0;
    foreach ($items as $item) {
        $total += $item['valor'];
    }
    return $total - $desconto;
}

// ✅ DEPOIS
function calcularTotal(array $items, float $desconto = 0): float
{
    $total = array_sum(array_column($items, 'valor'));
    return max(0, $total - $desconto);
}

// ✅ MELHOR: Com objetos
function calcularTotal(Collection $items, float $desconto = 0): float
{
    return max(0, $items->sum('valor') - $desconto);
}

3. Procedural para OOP

php
// ❌ ANTES: Funções soltas
function criar_contrato($dados) { ... }
function atualizar_contrato($id, $dados) { ... }
function buscar_contrato($id) { ... }
function listar_contratos($filtros) { ... }

// ✅ DEPOIS: Service Class
class ContractService
{
    public function __construct(
        private ContractRepository $repository,
        private EventDispatcher $events
    ) {}

    public function create(array $data): Contract
    {
        $contract = $this->repository->create($data);
        $this->events->dispatch(new ContractCreated($contract));
        return $contract;
    }

    public function update(int $id, array $data): Contract
    {
        $contract = $this->repository->findOrFail($id);
        $contract->update($data);
        return $contract;
    }

    public function find(int $id): ?Contract
    {
        return $this->repository->find($id);
    }

    public function list(array $filters = []): Collection
    {
        return $this->repository->filter($filters);
    }
}

4. Global State para DI

php
// ❌ ANTES: Globals
function enviarEmail($para, $assunto, $corpo) {
    global $config;
    $mail = new PHPMailer();
    $mail->Host = $config['smtp_host'];
    // ...
}

// ✅ DEPOIS: Dependency Injection
class EmailService
{
    public function __construct(
        private readonly MailerInterface $mailer,
        private readonly MailConfig $config
    ) {}

    public function send(string $to, string $subject, string $body): void
    {
        $this->mailer
            ->to($to)
            ->subject($subject)
            ->body($body)
            ->send();
    }
}

5. Arrays para Value Objects

php
// ❌ ANTES: Array associativo
$contrato = [
    'cliente' => 'João',
    'valor' => 1500.00,
    'data' => '2024-01-15',
    'status' => 'ativo'
];

// ✅ DEPOIS: DTO/Value Object
readonly class Contract
{
    public function __construct(
        public string $cliente,
        public float $valor,
        public DateTimeImmutable $data,
        public ContractStatus $status
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            cliente: $data['cliente'],
            valor: (float) $data['valor'],
            data: new DateTimeImmutable($data['data']),
            status: ContractStatus::from($data['status'])
        );
    }
}

enum ContractStatus: string
{
    case Ativo = 'ativo';
    case Cancelado = 'cancelado';
    case Finalizado = 'finalizado';

    public function label(): string
    {
        return match($this) {
            self::Ativo => 'Ativo',
            self::Cancelado => 'Cancelado',
            self::Finalizado => 'Finalizado',
        };
    }
}

6. Callbacks para Arrow Functions

php
// ❌ ANTES: Callback verbose
$ativos = array_filter($contratos, function($c) {
    return $c['status'] == 'ativo';
});

$valores = array_map(function($c) {
    return $c['valor'];
}, $ativos);

$total = array_reduce($valores, function($carry, $v) {
    return $carry + $v;
}, 0);

// ✅ DEPOIS: Arrow functions + Collection
$total = collect($contratos)
    ->filter(fn($c) => $c['status'] === 'ativo')
    ->sum('valor');

7. Switch para Match

php
// ❌ ANTES
switch ($status) {
    case 'pending':
        $label = 'Pendente';
        $color = 'yellow';
        break;
    case 'active':
        $label = 'Ativo';
        $color = 'green';
        break;
    case 'cancelled':
        $label = 'Cancelado';
        $color = 'red';
        break;
    default:
        $label = 'Desconhecido';
        $color = 'gray';
}

// ✅ DEPOIS
[$label, $color] = match($status) {
    'pending' => ['Pendente', 'yellow'],
    'active' => ['Ativo', 'green'],
    'cancelled' => ['Cancelado', 'red'],
    default => ['Desconhecido', 'gray'],
};

8. Error Handling

php
// ❌ ANTES: Error codes
function processarPagamento($dados) {
    if (!isset($dados['valor'])) {
        return ['erro' => true, 'mensagem' => 'Valor obrigatório'];
    }
    if ($dados['valor'] <= 0) {
        return ['erro' => true, 'mensagem' => 'Valor inválido'];
    }
    // processar...
    return ['erro' => false, 'id' => 123];
}

// ✅ DEPOIS: Exceptions
class PaymentService
{
    public function process(PaymentData $data): Payment
    {
        $this->validate($data);
        
        try {
            return $this->gateway->charge($data);
        } catch (GatewayException $e) {
            throw new PaymentFailedException(
                "Falha no pagamento: {$e->getMessage()}",
                previous: $e
            );
        }
    }

    private function validate(PaymentData $data): void
    {
        if ($data->valor <= 0) {
            throw new InvalidArgumentException('Valor deve ser positivo');
        }
    }
}

Padrão de Migração Incremental

code
1. Criar interfaces para código existente
2. Implementar nova versão atrás da interface
3. Usar feature flags para alternar
4. Remover código antigo após validação
php
// Interface de abstração
interface ContractRepositoryInterface
{
    public function find(int $id): ?Contract;
    public function save(Contract $contract): void;
}

// Implementação legada (wrapper)
class LegacyContractRepository implements ContractRepositoryInterface
{
    public function find(int $id): ?Contract
    {
        $data = buscar_contrato($id); // função legada
        return $data ? Contract::fromArray($data) : null;
    }
}

// Nova implementação
class EloquentContractRepository implements ContractRepositoryInterface
{
    public function find(int $id): ?Contract
    {
        return ContractModel::find($id)?->toDomain();
    }
}

// Service Provider com feature flag
$this->app->bind(
    ContractRepositoryInterface::class,
    config('features.new_repository') 
        ? EloquentContractRepository::class 
        : LegacyContractRepository::class
);

Scripts Úteis

  • scripts/analyze-legacy.php - Análise de código legado
  • scripts/convert-array-to-dto.php - Converter arrays em DTOs