Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Modifiers] Helper methods to use our custom generators #155

Merged
merged 16 commits into from Jan 19, 2022
24 changes: 24 additions & 0 deletions phpstan-baseline.neon
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
@@ -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
@@ -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
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
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
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
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
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
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
Expand Up @@ -25,6 +25,9 @@ public function testGeneratorReturnsDefaultValueForAnyPropertyGet()
self::assertNotNull($generator->bar);
}

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