Skip to content

Lightweight PHP utility for seamless type-casting and data retrieval from dynamic variables, arrays, and objects.

License

Notifications You must be signed in to change notification settings

WPLake/PHP-Typed

Repository files navigation

PHP Typed

Typed is a lightweight PHP utility for seamless type-casting and data retrieval from dynamic variables, arrays, and objects.

This package provides a single Typed class with static methods and offers compatibility with PHP versions 7.4+ and 8.0+.

1. Why Use Typed?

Handling type casting in PHP often leads to verbose and repetitive constructions, especially in array-related cases.

Typed streamlines this process, allowing you to fetch and cast values with concise, readable code.

Example: Plain PHP

function getUserAge(array $userData): int
{
    return isset($userData['meta']['age']) &&
           is_numeric($userData['meta']['age'])
           ? (int) $userData['meta']['age']
           : 0;
}

function upgradeUserById($mixedUserId): void
{
    $userId = is_string($mixedUserId) || 
    is_numeric($mixedUserId)
        ? (string) $mixedUserId
        : '';
}

The same with the Typed utility

use function WPLake\Typed\int;
use function WPLake\Typed\string;

function getUserAge(array $userData): int
{
    return int($userData, 'meta.age');
}

function upgradeUserById($mixedUserId): void
{
    $userId = string($mixedUserId);
}

The code like string($array, 'key') resembles (string)$array['key'] while being safe and smart — it even handles nested keys and default values.

In case now you're thinking: "Hold on guys, but this code won't work! Are your using type names as function names?"

Our answer is: "Yes! And actually it isn't prohibited."

See the explanation in the special section - 5. Note about the function names

Backing to the package. Want to provide a default value when the key is missing? Here you go:

string($data, 'some.key', 'Default Value');

Can't stand functions? The same functions set is available as static methods of the Typed class:

use WPLake\Typed\Typed;

Typed::int($data,'key');

2. Installation and usage

Typed class is distributed as a Composer package, making installation straightforward:

composer require wplake/typed

After installation, ensure that your application includes the Composer autoloader (if it hasn’t been included already):

require __DIR__ . '/vendor/autoload.php';

Usage:

use function WPLake\Typed\string;
use WPLake\Typed\Typed;

$string = string($array, 'first.second');
// alternatively:
$string = Typed::string($array, 'first.second');
// custom fallback:
$string = string($array, 'first.second', 'custom default');

3. How It Works

The logic of all casting methods follows this simple principle:

“Provide me a value of the requested type from the given source by the given path, or return the default value.”

For example, let's review the string method declaration:

namespace WPLake\Typed;

/**
 * @param mixed $source
 * @param int|string|array<int,int|string>|null $keys
 */
function string($source, $keys = null, string $default = ''): string;

Usage Scenarios:

1. Extract a string from a mixed variable

By default, returning an empty string if the variable can't be converted to a string:

$userName = string($unknownVar);
// you can customize the fallback:
$userName = string($unknownVar, null, 'custom fallback value');

2. Retrieve a string from an array

Including nested structures (with dot notation or as an array):

$userName = string($array, 'user.name');
// alternatively:
$userName = string($array, ['user','name',]);
// custom fallback:
$userName = string($array, 'user.name', 'Guest');

3. Access a string from an object

Including nested properties:

$userName = string($companyObject, 'user.name');
// alternatively:
$userName = string($companyObject, ['user', 'name',]);
// custom fallback:
$userName = string($companyObject, 'user.name', 'Guest');

4. Work with mixed structures

(e.g., object->arrayProperty['key']->anotherProperty or ['key' => $object])

$userName = string($companyObject,'users.john.name');
// alternatively:
$userName = string($companyObject,['users','john','name',]);
// custom fallback:
$userName = string($companyObject, 'users.john.name', 'Guest');

In all the cases, the fallback value is the 'empty' value for the specific type (e.g. 0, false, "", and so on), but you can pass a custom default value as the third argument:

$userName = string($companyObject,'users.john.name', 'Guest');

4. Supported types

Functions for the following types are present:

  • string
  • int
  • float
  • bool
  • object
  • dateTime
  • arr (stands for array, because it's a keyword)
  • any (allows to use short dot-keys usage for unknowns)

Additionally:

  • boolExtended (true,1,"1", "on" are treated as true, false,0,"0", "off" as false)
  • stringExtended (supports objects with __toString)

For optional cases, when you need to apply the logic only when the item is present, each function has an OrNull variation (e.g. stringOrNull, intOrNull, and so on), which returns null if the key doesn’t exist.

5. Note about the function names

Surprisingly, PHP allows functions to use the same names as variable types.

Think it’s prohibited? Not quite! While certain names are restricted for classes, interfaces, and traits, function names are not:

“These names cannot be used to name a **class, interface, or trait **” - PHP Manual: Reserved Other Reserved Words

This means you we can have things like string($array, 'key'), which resembles (string)$array['key'] while being safer and smarter — it even handles nested keys.

By the way, importing these functions does not interfere with native type casting in PHP. So, while practically unnecessary, the following construction will still work:

use function WPLake\Typed\string;

echo (string)string('hello');

Note: Unlike all the other types, the array keyword falls under a different category, which also prohibits its usage for function names. That's why in this case we used the arr name instead.

6. FAQ

6.1) Why not just straight type casting?

Straight type casting in PHP can be unsafe and unpredictable in certain scenarios.

For example, the following code will throw an error if the $mixed variable is an object of a class that doesn’t explicitly implement __toString:

class Example {
// ...
}
$mixed = new Example();
// ...
function getName($mixed):void{
 return (string)$mixed;
}

Additionally, attempting to cast an array to a string, like (string)$myArray will:

  1. Produce a PHP Notice: Array to string conversion.
  2. Return the string "Array", which is rarely the intended behavior.

This unpredictability can lead to unexpected bugs and unreliable code.

6.2) Why not just Null Coalescing Operator?

While the Null Coalescing Operator (??) is useful, it doesn’t address type checking or casting requirements.

// Plain PHP:
$number = $data['meta']['number']?? 10;
$number = is_numeric($number)?
(int) $number:
10;

// Typed:
$number = int($data, 'meta.number', 10);

Additionally, with Null Coalescing Operator and a custom default value, you have to repeat yourself.

6.3) Shouldn't we use typed objects instead?

OOP is indeed powerful, and you should always prioritize using objects whenever possible. However, the reality is that our code often interacts with external dependencies beyond our control.

This package simplifies handling such scenarios. Any seasoned PHP developer knows the pain of type-casting when working with environments outside of frameworks, e.g. in WordPress.

6.4) Is the dot syntax in keys inspired by Laravel Helpers?

Yes, the dot syntax is inspired by Laravel’s Arr::get and similar solutions. It provides an intuitive way to access nested data structures.

6.5) Why not just use Laravel Collections?

Laravel Collections and similar libraries don’t offer type-specific methods like this package does.

While extending Laravel Collections package could be a theoretical solution, we opted for a standalone package because:

  1. PHP Version Requirements: Laravel Collections require PHP 8.2+, while Typed supports PHP 7.4+.
  2. Dependencies: Laravel Collections bring several external Laravel-specific dependencies.
  3. Global Functions: Laravel Collections rely on global helper functions, which are difficult to scope when needed.

In addition, when we only need to extract a single variable, requiring the entire array to be wrapped in a collection would be excessive.

7. Contribution

We would be excited if you decide to contribute 🤝

Please open Pull Requests against the main branch.

Code Style Agreements:

7.1) PSR-12 Compliance

Use the PHP_CodeSniffer tool in your IDE with the provided phpcs.xml, or run composer phpcbf to format your code.

7.2) Static Analysis with PHPStan

Set up PHPStan in your IDE with the provided phpstan.neon, or run composer phpstan to validate your code.

7.3) Unit Tests

Pest is setup for Unit tests. Run them using composer pest.

About

Lightweight PHP utility for seamless type-casting and data retrieval from dynamic variables, arrays, and objects.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages