The Anchor framework includes a powerful package management system that allows you to install, manage, and uninstall packages with ease. Packages can contain configurations, migrations, commands, service providers, and middleware.
Not all packages require installation! The
package:installandpackage:uninstallcommands are only needed for packages that integrate with the application's core systems (service providers, middleware, migrations, or CLI commands). Standalone library packages that provide pure PHP classes can be used directly without installation.
Install Required (Needs setup.php)
Use package:install when your package needs to:
- Register service providers in
App/Config/providers.php - Add middleware to web or API routes
- Publish configuration files to
App/Config/ - Run database migrations
- Provide CLI commands (automatically discovered) Examples: Authentication, Queue, Watcher, Payment Gateway, Multi-tenancy
Your package doesn't need installation if it:
- Only provides utility classes/helpers
- Is a pure PHP library (like a PDF generator, UUID library, etc.)
- Doesn't integrate with the application's configuration or service layer
- Is used via direct class instantiation (
new MyClass()orMyClass::method())
Examples: String utilities, Date formatters, Pure calculation libraries
Before you can install a package that isn't already in your packages/ directory, you need to download it from the official Anchor GitHub organization.
Anchor provides a convenient download command to fetch packages via Git.
php dock download PackageNameThis command requires
gitto be installed and available in your system path.
| Option | Shortcut | Description | Default |
|---|---|---|---|
--branch |
-b |
Specify a specific Git branch or tag to download | main |
--install |
-i |
Automatically run the package:install process after downloading |
false |
Download a package:
php dock download AllyDownload and install in one step:
php dock download Pay --installDownload a specific version/branch:
php dock download Bridge --branch v2.0Anchor provides a simple command to install packages. By default, the system will automatically search for the package in both the packages/ and System/ directories.
php dock package:install PackageNameWhile auto-detection works in most cases, you can use these flags to be explicit or if you have name collisions:
| Flag | Description |
|---|---|
--packages |
Force installation from the packages/ directory |
--system |
Force installation from the System/ directory |
Examples:
# Auto-detect (Recommended)
php dock package:install Shield
# Explicit sources
php dock package:install Notifications --system
php dock package:install Watcher --packages- Status Check: The system verifies if the package is already installed
- Self-Healing: If partially installed (corrupted), prompts to repair
- Publishing:
- Recursively copies configuration files from
Package/Config/toApp/Config/ - Recursively copies migrations from
Package/Database/Migrations/toApp/storage/database/migrations - Preserves directory structure (subdirectories are copied intact)
- Creates destination directories if they don't exist
- Recursively copies configuration files from
- Migration Execution: Automatically runs package-specific migrations to create database tables
- Registration:
- Adds service providers to
App/Config/providers.php - Adds middleware to
App/Config/middleware.php
- Adds service providers to
- Commands: Package commands are auto-discovered (no copying needed)
Automatic Migration Execution
package:installautomatically runs the package's migrations after publishing them. You don't need to manually runphp dock migration:rununless you want to run additional pending migrations from other sources.
Since package:install handles both file publishing and migrations, a single command is usually all you need:
php dock package:install MyPackage| Status | Description |
|---|---|
INSTALLED |
All package files are present and registered |
MISSING_FILES |
Some files are missing (partial/corrupted installation) |
NOT_INSTALLED |
Package has never been installed |
If a package is in a MISSING_FILES state (e.g., you manually deleted a config file), the installer will detect this and prompt you to repair:
[WARNING] Package Watcher is in a partial state (some files missing).
Do you want to repair/re-install it? (yes/no) [yes]:
Selecting yes will:
- Uninstall the corrupted package completely
- Re-install it fresh
php dock package:uninstall PackageNameExample:
php dock package:uninstall WatcherFor automated scripts or CI/CD pipelines, use the --yes flag to skip the confirmation prompt:
php dock package:uninstall Watcher --packages --yes
- Confirmation Prompt: Asks for confirmation (destructive operation) - can be skipped with
--yesflag - Migration Rollback: Automatically runs
down()methods for all package migrations (drops tables) - File Removal:
- Deletes configuration files from
App/Config/ - Deletes migration files from
App/storage/database/migrations/
- Deletes configuration files from
- Unregistration:
- Removes service providers from
App/Config/providers.php - Removes middleware from
App/Config/middleware.php
- Removes service providers from
- Commands: Package commands are removed automatically when uninstalled
Uninstalling a package will automatically delete data by rolling back migrations. Always backup your database before uninstalling. You don't need to manually run
migration:rollback.
This section applies only to packages that need installation. Standalone library packages don't need this structure - they can just be regular PHP classes.
A package that needs installation requires at minimum:
- A
setup.phpmanifest file (required - triggers installation) - At least one resource that needs integration:
- Config file to publish to
App/Config/ - Migration to run
- CLI command (auto-discovered from package)
- Service provider to register
- Middleware to add
- Config file to publish to
If your package doesn't have a setup.php, it doesn't use the installation system.
System/MyPackage/
├── setup.php # Manifest file (required)
├── Config/
│ └── mypackage.php # Package configuration
├── Database/
│ └── Migrations/
│ └── 2025_01_01_000000_create_mypackage_table.php
├── Commands/
│ └── MyPackageCommand.php
└── Providers/
└── MyPackageServiceProvider.php
mkdir -p System/MyPackage/{Config,Database/Migrations,Commands,Providers,Middleware}Create System/MyPackage/setup.php:
<?php
declare(strict_types=1);
return [
'providers' => [
MyPackage\Providers\MyPackageServiceProvider::class,
],
'middleware' => [
'web' => [
MyPackage\Middleware\MyPackageMiddleware::class,
],
'api' => [
MyPackage\Middleware\ApiMiddleware::class,
],
],
];Create System/MyPackage/Config/mypackage.php:
<?php
declare(strict_types=1);
return [
'enabled' => true,
'default_timeout' => 30,
'api_endpoint' => env('MYPACKAGE_API_ENDPOINT', 'https://api.example.com'),
'features' => [
'feature_one' => true,
'feature_two' => false,
],
];Create System/MyPackage/Database/Migrations/2025_01_01_000000_create_mypackage_table.php:
<?php
declare(strict_types=1);
use Database\Migration\BaseMigration;
use Database\Schema\Schema;
class CreateMypackageTable extends BaseMigration
{
public function up(): void
{
Schema::create('mypackage_items', function ($table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('mypackage_items');
}
}Create System/MyPackage/Commands/MyPackageCommand.php:
<?php
declare(strict_types=1);
namespace MyPackage\Commands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class MyPackageCommand extends Command
{
protected function configure(): void
{
$this->setName('mypackage:sync')
->setDescription('Synchronize MyPackage data');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->success('MyPackage synchronized successfully!');
return Command::SUCCESS;
}
}Create System/MyPackage/Providers/MyPackageServiceProvider.php:
<?php
declare(strict_types=1);
namespace MyPackage\Providers;
use Core\Services\ServiceProvider;
use MyPackage\Services\MyPackageService;
class MyPackageServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->container->singleton(MyPackageService::class);
}
public function boot(): void
{
// Boot logic (if needed)
}
}php dock package:install MyPackageThis will automatically:
- Publish your configuration.
- Register your Service Provider and Middleware.
- Run your migrations to create the
mypackage_itemstable.
You can now use your package immediately! Try running your sync command:
php dock mypackage:syncSystem/SimplePackage/
├── setup.php
└── Config/
└── simple.php
System/StandardPackage/
├── setup.php
├── Config/
│ └── standard.php
└── Providers/
└── StandardServiceProvider.php
System/FullPackage/
├── setup.php
├── Config/
│ ├── config.php
│ └── api.php
├── Database/
│ └── Migrations/
│ ├── 2025_01_01_000000_create_table_one.php
│ └── 2025_01_02_000000_create_table_two.php
├── Commands/
│ ├── SyncCommand.php
│ └── CleanupCommand.php
├── Providers/
│ ├── ServiceProvider.php
│ └── EventServiceProvider.php
└── Middleware/
├── AuthMiddleware.php
└── RateLimitMiddleware.php
| Type | Convention | Example |
|---|---|---|
| Config | packagename.php |
watcher.php, queue.php |
| Migration | YYYY_MM_DD_HHMMSS_description.php |
2025_01_01_120000_create_users_table.php |
| Command | PascalCaseCommand.php |
SyncDataCommand.php |
| Provider | PascalCaseServiceProvider.php |
QueueServiceProvider.php |
| Middleware | PascalCaseMiddleware.php |
AuthenticationMiddleware.php |
The setup.php file is the package's manifest that tells the installer what to register.
<?php
declare(strict_types=1);
return [
'providers' => [
// List of service provider class names
],
'middleware' => [
'web' => [
// Web middleware classes
],
'api' => [
// API middleware classes
],
],
];<?php
return [
'providers' => [
MyPackage\Providers\MyServiceProvider::class,
MyPackage\Providers\MyEventServiceProvider::class,
],
'middleware' => [],
];<?php
return [
'providers' => [],
'middleware' => [
'web' => [
MyPackage\Middleware\WebGuardMiddleware::class,
],
],
];<?php
return [
'providers' => [
Watcher\Providers\WatcherServiceProvider::class,
],
'middleware' => [
'web' => [
Watcher\Middleware\WatcherMiddleware::class,
],
],
];<?php
return [
'providers' => [
ApiAuth\Providers\ApiServiceProvider::class,
],
'middleware' => [
'api' => [
ApiAuth\Middleware\ApiKeyMiddleware::class,
ApiAuth\Middleware\RateLimitMiddleware::class,
],
],
];If your package only provides configs or migrations without any services:
<?php
return [
'providers' => [],
'middleware' => [],
];Track user events and analytics
System/Analytics/
├── setup.php
├── Config/
│ └── analytics.php
├── Database/
│ └── Migrations/
│ └── 2025_01_15_000000_create_analytics_events_table.php
├── Providers/
│ └── AnalyticsServiceProvider.php
└── Middleware/
└── TrackAnalyticsMiddleware.php
<?php
return [
'providers' => [
Analytics\Providers\AnalyticsServiceProvider::class,
],
'middleware' => [
'web' => [
Analytics\Middleware\TrackAnalyticsMiddleware::class,
],
],
];php dock package:install AnalyticsIntegrate Stripe payment processing
System/PaymentGateway/
├── setup.php
├── Config/
│ └── payment.php
├── Database/
│ └── Migrations/
│ ├── 2025_01_20_000000_create_payments_table.php
│ └── 2025_01_20_000001_create_subscriptions_table.php
├── Commands/
│ ├── SyncPaymentsCommand.php
│ └── ProcessRefundsCommand.php
└── Providers/
└── PaymentServiceProvider.php
<?php
return [
'providers' => [
PaymentGateway\Providers\PaymentServiceProvider::class,
],
'middleware' => [],
];<?php
return [
'stripe' => [
'public_key' => env('STRIPE_PUBLIC_KEY'),
'secret_key' => env('STRIPE_SECRET_KEY'),
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
'currency' => env('PAYMENT_CURRENCY', 'USD'),
'supports_subscriptions' => true,
];php dock package:install PaymentGatewayphp dock payments:sync
php dock payments:process-refundsCustom multi-factor authentication
packages/MFAAuth/
├── setup.php
├── Config/
│ └── mfa.php
├── Database/
│ └── Migrations/
│ └── 2025_01_25_000000_create_mfa_tokens_table.php
├── Middleware/
│ ├── RequireMFAMiddleware.php
│ └── ValidateMFATokenMiddleware.php
└── Providers/
├── MFAServiceProvider.php
└── MFAEventServiceProvider.php
<?php
return [
'providers' => [
MFAAuth\Providers\MFAServiceProvider::class,
MFAAuth\Providers\MFAEventServiceProvider::class,
],
'middleware' => [
'web' => [
MFAAuth\Middleware\RequireMFAMiddleware::class,
],
'api' => [
MFAAuth\Middleware\ValidateMFATokenMiddleware::class,
],
],
];php dock package:install MFAAuthSaaS email marketing automation
packages/EmailMarketing/
├── setup.php
├── Config/
│ └── email_marketing.php
├── Database/
│ └── Migrations/
│ ├── 2025_02_01_000000_create_campaigns_table.php
│ ├── 2025_02_01_000001_create_subscribers_table.php
│ └── 2025_02_01_000002_create_campaign_logs_table.php
├── Commands/
│ ├── SendCampaignCommand.php
│ └── GenerateReportCommand.php
├── Providers/
│ └── EmailMarketingServiceProvider.php
└── Middleware/
└── TrackEmailOpenMiddleware.php
<?php
return [
'providers' => [
EmailMarketing\Providers\EmailMarketingServiceProvider::class
],
'middleware' => [
'web' => [
EmailMarketing\Middleware\TrackEmailOpenMiddleware::class,
],
],
];php dock package:install EmailMarketing
php dock campaign:send --campaign=newsletterMulti-driver cloud storage (no migrations needed)
System/CloudStorage/
├── setup.php
├── Config/
│ └── filesystems.php
└── Providers/
└── StorageServiceProvider.php
php dock package:install CloudStorage$storage = resolve(StorageManager::class);
$storage->disk('s3')->put('file.pdf', $contents);PDF generation library
System/PdfGenerator/
├── PdfGenerator.php
├── PdfTemplate.php
└── Helpers/
├── FontHelper.php
└── ImageHelper.php
NO setup.php file - This package doesn't need installation!
Usage (Direct instantiation):
use PdfGenerator\PdfGenerator;
$pdf = new PdfGenerator();
$pdf->addPage()
->addText('Hello World')
->save('output.pdf');- No service providers to register
- No middleware to add
- No migrations required
- No CLI commands
- Just pure utility classes used directly in your code
System/StringUtils/
├── StringHelper.php
├── UrlBuilder.php
└── Slugify.php
use StringUtils\Slugify;
$slug = Slugify::make('Hello World'); // "hello-world"These packages work like any standard PHP library - just use the classes directly!
Always use a unique namespace for your package to avoid conflicts:
namespace MyCompany\MyPackage\Providers;Never hardcode API keys or secrets in config files:
// Bad
'api_key' => '12345-secret-key',
// Good
'api_key' => env('MYPACKAGE_API_KEY'),Ensure migrations can be rolled back during uninstallation:
public function down(): void
{
Schema::dropIfExists('my_table');
}Use timestamps in migration filenames to avoid conflicts:
2025_01_15_120000_create_users_table.php
2025_01_15_120100_add_email_to_users.php
Always test the full installation/uninstallation cycle:
# Test install
php dock package:install MyPackage
# Test commands work
php dock mypackage:test
# Test uninstall
php dock package:uninstall MyPackageError: Package not found at: /path/to/package
Solution: Verify the package directory exists. The system searches both packages/ and System/ automatically.
Issue: Middleware not appearing in routes.
Solution: Check that setup.php has the correct middleware group (web or api).
Issue: Command doesn't appear in php dock list.
Solution:
- Verify the command file exists in package's
Commands/directory - Ensure the command extends
Symfony\Component\Console\Command\Command - Check class namespace matches the directory structure
- Clear any PHP opcache:
php dock cache:clear(if available)
Issue: Some files installed, others missing.
Solution: Run the installer again. It will auto-detect and prompt for repair:
php dock package:install MyPackageError: Failed to copy file: /source/path to /dest/path
Causes:
- Insufficient permissions on destination directory
- Disk space full
- Source file doesn't exist or is locked
Solution:
- Check filesystem permissions:
chmod 755 App/Config - Verify disk space:
df -h - Ensure source files exist in the package directory
- Try running with elevated permissions (if safe)
- Use
package:installto install packages - Use
package:uninstallto cleanly remove packages - Create
setup.phpas your package manifest - Follow directory structure conventions
- Test your package before distribution
- The system auto-detects and repairs corrupted installations
For more information, see the Service Providers and Middleware documentation.