├── .github └── workflows │ ├── packagist-deploy.yml │ └── tests.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── composer.json ├── docker-compose.yml ├── phpunit.xml ├── src ├── Calculators │ ├── Iban.php │ └── Luhn.php ├── Container │ ├── Container.php │ └── Traits │ │ ├── HasExtensions.php │ │ ├── HasLocale.php │ │ ├── HasModifiers.php │ │ └── HasStrategies.php ├── Exceptions │ ├── BadParameterException.php │ ├── MaximumTriesReached.php │ └── NoExtensionLocaleFound.php ├── Extensions │ ├── ArrayExtension.php │ ├── BooleanExtension.php │ ├── ColorsExtension.php │ ├── DateTimeExtension.php │ ├── Extension.php │ ├── FinancialExtension.php │ ├── HashExtension.php │ ├── InternetExtension.php │ ├── NumbersExtension.php │ ├── PersonExtension.php │ ├── StringsExtension.php │ ├── TextExtension.php │ └── Traits │ │ └── HasLocale.php ├── Faker.php ├── FakerServiceProvider.php ├── Manifests │ ├── ContainerMixinManifest.php │ └── PackageManifest.php ├── Modifiers │ ├── LowercaseModifier.php │ ├── Modifier.php │ ├── NullableModifier.php │ └── UppercaseModifier.php ├── Providers │ ├── Provider.php │ └── ProviderRepository.php ├── Seeds │ └── HasSeeds.php └── Strategies │ ├── RegexStrategy.php │ ├── Strategy.php │ └── UniqueStrategy.php └── tests ├── Support ├── Extensions │ ├── EnEnExtensionTest.php │ ├── EnUsExtensionTest.php │ ├── FrFrExtensionTest.php │ ├── MixinTestExtension.php │ ├── NullLocaleExtensionTest.php │ ├── NumberTestExtension.php │ └── StringTestExtension.php ├── TestServiceProvider.php └── vendor │ └── composer │ └── installed.json └── Unit ├── Calculators ├── IbanTest.php └── LuhnTest.php ├── ContainerMixinManifestTest.php ├── ContainerTest.php ├── ExtensionTest.php ├── Extensions ├── ArraysExtensionTest.php ├── BooleanExtensionTest.php ├── ColorsExtensionTest.php ├── DatetimeExtensionTest.php ├── FinancialExtensionTest.php ├── HashExtensionTest.php ├── InternetExtensionTest.php ├── NumbersExtensionTest.php ├── PersonExtensionTest.php ├── StringsExtensionTest.php ├── TestCase.php └── TextExtensionTest.php ├── LocaleTest.php ├── Modifiers ├── LowercaseModifierTest.php ├── OptionalModifierTest.php └── UppercaseModifierTest.php ├── PackageManifestTest.php ├── ProviderRepositoryTest.php ├── Strategies ├── RegexStrategyTest.php └── UniqueStrategyTest.php └── TestCase.php /.github/workflows/packagist-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Packagist Deploy 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: mnavarrocarter/packagist-update@v1.0.0 18 | with: 19 | username: "GautierDele" 20 | api_token: ${{ secrets.PACKAGIST_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | php-version: [ '8.3', '8.4' ] 18 | 19 | name: Tests on PHP ${{ matrix.php-version }} 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: Setup PHP 26 | uses: shivammathur/setup-php@v2 27 | with: 28 | php-version: ${{ matrix.php-version }} 29 | coverage: none 30 | 31 | - name: Validate composer.json and composer.lock 32 | run: composer validate 33 | 34 | - name: Cache Composer packages 35 | id: composer-cache 36 | uses: actions/cache@v4 37 | with: 38 | path: vendor 39 | key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-php-${{ matrix.php-version }}- 42 | 43 | - name: Install dependencies 44 | if: steps.composer-cache.outputs.cache-hit != 'true' 45 | run: composer i --prefer-dist --no-progress 46 | 47 | - name: Run test suite 48 | run: vendor/bin/phpunit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .phpunit* 4 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:latest 2 | 3 | RUN apt update 4 | RUN apt install unzip curl -y 5 | 6 | RUN curl -sS https://getcomposer.org/installer -o /usr/local/composer-setup.php 7 | 8 | RUN php /usr/local/composer-setup.php --install-dir=/usr/local/bin --filename=composer 9 | 10 | RUN rm /usr/local/composer-setup.php 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Social Card of Faker PHP

2 | 3 | # Faker PHP 4 | 5 | Faker PHP allows you to easily generate mock data for your tests, providing a quick and flexible way to simulate realistic data effortlessly—perfect for creating robust development and testing environments. 6 | 7 | ## Requirements 8 | 9 | PHP 8.3+ 10 | 11 | ## Documentation, Installation, and Usage Instructions 12 | 13 | See the [documentation](https://faker-php.xefi.com) for detailed installation and usage instructions. 14 | 15 | ## What It Does 16 | 17 | You'll be able to generate fake data for your applications, making your test environment more flexible. 18 | 19 | Here is a quick look at what you can do: 20 | 21 | ```php 22 | $faker = new Xefi\Faker\Faker(); 23 | 24 | $faker->name() // John Doe 25 | $faker->name() // Charles Brown 26 | 27 | $faker->sentence() // equus canis populus servus aquaeductus fidelitas 28 | 29 | $faker->iban() // PX41711762752955497163783543 30 | ``` 31 | 32 | ## Support us 33 | 34 |

35 | 36 | Since 1997, XEFI is a leader in IT performance support for small and medium-sized businesses through its nearly 200 local agencies based in France, Belgium, Switzerland and Spain. 37 | A one-stop shop for IT, office automation, software, [digitalization](https://www.xefi.com/solutions-software/), print and cloud needs. 38 | [Want to work with us ?](https://carriere.xefi.fr/metiers-software) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xefi/faker-php", 3 | "type": "library", 4 | "description": "Faker allows you to generate realistic fake data for your php applications", 5 | "keywords": [ 6 | "faker", 7 | "data", 8 | "seeding", 9 | "fixtures" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Gautier Deleglise" 15 | }, 16 | { 17 | "name": "Martin Soenen" 18 | } 19 | ], 20 | "require": { 21 | "php": "^8.3", 22 | "phpdocumentor/reflection-docblock": "^5.6", 23 | "psr/container": "^2.0" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^11" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Xefi\\Faker\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Xefi\\Faker\\Tests\\": "tests/" 36 | } 37 | }, 38 | "config": { 39 | "sort-packages": true 40 | }, 41 | "extra": { 42 | "faker": { 43 | "providers": [ 44 | "Xefi\\Faker\\FakerServiceProvider" 45 | ] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | php: 3 | build: . 4 | image: php:latest 5 | volumes: 6 | - ./:/var/www/html 7 | working_dir: /var/www/html 8 | tty: true -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests/Unit 13 | 14 | 15 | 16 | 17 | src 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Calculators/Iban.php: -------------------------------------------------------------------------------- 1 | = 0; $i--) { 20 | $factor = $parity ? 2 : 1; 21 | $parity = $parity ? 0 : 1; 22 | $sum += array_sum(str_split($number[$i] * $factor)); 23 | } 24 | 25 | return $sum; 26 | } 27 | 28 | /** 29 | * Compute check digit. 30 | * 31 | * @param int $number Number to compute. 32 | * 33 | * @return int 34 | */ 35 | public static function checksum(int $number): int 36 | { 37 | return (self::algorithm($number) * 9) % 10; 38 | } 39 | 40 | /** 41 | * Validate number containing check digit. 42 | * 43 | * @param int $number Number to validate. 44 | * 45 | * @return bool 46 | */ 47 | public static function isValid(int $number): bool 48 | { 49 | return (self::algorithm($number.'0') % 10) === 0; 50 | } 51 | 52 | /** 53 | * Add check digit to number. 54 | * 55 | * @param int $number Number to checksum. 56 | * @param bool $soft Do not add check digit if number already validates. 57 | * 58 | * @return int 59 | */ 60 | public static function create(int $number, bool $soft = false): int 61 | { 62 | return !self::isValid($number) || !$soft 63 | ? $number.self::checksum($number) 64 | : $number; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Container/Container.php: -------------------------------------------------------------------------------- 1 | areExtensionsInitialized()) { 58 | $this->registerConfiguredProviders(); 59 | 60 | $this->bootstrap(); 61 | } 62 | 63 | if ($shouldBuildContainerMixin) { 64 | $this->buildContainerMixinManifest(); 65 | } 66 | } 67 | 68 | /** 69 | * Set the package manifest path. 70 | * 71 | * @param string $manifestPath 72 | * 73 | * @return void 74 | */ 75 | public static function packageManifestPath(string $manifestPath) 76 | { 77 | static::$packageManifestPath = $manifestPath; 78 | } 79 | 80 | /** 81 | * Set the container mixin manifest path. 82 | * 83 | * @param string $manifestPath 84 | * 85 | * @return void 86 | */ 87 | public static function containerMixinManifestPath(string $containerMixinManifestPath) 88 | { 89 | static::$containerMixinManifestPath = $containerMixinManifestPath; 90 | } 91 | 92 | /** 93 | * Set the base path. 94 | * 95 | * @param string $basePath 96 | * 97 | * @return void 98 | */ 99 | public static function basePath(string $basePath) 100 | { 101 | static::$basePath = $basePath; 102 | } 103 | 104 | /** 105 | * Build container mixin manifest. 106 | */ 107 | protected function buildContainerMixinManifest() 108 | { 109 | $containerMixinManifest = new ContainerMixinManifest(static::$basePath, static::$containerMixinManifestPath); 110 | $containerMixinManifest->buildIfShould($this->getExtensionMethods(), $this->getExtensions()); 111 | } 112 | 113 | /** 114 | * Register all of the configured providers. 115 | * 116 | * @return void 117 | */ 118 | protected function registerConfiguredProviders() 119 | { 120 | $packageManifest = new PackageManifest(static::$basePath, static::$packageManifestPath); 121 | 122 | $providers = $packageManifest->providers(); 123 | 124 | $collapsedProviders = []; 125 | foreach ($providers as $values) { 126 | $collapsedProviders[] = $values; 127 | } 128 | 129 | (new ProviderRepository()) 130 | ->load(array_merge([], ...$collapsedProviders)); 131 | } 132 | 133 | /** 134 | * Register a console "starting" bootstrapper. 135 | * 136 | * @param Closure $callback 137 | * 138 | * @return void 139 | */ 140 | public static function starting(Closure $callback): void 141 | { 142 | static::$bootstrappers[] = $callback; 143 | } 144 | 145 | /** 146 | * Bootstrap the console application. 147 | * 148 | * @return void 149 | */ 150 | protected function bootstrap() 151 | { 152 | foreach (static::$bootstrappers as $bootstrapper) { 153 | $bootstrapper($this); 154 | } 155 | } 156 | 157 | /** 158 | * Reset the bootstrappers. 159 | * 160 | * @return void 161 | */ 162 | public function forgetBootstrappers(): void 163 | { 164 | static::$bootstrappers = []; 165 | } 166 | 167 | /** 168 | * @param $method 169 | * 170 | * @return mixed 171 | */ 172 | public function run($method, $parameters) 173 | { 174 | $tries = 0; 175 | do { 176 | $generatedValue = $this->callExtensionMethod($method, $parameters); 177 | 178 | // Apply modifiers 179 | $generatedValue = $this->applyModifiers($generatedValue); 180 | 181 | if (++$tries > 20000) { 182 | throw new MaximumTriesReached(sprintf('Maximum tries of %d reached without finding a value', 20000)); 183 | } 184 | } while (!$this->passStrategies($generatedValue)); 185 | 186 | // Here we assume the container has done his job and reset the strategies 187 | // in case the user wants to run the method again on another extension 188 | $this->forgetStrategies(); 189 | 190 | return $generatedValue; 191 | } 192 | 193 | /** 194 | * Dynamically call the extension. 195 | * 196 | * @param string $method 197 | * @param array $parameters 198 | * 199 | * @return mixed 200 | */ 201 | public function __call($method, $parameters) 202 | { 203 | return $this->run($method, $parameters); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/Container/Traits/HasExtensions.php: -------------------------------------------------------------------------------- 1 | resolve($extension); 37 | } 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * Add an extension, resolving through the application. 44 | * 45 | * @param Extension|string $extension 46 | * 47 | * @return Container 48 | */ 49 | protected function resolve(\Xefi\Faker\Extensions\Extension|string $extension): Container 50 | { 51 | $instance = $extension instanceof Extension ? $extension : new $extension(new Randomizer()); 52 | 53 | // If the extension supports locale variations 54 | if (method_exists($instance, 'getLocale')) { 55 | return $this->addLocaleExtension($instance); 56 | } 57 | 58 | return $this->addExtension($instance); 59 | } 60 | 61 | /** 62 | * Add an extension to the container. 63 | * 64 | * @param \Xefi\Faker\Extensions\Extension $extension 65 | * 66 | * @return \Xefi\Faker\Extensions\Extension 67 | */ 68 | protected function addExtension(Extension $extension): Container 69 | { 70 | if (isset(static::$extensions[$extension->getName()])) { 71 | trigger_error(sprintf('[XEFI FAKER] The \'%s\' extension is already registered', $extension->getName()), E_USER_WARNING); 72 | } 73 | 74 | static::$extensions[$extension->getName()] = $extension; 75 | 76 | // Here we register all the extensions methods in order to have a quick access after 77 | foreach (get_class_methods($extension) as $method) { 78 | // If the method is common 79 | if (in_array($method, ['getName', '__construct'])) { 80 | continue; 81 | } 82 | 83 | if (isset(static::$extensionsMethods[$method])) { 84 | trigger_error(sprintf('[XEFI FAKER] The \'%s\' method from \'%s\' is already registered', $method, $extension->getName()), E_USER_WARNING); 85 | } 86 | 87 | static::$extensionsMethods[$method] = $extension->getName(); 88 | } 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Add an extension to the container. 95 | * 96 | * @param \Xefi\Faker\Extensions\Extension $extension 97 | * 98 | * @return \Xefi\Faker\Extensions\Extension 99 | */ 100 | protected function addLocaleExtension(Extension $extension): Container 101 | { 102 | if (!isset(static::$extensions[$extension->getName()])) { 103 | static::$extensions[$extension->getName()] = [ 104 | 'locales' => [], 105 | ]; 106 | } 107 | 108 | if (isset(static::$extensions[$extension->getName()]['locales'][$extension->getLocale()])) { 109 | trigger_error(sprintf('[XEFI FAKER] The \'%s\' extension in locale \'%s\' is already registered', $extension->getName(), $extension->getLocale()), E_USER_WARNING); 110 | } 111 | 112 | static::$extensions[$extension->getName()]['locales'][$extension->getLocale()] = $extension; 113 | 114 | // Here we register all the extensions methods in order to have a quick access after 115 | foreach (get_class_methods($extension) as $method) { 116 | // If the method is common 117 | if (in_array($method, ['getName', '__construct'])) { 118 | continue; 119 | } 120 | 121 | // The method for another locale has been set 122 | if (isset(static::$extensionsMethods[$method])) { 123 | continue; 124 | } 125 | 126 | static::$extensionsMethods[$method] = $extension->getName(); 127 | } 128 | 129 | return $this; 130 | } 131 | 132 | /** 133 | * Get the container extensions. 134 | * 135 | * @return array 136 | */ 137 | public function getExtensions(): array 138 | { 139 | return static::$extensions; 140 | } 141 | 142 | /** 143 | * Get the container extensions methods. 144 | * 145 | * @return array 146 | */ 147 | public function getExtensionMethods(): array 148 | { 149 | return static::$extensionsMethods; 150 | } 151 | 152 | /** 153 | * See if the extensions has already been set. 154 | * 155 | * @return bool 156 | */ 157 | public function areExtensionsInitialized(): bool 158 | { 159 | return !empty(static::$extensions); 160 | } 161 | 162 | /** 163 | * Reset the container extensions. 164 | * 165 | * @return void 166 | */ 167 | public function forgetExtensions(): void 168 | { 169 | static::$extensions = []; 170 | static::$extensionsMethods = []; 171 | } 172 | 173 | /** 174 | * Resolve the method called to extensions. 175 | * 176 | * @param string $method 177 | * @param array $parameters 178 | * 179 | * @return mixed 180 | */ 181 | public function callExtensionMethod(string $method, array $parameters = []) 182 | { 183 | if (!isset(static::$extensionsMethods[$method])) { 184 | throw new \BadMethodCallException(sprintf('The %s method does not exist', $method)); 185 | } 186 | 187 | $extension = static::$extensions[static::$extensionsMethods[$method]]; 188 | 189 | // We assume we here have multiple extensions declined by locale, we will try 190 | // to get the extension with the current locale, defaulting to first element 191 | if (is_array($extension) && isset($extension['locales'])) { 192 | if (!isset($extension['locales'][$this->getLocale()]) && !isset($extension['locales'][null])) { 193 | throw new NoExtensionLocaleFound(sprintf('Locale \'%s\' and \'null\' for method \'%s\' was not found', $this->getLocale(), $method)); 194 | } 195 | $extension = $extension['locales'][$this->getLocale()] ?? $extension['locales'][null]; 196 | } 197 | 198 | return $extension->$method(...$parameters); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/Container/Traits/HasLocale.php: -------------------------------------------------------------------------------- 1 | locale ?? null; 22 | } 23 | 24 | /** 25 | * Change the locale. 26 | * 27 | * @param ?string $locale 28 | * 29 | * @return self 30 | */ 31 | public function locale(?string $locale): self 32 | { 33 | $this->locale = $locale; 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Container/Traits/HasModifiers.php: -------------------------------------------------------------------------------- 1 | modifiers[] = new NullableModifier(new Randomizer(), $weight); 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * Add a uppercase modifier. 36 | ** 37 | * @return $this 38 | */ 39 | public function uppercase(): self 40 | { 41 | $this->modifiers[] = new UppercaseModifier(); 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * Add a lowercase modifier. 48 | ** 49 | * @return $this 50 | */ 51 | public function lowercase(): self 52 | { 53 | $this->modifiers[] = new LowercaseModifier(); 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * Determine if a generated value passes the strategies. 60 | * 61 | * @param $generatedValue 62 | * 63 | * @return mixed 64 | */ 65 | public function applyModifiers($generatedValue): mixed 66 | { 67 | foreach ($this->getModifiers() as $modifier) { 68 | $generatedValue = $modifier->apply($generatedValue); 69 | } 70 | 71 | return $generatedValue; 72 | } 73 | 74 | /** 75 | * Reset the registered modifiers. 76 | * 77 | * @return void 78 | */ 79 | public function forgetModifiers(): void 80 | { 81 | $this->modifiers = []; 82 | } 83 | 84 | /** 85 | * Returns the current modifiers for the container. 86 | * 87 | * @return array 88 | */ 89 | public function getModifiers(): array 90 | { 91 | return $this->modifiers; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Container/Traits/HasStrategies.php: -------------------------------------------------------------------------------- 1 | strategies[] = UniqueStrategy::forSeed($seed); 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Add a regex strategy. 34 | * 35 | * @param string $regex 36 | * 37 | * @return $this 38 | */ 39 | public function regex(string $regex): self 40 | { 41 | $this->strategies[] = new RegexStrategy($regex); 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * Determine if a generated value passes the strategies. 48 | * 49 | * @param $generatedValue 50 | * 51 | * @return bool 52 | */ 53 | public function passStrategies($generatedValue): bool 54 | { 55 | foreach ($this->getStrategies() as $strategy) { 56 | if (!$strategy->pass($generatedValue)) { 57 | return false; 58 | } 59 | } 60 | 61 | return true; 62 | } 63 | 64 | /** 65 | * Reset the registered strategies. 66 | * 67 | * @return void 68 | */ 69 | public function forgetStrategies(): void 70 | { 71 | $this->strategies = []; 72 | } 73 | 74 | /** 75 | * Returns the current strategies for the container. 76 | * 77 | * @return array 78 | */ 79 | public function getStrategies(): array 80 | { 81 | return $this->strategies; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Exceptions/BadParameterException.php: -------------------------------------------------------------------------------- 1 | pickArrayRandomElement($array); 14 | } 15 | 16 | public function randomKey(array $array): mixed 17 | { 18 | return $this->pickArrayRandomKey($array); 19 | } 20 | 21 | public function randomKeyNumber(array $array): mixed 22 | { 23 | return $this->pickArrayRandomKeyNumber($array); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Extensions/BooleanExtension.php: -------------------------------------------------------------------------------- 1 | 100) { 15 | throw new BadParameterException('Percentage must be between 0 and 100'); 16 | } 17 | 18 | $randomValue = $this->randomizer->getInt(1, 100); 19 | 20 | return $randomValue <= $percentage; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Extensions/ColorsExtension.php: -------------------------------------------------------------------------------- 1 | pickArrayRandomElement($this->safeColorNames); 34 | } 35 | 36 | public function colorName(): string 37 | { 38 | return $this->pickArrayRandomElement($this->colorNames); 39 | } 40 | 41 | public function safeHexColor(): string 42 | { 43 | $rand = ''; 44 | for ($i = 0; $i < 3; $i++) { 45 | $rand .= str_repeat(dechex($this->randomizer->getInt(0, 15)), 2); 46 | } 47 | 48 | return '#'.$rand; 49 | } 50 | 51 | public function hexColor(): string 52 | { 53 | return '#'.bin2hex($this->randomizer->getBytes(3)); 54 | } 55 | 56 | public function rgbColorAsArray(): array 57 | { 58 | $colors = []; 59 | for ($i = 0; $i < 3; $i++) { 60 | $colors[] = $this->randomizer->getInt(0, 255); 61 | } 62 | 63 | return $colors; 64 | } 65 | 66 | public function rgbColor(): string 67 | { 68 | return implode(',', $this->rgbColorAsArray()); 69 | } 70 | 71 | public function rgbCssColor(): string 72 | { 73 | return 'rgb('.$this->rgbColor().')'; 74 | } 75 | 76 | public function rgbaColorAsArray(): array 77 | { 78 | $colors = []; 79 | for ($i = 0; $i < 3; $i++) { 80 | $colors[] = $this->randomizer->getInt(0, 255); 81 | } 82 | 83 | $colors[] = round($this->randomizer->getFloat(0, 1), 2); 84 | 85 | return $colors; 86 | } 87 | 88 | public function rgbaColor(): string 89 | { 90 | return implode(',', $this->rgbaColorAsArray()); 91 | } 92 | 93 | public function rgbaCssColor(): string 94 | { 95 | return 'rgba('.$this->rgbaColor().')'; 96 | } 97 | 98 | public function hslColorAsArray(): array 99 | { 100 | $colors = []; 101 | 102 | $colors[] = $this->randomizer->getInt(0, 360); // Hue 103 | $colors[] = $this->randomizer->getInt(0, 100); // Saturation 104 | $colors[] = $this->randomizer->getInt(0, 100); // Lightness 105 | 106 | return $colors; 107 | } 108 | 109 | public function hslColor(): string 110 | { 111 | return implode(',', $this->hslColorAsArray()); 112 | } 113 | 114 | public function hslCssColor(): string 115 | { 116 | return 'hsl('.$this->hslColor().')'; 117 | } 118 | 119 | public function hslaColorAsArray(): array 120 | { 121 | $colors = []; 122 | 123 | $colors[] = $this->randomizer->getInt(0, 360); // Hue 124 | $colors[] = $this->randomizer->getInt(0, 100); // Saturation 125 | $colors[] = $this->randomizer->getInt(0, 100); // Lightness 126 | $colors[] = round($this->randomizer->getFloat(0, 1), 2); // Alpha 127 | 128 | return $colors; 129 | } 130 | 131 | public function hslaColor(): string 132 | { 133 | return implode(',', $this->hslaColorAsArray()); 134 | } 135 | 136 | public function hslaCssColor(): string 137 | { 138 | return 'hsla('.$this->hslaColor().')'; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Extensions/DateTimeExtension.php: -------------------------------------------------------------------------------- 1 | getTimestamp(); 17 | } 18 | 19 | return strtotime($timestamp); 20 | } 21 | 22 | public function dateTime(DateTime|int|string $fromTimestamp = '-30 years', DateTime|int|string $toTimestamp = 'now'): DateTime 23 | { 24 | return new DateTime('@'.$this->timestamp($fromTimestamp, $toTimestamp)); 25 | } 26 | 27 | public function timestamp(DateTime|int|string $fromTimestamp = '-30 years', DateTime|int|string $toTimestamp = 'now'): int 28 | { 29 | return $this->randomizer->getInt($this->formatTimestamp($fromTimestamp), $this->formatTimestamp($toTimestamp)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Extensions/Extension.php: -------------------------------------------------------------------------------- 1 | randomizer = $randomizer; 15 | } 16 | 17 | /** 18 | * Returns the extension name. 19 | * 20 | * @return string 21 | */ 22 | public function getName(): string 23 | { 24 | return 25 | // Here we convert the class name to kebab case 26 | strtolower( 27 | preg_replace( 28 | '/([a-z])([A-Z])/', 29 | '$1-$2', 30 | ( 31 | new ReflectionClass($this))->getShortName() 32 | ) 33 | ); 34 | } 35 | 36 | /** 37 | * Return a given number of random elements from the given array. 38 | * 39 | * @param array $array 40 | * @param int $elements 41 | * 42 | * @return array 43 | */ 44 | protected function pickArrayRandomElements(array $array, int $elements = 1): array 45 | { 46 | $keys = $this->randomizer->pickArrayKeys($array, $elements); 47 | 48 | return array_intersect_key($array, array_flip($keys)); 49 | } 50 | 51 | /** 52 | * Return a random element from the given array. 53 | * 54 | * @param array $array 55 | * 56 | * @return mixed 57 | */ 58 | protected function pickArrayRandomElement(array $array): mixed 59 | { 60 | $elements = $this->pickArrayRandomElements($array); 61 | 62 | return reset($elements); 63 | } 64 | 65 | protected function pickArrayRandomKeys(array $array, int $elements = 1): array 66 | { 67 | return $this->randomizer->pickArrayKeys($array, $elements); 68 | } 69 | 70 | protected function pickArrayRandomKeyNumber(array $array): mixed 71 | { 72 | return $this->pickArrayRandomKeys($array)[0]; 73 | } 74 | 75 | protected function pickArrayRandomKey(array $array): mixed 76 | { 77 | return key($array[$this->pickArrayRandomKeys($array)[0]]); 78 | } 79 | 80 | protected function formatString(string $string): string 81 | { 82 | while (($pos = strpos($string, '{a}')) !== false) { 83 | $string = substr_replace($string, $this->pickArrayRandomElement(['{d}', '{l}']), $pos, 3); 84 | } 85 | 86 | while (($pos = strpos($string, '{d}')) !== false) { 87 | $string = substr_replace($string, (string) $this->randomizer->getInt(0, 9), $pos, 3); 88 | } 89 | 90 | while (($pos = strpos($string, '{l}')) !== false) { 91 | $string = substr_replace($string, $this->randomizer->getBytesFromString(implode(range('A', 'Z')), 1), $pos, 3); 92 | } 93 | 94 | return $string; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Extensions/FinancialExtension.php: -------------------------------------------------------------------------------- 1 | digit, {l} => letter, {a} => any] 15 | * 16 | * @return string 17 | */ 18 | public function iban(?string $countryCode = null, ?string $format = null): string 19 | { 20 | if ($format === null) { 21 | $format = str_repeat('{d}', 24); 22 | } 23 | 24 | if ($countryCode === null) { 25 | $countryCode = $this->randomizer->getBytesFromString(implode(range('A', 'Z')), 2); 26 | } 27 | 28 | $format = $this->formatString($format); 29 | 30 | $checksum = Iban::checksum($countryCode.'00'.$format); 31 | 32 | return $countryCode.$checksum.$format; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Extensions/HashExtension.php: -------------------------------------------------------------------------------- 1 | randomizer->getBytes(16)); 10 | } 11 | 12 | public function md5(): string 13 | { 14 | return $this->hash('md5'); 15 | } 16 | 17 | public function sha1(): string 18 | { 19 | return $this->hash('sha1'); 20 | } 21 | 22 | public function sha256(): string 23 | { 24 | return $this->hash('sha256'); 25 | } 26 | 27 | public function sha512(): string 28 | { 29 | return $this->hash('sha512'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Extensions/InternetExtension.php: -------------------------------------------------------------------------------- 1 | randomizer->getBytesFromString( 12 | implode(range('a', 'z')), 13 | $this->randomizer->getInt(5, 10) 14 | ); 15 | } 16 | 17 | public function tld(): string 18 | { 19 | return $this->pickArrayRandomElement($this->tld); 20 | } 21 | 22 | public function domain(): string 23 | { 24 | return sprintf('%s.%s', $this->sdl(), $this->tld()); 25 | } 26 | 27 | public function ipv4(): string 28 | { 29 | return long2ip($this->randomizer->getInt(0, PHP_INT_MAX)); 30 | } 31 | 32 | public function ipv6(): string 33 | { 34 | return inet_ntop($this->randomizer->getBytes(16)); 35 | } 36 | 37 | public function macAddress() 38 | { 39 | return implode(':', str_split(substr(md5($this->randomizer->getInt(0, 2147483647)), 0, 12), 2)); 40 | } 41 | 42 | public function email() 43 | { 44 | $letters = $this->randomizer->getBytesFromString( 45 | implode(range('a', 'z')), 46 | $this->randomizer->getInt(4, 30) 47 | ); 48 | 49 | return sprintf('%s@%s', $letters, $this->domain()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Extensions/NumbersExtension.php: -------------------------------------------------------------------------------- 1 | number(0, 9); 10 | } 11 | 12 | public function number(int $min, int $max): int 13 | { 14 | return $this->randomizer->getInt($min, $max); 15 | } 16 | 17 | public function float(float $min, float $max, int $decimals = 1): float 18 | { 19 | if ($min === $max) { 20 | return (float) number_format($min, $decimals, '.', ''); 21 | } 22 | 23 | $factor = pow(10, $decimals); 24 | $randomInt = $this->number((int) floor($min * $factor), (int) ceil($max * $factor)); 25 | 26 | return (float) number_format($randomInt / $factor, $decimals, '.', ''); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Extensions/PersonExtension.php: -------------------------------------------------------------------------------- 1 | firstName($gender), $this->lastName()); 72 | } 73 | 74 | public function firstName(?string $gender = null): string 75 | { 76 | if ($gender === static::GENDER_MALE) { 77 | return $this->pickArrayRandomElement($this->firstNameMale); 78 | } 79 | 80 | if ($gender === static::GENDER_FEMALE) { 81 | return $this->pickArrayRandomElement($this->firstNameFemale); 82 | } 83 | 84 | return $this->pickArrayRandomElement($this->randomizer->getInt(0, 1) === 0 ? $this->firstNameFemale : $this->firstNameMale); 85 | } 86 | 87 | public function lastName(): string 88 | { 89 | return $this->pickArrayRandomElement($this->lastName); 90 | } 91 | 92 | public function title(?string $gender = null) 93 | { 94 | if ($gender === static::GENDER_MALE) { 95 | return $this->pickArrayRandomElement($this->titleMale); 96 | } 97 | 98 | if ($gender === static::GENDER_FEMALE) { 99 | return $this->pickArrayRandomElement($this->titleFemale); 100 | } 101 | 102 | return $this->pickArrayRandomElement($this->randomizer->getInt(0, 1) === 0 ? $this->titleFemale : $this->titleMale); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Extensions/StringsExtension.php: -------------------------------------------------------------------------------- 1 | randomizer->getInt(97, 122)); 33 | } 34 | 35 | public function shuffle(array|string $needle): array|string 36 | { 37 | return $this->{'shuffle'.gettype($needle)}($needle); 38 | } 39 | 40 | public function shuffleString(string $needle): string 41 | { 42 | return $this->randomizer->shuffleBytes($needle); 43 | } 44 | 45 | public function shuffleArray(array $needle): array 46 | { 47 | return $this->randomizer->shuffleArray($needle); 48 | } 49 | 50 | public function convertCharacters(string $string): string 51 | { 52 | $patterns = [ 53 | '#' => fn () => $this->randomizer->getInt(0, 9), 54 | '?' => fn () => $this->letter(), 55 | ]; 56 | 57 | // Here we dynamically define the wildcard to take another option 58 | $patterns['*'] = fn () => $this->pickArrayRandomElement($patterns)(); 59 | 60 | for ($i = 0; $i < strlen($string); $i++) { 61 | $string[$i] = isset($patterns[$string[$i]]) ? $patterns[$string[$i]]() : $string[$i]; 62 | } 63 | 64 | return $string; 65 | } 66 | 67 | public function semver(): string 68 | { 69 | return sprintf( 70 | '%d.%d.%d', 71 | $this->randomizer->getInt(0, 9), 72 | $this->randomizer->getInt(0, 99), 73 | $this->randomizer->getInt(0, 99) 74 | ); 75 | } 76 | 77 | public function emoji(): string 78 | { 79 | return $this->pickArrayRandomElement($this->emojis); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Extensions/TextExtension.php: -------------------------------------------------------------------------------- 1 | Paragraphs => Sentences => Words. 13 | * 14 | * @var array|array[] 15 | */ 16 | protected array $paragraphs = [ 17 | [ 18 | ['Lorem', 'ipsum', 'dolor', 'sit', 'amet,', 'consectetur', 'adipiscing', 'elit.'], 19 | ['Nullam', 'eu', 'nunc', 'non', 'mi', 'aliquet', 'varius.'], 20 | ['Curabitur', 'et', 'vestibulum', 'nulla.'], 21 | ['Donec', 'placerat', 'tempor', 'arcu,', 'in', 'viverra', 'sapien', 'laoreet', 'eu.'], 22 | ['Sed', 'vitae', 'ligula', 'eget', 'mauris', 'malesuada', 'pretium', 'in', 'at', 'lorem.'], 23 | ['Integer', 'condimentum', 'urna', 'at', 'lacus', 'fermentum,', 'nec', 'sagittis', 'purus', 'venenatis.'], 24 | ], 25 | [ 26 | ['Nunc', 'at', 'ligula', 'id', 'nisl', 'varius', 'egestas.'], 27 | ['Suspendisse', 'eget', 'nulla', 'dapibus,', 'efficitur', 'purus', 'a,', 'congue', 'quam.'], 28 | ['Donec', 'sagittis', 'interdum', 'libero', 'non', 'ornare.'], 29 | ['Nam', 'non', 'massa', 'lacus.'], 30 | ['Etiam', 'fermentum', 'neque', 'ut', 'est', 'porttitor,', 'ut', 'tincidunt', 'risus', 'suscipit.'], 31 | ['Nam', 'id', 'nisi', 'eget', 'lorem', 'vehicula', 'eleifend.'], 32 | ], 33 | [ 34 | ['Quisque', 'accumsan', 'nisl', 'ut', 'quam', 'pretium,', 'eget', 'lacinia', 'arcu', 'lobortis.'], 35 | ['Nam', 'dapibus', 'justo', 'nec', 'nibh', 'dapibus,', 'ac', 'varius', 'velit', 'varius.'], 36 | ['Nulla', 'facilisi.'], 37 | ['Praesent', 'volutpat', 'suscipit', 'nibh,', 'eget', 'congue', 'ante', 'ornare', 'a.'], 38 | ['Nam', 'aliquet', 'risus', 'eget', 'leo', 'gravida', 'scelerisque.'], 39 | ], 40 | [ 41 | ['Aenean', 'accumsan', 'leo', 'at', 'odio', 'vestibulum,', 'non', 'fermentum', 'nisl', 'varius.'], 42 | ['Suspendisse', 'in', 'quam', 'sed', 'ligula', 'convallis', 'sodales.'], 43 | ['Mauris', 'consequat', 'risus', 'sit', 'amet', 'libero', 'iaculis,', 'quis', 'volutpat', 'eros', 'scelerisque.'], 44 | ['Pellentesque', 'habitants', 'morbi', 'tristique', 'senectus', 'et', 'netus', 'et', 'malesuada', 'fames', 'ac', 'turpis', 'egestas.'], 45 | ], 46 | [ 47 | ['Donec', 'ultricies', 'euismod', 'libero,', 'vel', 'scelerisque', 'enim', 'condimentum', 'ut.'], 48 | ['Fusce', 'varius', 'urna', 'ac', 'ipsum', 'ultricies,', 'vel', 'elementum', 'turpis', 'dictum.'], 49 | ['Proin', 'nec', 'ante', 'at', 'erat', 'pharetra', 'interdum.'], 50 | ['Etiam', 'nec', 'ligula', 'felis.'], 51 | ['Curabitur', 'sit', 'amet', 'varius', 'nisi,', 'in', 'sagittis', 'turpis.'], 52 | ['Sed', 'eget', 'ex', 'quis', 'risus', 'varius', 'pharetra', 'in', 'a', 'felis.'], 53 | ], 54 | ]; 55 | 56 | protected array $flattenedWords; 57 | 58 | protected array $flattenedSentences; 59 | 60 | protected function flattenedWords(): array 61 | { 62 | if (isset($this->flattenedWords)) { 63 | return $this->flattenedWords; 64 | } 65 | 66 | return array_merge(...$this->flattenedSentences()); 67 | } 68 | 69 | protected function flattenedSentences(): array 70 | { 71 | if (isset($this->flattenedSentences)) { 72 | return $this->flattenedSentences; 73 | } 74 | 75 | return array_merge(...$this->paragraphs); 76 | } 77 | 78 | public function wordsAsArray(int $words = 3): array 79 | { 80 | return $this->pickArrayRandomElements($this->flattenedWords(), $words); 81 | } 82 | 83 | public function words(int $words = 3): string 84 | { 85 | $words = $this->wordsAsArray($words); 86 | 87 | // Remove any uppercase / comma / dots 88 | return strtolower(preg_replace('/[.,]/', '', implode(' ', $words))); 89 | } 90 | 91 | public function sentencesAsArray(int $sentences = 3): array 92 | { 93 | return $this->pickArrayRandomElements($this->flattenedSentences(), $sentences); 94 | } 95 | 96 | public function sentences(int $sentences = 3): string 97 | { 98 | $sentences = $this->sentencesAsArray($sentences); 99 | $sentences = array_map(function ($sentence) { return implode(' ', $sentence); }, $sentences); 100 | 101 | return implode(' ', $sentences); 102 | } 103 | 104 | public function paragraphsAsArray(int $paragraphs = 3): array 105 | { 106 | return $this->pickArrayRandomElements($this->paragraphs, $paragraphs); 107 | } 108 | 109 | public function paragraphs(int $paragraphs = 3): string 110 | { 111 | $paragraphs = $this->paragraphsAsArray($paragraphs); 112 | $paragraphs = array_map(function ($sentences) { return implode(' ', array_merge(...$sentences)); }, $paragraphs); 113 | 114 | return implode(PHP_EOL, $paragraphs); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Extensions/Traits/HasLocale.php: -------------------------------------------------------------------------------- 1 | locale = $locale; 22 | } 23 | 24 | public function __call(string $method, array $parameters) 25 | { 26 | // We simply redirect calls to container to create a new container for each faker call 27 | return (new Container())->locale($this->locale)->{$method}(...$parameters); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/FakerServiceProvider.php: -------------------------------------------------------------------------------- 1 | extensions([ 23 | TextExtension::class, 24 | NumbersExtension::class, 25 | StringsExtension::class, 26 | HashExtension::class, 27 | DateTimeExtension::class, 28 | InternetExtension::class, 29 | ColorsExtension::class, 30 | PersonExtension::class, 31 | FinancialExtension::class, 32 | BooleanExtension::class, 33 | ArrayExtension::class, 34 | ]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Manifests/ContainerMixinManifest.php: -------------------------------------------------------------------------------- 1 | basePath = $basePath; 46 | $this->vendorPath = $basePath.'/vendor'; 47 | $this->containerMixinPath = $containerMixinPath ?? $basePath.'/_faker_helper.php'; 48 | } 49 | 50 | /** 51 | * Build the manifest only if it should. 52 | * 53 | * @param array $extensions 54 | * @param array $extensionMethods 55 | * 56 | * @return void 57 | */ 58 | public function buildIfShould(array $extensionMethods, array $extensions) 59 | { 60 | if ($this->shouldRecompile()) { 61 | $this->build($extensionMethods, $extensions); 62 | } 63 | } 64 | 65 | /** 66 | * Build the manifest and write it to disk. 67 | * 68 | * @param array $extensions 69 | * @param array $extensionMethods 70 | * 71 | * @return void 72 | */ 73 | public function build(array $extensionMethods, array $extensions) 74 | { 75 | $tags = []; 76 | 77 | foreach ($extensionMethods as $methodName => $extensionName) { 78 | $extension = $extensions[$extensionName]; 79 | 80 | // If the extension is localized we look for the first containing the method 81 | if (is_array($extension) && isset($extension['locales'])) { 82 | $extension = current(array_filter($extension['locales'], function ($extensionFiltered) use ($methodName) { 83 | return method_exists($extensionFiltered, $methodName); 84 | })); 85 | } 86 | 87 | $reflectionMethod = new \ReflectionMethod($extension, $methodName); 88 | 89 | $parameters = []; 90 | $typeResolver = new TypeResolver(); 91 | 92 | foreach ($reflectionMethod->getParameters() as $parameter) { 93 | $type = new Mixed_(); 94 | 95 | if ($parameter->hasType()) { 96 | $type = $parameter->getType(); 97 | 98 | if ($type instanceof \ReflectionUnionType) { 99 | $type = new Compound( 100 | array_map( 101 | fn ($type) => $typeResolver->resolve($type), 102 | $type->getTypes() 103 | ) 104 | ); 105 | } else { 106 | $type = $typeResolver->resolve($type->getName()); 107 | } 108 | 109 | if ($parameter->allowsNull()) { 110 | $type = new Nullable($type); 111 | } 112 | } 113 | 114 | $parameters[] = new DocBlock\Tags\MethodParameter( 115 | name: $parameter->getName(), 116 | type: $type, 117 | defaultValue: $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : DocBlock\Tags\MethodParameter::NO_DEFAULT_VALUE 118 | ); 119 | } 120 | 121 | $tags[] = new Method( 122 | $methodName, 123 | returnType: $reflectionMethod->hasReturnType() ? (new TypeResolver())->resolve($reflectionMethod->getReturnType()) : new Mixed_(), 124 | parameters: $parameters, 125 | ); 126 | } 127 | 128 | $docBlock = new DocBlock(tags: $tags); 129 | 130 | $serializer = new Serializer(); 131 | 132 | $this->write( 133 | $serializer->getDocComment($docBlock) 134 | ); 135 | } 136 | 137 | /** 138 | * Determine if the manifest should be compiled. 139 | * 140 | * @return bool 141 | */ 142 | public function shouldRecompile(): bool 143 | { 144 | return !is_file($this->containerMixinPath) || 145 | // We check here if the manifest has been generated before changing the installed.json composer file 146 | filemtime($this->containerMixinPath) <= filemtime($this->vendorPath.'/composer/installed.json'); 147 | } 148 | 149 | /** 150 | * Write the given manifest docblock to disk. 151 | * 152 | * @param string $docComment 153 | * 154 | * @return void 155 | */ 156 | protected function write(string $docComment): void 157 | { 158 | if (!is_writable(dirname($this->containerMixinPath))) { 159 | throw new Exception('The '.dirname($this->containerMixinPath).' directory must be present and writable.'); 160 | } 161 | 162 | file_put_contents( 163 | $this->containerMixinPath, 164 | "basePath = $basePath; 48 | $this->vendorPath = $basePath.'/vendor'; 49 | $this->manifestPath = $manifestPath; 50 | } 51 | 52 | /** 53 | * Get all of the service provider class names for all packages. 54 | * 55 | * @return array 56 | */ 57 | public function providers() 58 | { 59 | return $this->config('providers'); 60 | } 61 | 62 | /** 63 | * Get all of the values for all packages for the given configuration name. 64 | * 65 | * @param string $key 66 | * 67 | * @return array 68 | */ 69 | public function config($key) 70 | { 71 | return array_filter( 72 | array_map( 73 | function ($configuration) use ($key) { 74 | return (array) ($configuration[$key] ?? []); 75 | }, 76 | $this->getManifest() 77 | ) 78 | ); 79 | } 80 | 81 | /** 82 | * Get the current package manifest. 83 | * 84 | * @return array 85 | */ 86 | protected function getManifest() 87 | { 88 | if (!empty($this->manifest)) { 89 | return $this->manifest; 90 | } 91 | 92 | if ($this->shouldRecompile()) { 93 | $this->build(); 94 | } 95 | 96 | return $this->manifest = is_file($this->manifestPath) ? 97 | (require $this->manifestPath) : []; 98 | } 99 | 100 | /** 101 | * Build the manifest and write it to disk. 102 | * 103 | * @return void 104 | */ 105 | public function build() 106 | { 107 | $packages = []; 108 | 109 | if (is_file($path = $this->vendorPath.'/composer/installed.json')) { 110 | $installed = json_decode(file_get_contents($path), true); 111 | 112 | $packages = $installed['packages'] ?? $installed; 113 | } 114 | 115 | $packagesToProvide = []; 116 | 117 | foreach ($packages as $package) { 118 | if (isset($package['extra']['faker'])) { 119 | $packagesToProvide[$package['name']] = $package['extra']['faker']; 120 | } 121 | } 122 | 123 | $this->write( 124 | $packagesToProvide 125 | ); 126 | } 127 | 128 | /** 129 | * Determine if the manifest should be compiled. 130 | * 131 | * @return bool 132 | */ 133 | public function shouldRecompile(): bool 134 | { 135 | return !is_file($this->manifestPath) || 136 | // We check here if the manifest has been generated before changing the installed.json composer file 137 | filemtime($this->manifestPath) <= filemtime($this->vendorPath.'/composer/installed.json'); 138 | } 139 | 140 | /** 141 | * Write the given manifest array to disk. 142 | * 143 | * @param array $manifest 144 | * 145 | * @throws \Exception 146 | * 147 | * @return void 148 | */ 149 | protected function write(array $manifest): void 150 | { 151 | if (!is_writable($dirname = dirname($this->manifestPath))) { 152 | throw new Exception("The {$dirname} directory must be present and writable."); 153 | } 154 | 155 | file_put_contents( 156 | $this->manifestPath, 157 | ' 60% chance of null). 11 | * 12 | * @var int 13 | */ 14 | protected int $weight; 15 | 16 | protected Randomizer $randomizer; 17 | 18 | public function __construct(Randomizer $randomizer, int $weight = 50) 19 | { 20 | $this->randomizer = $randomizer; 21 | $this->weight = $weight; 22 | } 23 | 24 | /** 25 | * Handle the given modifier. 26 | * 27 | * @param mixed $generatedValue 28 | * 29 | * @return mixed 30 | */ 31 | public function apply(mixed $generatedValue): mixed 32 | { 33 | if ($this->randomizer->getInt(0, 100) < $this->weight) { 34 | return null; 35 | } 36 | 37 | return $generatedValue; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Modifiers/UppercaseModifier.php: -------------------------------------------------------------------------------- 1 | resolveExtensions($extensions); 20 | }); 21 | } 22 | 23 | /** 24 | * Bootstrap any package services. 25 | */ 26 | public function boot(): void 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Providers/ProviderRepository.php: -------------------------------------------------------------------------------- 1 | createProvider($provider); 18 | 19 | $instance->boot(); 20 | } 21 | } 22 | 23 | /** 24 | * Create a new provider instance. 25 | * 26 | * @param string $provider 27 | * 28 | * @return \Xefi\Faker\Providers\Provider 29 | */ 30 | public function createProvider($provider): Provider 31 | { 32 | if (is_object($provider)) { 33 | return $provider; 34 | } 35 | 36 | return new $provider(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Seeds/HasSeeds.php: -------------------------------------------------------------------------------- 1 | regex = $regex; 12 | } 13 | 14 | /** 15 | * Handle the given strategy. 16 | * 17 | * @param mixed $generatedValue 18 | * 19 | * @return bool 20 | */ 21 | public function pass(mixed $generatedValue): bool 22 | { 23 | return (bool) preg_match($this->regex, $generatedValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Strategies/Strategy.php: -------------------------------------------------------------------------------- 1 | tried, true)) { 28 | return false; 29 | } 30 | 31 | $this->tried[] = $generatedValue; 32 | 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Support/Extensions/EnEnExtensionTest.php: -------------------------------------------------------------------------------- 1 | getLocale(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Support/Extensions/EnUsExtensionTest.php: -------------------------------------------------------------------------------- 1 | getLocale(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Support/Extensions/FrFrExtensionTest.php: -------------------------------------------------------------------------------- 1 | getLocale(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Support/Extensions/MixinTestExtension.php: -------------------------------------------------------------------------------- 1 | getLocale(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Support/Extensions/NumberTestExtension.php: -------------------------------------------------------------------------------- 1 | extensions([ 14 | NumberTestExtension::class, 15 | StringTestExtension::class, 16 | ]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Support/vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | { 4 | "name": "autoload-needed", 5 | "extra": { 6 | "faker": { 7 | "providers": [ 8 | "Xefi\\Faker\\Tests\\Support\\TestServiceProvider" 9 | ] 10 | } 11 | } 12 | }, 13 | { 14 | "name": "common-package", 15 | "extra": { 16 | "branch-alias": { 17 | "dev-master": "2.0.x-dev" 18 | } 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /tests/Unit/Calculators/IbanTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($checksum, Iban::checksum($iban), $iban); 88 | } 89 | 90 | public static function validatorProvider() 91 | { 92 | return [ 93 | ['AL47212110090000000235698741', true], 94 | ['AD1200012030200359100100', true], 95 | ['AT611904300234573201', true], 96 | ['AZ21NABZ00000000137010001944', true], 97 | ['BH67BMAG00001299123456', true], 98 | ['BE68539007547034', true], 99 | ['BA391290079401028494', true], 100 | ['BR7724891749412660603618210F3', true], 101 | ['BG80BNBG96611020345678', true], 102 | ['CR0515202001026284066', true], 103 | ['HR1210010051863000160', true], 104 | ['CY17002001280000001200527600', true], 105 | ['CZ6508000000192000145399', true], 106 | ['DK5000400440116243', true], 107 | ['DO28BAGR00000001212453611324', true], 108 | ['EE382200221020145685', true], 109 | ['FO6264600001631634', true], 110 | ['FI2112345600000785', true], 111 | ['FR1420041010050500013M02606', true], 112 | ['GE29NB0000000101904917', true], 113 | ['DE89370400440532013000', true], 114 | ['GI75NWBK000000007099453', true], 115 | ['GR1601101250000000012300695', true], 116 | ['GL8964710001000206', true], 117 | ['GT82TRAJ01020000001210029690', true], 118 | ['HU42117730161111101800000000', true], 119 | ['IS140159260076545510730339', true], 120 | ['IE29AIBK93115212345678', true], 121 | ['IL620108000000099999999', true], 122 | ['IT60X0542811101000000123456', true], 123 | ['KZ86125KZT5004100100', true], 124 | ['KW81CBKU0000000000001234560101', true], 125 | ['LV80BANK0000435195001', true], 126 | ['LB62099900000001001901229114', true], 127 | ['LI21088100002324013AA', true], 128 | ['LT121000011101001000', true], 129 | ['LU280019400644750000', true], 130 | ['MK07250120000058984', true], 131 | ['MT84MALT011000012345MTLCAST001S', true], 132 | ['MR1300020001010000123456753', true], 133 | ['MU17BOMM0101101030300200000MUR', true], 134 | ['MD24AG000225100013104168', true], 135 | ['MC5811222000010123456789030', true], 136 | ['ME25505000012345678951', true], 137 | ['NL91ABNA0417164300', true], 138 | ['NO9386011117947', true], 139 | ['PK36SCBL0000001123456702', true], 140 | ['PL61109010140000071219812874', true], 141 | ['PS92PALS000000000400123456702', true], 142 | ['PT50000201231234567890154', true], 143 | ['QA58DOHB00001234567890ABCDEFG', true], 144 | ['RO49AAAA1B31007593840000', true], 145 | ['SM86U0322509800000000270100', true], 146 | ['SA0380000000608010167519', true], 147 | ['RS35260005601001611379', true], 148 | ['SK3112000000198742637541', true], 149 | ['SI56263300012039086', true], 150 | ['ES9121000418450200051332', true], 151 | ['SE4550000000058398257466', true], 152 | ['CH9300762011623852957', true], 153 | ['TN5910006035183598478831', true], 154 | ['TR330006100519786457841326', true], 155 | ['AE070331234567890123456', true], 156 | ['GB29NWBK60161331926819', true], 157 | ['VG96VPVG0000012345678901', true], 158 | ['YY24KIHB12476423125915947930915268', true], 159 | ['ZZ25VLQT382332233206588011313776421', true], 160 | 161 | ['AL4721211009000000023569874', false], 162 | ['AD120001203020035910010', false], 163 | ['AT61190430023457320', false], 164 | ['AZ21NABZ0000000013701000194', false], 165 | ['BH67BMAG0000129912345', false], 166 | ['BE6853900754703', false], 167 | ['BA39129007940102849', false], 168 | ['BR7724891749412660603618210F', false], 169 | ['BG80BNBG9661102034567', false], 170 | ['CR051520200102628406', false], 171 | ['HR121001005186300016', false], 172 | ['CY1700200128000000120052760', false], 173 | ['CZ650800000019200014539', false], 174 | ['DK500040044011624', false], 175 | ['DO28BAGR0000000121245361132', false], 176 | ['EE38220022102014568', false], 177 | ['FO626460000163163', false], 178 | ['FI2112345600000780', false], 179 | ['FR1420041010050500013M0260', false], 180 | ['GE29NB000000010190491', false], 181 | ['DE8937040044053201300', false], 182 | ['GI75NWBK00000000709945', false], 183 | ['GR160110125000000001230069', false], 184 | ['GL896471000100020', false], 185 | ['GT82TRAJ0102000000121002969', false], 186 | ['HU4211773016111110180000000', false], 187 | ['IS14015926007654551073033', false], 188 | ['IE29AIBK9311521234567', false], 189 | ['IL62010800000009999999', false], 190 | ['IT60X054281110100000012345', false], 191 | ['KZ86125KZT500410010', false], 192 | ['KW81CBKU000000000000123456010', false], 193 | ['LV80BANK000043519500', false], 194 | ['LB6209990000000100190122911', false], 195 | ['LI21088100002324013A', false], 196 | ['LT12100001110100100', false], 197 | ['LU28001940064475000', false], 198 | ['MK0725012000005898', false], 199 | ['MT84MALT011000012345MTLCAST001', false], 200 | ['MR130002000101000012345675', false], 201 | ['MU17BOMM0101101030300200000MU', false], 202 | ['MD24AG00022510001310416', false], 203 | ['MC58112220000101234567890', false], 204 | ['ME2550500001234567895', false], 205 | ['NL91ABNA041716430', false], 206 | ['NO938601111794', false], 207 | ['PK36SCBL000000112345670', false], 208 | ['PL6110901014000007121981287', false], 209 | ['PS92PALS00000000040012345670', false], 210 | ['PT5000020123123456789015', false], 211 | ['QA58DOHB00001234567890ABCDEF', false], 212 | ['RO49AAAA1B3100759384000', false], 213 | ['SM86U032250980000000027010', false], 214 | ['SA038000000060801016751', false], 215 | ['RS3526000560100161137', false], 216 | ['SK311200000019874263754', false], 217 | ['SI5626330001203908', false], 218 | ['ES912100041845020005133', false], 219 | ['SE455000000005839825746', false], 220 | ['CH930076201162385295', false], 221 | ['TN591000603518359847883', false], 222 | ['TR33000610051978645784132', false], 223 | ['AE07033123456789012345', false], 224 | ['GB29NWBK6016133192681', false], 225 | ['VG96VPVG000001234567890', false], 226 | ['YY24KIHB1247642312591594793091526', false], 227 | ['ZZ25VLQT38233223320658801131377642', false], 228 | ]; 229 | } 230 | 231 | #[DataProvider('validatorProvider')] 232 | public function testIsValid($iban, $isValid): void 233 | { 234 | $this->assertEquals($isValid, Iban::isValid($iban), $iban); 235 | } 236 | 237 | public static function alphaToNumberProvider() 238 | { 239 | return [ 240 | ['A', 10], 241 | ['B', 11], 242 | ['C', 12], 243 | ['D', 13], 244 | ['E', 14], 245 | ['F', 15], 246 | ['G', 16], 247 | ['H', 17], 248 | ['I', 18], 249 | ['J', 19], 250 | ['K', 20], 251 | ['L', 21], 252 | ['M', 22], 253 | ['N', 23], 254 | ['O', 24], 255 | ['P', 25], 256 | ['Q', 26], 257 | ['R', 27], 258 | ['S', 28], 259 | ['T', 29], 260 | ['U', 30], 261 | ['V', 31], 262 | ['W', 32], 263 | ['X', 33], 264 | ['Y', 34], 265 | ['Z', 35], 266 | ]; 267 | } 268 | 269 | #[DataProvider('alphaToNumberProvider')] 270 | public function testAlphaToNumber($letter, $number): void 271 | { 272 | self::assertEquals($number, Iban::alphaToNumber($letter), $letter); 273 | } 274 | 275 | public static function mod97Provider() 276 | { 277 | // Large numbers 278 | $return = [ 279 | ['123456789123456789', 7], 280 | ['111222333444555666', 73], 281 | ['4242424242424242424242', 19], 282 | ['271828182845904523536028', 68], 283 | ]; 284 | 285 | // 0-200 286 | for ($i = 0; $i < 200; $i++) { 287 | $return[] = [(string) $i, $i % 97]; 288 | } 289 | 290 | return $return; 291 | } 292 | 293 | #[DataProvider('mod97Provider')] 294 | public function testMod97($number, $result): void 295 | { 296 | self::assertEquals($result, Iban::mod97($number), $number); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /tests/Unit/Calculators/LuhnTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($isValid, Luhn::isValid((string) $number)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/ContainerMixinManifestTest.php: -------------------------------------------------------------------------------- 1 | forgetExtensions(); 15 | $container->resolveExtensions([ 16 | \Xefi\Faker\Tests\Support\Extensions\MixinTestExtension::class, 17 | ]); 18 | } 19 | 20 | public function testContainerMixinBuild() 21 | { 22 | @unlink('/tmp/ContainerMixin.php'); 23 | $container = new \Xefi\Faker\Container\Container(shouldBuildContainerMixin: false); 24 | $manifest = new \Xefi\Faker\Manifests\ContainerMixinManifest(__DIR__.'/../Support', '/tmp/ContainerMixin.php'); 25 | $manifest->build($container->getExtensionMethods(), $container->getExtensions()); 26 | 27 | $this->assertFileExists('/tmp/ContainerMixin.php'); 28 | 29 | $containerMixinContent = file_get_contents('/tmp/ContainerMixin.php'); 30 | 31 | $this->assertStringContainsString( 32 | '@method mixed withoutParameters()', 33 | $containerMixinContent 34 | ); 35 | 36 | $this->assertStringContainsString( 37 | '@method mixed withNoTypeParameters(mixed $one, mixed $two)', 38 | $containerMixinContent 39 | ); 40 | 41 | $this->assertStringContainsString( 42 | '@method mixed withTypedParameters(int $one, string $two)', 43 | $containerMixinContent 44 | ); 45 | 46 | $this->assertStringContainsString( 47 | '@method void withVoidReturnType()', 48 | $containerMixinContent 49 | ); 50 | 51 | $this->assertStringContainsString( 52 | '@method string withStringReturnType()', 53 | $containerMixinContent 54 | ); 55 | 56 | $this->assertStringContainsString( 57 | '@method string withNullableParameter(?string $one = \'default value\')', 58 | $containerMixinContent 59 | ); 60 | 61 | $this->assertStringContainsString( 62 | '@method string|int withCompoundType(string|int $param)', 63 | $containerMixinContent 64 | ); 65 | 66 | $this->assertStringNotContainsString( 67 | '@method string|int withCompoundType(string&int $param)', 68 | $containerMixinContent 69 | ); 70 | 71 | unlink('/tmp/ContainerMixin.php'); 72 | } 73 | 74 | public function testContainerMixinNamespace() 75 | { 76 | @unlink('/tmp/ContainerMixin.php'); 77 | $container = new \Xefi\Faker\Container\Container(shouldBuildContainerMixin: false); 78 | $manifest = new \Xefi\Faker\Manifests\ContainerMixinManifest(__DIR__.'/../Support', '/tmp/ContainerMixin.php'); 79 | $manifest->build($container->getExtensionMethods(), $container->getExtensions()); 80 | 81 | $this->assertFileExists('/tmp/ContainerMixin.php'); 82 | 83 | $containerMixinContent = file_get_contents('/tmp/ContainerMixin.php'); 84 | 85 | $this->assertStringContainsString( 86 | 'namespace Xefi\\Faker\\Container;', 87 | $containerMixinContent 88 | ); 89 | 90 | unlink('/tmp/ContainerMixin.php'); 91 | } 92 | 93 | public function testShouldRecompile() 94 | { 95 | @unlink('/tmp/ContainerMixin.php'); 96 | $container = new \Xefi\Faker\Container\Container(shouldBuildContainerMixin: false); 97 | $manifest = new \Xefi\Faker\Manifests\ContainerMixinManifest(__DIR__.'/../Support', '/tmp/ContainerMixin.php'); 98 | $manifest->build($container->getExtensionMethods(), $container->getExtensions()); 99 | touch(__DIR__.'/../Support/vendor/composer/installed.json', time() - 1); 100 | 101 | $this->assertFalse($manifest->shouldRecompile()); 102 | 103 | // Test on current time 104 | touch(__DIR__.'/../Support/vendor/composer/installed.json'); 105 | $this->assertTrue($manifest->shouldRecompile()); 106 | 107 | // Test on future 108 | touch(__DIR__.'/../Support/vendor/composer/installed.json', time() + 1); 109 | $this->assertTrue($manifest->shouldRecompile()); 110 | 111 | unlink('/tmp/ContainerMixin.php'); 112 | } 113 | 114 | public function testNoExtension() 115 | { 116 | @unlink('/tmp/ContainerMixin.php'); 117 | $container = new \Xefi\Faker\Container\Container(shouldBuildContainerMixin: false); 118 | $container->forgetExtensions(); 119 | 120 | $manifest = new \Xefi\Faker\Manifests\ContainerMixinManifest(__DIR__.'/../Support', '/tmp/ContainerMixin.php'); 121 | $manifest->build($container->getExtensionMethods(), $container->getExtensions()); 122 | 123 | $this->assertFileExists('/tmp/ContainerMixin.php'); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/Unit/ContainerTest.php: -------------------------------------------------------------------------------- 1 | assertFileExists('/tmp/packages.php'); 21 | 22 | unlink('/tmp/packages.php'); 23 | } 24 | 25 | public function testExtensionsCorrectlyRegistered(): void 26 | { 27 | @unlink('/tmp/packages.php'); 28 | 29 | $container = new Container(); 30 | 31 | $this->assertEquals( 32 | [ 33 | 'number-test-extension' => new NumberTestExtension(new Randomizer()), 34 | 'string-test-extension' => new StringTestExtension(new Randomizer()), 35 | ], 36 | $container->getExtensions() 37 | ); 38 | 39 | unlink('/tmp/packages.php'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Unit/ExtensionTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 17 | 'hello', 18 | $container->returnHello() 19 | ); 20 | } 21 | 22 | public function testExtensionReturnOne(): void 23 | { 24 | $container = new Container(); 25 | 26 | $this->assertEquals( 27 | 1, 28 | $container->returnOne() 29 | ); 30 | } 31 | 32 | public function testExtensionPickArrayRandomElements(): void 33 | { 34 | $elements = range(1, 100); 35 | 36 | $method = (new ReflectionClass(Extension::class))->getMethod('pickArrayRandomElements'); 37 | $result = $method->invoke(new Extension(new Randomizer()), $elements, 5); 38 | 39 | $this->assertCount( 40 | 5, 41 | $result 42 | ); 43 | 44 | foreach ($result as $item) { 45 | $this->assertGreaterThanOrEqual(1, $item); 46 | $this->assertLessThanOrEqual(100, $item); 47 | } 48 | } 49 | 50 | public function testExtensionPickArrayRandomElement(): void 51 | { 52 | $elements = range(1, 100); 53 | 54 | $method = (new ReflectionClass(Extension::class))->getMethod('pickArrayRandomElement'); 55 | $result = $method->invoke(new Extension(new Randomizer()), $elements); 56 | 57 | $this->assertIsNotArray($result); 58 | $this->assertGreaterThanOrEqual(1, $result); 59 | $this->assertLessThanOrEqual(100, $result); 60 | } 61 | 62 | public function testExtensionPickArrayRandomKeys(): void 63 | { 64 | $elements = [ 65 | 'a' => 1, 66 | 'b' => 2, 67 | 'c' => 3, 68 | 'd' => 4, 69 | 'e' => 5, 70 | ]; 71 | 72 | $method = (new ReflectionClass(Extension::class))->getMethod('pickArrayRandomKeys'); 73 | $result = $method->invoke(new Extension(new Randomizer()), $elements, 3); 74 | 75 | $this->assertCount( 76 | 3, 77 | $result 78 | ); 79 | 80 | foreach ($result as $key) { 81 | $this->assertArrayHasKey($key, $elements); 82 | $this->assertIsString($key); 83 | } 84 | } 85 | 86 | public function testExtensionPickArrayRandomKey(): void 87 | { 88 | $elements = [ 89 | 'a' => 1, 90 | 'b' => 2, 91 | 'c' => 3, 92 | 'd' => 4, 93 | 'e' => 5, 94 | ]; 95 | 96 | $method = (new ReflectionClass(Extension::class))->getMethod('pickArrayRandomKeyNumber'); 97 | $result = $method->invoke(new Extension(new Randomizer()), $elements); 98 | 99 | $this->assertArrayHasKey($result, $elements); 100 | $this->assertIsString($result); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/ArraysExtensionTest.php: -------------------------------------------------------------------------------- 1 | testArray = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5]; 14 | } 15 | 16 | public function testRandomElement(): void 17 | { 18 | $elements = []; 19 | for ($i = 0; $i < count($this->testArray); $i++) { 20 | $elements[] = $this->faker->unique()->randomElement($this->testArray); 21 | } 22 | 23 | $this->assertEqualsCanonicalizing($elements, $this->testArray); 24 | } 25 | 26 | public function testRandomKeyNumber(): void 27 | { 28 | $elements = []; 29 | for ($i = 0; $i < count($this->testArray); $i++) { 30 | $elements[] = $this->faker->unique()->randomKeyNumber($this->testArray); 31 | } 32 | 33 | $this->assertEqualsCanonicalizing($elements, array_keys($this->testArray)); 34 | } 35 | 36 | public function testRandomKey(): void 37 | { 38 | $inputArray = [ 39 | ['firstname' => 'John'], 40 | ['lastname' => 'Doe'], 41 | ['nickname' => 'Johnny'], 42 | ['login' => 'j.doe'], 43 | ]; 44 | 45 | $result = $this->faker->randomKey($inputArray); 46 | $this->assertContains($result, ['firstname', 'lastname', 'nickname', 'login']); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/BooleanExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->unique()->boolean(); 14 | } 15 | 16 | $this->assertEqualsCanonicalizing($results, [true, false]); 17 | } 18 | 19 | public function testBooleanAlwaysTrue(): void 20 | { 21 | for ($i = 0; $i < 100; $i++) { 22 | $this->assertTrue($this->faker->boolean(100)); 23 | } 24 | } 25 | 26 | public function testBooleanAlwaysFalse(): void 27 | { 28 | for ($i = 0; $i < 100; $i++) { 29 | $this->assertFalse($this->faker->boolean(0)); 30 | } 31 | } 32 | 33 | public function testBooleanWithDefaultPercentage(): void 34 | { 35 | $trueCount = 0; 36 | $falseCount = 0; 37 | 38 | for ($i = 0; $i < 1000; $i++) { 39 | if ($this->faker->boolean()) { 40 | $trueCount++; 41 | } else { 42 | $falseCount++; 43 | } 44 | } 45 | 46 | // We expect 50% of "true" so we check that there is minimum 45% of each value 47 | $this->assertGreaterThan(450, $trueCount); 48 | $this->assertGreaterThan(450, $falseCount); 49 | } 50 | 51 | public function testBooleanWithPercentage(): void 52 | { 53 | $trueCount = 0; 54 | $falseCount = 0; 55 | 56 | for ($i = 0; $i < 1000; $i++) { 57 | if ($this->faker->boolean(30)) { 58 | $trueCount++; 59 | } else { 60 | $falseCount++; 61 | } 62 | } 63 | 64 | // We expect 30% of "true" so we check that there is minimum 25% of "true" and 65% of "false" 65 | $this->assertGreaterThan(250, $trueCount); 66 | $this->assertGreaterThan(650, $falseCount); 67 | } 68 | 69 | public function testBooleanWithPercentageLowerThan100(): void 70 | { 71 | $this->expectException(BadParameterException::class); 72 | $this->faker->boolean(-1); 73 | } 74 | 75 | public function testBooleanWithPercentageGreaterThan100(): void 76 | { 77 | $this->expectException(BadParameterException::class); 78 | $this->faker->boolean(101); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/ColorsExtensionTest.php: -------------------------------------------------------------------------------- 1 | safeColorNames = (new \ReflectionClass($colorExtension))->getProperty('safeColorNames')->getValue($colorExtension); 19 | $this->colorNames = (new \ReflectionClass($colorExtension))->getProperty('colorNames')->getValue($colorExtension); 20 | } 21 | 22 | public function testSafeColorName(): void 23 | { 24 | $results = []; 25 | for ($i = 0; $i < count($this->safeColorNames); $i++) { 26 | $results[] = $this->faker->unique()->safeColorName(); 27 | } 28 | 29 | $this->assertEqualsCanonicalizing( 30 | $this->safeColorNames, 31 | $results 32 | ); 33 | } 34 | 35 | public function testColorName(): void 36 | { 37 | $results = []; 38 | for ($i = 0; $i < count($this->colorNames); $i++) { 39 | $results[] = $this->faker->unique()->colorName(); 40 | } 41 | 42 | $this->assertEqualsCanonicalizing( 43 | $this->colorNames, 44 | $results 45 | ); 46 | } 47 | 48 | public function testSafeHexColor(): void 49 | { 50 | for ($i = 0; $i < 100; $i++) { 51 | $color = $this->faker->unique()->safeHexColor(); 52 | 53 | $this->assertStringStartsWith('#', $color); 54 | $this->assertTrue(ctype_xdigit(substr($color, 1))); 55 | $this->assertEquals(7, strlen($color)); 56 | } 57 | } 58 | 59 | public function testHexColor(): void 60 | { 61 | for ($i = 0; $i < 100; $i++) { 62 | $color = $this->faker->unique()->hexColor(); 63 | 64 | $this->assertStringStartsWith('#', $color); 65 | $this->assertTrue(ctype_xdigit(substr($color, 1))); 66 | $this->assertEquals(7, strlen($color)); 67 | } 68 | } 69 | 70 | public function testRgbColorAsArray(): void 71 | { 72 | for ($i = 0; $i < 100; $i++) { 73 | $color = $this->faker->unique()->rgbColorAsArray(); 74 | 75 | $this->assertCount(3, $color); 76 | 77 | // Red 78 | $this->assertGreaterThanOrEqual(0, $color[0]); 79 | $this->assertLessThanOrEqual(255, $color[0]); 80 | 81 | // Green 82 | $this->assertGreaterThanOrEqual(0, $color[1]); 83 | $this->assertLessThanOrEqual(255, $color[1]); 84 | 85 | // Blue 86 | $this->assertGreaterThanOrEqual(0, $color[2]); 87 | $this->assertLessThanOrEqual(255, $color[2]); 88 | } 89 | } 90 | 91 | public function testRgbColor(): void 92 | { 93 | $regex = '/^(\d+),(\d+),(\d+)$/'; 94 | 95 | for ($i = 0; $i < 100; $i++) { 96 | $color = $this->faker->unique()->rgbColor(); 97 | preg_match($regex, $color, $matches); 98 | 99 | /// Red 100 | $this->assertGreaterThanOrEqual(0, $matches[1]); 101 | $this->assertLessThanOrEqual(255, $matches[1]); 102 | 103 | // Green 104 | $this->assertGreaterThanOrEqual(0, $matches[2]); 105 | $this->assertLessThanOrEqual(255, $matches[2]); 106 | 107 | // Blue 108 | $this->assertGreaterThanOrEqual(0, $matches[3]); 109 | $this->assertLessThanOrEqual(255, $matches[3]); 110 | } 111 | } 112 | 113 | public function testRgbCssColor(): void 114 | { 115 | $regex = '/^rgb\((\d+),(\d+),(\d+)\)$/'; 116 | 117 | for ($i = 0; $i < 100; $i++) { 118 | $color = $this->faker->unique()->rgbCssColor(); 119 | preg_match($regex, $color, $matches); 120 | 121 | // String 122 | $this->assertStringStartsWith('rgb(', $color); 123 | $this->assertStringEndsWith(')', $color); 124 | 125 | /// Red 126 | $this->assertGreaterThanOrEqual(0, $matches[1]); 127 | $this->assertLessThanOrEqual(255, $matches[1]); 128 | 129 | // Green 130 | $this->assertGreaterThanOrEqual(0, $matches[2]); 131 | $this->assertLessThanOrEqual(255, $matches[2]); 132 | 133 | // Blue 134 | $this->assertGreaterThanOrEqual(0, $matches[3]); 135 | $this->assertLessThanOrEqual(255, $matches[3]); 136 | } 137 | } 138 | 139 | public function testRgbaColorAsArray(): void 140 | { 141 | for ($i = 0; $i < 100; $i++) { 142 | $color = $this->faker->unique()->rgbaColorAsArray(); 143 | 144 | $this->assertCount(4, $color); 145 | 146 | // Red 147 | $this->assertGreaterThanOrEqual(0, $color[0]); 148 | $this->assertLessThanOrEqual(255, $color[0]); 149 | 150 | // Green 151 | $this->assertGreaterThanOrEqual(0, $color[1]); 152 | $this->assertLessThanOrEqual(255, $color[1]); 153 | 154 | // Blue 155 | $this->assertGreaterThanOrEqual(0, $color[2]); 156 | $this->assertLessThanOrEqual(255, $color[2]); 157 | 158 | // Alpha 159 | $this->assertGreaterThanOrEqual(0, $color[3]); 160 | $this->assertLessThanOrEqual(1, $color[3]); 161 | } 162 | } 163 | 164 | public function testRgbaColor(): void 165 | { 166 | $regex = '/^(\d+),(\d+),(\d+),([01]|0\.\d+)$/'; 167 | 168 | for ($i = 0; $i < 100; $i++) { 169 | $color = $this->faker->unique()->rgbaColor(); 170 | preg_match($regex, $color, $matches); 171 | 172 | /// Red 173 | $this->assertGreaterThanOrEqual(0, $matches[1]); 174 | $this->assertLessThanOrEqual(255, $matches[1]); 175 | 176 | // Green 177 | $this->assertGreaterThanOrEqual(0, $matches[2]); 178 | $this->assertLessThanOrEqual(255, $matches[2]); 179 | 180 | // Blue 181 | $this->assertGreaterThanOrEqual(0, $matches[3]); 182 | $this->assertLessThanOrEqual(255, $matches[3]); 183 | 184 | // Alpha 185 | $this->assertGreaterThanOrEqual(0, $matches[4]); 186 | $this->assertLessThanOrEqual(1, $matches[4]); 187 | } 188 | } 189 | 190 | public function testRgbaCssColor(): void 191 | { 192 | $regex = '/^rgba\((\d+),(\d+),(\d+),([01]|0\.\d+)\)$/'; 193 | 194 | for ($i = 0; $i < 100; $i++) { 195 | $color = $this->faker->unique()->rgbaCssColor(); 196 | preg_match($regex, $color, $matches); 197 | 198 | // String 199 | $this->assertStringStartsWith('rgba(', $color); 200 | $this->assertStringEndsWith(')', $color); 201 | 202 | /// Red 203 | $this->assertGreaterThanOrEqual(0, $matches[1]); 204 | $this->assertLessThanOrEqual(255, $matches[1]); 205 | 206 | // Green 207 | $this->assertGreaterThanOrEqual(0, $matches[2]); 208 | $this->assertLessThanOrEqual(255, $matches[2]); 209 | 210 | // Blue 211 | $this->assertGreaterThanOrEqual(0, $matches[3]); 212 | $this->assertLessThanOrEqual(255, $matches[3]); 213 | 214 | // Alpha 215 | $this->assertGreaterThanOrEqual(0, $matches[4]); 216 | $this->assertLessThanOrEqual(1, $matches[4]); 217 | } 218 | } 219 | 220 | public function testHslColorAsArray(): void 221 | { 222 | for ($i = 0; $i < 100; $i++) { 223 | $color = $this->faker->unique()->hslColorAsArray(); 224 | 225 | $this->assertCount(3, $color); 226 | 227 | // HUE 228 | $this->assertGreaterThanOrEqual(0, $color[0]); 229 | $this->assertLessThanOrEqual(360, $color[0]); 230 | 231 | // Saturation 232 | $this->assertGreaterThanOrEqual(0, $color[1]); 233 | $this->assertLessThanOrEqual(100, $color[1]); 234 | 235 | // Lightness 236 | $this->assertGreaterThanOrEqual(0, $color[2]); 237 | $this->assertLessThanOrEqual(100, $color[2]); 238 | } 239 | } 240 | 241 | public function testHslColor(): void 242 | { 243 | $regex = '/^(\d+),(\d+),(\d+)$/'; 244 | 245 | for ($i = 0; $i < 100; $i++) { 246 | $color = $this->faker->unique()->hslColor(); 247 | preg_match($regex, $color, $matches); 248 | 249 | // HUE 250 | $this->assertGreaterThanOrEqual(0, $matches[1]); 251 | $this->assertLessThanOrEqual(360, $matches[1]); 252 | 253 | // Saturation 254 | $this->assertGreaterThanOrEqual(0, $matches[2]); 255 | $this->assertLessThanOrEqual(100, $matches[2]); 256 | 257 | // Lightness 258 | $this->assertGreaterThanOrEqual(0, $matches[3]); 259 | $this->assertLessThanOrEqual(100, $matches[3]); 260 | } 261 | } 262 | 263 | public function testHslCssColor(): void 264 | { 265 | $regex = '/^hsl\((\d+),(\d+),(\d+)\)$/'; 266 | 267 | for ($i = 0; $i < 100; $i++) { 268 | $color = $this->faker->unique()->hslCssColor(); 269 | 270 | preg_match($regex, $color, $matches); 271 | 272 | // String 273 | $this->assertStringStartsWith('hsl(', $color); 274 | $this->assertStringEndsWith(')', $color); 275 | 276 | // HUE 277 | $this->assertGreaterThanOrEqual(0, $matches[1]); 278 | $this->assertLessThanOrEqual(360, $matches[1]); 279 | 280 | // Saturation 281 | $this->assertGreaterThanOrEqual(0, $matches[2]); 282 | $this->assertLessThanOrEqual(100, $matches[2]); 283 | 284 | // Lightness 285 | $this->assertGreaterThanOrEqual(0, $matches[3]); 286 | $this->assertLessThanOrEqual(100, $matches[3]); 287 | } 288 | } 289 | 290 | public function testHslaColorAsArray(): void 291 | { 292 | for ($i = 0; $i < 100; $i++) { 293 | $color = $this->faker->unique()->hslaColorAsArray(); 294 | 295 | $this->assertIsArray($color); 296 | $this->assertCount(4, $color); 297 | 298 | // HUE 299 | $this->assertGreaterThanOrEqual(0, $color[0]); 300 | $this->assertLessThanOrEqual(360, $color[0]); 301 | 302 | // Saturation 303 | $this->assertGreaterThanOrEqual(0, $color[1]); 304 | $this->assertLessThanOrEqual(100, $color[1]); 305 | 306 | // Lightness 307 | $this->assertGreaterThanOrEqual(0, $color[2]); 308 | $this->assertLessThanOrEqual(100, $color[2]); 309 | 310 | // Alpha 311 | $this->assertGreaterThanOrEqual(0, $color[3]); 312 | $this->assertLessThanOrEqual(1, $color[3]); 313 | } 314 | } 315 | 316 | public function testHslaColor(): void 317 | { 318 | $regex = '/^(\d+),(\d+),(\d+),([01]|0\.\d+)$/'; 319 | 320 | for ($i = 0; $i < 100; $i++) { 321 | $color = $this->faker->unique()->hslaColor(); 322 | preg_match($regex, $color, $matches); 323 | 324 | // HUE 325 | $this->assertGreaterThanOrEqual(0, $matches[1]); 326 | $this->assertLessThanOrEqual(360, $matches[1]); 327 | 328 | // Saturation 329 | $this->assertGreaterThanOrEqual(0, $matches[2]); 330 | $this->assertLessThanOrEqual(100, $matches[2]); 331 | 332 | // Lightness 333 | $this->assertGreaterThanOrEqual(0, $matches[3]); 334 | $this->assertLessThanOrEqual(100, $matches[3]); 335 | 336 | // Alpha 337 | $this->assertGreaterThanOrEqual(0, $matches[4]); 338 | $this->assertLessThanOrEqual(1, $matches[4]); 339 | } 340 | } 341 | 342 | public function testHslaCssColor(): void 343 | { 344 | $regex = '/^hsla\((\d+),(\d+),(\d+),([01]|0\.\d+)\)$/'; 345 | 346 | for ($i = 0; $i < 100; $i++) { 347 | $color = $this->faker->unique()->hslaCssColor(); 348 | preg_match($regex, $color, $matches); 349 | 350 | // HUE 351 | $this->assertGreaterThanOrEqual(0, $matches[1]); 352 | $this->assertLessThanOrEqual(360, $matches[1]); 353 | 354 | // Saturation 355 | $this->assertGreaterThanOrEqual(0, $matches[2]); 356 | $this->assertLessThanOrEqual(100, $matches[2]); 357 | 358 | // Lightness 359 | $this->assertGreaterThanOrEqual(0, $matches[3]); 360 | $this->assertLessThanOrEqual(100, $matches[3]); 361 | 362 | // Alpha 363 | $this->assertGreaterThanOrEqual(0, $matches[4]); 364 | $this->assertLessThanOrEqual(1, $matches[4]); 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/DatetimeExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->timestamp(0, 5000); 15 | } 16 | 17 | foreach ($results as $result) { 18 | $this->assertGreaterThanOrEqual(0, $result); 19 | $this->assertLessThanOrEqual(5000, $result); 20 | } 21 | } 22 | 23 | public function testTimestampWithWrongIntOrder(): void 24 | { 25 | $this->expectException(\ValueError::class); 26 | $this->faker->timestamp(5000, 0); 27 | } 28 | 29 | public function testTimestampUsingDatetime(): void 30 | { 31 | $results = []; 32 | $fromDateTime = new \DateTime('1998-06-29'); 33 | $toDateTime = new \DateTime('1998-09-09'); 34 | 35 | for ($i = 0; $i < 50; $i++) { 36 | $results[] = $this->faker->timestamp($fromDateTime, $toDateTime); 37 | } 38 | 39 | foreach ($results as $result) { 40 | $this->assertGreaterThanOrEqual($fromDateTime->getTimestamp(), $result); 41 | $this->assertLessThanOrEqual($toDateTime->getTimestamp(), $result); 42 | } 43 | } 44 | 45 | public function testTimestampUsingString(): void 46 | { 47 | $results = []; 48 | 49 | for ($i = 0; $i < 50; $i++) { 50 | $results[] = $this->faker->timestamp('-10 years', '+20 years'); 51 | } 52 | 53 | $fromDateTime = new DateTime('-10 years'); 54 | $toDateTime = new DateTime('+20 years'); 55 | 56 | foreach ($results as $result) { 57 | $this->assertGreaterThanOrEqual($fromDateTime->getTimestamp(), $result); 58 | $this->assertLessThanOrEqual($toDateTime->getTimestamp(), $result); 59 | } 60 | } 61 | 62 | public function testDateTime(): void 63 | { 64 | $results = []; 65 | 66 | for ($i = 0; $i < 50; $i++) { 67 | $results[] = $this->faker->dateTime('-30 years', 'now'); 68 | } 69 | 70 | $fromDateTime = new DateTime('-30 years'); 71 | $toDateTime = new DateTime('now'); 72 | 73 | foreach ($results as $result) { 74 | $this->assertGreaterThanOrEqual($fromDateTime->getTimestamp(), $result->getTimestamp()); 75 | $this->assertLessThanOrEqual($toDateTime->getTimestamp(), $result->getTimestamp()); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/FinancialExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->iban(); 12 | 13 | $this->assertEquals(28, strlen($iban)); 14 | $this->assertMatchesRegularExpression('/^[A-Z]{2}/', $iban, 'IBAN does\'t contain a country code'); 15 | $this->assertTrue(Iban::isValid($iban)); 16 | } 17 | 18 | public function testIbanWithCustomCountryCode() 19 | { 20 | $iban = $this->faker->iban(countryCode: 'FR'); 21 | 22 | $this->assertEquals(28, strlen($iban)); 23 | $this->assertStringStartsWith('FR', $iban); 24 | } 25 | 26 | public function testIbanWithAnyFormat() 27 | { 28 | $iban = $this->faker->iban(format: str_repeat('{a}', 10)); 29 | 30 | $this->assertEquals(14, strlen($iban)); 31 | $this->assertMatchesRegularExpression('/^[A-Z0-9]+$/', substr($iban, 4)); 32 | } 33 | 34 | public function testIbanWithLetterFormat() 35 | { 36 | $iban = $this->faker->iban(format: str_repeat('{l}', 10)); 37 | 38 | $this->assertEquals(14, strlen($iban)); 39 | $this->assertMatchesRegularExpression('/^[A-Z]+$/', substr($iban, 4)); 40 | } 41 | 42 | public function testIbanWithDigitFormat() 43 | { 44 | $iban = $this->faker->iban(format: str_repeat('{d}', 10)); 45 | 46 | $this->assertEquals(14, strlen($iban)); 47 | $this->assertMatchesRegularExpression('/^[0-9]+$/', substr($iban, 4)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/HashExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->sha1(); 10 | 11 | $this->assertMatchesRegularExpression('/^[a-z0-9]{40}$/', $result); 12 | } 13 | 14 | public function testSha256(): void 15 | { 16 | $result = $this->faker->sha256(); 17 | 18 | $this->assertMatchesRegularExpression('/^[a-z0-9]{64}$/', $result); 19 | } 20 | 21 | public function testSha512(): void 22 | { 23 | $result = $this->faker->sha512(); 24 | 25 | $this->assertMatchesRegularExpression('/^[a-z0-9]{128}$/', $result); 26 | } 27 | 28 | public function testMd5(): void 29 | { 30 | $result = $this->faker->md5(); 31 | 32 | $this->assertMatchesRegularExpression('/^[a-fA-F0-9]{32}$/', $result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/InternetExtensionTest.php: -------------------------------------------------------------------------------- 1 | tld = (new ReflectionClass($internetExtension))->getProperty('tld')->getValue($internetExtension); 18 | } 19 | 20 | public function testSdl(): void 21 | { 22 | $faker = new Container(false); 23 | 24 | $results = []; 25 | 26 | for ($i = 0; $i < 50; $i++) { 27 | $results[] = $faker->sdl(); 28 | } 29 | 30 | foreach ($results as $result) { 31 | $this->assertTrue(ctype_alnum($result)); 32 | } 33 | } 34 | 35 | public function testTld(): void 36 | { 37 | $faker = new Container(false); 38 | 39 | $results = []; 40 | 41 | for ($i = 0; $i < count($this->tld); $i++) { 42 | $results[] = $faker->unique()->tld(); 43 | } 44 | 45 | $this->assertEqualsCanonicalizing($results, $this->tld); 46 | } 47 | 48 | public function testDomain(): void 49 | { 50 | $faker = new Container(false); 51 | 52 | $results = []; 53 | 54 | for ($i = 0; $i < 50; $i++) { 55 | $results[] = $faker->domain(); 56 | } 57 | 58 | foreach ($results as $result) { 59 | $this->assertMatchesRegularExpression('/^([a-zA-Z0-9-]{1,63}\.)+[a-zA-Z]{2,}$/', $result); 60 | } 61 | } 62 | 63 | public function testIpv4(): void 64 | { 65 | $faker = new Container(false); 66 | 67 | $results = []; 68 | 69 | for ($i = 0; $i < 50; $i++) { 70 | $results[] = $faker->ipv4(); 71 | } 72 | 73 | foreach ($results as $result) { 74 | $this->assertNotFalse(filter_var($result, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)); 75 | } 76 | } 77 | 78 | public function testIpv6(): void 79 | { 80 | $faker = new Container(false); 81 | 82 | $results = []; 83 | 84 | for ($i = 0; $i < 50; $i++) { 85 | $results[] = $faker->ipv6(); 86 | } 87 | 88 | foreach ($results as $result) { 89 | $this->assertNotFalse(filter_var($result, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); 90 | } 91 | } 92 | 93 | public function testMacAddress(): void 94 | { 95 | $faker = new Container(false); 96 | 97 | $results = []; 98 | 99 | for ($i = 0; $i < 50; $i++) { 100 | $results[] = $faker->macAddress(); 101 | } 102 | 103 | foreach ($results as $result) { 104 | $this->assertNotFalse(filter_var($result, FILTER_VALIDATE_MAC)); 105 | } 106 | } 107 | 108 | public function testEmail(): void 109 | { 110 | $faker = new Container(false); 111 | 112 | $results = []; 113 | 114 | for ($i = 0; $i < 100; $i++) { 115 | $results[] = $faker->unique()->email(); 116 | } 117 | 118 | foreach ($results as $result) { 119 | // Email regex according to RFC 5322 Official Standard (reference : https://emailregex.com/) 120 | $this->assertMatchesRegularExpression('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD', $result); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/NumbersExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->unique()->digit(); 14 | } 15 | 16 | $this->assertEqualsCanonicalizing( 17 | range(0, 9), 18 | $results 19 | ); 20 | } 21 | 22 | public function testNumberWithUniqueValues(): void 23 | { 24 | $results = []; 25 | for ($i = 0; $i < 100; $i++) { 26 | $results[] = $this->faker->unique()->number(1, 100); 27 | } 28 | 29 | $this->assertEqualsCanonicalizing( 30 | range(1, 100), 31 | $results 32 | ); 33 | } 34 | 35 | public function testNumberWithRandomValues(): void 36 | { 37 | $results = []; 38 | for ($i = 0; $i < 100; $i++) { 39 | $results[] = $this->faker->number(1, 100); 40 | } 41 | 42 | foreach ($results as $result) { 43 | $this->assertLessThanOrEqual(100, $result); 44 | $this->assertGreaterThanOrEqual(1, $result); 45 | } 46 | } 47 | 48 | public static function floatProvider() 49 | { 50 | return [ 51 | [1.0, 10.0, 2], 52 | [0.0, 1.0, 4], 53 | [-10.0, 10.0, 3], 54 | [5.5, 5.9, 1], 55 | [0.1, 0.2, 6], 56 | [-1000.0, 1000.0, 0], 57 | [3.333, 3.334, 5], 58 | [-50.0, -25.0, 2], 59 | [0.0, 0.1, 1], 60 | [999.9, 1000.0, 1], 61 | [-100.0, 100.0, 1], 62 | [50.0, 100.0, 0], 63 | [0.1234, 0.5678, 4], 64 | [10.5, 10.9, 2], 65 | [-0.1, 0.1, 5], 66 | [-200.0, -100.0, 1], 67 | [0.0001, 0.0002, 6], 68 | [-5.555, 5.555, 3], 69 | [0.0, 100000.0, 0], 70 | [123.456, 789.012, 3], 71 | [-0.0001, 0.0001, 6], 72 | [1.111, 1.119, 4], 73 | [2500.0, 5000.0, 0], 74 | [-0.999, 0.999, 2], 75 | [1234.567, 2345.678, 3], 76 | [-300.3, -100.1, 1], 77 | [0.000001, 0.000009, 7], 78 | [-50.25, -50.1, 2], 79 | [9.99, 10.0, 1], 80 | [10.20, 10.20, 2], 81 | ]; 82 | } 83 | 84 | #[DataProvider('floatProvider')] 85 | public function testFloatWithRandomValues(float $min, float $max, int $decimals): void 86 | { 87 | $result = $this->faker->float($min, $max, $decimals); 88 | 89 | $this->assertLessThanOrEqual($this->numberFormatPrecision($max, $decimals), $result); 90 | $this->assertGreaterThanOrEqual($this->numberFormatPrecision($min, $decimals), $result); 91 | 92 | $formattedResult = number_format($result, $decimals, '.', ''); 93 | $decimalParts = explode('.', $formattedResult); 94 | $decimalCount = isset($decimalParts[1]) ? strlen($decimalParts[1]) : 0; 95 | $this->assertEquals($decimals, $decimalCount); 96 | } 97 | 98 | /** 99 | * Used to cut numbers without round. 100 | * 101 | * @param float $number 102 | * @param int $precision 103 | * @param string $separator 104 | * 105 | * @return string 106 | */ 107 | protected function numberFormatPrecision(float $number, int $precision = 2, string $separator = '.') 108 | { 109 | $numberParts = explode($separator, $number); 110 | $response = $numberParts[0]; 111 | if (count($numberParts) > 1 && $precision > 0) { 112 | $response .= $separator; 113 | $response .= substr($numberParts[1], 0, $precision); 114 | } 115 | 116 | return $response; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/PersonExtensionTest.php: -------------------------------------------------------------------------------- 1 | firstNameMale = (new ReflectionClass($personExtension))->getProperty('firstNameMale')->getValue($personExtension); 22 | $this->firstNameFemale = (new ReflectionClass($personExtension))->getProperty('firstNameFemale')->getValue($personExtension); 23 | $this->lastName = (new ReflectionClass($personExtension))->getProperty('lastName')->getValue($personExtension); 24 | $this->titleMale = (new ReflectionClass($personExtension))->getProperty('titleMale')->getValue($personExtension); 25 | $this->titleFemale = (new ReflectionClass($personExtension))->getProperty('titleFemale')->getValue($personExtension); 26 | } 27 | 28 | public function testFirstNameFemale(): void 29 | { 30 | $results = []; 31 | for ($i = 0; $i < count($this->firstNameFemale); $i++) { 32 | $results[] = $this->faker->unique()->firstName(PersonExtension::GENDER_FEMALE); 33 | } 34 | 35 | $this->assertEqualsCanonicalizing( 36 | $this->firstNameFemale, 37 | $results 38 | ); 39 | } 40 | 41 | public function testFirstNameMale(): void 42 | { 43 | $results = []; 44 | for ($i = 0; $i < count($this->firstNameMale); $i++) { 45 | $results[] = $this->faker->unique()->firstName(PersonExtension::GENDER_MALE); 46 | } 47 | 48 | $this->assertEqualsCanonicalizing( 49 | $this->firstNameMale, 50 | $results 51 | ); 52 | } 53 | 54 | public function testFirstName(): void 55 | { 56 | $results = []; 57 | $firstnames = array_unique(array_merge($this->firstNameMale, $this->firstNameFemale)); 58 | for ($i = 0; $i < count($firstnames); $i++) { 59 | $results[] = $this->faker->unique()->firstName(); 60 | } 61 | 62 | $this->assertEqualsCanonicalizing( 63 | $firstnames, 64 | $results 65 | ); 66 | } 67 | 68 | public function testLastName(): void 69 | { 70 | $results = []; 71 | for ($i = 0; $i < count($this->lastName); $i++) { 72 | $results[] = $this->faker->unique()->lastName(); 73 | } 74 | 75 | $this->assertEqualsCanonicalizing( 76 | $this->lastName, 77 | $results 78 | ); 79 | } 80 | 81 | public function testNameMale(): void 82 | { 83 | $results = []; 84 | for ($i = 0; $i < 100; $i++) { 85 | $results[] = $this->faker->name(PersonExtension::GENDER_MALE); 86 | } 87 | 88 | foreach ($results as $result) { 89 | $matchesFirstName = array_filter($this->firstNameMale, function ($firstName) use ($result) { 90 | return str_contains($result, $firstName); 91 | }); 92 | $this->assertNotEmpty($matchesFirstName, "The first part of the result '{$result}' does not match any first name."); 93 | 94 | $matchesLastName = array_filter($this->lastName, function ($lastName) use ($result) { 95 | return str_contains($result, $lastName); 96 | }); 97 | $this->assertNotEmpty($matchesLastName, "The second part of the result '{$result}' does not match any last name."); 98 | } 99 | } 100 | 101 | public function testNameFemale(): void 102 | { 103 | $results = []; 104 | for ($i = 0; $i < 100; $i++) { 105 | $results[] = $this->faker->name(PersonExtension::GENDER_FEMALE); 106 | } 107 | 108 | foreach ($results as $result) { 109 | $matchesFirstName = array_filter($this->firstNameFemale, function ($firstName) use ($result) { 110 | return str_contains($result, $firstName); 111 | }); 112 | $this->assertNotEmpty($matchesFirstName, "The first part of the result '{$result}' does not match any first name."); 113 | 114 | $matchesLastName = array_filter($this->lastName, function ($lastName) use ($result) { 115 | return str_contains($result, $lastName); 116 | }); 117 | $this->assertNotEmpty($matchesLastName, "The second part of the result '{$result}' does not match any last name."); 118 | } 119 | } 120 | 121 | public function testName(): void 122 | { 123 | $results = []; 124 | for ($i = 0; $i < 100; $i++) { 125 | $results[] = $this->faker->name(); 126 | } 127 | 128 | foreach ($results as $result) { 129 | $matchesFirstName = array_filter(array_merge($this->firstNameFemale, $this->firstNameMale), function ($firstName) use ($result) { 130 | return str_contains($result, $firstName); 131 | }); 132 | $this->assertNotEmpty($matchesFirstName, "The first part of the result '{$result}' does not match any first name."); 133 | 134 | $matchesLastName = array_filter($this->lastName, function ($lastName) use ($result) { 135 | return str_contains($result, $lastName); 136 | }); 137 | $this->assertNotEmpty($matchesLastName, "The second part of the result '{$result}' does not match any last name."); 138 | } 139 | } 140 | 141 | public function testTitleFemale(): void 142 | { 143 | $results = []; 144 | for ($i = 0; $i < count($this->titleFemale); $i++) { 145 | $results[] = $this->faker->unique()->title(PersonExtension::GENDER_FEMALE); 146 | } 147 | 148 | $this->assertEqualsCanonicalizing( 149 | $this->titleFemale, 150 | $results 151 | ); 152 | } 153 | 154 | public function testTitleMale(): void 155 | { 156 | $results = []; 157 | for ($i = 0; $i < count($this->titleMale); $i++) { 158 | $results[] = $this->faker->unique()->title(PersonExtension::GENDER_MALE); 159 | } 160 | 161 | $this->assertEqualsCanonicalizing( 162 | $this->titleMale, 163 | $results 164 | ); 165 | } 166 | 167 | public function testTitle(): void 168 | { 169 | $titles = array_unique(array_merge($this->titleFemale, $this->titleMale)); 170 | 171 | $results = []; 172 | for ($i = 0; $i < count($titles); $i++) { 173 | $results[] = $this->faker->unique()->title(); 174 | } 175 | 176 | $this->assertEqualsCanonicalizing( 177 | $titles, 178 | $results 179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/StringsExtensionTest.php: -------------------------------------------------------------------------------- 1 | faker->unique()->letter(); 12 | } 13 | 14 | $this->assertEqualsCanonicalizing( 15 | range('a', 'z'), 16 | $results 17 | ); 18 | } 19 | 20 | public function testShuffleString(): void 21 | { 22 | $string = '!?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 23 | $result = $this->faker->shuffle($string); 24 | 25 | foreach (str_split($string) as $character) { 26 | $this->assertStringContainsString($character, $result); 27 | } 28 | } 29 | 30 | public function testShuffleArray(): void 31 | { 32 | $array = ['!', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; 33 | $result = $this->faker->shuffle($array); 34 | 35 | foreach ($array as $character) { 36 | $this->assertContains($character, $result); 37 | } 38 | } 39 | 40 | public function testConvertCharacters(): void 41 | { 42 | $result = $this->faker->convertCharacters('NotConverted|#####|?????|*****'); 43 | 44 | $result = explode('|', $result); 45 | 46 | $this->assertEquals('NotConverted', $result[0]); 47 | 48 | $this->assertTrue(ctype_digit($result[1])); 49 | 50 | $this->assertTrue(ctype_alpha($result[2])); 51 | 52 | $this->assertTrue(ctype_alnum($result[3])); 53 | } 54 | 55 | public function testSemVer(): void 56 | { 57 | // From: https://semver.org/spec/v2.0.0.html 58 | $regex = '/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/'; 59 | 60 | $result = $this->faker->semver(); 61 | 62 | $this->assertMatchesRegularExpression( 63 | $regex, 64 | $result 65 | ); 66 | } 67 | 68 | public function testEmoji(): void 69 | { 70 | $result = $this->faker->emoji(); 71 | 72 | $this->assertMatchesRegularExpression('/^\p{So}$/u', $result); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/TestCase.php: -------------------------------------------------------------------------------- 1 | boot(); 17 | 18 | $this->faker = new Container(false); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Unit/Extensions/TextExtensionTest.php: -------------------------------------------------------------------------------- 1 | paragraphs = (new ReflectionClass($testExtension))->getProperty('paragraphs')->getValue($testExtension); 18 | } 19 | 20 | public function testWordsWithDefaultValue(): void 21 | { 22 | $sentences = array_merge(...$this->paragraphs); 23 | $words = array_merge(...$sentences); 24 | $wordsWithoutPunctuationAndLowercased = array_map(function (string $word) { return strtolower(preg_replace('/[.,]/', '', $word)); }, $words); 25 | $result = $this->faker->words(); 26 | 27 | $this->assertCount(3, explode(' ', $result)); 28 | foreach (explode(' ', $result) as $word) { 29 | $this->assertContains($word, $wordsWithoutPunctuationAndLowercased); 30 | } 31 | } 32 | 33 | public static function wordsProvider() 34 | { 35 | return [ 36 | [1], 37 | [2], 38 | [3], 39 | [4], 40 | [5], 41 | [6], 42 | [7], 43 | [8], 44 | [9], 45 | [10], 46 | ]; 47 | } 48 | 49 | #[DataProvider('wordsProvider')] 50 | public function testWords(int $count): void 51 | { 52 | $sentences = array_merge(...$this->paragraphs); 53 | $words = array_merge(...$sentences); 54 | $wordsWithoutPunctuationAndLowercased = array_map(function (string $word) { return strtolower(preg_replace('/[.,]/', '', $word)); }, $words); 55 | $result = $this->faker->unique()->words(words: $count); 56 | 57 | $this->assertCount($count, explode(' ', $result)); 58 | foreach (explode(' ', $result) as $word) { 59 | $this->assertContains($word, $wordsWithoutPunctuationAndLowercased); 60 | } 61 | } 62 | 63 | public function testSentencesWithDefaultValue(): void 64 | { 65 | $sentences = array_merge(...$this->paragraphs); 66 | $words = array_merge(...$sentences); 67 | $result = $this->faker->sentences(); 68 | 69 | $this->assertCount(3, array_filter(explode('.', $result))); 70 | foreach (explode(' ', $result) as $word) { 71 | $this->assertContains($word, $words); 72 | } 73 | } 74 | 75 | public static function sentencesProvider() 76 | { 77 | return [ 78 | [1], 79 | [2], 80 | [3], 81 | [4], 82 | [5], 83 | [6], 84 | [7], 85 | [8], 86 | [9], 87 | [10], 88 | ]; 89 | } 90 | 91 | #[DataProvider('sentencesProvider')] 92 | public function testSentences(int $count): void 93 | { 94 | $sentences = array_merge(...$this->paragraphs); 95 | $words = array_merge(...$sentences); 96 | $result = $this->faker->unique()->sentences(sentences: $count); 97 | 98 | $this->assertCount($count, array_filter(explode('.', $result))); 99 | foreach (explode(' ', $result) as $word) { 100 | $this->assertContains($word, $words); 101 | } 102 | } 103 | 104 | public function testParagraphsWithDefaultValue(): void 105 | { 106 | $sentences = array_merge(...$this->paragraphs); 107 | $words = array_merge(...$sentences); 108 | $result = $this->faker->paragraphs(); 109 | 110 | $this->assertCount(3, array_filter(explode(PHP_EOL, $result))); 111 | foreach (preg_split('/\s+/', $result) as $word) { 112 | $this->assertContains($word, $words); 113 | } 114 | } 115 | 116 | public static function paragraphsProvider() 117 | { 118 | return [ 119 | [1], 120 | [2], 121 | [3], 122 | [4], 123 | [5], 124 | ]; 125 | } 126 | 127 | #[DataProvider('paragraphsProvider')] 128 | public function testParagraphs(int $count): void 129 | { 130 | $sentences = array_merge(...$this->paragraphs); 131 | $words = array_merge(...$sentences); 132 | $result = $this->faker->unique()->paragraphs(paragraphs: $count); 133 | 134 | $this->assertCount($count, array_filter(explode(PHP_EOL, $result))); 135 | foreach (preg_split('/\s+/', $result) as $word) { 136 | $this->assertContains($word, $words); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/Unit/LocaleTest.php: -------------------------------------------------------------------------------- 1 | resolveExtensions([ 10 | \Xefi\Faker\Tests\Support\Extensions\NullLocaleExtensionTest::class, 11 | \Xefi\Faker\Tests\Support\Extensions\EnEnExtensionTest::class, 12 | \Xefi\Faker\Tests\Support\Extensions\EnUsExtensionTest::class, 13 | \Xefi\Faker\Tests\Support\Extensions\FrFrExtensionTest::class, 14 | ]); 15 | } 16 | 17 | public function testExtensionsCorrectlyRegistered() 18 | { 19 | $this->assertEquals( 20 | [ 21 | 'locales' => [ 22 | null => new \Xefi\Faker\Tests\Support\Extensions\NullLocaleExtensionTest(new \Random\Randomizer()), 23 | 'en_EN' => new \Xefi\Faker\Tests\Support\Extensions\EnEnExtensionTest(new \Random\Randomizer()), 24 | 'en_US' => new \Xefi\Faker\Tests\Support\Extensions\EnUsExtensionTest(new \Random\Randomizer()), 25 | 'fr_FR' => new \Xefi\Faker\Tests\Support\Extensions\FrFrExtensionTest(new \Random\Randomizer()), 26 | ], 27 | ], 28 | (new \Xefi\Faker\Container\Container())->getExtensions()['locale-extension-test'] 29 | ); 30 | } 31 | 32 | public function testCallingDefaultExtension() 33 | { 34 | $this->assertEquals( 35 | null, 36 | (new \Xefi\Faker\Faker())->returnLocale() 37 | ); 38 | } 39 | 40 | public function testUsingDefaultLocale() 41 | { 42 | $faker = new Xefi\Faker\Faker('fr_FR'); 43 | $this->assertEquals( 44 | 'fr_FR', 45 | $faker->returnLocale() 46 | ); 47 | 48 | $this->assertEquals( 49 | 'fr_FR', 50 | $faker->locale('fr_FR')->returnLocale() 51 | ); 52 | 53 | $this->assertEquals( 54 | 'en_EN', 55 | $faker->locale('en_EN')->returnLocale() 56 | ); 57 | 58 | $this->assertEquals( 59 | 'fr_FR', 60 | $faker->returnLocale() 61 | ); 62 | 63 | $this->assertEquals( 64 | 'en_US', 65 | $faker->locale('en_US')->returnLocale() 66 | ); 67 | } 68 | 69 | public function testResettingLocale() 70 | { 71 | $faker = new Xefi\Faker\Faker('fr_FR'); 72 | $this->assertEquals( 73 | 'fr_FR', 74 | $faker->returnLocale() 75 | ); 76 | 77 | $this->assertEquals( 78 | null, 79 | $faker->locale(null)->returnLocale() 80 | ); 81 | } 82 | 83 | public function testUsingNotExistingLocaleFallingBackToNullLocale() 84 | { 85 | $faker = new Xefi\Faker\Faker('not-existing-locale'); 86 | $this->assertEquals( 87 | null, 88 | $faker->returnLocale() 89 | ); 90 | } 91 | 92 | public function testUsingNotExistingLocaleWithoutNullLocale() 93 | { 94 | $container = new \Xefi\Faker\Container\Container(); 95 | $container->forgetExtensions(); 96 | $container->forgetBootstrappers(); 97 | $container->resolveExtensions([ 98 | \Xefi\Faker\Tests\Support\Extensions\EnEnExtensionTest::class, 99 | \Xefi\Faker\Tests\Support\Extensions\EnUsExtensionTest::class, 100 | \Xefi\Faker\Tests\Support\Extensions\FrFrExtensionTest::class, 101 | ]); 102 | 103 | $this->expectException(\Xefi\Faker\Exceptions\NoExtensionLocaleFound::class); 104 | $this->expectExceptionMessage('Locale \'not-existing-locale\' and \'null\' for method \'returnLocale\' was not found'); 105 | 106 | $faker = new Xefi\Faker\Faker('not-existing-locale'); 107 | $faker->returnLocale(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/Unit/Modifiers/LowercaseModifierTest.php: -------------------------------------------------------------------------------- 1 | lowercase(); 16 | 17 | $this->assertEquals( 18 | [new LowercaseModifier()], 19 | $container->getModifiers() 20 | ); 21 | } 22 | 23 | public function testLowercaseModifier(): void 24 | { 25 | $faker = new Faker(); 26 | 27 | $this->assertEquals( 28 | 'hello', 29 | $faker->lowercase()->returnHelloUppercase(), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Modifiers/OptionalModifierTest.php: -------------------------------------------------------------------------------- 1 | nullable(); 17 | 18 | $this->assertEquals( 19 | [new NullableModifier(new Randomizer())], 20 | $container->getModifiers() 21 | ); 22 | } 23 | 24 | public function testOptionalModifierWithDefaultValue(): void 25 | { 26 | $faker = new Faker(); 27 | 28 | $this->assertContains( 29 | $faker->nullable()->returnHello(), 30 | ['hello', null] 31 | ); 32 | } 33 | 34 | public function testOptionalModifierWithZeroValue(): void 35 | { 36 | $faker = new Faker(); 37 | 38 | $this->assertEquals( 39 | 'hello', 40 | $faker->nullable(0)->returnHello() 41 | ); 42 | } 43 | 44 | public function testOptionalModifierWithHundredValue(): void 45 | { 46 | $faker = new Faker(); 47 | 48 | $this->assertEquals( 49 | null, 50 | $faker->nullable(100)->returnHello() 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Unit/Modifiers/UppercaseModifierTest.php: -------------------------------------------------------------------------------- 1 | uppercase(); 16 | 17 | $this->assertEquals( 18 | [new UppercaseModifier()], 19 | $container->getModifiers() 20 | ); 21 | } 22 | 23 | public function testUppercaseModifier(): void 24 | { 25 | $faker = new Faker(); 26 | 27 | $this->assertEquals( 28 | 'HELLO', 29 | $faker->uppercase()->returnHello() 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/PackageManifestTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 15 | [ 16 | 'autoload-needed' => [\Xefi\Faker\Tests\Support\TestServiceProvider::class], 17 | ], 18 | $manifest->providers() 19 | ); 20 | $this->assertNotContains(['common-package' => []], $manifest->providers()); 21 | unlink('/tmp/packages.php'); 22 | } 23 | 24 | public function testShouldRecompile() 25 | { 26 | @unlink('/tmp/packages.php'); 27 | $manifest = new PackageManifest(__DIR__.'/../Support', '/tmp/packages.php'); 28 | $manifest->build(); 29 | touch(__DIR__.'/../Support/vendor/composer/installed.json', time() - 1); 30 | 31 | $this->assertFalse($manifest->shouldRecompile()); 32 | 33 | // Test on current time 34 | touch(__DIR__.'/../Support/vendor/composer/installed.json'); 35 | $this->assertTrue($manifest->shouldRecompile()); 36 | 37 | // Test on future 38 | touch(__DIR__.'/../Support/vendor/composer/installed.json', time() + 1); 39 | $this->assertTrue($manifest->shouldRecompile()); 40 | 41 | unlink('/tmp/packages.php'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/ProviderRepositoryTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 14 | $repo->createProvider(\Xefi\Faker\Tests\Support\TestServiceProvider::class), 15 | new \Xefi\Faker\Tests\Support\TestServiceProvider() 16 | ); 17 | } 18 | 19 | public function testLoad() 20 | { 21 | $testServiceProvider = $this->createMock(\Xefi\Faker\Tests\Support\TestServiceProvider::class); 22 | 23 | $testServiceProvider->expects($this->once()) 24 | ->method('boot'); 25 | 26 | $repo = new \Xefi\Faker\Providers\ProviderRepository(); 27 | $repo->load([ 28 | $testServiceProvider, 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Strategies/RegexStrategyTest.php: -------------------------------------------------------------------------------- 1 | regex('//'); 18 | 19 | $this->assertEquals( 20 | [new RegexStrategy('//')], 21 | $container->getStrategies() 22 | ); 23 | } 24 | 25 | public function testUniqueSingleTest(): void 26 | { 27 | $faker = new Faker(); 28 | 29 | $this->assertEquals( 30 | 'hello', 31 | $faker->regex('//')->returnHello() 32 | ); 33 | } 34 | 35 | public function testUniqueThrowExceptionOnMaxRetriesReached(): void 36 | { 37 | $faker = new Faker(); 38 | 39 | $this->expectException(MaximumTriesReached::class); 40 | $this->expectExceptionMessage('Maximum tries of 20000 reached without finding a value'); 41 | 42 | $faker->regex('/^\d$/')->returnHello(); 43 | } 44 | 45 | public static function regexProvider(): array 46 | { 47 | return [ 48 | ['/\d/', 0, 9], 49 | ['/\d+/', 0, 100], 50 | ['/^[1-9]\d*$/', 1, 1000], 51 | ['/^-?\d+$/', -100, 100], 52 | ['/^[0-9]{1,3}$/', 0, 999], 53 | ['/^[1-9][0-9]{2,5}$/', 100, 999999], 54 | ['/^0$/', 0, 0], 55 | ]; 56 | } 57 | 58 | #[DataProvider('regexProvider')] 59 | public function testMultipleRegexCases(string $regex, int $min, int $max): void 60 | { 61 | $faker = new Faker(); 62 | 63 | $result = $faker->regex($regex)->returnNumberBetween($min, $max); 64 | 65 | $this->assertMatchesRegularExpression($regex, (string) $result); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Unit/Strategies/UniqueStrategyTest.php: -------------------------------------------------------------------------------- 1 | unique(); 17 | 18 | $this->assertEquals( 19 | [new UniqueStrategy()], 20 | $container->getStrategies() 21 | ); 22 | } 23 | 24 | public function testUniqueSingleTest(): void 25 | { 26 | $faker = new Faker(); 27 | 28 | $this->assertEquals( 29 | 'hello', 30 | $faker->unique()->returnHello() 31 | ); 32 | } 33 | 34 | public function testUniqueThrowExceptionOnMaxRetriesReached(): void 35 | { 36 | $faker = new Faker(); 37 | 38 | $this->assertEquals( 39 | 'hello', 40 | $faker->unique()->returnHello() 41 | ); 42 | 43 | $this->expectException(MaximumTriesReached::class); 44 | $this->expectExceptionMessage('Maximum tries of 20000 reached without finding a value'); 45 | 46 | $faker->unique()->returnHello(); 47 | } 48 | 49 | public function testUniqueGetAllElements(): void 50 | { 51 | $faker = new Faker(); 52 | 53 | $numbers = []; 54 | for ($i = 0; $i < 10; $i++) { 55 | $numbers[] = $faker->unique()->returnNumberBetween(1, 10); 56 | } 57 | 58 | $this->assertEqualsCanonicalizing(range(1, 10), $numbers); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Unit/TestCase.php: -------------------------------------------------------------------------------- 1 |