Download the PHP package mrkindy/multi-tenant-wordpress without Composer

On this page you can find all versions of the php package mrkindy/multi-tenant-wordpress. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package multi-tenant-wordpress

MrKindy Multi-Tenant WordPress

Database-per-tenant bootstrap for WordPress and WooCommerce SaaS platforms. One WordPress codebase resolves the request host, reads tenant routing from an independent control database, retrieves the database password from a secret provider, and defines WordPress database constants before wpdb is created.

The package does not require WordPress Multisite, Bedrock, Laravel, or another framework. It supports PHP 8.3+, WordPress Core, Bedrock, Docker, FrankenPHP, Nginx, and Apache.

Why not WordPress Multisite?

While WordPress Multisite is a built-in feature, it often falls short for SaaS platforms due to its shared database architecture. This package offers several advantages over Multisite:

Installation

Required PHP extensions are PDO, JSON, and Sodium. The control database and tenant databases require separate credentials. The WordPress runtime control database account should have read-only access to the tenants table. Tenant provisioning jobs that call PdoTenantRepository::create(),PdoTenantRepository::update(), and PdoTenantRepository::delete() will need a separate writer account with INSERT, UPDATE, and DELETE access.

Request Lifecycle

Bootstrap::boot() performs these operations before WordPress loads:

  1. Reads and normalizes $_SERVER['HTTP_HOST'].
  2. Rejects empty, malformed, IP, untrusted, and disallowed localhost hosts.
  3. Resolves the tenant through the cache and PDO control repository.
  4. Rejects any tenant whose status is not active.
  5. retrieves the tenant password through the configured secret provider.
  6. Defines DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST.

Host validation intentionally happens before the control-database query.

Configuration

trustedDomainSuffixes accepts wildcard suffixes and literal suffixes. *.example.com matches subdomains but not example.com; example.com matches the apex and its subdomains. An empty list allows any syntactically valid hostname. Production deployments should always provide an allowlist.

Custom implementations can be injected with tenantRepository, customSecretProvider, and customCache. The bundled array cache is request-local under traditional PHP and process-local under long-running servers. It encrypts cached tenant payloads with encryptionKey, which must be a base64-encoded 32-byte Sodium key. Use a bounded external Redis or Memcached implementation in a multi-node deployment.

Control Database Schema

Apply the base schema first:

Then apply the provisioning migration:

The encrypted_database_password column stores an opaque reference, never a plaintext password:

Normalize domains to lowercase without ports or trailing dots before insert. Each tenant database user should have access only to its own database.

Tenant Provisioning

The package includes a complete automated provisioning system that creates databases, installs WordPress, and seeds default content.

Provisioning Flow

  1. Create Tenant Record - Insert tenant with pending status
  2. Provision - Run the provisioner which:
    • Creates database and database user
    • Installs WordPress schema using dbDelta()
    • Seeds default pages (Home, Privacy Policy, Terms)
    • Creates WordPress admin account
    • Marks tenant as installed then active

Quick Provisioning Example

See examples/create-tenant.php and examples/provision-tenant.php for complete standalone examples.

Provisioning Status States

Database Credentials for Provisioning

Provisioning requires elevated database privileges:

The runtime WordPress user only needs:

Asynchronous Provisioning with Events

For production, dispatch provisioning jobs asynchronously:

Manual Tenant Management

Tenant records can be managed through PdoTenantRepository:

See examples/update-tenant.php and examples/delete-tenant.php for standalone examples. Custom provisioning repositories can implement TenantProvisioningRepositoryInterface without changing runtime tenant lookup.

The equivalent SQL record is:

metadata.uploads_path is reserved for future uploads isolation support. This release isolates databases and configuration; it does not rewrite WordPress upload paths.

Storage Folder Isolation

Each tenant gets a unique storage folder for complete file isolation. The storage_folder column stores a unique folder name generated automatically during tenant creation.

Storage Folder Configuration

Storage Folder Structure

Each tenant's storage folder follows this pattern:

Example: /var/www/tenants/tenant_42_a7x9k2m8pQ3LwRtZvBnJy/

The folder name includes:

Accessing Tenant Storage

The Tenant DTO provides the storage folder path:

WordPress Uploads Configuration

The bootstrap automatically configures WordPress uploads for each tenant:

This ensures each tenant's media files are completely isolated from other tenants.

WordPress Core Integration

Require Composer and boot the package in wp-config.php before this line:

Do not define the four database constants before bootstrapping. See examples/wordpress-core.php for generic HTTP error handling. The bootstrap returns the resolved immutable Tenant DTO when application code needs tenant metadata.

Bedrock Integration

Place the bootstrap near the top of config/application.php, after Composer autoloading and environment loading, but before Roots\Config::apply(). Remove Bedrock's normal DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST definitions. See examples/bedrock.php.

Bedrock is supported as an integration target, not required as a dependency.

Docker Integration

Pass only control-plane credentials and secret-provider configuration to the WordPress container. Do not inject every tenant password into a shared image.

Docker secrets exposed as files should be read by the application's configuration layer and passed to Config. See examples/docker.php. The same early-bootstrap rule applies to FrankenPHP, Nginx/PHP-FPM, and Apache.

AWS Secrets Manager

Set secretProvider to Config::SECRET_PROVIDER_AWS. AWS credentials are resolved by the AWS SDK default credential chain, so IAM roles for EC2, ECS, or EKS are preferred over static access keys.

The AWS secret may be a raw password or a JSON object:

Grant the runtime identity secretsmanager:GetSecretValue only for tenant secret ARNs it needs. Secret values are held in memory only long enough to configure WordPress.

Local Encryption

EncryptionService provides authenticated Sodium Secretbox encryption for control-plane tooling. Create it once with the configured key, then reuse that instance for every encryption and decryption operation:

Keys are base64 encoded and must be stored outside the control database. In production, generate the key once and pass it through Config::$encryptionKey from an environment variable or secret manager; do not generate a new key per request. Secret-provider references remain the recommended runtime model.

Security Model

Behind a reverse proxy, configure the proxy to replace the incoming Host header and allow only expected virtual hosts. This package deliberately does not trust X-Forwarded-Host.

Logging and Error Handling

Pass any PSR-3 logger through Config::$logger. Without one, NullLogger is used. At the web boundary, catch package exceptions and return generic pages:

Do not render exception traces or control-database errors to clients.

Testing and Quality

CI tests PHP 8.3 and 8.4, runs PHPStan level 9, enforces PSR-12, and fails below 90% statement coverage.

Troubleshooting

WordPress connects to the old database

The package ran after database constants were defined or after wp-settings.php. Move Bootstrap::boot() earlier and remove old constants.

Every request returns an invalid-host error

Check the proxy-preserved Host value and trustedDomainSuffixes. Wildcard entries do not match the apex domain.

Tenant not found

Store the normalized lowercase domain without a port or trailing dot. Confirm the control database user can select from tenants.

Secret unavailable

For environment secrets, the reference must be an uppercase variable name. For AWS, verify region, secret ID/ARN, IAM permissions, and the configured JSON password key.

Long-running server serves the wrong tenant

Do not define database constants once and then reuse the same PHP worker for different hosts. WordPress database constants are process-global and cannot be changed. FrankenPHP worker mode or other persistent runtimes must isolate one tenant per worker/process or use non-worker request execution.

Provisioning fails with database error

Ensure the provisioning database user has CREATE, DROP, CREATE USER, and GRANT OPTION privileges. Check installation_error column in the tenants table for specific error messages.

Tenant stuck in 'installing' status

If provisioning is interrupted, the tenant may remain in 'installing' status. The provisioning system is idempotent - you can safely re-run provisioning for the same tenant.


All versions of multi-tenant-wordpress with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
ext-json Version *
ext-pdo Version *
ext-sodium Version *
aws/aws-sdk-php Version ^3.300
filp/whoops Version ^2.18
psr/log Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package mrkindy/multi-tenant-wordpress contains the following files

Loading the files please wait ...