Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ on:
name: build

jobs:
codeception:
uses: php-forge/actions/.github/workflows/codeception.yml@main
secrets:
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
codeception-command: vendor/bin/codecept run --env php-builtin --coverage-xml
coverage-file: runtime/output/coverage.xml
extensions: gd, intl, pcov
franken-php:
runs-on: ubuntu-latest

steps:
- name: Checkout.
uses: actions/checkout@v5

- name: Build and start containers.
run: docker compose up -d --build --wait

- name: Codeception build.
run: docker exec yii2-frankenphp vendor/bin/codecept build

- name: Codeception tests.
run: docker exec yii2-frankenphp vendor/bin/codecept run
33 changes: 7 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<a href="https://github.com/yiisoft/yii2/tree/22.0" target="_blank">
<img src="https://img.shields.io/badge/22.0.x-0073AA.svg?style=for-the-badge&logo=yii&logoColor=white" alt="Yii 22.0.x">
</a>
<a href="https://github.com/yii2-extensions/app-basic/actions/workflows/build.yml" target="_blank">
<img src="https://img.shields.io/github/actions/workflow/status/yii2-extensions/app-basic/build.yml?branch=main&style=for-the-badge&label=Codeception" alt="Codeception">
<a href="https://github.com/yii2-extensions/app-basic/actions/workflows/build.yml?query=branch%3Afranken-php-classic" target="_blank">
<img src="https://img.shields.io/github/actions/workflow/status/yii2-extensions/app-basic/build.yml?branch=franken-php-classic&style=for-the-badge&label=Codeception" alt="Codeception">
</a>
<a href="https://github.com/yii2-extensions/app-basic/actions/workflows/static.yml" target="_blank">
<img src="https://img.shields.io/github/actions/workflow/status/yii2-extensions/app-basic/static.yml?branch=main&style=for-the-badge&label=PHPStan" alt="PHPStan">
<a href="https://github.com/yii2-extensions/app-basic/actions/workflows/static.yml?query=branch%3Afranken-php-classic" target="_blank">
<img src="https://img.shields.io/github/actions/workflow/status/yii2-extensions/app-basic/static.yml?branch=franken-php-classic&style=for-the-badge&label=PHPStan" alt="PHPStan">
</a>
</p>

Expand All @@ -40,24 +40,6 @@ A modern, Bootstrap 5-powered Yii2 application template designed for rapid web-a
- ✅ **Modern Bootstrap 5 UI** - Responsive, mobile-first design with latest Bootstrap components.
- ✅ **Testing Ready** - Codeception test suite with examples for functional and unit testing.

## Available deployment options

### Traditional Web Servers

Classic web-server + PHP-FPM setup; simple and widely supported for deployment.

[![Apache](https://img.shields.io/badge/apache-%23D42029.svg?style=for-the-badge&logo=apache&logoColor=white)](https://github.com/yii2-extensions/app-basic/tree/apache)
[![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&logo=nginx&logoColor=white)](https://github.com/yii2-extensions/app-basic/tree/nginx)

### High-Performance Worker Mode

Long-running PHP workers for higher throughput and lower latency.

[![FrankenPHP](https://img.shields.io/badge/FrankenPHP-777BB4?style=for-the-badge&logo=php&logoColor=white)](https://github.com/yii2-extensions/app-basic/tree/franken-php)
[![RoadRunner](https://img.shields.io/badge/RoadRunner-%23FF6B35.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyIDJMMjIgMTJMMTIgMjJMMiAxMkwxMiAyWiIgZmlsbD0iI0ZGRkZGRiIvPgo8cGF0aCBkPSJNOCAyTDE2IDEwTDggMThaIiBmaWxsPSIjRkY2QjM1Ii8+CjxwYXRoIGQ9Ik0xNiA2TDIwIDEwTDE2IDE0WiIgZmlsbD0iI0ZGNkIzNSIvPgo8L3N2Zz4K&logoColor=white)](https://github.com/yii2-extensions/app-basic/tree/road-runner)

> For setup instructions, see `README.md` in each branch.

## How it works

The Yii2 Web Application Basic template provides a complete foundation for building modern web applications. Unlike starting from scratch, this template includes.
Expand Down Expand Up @@ -154,14 +136,13 @@ final class SiteController extends Controller

## Package information

[![Latest Stable Version](https://img.shields.io/packagist/v/yii2-extensions/app-basic.svg?style=for-the-badge&logo=packagist&logoColor=white&label=Stable)](https://packagist.org/packages/yii2-extensions/app-basic)
[![Total Downloads](https://img.shields.io/packagist/dt/yii2-extensions/app-basic.svg?style=for-the-badge&logo=packagist&logoColor=white&label=Downloads)](https://packagist.org/packages/yii2-extensions/app-basic)
[![Development Status](https://img.shields.io/badge/Status-Dev-orange.svg?style=for-the-badge&logo=packagist&logoColor=white)](https://github.com/yii2-extensions/app-basic/tree/franken-php-classic)

## Quality code

[![Codecov](https://img.shields.io/codecov/c/github/yii2-extensions/app-basic.svg?branch=main&style=for-the-badge&logo=codecov&logoColor=white&label=Coverage)](https://codecov.io/github/yii2-extensions/app-basic)
[![PHPStan Level Max](https://img.shields.io/badge/PHPStan-Level%20Max-4F5D95.svg?style=for-the-badge&logo=php&logoColor=white)](https://github.com/yii2-extensions/app-basic/actions/workflows/static.yml)
[![StyleCI](https://img.shields.io/badge/StyleCI-Passed-44CC11.svg?style=for-the-badge&logo=styleci&logoColor=white)](https://github.styleci.io/repos/165419144?branch=main)
[![PHPStan Level Max](https://img.shields.io/badge/PHPStan-Level%20Max-4F5D95.svg?style=for-the-badge&logo=php&logoColor=white)](https://github.com/yii2-extensions/app-basic/actions/workflows/static.yml?query=branch%3Afranken-php-classic)
[![StyleCI](https://img.shields.io/badge/StyleCI-Passed-44CC11.svg?style=for-the-badge&logo=styleci&logoColor=white)](https://github.styleci.io/repos/165419144?branch=franken-php-classic)

## Documentation

Expand Down
1 change: 0 additions & 1 deletion codeception.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ paths:
output: runtime/output
data: tests/Support/data
support: tests/Support
envs: tests/_envs
actor_suffix: Tester
settings:
memory_limit: 1024M
Expand Down
41 changes: 41 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
services:
yii2-frankenphp:
build:
args:
USER_ID: ${USER_ID:-1000}
GROUP_ID: ${GROUP_ID:-1000}
USER_NAME: ${USER_NAME:-www-data}
GROUP_NAME: ${GROUP_NAME:-www-data}
context: .
dockerfile: docker/frankenphp/Dockerfile
container_name: yii2-frankenphp
entrypoint: ["/usr/local/bin/entrypoint.sh"]
env_file:
- .env
environment:
TZ: "UTC"
YII_DEBUG: "${YII_DEBUG:-false}"
YII_ENV: "${YII_ENV:-prod}"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
ports:
- '8080:80'
- '8443:443'
- '8443:443/udp'
restart: always
tty: true
volumes:
- ./:/app
- caddy_config:/config
- caddy_data:/data
- composer_cache:/var/www/.composer/cache
working_dir: /app

volumes:
caddy_data:
caddy_config:
composer_cache:
11 changes: 11 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -euo pipefail

echo "=== Container Starting ==="
echo "Running initialization script..."

# Ensure init script is executable
chmod +x /usr/local/bin/init.sh

# Execute init script; replace the PID 1 shell
exec /usr/local/bin/init.sh
66 changes: 66 additions & 0 deletions docker/frankenphp/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
# Global options
auto_https off
}

# HTTPS server block using mkcert certificates
https://localhost:443 {
# Specify mkcert certificates
tls /app/docker/ssl/localhost.pem /app/docker/ssl/localhost-key.pem

# Document root
root * /app/web

# Enable PHP processing with FrankenPHP
php_server

# Security headers
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), camera=(), microphone=()"
# For prod domains only, consider HSTS; avoid for localhost:
# Strict-Transport-Security "max-age=31536000; includeSubDomains"
-Server
}

# Logging
log {
output stdout
format console
}

encode zstd gzip

# Handle static files
@static {
file
path *.css *.js *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2 *.ttf *.eot
}
handle @static {
header Cache-Control "public, max-age=31536000, immutable"
file_server
}

# Block access to sensitive directories
@forbidden {
path /.git/* /vendor/* /runtime/* /.env*
}
respond @forbidden 404

# Deny PHP execution in assets directory (Yii2 security)
@assets_php {
path /assets/*.php
}
respond @assets_php 403

# Try files for Yii2 URL rewriting
try_files {path} {path}/ /index.php?{query}
}

# HTTP server block - redirect to HTTPS
http://localhost:80 {
# Redirect all HTTP traffic to HTTPS
redir https://localhost:8443{uri} permanent
}
74 changes: 74 additions & 0 deletions docker/frankenphp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
FROM dunglas/frankenphp:1.8-php8.4

# Build arguments for user/group
ARG USER_ID=1000
ARG GROUP_ID=1000
ARG USER_NAME=www-data
ARG GROUP_NAME=www-data

# Change web server config
COPY docker/frankenphp/Caddyfile /etc/caddy/Caddyfile

# Set document root to /app/web (Yii2 structure)
WORKDIR /app

# Install required system packages for PHP extensions for Yii 2.0 Framework
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions \
bcmath \
@composer \
exif \
gd \
imagick \
intl \
opcache \
pdo_mysql \
pdo_pgsql \
soap \
xdebug \
zip

# Set composer environment
ENV COMPOSER_ALLOW_SUPERUSER=1

# Change PHP config
COPY docker/php/php.ini /usr/local/etc/php/conf.d/base.ini

# Install supervisor, gosu, and Node.js (version simple)
RUN apt-get update && apt-get install -y --no-install-recommends \
supervisor \
curl \
gosu \
&& curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Apply the user/group IDs to www-data
RUN usermod -u ${USER_ID} www-data && groupmod -g ${GROUP_ID} www-data

# Create composer and npm cache directories with proper ownership
RUN mkdir -p /var/www/.composer/cache /var/www/.npm && \
chown -R www-data:www-data /var/www/.composer /var/www/.npm

# Copy supervisor program configs
COPY docker/supervisor/conf.d/frankenphp.conf /etc/supervisor/conf.d/frankenphp.conf

# Copy queue worker config uncommented for use with yii2-queue
#COPY docker/supervisor/conf.d/queue.conf /etc/supervisor/conf.d/queue.conf

# Copy scripts
COPY docker/init.sh /usr/local/bin/init.sh
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh

# Make scripts executable and validate
RUN chmod +x /usr/local/bin/init.sh /usr/local/bin/entrypoint.sh && \
# Convert any Windows line endings
sed -i 's/\r$//' /usr/local/bin/init.sh /usr/local/bin/entrypoint.sh && \
# Test that scripts have valid syntax
bash -n /usr/local/bin/init.sh && \
bash -n /usr/local/bin/entrypoint.sh && \
echo "✓ Scripts validated successfully..."

# Use ENTRYPOINT to guarantee execution
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
105 changes: 105 additions & 0 deletions docker/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${GREEN}Starting container setup...${NC}"

# Create necessary Caddy directories with proper permissions
echo -e "${YELLOW}Creating Caddy directories...${NC}"
mkdir -p /data/caddy/locks /config/caddy
chown -R www-data:www-data /data /config
chmod -R 755 /data /config

# Create necessary Yii2 directories if they don't exist
echo -e "${YELLOW}Creating Yii2 directories...${NC}"
mkdir -p /app/runtime/cache
mkdir -p /app/runtime/logs
mkdir -p /app/web/assets

# Configure permissions for Yii2 directories
echo -e "${YELLOW}Setting up permissions...${NC}"

# Try to set permissions and ownership - handle both mounted volumes and container-only scenarios
if chown -R www-data:www-data /app/runtime 2>/dev/null; then
chmod -R 775 /app/runtime
echo -e "${GREEN}✓ Runtime directory configured correctly${NC}"
else
# If chown fails (mounted volume), try chmod only
if chmod -R 777 /app/runtime 2>/dev/null; then
echo -e "${YELLOW}⚠ Runtime directory permissions set to 777 (mounted volume)${NC}"
else
echo -e "${RED}✗ Error: Could not configure runtime directory${NC}"
fi
fi

if chown -R www-data:www-data /app/web/assets 2>/dev/null; then
chmod -R 775 /app/web/assets
echo -e "${GREEN}✓ Assets directory configured correctly${NC}"
else
# If chown fails (mounted volume), try chmod only
if chmod -R 777 /app/web/assets 2>/dev/null; then
echo -e "${YELLOW}⚠ Assets directory permissions set to 777 (mounted volume)${NC}"
else
echo -e "${RED}✗ Error: Could not configure assets directory${NC}"
fi
fi

echo -e "${GREEN}Setup completed.${NC}"

# Check if composer.json exists and vendor directory doesn't exist
if [ -f "/app/composer.json" ] && [ ! -d "/app/vendor" ]; then
echo -e "${YELLOW}Installing Composer dependencies...${NC}"

# Give www-data write access without exposing the tree to everyone
chown -R www-data:www-data /app && \
chmod -R u+rwX,g+rwX /app

# Create and configure npm cache directory for www-data
mkdir -p /var/www/.npm
chown -R www-data:www-data /var/www/.npm

# Create and own Composer home & cache for www-data
mkdir -p /var/www/.composer/cache
chown -R www-data:www-data /var/www/.composer

# Install dependencies with proper environment variables
if [ "$YII_ENV" = "prod" ]; then
# Production: exclude dev dependencies and optimize autoloader
gosu www-data env \
HOME=/var/www \
COMPOSER_HOME=/var/www/.composer \
COMPOSER_CACHE_DIR=/var/www/.composer/cache \
npm_config_cache=/var/www/.npm \
composer install --no-dev --optimize-autoloader --no-interaction
else
# Development: include dev dependencies
gosu www-data env \
HOME=/var/www \
COMPOSER_HOME=/var/www/.composer \
COMPOSER_CACHE_DIR=/var/www/.composer/cache \
npm_config_cache=/var/www/.npm \
composer install --optimize-autoloader --no-interaction
fi

echo -e "${GREEN}✓ Composer dependencies installed successfully${NC}"
fi

# Copy supervisor configuration
echo -e "${YELLOW}Configuring supervisor...${NC}"

if [ -f "/app/docker/supervisor/supervisord.conf" ]; then
cp /app/docker/supervisor/supervisord.conf /etc/supervisor/supervisord.conf
echo -e "${GREEN}✓ Supervisor configuration copied successfully${NC}"
else
echo -e "${RED}✗ Error: Supervisor configuration file not found${NC}"
exit 1
fi

echo -e "${GREEN}Starting supervisor daemon...${NC}"

# Start supervisor daemon
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
Loading
Loading