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

Adding support for extensions and PSR-11 #154

Merged
merged 29 commits into from
Dec 22, 2020
Merged

Conversation

Nyholm
Copy link
Member

@Nyholm Nyholm commented Dec 19, 2020

This is the first steps to move towards what is described in https://github.com/Nyholm/next-faker

All classes/interfaces are marked with "Experimental". This PR does not mean that it is a perfect solution, but it is a step towards it.

@Nyholm Nyholm marked this pull request as ready for review December 19, 2020 10:30
src/Faker/Container/ContainerException.php Outdated Show resolved Hide resolved
src/Faker/Container/NotInContainerException.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/ContainerBuilder.php Outdated Show resolved Hide resolved
return new Container($this->container);
}

public static function getDefault(): ContainerInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static function getDefault(): ContainerInterface
public static function create(): ContainerInterface

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be confused with "build".

I want to be clear that this builds the default container.. Maybe this should be part of Generator or Factory instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not even sure we need this class, to be honest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want the container to be immutable. Using a builder class is the simplest and easiest to understand.

The alternative would be that the container builds itself and it is immutable only after we run Container::build().

Copy link
Member Author

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review

src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
Comment on lines 40 to 80
try {
return $this->cache[$id] = new $data();
} catch (\Throwable $e) {
throw new ContainerException(sprintf('Could not instantiate class "%s"', $id), 0, $e);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit of this change?

@Nyholm
Copy link
Member Author

Nyholm commented Dec 19, 2020

Thank you for the review, The PR is updated and build is green.

I left you some questions

src/Faker/Excpetion/ExtensionNotFound.php Outdated Show resolved Hide resolved
src/Faker/Extension/GeneratorAwareExtension.php Outdated Show resolved Hide resolved
src/Faker/Generator.php Outdated Show resolved Hide resolved
test/Faker/Container/ContainerBuilderTest.php Outdated Show resolved Hide resolved
test/Faker/Container/ContainerTest.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/ContainerBuilder.php Outdated Show resolved Hide resolved

declare(strict_types=1);

namespace Faker\English;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the namespace, is an implementation for German going to be different?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this. I added it in this namespace to be constant with the German package.

The German package is called fakerphp/german, it lives in https://github.com/FakerPHP/German and its root namespace would be Faker\German.

So there will be a Faker\German\PersonDe and Faker\German\PersonAu..


However, The Australian English will not be in the root package. There should also be a fakerphp/english with Brittish, Australian... etc.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently have quite a few providers that are not necessarily language specific (besides this one), such as UserAgent or Barcode.
How about adding a dedicated package/namespace that provides those?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have considered that and decided not to.

It is impossible of us to know if there is a specific Russian barcode or a Chinese UserAgent. So I think the core should treat all extensions the same. But I agree with you, there are only some that are likely to be localised.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo English should not be in the core package as well? Lets just put it in its own package.

Meaning people will require then locale extension and not the core directly?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have considered that and decided not to.

It is impossible of us to know if there is a specific Russian barcode or a Chinese UserAgent. So I think the core should treat all extensions the same. But I agree with you, there are only some that are likely to be localised.

But arent we duplicating a lot this way? There are a lot of generic things so. Medical / Barcore / Number / Date etc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We are not duplicating.
Since the core package includes all English and “generic providers”, no language specific extensions need to duplicate anything.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm somewhat reminded of our conversation in Nyholm/next-faker#14.
These "generic providers" are in the fakerphp/english package or in the core? How do we differentiate between generic and "localizable"?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why even put english in the core package. I think it is a benefit to keep it separated and just require it. Reason for this is that it is more easy to setup new locale extensions from a proper example repo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo English should not be in the core package as well? Lets just put it in its own package.

If so, a third party library will require fakerphp/faker and get an unusable library because it does not have any extension. So the third partly library will then require fakerphp/faker and fakerphp/english and the potential "win" for separating them is lost and we only caused confusion.

But arent we duplicating a lot this way? There are a lot of generic things so. Medical / Barcore / Number / Date etc

No, we are not. The fakerphp/german does not have to include a Medical extension, it will just keep using the default ones.

These "generic providers" are in the fakerphp/english package or in the core?

They are in the code.

How do we differentiate between generic and "localizable"?

Just for context (so I understand), I have a counter question: Why would you need to differentiate between generic and "localizable"?

Reason for this is that it is more easy to setup new locale extensions from a proper example repo

Im not sure this is true. Feel free to open a new issue discussion the pros and cons. Consider that 90% of the users will only use the english locale and also see the discussions on https://github.com/Nyholm/next-faker


I would also like to remind you about my PR description:

This PR does not mean that it is a perfect solution, but it is a step towards it.

I dont mean to turn down this discussion, but it is quite far from the purpose of this PR.

src/Faker/Generator.php Outdated Show resolved Hide resolved
src/Faker/Generator.php Show resolved Hide resolved
@Nyholm Nyholm force-pushed the psr-11 branch 2 times, most recently from cb5e45e to 06c25bb Compare December 19, 2020 13:36
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
src/Faker/Container/Container.php Outdated Show resolved Hide resolved
}

try {
return $this->services[$id] = new $definition();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing code this way has the following downsides:

  • a reader does not know whether the intent is to compare or assign a value

    return $this->services[$id] == new $definition();
  • it does not separate concerns

    • trying to create a value and catching an exception that can be potentially thrown
    • assigning a value
    • returning a value
  • it also hides whether we have tested both failure and success cases, this line of code will be covered even when we have only a test case for one of failure or success case

src/Faker/Container/ContainerBuilder.php Outdated Show resolved Hide resolved
test/Faker/Container/ContainerTest.php Outdated Show resolved Hide resolved
test/Faker/Container/ContainerTest.php Outdated Show resolved Hide resolved
test/Faker/GeneratorTest.php Outdated Show resolved Hide resolved
test/Faker/Container/ContainerTest.php Outdated Show resolved Hide resolved
test/Faker/GeneratorTest.php Outdated Show resolved Hide resolved
@localheinz
Copy link
Member

@Nyholm

Probably best to rebase on top of `main!

public function has($id)
{
if (!is_string($id)) {
throw new \LogicException(sprintf('First argument of %s::get() must be string', self::class));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new \LogicException(sprintf('First argument of %s::get() must be string', self::class));
throw new \InvalidArgumentException(sprintf('First argument of %s::get() must be string', self::class));

Comment on lines 7 to 8
use Faker\Core\File;
use Faker\Extension\FileExtension;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nyholm

Hm..

Faker\Extension\File is an interface. Faker\English\FileExtension is the implementation of that interface.

I could have named it Faker\English\File but that would be more confusing.

It is only confusing when we always import every single class:

diff --git a/src/Faker/Container/ContainerBuilder.php b/src/Faker/Container/ContainerBuilder.php
index b86992b9..109f03ea 100644
--- a/src/Faker/Container/ContainerBuilder.php
+++ b/src/Faker/Container/ContainerBuilder.php
@@ -4,8 +4,8 @@ declare(strict_types=1);

 namespace Faker\Container;

-use Faker\Core\File;
-use Faker\Extension\FileExtension;
+use Faker\Core;
+use Faker\Extension;
 use Psr\Container\ContainerInterface;

 /**
@@ -61,7 +61,7 @@ final class ContainerBuilder
     public static function defaultExtensions(): array
     {
         return [
-            FileExtension::class => File::class
+            Extension\File::class => Core\File::class
         ];
     }

@localheinz localheinz assigned Nyholm and unassigned localheinz Dec 21, 2020
@Nyholm Nyholm marked this pull request as ready for review December 21, 2020 19:27
@Nyholm
Copy link
Member Author

Nyholm commented Dec 21, 2020

Thank you for all the reviews, suggestions and opinions. I think this PR is way better now than a few days ago.

@pimjansen
Copy link

@localheinz im happy to take this in as is, what is your view? We can always change from there. Like @Nyholm said it is not a 100% perfect solution yet but the foundation of the future.

Copy link
Member

@localheinz localheinz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

We can still reiterate when necessary!

@pimjansen pimjansen merged commit 9cca954 into FakerPHP:main Dec 22, 2020
@Nyholm
Copy link
Member Author

Nyholm commented Dec 22, 2020

Wohoo. Thank you for merging

@Nyholm Nyholm deleted the psr-11 branch December 22, 2020 09:32
krsriq pushed a commit to krsriq/Faker that referenced this pull request Jan 7, 2021
* Adding support for extensions and PSR-11

* Added getDefaultExtensions

* minor

* Apply suggestions from code review

Co-authored-by: Andreas Möller <am@localheinz.com>

* CS fixes

* Refactor container

* CS

* Renamed namespace

* Updated names and namespaces

* Fix: Run 'make cs'

* Fix: Remove unnecessary initialization

* Fix: Add missing throws annotation

* Fix: Run 'make baseline'

* Fix: Wrapping

* Fix: Throw InvalidArgumentException

* Fix: Run 'make cs'

* Fix: Use elvis operator

* Fix: Move exception to Extension namespace

* Fix: Add return type declarations

* Fix: Avoid unnecessary imports

* Fix: Whitespace

* Fix: Add covers annotation

* Enhancement: Throw RuntimeException when service can not be resolved to extension

* Fix: DocBlock

* Fix: Run 'make cs'

* Fix: Namespace

* Enhancement: Merge Container into Extension namespace

* Added dock block

* cs

Co-authored-by: Andreas Möller <am@localheinz.com>
@localheinz localheinz mentioned this pull request Sep 5, 2023
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants