PHPackages                             cmatosbc/ananke - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. cmatosbc/ananke

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

cmatosbc/ananke
===============

A flexible PHP service container that supports conditional service instantiation, relying on PHP 8+ match expression.

1.3.0(1y ago)36gpl-3.0-or-laterPHPPHP &gt;=8.0

Since Dec 23Pushed 1y ago1 watchersCompare

[ Source](https://github.com/cmatosbc/ananke)[ Packagist](https://packagist.org/packages/cmatosbc/ananke)[ RSS](/packages/cmatosbc-ananke/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (2)Versions (5)Used By (0)

Ananke
======

[](#ananke)

[![PHP Lint](https://github.com/cmatosbc/ananke/actions/workflows/lint.yml/badge.svg)](https://github.com/cmatosbc/ananke/actions/workflows/lint.yml) [![PHPUnit Tests](https://github.com/cmatosbc/ananke/actions/workflows/phpunit.yml/badge.svg)](https://github.com/cmatosbc/ananke/actions/workflows/phpunit.yml) [![PHP Composer](https://github.com/cmatosbc/ananke/actions/workflows/composer.yml/badge.svg)](https://github.com/cmatosbc/ananke/actions/workflows/composer.yml) [![Latest Stable Version](https://camo.githubusercontent.com/41ddfc5fd493642ee5659c62cc9307f342af556dd86f6fa1f688caba87425760/68747470733a2f2f706f7365722e707567782e6f72672f636d61746f7362632f616e616e6b652f76)](https://packagist.org/packages/cmatosbc/ananke) [![License](https://camo.githubusercontent.com/f0cf5b53746e98465f59db87652115f180da9b9f2d3e03252c6bc367afdad0e7/68747470733a2f2f706f7365722e707567782e6f72672f636d61746f7362632f616e616e6b652f6c6963656e7365)](https://packagist.org/packages/cmatosbc/ananke)

A flexible PHP 8+ service container that supports conditional service instantiation. This package allows you to register services with multiple conditions that must be met before the service can be instantiated.

Requirements
------------

[](#requirements)

- PHP 8.0 or higher

Features
--------

[](#features)

- Register services with their class names and constructor parameters
- Define conditions as callable functions
- Associate multiple conditions with services
- Dynamic service instantiation based on condition evaluation
- Clear error handling with specific exceptions

Installation
------------

[](#installation)

```
composer require cmatosbc/ananke
```

Basic Usage
-----------

[](#basic-usage)

```
use Ananke\ServiceFactory;

$factory = new ServiceFactory();

// Register a service with constructor parameters
$factory->register('logger', Logger::class, ['debug']);

// Register conditions
$factory->registerCondition('is-development', fn() => getenv('APP_ENV') === 'development');
$factory->registerCondition('has-permissions', fn() => is_writable('/var/log'));

// Associate multiple conditions with service
$factory->associateCondition('logger', 'is-development');
$factory->associateCondition('logger', 'has-permissions');

// Create service (only works if ALL conditions are met)
if ($factory->has('logger')) {
    $logger = $factory->create('logger');
}
```

Multiple Conditions
-------------------

[](#multiple-conditions)

Services can have multiple conditions that must ALL be satisfied before instantiation:

```
// Premium feature example
$factory->register('premium.feature', PremiumFeature::class);

// Register all required conditions
$factory->registerCondition('is-premium-user', fn() => $user->hasPremiumSubscription());
$factory->registerCondition('feature-enabled', fn() => $featureFlags->isEnabled('new-feature'));
$factory->registerCondition('has-valid-license', fn() => $license->isValid());

// Associate ALL conditions with the service
$factory->associateCondition('premium.feature', 'is-premium-user');
$factory->associateCondition('premium.feature', 'feature-enabled');
$factory->associateCondition('premium.feature', 'has-valid-license');

// Service will only be created if ALL conditions are met
if ($factory->has('premium.feature')) {
    $feature = $factory->create('premium.feature');
}
```

Condition Decorators
--------------------

[](#condition-decorators)

Ananke provides a powerful set of condition decorators that allow you to compose complex condition logic:

### Not Condition

[](#not-condition)

Negate any condition:

```
use Ananke\Conditions\{NotCondition, CallableCondition};

// Basic condition
$factory->registerCondition('is-maintenance',
    new CallableCondition('is-maintenance', fn() => $maintenance->isActive()));

// Negate it
$factory->registerCondition('not-maintenance',
    new NotCondition($factory->getCondition('is-maintenance')));

// Use in service
$factory->register('api', APIService::class);
$factory->associateCondition('api', 'not-maintenance');
```

### Cached Condition

[](#cached-condition)

Cache expensive condition evaluations:

```
use Ananke\Conditions\CachedCondition;

// Cache an expensive API check for 1 hour
$factory->registerCondition('api-status',
    new CachedCondition(
        new CallableCondition('api-check', fn() => $api->checkStatus()),
        3600 // Cache for 1 hour
    ));
```

### AND/OR Conditions

[](#andor-conditions)

Combine multiple conditions with logical operators:

```
use Ananke\Conditions\{AndCondition, OrCondition};

// Premium access: User must be premium OR have a trial subscription
$factory->registerCondition('can-access-premium',
    new OrCondition([
        new CallableCondition('is-premium', fn() => $user->isPremium()),
        new CallableCondition('has-trial', fn() => $user->hasTrial())
    ]));

// Database write: Need both connection AND proper permissions
$factory->registerCondition('can-write-db',
    new AndCondition([
        new CallableCondition('is-connected', fn() => $db->isConnected()),
        new CallableCondition('has-permissions', fn() => $user->canWrite())
    ]));
```

### XOR/NOR Conditions

[](#xornor-conditions)

For more complex logical operations, you can use XOR (exclusive OR) and NOR conditions:

```
use Ananke\Conditions\{XorCondition, NorCondition};

// XOR: Feature must be enabled in EXACTLY one environment (dev XOR prod)
$factory->registerCondition('feature-enabled-single-env',
    new XorCondition([
        new CallableCondition('dev-enabled', fn() => $featureFlags->isEnabled('dev')),
        new CallableCondition('prod-enabled', fn() => $featureFlags->isEnabled('prod'))
    ]));

// NOR: Service is available only when NONE of the maintenance modes are active
$factory->registerCondition('all-systems-available',
    new NorCondition([
        new CallableCondition('db-maintenance', fn() => $maintenance->isDatabaseMaintenance()),
        new CallableCondition('api-maintenance', fn() => $maintenance->isApiMaintenance()),
        new CallableCondition('ui-maintenance', fn() => $maintenance->isUiMaintenance())
    ]));
```

### Complex Condition Compositions

[](#complex-condition-compositions)

Combine decorators for complex logic:

```
// ((isPremium OR hasTrial) AND notMaintenance) AND (hasQuota OR isUnlimited)
$factory->registerCondition('can-use-service',
    new AndCondition([
        // Premium access check
        new OrCondition([
            new CallableCondition('premium', fn() => $user->isPremium()),
            new CallableCondition('trial', fn() => $user->hasTrial())
        ]),
        // Not in maintenance
        new NotCondition(
            new CallableCondition('maintenance', fn() => $maintenance->isActive())
        ),
        // Resource availability
        new OrCondition([
            new CallableCondition('has-quota', fn() => $user->hasQuota()),
            new CallableCondition('unlimited', fn() => $user->isUnlimited())
        ])
    ])
);

// Cache the entire complex condition
$factory->registerCondition('cached-access-check',
    new CachedCondition(
        $factory->getCondition('can-use-service'),
        300 // Cache for 5 minutes
    )
);
```

Service Types
-------------

[](#service-types)

Ananke supports two types of service instantiation: Singleton and Prototype.

### Singleton Services

[](#singleton-services)

Singleton services are instantiated only once and the same instance is returned for subsequent requests. This is useful for services that maintain state or are resource-intensive to create.

```
use Ananke\ServiceFactory;

$factory = new ServiceFactory();

// Register a service as singleton
$factory->register('database', DatabaseConnection::class);
$factory->registerAsSingleton('database');

// Both variables will reference the same instance
$db1 = $factory->create('database');
$db2 = $factory->create('database');

assert($db1 === $db2); // true
```

You can also clear singleton instances when needed:

```
// Clear all singleton instances
$factory->clearSingletons();

// Now you'll get a new instance
$db3 = $factory->create('database');
assert($db3 !== $db1); // true
```

### Prototype Services

[](#prototype-services)

Prototype services create a new instance every time they are requested. This is the default behavior and is ideal for services that should not share state.

```
use Ananke\ServiceFactory;

$factory = new ServiceFactory();

// Register a service (prototype by default)
$factory->register('transaction', Transaction::class);

// Or explicitly register as prototype
$factory->registerAsPrototype('transaction');

// Each call creates a new instance
$tx1 = $factory->create('transaction');
$tx2 = $factory->create('transaction');

assert($tx1 !== $tx2); // true
```

### Changing Service Types

[](#changing-service-types)

You can change a service's type after registration:

```
use Ananke\ServiceFactory;

$factory = new ServiceFactory();
$factory->register('cache', CacheService::class);

// Start as singleton
$factory->changeServiceType('cache', 'singleton');
$cache1 = $factory->create('cache');
$cache2 = $factory->create('cache');
assert($cache1 === $cache2); // true

// Switch to prototype
$factory->changeServiceType('cache', 'prototype');
$cache3 = $factory->create('cache');
$cache4 = $factory->create('cache');
assert($cache3 !== $cache4); // true
```

Best Practices
--------------

[](#best-practices)

1. **Caching**: Use `CachedCondition` for:

    - External API calls
    - Database queries
    - File system checks
    - Any expensive operations
2. **Composition**: Build complex conditions gradually:

    - Start with simple conditions
    - Combine them using AND/OR
    - Add negation where needed
    - Cache at appropriate levels
3. **Naming**: Use clear, descriptive names:

    - Negated: prefix with 'not-'
    - Cached: prefix with 'cached-'
    - Combined: use descriptive action names
4. **Testing**: Test complex conditions thoroughly:

    - Verify each sub-condition
    - Test boundary cases
    - Ensure proper short-circuit evaluation
    - Validate cache behavior

Real-World Use Cases
--------------------

[](#real-world-use-cases)

### 1. Environment-Specific Services

[](#1-environment-specific-services)

Control debug tools based on environment:

```
$factory->register('debugger', Debugger::class);
$factory->registerCondition('is-development', fn() => getenv('APP_ENV') === 'development');
$factory->registerCondition('debug-enabled', fn() => getenv('APP_DEBUG') === 'true');
$factory->associateCondition('debugger', 'is-development');
$factory->associateCondition('debugger', 'debug-enabled');
```

### 2. Feature Flags and A/B Testing

[](#2-feature-flags-and-ab-testing)

Implement feature toggles with multiple conditions:

```
$factory->register('new.ui', NewUIComponent::class);
$factory->registerCondition('feature-enabled', fn() => $featureFlags->isEnabled('new-ui'));
$factory->registerCondition('in-test-group', fn() => $abTest->isInGroup('new-ui-test'));
$factory->registerCondition('supported-browser', fn() => $browser->supportsFeature('grid-layout'));
$factory->associateCondition('new.ui', 'feature-enabled');
$factory->associateCondition('new.ui', 'in-test-group');
$factory->associateCondition('new.ui', 'supported-browser');
```

### 3. Database Connection Management

[](#3-database-connection-management)

Safe handling of database-dependent services:

```
$factory->register('user.repository', UserRepository::class);
$factory->registerCondition('db-connected', fn() => $database->isConnected());
$factory->registerCondition('db-migrated', fn() => $database->isMigrated());
$factory->registerCondition('has-permissions', fn() => $database->hasPermissions('users'));
$factory->associateCondition('user.repository', 'db-connected');
$factory->associateCondition('user.repository', 'db-migrated');
$factory->associateCondition('user.repository', 'has-permissions');
```

### 4. License-Based Feature Access

[](#4-license-based-feature-access)

Control access to premium features:

```
$factory->register('premium.api', PremiumAPIClient::class);
$factory->registerCondition('has-license', fn() => $license->isValid());
$factory->registerCondition('within-quota', fn() => $usage->isWithinQuota());
$factory->registerCondition('api-available', fn() => $api->isAvailable());
$factory->associateCondition('premium.api', 'has-license');
$factory->associateCondition('premium.api', 'within-quota');
$factory->associateCondition('premium.api', 'api-available');
```

Error Handling
--------------

[](#error-handling)

The service container throws specific exceptions:

- `ServiceNotFoundException`: When trying to create a non-registered service
- `ClassNotFoundException`: When registering a service with a non-existent class
- `InvalidArgumentException`: When a condition is not met or invalid

Testing
-------

[](#testing)

Run the test suite:

```
composer test
```

The tests provide detailed output showing the state of conditions and service creation:

```
🧪 Test: Multiple Conditions
    ✅ Registered premium feature service
    ✅ Registered all conditions

    📊 Current State:
       • Premium Status: ✅
       • Feature Flag: ✅
       • Valid License: ❌
    ℹ️  Testing with incomplete conditions
    ✅ Verified feature is not available

```

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

This project is licensed under the GPL-3.0-or-later License - see the LICENSE file for details.

###  Health Score

27

—

LowBetter than 49% of packages

Maintenance40

Moderate activity, may be stable

Popularity8

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

4

Last Release

502d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3d97279f52b70d502ac21c0618e2822a0bae0523a7e69bb3fd33a592f21d5f06?d=identicon)[cmatosbc](/maintainers/cmatosbc)

---

Top Contributors

[![cmatosbc](https://avatars.githubusercontent.com/u/118901076?v=4)](https://github.com/cmatosbc "cmatosbc (16 commits)")

---

Tags

php-conditional-instancesphp-containersphp-customphp-design-patternsphp-generatorsphp-libraryphp-matchphp-modernphp-service-containerphp8singleton-pattern

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/cmatosbc-ananke/health.svg)

```
[![Health](https://phpackages.com/badges/cmatosbc-ananke/health.svg)](https://phpackages.com/packages/cmatosbc-ananke)
```

###  Alternatives

[spatie/laravel-feed

Generate rss feeds

9743.6M28](/packages/spatie-laravel-feed)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
