PHPackages                             taujor/cally - 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. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. taujor/cally

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

taujor/cally
============

Cally is a lightweight, immutable registry for storing and retrieving data or services in PHP. It provides a simple yet powerful way to manage configurations, dependencies, or any key-value pairs with support for immutability and PSR-11 compliance. Cally is ideal for any project where a full-fledged dependency injection container is unnecessary.

1.0.0(5mo ago)71MITPHPPHP ^8.1

Since Nov 17Pushed 5mo agoCompare

[ Source](https://github.com/Taujor/Cally)[ Packagist](https://packagist.org/packages/taujor/cally)[ Docs](https://github.com/taujor/cally)[ GitHub Sponsors](https://github.com/taujor)[ RSS](/packages/taujor-cally/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

**Cally — A Lightweight PSR-11 Service Locator**
================================================

[](#cally--a-lightweight-psr-11-service-locator)

Cally is a minimal, elegant, and strict PSR-11–compatible service locator for PHP 8+.
It provides a simple API for defining services, factories, singletons, lazy-loaded objects, and values.

Cally is designed to be:

- **Small** – no dependencies
- **Predictable** – explicit, no magic
- **Strict** – throws meaningful exceptions
- **PSR-11 compliant** – works with existing standards
- **Fast** – closures only, no reflection

---

**Features**
------------

[](#features)

- **PSR-11 `ContainerInterface`** compatible
- Register **lazy-loaded services**
- Register **singletons**
- Register **factories**
- Register simple **values**
- **Freeze** the container to prevent modification
- Meaningful exception hierarchy:
    - `FrozenRegistryException`
    - `KeyAlreadyExistsException`
    - `KeyNotFoundException`

**Missing Essentials**
----------------------

[](#missing-essentials)

- **No circular dependency detection** - Lazy services could create infinite loops if A depends on B depends on A
- **No error context** - Exceptions don't capture which dependency failed during complex resolution chains
- **No type safety** - Can't specify/validate what type a key should return
- **No autowiring** - Must manually register everything (fine for simple apps, tedious for large ones)
- **No container awareness** - Factories can't receive the container itself to resolve their own dependencies
- **No unfreeze/clear** - Once frozen, you're stuck (problematic for testing)

**Debatable Missing Features**
------------------------------

[](#debatable-missing-features)

- **Aliases** - Point multiple keys to same service
- **Tags/groups** - Retrieve all services of a certain type
- **Service decoration/extension** - Wrap existing services
- **Performance** - No caching of resolution paths for complex dependency graphs

**Production Readiness**
------------------------

[](#production-readiness)

For a small-to-medium application with straightforward dependencies, it's probably fine. For production at scale, you'd likely want at least circular dependency detection and better error context. The rest depends on your specific needs.

Future
------

[](#future)

I plan to implement all missing features (and possibly some debatable ones) in future, while also staying commited to Cally's minimalistic nature.

---

**Installation**
----------------

[](#installation)

Install via Composer:

```
composer require taujor/cally
```

---

**Quick Start**
---------------

[](#quick-start)

### **Create the container**

[](#create-the-container)

```
use Taujor\Cally\Cally;

$container = new Cally();
```

---

**Registering Services**
------------------------

[](#registering-services)

### **Singleton**

[](#singleton)

A single shared instance:

```
$pdo = new PDO('sqlite::memory:');

$container->singleton('db', $pdo);
```

Usage:

```
$db = $container->get('db'); // always returns the same instance
```

---

### **Lazy Service**

[](#lazy-service)

Instantiated only once on first use:

```
$container->lazy('config', function () {
    return parse_ini_file('app.ini');
});
```

---

### **Factory**

[](#factory)

Produces a new instance every time:

```
$container->factory('uuid', fn() => bin2hex(random_bytes(16)));

$id1 = $container->get('uuid');
$id2 = $container->get('uuid');

// always different
```

---

### **Value**

[](#value)

Stores a simple immutable value:

```
$container->value('version', '1.0.0');

echo $container->get('version'); // "1.0.0"
```

---

**Retrieving Services**
-----------------------

[](#retrieving-services)

```
$service = $container->get('key');
```

If the key does not exist:
`KeyNotFoundException`

---

**Checking Keys**
-----------------

[](#checking-keys)

```
if ($container->has('cache')) {
    // ...
}
```

---

**Freezing the Container**
--------------------------

[](#freezing-the-container)

After freezing the container, no new services can be registered.

```
$container->freeze();

$container->set('foo', fn() => 'bar');
// Throws FrozenRegistryException
```

---

**Error Handling**
------------------

[](#error-handling)

Cally throws clear, meaningful exceptions:

ExceptionTrigger`FrozenRegistryException`Attempt to modify a frozen container`KeyAlreadyExistsException`Attempt to overwrite a key`KeyNotFoundException`Attempt to get a missing keyAll exceptions implement PSR-11 interfaces where appropriate.

---

**Why Cally?**
--------------

[](#why-cally)

- No unnecessary abstraction layers
- A clean alternative to overly complex DI containers
- Explicit over magic

---

**License**
-----------

[](#license)

MIT license

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance71

Regular maintenance activity

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

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

Unknown

Total

1

Last Release

174d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6d96ca2ae7f1c44f4153bd9d9351c76a742ba7bffa0809fa9559b17bd8718e84?d=identicon)[Taujor](/maintainers/Taujor)

---

Top Contributors

[![Taujor](https://avatars.githubusercontent.com/u/159553730?v=4)](https://github.com/Taujor "Taujor (6 commits)")

---

Tags

phpcontainerPSR-11dependency-injectiondiimmutableregistry

### Embed Badge

![Health badge](/badges/taujor-cally/health.svg)

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

###  Alternatives

[php-di/php-di

The dependency injection container for humans

2.8k48.9M994](/packages/php-di-php-di)

PHPackages © 2026

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