Reusable core utilities for the Thor framework (and standalone PHP projects).
This library provides small, focused components for day-to-day application code:
- Types: string helpers, arrays helpers, date/time helpers, placeholder formatting, a PSR-20-like clock interface
- Types\Collection: a fluent object wrapper around PHP arrays with many convenience methods
- Types\Safe\Option: an Option type (Some/None) to model optional values without nulls
- Types\Safe\Result: a small Result type (ok/error) inspired by Rust for safer error handling
- Configuration: ArrayObject-based configuration, YAML loader/writer, path interpolation
- Validation: simple filter/validator interfaces and a regex filter
- FileSystem: utilities for files, folders, and recursive operations
- Debug: a lightweight logger with log levels and throwable handling
- Encryption: GUID-like secure token generator
The components are decoupled; you can use only the parts you need.
- Thor\Common\Types
- Strings: split, token, trimOrPad, left, right, prefix, suffix, interpolate(…, PlaceholderFormat)
- Arrays: turnOver
- DateTimes (implements ClockInterface): translate, getRelativeDateTime, period, get()->now()
- PlaceholderFormat (enum): CURLY, SIGIL, SHELL with helper to build placeholders
- ClockInterface: now()
- Thor\Common\Types\Collection
- Collection: vast fluent API over arrays (build/combine/fill/range, map/filter/reduce, slice/chunk, sort, unique, set ops, column, groupBy, indexBy, keys/values, etc.)
- Enums: SortOrder, SortFlag, UniqueSortFlag, FilterMode, KeyCase
- Thor\Common\Types\Safe
- Option and Maybe: Some/None optional value container with unwrap/match helpers
- Result and ResultState: ok/error result container with match helpers
- Thor\Common\Configuration
- ConfigurationFromFile: load/multipleFromFile/write for YAML-based config
- ConfigurationFolder: path interpolation via context
- Thor\Common\Validation
- FilterInterface, ValidatorInterface
- Filters\RegexFilter: PCRE-based validator/filter
- Thor\Common\FileSystem
- FileSystem: read/write/append helpers
- Folders: createIfNotExists, copyTree, removeTree
- Thor\Common\Debug
- Logger: PSR-3-like methods, static global instance, writeDebug, logThrowable
- LogLevel (enum)
- Thor\Common\Encryption
- Guid: random bytes with hex/base64 formats
- PHP 8.3+
- Extensions: mbstring, openssl
- Composer dependencies: symfony/yaml, symfony/var-dumper (already declared)
Install via Composer:
composer require trehinos/thor-common
Autoloading is PSR-4 under the Thor\Common namespace.
Below are small, practical examples for the most used utilities. All namespaces assume use
statements or fully-qualified names as needed.
- Simple token interpolation with various placeholder syntaxes via PlaceholderFormat.
use Thor\Common\Types\Strings;
use Thor\Common\Types\PlaceholderFormat;
$template = 'Hello {name}, today is {day}!';
$out = Strings::interpolate($template, [
'name' => 'Ada',
'day' => 'Thursday',
]);
// "Hello Ada, today is Thursday!"
// Different placeholder styles
Strings::interpolate('Hello $name', ['name' => 'Ada'], PlaceholderFormat::SIGIL); // Hello Ada
Strings::interpolate('Hello ${name}', ['name' => 'Ada'], PlaceholderFormat::SHELL); // Hello Ada
- Other helpers
Strings::left('abcdef', 3); // 'abc'
Strings::right('abcdef', 3); // 'def'
Strings::prefix('ID-', '42'); // 'ID-42'
Strings::suffix('42', '-OK'); // '42-OK'
// Trim specific padding and pad/truncate to length
Strings::trimOrPad(' 42 ', 5, ' ', STR_PAD_LEFT); // ' 42'
Transpose/pivot a list of associative arrays into a dictionary of lists.
use Thor\Common\Types\Arrays;
$input = [
['id' => 1, 'name' => 'Ada'],
['id' => 2, 'name' => 'Grace'],
];
$out = Arrays::turnOver($input);
// [ 'id' => [1,2], 'name' => ['Ada','Grace'] ]
Formatting, relative display, periods, and a simple clock provider.
use Thor\Common\Types\DateTimes;
use DateTimeImmutable;
DateTimes::translate('2025-10-09'); // '09/10/2025'
$now = new DateTimeImmutable('2025-10-09 18:25:00');
$yesterday = $now->modify('-1 day 14:10:00');
DateTimes::getRelativeDateTime($yesterday, 'Y-m-d', 'Yesterday', $now); // 'Yesterday 14:10'
// Generate a DatePeriod over a month by days
$start = new DateTimeImmutable('2025-10-01');
$period = DateTimes::period($start); // until last day of the month by 1 day
// ClockInterface: get "now" from the singleton DateTimes implementation
$clockNow = DateTimes::get()->now();
A minimal Result type to avoid throwing exceptions for expected errors.
use Thor\Common\Types\Safe\Result;
function divide(int $a, int $b): Result {
if ($b === 0) {
return Result::error('Division by zero');
}
return Result::ok($a / $b);
}
$quotient = divide(10, 2)
->unwrapOr(-1); // 5
$message = divide(1, 0)
->match(
fn($ok) => "ok=$ok",
fn($err) => "error=$err",
); // 'error=Division by zero'
A lightweight Option type to represent the presence (Some) or absence (None) of a value. Avoids passing raw nulls around and makes intent explicit.
use Thor\Common\Types\Safe\Option;
use Thor\Common\Types\Safe\Maybe;
// Constructing
$opt1 = Option::some('value'); // Some('value')
$opt2 = Option::none(); // None
$opt3 = Option::from($_GET['q'] ?? null); // Some or None depending on null
// Inspecting
$opt1->isSome(); // true
$opt2->isNone(); // true
$opt1->is(); // Maybe::SOME
$opt2->is(); // Maybe::NONE
$opt1->isA(Maybe::SOME); // true
// Consuming
$name = $opt3->unwrapOr('guest'); // returns the inner value or 'guest'
$val = $opt3->unwrapOrElse(fn() => computeDefault());
// Pattern matching style
$msg = $opt3->matches(
fn($v) => "got=$v",
fn() => "none",
);
// Exceptions on None
try {
$must = $opt2->unwrap(); // throws RuntimeException if None
} catch (\Throwable $e) {
// handle
}
// Or throw custom exception
$must = $opt2->unwrapOrThrow(new \InvalidArgumentException('missing value'));
FilterInterface and ValidatorInterface define small contracts. A ready-to-use RegexFilter is provided.
use Thor\Common\Validation\Filters\RegexFilter;
$filter = new RegexFilter('/^[a-z]+$/i');
$ok = $filter->filter('Foobar'); // 'Foobar'
$fail = $filter->filter('Foo 123'); // null (per filter_var behavior)
ArrayObject-based configuration with YAML loading/writing and path interpolation.
use Thor\Common\Configuration\ConfigurationFromFile;
use Thor\Common\Configuration\ConfigurationFolder;
// Load a single config from YAML (provide path without extension)
$app = ConfigurationFromFile::fromFile('/path/to/app');
$dsn = $app->database['dsn'] ?? null;
// Load multiple configs from a YAML list
$items = ConfigurationFromFile::multipleFromFile('/path/to/list', ConfigurationFromFile::class);
// Write configuration to a YAML file
$app->write('/path/to/out.yml');
// Interpolate paths from a context
$cfgFolder = new ConfigurationFolder(['env' => 'prod', 'name' => 'myapp']);
$resolved = $cfgFolder->getPath('/opt/{name}/{env}/config.yml'); // '/opt/myapp/prod/config.yml'
A lightweight logger with log levels and an easy way to record throwables.
use Thor\Common\Debug\Logger;
use Thor\Common\Debug\LogLevel;
// Initialize a global logger (file will be created under base path)
Logger::set(LogLevel::DEBUG, __DIR__ . '/var/');
Logger::info('Server started');
Logger::error('Something went wrong', ['context' => 'value']);
try {
throw new RuntimeException('Boom');
} catch (Throwable $e) {
Logger::logThrowable($e); // writes ERROR + details at DEBUG level
}
Convenience functions to work with files and folders.
use Thor\Common\FileSystem\Folders;
use Thor\Common\FileSystem\FileSystem;
Folders::createIfNotExists(__DIR__ . '/cache');
FileSystem::write(__DIR__ . '/cache/app.log', "Started\n");
FileSystem::appendIfExists(__DIR__ . '/cache/app.log', "Tick\n");
// Recursively copy/remove
Folders::copyTree(__DIR__ . '/assets', __DIR__ . '/public');
$deleted = Folders::removeTree(__DIR__ . '/public', mask: '.*\\.(tmp|bak)$');
Secure random tokens and flexible string formats.
use Thor\Common\Encryption\Guid;
$g = new Guid(16);
(string)$g; // Uppercase hex grouped like a GUID
$g->getHex(); // Lowercase hex
$g->getBase64(); // Base64
Guid::hex(); // Fresh lowercase hex string
A rich wrapper around PHP arrays offering dozens of familiar operations (map/filter/reduce, set ops, sorting, chunking, etc.).
use Thor\Common\Types\Collection\Collection;
$users = new Collection([
['id' => 1, 'name' => 'Ada'],
['id' => 2, 'name' => 'Grace'],
]);
$names = $users->column('name')->values()->toArray(); // ['Ada','Grace']
Note: See the source of Types/Collection for the full list of methods and options.
This repository uses PHPUnit 10.x. Install dev dependencies and run tests:
composer install
vendor/bin/phpunit
This package follows semantic versioning as much as possible. Minor versions may add features without breaking changes. Check the CHANGELOG or git history for details.
Issues and PRs are welcome. Please include tests for bug fixes and new features when possible.
MIT License. See LICENSE file. © 2021–2025 Sébastien Geldreich