Utility classes used across Orisai libraries
Install with Composer
composer require orisai/utils
Sometimes it's useful to have dependency required only by specific class, method or function. In that case we should check whether the dependency is installed.
use Orisai\Utils\Dependencies\Dependencies;
use Orisai\Utils\Dependencies\Exception\ExtensionRequired;
$missing = Dependencies::getNotLoadedExtensions(['json', 'curl']);
if ($missing !== []) {
throw ExtensionRequired::forClass($missing, static::class);
throw ExtensionRequired::forMethod($missing, static::class, __FUNCTION__);
throw ExtensionRequired::forFunction($missing, __FUNCTION__);
}
You can also check whether a single extension is loaded
use Orisai\Utils\Dependencies\Dependencies;
if (Dependencies::isExtensionLoaded('curl')) {
// Do something
}
use Orisai\Utils\Dependencies\Dependencies;
use Orisai\Utils\Dependencies\Exception\PackageRequired;
$missing = Dependencies::getNotLoadedPackages(['example/package1', 'example/package2']);
if ($missing !== []) {
throw PackageRequired::forClass($missing, static::class);
throw PackageRequired::forMethod($missing, static::class, __FUNCTION__);
throw PackageRequired::forFunction($missing, __FUNCTION__);
}
If the class extends class, implements interface or uses trait from an optional package then exception must be thrown before class is defined. Otherwise php fatal error is thrown.
use Orisai\Utils\Dependencies\Exception\PackageRequired;
if (!class_exists(ClassFromOptionalDependency::class)) {
throw PackageRequired::forUndefinedClass(['example/package'], Example::class, __FILE__);
}
class Example extends ClassFromOptionalDependency
{
}
You can also check whether a single package is loaded
use Orisai\Utils\Dependencies\Dependencies;
if (Dependencies::isPackageLoaded('example/package')) {
// Do something
}
For testing, you can emulate extension and packages are not loaded instead of having not tested code branches or complicated CI.
Just make sure your optionals tests run in separate processes to prevent race conditions from parallel tests running.
e.g. with PHPUnit @runInSeparateProcess
annotation.
Testing extensions:
use Orisai\Utils\Dependencies\Dependencies;
use Orisai\Utils\Dependencies\DependenciesTester;
DependenciesTester::addIgnoredExtensions(['curl']);
Dependencies::getNotLoadedExtensions(['curl']); // ['curl']
Dependencies::isExtensionLoaded('curl'); // false
Testing packages:
use Orisai\Utils\Dependencies\Dependencies;
use Orisai\Utils\Dependencies\DependenciesTester;
DependenciesTester::addIgnoredPackages(['example/package']);
Dependencies::getNotLoadedPackages(['example/package']); // ['example/package']
Dependencies::isPackageLoaded('example/package'); // false
Intuitive merging
- merging arrays recursively
- preferring non-default values in case of collision
- overriding string keys
- adding numeric keys on the end of array
use Orisai\Utils\Arrays\ArrayMerger;
$default = [
'default' => 'default',
'overridden' => 'not-overridden',
'merged' => [
'foo' => 'not-overridden',
'bar' => 'not-overridden',
'baz' => 'not-overridden',
],
];
$toMerge = [
'overridden' => 'overridden',
'new' => 'new',
'merged' => [
'baz' => 'overridden',
],
];
$result = ArrayMerger::merge($default, $toMerge);
$expected = [
'default' => 'default',
'overridden' => 'overridden',
'merged' => [
'foo' => 'not-overridden',
'bar' => 'not-overridden',
'baz' => 'overridden',
],
'new' => 'new',
];
assert($result === $expected);
Shortcut functions wrapping behavior of php Reflector
classes
- Use these shortcuts just for single-time reflection uses. For more intensive reflection use cases always use built-in Reflector classes.
Get list of classes of parent classes, including actual class
use Orisai\Utils\Reflection\Classes;
$classList = Classes::getClassList(LogicException::class);
// $classList
[
LogicException::class,
Exception::class,
];
Get class directory
use Orisai\Exceptions\DomainException;
use Orisai\Utils\Reflection\Classes;
$classDir = Classes::getClassDir(DomainException::class);
// $classDir
'/path/to/vendor/orisai/exceptions';
Get class name without namespace
use Orisai\Exceptions\DomainException;
use Orisai\Utils\Reflection\Classes;
$classShortName = Classes::getShortName(DomainException::class);
// $classShortName
'DomainException';
Get method operator
use Orisai\Utils\Reflection\Classes;
$operator = Classes::getMethodOperator(Example::class, 'staticMethod');
// $operator
'::';
use Orisai\Utils\Reflection\Classes;
$operator = Classes::getMethodOperator(Example::class, 'objectMethod');
// $operator
'->';