mirror of
https://github.com/WPTechnix/wp-settings-framework.git
synced 2025-10-03 23:23:25 +08:00
chore: initialize the project
This commit is contained in:
commit
2c543b716c
31 changed files with 6135 additions and 0 deletions
34
.editorconfig
Normal file
34
.editorconfig
Normal file
|
@ -0,0 +1,34 @@
|
|||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Default for all files
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# PHP files (PSR-12)
|
||||
[*.php]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
max_line_length = 120
|
||||
insert_final_newline = true
|
||||
|
||||
# YAML (e.g., PHPStan, ECS)
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# JSON files
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Markdown files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Shell scripts
|
||||
[*.sh]
|
||||
end_of_line = lf
|
29
.gitattributes
vendored
Normal file
29
.gitattributes
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Ensure consistent line endings (LF) across all text files
|
||||
* text=auto eol=lf
|
||||
|
||||
# Force LF in specific file types (for stricter control)
|
||||
*.md text eol=lf
|
||||
*.json text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.neon text eol=lf
|
||||
*.php text eol=lf
|
||||
|
||||
# Exclude from GitHub language stats
|
||||
/.github/** linguist-documentation
|
||||
|
||||
# Exclude from GitHub-generated ZIP archives
|
||||
/tests export-ignore
|
||||
/docker export-ignore
|
||||
/bin export-ignore
|
||||
/.git* export-ignore
|
||||
/.husky export-ignore
|
||||
/package.json export-ignore
|
||||
/examples export-ignore
|
||||
/package-lock.json export-ignore
|
||||
/.release-it.json export-ignore
|
||||
/.editorconfig export-ignore
|
||||
/phpcs.xml.dist export-ignore
|
||||
/phpstan.neon.dist export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
name: Bug Report
|
||||
about: Report a bug or issue
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Bug Description
|
||||
|
||||
A clear description of what the bug is.
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
What you expected to happen.
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
What actually happened.
|
||||
|
||||
## Environment
|
||||
|
||||
- PHP version:
|
||||
- WordPress Version
|
||||
- Library version:
|
||||
|
||||
## Additional Context
|
||||
|
||||
Add any other context about the problem here (error messages, logs, etc.).
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: Feature Request
|
||||
about: Suggest a new feature or improvement
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Feature Description
|
||||
|
||||
A clear description of what you'd like to see added.
|
||||
|
||||
## Use Case
|
||||
|
||||
Explain why this feature would be useful and how you would use it.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
If you have ideas about how this could be implemented, describe them here.
|
||||
|
||||
## Additional Context
|
||||
|
||||
Add any other context and examples about the feature request here.
|
5
.github/SECURITY.md
vendored
Normal file
5
.github/SECURITY.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Reporting a Vulnerability
|
||||
|
||||
If you discover a security-related issue, please report it privately by emailing **[security@wptechnix.com](mailto:security@wptechnix.com)**.
|
||||
|
||||
Avoid disclosing security vulnerabilities publicly.
|
3
.github/pull_request_template.md
vendored
Normal file
3
.github/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
## Description
|
||||
|
||||
<!-- A brief summary of what this PR changes and the reason behind it ("Why" this change is necessary) -->
|
134
.github/workflows/ci.yml
vendored
Normal file
134
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, 'release/**' ]
|
||||
paths-ignore:
|
||||
# ignore markdowns and unrelated files
|
||||
- '**.md'
|
||||
- 'docker/**'
|
||||
- '.husky/**'
|
||||
- '.editorconfig'
|
||||
- '.gitattributes'
|
||||
- '.release-it.json'
|
||||
- 'bin/copy'
|
||||
- 'bin/docker'
|
||||
- 'bin/composer'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/commitlint.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
|
||||
pull_request:
|
||||
branches: [ main, 'release/**' ]
|
||||
paths-ignore:
|
||||
# ignore markdowns and unrelated files
|
||||
- '**.md'
|
||||
- 'docker/**'
|
||||
- '.husky/**'
|
||||
- '.editorconfig'
|
||||
- '.gitattributes'
|
||||
- '.release-it.json'
|
||||
- 'bin/copy'
|
||||
- 'bin/docker'
|
||||
- 'bin/composer'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- '.github/workflows/commitlint.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
|
||||
jobs:
|
||||
# PHPCS - Test with both minimum and maximum PHP versions
|
||||
# This ensures coding standards work with different dependency versions
|
||||
lint:
|
||||
name: Code Style (PHP ${{ matrix.php-version }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: [8.0, 8.4]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
# Cache composer dependencies per PHP version
|
||||
- name: Get Composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-php${{ matrix.php-version }}-composer-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-interaction --prefer-dist --no-progress
|
||||
|
||||
# For PRs: Only run on changed files (faster feedback)
|
||||
- name: Run PHPCS on changed files (Pull Request)
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} -- '*.php' || echo '')
|
||||
|
||||
if [[ -n "$CHANGED_FILES" ]]; then
|
||||
echo "$CHANGED_FILES" | xargs ./vendor/bin/phpcs --report=checkstyle --no-cache > phpcs-report-${{ matrix.php-version }}.xml || true
|
||||
else
|
||||
echo "No PHP files changed. Skipping PHPCS."
|
||||
echo '<checkstyle/>' > phpcs-report-${{ matrix.php-version }}.xml
|
||||
fi
|
||||
|
||||
# Only annotate from one PHP version to avoid duplicate comments
|
||||
- name: Create annotations from PHPCS report (Pull Request)
|
||||
if: github.event_name == 'pull_request' && matrix.php-version == '8.0'
|
||||
uses: staabm/annotate-pull-request-from-checkstyle-action@v1
|
||||
with:
|
||||
files: phpcs-report-${{ matrix.php-version }}.xml
|
||||
notices-as-warnings: true
|
||||
|
||||
# For pushes to main: Full scan as final safety check
|
||||
- name: Run PHPCS full scan (Push)
|
||||
if: github.event_name != 'pull_request'
|
||||
run: ./vendor/bin/phpcs --no-cache
|
||||
|
||||
# PHPStan - Run on highest PHP version for maximum coverage
|
||||
static-analysis:
|
||||
name: Static Analysis (PHPStan)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.4
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
- name: Get Composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-php8.4-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-php8.4-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --prefer-dist --no-progress
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyse --no-progress --error-format=github
|
||||
|
33
.github/workflows/commitlint.yml
vendored
Normal file
33
.github/workflows/commitlint.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# .github/workflows/lint.yml
|
||||
name: Lint Commit Messages
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
jobs:
|
||||
commitlint:
|
||||
name: Lint Commits
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Validate all commits in PR
|
||||
uses: wagoid/commitlint-github-action@v6
|
||||
with:
|
||||
# When validating all commits in a PR, you need to use the
|
||||
# base and head refs to determine the range of commits to lint.
|
||||
# The action automatically gets these from the event payload.
|
||||
# See: https://github.com/wagoid/commitlint-github-action#validating-all-commits-in-a-pr
|
||||
first-parent: false
|
48
.github/workflows/release.yml
vendored
Normal file
48
.github/workflows/release.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
# .github/workflows/release.yml
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows manual triggering of the workflow
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # To push commits and tags
|
||||
issues: write # To comment on issues and PRs
|
||||
pull-requests: write # To create releases
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# We need to fetch all history and tags for release-it to work correctly
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20' # Or your preferred Node.js version
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Check for uncommitted changes
|
||||
run: |
|
||||
echo "Running git status to check for modified files..."
|
||||
git status
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Create Release
|
||||
run: npm run release -- --ci
|
||||
env:
|
||||
# The GITHUB_TOKEN is automatically provided by GitHub Actions
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Composer
|
||||
/vendor/
|
||||
/composer.lock
|
||||
|
||||
# Node Modules
|
||||
/node_modules/
|
||||
# /package-lock.json
|
||||
|
||||
# PHPUnit
|
||||
/phpunit.xml
|
||||
/.phpunit.result.cache
|
||||
|
||||
# PHPCS
|
||||
phpcs.xml
|
||||
|
||||
# PHPStan
|
||||
phpstan.neon
|
||||
|
||||
# IDE
|
||||
/.idea/
|
||||
/.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs & Reports
|
||||
*.log
|
||||
*.tmp
|
||||
/tmp/
|
||||
/reports/
|
||||
/logs/
|
||||
/old/
|
||||
|
||||
# Environment
|
||||
/docker/.env
|
||||
/docker/php.ini
|
2
.husky/commit-msg
Normal file
2
.husky/commit-msg
Normal file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
npx --no-install commitlint --edit $1
|
49
.husky/pre-commit
Normal file
49
.husky/pre-commit
Normal file
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Do not run on CI environments (e.g., GitHub Actions)
|
||||
if [ "$CI" = "true" ]; then
|
||||
echo "CI environment detected. Skipping pre-push checks."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get staged file list
|
||||
# Use --diff-filter=d to exclude deleted files
|
||||
changed_files=$(git diff --cached --name-only --diff-filter=d)
|
||||
|
||||
# Detect PHP or config changes
|
||||
if ! echo "$changed_files" | grep -qE '\.php$|^phpstan\.neon(\.dist)?$|^phpcs\.xml(\.dist)?$'; then
|
||||
echo "No PHP or lint config changes detected. Skipping pre-commit checks."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if ./bin/composer exists and Docker is installed
|
||||
if [ -f "./bin/composer" ] && command -v docker >/dev/null 2>&1; then
|
||||
COMPOSER_CMD="./bin/composer"
|
||||
elif command -v composer >/dev/null 2>&1; then
|
||||
COMPOSER_CMD="composer"
|
||||
else
|
||||
echo "❌ Error: Neither ./bin/composer (with Docker installed) nor a global 'composer' command is available. Cannot run tests."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure vendor is available
|
||||
if [ ! -d "vendor" ]; then
|
||||
echo "Vendor directory not found. Running composer install..."
|
||||
$COMPOSER_CMD install --no-interaction --prefer-dist --no-progress
|
||||
fi
|
||||
|
||||
# Run checks
|
||||
echo "Running phpcbf (auto-fixer)..."
|
||||
$COMPOSER_CMD fix:phpcbf || true
|
||||
echo "Adding fixes to git..."
|
||||
echo "$changed_files" | grep -E '\.php$' | xargs git add
|
||||
|
||||
echo "Running phpcs..."
|
||||
$COMPOSER_CMD lint:phpcs
|
||||
|
||||
echo "Running phpstan..."
|
||||
$COMPOSER_CMD lint:phpstan
|
||||
|
||||
echo "✅ Pre-commit checks passed."
|
||||
|
||||
exit 0
|
24
.release-it.json
Normal file
24
.release-it.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"$schema": "https://unpkg.com/release-it@19/schema/release-it.json",
|
||||
"git": {
|
||||
"commitMessage": "chore: release v${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"releaseName": "Release v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/bumper": {
|
||||
"in": "composer.json",
|
||||
"out": "composer.json"
|
||||
},
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": "angular",
|
||||
"infile": "CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
18
LICENSE
Normal file
18
LICENSE
Normal file
|
@ -0,0 +1,18 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 WPTechnix
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# WPTechnix Settings Framework
|
||||
|
||||
A modern, object-oriented PHP framework for creating powerful and professional settings pages in WordPress. Designed with a clean, fluent API, this framework saves you time by abstracting away the complexities of the WordPress Settings API.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the MIT License.
|
64
bin/README.md
Executable file
64
bin/README.md
Executable file
|
@ -0,0 +1,64 @@
|
|||
# Command-Line Scripts
|
||||
|
||||
This directory contains all the wrapper scripts used to manage and interact with the project's development environment. These scripts are designed to be run from the project root.
|
||||
|
||||
---
|
||||
|
||||
## `bin/docker`
|
||||
|
||||
This is the primary script for interacting with the Docker environment. It's a smart wrapper around `docker compose` that simplifies container management and command execution.
|
||||
|
||||
### Shorthand Container Access
|
||||
|
||||
The script's default behavior is to execute commands directly inside the main `app` container. Any command that is not a special management command (listed below) is passed through.
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| `./bin/docker` | Open an interactive `bash` shell inside the default `app` container. |
|
||||
| `./bin/docker <cmd...>` | Run any command with its arguments inside the `app` container. |
|
||||
| **Example:** `./bin/docker php -v` | Checks the PHP version inside the container. |
|
||||
| **Example:** `./bin/docker ls -la` | Lists files in the container's default working directory (`/app`). |
|
||||
|
||||
### Environment Management Commands
|
||||
|
||||
These special commands are used to control the Docker Compose stack.
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------------- | ------------------------------------------------------------------------- |
|
||||
| `up` | Start all services defined in `docker-compose.yml` in detached mode. |
|
||||
| `down` | Stop and remove all containers, networks, and volumes. |
|
||||
| `build [service...]` | Rebuild and restart services (default: all). |
|
||||
| `restart [service...]` | Restart one or more services (default: all). |
|
||||
| `logs [service...]` | Follow log output from one or more services (default: all). |
|
||||
| `exec <service> <cmd...>` | Execute a command in a **specific** service container. |
|
||||
| **Example:** `exec db mysql` | Opens a MySQL command-line client inside the `db` container. |
|
||||
|
||||
---
|
||||
|
||||
## `bin/composer`
|
||||
|
||||
This is a dedicated wrapper script for Composer. It simplifies running Composer commands by automatically forwarding them to be executed inside the `app` container.
|
||||
|
||||
Instead of typing `./bin/docker composer <command>`, you can simply use:
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------------------------- | ------------------------------------------------- |
|
||||
| `./bin/composer install` | Install all PHP dependencies from `composer.lock`.|
|
||||
| `./bin/composer update` | Update PHP dependencies to their latest versions. |
|
||||
| `./bin/composer require vendor/package` | Add a new PHP package to the project. |
|
||||
| `./bin/composer remove vendor/package` | Remove a PHP package from the project. |
|
||||
|
||||
---
|
||||
|
||||
## Setup Scripts
|
||||
|
||||
These scripts are typically used only during the initial setup of the project.
|
||||
|
||||
### `bin/copy`
|
||||
|
||||
This script prepares your local environment by copying all necessary configuration files from their templates.
|
||||
|
||||
| Command | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------ |
|
||||
| `./bin/copy` | Copies template files (e.g., `.dist`, `.example`) if the destination does not already exist. |
|
||||
| `./bin/copy --override` | Forces the copy, overwriting any existing configuration files. |
|
22
bin/composer
Executable file
22
bin/composer
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# A wrapper script to run Composer commands inside the default app container.
|
||||
# It forwards all arguments to the main ./bin/docker script.
|
||||
#
|
||||
|
||||
# --- Strict Mode ---
|
||||
# Ensures the script exits on error and handles variables safely.
|
||||
set -euo pipefail
|
||||
|
||||
# --- Find the directory this script is in ---
|
||||
# This is important to reliably locate the main ./bin/docker script,
|
||||
# which should be in the same directory.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# The 'composer' command is passed as the first argument, followed by
|
||||
# all the arguments this script received ($@).
|
||||
#
|
||||
# Example: `./bin/composer install --no-dev`
|
||||
# Becomes: `./bin/docker composer install --no-dev`
|
||||
|
||||
exec "$SCRIPT_DIR/docker" composer "$@"
|
41
bin/copy
Executable file
41
bin/copy
Executable file
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
OVERRIDE=false
|
||||
|
||||
# Parse options
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o|--override)
|
||||
OVERRIDE=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
copy_file() {
|
||||
local src="$1"
|
||||
local dest="$2"
|
||||
|
||||
if [ -f "$src" ]; then
|
||||
if [ "$OVERRIDE" = true ] || [ ! -f "$dest" ]; then
|
||||
cp "$src" "$dest"
|
||||
echo "Copied $src -> $dest"
|
||||
else
|
||||
echo "Skipped $dest (already exists, use -o to override)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
copy_file "docker/.env.example" "docker/.env"
|
||||
copy_file "docker/php.ini.example" "docker/php.ini"
|
||||
copy_file "phpstan.neon.dist" "phpstan.neon"
|
||||
copy_file "phpcs.xml.dist" "phpcs.xml"
|
||||
copy_file "phpunit.xml.dist" "phpunit.xml"
|
||||
|
||||
echo "Done."
|
126
bin/docker
Executable file
126
bin/docker
Executable file
|
@ -0,0 +1,126 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# A smart wrapper for Docker that simplifies container interactions.
|
||||
# It is designed to be run from anywhere within the project.
|
||||
#
|
||||
|
||||
# --- Strict Mode ---
|
||||
# -e: exit on any error
|
||||
# -u: exit on use of an unset variable
|
||||
# -o pipefail: exit if any command in a pipeline fails
|
||||
set -euo pipefail
|
||||
|
||||
# --- Configuration ---
|
||||
# Path to the compose file, relative to the project root.
|
||||
COMPOSE_FILE="docker/docker-compose.yml"
|
||||
# Default service for shorthand commands.
|
||||
DEFAULT_SERVICE="app"
|
||||
# Default working directory inside the container.
|
||||
DOCKER_WORKDIR="/app"
|
||||
|
||||
|
||||
# --- Help Text ---
|
||||
print_help() {
|
||||
echo "Usage: ./bin/docker [command] [args...]"
|
||||
echo ""
|
||||
echo "A smart wrapper for Docker Compose that defaults to the '$DEFAULT_SERVICE' container."
|
||||
echo "All arguments are passed to the executed command."
|
||||
echo ""
|
||||
echo "Shorthand Commands (for '$DEFAULT_SERVICE' container):"
|
||||
echo " ./bin/docker # Open an interactive shell."
|
||||
echo " ./bin/docker <cmd...> # Run a command (e.g., './bin/docker composer install --no-dev')."
|
||||
echo ""
|
||||
echo "Management Commands:"
|
||||
echo " up [service...] Start services (default: all)."
|
||||
echo " down Stop and remove all containers."
|
||||
echo " build [service...] Rebuild and restart services."
|
||||
echo " restart [service...] Restart services."
|
||||
echo " logs [service...] Follow log output."
|
||||
echo " exec <service> <cmd...> Explicitly run a command in a different container."
|
||||
echo " help Show this help message."
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./bin/docker composer update --dry-run"
|
||||
echo " ./bin/docker phpcs --report=full src"
|
||||
echo " ./bin/docker build"
|
||||
echo " ./bin/docker exec db mysql -u root -p"
|
||||
}
|
||||
|
||||
# --- Set Context to Project Root ---
|
||||
# This ensures the script works regardless of where it's called from.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
|
||||
# --- Core Function to Execute Commands in a Container ---
|
||||
run_in_container() {
|
||||
local service="$1"
|
||||
shift # The rest of the arguments are the command to run.
|
||||
local cmd_array=("$@")
|
||||
|
||||
# If no command was provided, default to an interactive bash shell.
|
||||
if [ ${#cmd_array[@]} -eq 0 ]; then
|
||||
cmd_array=("bash")
|
||||
fi
|
||||
|
||||
echo "--- Ensuring '$service' is running..."
|
||||
docker compose -f "$COMPOSE_FILE" up -d "$service" >/dev/null 2>&1
|
||||
|
||||
local container_id
|
||||
container_id=$(docker compose -f "$COMPOSE_FILE" ps -q "$service")
|
||||
|
||||
if [ -z "$container_id" ]; then
|
||||
echo "Error: Could not find or start a container for service '$service'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use interactive flags only if the script is running in a terminal.
|
||||
local exec_flags=()
|
||||
if [ -t 0 ]; then
|
||||
exec_flags+=("-it")
|
||||
fi
|
||||
|
||||
if [ "$DEFAULT_SERVICE" = "$service" ]; then
|
||||
exec_flags+=("-w" "$DOCKER_WORKDIR")
|
||||
fi
|
||||
|
||||
echo "--- Running in '$service': ${cmd_array[*]} ---"
|
||||
exec docker exec "${exec_flags[@]}" "$container_id" "${cmd_array[@]}"
|
||||
}
|
||||
|
||||
|
||||
# --- Main Command Router ---
|
||||
COMMAND="${1:-}" # Default to empty string if no args
|
||||
|
||||
case "$COMMAND" in
|
||||
# Consolidate simple management commands
|
||||
up|down|build|restart)
|
||||
echo "--- Running management command: $COMMAND ${*:2} ---"
|
||||
docker compose -f "$COMPOSE_FILE" "$COMMAND" "${@:2}"
|
||||
;;
|
||||
|
||||
logs)
|
||||
echo "--- Following logs for services: ${*:2} ---"
|
||||
exec docker compose -f "$COMPOSE_FILE" logs -f "${@:2}"
|
||||
;;
|
||||
|
||||
exec)
|
||||
shift # Remove 'exec' from the arguments list
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: 'exec' command requires a service name." >&2
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
run_in_container "$@"
|
||||
;;
|
||||
|
||||
help|--help)
|
||||
print_help
|
||||
;;
|
||||
|
||||
*)
|
||||
# Default Case: Any other command is executed in the default container.
|
||||
run_in_container "$DEFAULT_SERVICE" "$@"
|
||||
;;
|
||||
esac
|
48
composer.json
Normal file
48
composer.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "wptechnix/wp-settings-framework",
|
||||
"version": "0.1.0",
|
||||
"description": "A modern, fluent, and object-oriented framework for creating powerful WordPress admin settings pages.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"wordpress",
|
||||
"settings",
|
||||
"options"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "WPTechnix",
|
||||
"email": "developers@wptechnix.com"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpcompatibility/php-compatibility": "*",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"squizlabs/php_codesniffer": "^3",
|
||||
"szepeviktor/phpstan-wordpress": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WPTechnix\\WPSettings\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"fix:phpcbf": "vendor/bin/phpcbf || true",
|
||||
"lint:phpcs": "vendor/bin/phpcs --report=full",
|
||||
"lint:phpstan": "vendor/bin/phpstan analyse --memory-limit=2G --error-format=table",
|
||||
"lint": [
|
||||
"@fix:phpcbf",
|
||||
"@lint:phpcs",
|
||||
"@lint:phpstan"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
14
docker/.env.example
Normal file
14
docker/.env.example
Normal file
|
@ -0,0 +1,14 @@
|
|||
PHP_VERSION=8.4
|
||||
COMPOSE_PROJECT_NAME=pkg-wp-settings-framework
|
||||
|
||||
UID=1000
|
||||
GID=1000
|
||||
|
||||
XDEBUG_MODE=off
|
||||
XDEBUG_START_WITH_REQUEST=no
|
||||
XDEBUG_DISCOVER_CLIENT_HOST=false
|
||||
XDEBUG_CLIENT_HOST=host.docker.internal
|
||||
XDEBUG_CLIENT_PORT=9000
|
||||
XDEBUG_IDEKEY=PHPSTORM
|
||||
XDEBUG_LOG_LEVEL=0
|
||||
XDEBUG_MAX_NESTING_LEVEL=512
|
62
docker/Dockerfile
Normal file
62
docker/Dockerfile
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Use build argument for dynamic PHP version
|
||||
ARG PHP_VERSION=8.4
|
||||
FROM php:${PHP_VERSION}-cli-alpine
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
nano \
|
||||
less \
|
||||
curl \
|
||||
linux-headers \
|
||||
oniguruma-dev \
|
||||
libxml2-dev \
|
||||
icu-dev \
|
||||
$PHPIZE_DEPS \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Install PHP extensions commonly needed for development
|
||||
RUN docker-php-ext-install \
|
||||
bcmath \
|
||||
exif \
|
||||
intl \
|
||||
mbstring
|
||||
|
||||
# Install and configure Xdebug
|
||||
RUN pecl install xdebug \
|
||||
&& docker-php-ext-enable xdebug
|
||||
|
||||
# Install Composer (latest version)
|
||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
|
||||
|
||||
ARG USER_ID=1000
|
||||
ARG GROUP_ID=1000
|
||||
|
||||
# Create a non-root user for security
|
||||
RUN addgroup -g $GROUP_ID -S developer && \
|
||||
adduser -u $USER_ID -S developer -G developer -h /home/developer
|
||||
|
||||
RUN chown -R $USER_ID:$GROUP_ID /home/developer /app
|
||||
|
||||
ENV COMPOSER_HOME=/home/developer/.composer
|
||||
ENV COMPOSER_CACHE_DIR=/home/developer/.cache/composer
|
||||
ENV PATH="/home/developer/.composer/vendor/bin:$PATH"
|
||||
ENV WP_TESTS_BASE_DIR=/tmp/wordpress-tests-suite
|
||||
|
||||
RUN mkdir -p $COMPOSER_HOME $COMPOSER_CACHE_DIR && \
|
||||
chown -R $USER_ID:$GROUP_ID $COMPOSER_HOME $COMPOSER_CACHE_DIR
|
||||
|
||||
RUN mkdir -p $WP_TESTS_BASE_DIR && \
|
||||
chown -R $USER_ID:$GROUP_ID $WP_TESTS_BASE_DIR
|
||||
|
||||
RUN mkdir -p /app/logs /tmp/.phpunit.cache/code-coverage /tmp/.phpstan \
|
||||
&& chown -R $USER_ID:$GROUP_ID /app/logs /tmp/.phpunit.cache/ /tmp/.phpunit.cache/code-coverage /tmp/.phpstan
|
||||
|
||||
# Switch to non-root user
|
||||
USER developer
|
||||
|
||||
# Default command
|
||||
CMD ["php", "-a"]
|
21
docker/docker-compose.yml
Normal file
21
docker/docker-compose.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
USER_ID: ${UID:-1000}
|
||||
GROUP_ID: ${GID:-1000}
|
||||
PHP_VERSION: ${PHP_VERSION:-8.4}
|
||||
container_name: ${COMPOSE_PROJECT_NAME:-wp-settings-framework}-php
|
||||
user: "${UID:-1000}:${GID:-1000}"
|
||||
volumes:
|
||||
- ../:/app
|
||||
- ./php.ini:/usr/local/etc/php/conf.d/custom.ini
|
||||
tty: true
|
||||
stdin_open: true
|
||||
networks:
|
||||
- wp-settings-framework
|
||||
|
||||
networks:
|
||||
wp-settings-framework:
|
||||
driver: bridge
|
17
docker/php.ini.example
Normal file
17
docker/php.ini.example
Normal file
|
@ -0,0 +1,17 @@
|
|||
; Error reporting
|
||||
error_reporting=E_ALL
|
||||
display_errors=On
|
||||
display_startup_errors=On
|
||||
log_errors=On
|
||||
error_log=/app/logs/php_errors.log
|
||||
|
||||
; Xdebug configuration
|
||||
[xdebug]
|
||||
xdebug.mode=${XDEBUG_MODE:-off}
|
||||
xdebug.start_with_request=${XDEBUG_START_WITH_REQUEST:-no}
|
||||
xdebug.discover_client_host=${XDEBUG_DISCOVER_CLIENT_HOST:-false}
|
||||
xdebug.client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal}
|
||||
xdebug.client_port=${XDEBUG_CLIENT_PORT:-9000}
|
||||
xdebug.idekey=${XDEBUG_IDEKEY:-PHPSTORM}
|
||||
xdebug.log_level=${XDEBUG_LOG_LEVEL:-0}
|
||||
xdebug.max_nesting_level=${XDEBUG_MAX_NESTING_LEVEL:-512}
|
5023
package-lock.json
generated
Normal file
5023
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
32
package.json
Normal file
32
package.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "wp-settings-framework",
|
||||
"description": "A modern, fluent, and object-oriented framework for creating powerful WordPress admin settings pages.",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"wordpress",
|
||||
"settings",
|
||||
"options"
|
||||
],
|
||||
"version": "0.1.0",
|
||||
"author": "WPTechnix <developers@wptechnix.com>",
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.8.1",
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@release-it/bumper": "^7.0.5",
|
||||
"@release-it/conventional-changelog": "^10.0.1",
|
||||
"husky": "^9.1.7",
|
||||
"release-it": "^19.0.4"
|
||||
},
|
||||
"directories": {
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "husky",
|
||||
"release": "release-it"
|
||||
},
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@commitlint/config-conventional"
|
||||
]
|
||||
}
|
||||
}
|
32
phpcs.xml.dist
Normal file
32
phpcs.xml.dist
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="Custom Coding standards">
|
||||
<!-- Show sniff codes in output -->
|
||||
<arg name="parallel" value="8"/>
|
||||
<arg name="cache" value="/tmp/.phpcs.cache"/>
|
||||
<arg name="report-width" value="120"/>
|
||||
<arg name="extensions" value="php"/>
|
||||
|
||||
<config name="installed_paths" value="vendor/phpcompatibility/php-compatibility" />
|
||||
|
||||
<!-- PHP version -->
|
||||
<config name="testVersion" value="8.0-"/>
|
||||
|
||||
<!-- Scan these directories -->
|
||||
<file>src</file>
|
||||
|
||||
<!-- Exclude patterns -->
|
||||
<exclude-pattern>*/bin/*</exclude-pattern>
|
||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||
<exclude-pattern>*/node_modules/*</exclude-pattern>
|
||||
|
||||
<rule ref="PSR12" />
|
||||
<rule ref="PHPCompatibility" />
|
||||
|
||||
<!-- Generic Rules -->
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<properties>
|
||||
<property name="lineLimit" value="120"/>
|
||||
<property name="absoluteLineLimit" value="160"/>
|
||||
</properties>
|
||||
</rule>
|
||||
</ruleset>
|
21
phpstan.neon.dist
Normal file
21
phpstan.neon.dist
Normal file
|
@ -0,0 +1,21 @@
|
|||
parameters:
|
||||
phpVersion:
|
||||
min: 80000
|
||||
max: 80410
|
||||
|
||||
level: 8
|
||||
tmpDir: /tmp/.phpstan
|
||||
paths:
|
||||
- src/
|
||||
|
||||
strictRules:
|
||||
noVariableVariables: false
|
||||
|
||||
# ignoreErrors:
|
||||
# - identifier: empty.notAllowed
|
||||
|
||||
treatPhpDocTypesAsCertain: false
|
||||
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||
- vendor/szepeviktor/phpstan-wordpress/extension.neon
|
41
src/Interfaces/FieldInterface.php
Normal file
41
src/Interfaces/FieldInterface.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WPTechnix\WPSettings\Interfaces;
|
||||
|
||||
/**
|
||||
* Defines the contract for a settings field.
|
||||
*/
|
||||
interface FieldInterface
|
||||
{
|
||||
/**
|
||||
* Render the field's HTML markup.
|
||||
*
|
||||
* This method is responsible for echoing the complete HTML for the form element.
|
||||
*
|
||||
* @param mixed $value The current value of the field.
|
||||
* @param array<string, string|int|bool> $attributes Additional HTML attributes for the field.
|
||||
*/
|
||||
public function render(mixed $value, array $attributes): void;
|
||||
|
||||
/**
|
||||
* Sanitize the field's value before saving.
|
||||
*
|
||||
* This method ensures the input data is clean and in the correct format
|
||||
* before being persisted to the database.
|
||||
*
|
||||
* @param mixed $value The raw input value to be sanitized.
|
||||
* @return mixed The sanitized value.
|
||||
*/
|
||||
public function sanitize(mixed $value): mixed;
|
||||
|
||||
/**
|
||||
* Get the default value for the field.
|
||||
*
|
||||
* Provides a fallback value when no value has been saved yet.
|
||||
*
|
||||
* @return mixed The default value.
|
||||
*/
|
||||
public function getDefaultValue(): mixed;
|
||||
}
|
83
src/Interfaces/SettingsInterface.php
Normal file
83
src/Interfaces/SettingsInterface.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WPTechnix\WPSettings\Interfaces;
|
||||
|
||||
/**
|
||||
* Defines the contract for a settings page builder.
|
||||
*/
|
||||
interface SettingsInterface
|
||||
{
|
||||
/**
|
||||
* Sets or overrides the main page title.
|
||||
*
|
||||
* @param string $pageTitle The new title for the settings page.
|
||||
* @return static
|
||||
*/
|
||||
public function setPageTitle(string $pageTitle): static;
|
||||
|
||||
/**
|
||||
* Sets or overrides the menu title.
|
||||
*
|
||||
* @param string $menuTitle The new title for the admin menu item.
|
||||
* @return static
|
||||
*/
|
||||
public function setMenuTitle(string $menuTitle): static;
|
||||
|
||||
/**
|
||||
* Adds a tab for organizing settings sections.
|
||||
*
|
||||
* @param string $id The unique identifier for the tab.
|
||||
* @param string $title The text to display on the tab.
|
||||
* @param string $icon Optional. A Dashicon class to display next to the title.
|
||||
* @return static
|
||||
*/
|
||||
public function addTab(string $id, string $title, string $icon = ''): static;
|
||||
|
||||
/**
|
||||
* Adds a settings section to the page.
|
||||
*
|
||||
* @param string $id The unique identifier for the section.
|
||||
* @param string $title The title displayed for the section.
|
||||
* @param string $description Optional. A description displayed below the section title.
|
||||
* @param string $tabId Optional. The ID of the tab this section should appear under.
|
||||
* @return static
|
||||
*/
|
||||
public function addSection(string $id, string $title, string $description = '', string $tabId = ''): static;
|
||||
|
||||
/**
|
||||
* Adds a field to a section.
|
||||
*
|
||||
* @param string $id The unique identifier for the field.
|
||||
* @param string $sectionId The ID of the section this field belongs to.
|
||||
* @param string $type The field type (e.g., 'text', 'toggle', 'code').
|
||||
* @param string $label The label displayed for the field.
|
||||
* @param array<string, mixed> $args Optional. An array of additional arguments.
|
||||
* @return static
|
||||
*/
|
||||
public function addField(string $id, string $sectionId, string $type, string $label, array $args = []): static;
|
||||
|
||||
/**
|
||||
* Initializes the settings page and hooks all components into WordPress.
|
||||
*
|
||||
* This method must be called after all configuration is complete.
|
||||
*/
|
||||
public function init(): void;
|
||||
|
||||
/**
|
||||
* Gets the WordPress option name where settings are stored.
|
||||
*
|
||||
* @return string The option name.
|
||||
*/
|
||||
public function getOptionName(): string;
|
||||
|
||||
/**
|
||||
* Retrieves a setting's value for this settings page.
|
||||
*
|
||||
* @param string $key The unique key of the setting to retrieve.
|
||||
* @param mixed $default A fallback value to return if the setting is not found.
|
||||
* @return mixed The stored setting value, or the default if not found.
|
||||
*/
|
||||
public function get(string $key, mixed $default = null): mixed;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue