Fence (Class Initialization Barrier)#
The Fence package provides a single core trait (Fence)
and three lightweight wrappers (Single, Multi, Limit) that let you:
Control exactly how many objects of a class may exist.
Choose whether instances are “keyed” by string or always a singleton.
Enforce optional PHP‐extension/class requirements at startup.
Inspect or reset active instances.
Everything lives in one place—no base classes, just include the trait you need.
Key Concepts#
Unified base (Fence)
Implements all logic for requirement checks, keyed vs. singleton behavior,
and instance‐count limits. You do not use Fence directly; one of the three
wrapper traits sets two class‐constants and the core logic resolves/retrieves
instances through ::instance().
Singleton (Single)
Classes define FENCE_KEYED = false. Only one instance can exist.
Key is ignored and stored in the internal '__single' slot.
Multiton (Multi)
Classes define FENCE_KEYED = true. Multiple instances allowed, keyed by first argument.
If omitted, key defaults to 'default'.
Limited Multiton (Limit)
Extends Multi with configurable instance limits.
Classes define FENCE_KEYED = true and FENCE_LIMIT = <int>.
Caller-provided keys are used until the configured limit is reached.
setLimit(int) changes the limit at runtime.
Constants Available
FENCE_KEYED– Whether class uses keyed instances (bool)FENCE_LIMIT– Maximum instances allowed (int)
Requirement Checking
::instance() accepts optional constraints array with extensions and/or
classes. If any extension or class is missing, a RequirementException is thrown
before a new instance is created.
New Features Added
Instance inspection –
hasInstance(),countInstances(),getInstances(),getKeys()Cache management –
clearInstances()for testingRuntime limit override –
setLimit()for dynamic configurationEnhanced error handling – Better exception messages and validations new object
Singleton (Single)
namespace App;
use Infocyph\InterMix\Fence\Single;
class Config
{
use Single;
}
// Usage:
$cfgA = Config::instance(); // new Config
$cfgB = Config::instance(); // same object as $cfgA
Config::hasInstance(); // true
Config::countInstances(); // 1
Config::getKeys(); // ["__single"]
Config::clearInstances(); // resets so next instance() yields new object
Multiton (Multi)
namespace App;
use Infocyph\InterMix\Fence\Multi;
class Connection
{
use Multi;
}
// Usage:
$conn1 = Connection::instance("db1");
$conn2 = Connection::instance("db2");
$conn3 = Connection::instance("db1"); // returns same as $conn1
Connection::countInstances(); // 2
Connection::getKeys(); // ["db1", "db2"]
Connection::hasInstance("db3"); // false
Connection::clearInstances();
Limited Multiton (Limit)
namespace App;
use Infocyph\InterMix\Fence\Limit;
class ReportCache
{
use Limit;
}
// By default, Limit uses 2. Override at runtime:
ReportCache::instance("rA");
ReportCache::instance("rB");
ReportCache::instance("rC"); // throws LimitExceededException if >2 and setLimit not called
ReportCache::setLimit(3); // Override to accept 3
ReportCache::instance("rC"); // now allowed
Requirement Checking
::instance() accepts an optional constraints array with extensions and/or
classes. If any extension or class is missing, a RequirementException is thrown
before any new instance is created.
Limit Enforcement
Attempting to create more instances than the configured limit throws
LimitExceededException.
Instance Inspection & Management
The wrapper traits supply static helpers:
hasInstance(?string $key = "default")→ boolcountInstances()→ intgetInstances()→ array(key → instance)getKeys()→ array of slots/keysclearInstances()→ resets to emptysetLimit(int $n)(only onLimit)
Exceptions#
RequirementException
Raised if provided constraints refer to missing extensions or classes. The message lists exactly which items were not found.
LimitExceededException
Raised if you attempt to create a new instance when the count of existing instances has reached the configured limit.
InvalidArgumentException
Raised by
setLimit()if you pass an integer less than 1.
Usage Examples#
Defining classes:
namespace App;
use Infocyph\InterMix\Fence\Single;
use Infocyph\InterMix\Fence\Multi;
use Infocyph\InterMix\Fence\Limit;
class Config {
use Single;
}
class Connection {
use Multi;
}
class ReportCache {
use Limit;
}
Creating and inspecting instances:
// SINGLETON:
$cfgA = Config::instance(); // new Config
$cfgB = Config::instance(); // same object as $cfgA
Config::hasInstance(); // true
Config::countInstances(); // 1
Config::getKeys(); // ["__single"]
Config::clearInstances(); // resets so next instance() is new
// MULTITON:
$conn1 = Connection::instance("db1");
$conn2 = Connection::instance("db2");
$conn3 = Connection::instance("db1"); // returns same as $conn1
Connection::countInstances(); // 2
Connection::getKeys(); // ["db1", "db2"]
Connection::hasInstance("db3"); // false
Connection::clearInstances();
// LIMITED MULTITON:
ReportCache::instance("rA");
ReportCache::instance("rB");
// Next line throws LimitExceededException (limit=2 by default):
ReportCache::instance("rC");
// Change limit to 3:
ReportCache::setLimit(3);
ReportCache::instance("rC"); // now allowed
ReportCache::countInstances(); // 3
Applying requirements:
// Suppose you need 'curl' and 'mbstring' extensions and 'PDO' class:
try {
$db = Connection::instance("main", [
'extensions' => ['curl','mbstring'],
'classes' => ['PDO'],
]);
} catch (RequirementException $e) {
echo $e->getMessage();
}
// If any extension/class is missing → RequirementException thrown earlier.
Best Practices#
**Always call
::instance()** instead ofnew.If your class must remain a singleton, use
Single.If you need per‐key instances, use
Multi.If you want to cap how many objects can coexist, use
Limit.To enforce startup requirements, pass a constraints array to
::instance()and catchRequirementException.Prefer
countInstances(),hasInstance()andgetKeys()for runtime inspection; usegetInstances()mainly for debug/tests.