├── tests ├── JsonSchema │ ├── dist │ │ └── .gitkeep │ ├── fixture │ │ ├── def │ │ │ ├── null.json │ │ │ ├── boolean.json │ │ │ ├── typeOnly.json │ │ │ ├── ref_inline.json │ │ │ ├── typeAndMaxItems.json │ │ │ └── ref.json │ │ ├── null.json │ │ ├── boolean.json │ │ ├── ref_file_ref.json │ │ ├── ref_file_double.json │ │ ├── ref_file.json │ │ ├── ref_array.json │ │ ├── format.json │ │ ├── array.json │ │ ├── ref_inline.json │ │ ├── integer.json │ │ ├── combining.json │ │ ├── number.json │ │ ├── string.json │ │ └── object.json │ ├── TestCase.php │ ├── FakeJsonsTest.php │ ├── FakerTest.php │ └── HelperTest.php └── index.php ├── .gitignore ├── src ├── Faker │ ├── Factory.php │ └── Generator.php ├── JsonSchema │ ├── InvalidItemsException.php │ ├── UnsupportedTypeException.php │ ├── FakeJsons.php │ ├── Ref.php │ └── Faker.php ├── Provider │ ├── zh_TW │ │ ├── PhoneNumber.php │ │ ├── DateTime.php │ │ ├── Color.php │ │ ├── Person.php │ │ └── Company.php │ ├── zh_CN │ │ ├── Internet.php │ │ ├── Payment.php │ │ ├── PhoneNumber.php │ │ ├── DateTime.php │ │ ├── Color.php │ │ ├── Address.php │ │ └── Company.php │ ├── Company.php │ ├── PhoneNumber.php │ ├── en_US │ │ ├── Payment.php │ │ ├── PhoneNumber.php │ │ └── Address.php │ ├── Uuid.php │ ├── Biased.php │ ├── Barcode.php │ ├── Person.php │ ├── Address.php │ ├── Image.php │ ├── Color.php │ ├── Text.php │ ├── UserAgent.php │ ├── Lorem.php │ ├── HtmlLorem.php │ └── Payment.php ├── DefaultGenerator.php ├── autoload.php ├── Calculator │ ├── Inn.php │ ├── Ean.php │ ├── TCNo.php │ ├── Iban.php │ └── Luhn.php ├── UniqueGenerator.php ├── ValidGenerator.php ├── Factory.php ├── Documentor.php ├── Guesser │ └── Name.php └── Generator.php ├── composer.json └── LICENSE /tests/JsonSchema/dist/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/def/null.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "null" 3 | } -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/null.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "null" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/JsonSchema/dist/* 2 | !tests/JsonSchema/dist/.gitkeep -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/boolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "boolean" 3 | } -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/def/boolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "boolean" 3 | } -------------------------------------------------------------------------------- /src/Faker/Factory.php: -------------------------------------------------------------------------------- 1 | optional(). 8 | */ 9 | class DefaultGenerator 10 | { 11 | protected $default; 12 | 13 | public function __construct($default = null) 14 | { 15 | $this->default = $default; 16 | } 17 | 18 | /** 19 | * @param string $attribute 20 | * 21 | * @return mixed 22 | */ 23 | public function __get($attribute) 24 | { 25 | return $this->default; 26 | } 27 | 28 | /** 29 | * @param string $method 30 | * @param array $attributes 31 | * 32 | * @return mixed 33 | */ 34 | public function __call($method, $attributes) 35 | { 36 | return $this->default; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/format.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "date-time", 5 | "email", 6 | "hostname", 7 | "ipv4", 8 | "ipv6", 9 | "uri" 10 | ], 11 | "properties": { 12 | "date-time": { 13 | "type": "string", 14 | "format": "date-time" 15 | }, 16 | "email": { 17 | "type": "string", 18 | "format": "email" 19 | }, 20 | "hostname": { 21 | "type": "string", 22 | "format": "hostname" 23 | }, 24 | "ipv4": { 25 | "type": "string", 26 | "format": "ipv4" 27 | }, 28 | "ipv6": { 29 | "type": "string", 30 | "format": "ipv6" 31 | }, 32 | "uri": { 33 | "type": "string", 34 | "format": "uri" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/array.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndMinItems", 6 | "typeAndMaxItems", 7 | "typeAndMinItems", 8 | "typeAndMultipleItems" 9 | ], 10 | "properties": { 11 | "typeOnly": { 12 | "type": "array" 13 | }, 14 | "typeAndMaxItems": { 15 | "type": "array", 16 | "maxItems": 10 17 | }, 18 | "typeAndMinItems": { 19 | "type": "array", 20 | "minItems": 10, 21 | "maxItems": 10 22 | }, 23 | "typeAndMultipleItems": { 24 | "type": "array", 25 | "items": [ 26 | { 27 | "type": "integer" 28 | }, 29 | { 30 | "type": "string" 31 | } 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poppy/faker", 3 | "type": "library", 4 | "description": "Poppy Faker is a PHP library that generates fake data for zh user.", 5 | "keywords": [ 6 | "faker", 7 | "fixtures", 8 | "data" 9 | ], 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Mark Zhao" 14 | }, 15 | { 16 | "name": "François Zaninotto" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.2" 21 | }, 22 | "require-dev": { 23 | "ext-intl": "*" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Faker\\": "src/Faker/", 28 | "Poppy\\Faker\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Poppy\\Faker\\Test\\": "tests/" 34 | } 35 | }, 36 | "config": { 37 | "sort-packages": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/JsonSchema/TestCase.php: -------------------------------------------------------------------------------- 1 | invokeArgs($instance, $args); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/autoload.php: -------------------------------------------------------------------------------- 1 | fakeJsons = new FakeJsons(); 20 | } 21 | 22 | public function testInvoke(): void 23 | { 24 | ($this->fakeJsons)(__DIR__ . '/fixture', __DIR__ . '/dist', 'http://example.com/schema'); 25 | $validator = new Validator(); 26 | $data = json_decode((string) file_get_contents(__DIR__ . '/dist/ref_file_double.json')); 27 | $validator->validate($data, (object) ['$ref' => 'file://' . __DIR__ . '/fixture/ref_file_double.json']); 28 | foreach ($validator->getErrors() as $error) { 29 | fwrite(STDOUT, sprintf("[%s] %s\n", $error['property'], $error['message'])); 30 | } 31 | $this->assertTrue($validator->isValid()); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Provider/Company.php: -------------------------------------------------------------------------------- 1 | generator->parse($format); 27 | } 28 | 29 | /** 30 | * @return string 31 | * @example 'Ltd' 32 | * 33 | */ 34 | public static function companySuffix() 35 | { 36 | return static::randomElement(static::$companySuffix); 37 | } 38 | 39 | /** 40 | * @return string 41 | * @example 'Job' 42 | * 43 | */ 44 | public function jobTitle() 45 | { 46 | $format = static::randomElement(static::$jobTitleFormat); 47 | 48 | return $this->generator->parse($format); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Calculator/Inn.php: -------------------------------------------------------------------------------- 1 | 2, 2 => 4, 3 => 10, 4 => 3, 5 => 5, 6 => 9, 7 => 4, 8 => 6, 9 => 8]; 18 | $sum = 0; 19 | for ($i = 1; $i <= 9; $i++) { 20 | $sum += intval(substr($inn, $i - 1, 1)) * $multipliers[$i]; 21 | } 22 | return strval(($sum % 11) % 10); 23 | } 24 | 25 | /** 26 | * Checks whether an INN has a valid checksum 27 | * 28 | * @param string $inn 29 | * @return boolean 30 | */ 31 | public static function isValid($inn) 32 | { 33 | return self::checksum(substr($inn, 0, -1)) === substr($inn, -1, 1); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 duoli 2 | Copyright (c) 2011 François Zaninotto 3 | Portions Copyright (c) 2008 Caius Durling 4 | Portions Copyright (c) 2008 Adam Royle 5 | Portions Copyright (c) 2008 Fiona Burrows 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/Provider/PhoneNumber.php: -------------------------------------------------------------------------------- 1 | generator->parse(static::randomElement(static::$formats))); 17 | } 18 | 19 | /** 20 | * @return string 21 | * @example +27113456789 22 | */ 23 | public function e164PhoneNumber(): string 24 | { 25 | $formats = ['+%############']; 26 | return static::numerify($this->generator->parse(static::randomElement($formats))); 27 | } 28 | 29 | /** 30 | * International Mobile Equipment Identity (IMEI) 31 | * 32 | * @link http://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity 33 | * @link http://imei-number.com/imei-validation-check/ 34 | * @example '720084494799532' 35 | * @return string $imei 36 | */ 37 | public function imei(): string 38 | { 39 | $imei = static::numerify('##############'); 40 | $imei .= Luhn::computeCheckDigit($imei); 41 | return $imei; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/ref_inline.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndMinItems", 6 | "typeAndMaxItems", 7 | "typeAndMinItems", 8 | "typeAndMultipleItems" 9 | ], 10 | "properties": { 11 | "typeOnly": { 12 | "$ref": "#/definitions/typeOnly" 13 | }, 14 | "typeAndMaxItems": { 15 | "$ref": "#/definitions/typeAndMaxItems" 16 | }, 17 | "typeAndMinItems": { 18 | "$ref": "#/definitions/typeAndMinItems" 19 | }, 20 | "typeAndMultipleItems": { 21 | "$ref": "#/definitions/typeAndMultipleItems" 22 | } 23 | }, 24 | "definitions": { 25 | "typeOnly": { 26 | "type": "array" 27 | }, 28 | "typeAndMaxItems": { 29 | "type": "array", 30 | "maxItems": 10 31 | }, 32 | "typeAndMinItems": { 33 | "type": "array", 34 | "minItems": 10, 35 | "maxItems": 10 36 | }, 37 | "typeAndMultipleItems": { 38 | "type": "array", 39 | "items": [ 40 | { 41 | "type": "integer" 42 | }, 43 | { 44 | "type": "string" 45 | } 46 | ] 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/integer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndEnum", 6 | "typeAndMinimum", 7 | "typeAndExcludeMinimum", 8 | "typeAndMaximum", 9 | "typeAndExcludeMinimum", 10 | "typeAndMinimumAndMaximum", 11 | "typeAndMultipleOf" 12 | ], 13 | "properties": { 14 | "typeOnly": { 15 | "type": "integer" 16 | }, 17 | "typeAndEnum": { 18 | "type": "integer", 19 | "enum": [ 20 | -100, 21 | 391, 22 | 2, 23 | 0 24 | ] 25 | }, 26 | "typeAndMinimum": { 27 | "type": "integer", 28 | "minimum": -1 29 | }, 30 | "typeAndExcludeMinimum": { 31 | "type": "integer", 32 | "minimum": -1, 33 | "maximum": 3, 34 | "exclusiveMinimum": true 35 | }, 36 | "typeAndMaximum": { 37 | "type": "integer", 38 | "maximum": 3 39 | }, 40 | "typeAndMinimumAndMaximum": { 41 | "type": "integer", 42 | "minimum": -3010210, 43 | "maximum": 312 44 | }, 45 | "typeAndMultipleOf": { 46 | "type": "integer", 47 | "multipleOf": 3 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Provider/en_US/Payment.php: -------------------------------------------------------------------------------- 1 | format('a') === 'am' ? '上午' : '下午'; 10 | } 11 | 12 | public static function dayOfWeek($max = 'now') 13 | { 14 | $map = [ 15 | 'Sunday' => '星期日', 16 | 'Monday' => '星期一', 17 | 'Tuesday' => '星期二', 18 | 'Wednesday' => '星期三', 19 | 'Thursday' => '星期四', 20 | 'Friday' => '星期五', 21 | 'Saturday' => '星期六', 22 | ]; 23 | $week = static::dateTime($max)->format('l'); 24 | return $map[$week] ?? $week; 25 | } 26 | 27 | public static function monthName($max = 'now') 28 | { 29 | $map = [ 30 | 'January' => '一月', 31 | 'February' => '二月', 32 | 'March' => '三月', 33 | 'April' => '四月', 34 | 'May' => '五月', 35 | 'June' => '六月', 36 | 'July' => '七月', 37 | 'August' => '八月', 38 | 'September' => '九月', 39 | 'October' => '十月', 40 | 'November' => '十一月', 41 | 'December' => '十二月', 42 | ]; 43 | $month = static::dateTime($max)->format('F'); 44 | return $map[$month] ?? $month; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Provider/zh_TW/DateTime.php: -------------------------------------------------------------------------------- 1 | format('a') === 'am' ? '上午' : '下午'; 10 | } 11 | 12 | public static function dayOfWeek($max = 'now') 13 | { 14 | $map = [ 15 | 'Sunday' => '星期日', 16 | 'Monday' => '星期一', 17 | 'Tuesday' => '星期二', 18 | 'Wednesday' => '星期三', 19 | 'Thursday' => '星期四', 20 | 'Friday' => '星期五', 21 | 'Saturday' => '星期六', 22 | ]; 23 | $week = static::dateTime($max)->format('l'); 24 | return $map[$week] ?? $week; 25 | } 26 | 27 | public static function monthName($max = 'now') 28 | { 29 | $map = [ 30 | 'January' => '一月', 31 | 'February' => '二月', 32 | 'March' => '三月', 33 | 'April' => '四月', 34 | 'May' => '五月', 35 | 'June' => '六月', 36 | 'July' => '七月', 37 | 'August' => '八月', 38 | 'September' => '九月', 39 | 'October' => '十月', 40 | 'November' => '十一月', 41 | 'December' => '十二月', 42 | ]; 43 | $month = static::dateTime($max)->format('F'); 44 | return $map[$month] ?? $month; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Calculator/Ean.php: -------------------------------------------------------------------------------- 1 | = 0; $i -= 2) { 30 | $even += (int) $digits[$i]; 31 | } 32 | 33 | $odd = 0; 34 | for ($i = $length - 2; $i >= 0; $i -= 2) { 35 | $odd += (int) $digits[$i]; 36 | } 37 | 38 | return (10 - ((3 * $even + $odd) % 10)) % 10; 39 | } 40 | 41 | /** 42 | * Checks whether the provided number is an EAN compliant number and that 43 | * the checksum is correct. 44 | * 45 | * @param string $ean An EAN number 46 | * @return boolean 47 | */ 48 | public static function isValid(string $ean): bool 49 | { 50 | if (!preg_match(self::PATTERN, $ean)) { 51 | return false; 52 | } 53 | 54 | return self::checksum(substr($ean, 0, -1)) === (int) substr($ean, -1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/number.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndEnum", 6 | "typeAndMinimum", 7 | "typeAndExcludeMinimum", 8 | "typeAndMaximum", 9 | "typeAndExcludeMinimum", 10 | "typeAndMinimumAndMaximum", 11 | "typeAndMultipleOf" 12 | ], 13 | "properties": { 14 | "typeOnly": { 15 | "type": "number" 16 | }, 17 | "typeAndEnum": { 18 | "type": "number", 19 | "enum": [ 20 | -10.0, 21 | 3.91, 22 | 2.1, 23 | -0.1 24 | ] 25 | }, 26 | "typeAndMinimum": { 27 | "type": "number", 28 | "minimum": -1 29 | }, 30 | "typeAndExcludeMinimum": { 31 | "type": "number", 32 | "minimum": -1, 33 | "exclusiveMinimum": true 34 | }, 35 | "typeAndMaximum": { 36 | "type": "number", 37 | "maximum": 3 38 | }, 39 | "typeAndExcludeMinimum": { 40 | "type": "number", 41 | "maximum": 3, 42 | "excludeMinimum": true 43 | }, 44 | "typeAndMinimumAndMaximum": { 45 | "type": "number", 46 | "minimum": -301.0210, 47 | "maximum": 312 48 | }, 49 | "typeAndMultipleOf": { 50 | "type": "number", 51 | "multipleOf": 3 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Calculator/TCNo.php: -------------------------------------------------------------------------------- 1 | $digit) { 29 | if ($index % 2 == 0) { 30 | $evenSum += $digit; 31 | } 32 | else { 33 | $oddSum += $digit; 34 | } 35 | } 36 | 37 | $tenthDigit = (7 * $evenSum - $oddSum) % 10; 38 | $eleventhDigit = ($evenSum + $oddSum + $tenthDigit) % 10; 39 | 40 | return $tenthDigit . $eleventhDigit; 41 | } 42 | 43 | /** 44 | * Checks whether a TCNo has a valid checksum 45 | * 46 | * @param string $tcNo 47 | * @return boolean 48 | */ 49 | public static function isValid($tcNo) 50 | { 51 | return self::checksum(substr($tcNo, 0, -2)) === substr($tcNo, -2, 2); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/UniqueGenerator.php: -------------------------------------------------------------------------------- 1 | unique() 8 | */ 9 | class UniqueGenerator 10 | { 11 | protected $generator; 12 | 13 | protected $maxRetries; 14 | 15 | protected $uniques = []; 16 | 17 | /** 18 | * @param Generator $generator 19 | * @param integer $maxRetries 20 | */ 21 | public function __construct(Generator $generator, $maxRetries = 10000) 22 | { 23 | $this->generator = $generator; 24 | $this->maxRetries = $maxRetries; 25 | } 26 | 27 | /** 28 | * Catch and proxy all generator calls but return only unique values 29 | * @param string $attribute 30 | * @return mixed 31 | */ 32 | public function __get($attribute) 33 | { 34 | return $this->__call($attribute, []); 35 | } 36 | 37 | /** 38 | * Catch and proxy all generator calls with arguments but return only unique values 39 | * @param string $name 40 | * @param array $arguments 41 | * @return mixed 42 | */ 43 | public function __call($name, $arguments) 44 | { 45 | if (!isset($this->uniques[$name])) { 46 | $this->uniques[$name] = []; 47 | } 48 | $i = 0; 49 | do { 50 | $res = call_user_func_array([$this->generator, $name], $arguments); 51 | $i++; 52 | if ($i > $this->maxRetries) { 53 | throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); 54 | } 55 | } while (array_key_exists(serialize($res), $this->uniques[$name])); 56 | $this->uniques[$name][serialize($res)] = null; 57 | 58 | return $res; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Calculator/Iban.php: -------------------------------------------------------------------------------- 1 | > 8) | (($tLo & 0xff000000) >> 24); 34 | $tMi = (($tMi & 0x00ff) << 8) | (($tMi & 0xff00) >> 8); 35 | $tHi = (($tHi & 0x00ff) << 8) | (($tHi & 0xff00) >> 8); 36 | } 37 | 38 | // apply version number 39 | $tHi &= 0x0fff; 40 | $tHi |= (3 << 12); 41 | 42 | // cast to string 43 | return sprintf( 44 | '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x', 45 | $tLo, 46 | $tMi, 47 | $tHi, 48 | $csHi, 49 | $csLo, 50 | $byte[10], 51 | $byte[11], 52 | $byte[12], 53 | $byte[13], 54 | $byte[14], 55 | $byte[15] 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndEnum", 6 | "typeAndMaxLength", 7 | "typeAndMinLength", 8 | "typeAndMinLengthAndMaxLength", 9 | "typeAndPattern", 10 | "typeAndFormat(date-time)", 11 | "typeAndFormat(email)", 12 | "typeAndFormat(hostname)", 13 | "typeAndFormat(ipv4)", 14 | "typeAndFormat(ipv6)", 15 | "typeAndFormat(uri)" 16 | ], 17 | "properties": { 18 | "typeOnly": { 19 | "type": "string" 20 | }, 21 | "typeAndEnum": { 22 | "type": "string", 23 | "enum": [ 24 | "aaa", 25 | "xabc1290あ_!#" 26 | ] 27 | }, 28 | "typeAndMaxLength": { 29 | "type": "string", 30 | "maxLength": 132 31 | }, 32 | "typeAndMinLength": { 33 | "type": "string", 34 | "minLength": 132 35 | }, 36 | "typeAndMinLengthAndMaxLength": { 37 | "type": "string", 38 | "minLength": 132, 39 | "maxLength": 13218 40 | }, 41 | "typeAndPattern": { 42 | "type": "string", 43 | "pattern": "^[0-9]{3}-[0-9]{4}$" 44 | }, 45 | "typeAndFormat(date-time)": { 46 | "type": "string", 47 | "format": "date-time" 48 | }, 49 | "typeAndFormat(email)": { 50 | "type": "string", 51 | "format": "email" 52 | }, 53 | "typeAndFormat(hostname)": { 54 | "type": "string", 55 | "format": "hostname" 56 | }, 57 | "typeAndFormat(ipv4)": { 58 | "type": "string", 59 | "format": "ipv4" 60 | }, 61 | "typeAndFormat(ipv6)": { 62 | "type": "string", 63 | "format": "ipv6" 64 | }, 65 | "typeAndFormat(uri)": { 66 | "type": "string", 67 | "format": "uri" 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Calculator/Luhn.php: -------------------------------------------------------------------------------- 1 | = 0; $i -= 2) { 27 | $sum += $number[$i]; 28 | } 29 | for ($i = $length - 2; $i >= 0; $i -= 2) { 30 | $sum += array_sum(str_split($number[$i] * 2)); 31 | } 32 | 33 | return $sum % 10; 34 | } 35 | 36 | /** 37 | * @param $partialNumber 38 | * @return string 39 | */ 40 | public static function computeCheckDigit($partialNumber) 41 | { 42 | $checkDigit = self::checksum($partialNumber . '0'); 43 | if ($checkDigit === 0) { 44 | return 0; 45 | } 46 | 47 | return (string) (10 - $checkDigit); 48 | } 49 | 50 | /** 51 | * Checks whether a number (partial number + check digit) is Luhn compliant 52 | * 53 | * @param string $number 54 | * @return bool 55 | */ 56 | public static function isValid($number) 57 | { 58 | return self::checksum($number) === 0; 59 | } 60 | 61 | /** 62 | * Generate a Luhn compliant number. 63 | * 64 | * @param string $partialValue 65 | * 66 | * @return string 67 | */ 68 | public static function generateLuhnNumber($partialValue) 69 | { 70 | if (!preg_match('/^\d+$/', $partialValue)) { 71 | throw new InvalidArgumentException('Argument should be an integer.'); 72 | } 73 | return $partialValue . Luhn::computeCheckDigit($partialValue); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Provider/Biased.php: -------------------------------------------------------------------------------- 1 | valid() 11 | */ 12 | class ValidGenerator 13 | { 14 | protected $generator; 15 | 16 | protected $validator; 17 | 18 | protected $maxRetries; 19 | 20 | /** 21 | * @param Generator $generator 22 | * @param callable|null $validator 23 | * @param integer $maxRetries 24 | */ 25 | public function __construct(Generator $generator, $validator = null, $maxRetries = 10000) 26 | { 27 | if (is_null($validator)) { 28 | $validator = function () { 29 | return true; 30 | }; 31 | } 32 | elseif (!is_callable($validator)) { 33 | throw new InvalidArgumentException('valid() only accepts callables as first argument'); 34 | } 35 | $this->generator = $generator; 36 | $this->validator = $validator; 37 | $this->maxRetries = $maxRetries; 38 | } 39 | 40 | /** 41 | * Catch and proxy all generator calls but return only valid values 42 | * @param string $attribute 43 | * 44 | * @return mixed 45 | */ 46 | public function __get($attribute) 47 | { 48 | return $this->__call($attribute, []); 49 | } 50 | 51 | /** 52 | * Catch and proxy all generator calls with arguments but return only valid values 53 | * @param string $name 54 | * @param array $arguments 55 | * 56 | * @return mixed 57 | */ 58 | public function __call($name, $arguments) 59 | { 60 | $i = 0; 61 | do { 62 | $res = call_user_func_array([$this->generator, $name], $arguments); 63 | $i++; 64 | if ($i > $this->maxRetries) { 65 | throw new OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries)); 66 | } 67 | } while (!call_user_func($this->validator, $res)); 68 | 69 | return $res; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | addProvider(new $providerClassName($generator)); 29 | } 30 | 31 | return $generator; 32 | } 33 | 34 | /** 35 | * @param string $provider 36 | * @param string $locale 37 | * @return string 38 | */ 39 | protected static function getProviderClassname($provider, $locale = '') 40 | { 41 | if ($providerClass = self::findProviderClassname($provider, $locale)) { 42 | return $providerClass; 43 | } 44 | // fallback to default locale 45 | if ($providerClass = self::findProviderClassname($provider, static::DEFAULT_LOCALE)) { 46 | return $providerClass; 47 | } 48 | // fallback to no locale 49 | if ($providerClass = self::findProviderClassname($provider)) { 50 | return $providerClass; 51 | } 52 | throw new InvalidArgumentException(sprintf('Unable to find provider "%s" with locale "%s"', $provider, $locale)); 53 | } 54 | 55 | /** 56 | * @param string $provider 57 | * @param string $locale 58 | * @return string 59 | */ 60 | protected static function findProviderClassname($provider, $locale = '') 61 | { 62 | $providerClass = 'Poppy\\Faker\\' . ($locale ? sprintf('Provider\%s\%s', $locale, $provider) : sprintf('Provider\%s', $provider)); 63 | if (class_exists($providerClass)) { 64 | return $providerClass; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/JsonSchema/FakerTest.php: -------------------------------------------------------------------------------- 1 | getFixture($type); 21 | $validator = new Validator(); 22 | 23 | $actual = (new Faker)->generate($schema); 24 | $validator->validate($actual, $schema); 25 | 26 | $this->assertTrue($validator->isValid(), (string) json_encode($validator->getErrors(), JSON_PRETTY_PRINT)); 27 | } 28 | 29 | /** 30 | * @dataProvider getTypesAndFile 31 | */ 32 | public function testFakeFromFile($type) 33 | { 34 | $schema = $this->getFile($type); 35 | $validator = new Validator(); 36 | 37 | $actual = (new Faker)->generate(new SplFileInfo($schema)); 38 | $validator->validate($actual, $schema); 39 | 40 | $this->assertTrue($validator->isValid(), (string) json_encode($validator->getErrors(), JSON_PRETTY_PRINT)); 41 | } 42 | 43 | public function testGenerateInvalidParameter() 44 | { 45 | $this->expectException(InvalidArgumentException::class); 46 | (new Faker)->generate(null); 47 | } 48 | 49 | public function getTypes(): array 50 | { 51 | return [ 52 | ['boolean'], 53 | ['null'], 54 | ['integer'], 55 | ['number'], 56 | ['string'], 57 | ['array'], 58 | ['object'], 59 | ['combining'], 60 | ['ref_inline'], 61 | ]; 62 | } 63 | 64 | public function getTypesAndFile(): array 65 | { 66 | return [ 67 | ['boolean'], 68 | ['null'], 69 | ['integer'], 70 | ['number'], 71 | ['string'], 72 | ['array'], 73 | ['object'], 74 | ['combining'], 75 | ['ref_file'], 76 | ['ref_file_ref'], 77 | ['ref_file_double'], 78 | ['ref_array'], 79 | ]; 80 | } 81 | 82 | public function testFakeMustThrowExceptionIfInvalidType() 83 | { 84 | $this->expectException(UnsupportedTypeException::class); 85 | 86 | (new Faker)->generate((object) ['type' => 'xxxxx']); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/JsonSchema/FakeJsons.php: -------------------------------------------------------------------------------- 1 | files($schemaDir) as $fileInfo) { 23 | /* @var SplFileInfo $fileInfo */ 24 | try { 25 | $fake = $faker->generate($fileInfo); 26 | $schemaFilename = $fileInfo->getFilename(); 27 | if ($fake instanceof stdClass && is_string($schemaUri)) { 28 | $fake->{'$schema'} = sprintf('%s/%s', $schemaUri, $schemaFilename); 29 | } 30 | $targetPath = $distDir . str_replace($schemaDir, '', $fileInfo->getPath()); 31 | if (!file_exists($targetPath)) { 32 | if (!mkdir($targetPath, 0755, true) && !is_dir($targetPath)) { 33 | throw new RuntimeException(sprintf('Directory "%s" was not created', $targetPath)); 34 | } 35 | } 36 | $distFile = sprintf('%s/%s', $targetPath, $schemaFilename); 37 | $fakeJson = json_encode($fake, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL; 38 | fwrite(STDOUT, sprintf("Generate fake file: %s\n", $distFile)); 39 | file_put_contents($distFile, $fakeJson); 40 | } catch (Exception $e) { 41 | fwrite(STDOUT, sprintf("%s: %s %s on line %d\n", $fileInfo->getFilename(), $e->getMessage(), $e->getFile(), $e->getLine())); 42 | continue; 43 | } 44 | } 45 | } 46 | 47 | private function files(string $dir): Iterator 48 | { 49 | return 50 | new RegexIterator( 51 | new RecursiveIteratorIterator( 52 | new RecursiveDirectoryIterator( 53 | $dir, 54 | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS 55 | ), 56 | RecursiveIteratorIterator::LEAVES_ONLY 57 | ), 58 | "/^.+\\.json/", 59 | RegexIterator::MATCH 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Provider/zh_CN/Color.php: -------------------------------------------------------------------------------- 1 | faker = $faker; 27 | $this->schemaDir = $schemaDir; 28 | } 29 | 30 | public function __invoke(stdClass $schema, stdClass $parentSchema = null) 31 | { 32 | $path = (string) $schema->{'$ref'}; 33 | if ($path[0] === '#') { 34 | $parentSchema = $parentSchema instanceof stdClass ? $parentSchema : $schema; 35 | 36 | return $this->inlineRef($parentSchema, $path); 37 | } 38 | 39 | return $this->externalRef($path, $parentSchema); 40 | } 41 | 42 | private function inlineRef(stdClass $parentSchema, string $path) 43 | { 44 | $paths = explode('/', substr($path, 2)); 45 | $prop = $parentSchema; 46 | foreach ($paths as $schemaPath) { 47 | $prop = $prop->{$schemaPath}; 48 | } 49 | 50 | return $this->faker->generate($prop, null); 51 | } 52 | 53 | private function externalRef(string $path, stdClass $parentSchema = null) 54 | { 55 | $jsonFileName = substr($path, 0, 2) === './' ? substr($path, 2) : $path; 56 | $jsonPath = sprintf('%s/%s', $this->schemaDir, $jsonFileName); 57 | $realPath = (string) realpath($jsonPath); 58 | if (is_int(strpos($jsonPath, '#'))) { 59 | return $this->inlineRefInExternalRef($jsonPath); 60 | } 61 | if (!file_exists($jsonPath)) { 62 | throw new RuntimeException("JSON file not exits:{$jsonPath}"); 63 | } 64 | if (!file_exists($realPath)) { 65 | return $this->inlineRefInExternalRef($realPath); 66 | } 67 | 68 | return $this->faker->generate(new SplFileInfo($realPath), $parentSchema, dirname($jsonPath)); 69 | } 70 | 71 | private function inlineRefInExternalRef(string $jsonPath) 72 | { 73 | $paths = explode('#', $jsonPath); 74 | if (count($paths) !== 2) { 75 | throw new RuntimeException("JSON file not exits:{$jsonPath}"); 76 | } 77 | $schemaFile = $paths[0]; 78 | $path = '.' . $paths[1]; 79 | if (!file_exists($schemaFile)) { 80 | throw new RuntimeException("JSON file not exits:{$jsonPath}"); 81 | } 82 | $json = json_decode((string) file_get_contents($schemaFile)); 83 | 84 | return $this->inlineRef($json, $path); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Documentor.php: -------------------------------------------------------------------------------- 1 | generator = $generator; 15 | } 16 | 17 | /** 18 | * @return array 19 | */ 20 | public function getFormatters() 21 | { 22 | $formatters = []; 23 | $providers = array_reverse($this->generator->getProviders()); 24 | $providers[] = new Provider\Base($this->generator); 25 | foreach ($providers as $provider) { 26 | $providerClass = get_class($provider); 27 | $formatters[$providerClass] = []; 28 | $refl = new \ReflectionObject($provider); 29 | foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflmethod) { 30 | if ($reflmethod->getDeclaringClass()->getName() == 'Poppy\Faker\Provider\Base' && $providerClass != 'Poppy\Faker\Provider\Base') { 31 | continue; 32 | } 33 | $methodName = $reflmethod->name; 34 | if ($reflmethod->isConstructor()) { 35 | continue; 36 | } 37 | $parameters = []; 38 | foreach ($reflmethod->getParameters() as $reflparameter) { 39 | $parameter = '$' . $reflparameter->getName(); 40 | if ($reflparameter->isDefaultValueAvailable()) { 41 | $parameter .= ' = ' . var_export($reflparameter->getDefaultValue(), true); 42 | } 43 | $parameters [] = $parameter; 44 | } 45 | $parameters = $parameters ? '(' . join(', ', $parameters) . ')' : ''; 46 | try { 47 | $example = $this->generator->format($methodName); 48 | } catch (\InvalidArgumentException $e) { 49 | $example = ''; 50 | } 51 | if (is_array($example)) { 52 | $example = "array('" . join("', '", $example) . "')"; 53 | } 54 | elseif ($example instanceof \DateTime) { 55 | $example = "DateTime('" . $example->format('Y-m-d H:i:s') . "')"; 56 | } 57 | elseif ($example instanceof Generator || $example instanceof UniqueGenerator) { // modifier 58 | $example = ''; 59 | } 60 | else { 61 | $example = var_export($example, true); 62 | } 63 | $formatters[$providerClass][$methodName . $parameters] = $example; 64 | } 65 | } 66 | 67 | return $formatters; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Provider/Barcode.php: -------------------------------------------------------------------------------- 1 | ean(13); 21 | } 22 | 23 | /** 24 | * Get a random EAN8 barcode. 25 | * @return string 26 | * @example '73513537' 27 | */ 28 | public function ean8() 29 | { 30 | return $this->ean(8); 31 | } 32 | 33 | /** 34 | * Get a random ISBN-10 code 35 | * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number 36 | * 37 | * @return string 38 | * @example '4881416324' 39 | */ 40 | public function isbn10() 41 | { 42 | $code = static::numerify(str_repeat('#', 9)); 43 | 44 | return $code . static::isbnChecksum($code); 45 | } 46 | 47 | /** 48 | * Get a random ISBN-13 code 49 | * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number 50 | * 51 | * @return string 52 | * @example '9790404436093' 53 | */ 54 | public function isbn13() 55 | { 56 | $code = '97' . static::numberBetween(8, 9) . static::numerify(str_repeat('#', 9)); 57 | 58 | return $code . static::eanChecksum($code); 59 | } 60 | 61 | /** 62 | * Utility function for computing EAN checksums 63 | * 64 | * @param string $input 65 | * 66 | * @return integer 67 | */ 68 | protected static function eanChecksum($input) 69 | { 70 | $sequence = (strlen($input) + 1) === 8 ? [3, 1] : [1, 3]; 71 | $sums = 0; 72 | foreach (str_split($input) as $n => $digit) { 73 | $sums += $digit * $sequence[$n % 2]; 74 | } 75 | return (10 - $sums % 10) % 10; 76 | } 77 | 78 | /** 79 | * ISBN-10 check digit 80 | * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digits 81 | * 82 | * @param string $input ISBN without check-digit 83 | * @throws LengthException When wrong input length passed 84 | * 85 | * @return string Check digit 86 | */ 87 | protected static function isbnChecksum($input): string 88 | { 89 | // We're calculating check digit for ISBN-10 90 | // so, the length of the input should be 9 91 | $length = 9; 92 | 93 | if (strlen($input) !== $length) { 94 | throw new LengthException(sprintf('Input length should be equal to %d', $length)); 95 | } 96 | 97 | $digits = str_split($input); 98 | array_walk( 99 | $digits, 100 | function (&$digit, $position) { 101 | $digit = (10 - $position) * $digit; 102 | } 103 | ); 104 | $result = (11 - array_sum($digits) % 11) % 11; 105 | 106 | // 10 is replaced by X 107 | return (string) (($result < 10) ? $result : 'X'); 108 | } 109 | 110 | private function ean($length = 13) 111 | { 112 | $code = static::numerify(str_repeat('#', $length - 1)); 113 | 114 | return $code . static::eanChecksum($code); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Provider/Person.php: -------------------------------------------------------------------------------- 1 | generator->parse($format); 60 | } 61 | 62 | /** 63 | * @param string|null $gender 'male', 'female' or null for any 64 | * @return string 65 | * @example 'John' 66 | */ 67 | public function firstName($gender = null) 68 | { 69 | if ($gender === static::GENDER_MALE) { 70 | return static::firstNameMale(); 71 | } 72 | elseif ($gender === static::GENDER_FEMALE) { 73 | return static::firstNameFemale(); 74 | } 75 | 76 | return $this->generator->parse(static::randomElement(static::$firstNameFormat)); 77 | } 78 | 79 | public static function firstNameMale() 80 | { 81 | return static::randomElement(static::$firstNameMale); 82 | } 83 | 84 | public static function firstNameFemale() 85 | { 86 | return static::randomElement(static::$firstNameFemale); 87 | } 88 | 89 | /** 90 | * @return string 91 | * @example 'Doe' 92 | */ 93 | public function lastName() 94 | { 95 | return static::randomElement(static::$lastName); 96 | } 97 | 98 | /** 99 | * @param string|null $gender 'male', 'female' or null for any 100 | * @return string 101 | * @example 'Mrs.' 102 | */ 103 | public function title($gender = null) 104 | { 105 | if ($gender === static::GENDER_MALE) { 106 | return static::titleMale(); 107 | } 108 | elseif ($gender === static::GENDER_FEMALE) { 109 | return static::titleFemale(); 110 | } 111 | 112 | return $this->generator->parse(static::randomElement(static::$titleFormat)); 113 | } 114 | 115 | /** 116 | * @example 'Mr.' 117 | */ 118 | public static function titleMale() 119 | { 120 | return static::randomElement(static::$titleMale); 121 | } 122 | 123 | /** 124 | * @example 'Mrs.' 125 | */ 126 | public static function titleFemale() 127 | { 128 | return static::randomElement(static::$titleFemale); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Provider/en_US/PhoneNumber.php: -------------------------------------------------------------------------------- 1 | generator->parse($format)); 71 | } 72 | 73 | /** 74 | * NPA-format area code 75 | * 76 | * @see https://en.wikipedia.org/wiki/North_American_Numbering_Plan#Numbering_system 77 | * 78 | * @return string 79 | */ 80 | public static function areaCode() 81 | { 82 | $digits[] = self::numberBetween(2, 9); 83 | $digits[] = self::randomDigit(); 84 | $digits[] = self::randomDigitNot($digits[1]); 85 | 86 | return join('', $digits); 87 | } 88 | 89 | /** 90 | * NXX-format central office exchange code 91 | * 92 | * @see https://en.wikipedia.org/wiki/North_American_Numbering_Plan#Numbering_system 93 | * 94 | * @return string 95 | */ 96 | public static function exchangeCode() 97 | { 98 | $digits[] = self::numberBetween(2, 9); 99 | $digits[] = self::randomDigit(); 100 | 101 | if ($digits[1] === 1) { 102 | $digits[] = self::randomDigitNot(1); 103 | } 104 | else { 105 | $digits[] = self::randomDigit(); 106 | } 107 | 108 | return join('', $digits); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Provider/Address.php: -------------------------------------------------------------------------------- 1 | generator->parse($format); 41 | } 42 | 43 | /** 44 | * @example 'Crist Parks' 45 | */ 46 | public function streetName() 47 | { 48 | $format = static::randomElement(static::$streetNameFormats); 49 | 50 | return $this->generator->parse($format); 51 | } 52 | 53 | /** 54 | * @example '791 Crist Parks' 55 | */ 56 | public function streetAddress() 57 | { 58 | $format = static::randomElement(static::$streetAddressFormats); 59 | 60 | return $this->generator->parse($format); 61 | } 62 | 63 | /** 64 | * @example '791 Crist Parks, Sashabury, IL 86039-9874' 65 | */ 66 | public function address() 67 | { 68 | $format = static::randomElement(static::$addressFormats); 69 | 70 | return $this->generator->parse($format); 71 | } 72 | 73 | /** 74 | * @example 'town' 75 | */ 76 | public static function citySuffix() 77 | { 78 | return static::randomElement(static::$citySuffix); 79 | } 80 | 81 | /** 82 | * @example 'Avenue' 83 | */ 84 | public static function streetSuffix() 85 | { 86 | return static::randomElement(static::$streetSuffix); 87 | } 88 | 89 | /** 90 | * @example '791' 91 | */ 92 | public static function buildingNumber() 93 | { 94 | return static::numerify(static::randomElement(static::$buildingNumber)); 95 | } 96 | 97 | /** 98 | * @example 86039-9874 99 | */ 100 | public static function postcode() 101 | { 102 | return static::toUpper(static::bothify(static::randomElement(static::$postcode))); 103 | } 104 | 105 | /** 106 | * @example 'Japan' 107 | */ 108 | public static function country():string 109 | { 110 | return static::randomElement(static::$country); 111 | } 112 | 113 | /** 114 | * 经纬度范围由于采取的数据是有限的,这里需要给予限制 115 | * @param float|int $min 116 | * @param float|int $max 117 | * @return float Uses signed degrees format (returns a float number between -90 and 90) 118 | * @example '77.147489' 119 | */ 120 | public static function latitude($min = -85.05, $max = 85.05): float 121 | { 122 | return static::randomFloat(6, $min, $max); 123 | } 124 | 125 | /** 126 | * @param float|int $min 127 | * @param float|int $max 128 | * @return float Uses signed degrees format (returns a float number between -180 and 180) 129 | * @example '86.211205' 130 | */ 131 | public static function longitude($min = -180, $max = 180) 132 | { 133 | return static::randomFloat(6, $min, $max); 134 | } 135 | 136 | /** 137 | * @return array [latitude, longitude] 138 | * @example array('77.147489', '86.211205') 139 | */ 140 | public static function localCoordinates() 141 | { 142 | return [ 143 | 'latitude' => static::latitude(), 144 | 'longitude' => static::longitude(), 145 | ]; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Provider/Image.php: -------------------------------------------------------------------------------- 1 | = 100 ? 100 : round($width / 10)); 54 | $url .= 'fs=' . $size . '&'; 55 | } 56 | 57 | return $baseUrl . rtrim($url, '&?'); 58 | } 59 | 60 | /** 61 | * Download a remote random image to disk and return its location 62 | * 63 | * Requires curl, or allow_url_fopen to be on in php.ini. 64 | * 65 | * @param null $dir 66 | * @param int $width 67 | * @param int $height 68 | * @param bool $fullPath 69 | * @return false|RuntimeException|string 70 | * @example '/path/to/dir/13b73edae8443990be1aa8f1a483bc27.jpg' 71 | */ 72 | public static function image($dir = null, $width = 640, $height = 480, $fullPath = true) 73 | { 74 | $dir = is_null($dir) ? sys_get_temp_dir() : $dir; // GNU/Linux / OS X / Windows compatible 75 | // Validate directory path 76 | if (!is_dir($dir) || !is_writable($dir)) { 77 | throw new InvalidArgumentException(sprintf('Cannot write to directory "%s"', $dir)); 78 | } 79 | 80 | // Generate a random filename. Use the server address so that a file 81 | // generated at the same time on a different server won't have a collision. 82 | $name = md5(uniqid(empty($_SERVER['SERVER_ADDR']) ? '' : $_SERVER['SERVER_ADDR'], true)); 83 | $filename = $name . '.jpg'; 84 | $filepath = $dir . DIRECTORY_SEPARATOR . $filename; 85 | 86 | $url = static::imageUrl($width, $height); 87 | 88 | // save file 89 | if (function_exists('curl_exec')) { 90 | // use cURL 91 | $fp = fopen($filepath, 'w'); 92 | $ch = curl_init($url); 93 | curl_setopt($ch, CURLOPT_FILE, $fp); 94 | $success = curl_exec($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200; 95 | fclose($fp); 96 | curl_close($ch); 97 | 98 | if (!$success) { 99 | unlink($filepath); 100 | 101 | // could not contact the distant URL or HTTP error - fail silently. 102 | return false; 103 | } 104 | } 105 | elseif (ini_get('allow_url_fopen')) { 106 | // use remote fopen() via copy() 107 | copy($url, $filepath); 108 | } 109 | else { 110 | return new RuntimeException('The image formatter downloads an image from a remote HTTP server. Therefore, it requires that PHP can request remote hosts, either via cURL or fopen()'); 111 | } 112 | 113 | return $fullPath ? $filepath : $filename; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /tests/JsonSchema/HelperTest.php: -------------------------------------------------------------------------------- 1 | true, 'maximum' => $maximum]; 21 | 22 | $actual = (new Faker)->getMaximum($schema); 23 | 24 | // -1 mean exclusive 25 | $this->assertSame($actual, $maximum - 1); 26 | } 27 | 28 | public function testGetMaximumMustReturnMaximumIfExclusiveMaximumFalse() 29 | { 30 | $maximum = 300; 31 | $schema = (object) ['exclusiveMaximum' => false, 'maximum' => $maximum]; 32 | 33 | $actual = (new Faker)->getMaximum($schema); 34 | 35 | $this->assertSame($actual, $maximum); 36 | } 37 | 38 | public function testGetMaximumMustReturnMaximumIfExclusiveMaximumAbsent() 39 | { 40 | $maximum = 300; 41 | $schema = (object) ['maximum' => $maximum]; 42 | 43 | $actual = (new Faker)->getMaximum($schema); 44 | 45 | $this->assertSame($actual, $maximum); 46 | } 47 | 48 | public function testGetMinimumMustReturnMinimumMinusOneIfExclusiveMinimumTrue() 49 | { 50 | $minimum = 300; 51 | $schema = (object) ['exclusiveMinimum' => true, 'minimum' => $minimum]; 52 | 53 | $actual = (new Faker)->getMinimum($schema); 54 | 55 | // +1 mean exclusive 56 | $this->assertSame($actual, $minimum + 1); 57 | } 58 | 59 | public function testGetMinimumMustReturnMinimumIfExclusiveMinimumFalse() 60 | { 61 | $minimum = 300; 62 | $schema = (object) ['exclusiveMinimum' => false, 'minimum' => $minimum]; 63 | 64 | $actual = (new Faker)->getMinimum($schema); 65 | 66 | $this->assertSame($actual, $minimum); 67 | } 68 | 69 | public function testGetMinimumMustReturnMinimumIfExclusiveMinimumAbsent() 70 | { 71 | $minimum = 300; 72 | $schema = (object) ['minimum' => $minimum]; 73 | 74 | $actual = (new Faker)->getMinimum($schema); 75 | 76 | $this->assertSame($actual, $minimum); 77 | } 78 | 79 | public function testGetMultipleOfMustReturnValueIfPresent() 80 | { 81 | $expected = 7; 82 | $schema = (object) ['multipleOf' => $expected]; 83 | 84 | $actual = (new Faker)->getMultipleOf($schema); 85 | 86 | $this->assertSame($actual, $expected); 87 | } 88 | 89 | public function testGetMultipleOfMustReturnOneIfAbsent() 90 | { 91 | $expected = 1; 92 | $schema = (object) []; 93 | 94 | $actual = (new Faker)->getMultipleOf($schema); 95 | 96 | $this->assertSame($actual, $expected); 97 | } 98 | 99 | public function testGetInternetFakerInstanceMustReturnInstance() 100 | { 101 | $actual = (new Faker)->getInternetFakerInstance(); 102 | 103 | $this->assertTrue($actual instanceof Internet); 104 | } 105 | 106 | /** 107 | * @dataProvider getFormats 108 | */ 109 | public function testGetFormattedValueMustReturnValidValue($format) 110 | { 111 | $schema = (object) ['type' => 'string', 'format' => $format]; 112 | $validator = new Validator(); 113 | 114 | $actual = (new Faker)->getFormattedValue($schema); 115 | $validator->validate($actual, $schema); 116 | 117 | $this->assertTrue($validator->isValid()); 118 | } 119 | 120 | public function testGetFormattedValueMustThrowExceptionIfInvalidFormat() 121 | { 122 | $this->expectException(Exception::class); 123 | 124 | (new Faker)->getFormattedValue((object) ['format' => 'xxxxx']); 125 | } 126 | 127 | /** 128 | * @see testGetFormattedValueMustReturnValidValue 129 | * @SuppressWarnings(PHPMD.UnusedPrivateMethod) 130 | */ 131 | public function getFormats(): array 132 | { 133 | return [ 134 | ['date-time'], 135 | ['email'], 136 | ['hostname'], 137 | ['ipv4'], 138 | ['ipv6'], 139 | ['uri'], 140 | ]; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Provider/Color.php: -------------------------------------------------------------------------------- 1 | city() . static::area(); 140 | } 141 | 142 | public static function postcode() 143 | { 144 | $prefix = str_pad(mt_rand(1, 85), 2, 0, STR_PAD_LEFT); 145 | $suffix = '00'; 146 | 147 | return $prefix . mt_rand(10, 88) . $suffix; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Provider/Text.php: -------------------------------------------------------------------------------- 1 | 5) { 47 | throw new InvalidArgumentException('indexSize must be at most 5'); 48 | } 49 | 50 | $words = $this->getConsecutiveWords($indexSize); 51 | $result = []; 52 | $resultLength = 0; 53 | // take a random starting point 54 | $next = static::randomKey($words); 55 | while ($resultLength < $maxNbChars && isset($words[$next])) { 56 | // fetch a random word to append 57 | $word = static::randomElement($words[$next]); 58 | 59 | // calculate next index 60 | $currentWords = static::explode($next); 61 | $currentWords[] = $word; 62 | array_shift($currentWords); 63 | $next = static::implode($currentWords); 64 | 65 | // ensure text starts with an uppercase letter 66 | if ($resultLength == 0 && !static::validStart($word)) { 67 | continue; 68 | } 69 | 70 | // append the element 71 | $result[] = $word; 72 | $resultLength += static::strlen($word) + static::$separatorLen; 73 | } 74 | 75 | // remove the element that caused the text to overflow 76 | array_pop($result); 77 | 78 | // build result 79 | $result = static::implode($result); 80 | 81 | return static::appendEnd($result); 82 | } 83 | 84 | protected function getConsecutiveWords($indexSize) 85 | { 86 | if (!isset($this->consecutiveWords[$indexSize])) { 87 | $parts = $this->getExplodedText(); 88 | $words = []; 89 | $index = []; 90 | for ($i = 0; $i < $indexSize; $i++) { 91 | $index[] = array_shift($parts); 92 | } 93 | 94 | for ($i = 0, $count = count($parts); $i < $count; $i++) { 95 | $stringIndex = static::implode($index); 96 | if (!isset($words[$stringIndex])) { 97 | $words[$stringIndex] = []; 98 | } 99 | $word = $parts[$i]; 100 | $words[$stringIndex][] = $word; 101 | array_shift($index); 102 | $index[] = $word; 103 | } 104 | // cache look up words for performance 105 | $this->consecutiveWords[$indexSize] = $words; 106 | } 107 | 108 | return $this->consecutiveWords[$indexSize]; 109 | } 110 | 111 | protected function getExplodedText() 112 | { 113 | if ($this->explodedText === null) { 114 | $this->explodedText = static::explode(preg_replace('/\s+/u', ' ', static::$baseText)); 115 | } 116 | 117 | return $this->explodedText; 118 | } 119 | 120 | protected static function explode($text) 121 | { 122 | return explode(static::$separator, $text); 123 | } 124 | 125 | protected static function implode($words) 126 | { 127 | return implode(static::$separator, $words); 128 | } 129 | 130 | protected static function strlen($text) 131 | { 132 | return function_exists('mb_strlen') ? mb_strlen($text, 'UTF-8') : strlen($text); 133 | } 134 | 135 | protected static function validStart($word) 136 | { 137 | $isValid = true; 138 | if (static::$textStartsWithUppercase) { 139 | $isValid = preg_match('/^\p{Lu}/u', $word); 140 | } 141 | return $isValid; 142 | } 143 | 144 | protected static function appendEnd($text) 145 | { 146 | return preg_replace("/([ ,-:;\x{2013}\x{2014}]+$)/us", '', $text) . '.'; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /tests/JsonSchema/fixture/object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "required": [ 4 | "typeOnly", 5 | "typeAndAdditionalPropertiesFalse", 6 | "typeAndAdditionalPropertiesTrue", 7 | "typeAndPatternPropertiesAndAdditionalPropertiesFalse", 8 | "typeAndPatternPropertiesAndAdditionalPropertiesTrue", 9 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObject", 10 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObjectAndMinProperties", 11 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObjectAndMinPropertiesAndRequied", 12 | "deepNested" 13 | ], 14 | "properties": { 15 | "typeOnly": { 16 | "type": "object" 17 | }, 18 | "typeAndMinProperties": { 19 | "type": "object", 20 | "minProperties": 5 21 | }, 22 | "typeAndMaxProperties": { 23 | "type": "object", 24 | "maxProperties": 5 25 | }, 26 | "typeAndMinPropertiesAndMaxProperties": { 27 | "type": "object", 28 | "minProperties": 2, 29 | "maxProperties": 10 30 | }, 31 | "typeAndAdditionalPropertiesFalse": { 32 | "type": "object", 33 | "additionalProperties": false 34 | }, 35 | "typeAndAdditionalPropertiesTrue": { 36 | "type": "object", 37 | "additionalProperties": true 38 | }, 39 | "typeAndPatternPropertiesAndAdditionalPropertiesFalse": { 40 | "type": "object", 41 | "patternProperties": { 42 | "^S_[a-z]{0,20}": { 43 | "type": "string" 44 | }, 45 | "^I_[A-Z]{3}[0-9]{4,8}": { 46 | "type": "integer" 47 | } 48 | }, 49 | "additionalProperties": false 50 | }, 51 | "typeAndPatternPropertiesAndAdditionalPropertiesTrue": { 52 | "type": "object", 53 | "patternProperties": { 54 | "^S_[a-z]{0,20}": { 55 | "type": "string" 56 | }, 57 | "^I_[A-Z]{3}[0-9]{4,8}": { 58 | "type": "integer" 59 | } 60 | }, 61 | "additionalProperties": true 62 | }, 63 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObject": { 64 | "type": "object", 65 | "properties": { 66 | "builtin": { 67 | "type": "number" 68 | } 69 | }, 70 | "patternProperties": { 71 | "^S_[a-z]{0,20}": { 72 | "type": "string" 73 | }, 74 | "^I_[A-Z]{3}[0-9]{4,8}": { 75 | "type": "integer" 76 | } 77 | }, 78 | "additionalProperties": { 79 | "type": "string" 80 | } 81 | }, 82 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObjectAndMinProperties": { 83 | "type": "object", 84 | "minProperties": 3, 85 | "properties": { 86 | "builtin": { 87 | "type": "number" 88 | } 89 | }, 90 | "patternProperties": { 91 | "^S_[a-z]{0,20}": { 92 | "type": "string" 93 | }, 94 | "^I_[A-Z]{3}[0-9]{4,8}": { 95 | "type": "integer" 96 | } 97 | }, 98 | "additionalProperties": { 99 | "type": "string" 100 | } 101 | }, 102 | "typeAndPropertiesAndPatternPropertiesAndAdditionalPropertiesObjectAndMinPropertiesAndRequied": { 103 | "type": "object", 104 | "minProperties": 3, 105 | "required": [ 106 | "builtin" 107 | ], 108 | "properties": { 109 | "builtin": { 110 | "type": "number" 111 | } 112 | }, 113 | "patternProperties": { 114 | "^S_[a-z]{0,20}": { 115 | "type": "string" 116 | }, 117 | "^I_[A-Z]{3}[0-9]{4,8}": { 118 | "type": "integer" 119 | } 120 | }, 121 | "additionalProperties": { 122 | "type": "string" 123 | } 124 | }, 125 | "deepNested": { 126 | "type": "object", 127 | "required": [ 128 | "first" 129 | ], 130 | "properties": { 131 | "first": { 132 | "type": "object", 133 | "required": [ 134 | "second" 135 | ], 136 | "properties": { 137 | "second": { 138 | "type": "object", 139 | "required": [ 140 | "third" 141 | ], 142 | "properties": { 143 | "third": { 144 | "type": "object" 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Guesser/Name.php: -------------------------------------------------------------------------------- 1 | generator = $generator; 18 | } 19 | 20 | /** 21 | * @param string $name 22 | * @param int|null $size Length of field, if known 23 | * @return callable 24 | */ 25 | public function guessFormat($name, $size = null) 26 | { 27 | $name = Base::toLower($name); 28 | $generator = $this->generator; 29 | if (preg_match('/^is[_A-Z]/', $name)) { 30 | return function () use ($generator) { 31 | return $generator->boolean; 32 | }; 33 | } 34 | if (preg_match('/(_a|A)t$/', $name)) { 35 | return function () use ($generator) { 36 | return $generator->dateTime; 37 | }; 38 | } 39 | switch (str_replace('_', '', $name)) { 40 | case 'firstname': 41 | return function () use ($generator) { 42 | return $generator->firstName; 43 | }; 44 | case 'lastname': 45 | return function () use ($generator) { 46 | return $generator->lastName; 47 | }; 48 | case 'username': 49 | case 'login': 50 | return function () use ($generator) { 51 | return $generator->userName; 52 | }; 53 | case 'email': 54 | case 'emailaddress': 55 | return function () use ($generator) { 56 | return $generator->email; 57 | }; 58 | case 'phonenumber': 59 | case 'phone': 60 | case 'telephone': 61 | case 'telnumber': 62 | return function () use ($generator) { 63 | return $generator->phoneNumber; 64 | }; 65 | case 'address': 66 | return function () use ($generator) { 67 | return $generator->address; 68 | }; 69 | case 'city': 70 | case 'town': 71 | return function () use ($generator) { 72 | return $generator->city; 73 | }; 74 | case 'streetaddress': 75 | return function () use ($generator) { 76 | return $generator->streetAddress; 77 | }; 78 | case 'postcode': 79 | case 'zipcode': 80 | return function () use ($generator) { 81 | return $generator->postcode; 82 | }; 83 | case 'state': 84 | return function () use ($generator) { 85 | return $generator->state; 86 | }; 87 | case 'county': 88 | if ($this->generator->locale == 'en_US') { 89 | return function () use ($generator) { 90 | return sprintf('%s County', $generator->city); 91 | }; 92 | } 93 | 94 | return function () use ($generator) { 95 | return $generator->state; 96 | }; 97 | case 'country': 98 | switch ($size) { 99 | case 2: 100 | return function () use ($generator) { 101 | return $generator->countryCode; 102 | }; 103 | case 3: 104 | return function () use ($generator) { 105 | return $generator->countryISOAlpha3; 106 | }; 107 | case 5: 108 | case 6: 109 | return function () use ($generator) { 110 | return $generator->locale; 111 | }; 112 | default: 113 | return function () use ($generator) { 114 | return $generator->country; 115 | }; 116 | } 117 | case 'locale': 118 | return function () use ($generator) { 119 | return $generator->locale; 120 | }; 121 | case 'currency': 122 | case 'currencycode': 123 | return function () use ($generator) { 124 | return $generator->currencyCode; 125 | }; 126 | case 'url': 127 | case 'website': 128 | return function () use ($generator) { 129 | return $generator->url; 130 | }; 131 | case 'company': 132 | case 'companyname': 133 | case 'employer': 134 | return function () use ($generator) { 135 | return $generator->company; 136 | }; 137 | case 'title': 138 | if ($size !== null && $size <= 10) { 139 | return function () use ($generator) { 140 | return $generator->title; 141 | }; 142 | } 143 | 144 | return function () use ($generator) { 145 | return $generator->sentence; 146 | }; 147 | case 'body': 148 | case 'summary': 149 | case 'article': 150 | case 'description': 151 | return function () use ($generator) { 152 | return $generator->text; 153 | }; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Provider/zh_CN/Company.php: -------------------------------------------------------------------------------- 1 | 10, 12 | 'B' => 11, 13 | 'C' => 12, 14 | 'D' => 13, 15 | 'E' => 14, 16 | 'F' => 15, 17 | 'G' => 16, 18 | 'H' => 17, 19 | 'I' => 34, 20 | 'J' => 18, 21 | 'K' => 19, 22 | 'M' => 21, 23 | 'N' => 22, 24 | 'O' => 35, 25 | 'P' => 23, 26 | 'Q' => 24, 27 | 'T' => 27, 28 | 'U' => 28, 29 | 'V' => 29, 30 | 'W' => 32, 31 | 'X' => 30, 32 | 'Z' => 33, 33 | ]; 34 | 35 | /** 36 | * @see https://zh.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E5%9C%8B%E6%B0%91%E8%BA%AB%E5%88%86%E8%AD%89 37 | */ 38 | public static array $idDigitValidator = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1]; 39 | 40 | protected static array $maleNameFormats = [ 41 | '{{lastName}}{{firstNameMale}}', 42 | ]; 43 | 44 | protected static array $femaleNameFormats = [ 45 | '{{lastName}}{{firstNameFemale}}', 46 | ]; 47 | 48 | protected static array $titleMale = ['先生', '博士', '教授']; 49 | 50 | protected static array $titleFemale = ['小姐', '太太', '博士', '教授']; 51 | 52 | /** 53 | * @link http://zh.wikipedia.org/wiki/%E7%99%BE%E5%AE%B6%E5%A7%93 54 | */ 55 | protected static array $lastName = [ 56 | '趙', '錢', '孫', '李', '周', '吳', '鄭', '王', '馮', 57 | '陳', '褚', '衛', '蔣', '沈', '韓', '楊', '朱', '秦', 58 | '尤', '許', '何', '呂', '施', '張', '孔', '曹', '嚴', 59 | '華', '金', '魏', '陶', '姜', '戚', '謝', '鄒', '喻', 60 | '柏', '水', '竇', '章', '雲', '蘇', '潘', '葛', 61 | '奚', '范', '彭', '郎', '魯', '韋', '昌', '馬', 62 | '苗', '鳳', '花', '方', '俞', '任', '袁', '柳', 63 | '酆', '鮑', '史', '唐', '費', '廉', '岑', '薛', 64 | '雷', '賀', '倪', '湯', '滕', '殷', '羅', '畢', 65 | '郝', '鄔', '安', '常', '樂', '于', '時', '傅', 66 | '皮', '卞', '齊', '康', '伍', '余', '元', '卜', 67 | '顧', '孟', '平', '黃', '和', '穆', '蕭', '尹', 68 | '姚', '邵', '湛', '汪', '祁', '毛', '禹', '狄', 69 | '米', '貝', '明', '臧', '計', '伏', '成', '戴', 70 | '談', '宋', '茅', '龐', '熊', '紀', '舒', '屈', 71 | '項', '祝', '董', '梁', '杜', '阮', '藍', '閔', 72 | '席', '季', '麻', '強', '賈', '路', '婁', '危', 73 | '江', '童', '顏', '郭', '梅', '盛', '林', '刁', 74 | '鍾', '徐', '丘', '駱', '高', '夏', '蔡', '田', 75 | '樊', '胡', '凌', '霍', '虞', '萬', '支', '柯', 76 | '昝', '管', '盧', '莫', '經', '房', '裘', '繆', 77 | '干', '解', '應', '宗', '丁', '宣', '賁', '鄧', 78 | '郁', '單', '杭', '洪', '包', '諸', '左', '石', 79 | '崔', '吉', '鈕', '龔', '程', '嵇', '邢', '滑', 80 | '裴', '陸', '榮', '翁', '荀', '羊', '於', '惠', 81 | '甄', '麴', '家', '封', '芮', '羿', '儲', '靳', 82 | '汲', '邴', '糜', '松', '井', '段', '富', '巫', 83 | '烏', '焦', '巴', '弓', '牧', '隗', '山', '谷', 84 | '車', '侯', '宓', '蓬', '全', '郗', '班', '仰', 85 | '秋', '仲', '伊', '宮', '甯', '仇', '欒', '暴', 86 | '甘', '鈄', '厲', '戎', '祖', '武', '符', '劉', 87 | '景', '詹', '束', '龍', '葉', '幸', '司', '韶', 88 | '郜', '黎', '薊', '薄', '印', '宿', '白', '懷', 89 | '蒲', '邰', '從', '鄂', '索', '咸', '籍', '賴', 90 | '卓', '藺', '屠', '蒙', '池', '喬', '陰', '鬱', 91 | '胥', '能', '蒼', '雙', '聞', '莘', '黨', '翟', 92 | '譚', '貢', '勞', '逄', '姬', '申', '扶', '堵', 93 | '冉', '宰', '酈', '雍', '郤', '璩', '桑', '桂', 94 | '濮', '牛', '壽', '通', '邊', '扈', '燕', '冀', 95 | '郟', '浦', '尚', '農', '溫', '別', '莊', '晏', 96 | '柴', '瞿', '閻', '充', '慕', '連', '茹', '習', 97 | '宦', '艾', '魚', '容', '向', '古', '易', '慎', 98 | '戈', '廖', '庾', '終', '暨', '居', '衡', '步', 99 | '都', '耿', '滿', '弘', '匡', '國', '文', '寇', 100 | '廣', '祿', '闕', '東', '歐', '殳', '沃', '利', 101 | '蔚', '越', '夔', '隆', '師', '鞏', '厙', '聶', 102 | '晁', '勾', '敖', '融', '冷', '訾', '辛', '闞', 103 | '那', '簡', '饒', '空', '曾', '毋', '沙', '乜', 104 | '養', '鞠', '須', '豐', '巢', '關', '蒯', '相', 105 | '查', '后', '荊', '紅', '游', '竺', '權', '逯', 106 | '蓋', '益', '桓', '公', '万俟', '司馬', '上官', 107 | '歐陽', '夏侯', '諸葛', '聞人', '東方', '赫連', 108 | '皇甫', '尉遲', '公羊', '澹臺', '公冶', '宗政', 109 | '濮陽', '淳于', '單于', '太叔', '申屠', '公孫', 110 | '仲孫', '軒轅', '令狐', '鍾離', '宇文', '長孫', 111 | '慕容', '鮮于', '閭丘', '司徒', '司空', '亓官', 112 | '司寇', '仉', '督', '子車', '顓孫', '端木', '巫馬', 113 | '公西', '漆雕', '樂正', '壤駟', '公良', '拓跋', 114 | '夾谷', '宰父', '穀梁', '晉', '楚', '閆', '法', 115 | '汝', '鄢', '涂', '欽', '段干', '百里', '東郭', 116 | '南門', '呼延', '歸', '海', '羊舌', '微生', '岳', 117 | '帥', '緱', '亢', '況', '後', '有', '琴', '梁丘', 118 | '左丘', '東門', '西門', '商', '牟', '佘', '佴', 119 | '伯', '賞', '南宮', '墨', '哈', '譙', '笪', '年', 120 | '愛', '陽', '佟', '第五', '言', '福', 121 | ]; 122 | 123 | /** 124 | * @link http://technology.chtsai.org/namefreq/ 125 | */ 126 | protected static $characterMale = [ 127 | '佳', '俊', '信', '偉', '傑', '冠', '君', '哲', 128 | '嘉', '威', '宇', '安', '宏', '宗', '宜', '家', 129 | '庭', '廷', '建', '彥', '心', '志', '思', '承', 130 | '文', '柏', '樺', '瑋', '穎', '美', '翰', '華', 131 | '詩', '豪', '賢', '軒', '銘', '霖', 132 | ]; 133 | 134 | protected static $characterFemale = [ 135 | '伶', '佩', '佳', '依', '儀', '冠', '君', '嘉', 136 | '如', '娟', '婉', '婷', '安', '宜', '家', '庭', 137 | '心', '思', '怡', '惠', '慧', '文', '欣', '涵', 138 | '淑', '玲', '珊', '琪', '琬', '瑜', '穎', '筑', 139 | '筱', '美', '芬', '芳', '華', '萍', '萱', '蓉', 140 | '詩', '貞', '郁', '鈺', '雅', '雯', '靜', '馨', 141 | ]; 142 | 143 | public static function randomName($pool, $n) 144 | { 145 | $name = ''; 146 | for ($i = 0; $i < $n; ++$i) { 147 | $name .= static::randomElement($pool); 148 | } 149 | return $name; 150 | } 151 | 152 | public static function firstNameMale() 153 | { 154 | return static::randomName(static::$characterMale, mt_rand(1, 2)); 155 | } 156 | 157 | public static function firstNameFemale() 158 | { 159 | return static::randomName(static::$characterFemale, mt_rand(1, 2)); 160 | } 161 | 162 | public static function suffix() 163 | { 164 | return ''; 165 | } 166 | 167 | /** 168 | * @param string $gender Person::GENDER_MALE || Person::GENDER_FEMALE 169 | * 170 | * @return string Length 10 alphanumeric characters, begins with 1 latin character (birthplace), 171 | * 1 number (gender) and then 8 numbers (the last one is check digit). 172 | * @see https://en.wikipedia.org/wiki/National_Identification_Card_(Republic_of_China) 173 | * 174 | */ 175 | public function personalIdentityNumber($gender = null) 176 | { 177 | $birthPlace = self::randomKey(self::$idBirthplaceCode); 178 | $birthPlaceCode = self::$idBirthplaceCode[$birthPlace]; 179 | 180 | $gender = ($gender != null) ? $gender : self::randomElement([self::GENDER_FEMALE, self::GENDER_MALE]); 181 | $genderCode = ($gender === self::GENDER_MALE) ? 1 : 2; 182 | 183 | $randomNumberCode = self::randomNumber(7, true); 184 | 185 | $codes = str_split($birthPlaceCode . $genderCode . $randomNumberCode); 186 | $total = 0; 187 | 188 | foreach ($codes as $key => $code) { 189 | $total += $code * self::$idDigitValidator[$key]; 190 | } 191 | 192 | $checkSumDigit = 10 - ($total % 10); 193 | 194 | if ($checkSumDigit == 10) { 195 | $checkSumDigit = 0; 196 | } 197 | 198 | return $birthPlace . $genderCode . $randomNumberCode . $checkSumDigit; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/Provider/zh_TW/Company.php: -------------------------------------------------------------------------------- 1 | generator->parse($format); 231 | } 232 | 233 | public static function companyModifier() 234 | { 235 | return static::randomElement(static::$companyModifier); 236 | } 237 | 238 | public static function companyPrefix() 239 | { 240 | return static::randomElement(static::$companyPrefix); 241 | } 242 | 243 | public function catchPhrase() 244 | { 245 | return static::randomElement(static::$catchPhrase); 246 | } 247 | 248 | public function bs() 249 | { 250 | $result = ''; 251 | foreach (static::$bsWords as &$word) { 252 | $result .= static::randomElement($word); 253 | } 254 | return $result; 255 | } 256 | 257 | /** 258 | * return standard VAT / Tax ID / Uniform Serial Number 259 | * 260 | * @return int 261 | * @example 28263822 262 | * 263 | */ 264 | public function VAT() 265 | { 266 | return static::randomNumber(8, true); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/Provider/Lorem.php: -------------------------------------------------------------------------------- 1 | 24 | {$desc}{$codeHl}{$content}"; 25 | } 26 | 27 | } 28 | 29 | $md = isset($_GET['md']); 30 | 31 | /** 32 | * @param $title 33 | * @param $items 34 | * @param bool $md 35 | */ 36 | function py_example($title, $items, $md = true) 37 | { 38 | if ($md) { 39 | echo PHP_EOL . PHP_EOL . '### ' . $title . PHP_EOL . PHP_EOL; 40 | } 41 | else { 42 | echo "