Initial commit

This commit is contained in:
Snider 2026-01-26 20:48:24 +00:00 committed by GitHub
commit 8ee3a54482
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 1560 additions and 0 deletions

24
.editorconfig Normal file
View file

@ -0,0 +1,24 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.{js,jsx,ts,tsx,vue}]
indent_size = 2
[*.json]
indent_size = 2
[docker-compose.yml]
indent_size = 2

76
.env.example Normal file
View file

@ -0,0 +1,76 @@
APP_NAME="Core PHP App"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en_GB
APP_FALLBACK_LOCALE=en_GB
APP_FAKER_LOCALE=en_GB
APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=core
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
# Core PHP Framework
CORE_CACHE_DISCOVERY=true
# CDN Configuration (optional)
CDN_ENABLED=false
CDN_DRIVER=bunny
BUNNYCDN_API_KEY=
BUNNYCDN_STORAGE_ZONE=
BUNNYCDN_PULL_ZONE=
# Flux Pro (optional)
FLUX_LICENSE_KEY=

12
.gemini/settings.json Normal file
View file

@ -0,0 +1,12 @@
{
"$schema": "https://schema.gemini.google.dev/settings.json",
"codeAssist": {
"enabled": true,
"contextFiles": [
"GEMINI.md",
"CLAUDE.md",
"composer.json",
"config/core.php"
]
}
}

11
.gitattributes vendored Normal file
View file

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

4
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,4 @@
# GitHub Sponsors configuration
# Support Trees for the Future through our fundraiser
custom: ["https://donate.trees.org/-/NPMMSVUP?member=SWZTDDWH"]

83
.github/copilot-instructions.md vendored Normal file
View file

@ -0,0 +1,83 @@
# GitHub Copilot Instructions
## Project Overview
This is a Laravel application using the Core PHP Framework - a modular monolith architecture with event-driven module registration.
## Architecture
### Module System
- Modules live in `app/Mod/{ModuleName}/`
- Each module has a `Boot.php` class with event listeners
- Events: `WebRoutesRegistering`, `ApiRoutesRegistering`, `AdminPanelBooting`
### Example Boot.php
```php
<?php
namespace App\Mod\Blog;
use Core\Events\WebRoutesRegistering;
use Core\Events\ApiRoutesRegistering;
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
ApiRoutesRegistering::class => 'onApiRoutes',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->routes(fn() => require __DIR__.'/Routes/web.php');
$event->views('blog', __DIR__.'/Views');
}
}
```
## Coding Standards
### Language
- Use UK English (colour, organisation, centre, behaviour)
- No American spellings (color, organization, center, behavior)
### PHP Style
- PSR-12 with Laravel conventions
- Strict types: `declare(strict_types=1);`
- Type hints on all parameters and return types
- Final classes by default unless inheritance is intended
### Naming
- Models: singular PascalCase (`Post`, `Comment`)
- Tables: plural snake_case (`posts`, `comments`)
- Controllers: `{Model}Controller`
- Livewire: `{Feature}Page`, `{Feature}Modal`
### Testing
- Use Pest, not PHPUnit directly
- Feature tests for HTTP/Livewire
- Unit tests for services/utilities
## UI Framework
- **Livewire 3** for reactive components
- **Flux Pro** for UI components (not vanilla Alpine)
- **Tailwind CSS** for styling
- **Font Awesome Pro** for icons (not Heroicons)
## Key Packages
| Package | Purpose |
|---------|---------|
| `host-uk/core` | Core framework, events, modules |
| `host-uk/core-admin` | Admin panel, modals |
| `host-uk/core-api` | REST API, rate limiting |
| `host-uk/core-mcp` | AI agent tools (MCP) |
## Don't
- Don't use Heroicons (use Font Awesome Pro)
- Don't use vanilla Alpine components (use Flux Pro)
- Don't create controllers for Livewire pages
- Don't use American English spellings
- Don't add unnecessary abstractions

29
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,29 @@
version: 2
updates:
- package-ecosystem: composer
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
labels:
- dependencies
- php
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
labels:
- dependencies
- javascript
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 3
labels:
- dependencies
- ci

62
.github/package-workflows/README.md vendored Normal file
View file

@ -0,0 +1,62 @@
# Package Workflows
These workflow templates are for **library packages** (host-uk/core, host-uk/core-api, etc.), not application projects.
## README Badges
Add these badges to your package README (replace `{package}` with your package name):
```markdown
[![CI](https://github.com/host-uk/{package}/actions/workflows/ci.yml/badge.svg)](https://github.com/host-uk/{package}/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/host-uk/{package}/graph/badge.svg)](https://codecov.io/gh/host-uk/{package})
[![Latest Version](https://img.shields.io/packagist/v/host-uk/{package})](https://packagist.org/packages/host-uk/{package})
[![PHP Version](https://img.shields.io/packagist/php-v/host-uk/{package})](https://packagist.org/packages/host-uk/{package})
[![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](LICENSE)
```
## Usage
Copy the relevant workflows to your library's `.github/workflows/` directory:
```bash
# In your library repo
mkdir -p .github/workflows
cp path/to/core-template/.github/package-workflows/ci.yml .github/workflows/
cp path/to/core-template/.github/package-workflows/release.yml .github/workflows/
```
## Workflows
### ci.yml
- Runs on push/PR to main
- Tests against PHP 8.2, 8.3, 8.4
- Tests against Laravel 11 and 12
- Runs Pint linting
- Runs Pest tests
### release.yml
- Triggers on version tags (v*)
- Generates changelog using git-cliff
- Creates GitHub release
## Requirements
For these workflows to work, your package needs:
1. **cliff.toml** - Copy from core-template root
2. **Pest configured** - `composer require pestphp/pest --dev`
3. **Pint configured** - `composer require laravel/pint --dev`
4. **CODECOV_TOKEN** - Add to repo secrets for coverage uploads
5. **FUNDING.yml** - Copy `.github/FUNDING.yml` for sponsor button
## Recommended composer.json scripts
```json
{
"scripts": {
"lint": "pint",
"test": "pest",
"test:coverage": "pest --coverage"
}
}
```

55
.github/package-workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,55 @@
# CI workflow for library packages (host-uk/core-*, etc.)
# Copy this to .github/workflows/ci.yml in library repos
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [8.2, 8.3, 8.4]
laravel: [11.*, 12.*]
exclude:
- php: 8.2
laravel: 12.*
name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
coverage: pcov
- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
composer update --prefer-dist --no-interaction --no-progress
- name: Run Pint
run: vendor/bin/pint --test
- name: Run tests
run: vendor/bin/pest --ci --coverage --coverage-clover coverage.xml
- name: Upload coverage to Codecov
if: matrix.php == '8.3' && matrix.laravel == '12.*'
uses: codecov/codecov-action@v4
with:
files: coverage.xml
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

40
.github/package-workflows/release.yml vendored Normal file
View file

@ -0,0 +1,40 @@
# Release workflow for library packages
# Copy this to .github/workflows/release.yml in library repos
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
name: Create Release
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v3
with:
config: cliff.toml
args: --latest --strip header
env:
OUTPUT: CHANGELOG.md
- name: Create release
uses: softprops/action-gh-release@v2
with:
body_path: CHANGELOG.md
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

66
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [8.2, 8.3, 8.4]
name: PHP ${{ matrix.php }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
coverage: pcov
- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress
- name: Run Pint
run: vendor/bin/pint --test
- name: Run tests
run: vendor/bin/pest --ci --coverage --coverage-clover coverage.xml
- name: Upload coverage to Codecov
if: matrix.php == '8.3'
uses: codecov/codecov-action@v5
with:
files: coverage.xml
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
assets:
runs-on: ubuntu-latest
name: Assets
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Build assets
run: npm run build

37
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
name: Create Release
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --latest --strip header
env:
OUTPUT: CHANGELOG.md
- name: Create release
uses: softprops/action-gh-release@v2
with:
body_path: CHANGELOG.md
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
.DS_Store
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/storage/pail
/vendor
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.nova
/.vscode
/.zed

124
AGENTS.md Normal file
View file

@ -0,0 +1,124 @@
# AI Agent Instructions
> For Jules, Devin, and other autonomous coding agents.
## Quick Start
1. This is a Laravel 12 + Livewire 3 application
2. Modules go in `app/Mod/{Name}/Boot.php`
3. Use UK English (colour, not color)
4. Run `vendor/bin/pint --dirty` before committing
5. Run `vendor/bin/pest` to test
## Architecture
**Modular monolith** - Features are self-contained modules that register via events.
### Creating a Module
```bash
php artisan make:mod {Name} --all
```
Or manually create `app/Mod/{Name}/Boot.php`:
```php
<?php
declare(strict_types=1);
namespace App\Mod\{Name};
use Core\Events\WebRoutesRegistering;
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->routes(fn() => require __DIR__.'/Routes/web.php');
$event->views('{name}', __DIR__.'/Views');
}
}
```
## Task Checklist
When implementing features:
- [ ] Create module in `app/Mod/{Name}/`
- [ ] Add `Boot.php` with event listeners
- [ ] Create routes in `Routes/web.php` or `Routes/api.php`
- [ ] Create Livewire components in `Livewire/`
- [ ] Create Blade views in `Views/`
- [ ] Add migrations in `Migrations/`
- [ ] Write tests in `Tests/`
- [ ] Run `vendor/bin/pint --dirty`
- [ ] Run `vendor/bin/pest`
## File Locations
| What | Where |
|------|-------|
| Models | `app/Mod/{Name}/Models/` |
| Livewire | `app/Mod/{Name}/Livewire/` |
| Views | `app/Mod/{Name}/Views/` |
| Routes | `app/Mod/{Name}/Routes/` |
| Migrations | `app/Mod/{Name}/Migrations/` |
| Tests | `app/Mod/{Name}/Tests/` |
| Services | `app/Mod/{Name}/Services/` |
## Critical Rules
1. **UK English** - colour, organisation, centre (never American spellings)
2. **Strict types** - `declare(strict_types=1);` in every PHP file
3. **Type hints** - All parameters and return types
4. **Flux Pro** - Use Flux components, not vanilla Alpine
5. **Font Awesome** - Use FA icons, not Heroicons
6. **Pest** - Write tests using Pest syntax, not PHPUnit
## Example Livewire Component
```php
<?php
declare(strict_types=1);
namespace App\Mod\Blog\Livewire;
use App\Mod\Blog\Models\Post;
use Illuminate\Contracts\View\View;
use Livewire\Component;
use Livewire\WithPagination;
class PostListPage extends Component
{
use WithPagination;
public function render(): View
{
return view('blog::posts.index', [
'posts' => Post::latest()->paginate(10),
]);
}
}
```
## Testing Example
```php
<?php
use App\Mod\Blog\Models\Post;
it('displays posts on the index page', function () {
$posts = Post::factory()->count(3)->create();
$this->get('/blog')
->assertOk()
->assertSee($posts->first()->title);
});
```

66
CLAUDE.md Normal file
View file

@ -0,0 +1,66 @@
# Core PHP Framework Project
## Architecture
Modular monolith using Core PHP Framework. Modules live in `app/Mod/{Name}/Boot.php`.
**Event-driven registration:**
```php
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
ApiRoutesRegistering::class => 'onApiRoutes',
AdminPanelBooting::class => 'onAdminPanel',
];
}
```
## Commands
```bash
composer run dev # Dev server (if configured)
php artisan serve # Laravel dev server
npm run dev # Vite
./vendor/bin/pint --dirty # Format changed files
php artisan test # All tests
php artisan make:mod Blog # Create module
```
## Module Structure
```
app/Mod/Blog/
├── Boot.php # Event listeners
├── Models/ # Eloquent models
├── Routes/
│ ├── web.php # Web routes
│ └── api.php # API routes
├── Views/ # Blade templates
├── Livewire/ # Livewire components
├── Migrations/ # Database migrations
└── Tests/ # Module tests
```
## Packages
| Package | Purpose |
|---------|---------|
| `host-uk/core` | Core framework, events, module discovery |
| `host-uk/core-admin` | Admin panel, Livewire modals |
| `host-uk/core-api` | REST API, scopes, rate limiting, webhooks |
| `host-uk/core-mcp` | Model Context Protocol for AI agents |
## Conventions
- UK English (colour, organisation, centre)
- PSR-12 coding style (Laravel Pint)
- Pest for testing
- Livewire + Flux Pro for UI
## License
- `Core\` namespace and vendor packages: EUPL-1.2 (copyleft)
- `app/Mod/*`, `app/Website/*`: Your choice (no copyleft)
See LICENSE for full details.

122
GEMINI.md Normal file
View file

@ -0,0 +1,122 @@
# Core PHP Framework - AI Assistant Context
> For Gemini Code Assist, Jules, and other Google AI tools.
## Project Type
Laravel 12 application using Core PHP Framework - a modular monolith with event-driven architecture.
## Directory Structure
```
app/
├── Mod/ # Feature modules (your code)
│ └── {Name}/
│ ├── Boot.php # Event listeners
│ ├── Models/
│ ├── Routes/
│ ├── Views/
│ └── Livewire/
├── Core/ # Local framework overrides (EUPL-1.2)
└── Providers/
config/core.php # Framework configuration
```
## Module Pattern
Every module has a `Boot.php` with static `$listens` array:
```php
<?php
declare(strict_types=1);
namespace App\Mod\Shop;
use Core\Events\WebRoutesRegistering;
use Core\Events\ApiRoutesRegistering;
use Core\Events\AdminPanelBooting;
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
ApiRoutesRegistering::class => 'onApiRoutes',
AdminPanelBooting::class => 'onAdminPanel',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->routes(fn() => require __DIR__.'/Routes/web.php');
$event->views('shop', __DIR__.'/Views');
}
public function onApiRoutes(ApiRoutesRegistering $event): void
{
$event->routes(fn() => require __DIR__.'/Routes/api.php');
}
public function onAdminPanel(AdminPanelBooting $event): void
{
$event->navigation('Shop', 'shop.admin.index', 'shopping-cart');
}
}
```
## Code Style Requirements
### Language: UK English
- colour (not color)
- organisation (not organization)
- centre (not center)
- behaviour (not behavior)
- licence (noun), license (verb)
### PHP Standards
- `declare(strict_types=1);` in all files
- Full type hints (parameters + return types)
- PSR-12 formatting (use Laravel Pint)
- Pest for testing
### Naming Conventions
| Type | Convention | Example |
|------|------------|---------|
| Model | Singular PascalCase | `Product` |
| Table | Plural snake_case | `products` |
| Controller | `{Model}Controller` | `ProductController` |
| Livewire Page | `{Feature}Page` | `ProductListPage` |
| Livewire Modal | `{Feature}Modal` | `EditProductModal` |
## UI Stack
- **Livewire 3** - Server-side reactivity
- **Flux Pro** - UI component library (NOT vanilla Alpine)
- **Tailwind CSS** - Utility-first styling
- **Font Awesome Pro** - Icons (NOT Heroicons)
## Common Commands
```bash
php artisan make:mod Blog --all # Create module with all features
php artisan serve # Development server
vendor/bin/pint --dirty # Format changed files
vendor/bin/pest # Run tests
```
## Packages
| Package | Namespace | Purpose |
|---------|-----------|---------|
| host-uk/core | `Core\` | Framework core |
| host-uk/core-admin | `Core\Admin\` | Admin panel |
| host-uk/core-api | `Core\Api\` | REST API |
| host-uk/core-mcp | `Core\Mcp\` | AI agent tools |
## Avoid
- American English spellings
- Heroicons (use Font Awesome)
- Vanilla Alpine components (use Flux)
- Over-engineering / premature abstraction
- PHPUnit syntax (use Pest)

108
LICENSE Normal file
View file

@ -0,0 +1,108 @@
Core PHP Framework Template
===========================
Copyright (c) 2026 Host UK / Snider
This project uses a dual-licensing approach:
1. CORE FRAMEWORK CODE (EUPL-1.2)
---------------------------------
The Core PHP Framework packages (host-uk/core, host-uk/core-admin,
host-uk/core-api, host-uk/core-mcp) and any code within the `Core\`
namespace are licensed under the European Union Public Licence v1.2
(EUPL-1.2).
The full EUPL-1.2 license text is available at:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
And in the Core PHP Framework repository:
https://github.com/host-uk/core-php/blob/main/LICENSE
2. APPLICATION CODE EXCEPTION
-----------------------------
As the copyright holder, we grant the following additional permission:
Code you create within the following directories is EXEMPT from the
EUPL-1.2 copyleft requirements and may be licensed under any terms
you choose (including proprietary licenses):
app/Mod/ - Your feature modules
app/Website/ - Your website/domain modules
app/Service/ - Your service classes
database/ - Your migrations, seeders, factories
resources/ - Your views, assets, translations
routes/ - Your route definitions
tests/ - Your test files
config/ - Your configuration files (except config/core.php)
This exception applies ONLY to original code you create in these
directories. It does NOT apply to:
- Modified copies of Core framework code
- Code that extends or overrides the `Core\` namespace
- Derivative works of the Core framework itself
3. CORE NAMESPACE MODIFICATIONS
-------------------------------
If you create local overrides or extensions within the `Core\` namespace
(e.g., in `app/Core/`), those modifications ARE subject to EUPL-1.2 and
its copyleft requirements.
We encourage you to submit such modifications as pull requests to:
https://github.com/host-uk/core-php
This requirement constitutes the consideration for the license grant
herein. The Core PHP Framework is not provided without cost; rather,
the license fee is paid in kind through the contribution of improvements
to the community. By using this software, you acknowledge and agree that
modifications to the Core\ namespace shall be made available to the
community under the same terms, ensuring that all users benefit from
collective improvements. This reciprocal obligation is a fundamental
condition of the license grant and not merely a request.
4. SUMMARY
----------
┌─────────────────────────────────────────────────────────────────────┐
│ Location │ License │ Copyleft Required? │
├─────────────────────────────────────────────────────────────────────┤
│ vendor/host-uk/* │ EUPL-1.2 │ Yes │
│ app/Core/* │ EUPL-1.2 │ Yes (submit as PR) │
│ Core\ namespace │ EUPL-1.2 │ Yes │
├─────────────────────────────────────────────────────────────────────┤
│ app/Mod/* │ Your choice │ No │
│ app/Website/* │ Your choice │ No │
│ app/Service/* │ Your choice │ No │
│ database/* │ Your choice │ No │
│ resources/* │ Your choice │ No │
│ routes/* │ Your choice │ No │
│ tests/* │ Your choice │ No │
└─────────────────────────────────────────────────────────────────────┘
5. WARRANTY DISCLAIMER
----------------------
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.
6. QUESTIONS
------------
If you have questions about licensing, contact: hello@host.uk.com
For the full EUPL-1.2 text, see:
https://github.com/host-uk/core-php/blob/main/LICENSE

138
README.md Normal file
View file

@ -0,0 +1,138 @@
# Core PHP Framework Project
[![CI](https://github.com/host-uk/core-template/actions/workflows/ci.yml/badge.svg)](https://github.com/host-uk/core-template/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/host-uk/core-template/graph/badge.svg)](https://codecov.io/gh/host-uk/core-template)
[![PHP Version](https://img.shields.io/packagist/php-v/host-uk/core-template)](https://packagist.org/packages/host-uk/core-template)
[![Laravel](https://img.shields.io/badge/Laravel-12.x-FF2D20?logo=laravel)](https://laravel.com)
[![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](LICENSE)
A modular monolith Laravel application built with Core PHP Framework.
## Features
- **Core Framework** - Event-driven module system with lazy loading
- **Admin Panel** - Livewire-powered admin interface with Flux UI
- **REST API** - Scoped API keys, rate limiting, webhooks, OpenAPI docs
- **MCP Tools** - Model Context Protocol for AI agent integration
## Requirements
- PHP 8.2+
- Composer 2.x
- SQLite (default) or MySQL/PostgreSQL
- Node.js 18+ (for frontend assets)
## Installation
```bash
# Clone or create from template
git clone https://github.com/host-uk/core-template.git my-project
cd my-project
# Install dependencies
composer install
npm install
# Configure environment
cp .env.example .env
php artisan key:generate
# Set up database
touch database/database.sqlite
php artisan migrate
# Start development server
php artisan serve
```
Visit: http://localhost:8000
## Project Structure
```
app/
├── Console/ # Artisan commands
├── Http/ # Controllers & Middleware
├── Models/ # Eloquent models
├── Mod/ # Your custom modules
└── Providers/ # Service providers
config/
└── core.php # Core framework configuration
routes/
├── web.php # Public web routes
├── api.php # REST API routes
└── console.php # Artisan commands
```
## Creating Modules
```bash
# Create a new module with all features
php artisan make:mod Blog --all
# Create module with specific features
php artisan make:mod Shop --web --api --admin
```
Modules follow the event-driven pattern:
```php
<?php
namespace App\Mod\Blog;
use Core\Events\WebRoutesRegistering;
use Core\Events\ApiRoutesRegistering;
use Core\Events\AdminPanelBooting;
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
ApiRoutesRegistering::class => 'onApiRoutes',
AdminPanelBooting::class => 'onAdminPanel',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->routes(fn() => require __DIR__.'/Routes/web.php');
$event->views('blog', __DIR__.'/Views');
}
}
```
## Core Packages
| Package | Description |
|---------|-------------|
| `host-uk/core` | Core framework components |
| `host-uk/core-admin` | Admin panel & Livewire modals |
| `host-uk/core-api` | REST API with scopes & webhooks |
| `host-uk/core-mcp` | Model Context Protocol tools |
## Flux Pro (Optional)
This template uses the free Flux UI components. If you have a Flux Pro license:
```bash
# Configure authentication
composer config http-basic.composer.fluxui.dev your-email your-license-key
# Add the repository
composer config repositories.flux-pro composer https://composer.fluxui.dev
# Install Flux Pro
composer require livewire/flux-pro
```
## Documentation
- [Core PHP Framework](https://github.com/host-uk/core-php)
- [Getting Started Guide](https://host-uk.github.io/core-php/guide/)
- [Architecture](https://host-uk.github.io/core-php/architecture/)
## License
EUPL-1.2 (European Union Public Licence)

View file

0
app/Mod/.gitkeep Normal file
View file

0
app/Models/.gitkeep Normal file
View file

View file

@ -0,0 +1,24 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}

15
artisan Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
->handleCommand(new ArgvInput);
exit($status);

26
bootstrap/app.php Normal file
View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withProviders([
// Core PHP Framework
\Core\LifecycleEventProvider::class,
\Core\Website\Boot::class,
\Core\Front\Boot::class,
\Core\Mod\Boot::class,
])
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
\Core\Front\Boot::middleware($middleware);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

2
bootstrap/cache/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

5
bootstrap/providers.php Normal file
View file

@ -0,0 +1,5 @@
<?php
return [
App\Providers\AppServiceProvider::class,
];

48
cliff.toml Normal file
View file

@ -0,0 +1,48 @@
# git-cliff configuration
# https://git-cliff.org
[changelog]
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\
{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
footer = """
<!-- generated by git-cliff -->
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_parsers = [
{ message = "^feat", group = "Features" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^doc", group = "Documentation" },
{ message = "^perf", group = "Performance" },
{ message = "^refactor", group = "Refactor" },
{ message = "^style", group = "Styling" },
{ message = "^test", group = "Testing" },
{ message = "^chore\\(release\\)", skip = true },
{ message = "^chore\\(deps.*\\)", skip = true },
{ message = "^chore|^ci", group = "Miscellaneous" },
{ body = ".*security", group = "Security" },
]
protect_breaking_commits = false
filter_commits = false
topo_order = false
sort_commits = "oldest"

78
composer.json Normal file
View file

@ -0,0 +1,78 @@
{
"name": "host-uk/core-template",
"type": "project",
"description": "Core PHP Framework - Project Template",
"keywords": ["laravel", "core-php", "modular", "framework", "template"],
"license": "EUPL-1.2",
"require": {
"php": "^8.2",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10",
"livewire/flux": "^2.0",
"livewire/livewire": "^3.0",
"host-uk/core": "dev-main",
"host-uk/core-admin": "dev-main",
"host-uk/core-api": "dev-main",
"host-uk/core-mcp": "dev-main"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/pail": "^1.2",
"laravel/pint": "^1.18",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"pestphp/pest": "^3.0",
"pestphp/pest-plugin-laravel": "^3.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/host-uk/core-php.git"
}
],
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

24
config/core.php Normal file
View file

@ -0,0 +1,24 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Core PHP Framework Configuration
|--------------------------------------------------------------------------
*/
'module_paths' => [
app_path('Core'),
app_path('Mod'),
app_path('Website'),
],
'services' => [
'cache_discovery' => env('CORE_CACHE_DISCOVERY', true),
],
'cdn' => [
'enabled' => env('CDN_ENABLED', false),
'driver' => env('CDN_DRIVER', 'bunny'),
],
];

View file

View file

View file

@ -0,0 +1,16 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// Core modules handle their own seeding
}
}

16
package.json Normal file
View file

@ -0,0 +1,16 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
"axios": "^1.7.4",
"laravel-vite-plugin": "^2.1.0",
"postcss": "^8.4.47",
"tailwindcss": "^4.1.18",
"vite": "^7.3.1"
}
}

33
phpunit.xml Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>app</directory>
</include>
</source>
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="PULSE_ENABLED" value="false"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
</phpunit>

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

21
public/.htaccess Normal file
View file

@ -0,0 +1,21 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

17
public/index.php Normal file
View file

@ -0,0 +1,17 @@
<?php
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Determine if the application is in maintenance mode...
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
// Register the Composer autoloader...
require __DIR__.'/../vendor/autoload.php';
// Bootstrap Laravel and handle the request...
(require_once __DIR__.'/../bootstrap/app.php')
->handleRequest(Request::capture());

2
public/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Disallow:

3
resources/css/app.css Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

1
resources/js/app.js Normal file
View file

@ -0,0 +1 @@
import './bootstrap';

3
resources/js/bootstrap.js vendored Normal file
View file

@ -0,0 +1,3 @@
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

View file

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Core PHP Framework</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.container {
text-align: center;
padding: 2rem;
}
h1 {
font-size: 3rem;
margin-bottom: 0.5rem;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.version {
color: #888;
font-size: 0.9rem;
margin-bottom: 2rem;
}
.links {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
a {
color: #667eea;
text-decoration: none;
padding: 0.75rem 1.5rem;
border: 1px solid #667eea;
border-radius: 0.5rem;
transition: all 0.2s;
}
a:hover {
background: #667eea;
color: #fff;
}
</style>
</head>
<body>
<div class="container">
<h1>Core PHP Framework</h1>
<p class="version">Laravel {{ Illuminate\Foundation\Application::VERSION }} | PHP {{ PHP_VERSION }}</p>
<div class="links">
<a href="https://github.com/host-uk/core-php">Documentation</a>
<a href="/admin">Admin Panel</a>
<a href="/api/docs">API Docs</a>
</div>
</div>
</body>
</html>

5
routes/api.php Normal file
View file

@ -0,0 +1,5 @@
<?php
use Illuminate\Support\Facades\Route;
// API routes are registered via Core modules

3
routes/console.php Normal file
View file

@ -0,0 +1,3 @@
<?php
// Console commands are registered via Core modules

7
routes/web.php Normal file
View file

@ -0,0 +1,7 @@
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});

3
storage/app/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*
!public/
!.gitignore

2
storage/app/public/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

9
storage/framework/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
compiled.php
config.php
down
events.scanned.php
maintenance.php
routes.php
routes.scanned.php
schedule-*
services.json

3
storage/framework/cache/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*
!data/
!.gitignore

View file

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/sessions/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/testing/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/views/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/logs/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
],
theme: {
extend: {},
},
plugins: [],
};

0
tests/Feature/.gitkeep Normal file
View file

10
tests/TestCase.php Normal file
View file

@ -0,0 +1,10 @@
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
//
}

0
tests/Unit/.gitkeep Normal file
View file

11
vite.config.js Normal file
View file

@ -0,0 +1,11 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});