cosmologist / gears
PHP-Gears - handy helper library for PHP and Symfony
Installs: 4 259
Dependents: 1
Suggesters: 0
Security: 0
Stars: 8
Watchers: 3
Forks: 1
Open Issues: 0
Suggests
README
- Installation
- Common
- Doctrine Utils
- Symfony
Installation
composer require cosmologist/gears
Array functions
Get an item from the array by key
ArrayType::get(['fruit' => 'apple', 'color' => 'red'], 'fruit'); // 'apple' ArrayType::get(['fruit' => 'apple', 'color' => 'red'], 'weight'); // null ArrayType::get(['fruit' => 'apple', 'color' => 'red'], 'weight', 15); // 15 ArrayType::get(['apple', 'red'], -1); // 'red'
Adds a value to an array with a specific key only if key not presents in an array
It's more intuitive variant to <code>$array += [$key => $value];
$array = ['fruit' => 'apple']; ArrayType::touch($array, 'color', 'red']); // ['fruit' => 'apple', 'color' => 'red']; ArrayType::touch($array, 'fruit', 'banana']); // ['fruit' => 'apple'];
Push element onto the end of array and returns the modified array
$a = [1,2]; ArrayType::push($a, 3); // [1,2,3]
Prepend element to the beginning of an array and returns the modified array
$a = [2,3]; ArrayType::unshift($a, 1); // [1,2,3]
Calculate the average of values in an array (array_avg)
ArrayType::average([1, 2, 3]); // 3
Check if array is associative
ArrayType::checkAssoc([1, 2, 3]); // false ArrayType::checkAssoc(['foo' => 'bar']); // true
Check if a value exists in an array
ArrayType::contains(array $list, mixed $item);
Inserts an array after the key
ArrayType::insertAfter(['a' => 1, 'c' => 3], 'a', ['b' => 2]); // ['a' => 1, 'b' => 2, 'c' => 3] // If the key doesn't exist ArrayType::insertAfter(['a' => 1, 'b' => 2], 'c', ['foo' => 'bar']); // ['a' => 1, 'b' => 2, 'foo' => 'bar']
Inserts an array before the key
ArrayType::insertBefore(['a' => 1, 'c' => 3], 'c', ['b' => 2]); // ['a' => 1, 'b' => 2, 'c' => 3] // If the key doesn't exist ArrayType::insertBefore(['a' => 1, 'b' => 2], 'c', ['foo' => 'bar']); // ['foo' => 'bar', 'a' => 1, 'b' => 2]
Convert list of items to ranges
ArrayType::ranges([1, 3, 7, 9]); // [[1, 3], [3, 7], [7, 9]]
Unset array item by value
ArrayType::unsetValue(['a', 'b', 'c'], 'b'); // ['a', 'c']
Calculate the standard deviation of values in an array
ArrayType::deviation([1, 2, 1]); // float(0.4714045207910317)
Cast to an array
Behavior for different types:
- array - returns as is
- iterable - converts to a native array (
iterator_to_array()
) - another - creates an array with argument ([value])
ArrayType::toArray($value);
Get the array encoded in json
If encoded value is false, true or null then returns empty array.
JSON_THROW_ON_ERROR always enabled.
ArrayType::fromJson($json): array;
Callable functions
Determine if a callable a closure
CallableType::isClosure(fn($foo) => $foo); // bool(true) CallableType::isClosure('foo'); // bool(false) CallableType::isClosure([$foo, 'bar']); // bool(false) CallableType::isClosure('Foo\Bar::baz'); // bool(false)
Determine if a callable a function
CallableType::isFunction(fn($foo) => $foo); // bool(false) CallableType::isFunction('foo'); // bool(true) CallableType::isFunction([$foo, 'bar']); // bool(false) CallableType::isFunction('Foo\Bar::baz'); // bool(false)
Determine if a callable a method
CallableType::isMethod(fn($foo) => $foo); // bool(false) CallableType::isMethod('foo'); // bool(false) CallableType::isMethod([$foo, 'bar']); // bool(true) CallableType::isMethod('Foo\Bar::baz'); // bool(true)
Determine if a callable a static method
CallableType::isStaticMethod(fn($foo) => $foo); // bool(false) CallableType::isStaticMethod('foo'); // bool(false) CallableType::isStaticMethod([$foo, 'bar']); // bool(false) CallableType::isStaticMethod('Foo\Bar::baz'); // bool(true)
Get suitable reflection implementation for the callable
CallableType::reflection(fn($foo) => $foo); // object(ReflectionFunction) CallableType::reflection('foo'); // object(ReflectionFunction) CallableType::reflection([$foo, 'bar']); // object(ReflectionMethod) CallableType::reflection('Foo\Bar::baz'); // object(ReflectionMethod)
Class functions
Get the class or an object class short name
ClassType::short('Foo\Bar'); // "Bar" ClassType::short(Foo\Bar::class); // "Bar" ClassType::short(new Foo\Bar()); // "Bar"
Get the class and the parent classes
namespace Foo; class Bar {}; class Baz extends Foo {}; ... ClassType::selfAndParents('Foo\Bar'); // ["Foo\Bar"] ClassType::selfAndParents(Foo\Bar::class); // ["Foo\Bar"] ClassType::selfAndParents(new Foo\Bar()); // ["Foo\Bar"] ClassType::selfAndParents('Foo\Baz'); // ["Foo\Baz", "Foo\Bar"] ClassType::selfAndParents(Foo\Baz::class); // ["Foo\Baz", "Foo\Bar"] ClassType::selfAndParents(new Foo\Baz()); // ["Foo\Baz", "Foo\Bar"]
Get the class and the parent classes
namespace Foo; class Bar {}; class Baz extends Foo {}; ... ClassType::selfAndParents('Foo\Bar'); // ["Foo\Bar"] ClassType::selfAndParents(Foo\Bar::class); // ["Foo\Bar"] ClassType::selfAndParents(new Foo\Bar()); // ["Foo\Bar"] ClassType::selfAndParents('Foo\Baz'); // ["Foo\Baz", "Foo\Bar"] ClassType::selfAndParents(Foo\Baz::class); // ["Foo\Baz", "Foo\Bar"] ClassType::selfAndParents(new Foo\Baz()); // ["Foo\Baz", "Foo\Bar"]
Get the corresponding basic enum case dynamically from variable
Basic enumerations does not implement from() or tryFrom() methods, but it is possible to return the corresponding enum case using the constant() function.
ClassType::enumCase(FooEnum::class, 'bar');
File functions
Get the extension of a file name
FileType::extension('/foo/bar.baz'); // 'baz' FileType::extension('/foo/bar'); // ''
Write a string to a file and create the file directory recursively if it does not exist
FileType::put('/foo/bar.txt', 'baz');
Get the path to the file with $name inside the system temporary directory
FileType::temporary('foo.txt'); // '/tmp/foo.txt'
Determine if the path an absolute path
FileType::isAbsolutePath('C:/foo'); true FileType::isAbsolutePath('C:\\bar'); true FileType::isAbsolutePath('foo/bar'); false FileType::isAbsolutePath('/foo/bar'); true FileType::isAbsolutePath('\\foo\\bar'); true
Join the paths into one and fix the directory separators
FileType::joinPaths('a/', '/b/', '\\c', 'd'); // Return a/b/c/d
Fix the directory separators (remove duplicates and replace with the current system directory separator)
FileType::fixPath('/foo//bar\baz'); '/foo/bar/baz'
Guess the file extensions of the file
FileType::guessExtensions('/foo/bar.txt'); // ['txt'] FileType::guessExtensions('/foo/bar.jpg'); // ['jpeg', 'jpg', 'jpe', 'jfif']
Guess the file extension of the file
FileType::guessExtension('/foo/bar.txt'); // 'txt' FileType::guessExtension('/foo/bar.jpg'); // 'jpeg'
Guess the mime-type of the file
FileType::guessMime('/foo/bar.txt'); // 'text/plain' FileType::guessMime('/foo/bar.jpg'); // 'image/jpeg'
Number functions
Parse a float or integer value from the argument
Remove all characters except digits, +-.,eE from the argument and returns result as the float value or NULL if the parser fails.
NumberType::parse(" 123 "); // int(123) NumberType::parse(" 123.45 "); // float(123.45) NumberType::parse(" 123.00 "); // int(123)
Parse a float value from the argument
Remove all characters except digits, +-.,eE from the argument and returns result as the float value or NULL if the parser fails.
NumberType::parseFloat(" 123 "); // float(123) NumberType::parseFloat(" 123.45 "); // float(123.45)
Parse a integer value from the argument
Remove all characters except digits, plus and minus sign and returns result as the integer value or NULL if the parser fails.
NumberType::parseInteger(" 123 "); // int(123) NumberType::parseFloat(" 123.45 "); // int(12345)
Returns fractions of the float value
NumberType::fractions(123.45); // float(0.45) NumberType::parseFloat(123); // float(0)
Checks if the value is odd
NumberType::odd(2); // false NumberType::odd(3); // true
Checks if the value is even
NumberType::even(2); // true NumberType::even(3); // false
Round to nearest multiple
NumberType::roundStep(50, 5); // 50 NumberType::roundStep(52, 5); // 50 NumberType::roundStep(53, 5); // 55
Round down to nearest multiple
NumberType::floorStep(50, 5); // 50 NumberType::floorStep(52, 5); // 50 NumberType::floorStep(53, 5); // 50
Round up to nearest multiple
NumberType::ceilStep(50, 5); // 50 NumberType::ceilStep(52, 5); // 55 NumberType::ceilStep(53, 5); // 55
Spell out
// Current locale used NumberType::spellout(123.45); // one hundred twenty-three point four five // Specific locale used NumberType::spellout(123.45, 'ru'); // сто двадцать три целых сорок пять сотых
Division with zero tolerance
NumberType::divideSafely(1, 0); // null NumberType::divideSafely(1, null); // null NumberType::divideSafely(1, 0, 'zero'); // 'zero'
Percent calculation
The first argument is a value for calculating the percentage. The second argument is a base value corresponding to 100%.
NumberType::percentage(10, 100); // 10 NumberType::percentage(100, 100); // 100 NumberType::percentage(200, 100); // 200
Unsign a number
A negative value will be converted to zero, positive or zero value will be returned unchanged.
NumberType::unsign(-1); // 0 NumberType::unsign(-0.99); // 0 NumberType::unsign(0); // 0 NumberType::unsign(0.99); // 0.99 NumberType::unsign(1); // 1
Converts a number to string with sign.
NumberType::toStringWithSign(-1); // "-1" NumberType::toStringWithSign(1); // "+1" NumberType::toStringWithSign(0); // "0"
Object functions
Read the value at the end of the property path of the object graph
ObjectType::get($person, 'address.street');
Uses Symfony PropertyAccessor
Read the value of internal object property (protected and private)
Read ocramius
ObjectType::getInternal($object, $property);
Get the values of the property path of the object recursively
Read ocramius
$grandfather = new Person(name: 'grandfather'); $dad = new Person(name: 'dad', parent: $grandfather); $i = new Person(name: 'i', parent: $dad); ObjectType::getRecursive($i, 'parent'); // [Person(dad), Person(grandfather)]
Set the value at the end of the property path of the object graph
ObjectType::set($person, 'address.street', 'Abbey Road');
Uses Symfony PropertyAccessor
Write the value to internal object property (protected and private)
Read ocramius
ObjectType::setInternal($object, $property, $value);
Call the internal object method (protected and private) and returns result
Read ocramius
ObjectType::callInternal($object, $method, $arg1, $arg2, $arg3, ...);
Get a string representation of the object or enum
- Result of __toString method if presents
- String value of case for the BackedEnum
- Name of case for the UnitEnum
- or generated string like "FQCN@spl_object_id"
PHP default behavior: if the method is not defined, an error (Object of class X could not be converted to string
) is triggered.
namespace Foo; class Bar { } class BarMagicMethod { public function __toString(): string { return 'Bar'; } } enum BazUnitEnum { case APPLE; } enum BazStringBackedEnum: string { case APPLE = 'apple'; } enum BazIntBackedEnum: int { case APPLE = 1; } ObjectType::toString(new Foo); // 'Foo/Bar@1069' ObjectType::toString(new FooMagicMethod); // 'Foo' ObjectType::toString(BazUnitEnum::APPLE); // 'APPLE' ObjectType::toString(BazStringBackedEnum::APPLE); // '1'
Cast an object or a FQCN to FQCN
Returns the result of __toString
or null if the method is not defined.
PHP default behavior: if the method is not defined, an error (Object of class X could not be converted to string
) is triggered.
ObjectType::toClassName($objectOrClass): string;
String functions
Determine if a given string contains a given substring
StringType::contains('Foo', 'Bar'); // false StringType::contains('FooBar', 'Bar'); // true
Simple symmetric decryption of a string with a key (using libsodium)
StringType::decrypt(StringType::encrypt('The sensitive string', 'qwerty123456'), 'qwerty123456'); // 'The sensitive string'
Simple symmetric encryption of a string with a key (using libsodium)
StringType::encrypt('The sensitive string', 'qwerty123456');
Convenient way to perform a regular expression match
Default behaviour like preg_match_all(..., ..., PREG_SET_ORDER)
StringType::regexp('a1b2', '\S(\d)'); // [0 => [0 => 'a1', 1 => '1'], 1 => [0 => 'b2', 1 => '2']]
Exclude full matches from regular expression matches
StringType::regexp('a1b2', '\S(\d)', true); // [0 => [0 => '1'], 1 => [0 => '2']]
Get only first set from regular expression matches (exclude full matches)
StringType::regexp('a1b2', '(\S)(\d)', true, true); // [0 => 'a', 1 => '1']
Get only first match of each set from regular expression matches (exclude full matches)
StringType::regexp('a1b2', '(\S)(\d)', true, false, true); // [0 => 'a', 1 => 'b']
Get only first match of the first set from regular expression matches as single scalar value
StringType::regexp('a1b2', '(\S)(\d)', true, true, true); // 'a'
Replace first string occurrence in a string
StringType::replaceFirst('name name name', 'name', 'title'); // 'title name name'
Wrap string
StringType::wrap('target', '/'); // '/target/'
Guess the MIME-type of the string data
StringType::guessMime('foo bar'); // "text/plain" StringType::guessMime(file_get_content("foo.jpg")); // "image/jpeg"
Guess the file extension from the string data.
StringType::guessExtension('foo bar'); // "txt" StringType::guessExtension(file_get_content("foo.jpg")); // "jpeg"
Check if a string is a binary string
StringType::isBinary('Foo bar baz'); // false
Convert string to CamelCase
StringType::toCamelCase('string like this'); // 'StringLikeThis' StringType::toCamelCase('string_like_this'); // 'StringLikeThis'
Convert string to snake_case
StringType::toSnakeCase('StringLikeThis'); // 'string_like_this' StringType::toSnakeCase('string Like this'); // 'string_like_this'
ltrim()/rtrim()/trim() replacements supports UTF-8 chars in the charlist
Use these only if you are supplying the charlist optional arg and it contains UTF-8 characters. Otherwise trim will work normally on a UTF-8 string.
trim('«foo»', '»'); // "�foo" StringType::trim('«foo»', '»'); // "«foo"
Split text into sentences
StringType::sentences('Fry me a Beaver. Fry me a Beaver! Fry me a Beaver? Fry me Beaver no. 4?! Fry me many Beavers... End);
returns
[ [0] => 'Fry me a Beaver.', [1] => 'Fry me a Beaver!', [2] => 'Fry me a Beaver?', [3] => 'Fry me Beaver no. 4?!', [4] => 'Fry me many Beavers...', [5] => 'End' ]
Split text into words
StringType::words('Fry me many Beavers... End'); // ['Fry', 'me', 'many', 'Beavers', 'End']
Remove word from text
StringType::unword('Remove word from text', 'word'); // 'Remove from text'
Doctrine utils
DoctrineUtils activation
Manually instance a DoctrineUtils
public function __construct(private Doctrine\Persistence\ManagerRegistry $doctrine) { $doctrineUtils = new Cosmologist\Gears\Doctrine\DoctrineUtils($doctrine); }
Register DoctrineUtils as a service with Symfony DI
# config/services.yaml services: _defaults: autowire: true Cosmologist\Gears\Doctrine\DoctrineUtils:
Get metadata for a persistent object or a persistent object class
$doctrineUtils->getClassMetadata(new App\Entity\User()); // object(ClassMetadata) $doctrineUtils->getClassMetadata(App\Entity\User::class); // object(ClassMetadata) $doctrineUtils->getClassMetadata(new App\Controller\FooController())); // null $doctrineUtils->getClassMetadata(App\Controller\FooController::class); // null
Get real class of a persistent object (resolve a proxy class)
$doctrineUtils->getRealClass(Proxies\__CG__\App\Entity\User::class); // 'App\Entity\User' $doctrineUtils->getRealClass(new Proxies\__CG__\App\Entity\User()); // 'App\Entity\User' $doctrineUtils->getRealClass(App\Entity\User::class); // 'App\Entity\User' $doctrineUtils->getRealClass(new App\Entity\User()); // 'App\Entity\User' $doctrineUtils->getRealClass(new App\Controller\FooController()); // null $doctrineUtils->getRealClass(App\Controller\FooController::class); // null
Symfony ExpressionLanguage utils
Create an ExpressionFunction from a callable
ExpressionFunctionUtils::fromCallable('Foo\Bar::baz'); // object(ExpressionFunction)
For example, this can be useful for injecting simple objects (like ValueObject) into a Symfony service container
class AppExtension extends Extension { #[Override] public function load(array $configs, ContainerBuilder $container) { $container->addExpressionLanguageProvider(new class implements ExpressionFunctionProviderInterface { public function getFunctions(): array { return [ExpressionFunctionUtils::fromCallable([WalletIdentifier::class, 'create'], 'walletId')]; } }); $container ->getDefinition(OrderService::class) ->setArgument('$wallet', expr('walletId(13)')); } }
Symfony Forms utils
Convert domain model constraint violation to the form constraint violation
It's maybe useful when you validate your model from form on the domain layer and want to map violations to the form.
use Cosmologist\Gears\Symfony\Form\FormUtils; use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; use Symfony\Component\Validator\Exception\ValidationFailedException; if ($form->isSubmitted()) { try { return $this->handler->create($form->getData()); } catch (ValidationFailedException $exception) { $violationMapper = new ViolationMapper(); foreach ($exception->getViolations() as $domainViolation) { $violationMapper->mapViolation(FormUtils::convertDomainViolationToFormViolation($domainViolation), $form); } } } return $form->createView();
Trait with a method implementing DataMapperInterface::mapDataToForms with default behavior
This is convenient for mapping of form data to a model via DataMapperInterface::mapFormsToData(), for example, to create a model via a constructor, in this case, the mapping of model data to a form via DataMapperInterface::mapDataToForms() will remain unchanged, and you cannot not define it, since it is required by the DataMapperInterface.
use Cosmologist\Gears\Symfony\Form\DataFormsMapperDefaultTrait; class TransactionFormType extends AbstractType implements DataMapperInterface { use DataFormsMapperDefaultTrait; #[Override] public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('name', TextType::class) ->setDataMapper($this); } #[Override] public function mapFormsToData(Traversable $forms, mixed &$viewData): void { $forms = iterator_to_array($forms); $viewData = new Contact($forms['name']->getData()); }
Symfony Framework utils
Configure your Symfony application as a bundle using service container extension and configuration
namespace App; use App\DependencyInjection\AppExtension; use Cosmologist\Gears\Symfony\Framework\AppExtension\AppExtensionKernelInterface; use Cosmologist\Gears\Symfony\Framework\AppExtension\RegisterAppExtensionKernelTrait; use Override; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\HttpKernel\Kernel as BaseKernel; class Kernel extends BaseKernel implements AppExtensionKernelInterface { use MicroKernelTrait; use RegisterAppExtensionKernelTrait; #[Override] public function getAppExtension(): ExtensionInterface { return new AppExtension(); } }
Symfony Messenger utils
Symfony Messenger transport to redispatch messages on kernel.terminate event
It's a convenient way to speed up your app response to clients by scheduling hard tasks after the server response, thanks to the kernel.terminate event.
Firstly, you should enable this transport:
# config/services.yaml services: _defaults: autoconfigure: true Cosmologist\Gears\Symfony\Messenger\Transport\KernelTerminate\KernelTerminateTransportFactory:
# config/packages/messenger.yaml framework: messenger: transports: terminate: symfony-kernel-terminate://
Then, you should define a rule to route messages to this transport:
# config/packages/messenger.yaml framework: messenger: routing: App\Event\FooEvent: terminate
and
$this->messenger->dispatch(new App\Event\FooEvent('bar')); // or $this->messengerBus->dispatch(new App\Event\FooEvent('bar'));
Symfony PropertyAccess utils
Get the values of the property path of the object or of the array recursively
use Cosmologist\Gears\Symfony\PropertyAccessor\RecursivePropertyAccessor; $grandfather = new Person(name: 'grandfather'); $dad = new Person(name: 'dad', parent: $grandfather); $i = new Person(name: 'i', parent: $dad); (new RecursivePropertyAccessor())->getValue($i, 'parent'); // [Person(dad), Person(grandfather)]
Symfony Validator utils
Simple and convenient way instance of ValidationFailedException with single ConstraintViolation
use Cosmologist\Gears\Symfony\Validator\ValidationFailedException; ValidationFailedException::violate($foo, "Foo with invalid bar"); ValidationFailedException::violate($foo, "Foo with invalid {{ bar }}", compact('bar')); ValidationFailedException::violate($foo, "Foo with invalid bar", propertyPath: 'bar');