Skip to content

Commit

Permalink
[Modifiers] Helper methods to use our custom generators (#155)
Browse files Browse the repository at this point in the history
* Helper functions to use our custom validators

* Update name

* Make sure we support extensions

* Updated some tests

* deprecated DefaultGenerator

* Name it back to "optional"

* Fixed tests

* fix

* cs

* cs

* Fix UniqueGenerator not storing previously generated values when calling ->ext() method

* Fix types in randomFloat

* Generate baseline for proxy classes

* Add baseline for psalm

* Add regression test for UniqueGenerator

* Add ValidGeneratorTest.php

Co-authored-by: Bram Ceulemans <[email protected]>
  • Loading branch information
Nyholm and Bram Ceulemans authored Jan 19, 2022
1 parent 9594d0a commit fa135b4
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 37 deletions.
24 changes: 24 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ parameters:
count: 1
path: src/Faker/Extension/ContainerBuilder.php

-
message: "#^Method Faker\\\\Generator\\:\\:optional\\(\\) should return Faker\\\\Generator but returns Faker\\\\ChanceGenerator\\.$#"
count: 1
path: src/Faker/Generator.php

-
message: "#^Method Faker\\\\Generator\\:\\:unique\\(\\) should return Faker\\\\Generator but returns Faker\\\\UniqueGenerator\\.$#"
count: 1
path: src/Faker/Generator.php

-
message: "#^Method Faker\\\\Generator\\:\\:valid\\(\\) should return Faker\\\\Generator but returns Faker\\\\ValidGenerator\\.$#"
count: 1
path: src/Faker/Generator.php

-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
Expand Down Expand Up @@ -735,6 +750,15 @@ parameters:
count: 2
path: src/Faker/Provider/Base.php

-
message:
"""
#^Instantiation of deprecated class Faker\\\\DefaultGenerator\\:
Use ChanceGenerator instead$#
"""
count: 1
path: src/Faker/Provider/Base.php

-
message: "#^Negated boolean expression is always false\\.$#"
count: 1
Expand Down
17 changes: 12 additions & 5 deletions psalm.baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.7.1@cd53e047a58f71f646dd6bf45476076ab07b5d44">
<files psalm-version="4.17.0@6f4707aa41c9174353a6434bba3fc8840f981d9c">
<file src="src/Faker/Calculator/Luhn.php">
<InvalidReturnStatement occurrences="1">
<code>0</code>
Expand All @@ -9,10 +9,17 @@
</InvalidReturnType>
</file>
<file src="src/Faker/Generator.php">
<InvalidThrow occurrences="1">
<code>ContainerExceptionInterface</code>
</InvalidThrow>
</file>
<InvalidReturnStatement occurrences="3">
<code>$this-&gt;uniqueGenerator</code>
<code>new ChanceGenerator($this, $weight, $default)</code>
<code>new ValidGenerator($this, $validator, $maxRetries)</code>
</InvalidReturnStatement>
<InvalidReturnType occurrences="3">
<code>self</code>
<code>self</code>
<code>self</code>
</InvalidReturnType>
</file>
<file src="src/Faker/ORM/CakePHP/EntityPopulator.php">
<UndefinedClass occurrences="1">
<code>TableRegistry</code>
Expand Down
60 changes: 60 additions & 0 deletions src/Faker/ChanceGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace Faker;

use Faker\Extension\Extension;

/**
* This generator returns a default value for all called properties
* and methods. It works with Faker\Generator::optional().
*
* @mixin Generator
*/
class ChanceGenerator
{
private $generator;
private $weight;
protected $default;

/**
* @param Extension|Generator $generator
*/
public function __construct($generator, float $weight, $default = null)
{
$this->default = $default;
$this->generator = $generator;
$this->weight = $weight;
}

public function ext(string $id)
{
return new self($this->generator->ext($id), $this->weight, $this->default);
}

/**
* Catch and proxy all generator calls but return only valid values
*
* @param string $attribute
*
* @deprecated Use a method instead.
*/
public function __get($attribute)
{
trigger_deprecation('fakerphp/faker', '1.14', 'Accessing property "%s" is deprecated, use "%s()" instead.', $attribute, $attribute);

return $this->__call($attribute, []);
}

/**
* @param string $name
* @param array $arguments
*/
public function __call($name, $arguments)
{
if (mt_rand(1, 100) <= (100 * $this->weight)) {
return call_user_func_array([$this->generator, $name], $arguments);
}

return $this->default;
}
}
2 changes: 1 addition & 1 deletion src/Faker/Core/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function randomDigitNotZero(): int
return mt_rand(1, 9);
}

public function randomFloat(int $nbMaxDecimals = null, float $min = 0, float $max = null): float
public function randomFloat(?int $nbMaxDecimals = null, float $min = 0, ?float $max = null): float
{
if (null === $nbMaxDecimals) {
$nbMaxDecimals = $this->randomDigit();
Expand Down
11 changes: 10 additions & 1 deletion src/Faker/DefaultGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@

/**
* This generator returns a default value for all called properties
* and methods. It works with Faker\Generator\Base->optional().
* and methods.
*
* @mixin Generator
*
* @deprecated Use ChanceGenerator instead
*/
class DefaultGenerator
{
protected $default;

public function __construct($default = null)
{
trigger_deprecation('fakerphp/faker', '1.16', 'Class "%s" is deprecated, use "%s" instead.', __CLASS__, ChanceGenerator::class);

$this->default = $default;
}

public function ext()
{
return $this;
}

/**
* @param string $attribute
*
Expand Down
2 changes: 1 addition & 1 deletion src/Faker/Extension/NumberExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function randomDigitNotZero(): int;
*
* @example 48.8932
*/
public function randomFloat(?int $nbMaxDecimals, float $min, float $max): float;
public function randomFloat(?int $nbMaxDecimals, float $min, ?float $max): float;

/**
* Returns a random integer with 0 to $nbDigits digits.
Expand Down
92 changes: 78 additions & 14 deletions src/Faker/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Faker;

use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;

/**
Expand Down Expand Up @@ -122,18 +121,6 @@
*
* @method string toUpper($string = '')
*
* @property mixed $optional
*
* @method mixed optional($weight = null, $default = null)
*
* @property UniqueGenerator $unique
*
* @method UniqueGenerator unique($reset = false, $maxRetries = 10000)
*
* @property ValidGenerator $valid
*
* @method ValidGenerator valid($validator = null, $maxRetries = 10000)
*
* @property int $biasedNumberBetween
*
* @method int biasedNumberBetween($min = 0, $max = 100, $function = 'sqrt')
Expand Down Expand Up @@ -565,6 +552,11 @@ class Generator

private $container;

/**
* @var UniqueGenerator
*/
private $uniqueGenerator;

public function __construct(ContainerInterface $container = null)
{
$this->container = $container ?: Extension\ContainerBuilder::getDefault();
Expand All @@ -575,7 +567,6 @@ public function __construct(ContainerInterface $container = null)
*
* @param class-string<T> $id
*
* @throws ContainerExceptionInterface
* @throws Extension\ExtensionNotFound
*
* @return T
Expand Down Expand Up @@ -610,6 +601,79 @@ public function getProviders()
return $this->providers;
}

/**
* With the unique generator you are guaranteed to never get the same two
* values.
*
* <code>
* // will never return twice the same value
* $faker->unique()->randomElement(array(1, 2, 3));
* </code>
*
* @param bool $reset If set to true, resets the list of existing values
* @param int $maxRetries Maximum number of retries to find a unique value,
* After which an OverflowException is thrown.
*
* @throws \OverflowException When no unique value can be found by iterating $maxRetries times
*
* @return self A proxy class returning only non-existing values
*/
public function unique($reset = false, $maxRetries = 10000)
{
if ($reset || $this->uniqueGenerator === null) {
$this->uniqueGenerator = new UniqueGenerator($this, $maxRetries);
}

return $this->uniqueGenerator;
}

/**
* Get a value only some percentage of the time.
*
* @param float $weight A probability between 0 and 1, 0 means that we always get the default value.
*
* @return self
*/
public function optional(float $weight = 0.5, $default = null)
{
if ($weight > 1) {
trigger_deprecation('fakerphp/faker', '1.16', 'First argument ($weight) to method "optional()" must be between 0 and 1. You passed %f, we assume you meant %f.', $weight, $weight / 100);
$weight = $weight / 100;
}

return new ChanceGenerator($this, $weight, $default);
}

/**
* To make sure the value meet some criteria, pass a callable that verifies the
* output. If the validator fails, the generator will try again.
*
* The value validity is determined by a function passed as first argument.
*
* <code>
* $values = array();
* $evenValidator = function ($digit) {
* return $digit % 2 === 0;
* };
* for ($i=0; $i < 10; $i++) {
* $values []= $faker->valid($evenValidator)->randomDigit;
* }
* print_r($values); // [0, 4, 8, 4, 2, 6, 0, 8, 8, 6]
* </code>
*
* @param ?\Closure $validator A function returning true for valid values
* @param int $maxRetries Maximum number of retries to find a valid value,
* After which an OverflowException is thrown.
*
* @throws \OverflowException When no valid value can be found by iterating $maxRetries times
*
* @return self A proxy class returning only valid values
*/
public function valid(?\Closure $validator = null, int $maxRetries = 10000)
{
return new ValidGenerator($this, $validator, $maxRetries);
}

public function seed($seed = null)
{
if ($seed === null) {
Expand Down
21 changes: 12 additions & 9 deletions src/Faker/UniqueGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Faker;

use Faker\Extension\Extension;

/**
* Proxy for other generators that returns only unique values.
*
Expand All @@ -11,14 +13,7 @@
*/
class UniqueGenerator
{
/**
* @var Generator
*/
protected $generator;

/**
* @var int
*/
protected $maxRetries;

/**
Expand All @@ -34,12 +29,20 @@ class UniqueGenerator
protected $uniques = [];

/**
* @param int $maxRetries
* @param Extension|Generator $generator
* @param int $maxRetries
* @param array<string, array<string, null>> $uniques
*/
public function __construct(Generator $generator, $maxRetries = 10000)
public function __construct($generator, $maxRetries = 10000, &$uniques = [])
{
$this->generator = $generator;
$this->maxRetries = $maxRetries;
$this->uniques = &$uniques;
}

public function ext(string $id)
{
return new self($this->generator->ext($id), $this->maxRetries, $this->uniques);
}

/**
Expand Down
14 changes: 11 additions & 3 deletions src/Faker/ValidGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Faker;

use Faker\Extension\Extension;

/**
* Proxy for other generators, to return only valid values. Works with
* Faker\Generator\Base->valid()
Expand All @@ -15,10 +17,11 @@ class ValidGenerator
protected $maxRetries;

/**
* @param callable|null $validator
* @param int $maxRetries
* @param Extension|Generator $generator
* @param callable|null $validator
* @param int $maxRetries
*/
public function __construct(Generator $generator, $validator = null, $maxRetries = 10000)
public function __construct($generator, $validator = null, $maxRetries = 10000)
{
if (null === $validator) {
$validator = static function () {
Expand All @@ -32,6 +35,11 @@ public function __construct(Generator $generator, $validator = null, $maxRetries
$this->maxRetries = $maxRetries;
}

public function ext(string $id)
{
return new self($this->generator->ext($id), $this->validator, $this->maxRetries);
}

/**
* Catch and proxy all generator calls but return only valid values
*
Expand Down
3 changes: 3 additions & 0 deletions test/Faker/DefaultGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public function testGeneratorReturnsDefaultValueForAnyPropertyGet()
self::assertNotNull($generator->bar);
}

/**
* @group legacy
*/
public function testGeneratorReturnsDefaultValueForAnyMethodCall()
{
$generator = new DefaultGenerator(123);
Expand Down
Loading

0 comments on commit fa135b4

Please sign in to comment.