# Usar contenedores para el desarrollo de PHP


## Requisitos previos

Completa [Contenedorizar una aplicación PHP](/guides/php/develop/containerize/).

## Descripción general

En esta sección, aprenderás cómo configurar un entorno de desarrollo para tu aplicación contenedorizada. Esto incluye:

- Agregar una base de datos local y persistir datos
- Agregar phpMyAdmin para interactuar con la base de datos
- Configurar Compose para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código
- Crear un contenedor de desarrollo que contenga las dependencias de desarrollo

## Agregar una base de datos local y persistir datos

Puedes usar contenedores para configurar servicios locales, como una base de datos.
Para hacer esto en la aplicación de ejemplo, deberás hacer lo siguiente:

- Actualizar el `Dockerfile` para instalar extensiones para conectarse a la base de datos
- Actualizar el archivo `compose.yaml` para agregar un servicio de base de datos y un volumen para persistir datos

### Actualizar el Dockerfile para instalar extensiones

Para instalar extensiones de PHP, necesitas actualizar el `Dockerfile`. Abre tu Dockerfile en un IDE o editor de texto y luego actualiza su contenido. El siguiente `Dockerfile` incluye una nueva línea que instala las extensiones `pdo` y `pdo_mysql`. Se han eliminado todos los comentarios.

```dockerfile {hl_lines=11}
# syntax=docker/dockerfile:1

FROM composer:lts as deps
WORKDIR /app
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM php:8.2-apache as final
RUN docker-php-ext-install pdo pdo_mysql
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=deps app/vendor/ /var/www/html/vendor
COPY ./src /var/www/html
USER www-data
```

Para obtener más detalles sobre la instalación de extensiones de PHP, consulta la [Imagen oficial de Docker para PHP](https://hub.docker.com/_/php).

### Actualizar el archivo compose.yaml para agregar una base de datos y persistir datos

Abre el archivo `compose.yaml` en un IDE o editor de texto. Notarás que ya contiene instrucciones comentadas para una base de datos PostgreSQL y un volumen. Para esta aplicación, utilizarás MariaDB. Para obtener más detalles sobre MariaDB, consulta la [Imagen oficial de Docker para MariaDB](https://hub.docker.com/_/mariadb).

Abre el archivo `src/database.php` en un IDE o editor de texto. Notarás que lee variables de entorno para conectarse a la base de datos.

En el archivo `compose.yaml`, deberás actualizar lo siguiente:

1. Descomentar y actualizar las instrucciones de la base de datos para MariaDB.
2. Agregar un secreto al servicio del servidor para pasar la contraseña de la base de datos.
3. Agregar las variables de entorno de conexión a la base de datos al servicio del servidor.
4. Descomentar las instrucciones del volumen para persistir datos.

El siguiente es el archivo `compose.yaml` actualizado. Se han eliminado todos los comentarios.

```yaml
services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt
```

> [!NOTE]
>
> Para obtener más información sobre las instrucciones en el archivo de Compose, consulta la [referencia del archivo Compose](/reference/compose-file/).

Antes de ejecutar la aplicación usando Compose, ten en cuenta que este archivo de Compose utiliza `secrets` y especifica un archivo `password.txt` para contener la contraseña de la base de datos. Debes crear este archivo, ya que no está incluido en el repositorio de origen.

En el directorio `docker-php-sample`, crea un nuevo directorio llamado `db` y dentro de ese directorio crea un archivo llamado `password.txt`. Abre `password.txt` en un IDE o editor de texto y agrega la siguiente contraseña. La contraseña debe estar en una sola línea, sin líneas adicionales en el archivo.

```text
example
```

Guarda y cierra el archivo `password.txt`.

Ahora deberías tener lo siguiente en tu directorio `docker-php-sample`.

```text
├── docker-php-sample/
│ ├── .git/
│ ├── db/
│ │ └── password.txt
│ ├── src/
│ ├── tests/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── composer.json
│ ├── composer.lock
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.md
```

Ejecuta el siguiente comando para iniciar tu aplicación.

```console
$ docker compose up --build
```

Abre un navegador y visita la aplicación en [http://localhost:9000/database.php](http://localhost:9000/database.php). Deberías ver una aplicación web sencilla con texto y un contador que se incrementa cada vez que actualizas la página.

Presiona `ctrl+c` en la terminal para detener tu aplicación.

## Verificar que los datos persisten en la base de datos

En la terminal, ejecuta `docker compose rm` para eliminar tus contenedores y luego ejecuta `docker compose up` para ejecutar tu aplicación nuevamente.

```console
$ docker compose rm
$ docker compose up --build
```

Actualiza [http://localhost:9000/database.php](http://localhost:9000/database.php) en tu navegador y verifica que el conteo anterior aún exista. Sin un volumen, los datos de la base de datos no persistirían después de eliminar el contenedor.

Presiona `ctrl+c` en la terminal para detener tu aplicación.

## Agregar phpMyAdmin para interactuar con la base de datos

Puedes agregar servicios fácilmente a tu pila de aplicaciones actualizando el archivo `compose.yaml`.

Actualiza tu `compose.yaml` para agregar un nuevo servicio para phpMyAdmin. Para obtener más detalles, consulta la [Imagen oficial de Docker para phpMyAdmin](https://hub.docker.com/_/phpmyadmin). El siguiente es el archivo `compose.yaml` actualizado.

```yaml {hl_lines="42-49"}
services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
  phpmyadmin:
    image: phpmyadmin
    ports:
      - 8080:80
    depends_on:
      - db
    environment:
      - PMA_HOST=db
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt
```

En la terminal, ejecuta `docker compose up` para ejecutar tu aplicación nuevamente.

```console
$ docker compose up --build
```

Abre [http://localhost:8080](http://localhost:8080) en tu navegador para acceder a phpMyAdmin. Inicia sesión usando `root` como nombre de usuario y `example` como contraseña. Ahora puedes interactuar con la base de datos a través de phpMyAdmin.

Presiona `ctrl+c` in the terminal to stop your application.

## Actualizar servicios automáticamente

Usa Compose Watch para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código. Para obtener más detalles sobre Compose Watch, consulta [Usar Compose Watch](/compose/how-tos/file-watch/).

Abre tu archivo `compose.yaml` en un IDE o editor de texto y luego agrega las instrucciones de Compose Watch. El siguiente es el archivo `compose.yaml` actualizado.

```yaml {hl_lines="17-21"}
services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
    develop:
      watch:
        - action: sync
          path: ./src
          target: /var/www/html
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
  phpmyadmin:
    image: phpmyadmin
    ports:
      - 8080:80
    depends_on:
      - db
    environment:
      - PMA_HOST=db
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt
```

Ejecuta el siguiente comando para ejecutar tu aplicación con Compose Watch.

```console
$ docker compose watch
```

Abre un navegador y verifica que la aplicación se esté ejecutando en [http://localhost:9000/hello.php](http://localhost:9000/hello.php).

Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará inmediatamente en el contenedor en ejecución.

Abre `hello.php` en un IDE o editor de texto y actualiza la cadena `Hello, world!` a `Hello, Docker!`.

Guarda los cambios en `hello.php` y espera unos segundos para que la aplicación se sincronice. Actualiza [http://localhost:9000/hello.php](http://localhost:9000/hello.php) en tu navegador y verifica que aparezca el texto actualizado.

Presiona `ctrl+c` en la terminal para detener Compose Watch. Ejecuta `docker compose down` en la terminal para detener la aplicación.

## Crear un contenedor de desarrollo

En este punto, cuando ejecutas tu aplicación contenedorizada, Composer no está instalando las dependencias de desarrollo. Si bien esta imagen pequeña es buena para producción, carece de las herramientas y dependencias que puedes necesitar al desarrollar y no incluye el directorio `tests`. Puedes utilizar construcciones multietapa para construir etapas tanto para desarrollo como para producción en el mismo Dockerfile. Para obtener más detalles, consulta [Construcciones multietapa](/build/building/multi-stage/).

En el `Dockerfile`, deberás actualizar lo siguiente:

1. Dividir la etapa `deps` en dos etapas. Una etapa para producción (`prod-deps`) y una etapa (`dev-deps`) para instalar dependencias de desarrollo.
2. Crear una etapa `base` común.
3. Crear una nueva etapa `development` para desarrollo.
4. Actualizar la etapa `final` para copiar dependencias de la nueva etapa `prod-deps`.

El siguiente es el `Dockerfile` antes y después de los cambios.

**Antes**



```dockerfile
# syntax=docker/dockerfile:1

FROM composer:lts as deps
WORKDIR /app
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM php:8.2-apache as final
RUN docker-php-ext-install pdo pdo_mysql
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=deps app/vendor/ /var/www/html/vendor
COPY ./src /var/www/html
USER www-data
```

**Después**



```dockerfile
# syntax=docker/dockerfile:1

FROM composer:lts as prod-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM composer:lts as dev-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-interaction

FROM php:8.2-apache as base
RUN docker-php-ext-install pdo pdo_mysql
COPY ./src /var/www/html

FROM base as development
COPY ./tests /var/www/html/tests
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY --from=dev-deps app/vendor/ /var/www/html/vendor

FROM base as final
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=prod-deps app/vendor/ /var/www/html/vendor
USER www-data
```



Actualiza tu archivo `compose.yaml` agregando una instrucción para apuntar a la etapa de desarrollo.

La siguiente es la sección actualizada del archivo `compose.yaml`.

```yaml {hl_lines=5}
services:
  server:
    build:
      context: .
      target: development
      # ...
```

Tu aplicación contenedorizada ahora instalará las dependencias de desarrollo.

Ejecuta el siguiente comando para iniciar tu aplicación.

```console
$ docker compose up --build
```

Abre un navegador y visita la aplicación en [http://localhost:9000/hello.php](http://localhost:9000/hello.php). Deberías seguir ver la aplicación simple "Hello, Docker!".

Presiona `ctrl+c` en la terminal para detener tu aplicación.

Aunque la aplicación se vea igual, ahora puedes hacer uso de las dependencias de desarrollo. Continúa con la siguiente sección para aprender cómo puedes ejecutar pruebas usando Docker.

## Resumen

En esta sección, revisamos cómo configurar tu archivo de Compose para agregar una base de datos local y persistir datos. También aprendiste cómo usar Compose Watch para sincronizar automáticamente tu aplicación al actualizar tu código. Y finalmente, aprendiste cómo crear un contenedor de desarrollo que contenga las dependencias necesarias para el desarrollo.

Información relacionada:

- [Referencia del archivo Compose](/reference/compose-file/)
- [Sincronización de archivos en Compose (Watch)](/compose/how-tos/file-watch/)
- [Referencia de Dockerfile](/reference/dockerfile/)
- [Imagen oficial de Docker para PHP](https://hub.docker.com/_/php)

## Pasos siguientes

En la siguiente sección, aprenderás cómo ejecutar pruebas unitarias usando Docker.

