├── .gitignore ├── docker-compose.yml ├── src ├── Item.php └── GildedRose.php ├── composer.json ├── Dockerfile ├── phpunit.xml ├── example.php ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | /vendor/ 3 | composer.lock 4 | 5 | # PHPUnit 6 | .phpunit.result.cache 7 | /coverage/ 8 | 9 | # IDE 10 | .idea/ 11 | .vscode/ 12 | *.swp 13 | *.swo 14 | *~ 15 | 16 | # OS 17 | .DS_Store 18 | Thumbs.db 19 | 20 | # Docker 21 | .docker/ 22 | 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | php: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | container_name: gilded-rose-php 9 | volumes: 10 | - .:/app 11 | working_dir: /app 12 | stdin_open: true 13 | tty: true 14 | -------------------------------------------------------------------------------- /src/Item.php: -------------------------------------------------------------------------------- 1 | name = $name; 14 | $this->sellIn = $sellIn; 15 | $this->quality = $quality; 16 | } 17 | 18 | public function __toString(): string 19 | { 20 | return "{$this->name}, {$this->sellIn}, {$this->quality}"; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gilded-rose/kata", 3 | "description": "Gilded Rose Kata in PHP", 4 | "type": "project", 5 | "require": { 6 | "php": ">=8.2" 7 | }, 8 | "require-dev": { 9 | "phpunit/phpunit": "^10.0" 10 | }, 11 | "autoload": { 12 | "psr-4": { 13 | "GildedRose\\": "src/" 14 | } 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "GildedRose\\Tests\\": "tests/" 19 | } 20 | }, 21 | "scripts": { 22 | "test": "phpunit" 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-cli 2 | 3 | # Install system dependencies 4 | RUN apt-get update && apt-get install -y \ 5 | git \ 6 | unzip \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | # Install Composer 10 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 11 | 12 | # Set working directory 13 | WORKDIR /app 14 | 15 | # Copy composer files 16 | COPY composer.json ./ 17 | 18 | # Install PHP dependencies 19 | RUN composer install --no-interaction --prefer-dist --optimize-autoloader 20 | 21 | # Copy application files 22 | COPY . . 23 | 24 | CMD ["bash"] 25 | 26 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | tests 11 | 12 | 13 | 14 | 15 | src 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | updateQuality(); 35 | } 36 | 37 | echo "Ejecución completada con éxito! ✓\n"; 38 | 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help up down build shell test example install clean 2 | 3 | # Colors for output 4 | BLUE := \033[0;34m 5 | GREEN := \033[0;32m 6 | YELLOW := \033[0;33m 7 | NC := \033[0m # No Color 8 | 9 | help: ## Muestra esta ayuda 10 | @echo "$(BLUE)Gilded Rose - Comandos disponibles:$(NC)" 11 | @echo "" 12 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-15s$(NC) %s\n", $$1, $$2}' 13 | @echo "" 14 | 15 | up: ## Levanta el contenedor de Docker 16 | @echo "$(BLUE)Levantando contenedor...$(NC)" 17 | docker-compose up -d 18 | @echo "$(GREEN)✓ Contenedor levantado$(NC)" 19 | 20 | down: ## Detiene y elimina el contenedor 21 | @echo "$(BLUE)Deteniendo contenedor...$(NC)" 22 | docker-compose down 23 | @echo "$(GREEN)✓ Contenedor detenido$(NC)" 24 | 25 | build: ## Construye la imagen de Docker 26 | @echo "$(BLUE)Construyendo imagen...$(NC)" 27 | docker-compose build 28 | @echo "$(GREEN)✓ Imagen construida$(NC)" 29 | 30 | shell: ## Abre una terminal dentro del contenedor 31 | @echo "$(BLUE)Abriendo shell en el contenedor...$(NC)" 32 | docker-compose exec php bash 33 | 34 | test: ## Ejecuta los tests con PHPUnit 35 | @echo "$(BLUE)Ejecutando tests...$(NC)" 36 | docker-compose exec php vendor/bin/phpunit 37 | @echo "$(GREEN)✓ Tests completados$(NC)" 38 | 39 | example: ## Ejecuta el ejemplo de funcionamiento 40 | @echo "$(BLUE)Ejecutando ejemplo...$(NC)" 41 | @echo "" 42 | docker-compose exec php php example.php 43 | 44 | install: ## Instala las dependencias de Composer 45 | @echo "$(BLUE)Instalando dependencias...$(NC)" 46 | docker-compose exec php composer install 47 | @echo "$(GREEN)✓ Dependencias instaladas$(NC)" 48 | 49 | clean: down ## Limpia contenedores, volúmenes e imágenes 50 | @echo "$(BLUE)Limpiando recursos de Docker...$(NC)" 51 | docker-compose down -v --rmi local 52 | @echo "$(GREEN)✓ Limpieza completada$(NC)" 53 | 54 | -------------------------------------------------------------------------------- /src/GildedRose.php: -------------------------------------------------------------------------------- 1 | items = $items; 18 | } 19 | 20 | public function updateQuality(): void 21 | { 22 | foreach ($this->items as $item) { 23 | if ($item->name != 'Aged Brie' && $item->name != 'Backstage passes to a TAFKAL80ETC concert') { 24 | if ($item->quality > 0) { 25 | if ($item->name != 'Sulfuras, Hand of Ragnaros') { 26 | $item->quality = $item->quality - 1; 27 | } 28 | } 29 | } else { 30 | if ($item->quality < 50) { 31 | $item->quality = $item->quality + 1; 32 | if ($item->name == 'Backstage passes to a TAFKAL80ETC concert') { 33 | if ($item->sellIn < 11) { 34 | if ($item->quality < 50) { 35 | $item->quality = $item->quality + 1; 36 | } 37 | } 38 | if ($item->sellIn < 6) { 39 | if ($item->quality < 50) { 40 | $item->quality = $item->quality + 1; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | 47 | if ($item->name != 'Sulfuras, Hand of Ragnaros') { 48 | $item->sellIn = $item->sellIn - 1; 49 | } 50 | 51 | if ($item->sellIn < 0) { 52 | if ($item->name != 'Aged Brie') { 53 | if ($item->name != 'Backstage passes to a TAFKAL80ETC concert') { 54 | if ($item->quality > 0) { 55 | if ($item->name != 'Sulfuras, Hand of Ragnaros') { 56 | $item->quality = $item->quality - 1; 57 | } 58 | } 59 | } else { 60 | $item->quality = $item->quality - $item->quality; 61 | } 62 | } else { 63 | if ($item->quality < 50) { 64 | $item->quality = $item->quality + 1; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gilded Rose Kata - PHP 2 | 3 | ## ¿En qué consiste? 4 | 5 | Bienvenido a **Gilded Rose**, una pequeña posada con una ubicación privilegiada en una ciudad prominente, regentada por la amable posadera Allison. También compra y vende mercancías de la mejor calidad. 6 | 7 | Desafortunadamente, los productos van bajando de calidad a medida que se aproxima su fecha de venta. Tienes un sistema que actualiza el inventario automáticamente. Fue desarrollado por un tipo razonable llamado Leeroy, que ahora se dedica a nuevas aventuras. 8 | 9 | Tu tarea es **añadir una nueva funcionalidad** al sistema, pero hay un problema: el código es un desastre de `ifs` anidados que nadie entiende bien. Además, hay un goblin en la esquina que te apuñalará si tocas la clase `Item`. 10 | 11 | ### El sistema 12 | 13 | Cada artículo tiene tres propiedades: 14 | - **name**: Nombre del artículo 15 | - **sellIn**: Días restantes para venderlo 16 | - **quality**: Valor de calidad (0-50) 17 | 18 | ### Reglas del negocio: 19 | 20 | 1. Al final de cada día, `sellIn` y `quality` disminuyen 21 | 2. Pasada la fecha de venta (`sellIn < 0`), la calidad se degrada el doble de rápido 22 | 3. La calidad nunca es negativa ni mayor a 50 23 | 4. **"Aged Brie"** incrementa su calidad con el tiempo (mejora como el vino) 24 | 5. **"Sulfuras"** nunca cambia (es un artículo legendario) 25 | 6. **"Backstage passes"** (pases para conciertos): 26 | - +2 calidad cuando quedan ≤10 días 27 | - +3 calidad cuando quedan ≤5 días 28 | - Calidad = 0 después del concierto (nadie los quiere) 29 | 30 | ### Tu misión: 31 | 32 | Refactorizar el código legacy para que sea mantenible y añadir soporte para artículos **"Conjured"** (conjurados), que degradan su calidad el doble de rápido que los artículos normales. 33 | 34 | ⚠️ **RESTRICCIÓN: NO modificar la clase `Item`** (el goblin te está vigilando) 35 | 36 | ## Cómo empezar 37 | 38 | ```bash 39 | # 1. Clonar y entrar 40 | git clone 41 | cd gilded-rose-php 42 | 43 | # 2. Levantar Docker 44 | make up 45 | 46 | # 3. Ejecutar el ejemplo 47 | make example 48 | 49 | # 4. Ejecutar los tests 50 | make test 51 | 52 | # 5. Cuando termines 53 | make down 54 | ``` 55 | 56 | ## Comandos disponibles 57 | 58 | El proyecto incluye un `Makefile` con los siguientes comandos: 59 | 60 | | Comando | Descripción | 61 | |---------|-------------| 62 | | `make help` | Muestra todos los comandos disponibles | 63 | | `make up` | Levanta el contenedor de Docker | 64 | | `make down` | Detiene y elimina el contenedor | 65 | | `make build` | Construye la imagen de Docker | 66 | | `make shell` | Abre una terminal dentro del contenedor | 67 | | `make test` | Ejecuta los tests con PHPUnit | 68 | | `make example` | Ejecuta el ejemplo de funcionamiento | 69 | | `make install` | Instala las dependencias de Composer | 70 | | `make clean` | Limpia contenedores, volúmenes e imágenes | 71 | 72 | ### Flujo de trabajo típico 73 | 74 | ```bash 75 | # Iniciar el proyecto 76 | make up 77 | 78 | # Ver el ejemplo funcionando 79 | make example 80 | 81 | # Ejecutar los tests 82 | make test 83 | 84 | # Entrar al contenedor para trabajar 85 | make shell 86 | 87 | # Cuando termines 88 | make down 89 | ``` 90 | --------------------------------------------------------------------------------