Framework
Utilities

Utils

A broad, strict-typed standard library. Every helper is static: Strings::toKebabCase($x).

Strings

Strings is the largest of them — every string operation the framework needs, always returning a string rather than false, so results chain without checks.

Checking

use Framework\Utils\Strings;

Strings::isEqual("Ada", "ada");            // case-insensitive by default
Strings::startsWith("image.png", "img");   // and endsWith, contains
Strings::contains("hello", ["ell", "xyz"]); // any of several needles
Strings::match($value, "/^\d+$/");         // a regular expression
Strings::isAlphaNum($value);
Strings::hasEmoji($text);                  // and isOnlyEmojis
Strings::length($text);

The case checks pair with the converters below — isCamelCase, isSnakeCase, isKebabCase, isPascalCase and isConstantCase.

Slicing & joining

Strings::substring($text, 0, 10);
Strings::substringBefore("a.b.c", ".");    // "a"
Strings::substringAfter("a.b.c", ".");     // "b.c"
Strings::substringBetween($text, "[", "]");

Strings::split("a, b, c", ",");            // trims each part
Strings::join([ "a", "b" ], ", ");
Strings::joinKeys($map, ", ");             // and joinValues

Strings::stripStart($path, "/");           // and stripEnd, stripStartEnd
Strings::addPrefix($file, "img_");         // and addSuffix, addPrefixSuffix

Case & formatting

Strings::toCamelCase("hello_world");     // "helloWorld"
Strings::toPascalCase("hello world");    // "HelloWorld"
Strings::toSnakeCase("helloWorld");      // "hello_world"
Strings::toKebabCase("Hello World");     // "hello-world"
Strings::toConstantCase("helloWorld");   // "HELLO_WORLD"
Strings::toTitleCase("john smith");      // "John Smith"

Strings::upperCaseFirst($text);          // and lowerCaseFirst
Strings::padLeft($number, 3, "0");       // and padRight
Strings::makeShort($text, 80);           // truncate with an ellipsis
Strings::splitToWords("helloWorld");     // [ "hello", "World" ]

Replacing, cleaning & generating

Strings::replace($text, "a", "b");
Strings::replaceStart($path, "/old", "/new");   // and replaceEnd
Strings::replacePattern($text, "/\d+/", "#");
Strings::replaceCallback($text, "/\{(\w+)\}/", $callback);

Strings::sanitize($input);        // safe to store
Strings::removeHtml($html);       // and toHtml, decodeHtml
Strings::normalized($text);       // strip accents for comparing

Strings::random(12);              // a random string
Strings::randomCode(6);           // a short code
Strings::toString($value);        // and toNumber, getNumber, getLetter

Arrays

Arrays works on both lists and maps, and always hands back an array of the type you asked for.

Finding & testing

use Framework\Utils\Arrays;

$rows = [ [ "id" => 1, "name" => "A" ], [ "id" => 2, "name" => "B" ] ];

Arrays::findValue($rows, "id", 2);      // the whole row
Arrays::findValues($rows, "id", 2);     // every matching row
Arrays::findIndex($rows, "id", 2);      // its position
Arrays::getValues($rows, "name");       // [ "A", "B" ]
Arrays::getOneValue($rows, "name");     // the first name

Arrays::contains($list, $value);        // and hasValue, containsKey
Arrays::isEmpty($array);                // and isList, isMap, isDict
Arrays::isEqual($a, $b);                // and isEqualWithKeys, isEqualJSON
Arrays::intersects($a, $b);
findIndex, findValue and findValues compare Enums by their string value, so an enum case and its backing string match interchangeably.

Changing

Arrays::addFirst($list, $value);      // and addAt
Arrays::removeFirst($list);           // and removeLast, removeAt, removeValue
Arrays::removeEmpty($list);           // drop empty entries
Arrays::removeDuplicates($list);

Arrays::merge($a, $b);                // and mergeLists, extend
Arrays::slice($list, 0, 10);          // and subArray, paginate
Arrays::sort($map, $callback);        // sort() keeps keys, sortList() reindexes
Arrays::reverse($list);
Arrays::map($list, $callback);

Converting

The to… family casts a loose array into a typed one — the way request data becomes something a model can use:

Arrays::toInts([ "1", "2", "x" ]);      // [ 1, 2 ]
Arrays::toStrings($values);
Arrays::toList($value);                 // wrap a single value in a list
Arrays::toArray($value);

Arrays::toIntsMap([ "1" => "2" ]);      // [ 1 => 2 ]
Arrays::toStringsMap($map);             // and toStringIntMap, toIntStringMap
Arrays::toIntFloatMap($map);            // and toStringFloatMap
Arrays::createMap($rows, "id");         // index rows by a column

Plus the small maths helpers: sum, average, max, length, random and getDiff.

Numbers

Numbers parses, rounds and formats — including money, which it keeps in cents to avoid float drift:

use Framework\Utils\Numbers;

Numbers::toInt("42.7");             // 43 (rounds)
Numbers::toFloat($value);           // and toIntOrFloat
Numbers::isValid($value);           // and isValidFloat, isValidPrice
Numbers::hasDecimals($value);

Numbers::round($value, 2);          // and roundInt, roundCents
Numbers::clampInt($value, 0, 100);  // and clampFloat
Numbers::divide($a, $b);            // and divideInt — no division by zero

Money is stored as an integer number of cents, converted only at the edges:

Numbers::toCents(19.99);        // 1999
Numbers::fromCents(1999);       // 19.99
Numbers::formatCents(1999);     // "19.99"
Numbers::formatPrice(1999);     // "$19.99"
Numbers::applyDiscount($price, 20);    // and applyIncrement
Numbers::formatInt(1234567);        // "1,234,567"
Numbers::formatFloat(1234.5, 2);    // "1,234.50"
Numbers::toBytesString(1048576);    // "1 MB"
Numbers::percent(30, 200);          // 15
Numbers::zerosPad(7, 3);            // "007"
Numbers::coordinatesDistance($lat1, $lng1, $lat2, $lng2);

Dictionary

Dictionary wraps an array of loose values — decoded JSON, a config block, a database row — and reads it as types, so nothing downstream deals with strings-that-might-be-numbers:

use Framework\Utils\Dictionary;

$dict = new Dictionary([ "name" => "Ada", "age" => "36", "active" => "1" ]);

$dict->getString("name");    // "Ada"
$dict->getInt("age");        // 36
$dict->getFloat("price");
$dict->getBool("active");    // true
$dict->getPrice("total");    // in cents
$dict->getDate("createdAt"); // a Date — and getDateParsed()

Nested values come back wrapped, so the same typed reads continue down the tree:

$dict->getDict("options");     // another Dictionary
$dict->getList("rows");        // a list of Dictionaries
$dict->getArray("tags");       // and getInts, getStrings, getJSON

$dict->findDict("id", 7);      // search a list of Dictionaries
$dict->has("name");            // and hasValue, contains, containsInt
$dict->isEmpty();              // and isNotEmpty, isList, isEqual

$dict->set("name", "Ada");     // and setInt, setString, setEnum
$dict->push($value);
$dict->remove("name");
$dict->merge($other);
$dict->clone();

It implements Countable, IteratorAggregate and JsonSerializable, so it counts, iterates and encodes like an array — and the to… converters (toStringsMap(), toIntsMap(), toIntFloatMap(), toArray(), toJSON()) hand back plain data when a boundary needs it.

Validation & parsing

Utils validates and parses the everyday user data — emails, phones, names and national IDs:

use Framework\Utils\Utils;

Utils::isValidEmail("a@b.com");        // true
Utils::isValidPassword($password);     // strength check
Utils::isValidPhone($phone);           // and isValidUsername, isValidFullName
Utils::isValidColor($value);

Utils::parseName("john smith");        // "John Smith" — and parseNameCase
Utils::generateUsername($name);
Utils::extractEmail($text);            // and getEmailDomain

Utils::hideEmail("ada@example.com");   // "a**@example.com" — and hidePhone
Utils::getWhatsAppUrl($phone);         // a wa.me link
Utils::getAvatarUrl($email);

The Argentine document helpers live here too — isValidDNI / dniToNumber and isValidCUIT / parseCUIT / cuitToNumber.

JSON

Safe encode and decode, reading and writing JSON files and urls, and decoding straight into a Dictionary:

use Framework\Utils\JSON;

$json = JSON::encode(["a" => 1]);         // {"a":1}
$data = JSON::decodeAsArray($json);       // ["a" => 1]
$dict = JSON::decodeAsDictionary($json);  // a Dictionary
JSON::isValid($json);                     // true

$config = JSON::readFile("config.json");  // decoded array
JSON::writeFile("out.json", $data);
$remote = JSON::readUrl("https://api.example.com/data");

URL

Parse and build urls — query params, hosts and slugs — with validation helpers:

use Framework\Utils\URL;

URL::toSlug("Hello, World!");                 // "hello-world"
URL::addParams($url, ["page" => 2]);          // append query params
URL::getHost("https://app.example.com/x");    // "app.example.com"
URL::getDomain("https://app.example.com/x");  // "example.com"
URL::isValid($url);                           // true

Server

The raw incoming request and server environment — the low level beneath the typed Request:

use Framework\Utils\Server;

Server::getIP();          // the client IP
Server::getFullUrl();     // the full request url
Server::getPlatform();    // "iOS", "Android", "Web"…
Server::getUserAgent();
Server::getPayload();     // the raw JSON body
Server::isPostRequest();

AES

Symmetric AES encryption with byte and hex helpers — this is what encrypts the database's protected values:

use Framework\Utils\AES;

$cipher = AES::encrypt($valueBytes, $keyBytes);
$bytes  = AES::toHexBytes($hexString);

Encoding

Convert text between character encodings and clean up stray bytes:

use Framework\Utils\Encoding;

Encoding::toUTF8($text);     // → UTF-8
Encoding::toISO8859($text);  // → ISO-8859-1
Encoding::removeBOM($text);  // strip a byte-order mark

CSV

Low-level CSV encode/decode and file read/write — the Import & Export module builds typed rows on top of these:

use Framework\Utils\CSV;

$rows = CSV::readFile("data.csv");   // array of rows
CSV::writeFile("out.csv", $rows);
$text = CSV::encode($rows);

Color

The brand color palette as an enum — the named hues the UI is themed with:

use Framework\Utils\Color;

Color::Blue;
Color::HotPink;
Color::getAll();   // every case (an IsEnum helper)

Cases run the spectrum from Red to Pink, plus White, Gray and the empty None.