Guía completa de PHP moderno para desarrolladores

php Guía completa de PHP moderno para desarrolladores

Guía completa de PHP moderno para desarrolladores

Esta guía reúne las prácticas, herramientas y patrones esenciales para escribir aplicaciones PHP seguras, mantenibles y rápidas usando PHP 8+. Incluye estructura de proyecto, ejemplos reales, seguridad, rendimiento, pruebas y despliegue.

1. Filosofía: por qué PHP moderno

PHP ya no es solo scripts rápidos; hoy es un lenguaje orientado a objetos con tipado, atributos, manejo mejorado de errores y ecosistema robusto (Composer, PSR, frameworks, herramientas de análisis estático). Adopta estas prácticas para reducir bugs y mejorar la velocidad de desarrollo.

2. Estructura mínima recomendada

my-app/
├─ src/               # código fuente (PSR-4)
│  ├─ Controller/
│  ├─ Service/
│  ├─ Repository/
│  └─ Domain/
├─ public/            # entrypoint (index.php)
├─ config/
├─ tests/
├─ docker/
├─ composer.json
└─ .env

Por qué: separación clara entre capa HTTP y dominio facilita testing y mantenimiento.

3. Composer y PSR-4 - ejemplo mínimo

{
  "name": "acme/my-app",
  "autoload": {
    "psr-4": { "App\\": "src/" }
  },
  "require": {
    "php": "^8.0"
  }
}

4. Ejemplo de código: controlador, servicio y repositorio

Objetivo: mostrar inyección de dependencias, tipado, DTO y manejo de excepciones.

<?php

namespace App\Controller;

use App\Service\UserService;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Response;

final class UserController
{
    public function __construct(private UserService $userService) {}

    public function getUser(ServerRequestInterface $req): ResponseInterface
    {
        $id = (int) $req->getQueryParams()['id'] ?? 0;

        try {
            $dto = $this->userService->findUserById($id);
            $body = json_encode($dto);
            return new Response(200, ['content-type' => 'application/json'], $body);
        } catch (\InvalidArgumentException $e) {
            return new Response(400, [], json_encode(['error' => $e->getMessage()]));
        }
    }
}
<?php

namespace App\Service;

use App\Repository\UserRepository;
use App\Domain\UserDTO;

final class UserService
{
    public function __construct(private UserRepository $repo) {}

    public function findUserById(int $id): UserDTO
    {
        if ($id <= 0) {
            throw new \InvalidArgumentException('ID inválido');
        }

        $row = $this->repo->find($id);
        if ($row === null) {
            throw new \RuntimeException('Usuario no encontrado');
        }

        return new UserDTO(id: $row['id'], name: $row['name'], email: $row['email']);
    }
}
<?php

namespace App\Domain;

readonly class UserDTO
{
    public function __construct(
        public int $id,
        public string $name,
        public string $email
    ) {}
}

5. Acceso a base de datos seguro: PDO y consultas preparadas

<?php

// Repository simple con PDO
final class UserRepository
{
    public function __construct(private \PDO $pdo) {}

    public function find(int $id): ?array
    {
        $stmt = $this->pdo->prepare('SELECT id, name, email FROM users WHERE id = :id');
        $stmt->execute([':id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row === false ? null : $row;
    }
}

Por qué: evita inyección SQL y permite statements reusables.

6. Seguridad esencial

  • Escapa siempre la salida: htmlspecialchars(..., ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8').
  • Protege contra CSRF: tokens por sesión/usuario para formularios/requests state-changing.
  • Contraseñas: password_hash() y password_verify(), nunca MD5/SHA sin salt manual.
  • No uses unserialize() en datos no confiables; prefiere json_decode() o libs seguras.
  • Valida entradas en la capa de dominio usando allowlists y filtros; no confíes en el front-end.
  • Cabeceras: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options.
  • Desactiva display_errors en producción; usa logging y monitoreo.

7. Manejo de errores y logging

Usa excepciones específicas y un middleware/global handler que transforme excepciones a respuestas HTTP seguras. Registra con Monolog y centraliza la política de niveles (DEBUG, INFO, WARNING, ERROR).

8. Testing

Unit tests con PHPUnit y pruebas funcionales/HTTP con frameworks o herramientas como Symfony HttpClient o PestPHP.

// Ejemplo básico PHPUnit
final class UserServiceTest extends \PHPUnit\Framework\TestCase
{
    public function testFindUserByIdThrowsOnInvalid(): void
    {
        $repo = $this->createMock(\App\Repository\UserRepository::class);

        $service = new \App\Service\UserService($repo);

        $this->expectException(\InvalidArgumentException::class);
        $service->findUserById(0);
    }
}

9. Rendimiento y optimización

  • Activa OPcache en producción (valor más efectivo por bajo coste).
  • Cachea respuestas y consultas frecuentes con Redis o Memcached.
  • Evita N+1 en consultas; usa joins o repositorios optimizados.
  • Profiling: Xdebug, Blackfire o Tideways para localizar hotspots.
  • Reduce latencia externa con timeouts, retries y circuit breakers.
  • Considera precompilar rutas/mapeos y usar JIT solo si tu carga lo beneficia.

10. CI/CD y análisis estático

  • PHPStan o Psalm para análisis estático. Nivel 7+ en PHPStan atrapa muchos bugs antes de ejecutar.
  • PHPCS/PSR-12 para estilo. Git hooks con pre-commit para formateo automático.
  • Pipeline básico: composer install --no-dev, phpstan analyse, phpunit, build artefacto y desplegar.

11. Despliegue: Docker minimal

# docker/php-fpm/Dockerfile
FROM php:8.1-fpm
RUN apt-get update && apt-get install -y zlib1g-dev git zip unzip
RUN docker-php-ext-install pdo_mysql
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
# docker-compose.yml (extracto)
version: '3.8'
services:
  app:
    build: ./docker/php-fpm
    volumes:
      - ./:/var/www
    environment:
      - APP_ENV=production
  web:
    image: nginx:alpine
    ports:
      - '80:80'
    volumes:
      - ./public:/var/www/public:ro
      - ./docker/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: root

12. Buenas prácticas rápidas

  1. Prefiere tipos (param/return) y readonly cuando tenga sentido.
  2. Usa DTOs/Value Objects para modelar datos, no arrays asociativos en todo el código.
  3. Inyección de dependencias en constructor; evita Singletons globales.
  4. Aplica principio SOLID y separación de responsabilidades.
  5. Centraliza configuración por entorno (.env + secrets manager en producción).
  6. Documenta contratos (interfaces) y escribe tests para ellos.
  7. Monitorea métricas: latencia, errores 5xx, tasa de respuestas cacheadas.

13. Herramientas recomendadas

  • Composer, PHPStan, Psalm, PHPUnit, PHPCS, Rector (refactorización automática).
  • Monolog para logging; Sentry o Bugsnag para error tracking.
  • Redis para caching, queues y sessions cuando haga falta.
  • Docker + docker-compose para entornos reproducibles.

Empieza migrando una pieza a la vez: añade tipado, luego análisis estático, después tests y finalmente caching. Si estás listo para el siguiente paso, integra PHPStan/ Psalm en tu pipeline y sube el nivel gradualmente: cada fallo que corriges ahora evita problemas en producción.

Consejo avanzado: complementa PHPStan con un conjunto de reglas personalizadas que reflejen tus invariantes de dominio (por ejemplo: ciertos servicios siempre devuelven DTOs, ciertas clases no deben depender de infraestructura). Esto detecta violaciones arquitectónicas antes de que lleguen al main branch.

Comentarios
¿Quieres comentar?

Inicia sesión con Telegram para participar en la conversación


Comentarios (0)

Aún no hay comentarios. ¡Sé el primero en comentar!

Iniciar Sesión