miguelex/php-template
php-template
Plantilla base para proyectos PHP 8.2–8.5 con arquitectura MVC, tooling moderno y soporte para desarrollo con agentes IA.
Novedades v2.0
- 🐘 Compatibilidad PHP 8.2 → 8.5 — CI probado en 8.2, 8.3, 8.4 y 8.5.
- 🔧 Rector con set
php85+ sets de calidad (composer rector/rector:fix) para modernizar y detectar deprecaciones. - 🧭 Router con parámetros de ruta (
/users/{id}) y middleware por ruta encadenable. - 🛡 Blindaje SQL — validación de identificadores/
ORDER BYenActiveRecordyBaseRepository(los valores ya iban parametrizados). - 🔐 CSRF también vía cabecera
X-CSRF-Token(APIs/fetch), escape de emails y fix de hidratación de columnasNULL. - 📈 PHPStan 2.x y
.gitattributes(LF forzado, fin de los falsos fallos de lint en Windows). - 📦 Publicado en Packagist:
composer create-project miguelex/php-template mi-proyecto.
¿Qué incluye?
🏗 Arquitectura MVC
- Router — rutas GET/POST/PUT/PATCH/DELETE, parámetros
{id}, middleware global y por ruta,_methodoverride, manejo de 404/500 - ActiveRecord — ORM ligero sobre PDO con prepared statements,
find,where,save,delete, validación, alertas y validación de identificadores SQL - BaseRepository — patrón Repository con paginación, transacciones y
findWhere - Database — PDO multi-driver: MySQL, SQLite y PostgreSQL (DSN por
DB_CONNECTION) - Log — logging PSR-3 con Monolog (
Log::info(),Log::error()) astorage/logs/ - Whoops — páginas de error enriquecidas en desarrollo
- EmailService — envío SMTP vía PHPMailer con templates HTML base (con escape)
- Html helper — escape seguro, CSRF (campo o cabecera
X-CSRF-Token), alertas en vista, dump de debug
✅ Calidad de código PHP
- PHPStan 2.x nivel 6 — análisis estático
- Rector — modernización automática y readiness de versión (set
php85) - PHP_CodeSniffer — PSR-12
- PHP-CS-Fixer — autocorrección de estilo
- PHPUnit 11 + Pest 3 — tests unitarios, integración y feature
⚡ Frontend (modo fullstack)
Dos opciones de bundler, elige la que prefieras:
| Gulp | Vite | |
|---|---|---|
| Ideal para | JS vanilla, proyectos sin módulos | Proyectos con imports/exports, HMR |
| SCSS → CSS | ✅ | ✅ |
| Imágenes WebP + optimización | ✅ | ✅ (plugin) |
| BrowserSync / HMR | BrowserSync | HMR nativo |
| Configuración | gulpfile.js |
vite.config.js |
Linting y tests front:
- ESLint + Stylelint
- Vitest — tests unitarios JS
- Playwright — tests E2E
🤖 Preparado para agentes IA
Ficheros .agent/ leídos automáticamente por Claude Code, Cursor y similares:
AGENTS.md— permisos, restricciones, comandos disponiblesCONVENTIONS.md— PSR-12, BEM, Conventional CommitsPROJECT.md— contexto de negocio (rellenar por proyecto)TASKS.md— backlog de tareas
🔄 CI/CD
GitHub Actions con dos jobs paralelos: PHP QA (PHPStan + Rector + PHPCS + PHPUnit + Pest, matriz PHP 8.2/8.3/8.4/8.5) y Front QA (ESLint + Stylelint + Vitest + Playwright).
Además: Dependabot (Composer + npm + Actions), release-please (versionado + CHANGELOG + release automáticos desde Conventional Commits) y tests de arquitectura (pest-arch). La base de datos se prueba de verdad con SQLite en memoria.
Requisitos
| Herramienta | Versión mínima | Notas |
|---|---|---|
| PHP | 8.2 (soporta hasta 8.5) | php --version |
| Composer | 2.x | composer --version |
| Node.js | 20 LTS | Solo modo fullstack |
| npm | 10+ | Solo modo fullstack |
| Git | cualquiera |
Windows
- Instalar PHP desde windows.php.net o via Laragon / XAMPP
- Instalar Node.js desde nodejs.org
- Añadir PHP y Composer al PATH
- El script
dev.shrequiere WSL o Git Bash; alternativamente usarnpm run devdirectamente
WSL (Windows Subsystem for Linux)
# PHP 8.2
sudo apt update && sudo apt install php8.2 php8.2-mbstring php8.2-xml php8.2-curl
# Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Node.js via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20 && nvm use 20
macOS
brew install php composer node@20
Linux (Ubuntu/Debian)
sudo apt install php8.2 php8.2-mbstring php8.2-xml php8.2-curl
composer # desde getcomposer.org
nvm install 20
Crear un nuevo proyecto
Opción A — composer create-project (más rápido)
composer create-project miguelex/php-template mi-proyecto
cd mi-proyecto
Descarga la última versión estable desde Packagist sin la historia de git de la plantilla. Luego ajusta el name de composer.json e inicializa tu propio repositorio.
Opción B — Clonar y usar el script init-project.sh
git clone https://github.com/miguelex/php-template.git
cd php-template
./init-project.sh
El script es interactivo y pregunta:
- Nombre del proyecto (se usará como nombre de directorio y en composer/package.json)
- Modo:
backend-onlyofullstack
También acepta argumentos directos:
./init-project.sh mi-api backend # API PHP sin front
./init-project.sh mi-web full # Web completa con assets
El script genera ./mi-proyecto/ con:
- Git inicializado (commit inicial limpio, sin historia de la plantilla)
- Ficheros personalizados con el nombre del proyecto
- Modo
backendelimina automáticamente gulpfile, vite.config, tests/front, etc.
Opción C — Fork en GitHub
- Hacer fork del repositorio
- Clonar tu fork:
git clone https://github.com/TU_USER/php-template.git mi-proyecto - Eliminar la historia:
rm -rf .git && git init && git add . && git commit -m "chore: init" - Ajustar manualmente
composer.jsonypackage.json
Instalación (tras crear el proyecto)
cd mi-proyecto
# Dependencias PHP (copia .env.example a .env automáticamente)
composer install
# Dependencias Node (solo fullstack)
npm install
# Instalar navegadores para Playwright (solo fullstack)
npx playwright install chromium
Editar .env con los valores reales del proyecto:
APP_NAME="Mi Proyecto"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:8000
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=mi_base_de_datos
DB_USERNAME=root
DB_PASSWORD=
Multi-BD:
DB_CONNECTIONaceptamysql,sqliteopgsql. Para SQLite bastaDB_CONNECTION=sqliteyDB_DATABASE=storage/database.sqlite(o:memory:), sin host ni credenciales. El ORM usa SQL portable; las migraciones que escribas sí son específicas de tu motor.
Desarrollo
Arrancar el entorno
Opción 1 — concurrently (recomendado en Windows/WSL sin tmux)
# Vite + PHP server en una sola terminal (por defecto)
npm run dev
# Gulp + PHP server (alternativa para JS vanilla)
npm run dev:gulp
# Solo servidor PHP (backend-only)
npm run php
Cada proceso aparece con prefijo de color: [PHP] en azul, [GULP]/[VITE] en verde/amarillo.
Opción 2 — dev.sh con tmux (Linux/macOS/WSL)
./dev.sh # Gulp + PHP (paneles separados en tmux)
./dev.sh --bundler=vite # Vite + PHP
./dev.sh --php-only # Solo PHP (backend)
./dev.sh --tests # Gulp + PHP + Vitest watch
Atajos tmux: Ctrl+B → flechas para moverse entre paneles · Ctrl+B → D para desconectar · tmux attach -t dev para reconectar.
Opción 3 — Terminales manuales
# Terminal 1
php -S localhost:8000 -t public
# Terminal 2 (fullstack Gulp)
npx gulp watch
# Terminal 2 (fullstack Vite)
npx vite
Compilar assets
# Desarrollo (sourcemaps, sin minificar)
npm run build # Vite (por defecto)
npm run build:gulp # Gulp
# Producción (minificado, optimizado)
npm run build:prod # Vite (por defecto)
npm run build:gulp:prod # Gulp
Comandos de calidad
PHP
composer qa # Todo: lint + stan + tests (pasar antes de cada commit)
composer lint # PHPCS — detecta problemas de estilo
composer lint:fix # PHP-CS-Fixer — autocorrige el estilo
composer stan # PHPStan — análisis estático nivel 6
composer rector # Rector — muestra modernizaciones propuestas (dry-run)
composer rector:fix # Rector — aplica las modernizaciones
composer test # PHPUnit
composer test:pest # Pest
composer test:cover # PHPUnit + coverage HTML en storage/coverage/
Front (fullstack)
npm run test # Vitest unit
npm run test:watch # Vitest en modo watch
npm run test:e2e # Playwright E2E (requiere servidor PHP activo)
npm run test:all # Vitest + Playwright
npm run lint:js # ESLint
npm run lint:css # Stylelint
npm run lint:fix # ESLint --fix
npm run format # Prettier
Estructura del proyecto
php-template/
├── .agent/ ← Contexto para agentes IA
│ ├── AGENTS.md ← Instrucciones, permisos, comandos
│ ├── CONVENTIONS.md ← Convenciones de código
│ ├── PROJECT.md ← Contexto de negocio (rellenar)
│ └── TASKS.md ← Backlog de tareas
│
├── .github/workflows/ci.yml ← GitHub Actions (PHP QA + Front QA)
│
├── src/ ← Código fuente PHP
│ ├── Core/
│ │ ├── App.php ← Bootstrap (env, config, BD)
│ │ ├── Router.php ← Router MVC (params + middleware)
│ │ ├── ActiveRecord.php ← ORM base con PDO
│ │ ├── Database.php ← Conexión PDO (MySQL/SQLite/PostgreSQL)
│ │ └── Log.php ← Logging PSR-3 (Monolog)
│ ├── Controllers/ ← HTTP handlers
│ ├── Models/ ← Entidades (extienden ActiveRecord)
│ │ └── User.php ← Modelo de ejemplo
│ ├── Services/
│ │ └── EmailService.php ← SMTP con PHPMailer
│ ├── Helpers/
│ │ └── Html.php ← Escape, CSRF, alertas, dump
│ └── Exceptions/
│ ├── RouteNotFoundException.php
│ └── DatabaseException.php
│
├── views/ ← Vistas PHP
│ ├── layouts/default.php ← Layout base
│ ├── templates/
│ │ ├── header.php
│ │ └── footer.php
│ ├── errors/
│ │ ├── 404.php
│ │ └── 500.php
│ └── home.php ← Vista de ejemplo
│
├── config/
│ └── routes.php ← Definición de rutas
│
├── public/ ← Document root del servidor web
│ ├── index.php ← Entry point
│ └── assets/ ← Generado por Gulp/Vite (no editar)
│
├── resources/ ← Fuentes front (solo fullstack)
│ ├── scss/
│ │ ├── app.scss
│ │ ├── _variables.scss
│ │ ├── _mixins.scss
│ │ ├── _base.scss
│ │ └── components/
│ └── js/
│ ├── app.js
│ └── modules/
│
├── tests/
│ ├── TestCase.php ← Base para PHPUnit/Pest
│ ├── Unit/ ← Tests unitarios PHPUnit
│ ├── Integration/ ← Tests de integración
│ ├── Feature/ ← Tests Pest
│ └── front/ ← Solo fullstack
│ ├── unit/ ← Vitest
│ └── e2e/ ← Playwright
│
├── database/
│ ├── migrations/
│ └── seeders/
│
├── storage/
│ ├── logs/
│ └── cache/
│
├── CHANGELOG.md ← Mantenido por release-please
├── composer.json
├── phpstan.neon ← Nivel 6 (PHPStan 2.x)
├── rector.php ← Rector (set php85 + calidad)
├── phpcs.xml ← PSR-12
├── .php-cs-fixer.php
├── phpunit.xml
├── pest.php
├── gulpfile.js ← Bundler opción A
├── vite.config.js ← Bundler opción B
├── package.json
├── vitest.config.js
├── playwright.config.js
├── eslint.config.js
├── .stylelintrc.json
├── .editorconfig
├── .gitattributes ← Normaliza finales de línea a LF
├── .gitignore
├── .env.example
├── dev.sh ← Lanzador tmux
└── init-project.sh ← Script de inicialización
Decisiones de diseño
¿Por qué sin framework como Laravel/Symfony? Para proyectos donde el overhead de un framework completo no está justificado: módulos de negocio específicos, integraciones con sistemas legacy, o proyectos donde el control fino sobre cada capa es importante.
¿Gulp o Vite?
Ambos están disponibles. Gulp es ideal si el JS es simple (unas funciones, jQuery): pipeline directo, sin módulos, sin bundling. Vite es mejor si empiezas a usar import/export, necesitas HMR instantáneo, o prevés que el front crezca.
¿Por qué PHPStan nivel 6 y no 9? El nivel 9 en un proyecto real con código legacy o integraciones externas genera decenas de falsos positivos. El 6 ofrece el 90% del valor sin el ruido. Se puede subir gradualmente.
ActiveRecord vs Repository
Para proyectos pequeños/medianos ActiveRecord es más directo. Si el dominio crece, el patrón Repository es el paso siguiente: extraer la lógica de acceso a datos de los modelos a clases UserRepository, etc.
Contribuir
¡Las contribuciones son bienvenidas! Esto incluye mejoras al core MVC, nuevas features de tooling, correcciones, documentación o ejemplos adicionales.
Antes de abrir un PR
- Haz fork del repositorio y clona tu fork
- Crea una rama descriptiva:
git checkout -b feature/nombre-corto # o git checkout -b fix/descripcion-del-bug - Asegúrate de que
composer qapasa en verde antes de hacer push - Si añades código PHP, añade también sus tests
- Sigue las convenciones de Conventional Commits:
feat: añadir soporte para rutas con parámetros dinámicos fix: corregir resolución de URI con query string chore: actualizar dependencias Composer docs: mejorar sección de contribución en README test: añadir tests para ActiveRecord::whereMany
Tipos de contribución bienvenidas
- 🐛 Bug fixes — con test que reproduzca el fallo
- ✨ Features — discutir primero en un issue si es algo grande
- 📖 Documentación — mejoras al README, comentarios en código
- 🧪 Tests — más cobertura siempre es bienvenida
- 🌐 Compatibilidad — mejoras para Windows, macOS, distintas versiones de PHP
Tipos de contribución que no encajan
- Añadir dependencias de frameworks completos (Laravel, Symfony, etc.)
- Cambios de estilo sin funcionalidad asociada
- Romper compatibilidad con PHP 8.2
Flujo de un PR
fork → rama → código + tests → composer qa verde → push → PR
En el PR describe:
- Qué hace el cambio
- Por qué es necesario
- Cómo probarlo
Licencia
MIT — ver LICENSE.
Agradecimientos
Inspirado en el mini-framework de codigoconjuan, modernizado con PHP 8.2, PDO, typed properties y tooling actual.
CLI — bin/console
La plantilla incluye una herramienta de línea de comandos para las tareas más comunes:
# Migraciones
php bin/console migrate # ejecutar pendientes
php bin/console migrate:status # ver estado
php bin/console migrate:rollback # deshacer última
php bin/console migrate:rollback 3 # deshacer últimas 3
php bin/console migrate:fresh # rollback todo + migrate
# Generadores de código
php bin/console make:migration create_posts_table
php bin/console make:controller Post
php bin/console make:model Post
php bin/console make:repository Post
# Otros
php bin/console cache:clear
php bin/console help
O con Make:
make migrate
make migration NAME=create_posts_table
make controller NAME=Post
make model NAME=Post
Rutas y middleware
// config/routes.php
// Ruta simple
$router->get('/', [HomeController::class, 'index']);
// Parámetros de ruta → llegan como 2º argumento asociativo del handler
$router->get('/users/{id}', function (Router $router, array $params): void {
$id = (int) $params['id'];
// ...
});
// Middleware por ruta (encadenable)
$router->get('/dashboard', [DashboardController::class, 'index'])
->middleware(App\Middleware\AuthMiddleware::check(...));
// Middleware global (todas las rutas)
$router->use(App\Middleware\AuthMiddleware::check(...));
Patrones disponibles: ActiveRecord vs Repository
ActiveRecord — para proyectos simples/medianos
// src/Models/Post.php
final class Post extends ActiveRecord {
protected static string $table = 'posts';
protected static array $columns = ['id', 'title', 'body'];
public string $title = '';
public string $body = '';
}
// Uso directo
$post = Post::find(1);
$posts = Post::all();
$post->title = 'Nuevo título';
$post->save();
Repository — para proyectos con dominio complejo
// src/Repositories/PostRepository.php
final class PostRepository extends BaseRepository {
protected string $table = 'posts';
public function findPublished(): array {
return $this->findWhere(['published' => 1], 'created_at DESC');
}
protected function hydrate(array $row): Post { ... }
protected function extract(object $entity): array { ... }
}
// Uso con inyección
$repo = new PostRepository(Database::connect());
$posts = $repo->paginate(page: 1, perPage: 10);
Makefile
make help # ver todos los comandos disponibles
make install # composer install + npm install
make dev # PHP + Gulp (concurrently)
make dev-vite # PHP + Vite (concurrently)
make qa # lint + stan + tests
make test-all # PHP + JS + E2E
make migrate # ejecutar migraciones
make cache-clear # vaciar storage/cache/
En Windows sin WSL, instalar make con:
winget install GnuWin32.Make
# o
choco install make
Sistema de agentes IA — estructura completa
.agent/
├── AGENTS.md ← Punto de entrada: qué es el proyecto, permisos, comandos
├── WORKFLOW.md ← Cómo actuar: planificar, verificar, aprender, elegancia
├── CONVENTIONS.md ← Cómo escribir: PHP, JS, SCSS, Git, nomenclatura
├── PROJECT.md ← Contexto de negocio (rellenar por proyecto)
└── TASKS.md ← Backlog estratégico del proyecto
tasks/
├── todo.md ← Tarea activa: plan + checkboxes + review section
└── lessons.md ← Errores pasados + reglas para no repetirlos
Los agentes que leen AGENTS.md de forma nativa (Claude Code, Cursor en modo agente)
cargan automáticamente todo el contexto. El fichero WORKFLOW.md garantiza que el agente
planifica antes de actuar, hace cambios quirúrgicos, y aprende de los errores del proyecto.
Related Packages
Shared strict coding standards, static analysis rules and tooling for svnjn PHP...
Opinionated ECS, Rector, and PHPStan presets for PHP and Laravel packages and ap...
Version History
| Version | Released | PHP | License |
|---|---|---|---|
| v2.1.2 | ^8.2 | MIT | |
| v2.1.1 | ^8.2 | MIT | |
| v2.1.0 | ^8.2 | MIT | |
| v2.0.0 | ^8.2 | MIT | |
| v1.0.4 | ^8.2 | MIT | |
| v1.0.3 | ^8.2 | MIT | |
| v1.0.2 | ^8.2 | MIT | |
| v1.0.1 | ^8.2 | MIT | |
| v1.0.0 | ^8.2 | MIT |