Giter Club home page Giter Club logo

next-faker's Introduction

Faker 2.0

Here are some rough ideas on how we think the next version of Faker could work.

Basic usage

use Faker\Factory;

$faker = Factory::create();
$faker->firstname();
$faker->address();
$faker->city();
$faker->maleTitle();

Extensions

All features should be implemented as an "extension". An extension is always single locale. The "core" Faker\Generator contains a set of predefined methods; firstname(), address(), companyName() etc, which is syntactic sugar for calling the extensions.

Registering an extension

We use PSR-11 to configure extensions:

<?php

use Example\Person;
use Faker\ContainerBuilder;
use Faker\Factory;
use Psr\Container;

$containerBuilder = new ContainerBuilder();

$containerBuilder->register(Person::class);

/** @var Container\ContainerInterface $container */
$container = $containerBuilder->build();

$faker = Factory::create($container);

Implementing an extension

An empty marker interface is provided:

<?php

namespace Faker\Extension;

interface Extension
{
}

This marker interface can be implemented to help with auto-discovery of extensions:

<?php

namespace Example;

use Faker\Extension;

final class Person implements Extension\Extension
{
    /**
     * @var array<int, string> 
     */
    private static array $firstNames = [
        // ...        
    ];

    public function firstName(): string
    {
        $key = array_rand(self::$firstNames);
        
        return self::$firstNames[$key];
    }
}

A GeneratorAwareExtension interface is provided:

<?php

namespace Faker\Extension;

use Faker\Generator;

interface GeneratorAwareExtension extends Extension
{
    public function withGenerator(Generator $generator): self
}

This interface can be implemented to gain access to the instance of Faker\Generator when returning the extension from the container:

<?php

namespace Example\Person;

use Faker\Extension;
use Faker\Generator;

final class Person implements Extension\GeneratorAwareExtension
{
    private Generator $generator = null;
    
    /**
     * @var array<int, string> 
     */
    private static array $firstNames = [
        // ...        
    ];

    public function firstName(): string
    {
        return $this->generator->randomElement(self::$firstnames);
    }

    public function withGenerator(Generator $generator): self
    {
        $instance  = clone $this;
        
        $instance->generator = $generator;
        
        return $instance;
    }
}

A GeneratorAwareExtensionTrait is provided with a default implementation of the GeneratorAwareExtension interface.

Using an extension

<?php

use Example\Person;

$faker->ext(Person::class)->firstName();

Example of implementing a core method with an extension

<?php

use Example\Person;

public function firstName() {
    return $faker->ext(Person::class)->firstName();
}

Some "core features" in 1.0 like Doctrine support could be moved to an extension, and it will probably live in its own package.

A suggestion about what extensions could be in the core package can be found in providers.md.

Packages

There could be a fakerphp/faker "core" package with one English US Provider. That way we know that Faker will work "out of the box".

Other languages are split into separate packages like;

  • fakerphp/spanish (contains es_AR, es_ES, es_PE, es_VE)
  • fakerphp/french (contains fr_BE, fr_CA, fr_CH, fr_FR)
  • fakerphp/german
  • fakerphp/swedish
  • etc

The language specific packages contain a localized version of the "core" English extensions and possibly language specific extensions. They are maintained and versioned separately from the "core" package.

Loading multiple languages

The default way of instantiating a Generator is with a factory method. The factory method loads all default extensions. If the factory method lives in Generator::create() or Factory::create() in an implementation detail.

There could be multiple factory methods, one for each locale.

use Faker\German\GermanDE;
use Faker\English\EnglishNZ;

$fakerEn = EnglishNZ::create();
$fakerDe = GermanDE::create();

One could of course build up all the extensions without a factory method, ie like using dependency injection.

If multiple instances of the PersonInterface is provided, we could pick one at random.

use Faker\German\German\Person as PersonDe;
use Faker\English\English\Nz\Person as PersonNz;

$faker = // build a generator somehow with both PersonDe and PersonNz
$faker->firstName(); // German or English name
$faker->ext(PersonInterface::class)->firstName(); // Same as above

$faker->ext(PersonDe::class)->firstName(); // German name

Modifiers

use Faker\Factory;

$faker = Factory::create();
$faker->withUnique()->name();
$faker->withMaybe(0.8)->name();
$faker->withValid(fn($v) => strlen($v) > 3))->name();
$faker->take(5)->name(); // returns 5 names

We can keep the modifier as Proxy classes like they are in 1.0.

class Generator {
  private $unique;

  public function __construct()
  {
     // ..

     $this->unique = new UniqueGenerator($this);
  }

  /**
   * @return self The UniqueGenerator is a proxy
   */
  public function withUnique()
  {
    return $this->unique;
  }

  /**
   * @return self
   */
  public function withMaybe($weight = 0.5, $default = null)
  {
    if (is_int($weight) && mt_rand(1, 100) <= $weight) {
      return $this;
    }

    return new DefaultGenerator($default);
  }

  /**
   * @return self
   */
  public function withValid(callable $validator, int $maxRetries = 10000)
  {
    return new ValidGenerator($this, $validator, $maxRetries);
  }
}

Magic

All classes have defined methods. No magic __call() or __get(). It is only the Modifier proxy classes that includes the magic __get().

This will help IDE auto completion.

Reproducible builds

If someone uses a "seed" to make sure the generated content is reproducible over multiple runs, they can only expect that to be true for the same version of Faker.

use Faker\Factory;

$faker = Factory::create();
$faker->seed(1);

next-faker's People

Contributors

derrabus avatar jbelien avatar krsriq avatar localheinz avatar nyholm avatar pimjansen avatar samnela 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.