Utils Package Cheatsheet¶
Dense reference for Cognesy/Utils package capabilities. All examples show public API methods only.
Core Utilities¶
Json - JSON Processing¶
// Creation
Json::fromString('{"key":"value"}') // Extract complete JSON from text
Json::fromPartial('{"key":"val') // Handle incomplete JSON
Json::fromArray(['key' => 'value']) // Convert array to Json
Json::none() // Empty JSON
// Conversion
$json->toArray() // → array (JSON root must be object or array)
$json->toString() // → string
$json->isEmpty() // → bool
$json->format(JSON_PRETTY_PRINT) // → formatted string
$json->format(0, 64) // → formatted string with depth limit
// Static helpers
Json::decode('{"a":1}', []) // → mixed, with default fallback
Json::encode(['a' => 1]) // → string
Json::encode(['a' => 1], JSON_PRETTY_PRINT) // → string with options
Notes:
- Json::encode() and Json::fromArray() throw InvalidArgumentException on encoding failure.
- Json::decode() throws JsonException when no default is provided and input is invalid; returns default otherwise.
Result - Monadic Error Handling¶
// Creation
Result::success($value) // → Success
Result::failure($error) // → Failure
Result::from($anyValue) // Auto-wrap (Throwable → failure, Result → passthrough)
Result::try(fn() => riskyOperation()) // Catch exceptions → Result
// Checking state
$result->isSuccess() // → bool
$result->isFailure() // → bool
$result->isSuccessAndNull() // → bool (success with null value)
$result->isSuccessAndTrue() // → bool (success with true value)
$result->isSuccessAndFalse() // → bool (success with false value)
$result->isType('string') // → bool (success + gettype matches)
$result->isInstanceOf(Foo::class) // → bool (success + instanceof)
$result->matches(fn($v) => $v > 0) // → bool (success + predicate)
// Value access
$result->unwrap() // → value (throws on Failure)
$result->error() // → error value (throws on Success)
$result->exception() // → Throwable (throws on Success)
$result->valueOr('default') // Get value or fallback
$result->exceptionOr(null) // Get exception or fallback
// Success-side transformations
$result->map(fn($v) => transform($v)) // Transform success value
$result->then(fn($v) => Result::success(transform($v))) // Chain Results
$result->ensure(fn($v) => $v > 0, fn($v) => 'too small') // Guard predicate → Failure
$result->tap(fn($v) => log($v)) // Side effect, returns self (or Failure on throw)
// Failure-side transformations
$result->recover(fn($e) => fallback($e)) // Recover from failure → Success
$result->mapError(fn($e) => new MyException($e)) // Transform error value
// Side effects
$result->ifSuccess(fn($v) => log($v)) // Execute on success, returns self
$result->ifFailure(fn($e) => log($e)) // Execute on failure, returns self
// Batch operations
Result::tryAll($args, $callback1, $callback2) // Try all, collect results or errors
Result::tryUntil($condition, $args, $callbacks) // Until condition met
Notes:
- Failure::errorMessage() converts any error to a string representation.
Option - Optional Value Type¶
// Creation
Option::some($value) // Wrap value in Some
Option::none() // Create None
Option::fromNullable($valueOrNull) // null → None, otherwise Some
Option::fromResult($result) // Success → Some, Failure → None
// Checking state
$opt->isSome() // → bool
$opt->isNone() // → bool
$opt->exists(fn($v) => $v > 0) // Some + predicate holds → true
$opt->forAll(fn($v) => $v > 0) // None or predicate holds → true
// Transformations
$opt->map(fn($v) => $v * 2) // Transform inner value
$opt->flatMap(fn($v) => Option::some($v)) // Chain returning Option
$opt->andThen(fn($v) => ...) // Alias for flatMap
$opt->filter(fn($v) => $v > 0) // Some + predicate → Some, else None
$opt->zipWith($other, fn($a,$b) => $a+$b) // Combine two Options
// Side effects
$opt->ifSome(fn($v) => log($v)) // Execute on Some, returns self
$opt->ifNone(fn() => log('empty')) // Execute on None, returns self
// Destructuring
$opt->match(fn() => 'none', fn($v) => $v) // Pattern match
$opt->getOrElse('default') // Value or fallback (value or callable)
$opt->orElse(Option::some(42)) // This Option or alternative Option
$opt->toNullable() // → value|null
// Conversion to Result
$opt->toResult(new RuntimeException('missing')) // None → failure
$opt->toSuccessOr('fallback') // None → success with default
Str - String Operations¶
// Splitting
Str::split('hello world', ' ') // → ['hello', 'world']
// Case conversion
Str::pascal('hello_world') // → HelloWorld
Str::snake('HelloWorld') // → hello_world
Str::camel('hello-world') // → helloWorld
Str::kebab('HelloWorld') // → hello-world
Str::title('hello world') // → Hello World
// Searching
Str::contains('text', 'needle', true) // Case sensitive search (default)
Str::containsAll('text', ['a','b']) // All needles present
Str::containsAny('text', ['a','b']) // Any needle present
Str::startsWith('text', 'prefix') // → bool
Str::endsWith('text', 'suffix') // → bool
// Extraction
Str::between('a<content>b', '<', '>') // → content
Str::after('prefix:content', ':') // → content
Str::limit('long text', 5, '...') // → long...
Str::limit('long text', 5, '...', STR_PAD_LEFT) // → ...text (trim from left)
Str::limit('long text', 5, '...', STR_PAD_RIGHT, false) // → long ... (no fit)
// Conditionals
Str::when(true, 'yes', 'no') // → yes
Arrays - Array Utilities¶
// Transformation
Arrays::asArray($value) // Force to array (null → [], scalar → [$value])
Arrays::map($array, fn($v,$k) => $v) // Map with key access
Arrays::flatten([$nested, $arrays]) // → flat array
Arrays::fromAny($object) // Convert any type to array (handles circular refs)
// Merging
Arrays::mergeNull($arr1, $arr2) // Merge handling nulls → ?array
Arrays::mergeMany($iterableOfArrays) // Efficiently merge many arrays
Arrays::mergeOver($items, fn($item, $key) => [...]) // Map to arrays then merge
// Manipulation
Arrays::unset($array, ['key1','key2']) // Remove keys
Arrays::removeTail($array, 2) // Remove last N elements
Arrays::removeRecursively($arr, $keys, $skip) // Deep key removal
// Validation
Arrays::isSubset($subset, $full) // → bool
Arrays::valuesMatch($arr1, $arr2) // Same length + same values, any order (see note on duplicates)
Arrays::hasOnlyStrings($array) // → bool
// Output
Arrays::toBullets($array) // → " - item1\n - item2"
Arrays::flattenToString($arrays, ' ') // Join non-empty nested leaves with a separator
Files - File System Operations¶
// Directory operations
Files::removeDirectory($path) // Recursive delete → bool
Files::copyDirectory($src, $dst) // Recursive copy (throws on error, rejects symlinks)
Files::renameFileExtensions($dir, 'md', 'mdx') // Batch rename
// File operations
Files::copyFile($src, $dst) // Copy with dir creation (throws)
// Iteration
Files::files($path) // → Iterator<SplFileInfo> (files only)
Files::directories($path) // → Iterator<SplFileInfo> (dirs only)
Data Structures¶
DataMap - Nested Data Access¶
// Creation
new DataMap(['key' => 'value'])
DataMap::fromArray($array)
DataMap::fromJson('{"key":"value"}') // JSON root must be object or array
// Dot notation access
$map->get('user.name', 'default') // Deep get with fallback
$map->set('user.name', 'John') // Deep set
$map->has('user.email') // → bool
$map->getType('user.age') // → 'integer'
// Magic access
$map->user // → DataMap|mixed
$map->user = 'value' // Set value
isset($map->user) // Check existence
// Conversion
$map->toArray() // → array
$map->toJson(JSON_PRETTY_PRINT) // → string
$map->fields() // → array of top-level keys
// Manipulation
$map->merge(['new' => 'data']) // Add/update data
$map->except('key1', 'key2') // New map without keys
$map->only('key1', 'key2') // New map with only keys
$map->with(['key' => 'value']) // New map with added data
// Advanced operations
$map->toMap('users.*') // Aimeos\Map with wildcard collection
$map->clone() // Deep clone
ImmutableDataMap - Read-Only DataMap¶
new ImmutableDataMap(['key' => 'value'])
$map->get('user.name', 'default') // Deep get with fallback
$map->has('user.email') // → bool
$map->toArray() // → array
$map->toJson(JSON_PRETTY_PRINT) // → string
$map->jsonSerialize() // → mixed (for json_encode)
CachedMap - Lazy-Loading Map¶
// Creation
new CachedMap(fn($key) => compute($key), $preloaded)
CachedMap::from(fn($key) => compute($key), $preloaded)
// Access
$map->get('key', ...$extraArgs) // Resolve once, cache result
$map->set('key', $value) // Manually set, bypassing producer
$map->has('key') // → bool (exists in cache)
$map->isResolved('key') // → bool (computed or manually set)
// Cache management
$map->forget('key') // Clear cache for key
$map->fresh() // Clear all cached values
$map->keys() // → array of cached keys
$map->toArray() // → array of all cached values
$map->count() // → int (resolved entries)
// Array access (implements ArrayAccess, IteratorAggregate, Countable)
$map['key'] // same as get('key')
$map['key'] = $value // same as set('key', $value)
isset($map['key']) // same as isResolved('key')
unset($map['key']) // same as forget('key')
foreach ($map as $k => $v) { } // iterate cached entries
Context - Typed Service Container¶
// Creation
Context::empty()
new Context(['Service' => $instance])
// Service management (class-string keys)
$ctx->with(Service::class, $service) // → new Context (immutable)
$ctx->get(Service::class) // → T (throws MissingServiceException)
$ctx->tryGet(Service::class) // → Result<T, MissingServiceException>
$ctx->has(Service::class) // → bool
$ctx->merge($otherContext) // → new Context (right-bias)
// Keyed service management (Key<T> tokens)
$key = Key::of('myLogger', LoggerInterface::class) // → Key<T>
$ctx->withKey($key, $logger) // → new Context (immutable)
$ctx->getKey($key) // → T (throws MissingServiceException)
Key - Typed Service Token¶
Key::of('id', ServiceClass::class) // → Key<T>
$key->id // → string (unique identifier)
$key->type // → class-string<T>
Layer - Context Composition¶
// Simple providers
Layer::provides(Service::class, $svc) // Static service
Layer::providesFrom(Service::class, fn($ctx) => new Svc()) // Factory
Layer::providesKey($key, $svc) // Keyed service
Layer::providesFromKey($key, fn($ctx) => new Svc()) // Keyed factory
// Composition
$layer->dependsOn($other) // → Layer (other builds first, then this)
$layer->referredBy($other) // → Layer (this builds first, then other)
$layer->merge($other) // → Layer (parallel merge, right-bias)
// Apply
$layer->applyTo($context) // → Context
Container - DI Container¶
// SimpleContainer — standalone container
$c = new SimpleContainer();
// PsrContainer — wraps any PSR-11, overlays writes
$c = new PsrContainer($psrContainer);
// Registration
$c->set('id', fn(Container $c) => new Svc()) // Transient (new each time)
$c->singleton('id', fn(Container $c) => new Svc()) // Singleton (cached)
$c->instance('id', $object) // Pre-built instance
// Resolution (PSR-11 compatible)
$c->get('id') // → mixed (throws NotFoundException)
$c->has('id') // → bool
ArrayList - Immutable Indexed List¶
// Creation
ArrayList::empty()
ArrayList::of($a, $b, $c)
ArrayList::fromArray([$a, $b, $c])
// Access
$list->count() // → int
$list->isEmpty() // → bool
$list->itemAt(0) // → T (throws OutOfBoundsException)
$list->getOrNull(0) // → T|null
$list->first() // → T|null
$list->last() // → T|null
// Immutable operations (return new list)
$list->withAppended($item1, $item2) // Append items
$list->withInserted(1, $item) // Insert at index
$list->withRemovedAt(0, 2) // Remove N items at index
$list->filter(fn($v) => $v > 0) // Filter by predicate
$list->map(fn($v) => $v * 2) // Transform items
$list->reduce(fn($acc, $v) => $acc + $v, 0) // Fold
$list->concat($otherList) // Concatenate lists
$list->reverse() // Reverse order
// Conversion
$list->all() // → list<T>
$list->toArray() // → list<T>
ArrayMap - Immutable Key-Value Map¶
// Creation
ArrayMap::empty()
ArrayMap::fromArray(['a' => 1, 'b' => 2])
// Access
$map->count() // → int
$map->has('key') // → bool
$map->get('key') // → V (throws OutOfBoundsException)
$map->getOrNull('key') // → V|null
$map->keys() // → list<K>
$map->values() // → list<V>
// Immutable operations (return new map)
$map->with('key', $value) // Add/replace entry
$map->withAll(['a' => 1, 'b' => 2]) // Add entries (existing keys preserved)
$map->withRemoved('key') // Remove entry (idempotent)
$map->merge($otherMap) // Merge (other wins on collisions)
// Conversion
$map->toArray() // → array<K,V>
ArraySet - Immutable Hash-Based Set¶
// Creation (requires hash function)
ArraySet::empty(fn($item) => $item->id())
ArraySet::fromValues(fn($item) => $item->id(), $values)
ArraySet::fromValues(fn($i) => $i->id(), $values, fn($a,$b) => $a->equals($b))
// Access
$set->count() // → int
$set->contains($item) // → bool
// Immutable operations (return new set)
$set->withAdded($item1, $item2) // Add items
$set->withRemoved($item) // Remove items
$set->union($otherSet) // Set union
$set->intersect($otherSet) // Set intersection
$set->diff($otherSet) // Set difference
// Conversion
$set->values() // → list<T>
Deque - Double-Ended Queue¶
$deque = new Deque();
$deque->pushFront($value) // Add to front
$deque->pushBack($value) // Add to back
$deque->popFront() // → T (throws UnderflowException)
$deque->popBack() // → T (throws UnderflowException)
$deque->peekFront() // → T (throws UnderflowException)
$deque->peekBack() // → T (throws UnderflowException)
$deque->size() // → int
$deque->isEmpty() // → bool
$deque->clear() // Remove all items
$deque->toArray() // → list<T> (front to back)
Buffer - FIFO Buffers¶
// ArrayBuffer — unbounded, array-based
$buf = new ArrayBuffer();
// SimpleRingBuffer — fixed-size circular, overwrites oldest when full
$buf = new SimpleRingBuffer(capacity: 100);
// Shared API (BufferInterface)
$buf->push($value) // Append item
$buf->pop() // → T (FIFO, throws UnderflowException)
$buf->count() // → int
$buf->isEmpty() // → bool
$buf->toArray() // → list<T> (oldest to newest)
// BoundedBufferInterface (SimpleRingBuffer only)
$buf->isFull() // → bool
$buf->capacity() // → int
TagMap - Tagged Collection¶
// ImmutableTagMap — class-indexed, O(1) class lookup
$tags = ImmutableTagMap::create([$tag1, $tag2])
$tags = ImmutableTagMap::empty()
// IndexedTagMap — sequential IDs, dual-indexed
$tags = IndexedTagMap::create([$tag1, $tag2])
$tags = IndexedTagMap::empty()
// Shared API (TagMapInterface)
$tags->add($tag1, $tag2) // → new TagMap with tags appended
$tags->replace($tag1, $tag2) // → new TagMap with only these tags
$tags->has(TimingTag::class) // → bool
$tags->isEmpty() // → bool
$tags->getAllInOrder() // → TagInterface[]
$tags->merge($otherTagMap) // → merged TagMap
$tags->mergeInto($targetTagMap) // → merged into target
$tags->query() // → TagQuery (fluent interface)
// ImmutableTagMap extras
$tags->count() // → int (optionally by class)
$tags->count(TimingTag::class) // → int (tags of that class)
$tags->last(TimingTag::class) // → TagInterface|null
TagQuery - Fluent Tag Querying¶
$q = $tags->query()
// Chainable transformations
$q->ofType(TimingTag::class) // Filter by instanceof
$q->only(TagA::class, TagB::class) // Keep only named classes
$q->without(ErrorTag::class) // Exclude classes
$q->filter(fn($tag) => $tag->x > 0) // Custom predicate
$q->map(fn($tag) => transform($tag)) // Transform tags
$q->limit(5) // Take first N
$q->skip(2) // Skip first N
// Terminal operations
$q->all() // → TagInterface[]
$q->get() // → TagMapInterface
$q->first() // → TagInterface|null
$q->last() // → TagInterface|null
$q->count() // → int
$q->has(TimingTag::class) // → bool (class or instance)
$q->hasAll(TagA::class, TagB::class) // → bool
$q->hasAny(TagA::class, TagB::class) // → bool
$q->any(fn($tag) => $tag->x > 0) // → bool (any match predicate)
$q->every(fn($tag) => $tag->x > 0) // → bool (all match predicate)
$q->isEmpty() // → bool
$q->isNotEmpty() // → bool
$q->classes() // → class-string[]
$q->mapTo(fn($tag) => $tag->value) // → array<mixed>
$q->reduce(fn($acc, $tag) => ..., $init) // → mixed
Caching & Lazy Loading¶
Cached - Lazy Value Container¶
// Creation
Cached::from(fn() => expensiveOperation()) // Lazy evaluation
Cached::fromValue($value) // Pre-resolved value
// Usage
$cached->get(...$args) // Resolve once, cache result (null included)
$cached->isResolved() // → bool
$cached(...$args) // Invoke syntax
// String representation
echo $cached; // Safe debug output
Notes:
- Cached is final and immutable. There is no fresh() or reset method.
Utilities¶
Uuid - ID Generation¶
Uuid::uuid4() // → "550e8400-e29b-41d4-a716-446655440000"
Uuid::hex(8) // → random hex string (16 chars)
Uuid::isValid($string) // → bool (validates UUID v1-v5 format)
Uuid::assertValid($string) // throws InvalidArgumentException if invalid
Time - Clock Abstractions¶
// ClockInterface — single method: now(): DateTimeImmutable
// SystemClock — real wall clock
$clock = new SystemClock();
$clock->now() // → DateTimeImmutable (current time)
// FrozenClock — always returns same time (readonly, for tests)
FrozenClock::create() // Freeze at current time
FrozenClock::at('2025-01-01 12:00') // Freeze at specific time
FrozenClock::atEpoch() // Freeze at Unix epoch
$frozen->now() // → always the same DateTimeImmutable
// VirtualClock — manually controllable time (for tests)
VirtualClock::at('2025-06-15 09:00') // Start at specific time
VirtualClock::atEpoch() // Start at Unix epoch
$vclock->now() // → DateTimeImmutable
$vclock->setTime($dateTime) // Jump to specific time → self
$vclock->advance(60) // Move forward N seconds → self
$vclock->rewind(30) // Move backward N seconds → self
$vclock->advanceBy('+2 hours') // Move by interval string → self
$vclock->reset($timestamp) // Reset to Unix timestamp → self
$vclock->timestamp() // → int (Unix timestamp)
Profiler - Performance Measurement¶
// Static interface
Profiler::mark('operation') // → Checkpoint
Profiler::mark('op', ['key' => 'v']) // → Checkpoint with context
Profiler::delta() // → float (time since last mark)
Profiler::summary() // → string (timing report)
// Instance methods
$profiler = Profiler::get(); // → Profiler (singleton)
$profiler->addMark('step', $context) // → Checkpoint
$profiler->timeSinceLast() // → float (seconds)
$profiler->getTotalTime() // → float (microseconds)
$profiler->getSummary() // → string (timing report)
$profiler->getFirst() // → Checkpoint
$profiler->getLast() // → Checkpoint
$profiler->diff($checkpointA, $checkpointB) // → float (seconds)
// Checkpoint properties & methods
$cp->name // string
$cp->time // float (microtime)
$cp->delta // float (seconds since previous)
$cp->context // array
$cp->mili() // → float (delta in milliseconds)
$cp->micro() // → float (delta in microseconds)
XML Processing¶
Xml - XML to Array/Object¶
// Creation & parsing
Xml::from($xmlString)
->withTags(['tag1', 'tag2']) // Parse only specific tags
->wrapped('root') // Wrap in root element
// Output
$xml->toArray() // → array structure
$xml->toXmlElement() // → XmlElement object
XmlElement - XML DOM Node¶
// Creation
XmlElement::fromArray(['tag' => 'div', 'content' => '', 'attributes' => [], 'children' => []])
new XmlElement($tag, $content, $attributes, $children)
// Accessors
$el->tag() // → string
$el->content() // → string
$el->attributes() // → array<string, string>
$el->children() // → XmlElement[]
$el->attribute('name', $default) // → ?string
// Traversal
$el->first('tagName') // → ?XmlElement (first child with tag)
$el->all('tagName') // → XmlElement[] (children with tag)
$el->get('0.1') // → XmlElement (dot-notation index path)
// State checks
$el->hasChildren() // → bool
$el->hasContent() // → bool
// Conversion
$el->toArray() // → array
SimpleXmlParser - SimpleXML-based Parser¶
// Creation & fluent config
SimpleXmlParser::from($xmlString)
->withAttributes() // Include attributes in output
->withRoot() // Include root element in output
->wrapped('root') // Wrap in root element
->asCamelCase() // Keys as camelCase
->asSnakeCase() // Keys as snake_case
->withNaming('raw') // Custom naming convention
->toArray() // → array<string, mixed>
XmlValidator - XML Validation¶
$validator = new XmlValidator();
$validator->validate($xmlString); // throws RuntimeException on invalid XML
JSON Schema¶
JsonSchema - Schema Definition¶
// Type factory methods (all accept: $name, $description, $title, $nullable, $meta)
JsonSchema::string('field')
JsonSchema::integer('field')
JsonSchema::number('field')
JsonSchema::boolean('field')
JsonSchema::any('field')
JsonSchema::enum('status', ['active', 'inactive'])
// Object schema
JsonSchema::object(
name: 'User',
properties: [JsonSchema::string('id'), JsonSchema::string('name')],
requiredProperties: ['id', 'name'],
description: 'User object',
additionalProperties: false,
)
// Array/collection schema
JsonSchema::array('tags', itemSchema: JsonSchema::string())
JsonSchema::collection('users', itemSchema: JsonSchema::object('User', ...))
// From existing data
JsonSchema::fromArray($schemaArray, 'fieldName', required: true)
JsonSchema::document($rawSchemaArray) // Preserves raw anyOf/oneOf/allOf on roundtrip
// Fluent mutation (all return new immutable copy)
$schema->withName('newName')
$schema->withDescription('desc')
$schema->withTitle('title')
$schema->withNullable(true)
$schema->withMeta(['key' => 'value'])
$schema->withEnumValues(['a', 'b'])
$schema->withProperties([...])
$schema->withItemSchema(JsonSchema::string())
$schema->withRequiredProperties(['id'])
$schema->withAdditionalProperties(false)
$schema->withRef('#/$defs/Address')
$schema->withDefs(['Address' => $addressSchema])
$schema->withDef('Address', $addressSchema)
// Accessors
$schema->type() // → JsonSchemaType
$schema->name() // → string
$schema->description() // → string
$schema->title() // → string
$schema->isNullable() // → bool
$schema->properties() // → JsonSchema[]
$schema->property('name') // → ?JsonSchema
$schema->requiredProperties() // → array<string>
$schema->additionalProperties() // → ?bool
$schema->hasAdditionalProperties() // → bool
$schema->enumValues() // → array<int|string>
$schema->hasEnumValues() // → bool
$schema->itemSchema() // → ?JsonSchema
$schema->hasItemSchema() // → bool
$schema->itemType() // → ?JsonSchemaType
$schema->ref() // → ?string
$schema->hasRef() // → bool
$schema->defs() // → array<string, JsonSchema>
$schema->hasDefs() // → bool
$schema->def('Address') // → ?JsonSchema
$schema->meta('key', $default) // → mixed (null key returns all meta)
$schema->hasMeta('key') // → bool
$schema->hasDefaultValue() // → bool
$schema->defaultValue() // → mixed
$schema->objectClass() // → ?string (from x-php-class meta)
// Type checks
$schema->isObject() // → bool
$schema->isArray() // → bool (untyped array)
$schema->isCollection() // → bool (typed array with item schema)
$schema->isString() // → bool
$schema->isInteger() // → bool
$schema->isNumber() // → bool
$schema->isBoolean() // → bool
$schema->isNull() // → bool
$schema->isAny() // → bool
$schema->isEnum() // → bool
$schema->isOption() // → bool (string enum without class)
$schema->isScalar() // → bool
$schema->isScalarCollection() // → bool
$schema->isEnumCollection() // → bool
$schema->isObjectCollection() // → bool
$schema->isOptionCollection() // → bool
// Transformation
$schema->toArray() // → array (JSON Schema array)
$schema->toJsonSchema() // → array (alias for toArray)
$schema->toString() // → string (JSON-encoded)
$schema->toFunctionCall('name', 'desc', strict: false) // → OpenAI function call format
$schema->toResponseFormat('name', 'desc', strict: true) // → OpenAI response format
ToolSchema - Function Tool Wrapper¶
// Creation
ToolSchema::make('tool_name', 'description', $jsonSchema)
ToolSchema::fromArray($data)
new ToolSchema('tool_name', 'description', $jsonSchema)
// Properties (readonly)
$tool->name // → string
$tool->description // → string
$tool->parameters // → JsonSchema
// Conversion
$tool->toArray() // → array (OpenAI function tool format)
CanProvideJsonSchema - Contract¶
CLI Utilities¶
Console - Terminal Output¶
// Basic output
Console::print('message', Color::RED) // Print with color
Console::println('message', [Color::BOLD, Color::BLUE]) // Print with newline
// Layout
Console::center('text', 80, Color::GREEN) // → centered string
Console::columns([ // → formatted columns string
[20, 'Col 1', STR_PAD_RIGHT],
[-1, 'Col 2', STR_PAD_LEFT] // -1 = remaining width
], 80)
Console::printColumns($columns, 80, ' ') // Print columns directly
// Utilities
Console::clearScreen()
Console::getWidth() // → int (terminal width)
Color - ANSI Color Constants¶
// Bright foreground
Color::RED, Color::GREEN, Color::BLUE, Color::YELLOW
Color::MAGENTA, Color::CYAN, Color::WHITE, Color::DARK_GRAY
// Dark foreground
Color::BLACK, Color::DARK_RED, Color::DARK_GREEN, Color::DARK_YELLOW
Color::DARK_BLUE, Color::DARK_MAGENTA, Color::DARK_CYAN, Color::GRAY
// Background
Color::BG_BLACK, Color::BG_RED, Color::BG_GREEN, Color::BG_YELLOW
Color::BG_BLUE, Color::BG_MAGENTA, Color::BG_CYAN, Color::BG_WHITE, Color::BG_GRAY
// Styles
Color::BOLD, Color::ITALICS, Color::RESET, Color::CLEAR
// Use in arrays for combination: [Color::BOLD, Color::RED]
Text & Code Utilities¶
TextRepresentation - Convert Any Value to String¶
TextRepresentation::fromAny($input) // string|array|object → string
// Tries: string passthrough, array→JSON, ->toJson(), ->toArray(), ->toString(),
// BackedEnum->value, Closure invocation, fallback JSON encode
TextRepresentation::fromParameter($value, $key, $params) // Parameter-aware conversion
// Additional support: ->toSchema(), ->toOutputSchema(), ->value(), callable($key, $params)
Tokenizer - Token Counting¶
Tokenizer::tokenCount('some text') // → int (number of tokens)
Tokenizer::default() // → CanCountTokens (built once per process)
Tokenizer::setDefault($tokenizer) // swap the implementation process-wide
Tokenizer::reset() // back to the resolved default
The default is tiktoken with o200k_base, resolved by TokenizerResolver on
first use. Pick another one without touching code via INSTRUCTOR_TOKENIZER:
INSTRUCTOR_TOKENIZER=auto # default: tiktoken, bundled if unavailable
INSTRUCTOR_TOKENIZER=gpt3 # bundled r50k_base, never any network
INSTRUCTOR_TOKENIZER=tiktoken # tiktoken with o200k_base, fails loudly
INSTRUCTOR_TOKENIZER=tiktoken:cl100k_base # a specific encoding, fails loudly
auto falls back to the bundled tokenizer when the tiktoken vocabulary cannot be
obtained; the explicit forms do not - getting a different tokenizer than the one
you asked for would mean silently getting different counts.
Counting goes through CanCountTokens; drivers that can also produce token IDs
implement CanTokenizeText (encode(), encoding()). Prefer injecting the
contract over calling the static facade.
use Cognesy\Utils\Tokenization\Contracts\CanCountTokens;
use Cognesy\Utils\Tokenization\Drivers\Gpt3TokenizerDriver;
use Cognesy\Utils\Tokenization\Drivers\TiktokenDriver;
new Gpt3TokenizerDriver() // bundled r50k_base, no network
TiktokenDriver::forEncoding('cl100k_base') // default driver, o200k_base out of the box
TiktokenDriver::forModel('gpt-4o')
TiktokenDriver::using($encoder) // pre-built Yethee\Tiktoken\Encoder
TiktokenDriver::isAvailable() // → bool, is yethee/tiktoken installed
TiktokenDriver is ~25x faster on long texts and uses a third of the memory,
but it downloads its vocabulary on first use and caches it on disk (override the
location with TIKTOKEN_CACHE_DIR or the $cacheDir argument). That download is
why Gpt3TokenizerDriver is still shipped: it is the offline fallback.
Token counts are only comparable within one encoding. The default o200k_base
produces different numbers than the bundled r50k_base - generally fewer, since
it covers modern text more tightly - so persisted token budgets are worth
revisiting. tiktoken's own r50k_base is token-for-token identical to the
bundled tokenizer, and is the way to keep the old numbers with the new speed.
Components that count tokens take an optional CanCountTokens, falling back to
Tokenizer::default(): SplitMessages, MoveMessagesToBuffer,
SummarizeBuffer, and the agents UseSummarization capability.
That fallback resolves on the first count, not in the constructor - wiring one of
these components into a pipeline costs nothing until it actually counts. A
consequence worth knowing: a component built without an explicit tokenizer picks
up whatever Tokenizer::setDefault() installed most recently, so set the default
before counting starts rather than assuming construction order decides it.
ProgrammingLanguage - Language Enum & Helpers¶
// Enum cases: Bash, C, Cpp, Go, Java, JavaScript, Lua, Perl, Php, Python, Ruby, Shell, SQL, TypeScript
$lang = ProgrammingLanguage::Php;
$lang->value // → 'php'
$lang->extension() // → 'php'
// Static helpers (accept any language string, not just enum values)
ProgrammingLanguage::fileExtension('python') // → 'py'
ProgrammingLanguage::commentSyntax('python') // → '#'
ProgrammingLanguage::fileTemplate('php') // → doctest template string
ProgrammingLanguage::isCommentLine('php', '// comment') // → true
ProgrammingLanguage::linesOfCode('php', $code) // → int (non-empty, non-comment)
Data Helpers¶
Metadata - Immutable Key-Value Store¶
// Creation
new Metadata(['key' => 'value'])
Metadata::empty()
Metadata::fromArray($array)
// Access
$meta->get('key', 'default') // → mixed
$meta->hasKey('key') // → bool
$meta->keys() // → array
$meta->isEmpty() // → bool
$meta->count() // → int (Countable)
// Immutable mutations (return new Metadata)
$meta->withKeyValue('key', 'val') // Add/replace key
$meta->withoutKey('key') // Remove key
$meta->withMergedData(['k' => 'v']) // Merge array
// Conversion
$meta->toArray() // → array
// Also: iterable (IteratorAggregate)
OpaqueExternalId - Abstract Typed Identifier¶
// Extend to create domain-specific IDs: class UserId extends OpaqueExternalId {}
$id = UserId::fromString('abc-123') // Create from string
UserId::empty() // Empty ID (value = '')
UserId::null() // Alias for empty()
$id->value // Public readonly string property
$id->isEmpty() // → bool (true if blank)
$id->isPresent() // → bool (opposite of isEmpty)
$id->toString() // → string
$id->toNullableString() // → string|null (null if empty)
$id->equals($otherId) // → bool (same class + same value)
(string) $id // Stringable support
AbstractResolver - Priority-Based Provider Chain¶
// Extend and implement accepts(mixed $candidate): bool
// Constructor: new MyResolver([$provider1, $provider2], suppressErrors: true)
// Providers: callables or objects, evaluated lazily, first acceptable wins
// Call resolve() from subclass to get cached result (throws if none accepted)
Markdown¶
FrontMatter - YAML Front Matter Parser¶
// Parse markdown with optional YAML front matter
$fm = FrontMatter::parse($text); // Static factory (private constructor)
// Accessors
$fm->data() // → array (parsed YAML key-value map)
$fm->document() // → string (content after front matter)
$fm->hasFrontMatter() // → bool (true if --- delimiters found)
$fm->error() // → ?string (YAML parse error message)
Specialized Parsers¶
JsonExtractor - JSON Extraction from Text¶
use Cognesy\Utils\Json\JsonExtractor;
JsonExtractor::first($text) // → ?array (first valid JSON object/array, or null)
JsonExtractor::all($text) // → list<array> (all valid JSON objects/arrays)
// Tries: raw input, markdown fenced blocks, brace-matching scan
JsonDecoder - Resilient JSON Decoder¶
use Cognesy\Utils\Json\JsonDecoder;
JsonDecoder::decode($input) // → mixed (handles valid, repairable, and broken JSON)
JsonDecoder::decodeToArray($input) // → array ([] on failure or non-array result)
// Strategy: json_decode fast path → minimal repairs → JsonExtractor → tolerant tokenizer
IncrementalJsonParser - Streaming Chunk Parser¶
use Cognesy\Utils\Json\IncrementalJsonParser;
$parser = new IncrementalJsonParser();
$parser->append($chunk) // Feed next chunk of data
$parser->buffer() // → string (raw accumulated input)
$parser->currentJson() // → ?string (completed JSON so far, or null)
$parser->currentArray() // → ?array (decoded array so far, or null)
$parser->completionSuffix() // → ?string (closing tokens needed, or null)
$parser->reset() // Clear all state for reuse
XML Parsers¶
// SelectiveXmlParser - parse only specified tags (used internally by Xml)
$parser = new SelectiveXmlParser(['tag1', 'tag2']); // empty = parse all
$parser->parse($xmlContent) // → array of parsed node arrays
// XmlValidator - validate XML structure
$validator = new XmlValidator();
$validator->validate($xmlString); // throws RuntimeException on invalid XML
All classes follow immutable patterns where applicable and use strict typing throughout.