Welcome, Agent. This document defines the rules, patterns, and best practices for developing within the Anchor Framework. It should be used in conjunction with the best-practices.md guide. Adherence to these guidelines is mandatory to maintain the framework's integrity, testability, and "mutation-resistant" nature.
- Stability: Anchor provides a solid foundation. Don't fight the framework; use its built-in abstractions.
- Plug and Play (PnP): Packages should be hot-swappable. The system must remain functional regardless of which non-core packages are installed.
- Shipping: Code is not just deployed; it's shipped. It must be reliable, performant, secure, and "seaworthy."
- Analytic Robustness: Every feature should include robust analytics/logging to ensure visibility into production behavior.
- Separation of Concerns:
System/: Framework core. DO NOT MODIFY.App/: Your workspace. Implement business logic here.packages/: Modular extensions. Each package is a self-contained ecosystem.
- Organize by Feature/Domain, not just file type.
- Each module/package should be encapsulated with its own Controllers, Services, Models etc.
- Package Isolation: No package in
packages/may contain test files or aTests/directory. (See package-management.md for full package specifications). - Cross-Package Interaction: Packages must never import or manipulate models belonging to other packages. All interactions must pass through Service Facades (Soft Integration) or Event Hooks.
- Integration Guard (Graceful Failure): Before integrating with other packages, always check if they exist/are enabled and provide a graceful fallback inside a
try-catchblock if the integration is optional. - Modular Model Extension (Macros): Strictly prohibit direct modification of core models for package-specific logic. If a package requires a relationship or method on a core model (like
User), it MUST be implemented using a Macro in the package'sServiceProvider. - Extensibility (Macroable): If you create a new class that should be extensible by others, use the
System\Helpers\Macroabletrait. This allows other packages to "inject" methods into your class at runtime. - Clear SOC: Maintain a strict Separation of Concerns. Every Command should interact with a Service; logic should not reside in the Command itself.
Packages in packages/ or System/ must follow a predictable layout to be "installable" via php dock package:install:
setup.php: The manifest defining Service Providers and Middleware.Config/: Configuration files that get published toApp/Config/.Database/Migrations/: Migrations that get published and run automatically.Providers/: The "entry points" for your package logic.src/(Optional): PSR-4 compliant source code.
Components must follow strict naming and directory patterns aligned with the framework code:
- Events:
ExampleEvent(must reside in anEvents/directory). - Listeners:
ExampleListener(must reside in aListeners/directory). - Services:
ExampleService(must reside in aServices/directory). - Notifications:
ExampleNotification(must reside in aNotifications/directory). - Controllers:
ExampleController(must reside in aControllers/directory). - ViewModels:
ExampleViewModel(must reside in aViews/Models/orViewModels/directory). - Commands:
ExampleCommand(must reside in aCommands/directory). - Requests:
ExampleRequest(must reside in aRequests/directory). - Channels:
ExampleChannel(must reside in aChannels/directory). - Models:
Example(must reside in aModels/directory). Note: Models do not use a suffix. - Facades:
Example(must reside in aFacades/directory). - Traits:
HasExampleorInteractsWithExample(must reside in aTraits/directory). - Interfaces/Contracts:
ExampleInterface(reside inContracts/orInterfaces/). - Actions:
ExampleAction(must reside in anActions/directory). - Enforcement: If a class exists in these directories but lacks the suffix, it must be renamed, and all references updated.
- Pattern Matching: Refer to existing framework code (e.g., in
System/or stablepackages/) for other patterns.
- Constructor Injection: Mandatory for Services, Packages, and core logic. Improves testability and makes dependencies explicit.
- Global Helpers:
config(),session(),request(), etc. are acceptable in Views. For Controllers, dependencies should be injected (concrete classes or abstractions) to maintain a clean and testable architecture. - Golden Rule: If you need to mock it in a unit test, inject it.
- Bind interfaces to concrete implementations in Service Providers (
register()). - Use
singleton()for shared instances (DB, Config) andbind()for fresh instances (Validators, DTOs).
- Every PHP file MUST declare strict types:
declare(strict_types=1);. - Use native type hints for parameters, return types, and class properties.
- Use PHPDoc
/** @var ... */only for complex types (e.g., genericsarray<string, User>).
- The codebase targets PHPStan Level 5.
- Avoid
mixedtypes where possible. - Use guard clauses or null-coalescing to handle potentially nullable values.
- Avoid native I/O functions (
file_exists,mkdir,copy) in core logic/packages. - Use framework interfaces:
PathResolverInterface,FileMetaInterface,FileManipulationInterface. - Reason: Native functions break unit test isolation and cross-platform compatibility.
- Document Intent: Document the why for non-obvious code, not just the what.
- Purpose Docblocks: Every class, trait, interface, or enum under
App/orpackages/must have a top-level PHPDoc explaining:- Why the file exists.
- Why the logic was extracted there (vs. inlining).
- The "contract" or what callers should rely on (if non-obvious).
- Import Namespaces: Strictly prioritize
useimports over fully qualified class names (FQCNs) in all package code. Do not rely on implicit or global imports. - Explicit Naming: Avoid ambiguous names. No one-letter variables unless they are extremely local (e.g., in a short closure) and obvious.
- Guard Clauses: Use guard clauses to reduce nesting depth.
- Fluent Methods: Provide fluent interfaces (returning
$this) in Builders and Services where it improves Developer Experience (DX). - No Debugging: Never commit
dd(),dump(), or other debugging helpers. - No
final: Do not use thefinalkeyword; prioritize extensibility. - No Suppression: Never use the
@error suppression operator. Use explicit error handling and document why if it's unavoidable. - Visibility: Default to
protectedfor non-public methods and properties unless there is a reason forprivate.
- No Globals: Never use native PHP globals (
$_GET,$_POST,$_SESSION,$_FILES). Always use the framework'sRequestobject or relevant abstractions. - Validation First: Every piece of user-provided data must be validated before it is used to interact with the database or external services.
- Mass Assignment: Be explicit about
$fillablefields in Models; never use$guarded = [].
- Eager Loading: Always use
.with()or.load()to prevent the N+1 query problem when accessing relationships in loops. - Model Factories: Every model must have a corresponding Factory in
tests/System/Fixtures/Factories/or the package'sFixtures/directory to ensure consistent test data. - Queued Tasks: Offload heavy or time-consuming operations (e.g., sending emails, processing large files) to background Jobs.
- Type Safety: Standardize on
Throwablefor catch blocks unless you are targeting a specific custom exception. - Custom Exceptions: Create high-level domain exceptions (residing in
Exceptions/) to represent meaningful business failures that need special handling.
For long-running processes (CLI Workers, Watchers), static state is dangerous:
- Reset Static Caches: If your service uses static caches, implement a
reset()method and call it inKernel::terminate(). - Avoid Boundless Growth: Never append to static arrays without a cleanup mechanism.
- Memory Hygiene: Use the
php dock inspecttool to check for memory leaks in persistent components.
- Table Naming: Table names must be in singular form (e.g.,
user,order). - Defensive Migrations: Use
createIfNotExists()inup()anddropIfExists()indown()to ensure migrations are idempotent and "seaworthy" across environments. - One Migration, One Table: No multi-table migrations. Every table should belong to its own migration file.
- Timestamps: Use
dateTimeStamps()over standardtimestamps()for better precision/consistency across drivers. - Reversibility: Migrations should be reversible (
down()method implemented) whenever possible. - Immutability: Never edit a migration file after it has been merged/deployed. Always create a new migration to modify the existing schema.
Testing is not optional; it's the core of the Anchor Framework. All tests must comply with the standards outlined in testing.md.
- Testing Framework: Use Pest exclusively for all tests. PHPUnit must not be used.
- Centralized Testing: All tests for packages must reside under the global
tests/directory, mirroring thepackages/orApp/structure.
- Your
tests/directory MUST mirror yoursrc/directory. Unit/: Isolated logic. NO database, NO container.Feature/: Request-to-response journeys. Transactional database allowed.Architecture/: Structural rules usingarch().
- Goal: 100% Mutation Score for Unit tests.
- Use
with()datasets to cover boundaries (e.g., 17, 18, 19 for age 18 check) and edge cases (null, empty, max int). - Kill mutants like
>to>=by testing the exact boundary value.
- No Inline Migrations: Never define schema in test files.
- Use the
RefreshDatabasetrait. - Use
Fixtures/for test-only models andFactories/for data seeding.
- Every module/package MUST have an
ArchTest.php. - Naming Enforcement: Include ArchTests to automatically verify that classes in
Events/,Listeners/,Services/, andNotifications/have the required suffixes. - Enforce naming conventions (e.g.,
*ServiceProvider,*Command,*Test). - Prevent layer breaches (e.g., "System should not depend on App", "Packages should not import other package models").
Use the dock CLI for all lifecycle tasks:
php dock format: Auto-fix coding style and cleanup comments.php dock inspect: Run Pint and PHPStan to catch issues.php dock sail: The Ultimate Gatekeeper. Runs style, static analysis, and tests in parallel. Run this before declaring a task complete.php dock package:install {Name}: Use--system --forcefor core framework packages.
For deep dives into specific framework components, refer to these guides:
- Core Engine: architecture.md, lifecycle.md, container.md
- Data Layer: models.md, query-builder.md, migrations.md
- Logic & Services: services.md, providers.md, events.md
- Web & API: routing.md, controllers.md, middleware.md, request-validation.md
- Tooling: dock-command.md, code-quality.md, best-practices.md
- Strict Types on every file.
- Abstractions for File I/O.
- Constructor Injection for logic.
- Mirror Tests for every class.
- 100% MSI is the target.
php dock sailis your best friend.