Git Product home page Git Product logo

typed-arrays's Introduction

typed-arrays


Continuous Integration type-coverage psalm

Totally typed library to work with lists or maps.

Installation

To use this library in your project, please install it via composer:

$ composer require boesing/typed-arrays

Usage

The main reason why this library was created was the fact, that every array in PHP is a hashmap. If you primarily work with APIs, you might have experienced that json_encode of an array type sometimes leads to annoying issues.

To get rid of array being passed through an application, the OrderedListInterface and the MapInterface became very handy. To also provide most if not any of the array_* functions to the developers, most of these array functions do have a method within OrderedListInterface or MapInterface.

Common mistakes

Lets take some real-world use cases to better reflect the idea behind this library:

$listOfIntegers = [1, 2, 3, 4];

$myObject = new stdClass();
$myObject->integers = $listOfIntegers;

echo json_encode($myObject) . PHP_EOL;

// Output of the code above will be: `{"integers":[1,2,3,4]}`
// Now some refactoring has to be made since the requirement changed. The requirement now is that the integers list
// must not contain odd values anymore. So `array_filter` to the rescue, right?

$listOfEvenIntegers = array_filter([1, 2, 3, 4], static fn (int $integer): int => $integer % 2 === 0);

$myObject = new stdClass();
$myObject->integers = $listOfEvenIntegers;

echo json_encode($myObject) . PHP_EOL; 

// Output of the refactored code above now became: `{"integers":{"1":2,"3":4}}`
// So what now happened is a huge problem for highly type-sensitive API clients since we changed a list to a hashmap
// Same happens with hashmaps which suddenly become empty.

$hashmap = [
    'foo' => 'bar',
];
$myObject = new stdClass();
$myObject->map = $hashmap;

echo json_encode($myObject) . PHP_EOL; 

// Output of the code above will be: `{"map":{"foo":"bar"}}`
// So now some properties are being added, some are being removed, the definition of your API says
// "the object will contain additional properties because heck I do not want to declare every property"
// "so to make it easier, every property has a string value"
// can be easily done with something like this in JSONSchema: `{"type": "object", "additional_properties": {"type": "string"}}`
// Now, some string value might become `null` due to whatever reason, lets say it was a bug and thus the happy path always returned a string
// The most logical way here is, due to our lazyness, to use something like `array_filter` to get rid of all our non-string values

$hashmap = [
    'foo' => null,
];
$myObject = new stdClass();
$myObject->map = array_filter($hashmap);

echo json_encode($myObject) . PHP_EOL;

// Output of the refactored code above now became: `{"map":[]}`
// So in case that every array value is being wiped due to the filtering, we suddenly have a type-change from
// a hashmap to a list. This is ofc also problematic since we do not want to have a list here but an empty object like
// so: `{"map":{}}`

(The above example can be verified on 3v4l.org - a PHP sandbox: https://3v4l.org/Gfogn#v8.1.6)

typed-arrays to the rescue

So with this library, one is a little bit more type-safe when it comes to array handling. However, the MapInterface actually will become null within a json_encode in case it is empty.

So lets take the above example in combination with our factories:

use Boesing\TypedArrays\TypedArrayFactory;
$factory = new TypedArrayFactory();

$listOfIntegers = $factory->createOrderedList([1, 2, 3, 4]);

$myObject = new stdClass();
$myObject->integers = $listOfIntegers;

echo json_encode($myObject) . PHP_EOL;

// Output of the code above will be: `{"integers":[1,2,3,4]}`
// Now some refactoring has to be made since the requirement changed. The requirement now is that the integers list
// must not contain odd values anymore. So `array_filter` to the rescue, right?

$listOfEvenIntegers = $factory->createOrderedList([1, 2, 3, 4])->filter(static fn (int $integer): int => $integer % 2 === 0);

$myObject = new stdClass();
$myObject->integers = $listOfEvenIntegers;

echo json_encode($myObject) . PHP_EOL; 

// Output of the refactored code above now became: `{"integers":[2, 4]}`
// Due to the internal handling of `array_filter`, the `OrderedListInterface` won't change its type.

// Even hashmaps can be filtered, the type stays the same but in case of an empty map, `null` is being passed to the JSON object
$hashmap = $factory->createMap([
    'foo' => 'bar',
]);
$myObject = new stdClass();
$myObject->map = $hashmap;

echo json_encode($myObject) . PHP_EOL; 

// Output of the code above will be: `{"map":{"foo":"bar"}}`
// So now some properties are being added, some are being removed, the definition of your API says
// "the object will contain additional properties because heck I do not want to declare every property"
// "so to make it easier, every property has a string value"
// can be easily done with something like this in JSONSchema: `{"type": "object", "additional_properties": {"type": "string"}}`
// Now, some string value might become `null` due to whatever reason, lets say it was a bug and thus the happy path always returned a string
// The most logical way here is, due to our lazyness, to use something like `array_filter` to get rid of all our non-string values

$hashmap = $factory->createMap([
    'foo' => null,
]);
$myObject = new stdClass();
$myObject->map = $hashmap->filter(static fn ($value) => $value !== null);

echo json_encode($myObject) . PHP_EOL;

// Output of the refactored code above now became: `{"map":null}`
// So in case that every array value is being wiped due to the filtering, we suddenly have a type-change from
// a hashmap to a list. This is ofc also problematic since we do not want to have a list here but an empty object like
// so: `{"map":{}}`

Conclusion

When it comes to API responses, you might not want to rely on PHP array structure. Always prefer real objects with real properties and real property type-hints over non-empty-array.

typed-arrays's People

Contributors

boesing avatar dependabot[bot] avatar icanhazstring avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.