loophp/typed-generators

Generate random typed values and in any shape.

dev-main 2023-10-20 19:40 UTC

README

Latest Stable Version GitHub stars Total Downloads GitHub Workflow Status Scrutinizer code quality Type Coverage Code Coverage Mutation testing badge License Donate!

PHP Typed Generators

Description

Generate random typed values and in any shape.

Useful for writing your tests, there's no need to write static set of typed values, you can now generate them using this tool.

Each generated random values or shape is fully typed and can safely be used by existing static analysis tools such as PHPStan or PSalm.

Installation

composer require loophp/typed-generators

Usage

This library has a single entry point class factory. By using a single factory class, the user is able to quickly instantiate objects and use auto-completion.

Find the complete API directly in the TG class.

Quick API overview

<?php

declare(strict_types=1);

namespace Snippet;

use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$arrays    = TG::array(TG::string(), TG::string());
$arrayKeys = TG::arrayKey();
$booleans  = TG::bool();
$closures  = TG::closure();
$unions    = TG::union(TG::bool(), TG::int());
$customs   = TG::custom(TG::string(), static fn (): string => 'bar');
$datetimes = TG::datetime();
$faker     = TG::faker(TG::string(), static fn (Faker\Generator $faker): string => $faker->city());
$floats    = TG::float();
$integers  = TG::int();
$iterators = TG::iterator(TG::bool(), TG::string());
$lists     = TG::list(TG::string());
$negatives = TG::negativeInt();
$nulls     = TG::null();
$nullables = TG::nullable(TG::string());
$numerics  = TG::numeric();
$objects   = TG::object();
$positives = TG::positiveInt();
$statics   = TG::static(TG::string(), 'foo');
$strings   = TG::string();
$uniqids   = TG::uniqid();

Generate list of values

<?php

declare(strict_types=1);

namespace Snippet;

use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$strings = TG::string();       // Generate strings

foreach ($strings as $string) {
    var_dump($string);         // Random string generated
}

echo $strings();               // Print one random string

Generate KeyValue pairs

<?php

declare(strict_types=1);

namespace Snippet;

use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$iteratorStringBool = TG::iterator(
    TG::string(),       // Keys: Generate strings for keys
    TG::bool()          // Values: Generate booleans for values
);

foreach ($iteratorStringBool() as $key => $value) {
    var_dump($key, $value);   // Random string for key, random boolean for value.
}

Integration with Faker

<?php

declare(strict_types=1);

namespace Snippet;

use Faker\Generator;
use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$fakerType = TG::faker(
    TG::string(),
    fn (Generator $faker): string => $faker->city()
);

$iterator = TG::iterator(
    TG::string(4), // Keys: A random string of length 4
    $fakerType     // Values: A random city name
);

foreach ($iterator() as $key => $value) {
    var_dump($key, $value);
}

Use random union values

Union values are values that can be either of type A or type B.

<?php

declare(strict_types=1);

namespace Snippet;

use Faker\Generator;
use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$fakerType = TG::faker(
    TG::string(),
    fn (Generator $faker): string => $faker->firstName()
);

$iterator = TG::iterator(
    TG::bool(),    // Keys: A random boolean
    TG::union(  // Values: A random union value which can be
        $fakerType,// either a firstname
        TG::int()  // either an integer.
    )
);

foreach ($iterator() as $key => $value) {
    var_dump($key, $value);
}

Generate a complex typed array shape

<?php

declare(strict_types=1);

namespace Snippet;

use Faker\Generator;
use loophp\TypedGenerators\TypeGeneratorFactory as TG;

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

$iterator = TG::array(TG::static(TG::string(), 'id'), TG::int(6))
    ->add(
        TG::static(TG::string(), 'uuid'),
        TG::uniqid()
    )
    ->add(
        TG::static(TG::string(), 'firstName'),
        TG::faker(
            TG::string(),
            static fn (Generator $faker): string => $faker->firstName()
        )
    )
    ->add(
        TG::static(TG::string(), 'country'),
        TG::faker(
            TG::string(),
            static fn (Generator $faker): string => $faker->country()
        )
    )
    ->add(
        TG::static(TG::string(), 'isCitizen'),
        TG::bool()
    )
    ->add(
        TG::static(TG::string(), 'hometowm'),
        TG::faker(
            TG::string(),
            static fn (Generator $faker): string => $faker->city()
        )
    )
    ->add(
        TG::static(TG::string(), 'lastSeen'),
        TG::datetime()
    );

foreach ($iterator as $k => $v) {
    // \PHPStan\dumpType($v);
    /** @psalm-trace $v */
    print_r($v);
}

This example will produce such arrays:

Array
(
    [id] => 545327499
    [uuid] => 629f7198091ee
    [firstName] => Sandra
    [country] => Sardinia
    [isCitizen] => 1
    [hometowm] => Ecaussinnes
    [lastSeen] => DateTimeImmutable Object
        (
            [date] => 2009-06-02 07:40:37.000000
            [timezone_type] => 3
            [timezone] => UTC
        )
)
Array
(
    [id] => 623241523
    [uuid] => 629f719809290
    [firstName] => Paolo
    [country] => Sicily
    [isCitizen] =>
    [hometowm] => Quaregnon
    [lastSeen] => DateTimeImmutable Object
        (
            [date] => 1989-11-11 16:22:02.000000
            [timezone_type] => 3
            [timezone] => UTC
        )
)

Analyzing the $iterator variable with PSalm and PHPStan will give:

$ ./vendor/bin/phpstan analyse --level=9 test.php
 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ --------------------------------------------------------
  Line   test.php
 ------ --------------------------------------------------------
  45     Dumped type: array<string, bool|DateTimeInterface|int|string>
 ------ --------------------------------------------------------

With PSalm:

$ ./vendor/bin/psalm --show-info=true --no-cache test.php
Target PHP version: 7.4 (inferred from composer.json)
Scanning files...
Analyzing files...

I

INFO: Trace - test.php:46:5 - $v: array<string, DateTimeInterface|bool|int|string> (see https://psalm.dev/224)

Code quality, tests, benchmarks

Every time changes are introduced into the library, Github runs the tests.

The library has tests written with PHPUnit. Feel free to check them out in the tests directory.

Before each commit, some inspections are executed with GrumPHP; run composer grumphp to check manually.

The quality of the tests is tested with Infection a PHP Mutation testing framework - run composer infection to try it.

Static analyzers are also controlling the code. PHPStan and PSalm are enabled to their maximum level.

Contributing

Feel free to contribute by sending pull requests. We are a usually very responsive team and we will help you going through your pull request from the beginning to the end.

For some reasons, if you can't contribute to the code and willing to help, sponsoring is a good, sound and safe way to show us some gratitude for the hours we invested in this package.

Sponsor me on Github and/or any of the contributors.

Changelog

See CHANGELOG.md for a changelog based on git commits.

For more detailed changelogs, please check the release changelogs.