Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 GitHub - Trehinos/thor-common
Nothing Special   »   [go: up one dir, main page]

Skip to content

Trehinos/thor-common

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thor Common

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.

API inventory (by namespace)

  • 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

Requirements

  • PHP 8.3+
  • Extensions: mbstring, openssl
  • Composer dependencies: symfony/yaml, symfony/var-dumper (already declared)

Installation

Install via Composer:

composer require trehinos/thor-common

Autoloading is PSR-4 under the Thor\Common namespace.

Quick start and examples

Below are small, practical examples for the most used utilities. All namespaces assume use statements or fully-qualified names as needed.

Types\Strings

  • 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'

Types\Arrays

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'] ]

Types\DateTimes and ClockInterface

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();

Types\Safe\Result

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'

Types\Safe\Option

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'));

Validation

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)

Configuration

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'

Debug

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
}

FileSystem

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)$');

Encryption

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

Types\Collection (fluent array wrapper)

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.

Running tests

This repository uses PHPUnit 10.x. Install dev dependencies and run tests:

composer install
vendor/bin/phpunit

Versioning and stability

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.

Contributing

Issues and PRs are welcome. Please include tests for bug fixes and new features when possible.

License

MIT License. See LICENSE file. © 2021–2025 Sébastien Geldreich

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

0