commit f12a0a9b3812afb95c7c7bdb45fc3976cae0c0ff Author: Ron Rise Date: Wed Nov 1 18:13:06 2023 -0400 This changes nothing, don't look diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7bd3bb5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +stages: + - deploy + +deploy: + stage: deploy + image: alpine:latest + only: + - tags + tags: + - build + script: + - apk add curl + - 'curl -iL --header "Job-Token: $CI_JOB_TOKEN" --data tag=${CI_COMMIT_TAG} "${CI_API_V4_URL}/projects/$CI_PROJECT_ID/packages/composer"' + environment: production diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d27a7be --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,744 @@ +# CHANGELOG + +## 2019-12-03, v1.9.1 + +- Add link to PHPStan extension to readme [\#1834](https://github.com/fzaninotto/Faker/pull/1834) ([finwe](https://github.com/finwe)) +- Enhancement: Collect code coverage [\#1824](https://github.com/fzaninotto/Faker/pull/1824) ([localheinz](https://github.com/localheinz)) +- Enhancement: Use all columns when running tests [\#1823](https://github.com/fzaninotto/Faker/pull/1823) ([localheinz](https://github.com/localheinz)) +- Enhancement: Configure verbose output via phpunit.xml.dist [\#1822](https://github.com/fzaninotto/Faker/pull/1822) ([localheinz](https://github.com/localheinz)) +- Curly braces for arrays is deprecated in PHP 7.4 [\#1843](https://github.com/fzaninotto/Faker/pull/1843) ([wimg](https://github.com/wimg)) +- Fix: Reduce visibility of setUp\(\) and tearDown\(\) [\#1821](https://github.com/fzaninotto/Faker/pull/1821) ([localheinz](https://github.com/localheinz)) +- Fix: Mark test classes as final [\#1820](https://github.com/fzaninotto/Faker/pull/1820) ([localheinz](https://github.com/localheinz)) +- Fix: Remove unnecessary class-level DocBlocks [\#1819](https://github.com/fzaninotto/Faker/pull/1819) ([localheinz](https://github.com/localheinz)) + +## 2019-11-10, v1.9.0 + +This will the last minor release in the `1.x` cycle. + +- Add all Iran's provinces land lines numbers [\#1806](https://github.com/fzaninotto/Faker/pull/1806) ([kingofnull](https://github.com/kingofnull)) +- replace latin "B" to cyrillic "B" for uk\_UA locale [\#1800](https://github.com/fzaninotto/Faker/pull/1800) ([FI-LIFE](https://github.com/FI-LIFE)) +- Add elgentos/masquerade to third-party library list [\#1798](https://github.com/fzaninotto/Faker/pull/1798) ([erikhansen](https://github.com/erikhansen)) +- Add link to Gravatar provider repository [\#1796](https://github.com/fzaninotto/Faker/pull/1796) ([ottaviano](https://github.com/ottaviano)) +- Add mobileNumber\(\) method for nb\_NO [\#1788](https://github.com/fzaninotto/Faker/pull/1788) ([carestad](https://github.com/carestad)) +- Enhancement: Slightly clean up Company provider [\#1783](https://github.com/fzaninotto/Faker/pull/1783) ([localheinz](https://github.com/localheinz)) +- Added some more information for autoloading in the readme [\#1780](https://github.com/fzaninotto/Faker/pull/1780) ([pimjansen](https://github.com/pimjansen)) +- Enhancement: Add issue template for bug report [\#1779](https://github.com/fzaninotto/Faker/pull/1779) ([localheinz](https://github.com/localheinz)) +- Add hslColor to ColorProvider [\#1776](https://github.com/fzaninotto/Faker/pull/1776) ([icanhazstring](https://github.com/icanhazstring)) +- Enhancement: Consistently use composer package name for link content [\#1770](https://github.com/fzaninotto/Faker/pull/1770) ([localheinz](https://github.com/localheinz)) +- Add Correct Thai words for streets,soi etc names and add Thai First a… [\#1769](https://github.com/fzaninotto/Faker/pull/1769) ([pierrejoye](https://github.com/pierrejoye)) +- Enhancement: Assert that postcode generated by Austrian Address provider matches format [\#1766](https://github.com/fzaninotto/Faker/pull/1766) ([localheinz](https://github.com/localheinz)) +- Enhancement: Update squizlabs/php\_codesniffer [\#1763](https://github.com/fzaninotto/Faker/pull/1763) ([localheinz](https://github.com/localheinz)) +- Enhancement: Assert that imageUrl can be used with gray flag [\#1762](https://github.com/fzaninotto/Faker/pull/1762) ([localheinz](https://github.com/localheinz)) +- Enhancement: Introduce build and help targets [\#1761](https://github.com/fzaninotto/Faker/pull/1761) ([localheinz](https://github.com/localheinz)) +- Enhancement: Disable Xdebug as early as possible [\#1758](https://github.com/fzaninotto/Faker/pull/1758) ([localheinz](https://github.com/localheinz)) +- Enhancement: Clean up .gitattributes [\#1756](https://github.com/fzaninotto/Faker/pull/1756) ([localheinz](https://github.com/localheinz)) +- Enhancement: Reference phpunit.xsd as installed with composer [\#1755](https://github.com/fzaninotto/Faker/pull/1755) ([localheinz](https://github.com/localheinz)) +- add id\_ID Color [\#1754](https://github.com/fzaninotto/Faker/pull/1754) ([cacing69](https://github.com/cacing69)) +- PHP 7.4 compatibility [\#1748](https://github.com/fzaninotto/Faker/pull/1748) ([pimjansen](https://github.com/pimjansen)) +- Add lastName gender specific on ru\_RU locale [\#1747](https://github.com/fzaninotto/Faker/pull/1747) ([aanfarhan](https://github.com/aanfarhan)) +- Update README - Add a new faker. [\#1731](https://github.com/fzaninotto/Faker/pull/1731) ([drupol](https://github.com/drupol)) +- Add faker-eddy-malou third-party library [\#1717](https://github.com/fzaninotto/Faker/pull/1717) ([Metrakit](https://github.com/Metrakit)) +- \[de\_DE\] Company\JobTitle: list of common german job titles [\#1716](https://github.com/fzaninotto/Faker/pull/1716) ([amacado](https://github.com/amacado)) +- Added 'red' to $safeColorNames [\#1701](https://github.com/fzaninotto/Faker/pull/1701) ([xfudox](https://github.com/xfudox)) +- \[pt\_PT/Address\] Add abbreviature to 'travessa' and 'largo' [\#1606](https://github.com/fzaninotto/Faker/pull/1606) ([simaolemos](https://github.com/simaolemos)) +- Fix method name for en\_NZ provider in readme [\#1595](https://github.com/fzaninotto/Faker/pull/1595) ([gdhnz](https://github.com/gdhnz)) +- Refactoring of the TextTest class [\#1590](https://github.com/fzaninotto/Faker/pull/1590) ([stevegrunwell](https://github.com/stevegrunwell)) +- add gender variants for last name \[lt\_LT\] [\#1586](https://github.com/fzaninotto/Faker/pull/1586) ([ikerasLT](https://github.com/ikerasLT)) +- add .id TLD [\#1566](https://github.com/fzaninotto/Faker/pull/1566) ([ad3n](https://github.com/ad3n)) +- Add Color provider to es\_ES locale [\#1559](https://github.com/fzaninotto/Faker/pull/1559) ([juananruiz](https://github.com/juananruiz)) +- Enhancement: Keep packages sorted in composer.json [\#1551](https://github.com/fzaninotto/Faker/pull/1551) ([localheinz](https://github.com/localheinz)) +- Portuguese providers for Companies and Internet [\#1537](https://github.com/fzaninotto/Faker/pull/1537) ([promatik](https://github.com/promatik)) +- Improve Dutch company names [\#1529](https://github.com/fzaninotto/Faker/pull/1529) ([koole](https://github.com/koole)) +- Document randomDigitNot base formatter. [\#1514](https://github.com/fzaninotto/Faker/pull/1514) ([shawnlindstrom](https://github.com/shawnlindstrom)) +- Estonian names [\#1461](https://github.com/fzaninotto/Faker/pull/1461) ([much-rebel](https://github.com/much-rebel)) +- add birthplace support for ID nik generator [\#1451](https://github.com/fzaninotto/Faker/pull/1451) ([mikk150](https://github.com/mikk150)) +- List of banks in Brazil added [\#1427](https://github.com/fzaninotto/Faker/pull/1427) ([diegohenicka](https://github.com/diegohenicka)) +- Remove last names from the male first names array \(et\_EE\) [\#1799](https://github.com/fzaninotto/Faker/pull/1799) ([Mihkel100](https://github.com/Mihkel100)) +- Update out-of-date currency codes [\#1795](https://github.com/fzaninotto/Faker/pull/1795) ([Brindster](https://github.com/Brindster)) +- Floor is not always returning an int [\#1790](https://github.com/fzaninotto/Faker/pull/1790) ([pimjansen](https://github.com/pimjansen)) +- typo [\#1789](https://github.com/fzaninotto/Faker/pull/1789) ([enumag](https://github.com/enumag)) +- Added batch inserts for doctrine orm populate [\#1781](https://github.com/fzaninotto/Faker/pull/1781) ([pimjansen](https://github.com/pimjansen)) +- FIx: Avoid deprecation warning in newer CakePHP version [\#1777](https://github.com/fzaninotto/Faker/pull/1777) ([icanhazstring](https://github.com/icanhazstring)) +- Fix: Consistently link to GitHub repositories [\#1774](https://github.com/fzaninotto/Faker/pull/1774) ([localheinz](https://github.com/localheinz)) +- Fix: Name of package [\#1772](https://github.com/fzaninotto/Faker/pull/1772) ([localheinz](https://github.com/localheinz)) +- Remove BV and HM country codes. [\#1767](https://github.com/fzaninotto/Faker/pull/1767) ([oliverpool](https://github.com/oliverpool)) +- Fix: Remove sudo configuration [\#1760](https://github.com/fzaninotto/Faker/pull/1760) ([localheinz](https://github.com/localheinz)) +- Fix: Allow nightly builds to fail [\#1759](https://github.com/fzaninotto/Faker/pull/1759) ([localheinz](https://github.com/localheinz)) +- Fix: No need to update composer itself [\#1757](https://github.com/fzaninotto/Faker/pull/1757) ([localheinz](https://github.com/localheinz)) +- duplicate first name and last name removal [\#1729](https://github.com/fzaninotto/Faker/pull/1729) ([sagautam5](https://github.com/sagautam5)) +- Fix-Isreal--Israel [\#1724](https://github.com/fzaninotto/Faker/pull/1724) ([GitEvil](https://github.com/GitEvil)) +- fix\(issue-1721\): add phpdoc method and properties annotations for php… [\#1722](https://github.com/fzaninotto/Faker/pull/1722) ([lugus](https://github.com/lugus)) +- Typo in function PHPDoc [\#1711](https://github.com/fzaninotto/Faker/pull/1711) ([Dzhuneyt](https://github.com/Dzhuneyt)) +- Fix: Add allowed type DateTime to param annotation [\#1697](https://github.com/fzaninotto/Faker/pull/1697) ([localheinz](https://github.com/localheinz)) +- Calling a non static method as static not allowed. [\#1696](https://github.com/fzaninotto/Faker/pull/1696) ([wilco-1985](https://github.com/wilco-1985)) +- Bug Fix en\_SG mobile number generation [\#1658](https://github.com/fzaninotto/Faker/pull/1658) ([ziming](https://github.com/ziming)) +- Update ja\_JP Address Provider [\#1649](https://github.com/fzaninotto/Faker/pull/1649) ([atmngw](https://github.com/atmngw)) +- Fixed realText on ja\_JP locale broken, /u missing [\#1634](https://github.com/fzaninotto/Faker/pull/1634) ([mihit](https://github.com/mihit)) +- Removed comma and space from $firstNameMale for en\_IN [\#1608](https://github.com/fzaninotto/Faker/pull/1608) ([phpspider](https://github.com/phpspider)) +- Prevent Transliterator class autoloading [\#1575](https://github.com/fzaninotto/Faker/pull/1575) ([MontealegreLuis](https://github.com/MontealegreLuis)) +- building numbers in Germany never start with a zero or are zero [\#1530](https://github.com/fzaninotto/Faker/pull/1530) ([fostam](https://github.com/fostam)) +- Fixing sk\_SK men last names set [\#1459](https://github.com/fzaninotto/Faker/pull/1459) ([pavoltanuska](https://github.com/pavoltanuska)) +- Taiwan ID. The initial letter must be a capital letter. p-\>P [\#1434](https://github.com/fzaninotto/Faker/pull/1434) ([slawa-dev](https://github.com/slawa-dev)) +- Revert "Added 'red' to $safeColorNames" [\#1778](https://github.com/fzaninotto/Faker/pull/1778) ([fzaninotto](https://github.com/fzaninotto)) +- Added realText capability for fr\_CA [\#1686](https://github.com/fzaninotto/Faker/pull/1686) ([bobanum](https://github.com/bobanum)) +- Add loremflickr.com Provider to the thrid parties [\#1683](https://github.com/fzaninotto/Faker/pull/1683) ([xvladxtremal](https://github.com/xvladxtremal)) +- Add Undeclared Faker Instance to PhoneNumber Test in en\_NG [\#1682](https://github.com/fzaninotto/Faker/pull/1682) ([thearsalan](https://github.com/thearsalan)) +- PSR-2 updates to readme [\#1679](https://github.com/fzaninotto/Faker/pull/1679) ([b3none](https://github.com/b3none)) +- PHPDoc for `passthrough` function on Generator [\#1678](https://github.com/fzaninotto/Faker/pull/1678) ([tabakhase](https://github.com/tabakhase)) +- Make en\_CA Phone Provider Extend the en\_US One [\#1676](https://github.com/fzaninotto/Faker/pull/1676) ([darrylhein](https://github.com/darrylhein)) +- Add Valid National Code Generator to fa\_IR Person [\#1675](https://github.com/fzaninotto/Faker/pull/1675) ([thearsalan](https://github.com/thearsalan)) +- Updated readme.md: added a missing backslash [\#1673](https://github.com/fzaninotto/Faker/pull/1673) ([aubryfr](https://github.com/aubryfr)) +- Add 'Faker\Provider\en\_US\Company::catchPhrase' to ru\_RU locale [\#1668](https://github.com/fzaninotto/Faker/pull/1668) ([axklim](https://github.com/axklim)) +- Update deprecated \PHPUnit\_Framework\_TestCase with PHPUnit\Framework\TestCase [\#1664](https://github.com/fzaninotto/Faker/pull/1664) ([thewhit](https://github.com/thewhit)) +- Support populating Doctrine's immutable date types [\#1639](https://github.com/fzaninotto/Faker/pull/1639) ([voronkovich](https://github.com/voronkovich)) +- add titleMale and titleFemale method to all Person classes [\#1635](https://github.com/fzaninotto/Faker/pull/1635) ([RiverDanceGit](https://github.com/RiverDanceGit)) +- Minor typo fix [\#1631](https://github.com/fzaninotto/Faker/pull/1631) ([nhedger](https://github.com/nhedger)) +- Add link to er1z/fakemock [\#1622](https://github.com/fzaninotto/Faker/pull/1622) ([er1z](https://github.com/er1z)) +- Add Dutch \(nl\_NL\) job titles [\#1618](https://github.com/fzaninotto/Faker/pull/1618) ([SjorsO](https://github.com/SjorsO)) +- Exclude files and folder from release archive [\#1617](https://github.com/fzaninotto/Faker/pull/1617) ([ankurk91](https://github.com/ankurk91)) +- Update doc block [\#1615](https://github.com/fzaninotto/Faker/pull/1615) ([ankurk91](https://github.com/ankurk91)) +- Suggest PSR-4 compliant autoloader in comment [\#1614](https://github.com/fzaninotto/Faker/pull/1614) ([samnela](https://github.com/samnela)) +- Fix dateTimeBetween\(\) Argument on Generator phpdoc [\#1583](https://github.com/fzaninotto/Faker/pull/1583) ([hisomura](https://github.com/hisomura)) +- Fix Dutch typo [\#1580](https://github.com/fzaninotto/Faker/pull/1580) ([gizburdt](https://github.com/gizburdt)) +- Old city names changed to new ones [\#1578](https://github.com/fzaninotto/Faker/pull/1578) ([goszowski](https://github.com/goszowski)) +- Correct documentation [\#1571](https://github.com/fzaninotto/Faker/pull/1571) ([rquadling](https://github.com/rquadling)) +- Make $suffix protected not private in nl\_NL\Person provider [\#1561](https://github.com/fzaninotto/Faker/pull/1561) ([SpadXIII](https://github.com/SpadXIII)) +- Add PicsumPhotos Provider to the thrid parties [\#1560](https://github.com/fzaninotto/Faker/pull/1560) ([bluemmb](https://github.com/bluemmb)) +- fix austrian postal codes starting with invalid 0 [\#1545](https://github.com/fzaninotto/Faker/pull/1545) ([mpge](https://github.com/mpge)) +- Restore a random seed when the Generator is destroyed [\#1534](https://github.com/fzaninotto/Faker/pull/1534) ([lyrixx](https://github.com/lyrixx)) +- Add a swiss social security number \(AVS13\) generator [\#1533](https://github.com/fzaninotto/Faker/pull/1533) ([nhedger](https://github.com/nhedger)) +- Small tweaks [\#1526](https://github.com/fzaninotto/Faker/pull/1526) ([carusogabriel](https://github.com/carusogabriel)) +- fix ukraine first name person, delete space [\#1525](https://github.com/fzaninotto/Faker/pull/1525) ([Yasuslik](https://github.com/Yasuslik)) +- Removed unknown postcode [\#1440](https://github.com/fzaninotto/Faker/pull/1440) ([ayanozturk](https://github.com/ayanozturk)) +- Add more PhoneNumber options for es\_ES [\#1219](https://github.com/fzaninotto/Faker/pull/1219) ([driade](https://github.com/driade)) +- Fix duplication in uk\_UA male first names [\#1214](https://github.com/fzaninotto/Faker/pull/1214) ([fre5h](https://github.com/fre5h)) +- Added missing grayscale option/flag to Image provider [\#928](https://github.com/fzaninotto/Faker/pull/928) ([sebastianvilla](https://github.com/sebastianvilla)) + +## 2018-07-12, v1.8.0 + +- Typo in readme [\#1521](https://github.com/fzaninotto/Faker/pull/1521) ([jmhobbs](https://github.com/jmhobbs)) +- Replaced Hilll with Hill [\#1516](https://github.com/fzaninotto/Faker/pull/1516) ([MarkVaughn](https://github.com/MarkVaughn)) +- \[it\_IT\] Improve vat ID generated using official rules [\#1508](https://github.com/fzaninotto/Faker/pull/1508) ([mavimo](https://github.com/mavimo)) +- \[hu\_HU\] Address: Fix unnecessary new line in string [\#1507](https://github.com/fzaninotto/Faker/pull/1507) ([ntomka](https://github.com/ntomka)) +- add phone numer format [\#1506](https://github.com/fzaninotto/Faker/pull/1506) ([Enosh-Yu](https://github.com/Enosh-Yu)) +- Fix typo in fr\_CA Provider [\#1505](https://github.com/fzaninotto/Faker/pull/1505) ([ultreson](https://github.com/ultreson)) +- Add fake-car provider link [\#1497](https://github.com/fzaninotto/Faker/pull/1497) ([pelmered](https://github.com/pelmered)) +- create `passthrough` function [\#1493](https://github.com/fzaninotto/Faker/pull/1493) ([browner12](https://github.com/browner12)) +- update Polish bank list [\#1482](https://github.com/fzaninotto/Faker/pull/1482) ([IonBazan](https://github.com/IonBazan)) +- Update the parameters to check if the setter is callable [\#1470](https://github.com/fzaninotto/Faker/pull/1470) ([rossmitchell](https://github.com/rossmitchell)) +- Push the max date far into the future so the test can pass [\#1469](https://github.com/fzaninotto/Faker/pull/1469) ([rossmitchell](https://github.com/rossmitchell)) +- Update Address.php [\#1465](https://github.com/fzaninotto/Faker/pull/1465) ([Saibamen](https://github.com/Saibamen)) +- Turkish identity number for tr\_TR [\#1462](https://github.com/fzaninotto/Faker/pull/1462) ([aykutaras](https://github.com/aykutaras)) +- Fixing rare iin with 13-digits. [\#1450](https://github.com/fzaninotto/Faker/pull/1450) ([vadimonus](https://github.com/vadimonus)) +- Fix Polish PESEL faker [\#1449](https://github.com/fzaninotto/Faker/pull/1449) ([Dartui](https://github.com/Dartui)) +- Adds valid 08 number formats for fr\_FR [\#1439](https://github.com/fzaninotto/Faker/pull/1439) ([ppelgrims](https://github.com/ppelgrims)) +- Add YouTube provider link [\#1422](https://github.com/fzaninotto/Faker/pull/1422) ([aalaap](https://github.com/aalaap)) +- Update PHPDoc of the DateTime provider. [\#1419](https://github.com/fzaninotto/Faker/pull/1419) ([tomzx](https://github.com/tomzx)) +- Normalize name of variable [\#1412](https://github.com/fzaninotto/Faker/pull/1412) ([eaglewu](https://github.com/eaglewu)) +- Added "blockchain" to en-us company provider catchPhrase method [\#1411](https://github.com/fzaninotto/Faker/pull/1411) ([samoldenburg](https://github.com/samoldenburg)) +- Fix for Spot2 ORM EntityPopulator [\#1408](https://github.com/fzaninotto/Faker/pull/1408) ([michal-borek](https://github.com/michal-borek)) +- TH color name [\#1404](https://github.com/fzaninotto/Faker/pull/1404) ([Naruedom](https://github.com/Naruedom)) +- added Malaysia \[ms\_MY\] locale [\#1403](https://github.com/fzaninotto/Faker/pull/1403) ([kenfai](https://github.com/kenfai)) +- Implementation of the function that generates Brazilian area codes fixed. [\#1401](https://github.com/fzaninotto/Faker/pull/1401) ([jackmiras](https://github.com/jackmiras)) +- VISA retired the 13 digit PAN moved to new cardParams [\#1400](https://github.com/fzaninotto/Faker/pull/1400) ([hppycoder](https://github.com/hppycoder)) +- Remove unused variable inside closure [\#1395](https://github.com/fzaninotto/Faker/pull/1395) ([carusogabriel](https://github.com/carusogabriel)) +- .nz domain updates [\#1393](https://github.com/fzaninotto/Faker/pull/1393) ([xurizaemon](https://github.com/xurizaemon)) +- Add licenceCode method in the to es\_ES person provider [\#1392](https://github.com/fzaninotto/Faker/pull/1392) ([ffiguereo](https://github.com/ffiguereo)) +- allow `randomElements` to accept a Traversable object [\#1389](https://github.com/fzaninotto/Faker/pull/1389) ([browner12](https://github.com/browner12)) +- Doc: rg remove formatting [\#1387](https://github.com/fzaninotto/Faker/pull/1387) ([emtudo](https://github.com/emtudo)) +- Add numbers with start 4 [\#1386](https://github.com/fzaninotto/Faker/pull/1386) ([emtudo](https://github.com/emtudo)) +- update th\_TH mobile number format [\#1385](https://github.com/fzaninotto/Faker/pull/1385) ([earthpyy](https://github.com/earthpyy)) +- Translate country names for lv\_LV provider. [\#1383](https://github.com/fzaninotto/Faker/pull/1383) ([ronaldsgailis](https://github.com/ronaldsgailis)) +- Clean elses [\#1382](https://github.com/fzaninotto/Faker/pull/1382) ([carusogabriel](https://github.com/carusogabriel)) +- French vat formatter [\#1381](https://github.com/fzaninotto/Faker/pull/1381) ([ppelgrims](https://github.com/ppelgrims)) +- Replaces rtrim with preg\_replace [\#1380](https://github.com/fzaninotto/Faker/pull/1380) ([ppelgrims](https://github.com/ppelgrims)) +- Refactoring tests [\#1375](https://github.com/fzaninotto/Faker/pull/1375) ([carusogabriel](https://github.com/carusogabriel)) +- Added link in readme to provider FakerRestaurant [\#1374](https://github.com/fzaninotto/Faker/pull/1374) ([jzonta](https://github.com/jzonta)) +- Remove obsolete currency codes [\#1373](https://github.com/fzaninotto/Faker/pull/1373) ([tpraxl](https://github.com/tpraxl)) +- \[ru\_RU\] Updated countries and added source link [\#1372](https://github.com/fzaninotto/Faker/pull/1372) ([ilyahoilik](https://github.com/ilyahoilik)) +- Test against PHP 7.2 [\#1371](https://github.com/fzaninotto/Faker/pull/1371) ([carusogabriel](https://github.com/carusogabriel)) +- Feature: nl\_BE text provider [\#1370](https://github.com/fzaninotto/Faker/pull/1370) ([rauwebieten](https://github.com/rauwebieten)) +- default value for Payment::iban\(\) country code [\#1369](https://github.com/fzaninotto/Faker/pull/1369) ([madmanmax](https://github.com/madmanmax)) +- skip test failing on bigendian [\#1365](https://github.com/fzaninotto/Faker/pull/1365) ([remicollet](https://github.com/remicollet)) +- Update Person.php [\#1364](https://github.com/fzaninotto/Faker/pull/1364) ([majamusan](https://github.com/majamusan)) +- Prevent errors on private methods [\#1363](https://github.com/fzaninotto/Faker/pull/1363) ([petecoop](https://github.com/petecoop)) +- adds rijksregisternummer [\#1361](https://github.com/fzaninotto/Faker/pull/1361) ([ppelgrims](https://github.com/ppelgrims)) +- Add secondary address to fr\_FR provider [\#1356](https://github.com/fzaninotto/Faker/pull/1356) ([nicodmf](https://github.com/nicodmf)) +- Add company provider for tr\_TR [\#1355](https://github.com/fzaninotto/Faker/pull/1355) ([yuks](https://github.com/yuks)) +- nb\_NO provider updates [\#1350](https://github.com/fzaninotto/Faker/pull/1350) ([alexqhj](https://github.com/alexqhj)) +- only test available date range on 32-bit [\#1348](https://github.com/fzaninotto/Faker/pull/1348) ([remicollet](https://github.com/remicollet)) +- Bump PHPUnit version for namespace compatibility [\#1345](https://github.com/fzaninotto/Faker/pull/1345) ([carusogabriel](https://github.com/carusogabriel)) +- Use PSR-1 for PHPUnit TestCase [\#1344](https://github.com/fzaninotto/Faker/pull/1344) ([carusogabriel](https://github.com/carusogabriel)) +- Fix FR\_fr 07 prefix mobile number generation [\#1343](https://github.com/fzaninotto/Faker/pull/1343) ([svanpoeck](https://github.com/svanpoeck)) +- Update Text.php [\#1339](https://github.com/fzaninotto/Faker/pull/1339) ([gulaandrij](https://github.com/gulaandrij)) +- Add two new company type in the Swiss Provider [\#1336](https://github.com/fzaninotto/Faker/pull/1336) ([pvullioud](https://github.com/pvullioud)) +- Change symbol 'minus' with code 226 to 'minus' with code 45 [\#1333](https://github.com/fzaninotto/Faker/pull/1333) ([Negasus](https://github.com/Negasus)) +- \[sl\_SI\] Created provider for Company [\#1331](https://github.com/fzaninotto/Faker/pull/1331) ([alesvaupotic](https://github.com/alesvaupotic)) +- Update city name [\#1328](https://github.com/fzaninotto/Faker/pull/1328) ([s9801077](https://github.com/s9801077)) +- Fix \#1305 realText in some cases breaks last character [\#1326](https://github.com/fzaninotto/Faker/pull/1326) ([iamraccoon](https://github.com/iamraccoon)) +- Real Dutch postal codes [\#1323](https://github.com/fzaninotto/Faker/pull/1323) ([ametad](https://github.com/ametad)) +- Added male and female titles for the en\_ZA locale [\#1321](https://github.com/fzaninotto/Faker/pull/1321) ([ViGouRCanberra](https://github.com/ViGouRCanberra)) +- Add German Email Providers [\#1320](https://github.com/fzaninotto/Faker/pull/1320) ([Stoffo](https://github.com/Stoffo)) +- Fix "Resource temporarily unavailable" [\#1319](https://github.com/fzaninotto/Faker/pull/1319) ([eberkund](https://github.com/eberkund)) +- Introduced the ability to specify a default timezone... [\#1316](https://github.com/fzaninotto/Faker/pull/1316) ([telkins](https://github.com/telkins)) +- South African licence codes [\#1315](https://github.com/fzaninotto/Faker/pull/1315) ([royalmitten](https://github.com/royalmitten)) +- Fix with incorrect name city. [\#1309](https://github.com/fzaninotto/Faker/pull/1309) ([zzenmate](https://github.com/zzenmate)) +- Fixed type-o in readme under section about Language specific formatters [\#1302](https://github.com/fzaninotto/Faker/pull/1302) ([espenkn](https://github.com/espenkn)) +- Update Person.php [\#1298](https://github.com/fzaninotto/Faker/pull/1298) ([yappkahowe](https://github.com/yappkahowe)) +- Allow children classes to access self::$suffix [\#1296](https://github.com/fzaninotto/Faker/pull/1296) ([greg0ire](https://github.com/greg0ire)) +- Fix with namespace payment provider for uk\_UA [\#1293](https://github.com/fzaninotto/Faker/pull/1293) ([zzenmate](https://github.com/zzenmate)) +- Update zh\_TW text provider [\#1292](https://github.com/fzaninotto/Faker/pull/1292) ([s9801077](https://github.com/s9801077)) +- Fix CURL status code in ImageTest.php [\#1290](https://github.com/fzaninotto/Faker/pull/1290) ([Sanfra1407](https://github.com/Sanfra1407)) +- Tax Id for companies and new formats for es\_VE [\#1287](https://github.com/fzaninotto/Faker/pull/1287) ([DIOHz0r](https://github.com/DIOHz0r)) +- Added idNumber for nl\_NL [\#1283](https://github.com/fzaninotto/Faker/pull/1283) ([artorozenga](https://github.com/artorozenga)) +- Feature/en us company ein [\#1273](https://github.com/fzaninotto/Faker/pull/1273) ([zachflower](https://github.com/zachflower)) + +## 2017-08-15, v1.7.0 + +- Added more Ukrainian banks [\#1271](https://github.com/fzaninotto/Faker/pull/1271) ([iamraccoon](https://github.com/iamraccoon)) +- Hotfix/failing unit tests [\#1269](https://github.com/fzaninotto/Faker/pull/1269) ([zachflower](https://github.com/zachflower)) +- Lock Travis-CI environment to Ubuntu Precise [\#1268](https://github.com/fzaninotto/Faker/pull/1268) ([zachflower](https://github.com/zachflower)) +- Added Ukrainian job title [\#1267](https://github.com/fzaninotto/Faker/pull/1267) ([iamraccoon](https://github.com/iamraccoon)) +- Add compliant en\_US SSN generator [\#1266](https://github.com/fzaninotto/Faker/pull/1266) ([zachflower](https://github.com/zachflower)) +- Added more Ukrainian streets and removed irrelevant names. Added more Ukrainian mobile formats [\#1265](https://github.com/fzaninotto/Faker/pull/1265) ([iamraccoon](https://github.com/iamraccoon)) +- Add Internet Format for ja\_JP. [\#1260](https://github.com/fzaninotto/Faker/pull/1260) ([itigoppo](https://github.com/itigoppo)) +- rectify ISO 4217 codes [\#1258](https://github.com/fzaninotto/Faker/pull/1258) ([eidng8](https://github.com/eidng8)) +- Corrected of grammar of Ukrainian middlenames and test added [\#1257](https://github.com/fzaninotto/Faker/pull/1257) ([vladbuk](https://github.com/vladbuk)) +- Update ISO 4217 active codes [\#1251](https://github.com/fzaninotto/Faker/pull/1251) ([eidng8](https://github.com/eidng8)) +- Update Composer File [\#1248](https://github.com/fzaninotto/Faker/pull/1248) ([vinkla](https://github.com/vinkla)) +- Set capitals to false [\#1243](https://github.com/fzaninotto/Faker/pull/1243) ([Stichoza](https://github.com/Stichoza)) +- Use static instead of self [\#1242](https://github.com/fzaninotto/Faker/pull/1242) ([Stichoza](https://github.com/Stichoza)) +- Add VAT french format [\#1241](https://github.com/fzaninotto/Faker/pull/1241) ([baptistedonaux](https://github.com/baptistedonaux)) +- Add swedish job titles [\#1234](https://github.com/fzaninotto/Faker/pull/1234) ([vinkla](https://github.com/vinkla)) +- Name Simo shouldn't have comma in it [\#1230](https://github.com/fzaninotto/Faker/pull/1230) ([simoheinonen](https://github.com/simoheinonen)) +- Fix: Add method annotation for ValidGenerator [\#1223](https://github.com/fzaninotto/Faker/pull/1223) ([localheinz](https://github.com/localheinz)) +- Add real text for es\_ES [\#1220](https://github.com/fzaninotto/Faker/pull/1220) ([driade](https://github.com/driade)) +- Fix spelling errors [\#1218](https://github.com/fzaninotto/Faker/pull/1218) ([driade](https://github.com/driade)) +- Fix spelling errors [\#1217](https://github.com/fzaninotto/Faker/pull/1217) ([driade](https://github.com/driade)) +- Fixes typo [\#1212](https://github.com/fzaninotto/Faker/pull/1212) ([skullboner](https://github.com/skullboner)) +- Add Person::middleName for ru\_RU provider [\#1209](https://github.com/fzaninotto/Faker/pull/1209) ([JustBlackBird](https://github.com/JustBlackBird)) +- Fix creditCardDetails type hint [\#1208](https://github.com/fzaninotto/Faker/pull/1208) ([jejung](https://github.com/jejung)) +- Expand dictionaries for ru\_RU locale [\#1206](https://github.com/fzaninotto/Faker/pull/1206) ([pwsdotru](https://github.com/pwsdotru)) +- Fix ng\_NG to en\_NG [\#1205](https://github.com/fzaninotto/Faker/pull/1205) ([raphaeldealmeida](https://github.com/raphaeldealmeida)) +- Add INN and KPP support for ru\_RU locale [\#1204](https://github.com/fzaninotto/Faker/pull/1204) ([pwsdotru](https://github.com/pwsdotru)) +- Remove break line on pt\_PT Address format [\#1203](https://github.com/fzaninotto/Faker/pull/1203) ([raphaeldealmeida](https://github.com/raphaeldealmeida)) +- Fix syntax of phpdoc boolean property [\#1198](https://github.com/fzaninotto/Faker/pull/1198) ([pavelkovar](https://github.com/pavelkovar)) +- add en\_HK provider [\#1196](https://github.com/fzaninotto/Faker/pull/1196) ([miklcct](https://github.com/miklcct)) +- use secure https [\#1186](https://github.com/fzaninotto/Faker/pull/1186) ([jpuck](https://github.com/jpuck)) +- Add PhoneNumberFormat for ja\_JP. [\#1185](https://github.com/fzaninotto/Faker/pull/1185) ([itigoppo](https://github.com/itigoppo)) +- Fix: Add class-level method annotations for DateTime provider [\#1183](https://github.com/fzaninotto/Faker/pull/1183) ([localheinz](https://github.com/localheinz)) +- Add ar\_SA Color Provider [\#1182](https://github.com/fzaninotto/Faker/pull/1182) ([alhoqbani](https://github.com/alhoqbani)) +- Added uk\_UA Payment provider with bank name generator [\#1181](https://github.com/fzaninotto/Faker/pull/1181) ([spaghettimaster](https://github.com/spaghettimaster)) +- Typos [\#1177](https://github.com/fzaninotto/Faker/pull/1177) ([ankitpokhrel](https://github.com/ankitpokhrel)) +- Fix XML document example [\#1176](https://github.com/fzaninotto/Faker/pull/1176) ([ankitpokhrel](https://github.com/ankitpokhrel)) +- Added Emoji to Miscellaneous [\#1175](https://github.com/fzaninotto/Faker/pull/1175) ([thomasfdm](https://github.com/thomasfdm)) +- Typos and doc block fixes [\#1170](https://github.com/fzaninotto/Faker/pull/1170) ([ankitpokhrel](https://github.com/ankitpokhrel)) +- Rewrote deprecated `each\(\)` usage [\#1168](https://github.com/fzaninotto/Faker/pull/1168) ([hboomsma](https://github.com/hboomsma)) +- Refactor text method to remove duplication [\#1163](https://github.com/fzaninotto/Faker/pull/1163) ([ankitpokhrel](https://github.com/ankitpokhrel)) +- Generate valid individual identification numbers kk\_KZ [\#1161](https://github.com/fzaninotto/Faker/pull/1161) ([YerlenZhubangaliyev](https://github.com/YerlenZhubangaliyev)) +- Added Address and Company \[fa\_IR\] [\#1160](https://github.com/fzaninotto/Faker/pull/1160) ([thisissorna](https://github.com/thisissorna)) +- Add Peruvian DNI generator [\#1158](https://github.com/fzaninotto/Faker/pull/1158) ([jgwong](https://github.com/jgwong)) +- Removed double semicolon [\#1154](https://github.com/fzaninotto/Faker/pull/1154) ([pjona](https://github.com/pjona)) +- Add prefixes for nl\_NL [\#1151](https://github.com/fzaninotto/Faker/pull/1151) ([hyperized](https://github.com/hyperized)) +- Separated male and female names for sr\_RS locale. [\#1144](https://github.com/fzaninotto/Faker/pull/1144) ([bogdanpet](https://github.com/bogdanpet)) +- Add personal ID, VAT for zh\_TW [\#1135](https://github.com/fzaninotto/Faker/pull/1135) ([Dagolin](https://github.com/Dagolin)) +- Updating ninth digit on whole country [\#1132](https://github.com/fzaninotto/Faker/pull/1132) ([gpressutto5](https://github.com/gpressutto5)) +- Indian states added to en\_IN locale [\#1131](https://github.com/fzaninotto/Faker/pull/1131) ([jiveshsg](https://github.com/jiveshsg)) +- Add Text provider for ro\_MD [\#1129](https://github.com/fzaninotto/Faker/pull/1129) ([wecerny](https://github.com/wecerny)) +- Add strict to randomNumber example [\#1124](https://github.com/fzaninotto/Faker/pull/1124) ([leepownall](https://github.com/leepownall)) +- Say Eloquent is supported [\#1123](https://github.com/fzaninotto/Faker/pull/1123) ([guidocella](https://github.com/guidocella)) +- Link Eloquent Populator [\#1120](https://github.com/fzaninotto/Faker/pull/1120) ([guidocella](https://github.com/guidocella)) +- Removed dead code from Luhn.php [\#1118](https://github.com/fzaninotto/Faker/pull/1118) ([Newman101](https://github.com/Newman101)) +- Improve Internet::transliterate performance [\#1112](https://github.com/fzaninotto/Faker/pull/1112) ([dunglas](https://github.com/dunglas)) +- fix typo [\#1109](https://github.com/fzaninotto/Faker/pull/1109) ([johannesnagl](https://github.com/johannesnagl)) +- \[cs\_CZ\] Fixed Czech phone numbers [\#1108](https://github.com/fzaninotto/Faker/pull/1108) ([tomasbedrich](https://github.com/tomasbedrich)) +- Update MasterCard BIN Range [\#1103](https://github.com/fzaninotto/Faker/pull/1103) ([andysnell](https://github.com/andysnell)) +- Add biggest german cities [\#1102](https://github.com/fzaninotto/Faker/pull/1102) ([Konafets](https://github.com/Konafets)) +- Change postal code format for ko\_KR [\#1094](https://github.com/fzaninotto/Faker/pull/1094) ([coozplz](https://github.com/coozplz)) +- Introduced the ability to specify the timezone for dateTimeThis\*\(\) methods [\#1090](https://github.com/fzaninotto/Faker/pull/1090) ([telkins](https://github.com/telkins)) +- Fixed Issue \#1086 [\#1088](https://github.com/fzaninotto/Faker/pull/1088) ([Newman101](https://github.com/Newman101)) +- \[ja\_JP\]kana of Japanese name by gender. [\#1087](https://github.com/fzaninotto/Faker/pull/1087) ([itigoppo](https://github.com/itigoppo)) +- Fix unused code [\#1083](https://github.com/fzaninotto/Faker/pull/1083) ([borgogelli](https://github.com/borgogelli)) +- Amended permissions for en\_GB AddressTest.php [\#1071](https://github.com/fzaninotto/Faker/pull/1071) ([Newman101](https://github.com/Newman101)) +- Ensure unique IDs in randomHtml [\#1068](https://github.com/fzaninotto/Faker/pull/1068) ([vlakoff](https://github.com/vlakoff)) +- Updated \[de\_DE\] city names [\#1067](https://github.com/fzaninotto/Faker/pull/1067) ([plxx](https://github.com/plxx)) +- Update method signature in Generator phpdoc [\#1066](https://github.com/fzaninotto/Faker/pull/1066) ([vlakoff](https://github.com/vlakoff)) +- Add Thai providers [\#1065](https://github.com/fzaninotto/Faker/pull/1065) ([tuwannu](https://github.com/tuwannu)) +- \(Minor\) Fixed the default locale stated in the readme [\#1064](https://github.com/fzaninotto/Faker/pull/1064) ([taylankasap](https://github.com/taylankasap)) +- \[nl\_NL\] Make person provider behave more realistically [\#1061](https://github.com/fzaninotto/Faker/pull/1061) ([curry684](https://github.com/curry684)) +- Add allowDuplicates option to randomElements\(\) [\#1060](https://github.com/fzaninotto/Faker/pull/1060) ([vlakoff](https://github.com/vlakoff)) +- Docblocks: Add some missing @method tags [\#1059](https://github.com/fzaninotto/Faker/pull/1059) ([Kurre](https://github.com/Kurre)) +- \[fi\_FI\] Improve phone number generator [\#1054](https://github.com/fzaninotto/Faker/pull/1054) ([Kurre](https://github.com/Kurre)) +- Add personalIdentityNumber\(\) to fi\_FI/Person.php [\#1053](https://github.com/fzaninotto/Faker/pull/1053) ([oittaa](https://github.com/oittaa)) +- Issue \#1041 [\#1052](https://github.com/fzaninotto/Faker/pull/1052) ([daleattree](https://github.com/daleattree)) +- Update Text.php [\#1051](https://github.com/fzaninotto/Faker/pull/1051) ([gulaandrij](https://github.com/gulaandrij)) +- Fix French phone numbers with 07 prefix [\#1046](https://github.com/fzaninotto/Faker/pull/1046) ([fzaninotto](https://github.com/fzaninotto)) +- \[Generator.php\] mt\_rand\(\) changed in PHP 7.1 [\#1045](https://github.com/fzaninotto/Faker/pull/1045) ([oittaa](https://github.com/oittaa)) +- Add 'FI' to Payment Provider [\#1044](https://github.com/fzaninotto/Faker/pull/1044) ([oittaa](https://github.com/oittaa)) +- Added id number generator to Person Provider for the en\_ZA locale [\#1039](https://github.com/fzaninotto/Faker/pull/1039) ([smithandre](https://github.com/smithandre)) +- \[Feature\] Add nigerian provider [\#1030](https://github.com/fzaninotto/Faker/pull/1030) ([elchroy](https://github.com/elchroy)) +- \[pl\_PL\] Handle state. [\#1029](https://github.com/fzaninotto/Faker/pull/1029) ([piotrooo](https://github.com/piotrooo)) +- Fixed polish text - change '--' into '-'. [\#1027](https://github.com/fzaninotto/Faker/pull/1027) ([piotrooo](https://github.com/piotrooo)) +- Update Text.php [\#1025](https://github.com/fzaninotto/Faker/pull/1025) ([gulaandrij](https://github.com/gulaandrij)) +- Adding Nationalized Citizens to DNI in Person.php [\#1021](https://github.com/fzaninotto/Faker/pull/1021) ([celisflen-bers](https://github.com/celisflen-bers)) +- Add nik to indonesia [\#1019](https://github.com/fzaninotto/Faker/pull/1019) ([Nuffic](https://github.com/Nuffic)) +- fix mb\_substr missing parameter error when generating japanese string with realText method [\#1018](https://github.com/fzaninotto/Faker/pull/1018) ([horan-geeker](https://github.com/horan-geeker)) +- IBAN Formatters for New Locales [\#1015](https://github.com/fzaninotto/Faker/pull/1015) ([okj579](https://github.com/okj579)) +- German Bank Names [\#1014](https://github.com/fzaninotto/Faker/pull/1014) ([okj579](https://github.com/okj579)) +- Adding countries for pl\_PL provider [\#1009](https://github.com/fzaninotto/Faker/pull/1009) ([mertcanesen](https://github.com/mertcanesen)) +- Adding Pattern Lab plugin to list of 3rd party libraries [\#1008](https://github.com/fzaninotto/Faker/pull/1008) ([EvanLovely](https://github.com/EvanLovely)) +- Korea top 100 lastName [\#1006](https://github.com/fzaninotto/Faker/pull/1006) ([tael](https://github.com/tael)) +- Use real Belgian postcodes instead of random number [\#1004](https://github.com/fzaninotto/Faker/pull/1004) ([toonevdb](https://github.com/toonevdb)) +- Add bankAccountNumber implementations [\#1000](https://github.com/fzaninotto/Faker/pull/1000) ([akramfares](https://github.com/akramfares)) +- Generates a random NIR number \(fr\_FR\) [\#997](https://github.com/fzaninotto/Faker/pull/997) ([Ultim4T0m](https://github.com/Ultim4T0m)) +- \#989 Fix country typo [\#996](https://github.com/fzaninotto/Faker/pull/996) ([adriantombu](https://github.com/adriantombu)) +- adding back CNP [\#988](https://github.com/fzaninotto/Faker/pull/988) ([the-noob](https://github.com/the-noob)) +- Fix phpunit tests fail on 64-bit systems \#982 [\#983](https://github.com/fzaninotto/Faker/pull/983) ([Powerhead13](https://github.com/Powerhead13)) +- Remove trailing dot in username if any [\#975](https://github.com/fzaninotto/Faker/pull/975) ([vlakoff](https://github.com/vlakoff)) +- HTML Lorem [\#971](https://github.com/fzaninotto/Faker/pull/971) ([rudkjobing](https://github.com/rudkjobing)) +- Fix a mixup between male and female last names in Icelandic. [\#970](https://github.com/fzaninotto/Faker/pull/970) ([arthur-olafsson](https://github.com/arthur-olafsson)) +- Remove duplicate [\#969](https://github.com/fzaninotto/Faker/pull/969) ([mijgame](https://github.com/mijgame)) +- fix \[zh\_CN\]PhoneNumber illegal operator prefix [\#966](https://github.com/fzaninotto/Faker/pull/966) ([zhwei](https://github.com/zhwei)) +- es\_ES: Generate VAT Number [\#964](https://github.com/fzaninotto/Faker/pull/964) ([miguelgf](https://github.com/miguelgf)) +- Update Image.php [\#963](https://github.com/fzaninotto/Faker/pull/963) ([gulaandrij](https://github.com/gulaandrij)) +- Remove cnp formatter from RO\_ro locale \(fails tests\) [\#962](https://github.com/fzaninotto/Faker/pull/962) ([fzaninotto](https://github.com/fzaninotto)) +- Adding valid en\_GB postcodes [\#961](https://github.com/fzaninotto/Faker/pull/961) ([the-noob](https://github.com/the-noob)) +- Adding Text for ro\_RO [\#959](https://github.com/fzaninotto/Faker/pull/959) ([the-noob](https://github.com/the-noob)) +- Minor: Fixed trailing space in DateTime provider [\#956](https://github.com/fzaninotto/Faker/pull/956) ([tifabien](https://github.com/tifabien)) +- Remove 'Stripper' from en\_US job titles [\#954](https://github.com/fzaninotto/Faker/pull/954) ([amcsi](https://github.com/amcsi)) +- fix 32bits issue [\#953](https://github.com/fzaninotto/Faker/pull/953) ([remicollet](https://github.com/remicollet)) +- Fix EAN8 checkSum generator [\#951](https://github.com/fzaninotto/Faker/pull/951) ([MatthieuMota](https://github.com/MatthieuMota)) +- Fixed description and the use of early undocumented parameters. [\#949](https://github.com/fzaninotto/Faker/pull/949) ([andrey-helldar](https://github.com/andrey-helldar)) +- Pushing new mobile prefixes in philippines [\#944](https://github.com/fzaninotto/Faker/pull/944) ([napoleon101392](https://github.com/napoleon101392)) +- Update Company.php [\#943](https://github.com/fzaninotto/Faker/pull/943) ([thiagotalma](https://github.com/thiagotalma)) +- Fix to Issue \#935 - German Locale [\#936](https://github.com/fzaninotto/Faker/pull/936) ([Newman101](https://github.com/Newman101)) +- el\_CY Locale [\#930](https://github.com/fzaninotto/Faker/pull/930) ([softius](https://github.com/softius)) +- Add phpdoc method dateTimeInInterval in Generator.php [\#926](https://github.com/fzaninotto/Faker/pull/926) ([KeithYeh](https://github.com/KeithYeh)) +- Harmonize fr\_\*\Company [\#918](https://github.com/fzaninotto/Faker/pull/918) ([Max13](https://github.com/Max13)) +- Fix: fix invalid parameter of mb\_substr\(\) [\#917](https://github.com/fzaninotto/Faker/pull/917) ([tkawaji](https://github.com/tkawaji)) +- kk\_KZ Company/person identification numbers unit tests [\#916](https://github.com/fzaninotto/Faker/pull/916) ([YerlenZhubangaliyev](https://github.com/YerlenZhubangaliyev)) +- ka\_GE: overall improvements to ka\_GE locale [\#913](https://github.com/fzaninotto/Faker/pull/913) ([hertzg](https://github.com/hertzg)) +- Fix: Do not pick a random float less than minimum [\#909](https://github.com/fzaninotto/Faker/pull/909) ([localheinz](https://github.com/localheinz)) +- Fix: Prefer dependencies installed from dist [\#908](https://github.com/fzaninotto/Faker/pull/908) ([localheinz](https://github.com/localheinz)) +- Add a building number with letter to German speaking locales. [\#903](https://github.com/fzaninotto/Faker/pull/903) ([markuspoerschke](https://github.com/markuspoerschke)) +- \[RFR\] Remove parts of the hu\_HU address formatters [\#902](https://github.com/fzaninotto/Faker/pull/902) ([fzaninotto](https://github.com/fzaninotto)) +- use Luhn to calculate ar\_SA id numbers. [\#875](https://github.com/fzaninotto/Faker/pull/875) ([FooBarQuaxx](https://github.com/FooBarQuaxx)) +- Fix Doctrine ODM Support [\#489](https://github.com/fzaninotto/Faker/pull/489) ([cbourgois](https://github.com/cbourgois)) + + +## 2016-04-29, v1.6.0 + +- Remove parts of the Hungarian (hu\_HU) address formatters [\#902](https://github.com/fzaninotto/Faker/pull/902) ([fzaninotto](https://github.com/fzaninotto)) +- Renamed norwegian (nb\_NO) locale [\#901](https://github.com/fzaninotto/Faker/pull/901) ([fzaninotto](https://github.com/fzaninotto)) +- Improveed German (de\_DE) titles [\#897](https://github.com/fzaninotto/Faker/pull/897) ([christianbartels](https://github.com/christianbartels)) +- Added VAT formatter to nl\_BE and fr\_BE providers [\#896](https://github.com/fzaninotto/Faker/pull/896) ([anvanza](https://github.com/anvanza)) +- Fixed provider namespace for Lithuanian (lt\_LT) [\#894](https://github.com/fzaninotto/Faker/pull/894) ([sanis](https://github.com/sanis)) +- Removed unnecessary (and incompatible) license from Russian and Ukrainian (uk\_UA & ru\_RU) Text providers [\#892](https://github.com/fzaninotto/Faker/pull/892) ([Newman101](https://github.com/Newman101)) +- Improved `languageCode` formatted to include all ISO-639-1 standard codes [\#889](https://github.com/fzaninotto/Faker/pull/889) ([andrewnicols](https://github.com/andrewnicols)) +- Improved Hungarian provider [\#883](https://github.com/fzaninotto/Faker/pull/883) ([balping](https://github.com/balping)) +- Fixed typo in Austrian Person provider [\#880](https://github.com/fzaninotto/Faker/pull/880) ([xelan](https://github.com/xelan)) +- Added Chines (zh\_CN) `catchPhrase` formatter [\#878](https://github.com/fzaninotto/Faker/pull/878) ([z-song](https://github.com/z-song)) +- Added mention of Brazilian (pt\_BR) providers in readme [\#877](https://github.com/fzaninotto/Faker/pull/877) ([iget-master](https://github.com/iget-master)) +- Updated composer `require` section to allow PHP 7 in safer way [\#874](https://github.com/fzaninotto/Faker/pull/874) ([TomasVotruba](https://github.com/TomasVotruba)) +- Added Greek (el\_GR) `mobilePhoneNumber` and `tollFreeNumber` formatters [\#869](https://github.com/fzaninotto/Faker/pull/869) ([sebdesign](https://github.com/sebdesign)) +- Added Lorempixel check for `ImageTest.php` to avoid test fails when the service is offline [\#866](https://github.com/fzaninotto/Faker/pull/866) ([Newman101](https://github.com/Newman101)) +- Added Chinese (zh\_CN) Providers [\#864](https://github.com/fzaninotto/Faker/pull/864) ([z-song](https://github.com/z-song)) +- Added unit tests for Canadian (en\_CA) provider [\#862](https://github.com/fzaninotto/Faker/pull/862) ([Newman101](https://github.com/Newman101)) +- Added Dutch BTW \(vat\) Number [\#861](https://github.com/fzaninotto/Faker/pull/861) ([LauLaman](https://github.com/LauLaman)) +- Improved Australian (en\_AU) provider [\#858](https://github.com/fzaninotto/Faker/pull/858) ([Newman101](https://github.com/Newman101)) +- Added Propel2 ORM support [\#852](https://github.com/fzaninotto/Faker/pull/852) ([iTechDhaval](https://github.com/iTechDhaval)) +- Added en\_IN unit test for `Address.php` [\#849](https://github.com/fzaninotto/Faker/pull/849) ([Newman101](https://github.com/Newman101)) +- Updated docs to clarify that `randomElements` does not repeat input elements [\#848](https://github.com/fzaninotto/Faker/pull/848) ([sustmi](https://github.com/sustmi)) +- Optimized Taiwanese (zh\_TW) `realText` provider [\#844](https://github.com/fzaninotto/Faker/pull/844) ([Newman101](https://github.com/Newman101)) +- Added more Iranian (fa\_IR) TLDs [\#843](https://github.com/fzaninotto/Faker/pull/843) ([VagrantStory](https://github.com/VagrantStory)) +- Added Hebrew (he\_IL) `country` formatter [\#841](https://github.com/fzaninotto/Faker/pull/841) ([yonirom](https://github.com/yonirom)) +- Documented `boolean` formatter [\#840](https://github.com/fzaninotto/Faker/pull/840) ([danieliancu](https://github.com/danieliancu)) +- Fixed modifiers anchor readme [\#838](https://github.com/fzaninotto/Faker/pull/838) ([danieliancu](https://github.com/danieliancu)) +- Added Dutch (nl\_NL) real text provider [\#837](https://github.com/fzaninotto/Faker/pull/837) ([endroid](https://github.com/endroid)) +- Added `valid` modifier [\#836](https://github.com/fzaninotto/Faker/pull/836) ([fzaninotto](https://github.com/fzaninotto)) +- Added Iranian (fa\_IR) `PhoneNumber` provider [\#833](https://github.com/fzaninotto/Faker/pull/833) ([ghost](https://github.com/ghost)) +- Add Brazilian (pt\_BR) `region` and `regionAbbr` formatters [\#828](https://github.com/fzaninotto/Faker/pull/828) ([francinaldo](https://github.com/francinaldo)) +- Improved Austrian (de\_AT) names, states, and realtext [\#826](https://github.com/fzaninotto/Faker/pull/826) ([Findus23](https://github.com/Findus23)) +- Improved German (de\_DE) names [\#825](https://github.com/fzaninotto/Faker/pull/825) ([Findus23](https://github.com/Findus23)) +- Improved Latvian (lv\_LV) first names [\#823](https://github.com/fzaninotto/Faker/pull/823) ([veisis](https://github.com/veisis)) +- Improved Latvian (lv\_LV) `phoneNumber` formatter [\#822](https://github.com/fzaninotto/Faker/pull/822) ([veisis](https://github.com/veisis)) +- Updated phpDoc link to IBAN format reference in `Payment` provider [\#821](https://github.com/fzaninotto/Faker/pull/821) ([god107](https://github.com/god107)) +- Updated Sport ORM populator to populate values for numeric fields [\#820](https://github.com/fzaninotto/Faker/pull/820) ([urisavka](https://github.com/urisavka)) +- Updated Chinese (zh\_CN) operators' phone number prefix. [\#819](https://github.com/fzaninotto/Faker/pull/819) ([vistart](https://github.com/vistart)) +- Optimized Spot ORM `EntityPopulator` [\#817](https://github.com/fzaninotto/Faker/pull/817) ([Newman101](https://github.com/Newman101)) +- Added Korean (ko\_KR) `realText` formatter [\#815](https://github.com/fzaninotto/Faker/pull/815) ([jdssem](https://github.com/jdssem)) +- Updated `imageUrl` formatter phpDoc [\#814](https://github.com/fzaninotto/Faker/pull/814) ([jonwurtzler](https://github.com/jonwurtzler)) +- Optimized Taiwanese (zh\_TW) text provider [\#809](https://github.com/fzaninotto/Faker/pull/809) ([BePsvPT](https://github.com/BePsvPT)) +- Added strict comparison to Czech (cs\_CS) `birthNumber` formatter [\#807](https://github.com/fzaninotto/Faker/pull/807) ([Newman101](https://github.com/Newman101)) +- Added Greek (el\_GR) `realText` formatter [\#805](https://github.com/fzaninotto/Faker/pull/805) ([hootlex](https://github.com/hootlex)) +- Added Simplified Chinese \(zh\_CN\) `state` and `stateAbbr` formatters [\#804](https://github.com/fzaninotto/Faker/pull/804) ([zhanghuanchong](https://github.com/zhanghuanchong)) +- Update `Image` provider to allow generation of grayscale images [\#801](https://github.com/fzaninotto/Faker/pull/801) ([neutralrockets](https://github.com/neutralrockets)) +- Fixed Taiwanese (zh_TW) incorrect `mb_substr()` arguments [\#799](https://github.com/fzaninotto/Faker/pull/799) ([BePsvPT](https://github.com/BePsvPT)) +- Added Spot ORM populator [\#796](https://github.com/fzaninotto/Faker/pull/796) ([urisavka](https://github.com/urisavka)) +- Added Italian (it\_IT) `vatId` and `taxId` formatters [\#790](https://github.com/fzaninotto/Faker/pull/790) ([brainrepo](https://github.com/brainrepo)) +- Added some fixes to Armenian (hy\_AM) locale [\#788](https://github.com/fzaninotto/Faker/pull/788) ([mhamlet](https://github.com/mhamlet)) +- Removed duplicate entries in `toAscii()` transliteration table, used in `Internet` provider [\#787](https://github.com/fzaninotto/Faker/pull/787) ([vlakoff](https://github.com/vlakoff)) +- Added Indian (en\_IN) providers [\#785](https://github.com/fzaninotto/Faker/pull/785) ([kartiksomani](https://github.com/kartiksomani)) +- Removed duplicate country names in various locales, removed non-random country arrays [\#784](https://github.com/fzaninotto/Faker/pull/784) ([fzaninotto](https://github.com/fzaninotto)) +- Improved Swiss (de\_CH) phone numbers [\#782](https://github.com/fzaninotto/Faker/pull/782) ([z38](https://github.com/z38)) +- Added Swiss (de\_CH) names [\#781](https://github.com/fzaninotto/Faker/pull/781) ([z38](https://github.com/z38)) +- Make capitalization of first word optional in Text Provider [\#778](https://github.com/fzaninotto/Faker/pull/778) ([LagunaJavier](https://github.com/LagunaJavier)) +- Added Georgian (ka\_GE) providers [\#777](https://github.com/fzaninotto/Faker/pull/777) ([akalongman](https://github.com/akalongman)) +- Fix CakePHP populator [\#776](https://github.com/fzaninotto/Faker/pull/776) ([daniel-mueller](https://github.com/daniel-mueller)) +- Added unit tests for `Address` provider in many locales [\#775](https://github.com/fzaninotto/Faker/pull/775) [\#773](https://github.com/fzaninotto/Faker/pull/773) [\#772](https://github.com/fzaninotto/Faker/pull/772) [\#767](https://github.com/fzaninotto/Faker/pull/767) [\#765](https://github.com/fzaninotto/Faker/pull/765) [\#764](https://github.com/fzaninotto/Faker/pull/764) [\#758](https://github.com/fzaninotto/Faker/pull/758) [\#756](https://github.com/fzaninotto/Faker/pull/756) [\#747](https://github.com/fzaninotto/Faker/pull/747) [\#741](https://github.com/fzaninotto/Faker/pull/741) ([Newman101](https://github.com/Newman101)) +- Added `dbi` formatter to Spanish (es\_ES) Person provider [\#763](https://github.com/fzaninotto/Faker/pull/763) ([mikk150](https://github.com/mikk150)) +- Added South Africa (en\_ZA) locale [\#761](https://github.com/fzaninotto/Faker/pull/761) ([smithandre](https://github.com/smithandre)) [\#760](https://github.com/fzaninotto/Faker/pull/760) ([smithandre](https://github.com/smithandre)) [\#759](https://github.com/fzaninotto/Faker/pull/759) ([smithandre](https://github.com/smithandre)) +- Added E.164 phone number generator [\#753](https://github.com/fzaninotto/Faker/pull/753) ([daleattree](https://github.com/daleattree)) +- Fixed serialization issue in `unique` modifier [\#749](https://github.com/fzaninotto/Faker/pull/749) ([EmanueleMinotto](https://github.com/EmanueleMinotto)) +- Added Switzerland (de\_CH, fr\_CH, it\_CH) providers [\#739](https://github.com/fzaninotto/Faker/pull/739) ([r3h6](https://github.com/r3h6)) +- Added PHPDocs, removed unused variable [\#738](https://github.com/fzaninotto/Faker/pull/738) ([daniel-mueller](https://github.com/daniel-mueller)) +- Fixed building numbers to have non-zero first bumber [\#737](https://github.com/fzaninotto/Faker/pull/737) ([jmauerhan](https://github.com/jmauerhan)) +- Updated ninth digit for Brazilian cell phone numbers [\#734](https://github.com/fzaninotto/Faker/pull/734) ([igorsantos07](https://github.com/igorsantos07)) +- Simplified Factory code [\#732](https://github.com/fzaninotto/Faker/pull/732) ([vlakoff](https://github.com/vlakoff)) +- Added mention of [images-generator](https://github.com/bruceheller/images-generator) in readme [\#731](https://github.com/fzaninotto/Faker/pull/731) ([bruceheller](https://github.com/bruceheller)) +- Optimize Internet::toAscii\(\) by using a static cache and translitteration [\#730](https://github.com/fzaninotto/Faker/pull/730) [\#729](https://github.com/fzaninotto/Faker/pull/729) +[\#725](https://github.com/fzaninotto/Faker/pull/725) [\#724](https://github.com/fzaninotto/Faker/pull/724) ([vlakoff](https://github.com/vlakoff)) +- Added more English (en\_GB) Phone Number formats [\#721](https://github.com/fzaninotto/Faker/pull/721) ([nickwebcouk](https://github.com/nickwebcouk)) +- Cleaned up `use` statements across the code [\#719](https://github.com/fzaninotto/Faker/pull/719) ([pomaxa](https://github.com/pomaxa)) +- Fixed CackePHP populator [\#718](https://github.com/fzaninotto/Faker/pull/718) ([sdustinh](https://github.com/sdustinh)) +- Cleaned up various phpmd notices [\#715](https://github.com/fzaninotto/Faker/pull/715) ([pomaxa](https://github.com/pomaxa)) +- Added `Color` provider to Latvian (lv_LV) locale [\#714](https://github.com/fzaninotto/Faker/pull/714) ([pomaxa](https://github.com/pomaxa)) +- Fixed bad randomization in Doctrine populator [\#713](https://github.com/fzaninotto/Faker/pull/713) ([pomaxa](https://github.com/pomaxa)) +- Added Mongolian (mn\_MN) providers [\#709](https://github.com/fzaninotto/Faker/pull/709) ([selmonal](https://github.com/selmonal)) +- Improved Australian (en\_AU) `postcode` formatter [\#703](https://github.com/fzaninotto/Faker/pull/703) ([xfxf](https://github.com/xfxf)) +- Added support for asterisks in `bothify` and `optimize` [\#701](https://github.com/fzaninotto/Faker/pull/701) ([nineinchnick](https://github.com/nineinchnick)) +- Fixed important distinction between ORM and database framework in README’s reference to an external Faker provider for POMM that I have never even tested. Anyway, POMM is highly recommended if you are a Postgres fan, or if you want to please Grégoire and help him finish his lifelong project of listening to music on a hi-fi audio equipment he built from his own hands [\#696](https://github.com/fzaninotto/Faker/pull/696) ([chanmix51](https://github.com/chanmix51)) +- Fixed example `text()` output in README [\#694](https://github.com/fzaninotto/Faker/pull/694) ([vlakoff](https://github.com/vlakoff)) +- Added mention of CakePHP 2.x Seeder Plugin to readme [\#691](https://github.com/fzaninotto/Faker/pull/691) ([ravage84](https://github.com/ravage84)) +- Fixed invalid email bug for Korean (ko\_KR) [\#690](https://github.com/fzaninotto/Faker/pull/690) ([pearlc](https://github.com/pearlc)) +- Removed an invalid Dutch (nl\_NL) lastname that breaks email generator [\#689](https://github.com/fzaninotto/Faker/pull/689) ([SpaceK33z](https://github.com/SpaceK33z)) +- Updated `numberBetween()` to be order agnostic [\#683](https://github.com/fzaninotto/Faker/pull/683) ([xfxf](https://github.com/xfxf)) +- Added several English (en\_US) bank-related formatters [\#682](https://github.com/fzaninotto/Faker/pull/682) ([okj579](https://github.com/okj579)) +- Fixed `ipv4` formatter to avoid generating special purpose addresses [\#681](https://github.com/fzaninotto/Faker/pull/681) ([ravage84](https://github.com/ravage84)) +- Moved `intl` extension to `require-dev` in `composer.json` file [\#680](https://github.com/fzaninotto/Faker/pull/680) ([jaschweder](https://github.com/jaschweder)) +- Added more Turkish (tr\_TR) phones number formats [\#678](https://github.com/fzaninotto/Faker/pull/678) ([Quanthir](https://github.com/Quanthir)) +- Fixed primary Key warning in CakePHP ORM populator [\#677](https://github.com/fzaninotto/Faker/pull/677) ([davidyell](https://github.com/davidyell)) +- Added time zone support for provider methods returning DateTime instance [\#675](https://github.com/fzaninotto/Faker/pull/675) ([bishopb](https://github.com/bishopb)) +- Removed trailing spaces from some Argentinian (es\_AR) female first names [\#674](https://github.com/fzaninotto/Faker/pull/674) ([ivanmirson](https://github.com/ivanmirson)) +- Added Lithuanian (lt\_LT) locale [\#673](https://github.com/fzaninotto/Faker/pull/673) ([ekateiva](https://github.com/ekateiva)) +- Added mention of Alice to readme [\#665](https://github.com/fzaninotto/Faker/pull/665) ([Seldaek](https://github.com/Seldaek)) +- Fixed namespace in tests [\#663](https://github.com/fzaninotto/Faker/pull/663) ([localheinz](https://github.com/localheinz)) +- Fixed trailing spaces in `Color` provider [\#662](https://github.com/fzaninotto/Faker/pull/662) ([apsylone](https://github.com/apsylone)) +- Removed duplicate country names in Russian (ru\_RU) `Address` provider [\#659](https://github.com/fzaninotto/Faker/pull/659) ([nurolopher](https://github.com/nurolopher)) +- Added `rgba` formatter to `Color` provider [\#653](https://github.com/fzaninotto/Faker/pull/653) ([apsylone](https://github.com/apsylone)) +- Fixed bad randomization in CakePHP populator [\#648](https://github.com/fzaninotto/Faker/pull/648) ([jadb](https://github.com/jadb)) +- Updated phpunit configuration to better use colors [\#643](https://github.com/fzaninotto/Faker/pull/643) ([localheinz](https://github.com/localheinz)) +- Updated `makefile` to install dev dependencies by default [\#642](https://github.com/fzaninotto/Faker/pull/642) ([localheinz](https://github.com/localheinz)) +- Updated Travis configuration to cache dependencies between builds [\#641](https://github.com/fzaninotto/Faker/pull/641) ([localheinz](https://github.com/localheinz)) +- Added SVG badge to readme for displaying Travis build status [\#640](https://github.com/fzaninotto/Faker/pull/640) ([localheinz](https://github.com/localheinz)) +- Added Croatian (hr\_HR) locale [\#638](https://github.com/fzaninotto/Faker/pull/638) ([toniperic](https://github.com/toniperic)) +- Updated `dateTimeBetween` PHPDoc [\#635](https://github.com/fzaninotto/Faker/pull/635) ([theofidry](https://github.com/theofidry)) +- Add mention of Symfony2 bundles in readme [\#634](https://github.com/fzaninotto/Faker/pull/634) ([theofidry](https://github.com/theofidry)) +- Added Hebrew (he\_IL) locale [\#633](https://github.com/fzaninotto/Faker/pull/633) ([yonirom](https://github.com/yonirom)) +- Updated `seed` to accept non-integer seeds [\#632](https://github.com/fzaninotto/Faker/pull/632) ([theofidry](https://github.com/theofidry)) +- Added DocBlock to `Factory::create()` [\#631](https://github.com/fzaninotto/Faker/pull/631) ([tonynelson19](https://github.com/tonynelson19)) +- Added `jobTitle` generator [\#630](https://github.com/fzaninotto/Faker/pull/630) ([gregoryduckworth](https://github.com/gregoryduckworth)) +- Updated Chinese (zh\_CN) `Person` provider to generate more correct names [\#628](https://github.com/fzaninotto/Faker/pull/628) ([phoenixgao](https://github.com/phoenixgao)) +- Updated Brazilian (pt\_BR) `cellphone` formatter to make it more flexible [\#623](https://github.com/fzaninotto/Faker/pull/623) ([igorsantos07](https://github.com/igorsantos07)) +- Add Arabic for Saudi Arabia (ar\_SA) locale [\#618](https://github.com/fzaninotto/Faker/pull/618) ([ibrasho](https://github.com/ibrasho)) +- Updated en\_US phone numbers [\#615](https://github.com/fzaninotto/Faker/pull/615) ([okj579](https://github.com/okj579)) +- Fixed typos in variable names and exceptions [\#614](https://github.com/fzaninotto/Faker/pull/614) ([pborreli](https://github.com/pborreli)) +- Added a table of contents to the readme file. [\#613](https://github.com/fzaninotto/Faker/pull/613) ([camilopayan](https://github.com/camilopayan)) +- Added Brazilian (es_BR) credit card formatters [\#608](https://github.com/fzaninotto/Faker/pull/608) ([igorsantos07](https://github.com/igorsantos07)) +- Updated `iban` formatter to be cross-locale [\#607](https://github.com/fzaninotto/Faker/pull/607) ([okj579](https://github.com/okj579)) +- Improved ORM name guesser logic [\#606](https://github.com/fzaninotto/Faker/pull/606) ([watermanio](https://github.com/watermanio)) +- Fixed doc typo [\#605](https://github.com/fzaninotto/Faker/pull/605) ([igorsantos07](https://github.com/igorsantos07)) +- Removed executable bits [\#593](https://github.com/fzaninotto/Faker/pull/593) ([siwinski](https://github.com/siwinski)) +- Fixed `iban` generator [\#590](https://github.com/fzaninotto/Faker/pull/590) ([okj579](https://github.com/okj579)) +- Added Philippines (en\_PH) `mobileNumber` formatter [\#589](https://github.com/fzaninotto/Faker/pull/589) ([lozadaOmr](https://github.com/lozadaOmr)) +- Added support for min / max params in `latitude` and `longitude` formatters [\#570](https://github.com/fzaninotto/Faker/pull/570) ([actuallymab](https://github.com/actuallymab)) +- Added Czech (cs_CZ) `birthNumber` formatter [\#535](https://github.com/fzaninotto/Faker/pull/535) ([tomasbedrich](https://github.com/tomasbedrich)) +- Added `dateTimeInInterval` formatter [\#526](https://github.com/fzaninotto/Faker/pull/526) ([nicodmf](https://github.com/nicodmf)) +- Updated `optional` and `boolean` apis to be more consistent [\#513](https://github.com/fzaninotto/Faker/pull/513) ([EmanueleMinotto](https://github.com/EmanueleMinotto)) +- Added Greek (el\_GR) `Address` provider [\#504](https://github.com/fzaninotto/Faker/pull/504) ([drakakisgeo](https://github.com/drakakisgeo)) + +## 2015-05-29, v1.5.0 + +* Added ability to print custom text on the images fetched by the Image provider [\#583](https://github.com/fzaninotto/Faker/pull/583) ([fzaninotto](https://github.com/fzaninotto)) +* Fixed typos in Peruvian (es\_PE) Person provider [\#581](https://github.com/fzaninotto/Faker/pull/581) [\#580](https://github.com/fzaninotto/Faker/pull/580) ([ysramirez](https://github.com/ysramirez)) +* Added instructions for installing with composer to readme.md [\#572](https://github.com/fzaninotto/Faker/pull/572) ([totophe](https://github.com/totophe)) +* Added Kazakh (kk\_KZ) locale [\#569](https://github.com/fzaninotto/Faker/pull/569) ([YerlenZhubangaliyev](https://github.com/YerlenZhubangaliyev)) +* Added Korean (ko\_KR) locale [\#566](https://github.com/fzaninotto/Faker/pull/566) ([pearlc](https://github.com/pearlc)) +* Fixed file provider to ignore unreadable and special files [\#565](https://github.com/fzaninotto/Faker/pull/565) ([svrnm](https://github.com/svrnm)) +* Fixed Dutch (nl\_NL) Address and Person providers [\#560](https://github.com/fzaninotto/Faker/pull/560) ([killerog](https://github.com/killerog)) +* Fixed Dutch (nl\_NL) Person provider [\#559](https://github.com/fzaninotto/Faker/pull/559) ([pauledenburg](https://github.com/pauledenburg)) +* Added Russian (ru\_RU) Bank names provider [\#553](https://github.com/fzaninotto/Faker/pull/553) ([wizardjedi](https://github.com/wizardjedi)) +* Added mobile phone function in French (fr\_FR) provider [\#552](https://github.com/fzaninotto/Faker/pull/552) ([kletellier](https://github.com/kletellier)) +* Added phpdoc for new magic methods in Generator to help IntelliSense completion [\#550](https://github.com/fzaninotto/Faker/pull/550) ([stof](https://github.com/stof)) +* Fixed File provider bug 'The first argument to copy() function cannot be a directory' [\#547](https://github.com/fzaninotto/Faker/pull/547) ([svrnm](https://github.com/svrnm)) +* Added new Brazilian (pt\_BR) Providers [\#545](https://github.com/fzaninotto/Faker/pull/545) ([igorsantos07](https://github.com/igorsantos07)) +* Fixed ability to seed the generator [\#543](https://github.com/fzaninotto/Faker/pull/543) ([schmengler](https://github.com/schmengler)) +* Added streetAddress formatter to Russian (ru\_RU) provider [\#542](https://github.com/fzaninotto/Faker/pull/542) ([ZAYEC77](https://github.com/ZAYEC77)) +* Fixed Internet provider warning "Could not create transliterator"* [\#541](https://github.com/fzaninotto/Faker/pull/541) ([fonsecas72](https://github.com/fonsecas72)) +* Fixed Spanish for Argentina (es\_AR) Address provider [\#540](https://github.com/fzaninotto/Faker/pull/540) ([ivanmirson](https://github.com/ivanmirson)) +* Fixed region names in French for Belgium (fr\_BE) address provider [\#536](https://github.com/fzaninotto/Faker/pull/536) ([miclf](https://github.com/miclf)) +* Fixed broken Doctrine2 link in README [\#534](https://github.com/fzaninotto/Faker/pull/534) ([JonathanKryza](https://github.com/JonathanKryza)) +* Added link to faker-context Behat extension in readme [\#532](https://github.com/fzaninotto/Faker/pull/532) ([denheck](https://github.com/denheck)) +* Added PHP 7.0 nightly to Travis build targets [\#525](https://github.com/fzaninotto/Faker/pull/525) ([TomasVotruba](https://github.com/TomasVotruba)) +* Added Dutch (nl\_NL) color names [\#523](https://github.com/fzaninotto/Faker/pull/523) ([belendel](https://github.com/belendel)) +* Fixed Chinese (zh\_CN) Address provider (remove Taipei) [\#522](https://github.com/fzaninotto/Faker/pull/522) ([asika32764](https://github.com/asika32764)) +* Fixed phonenumber formats in Dutch (nl\_NL) PhoneNumber provider [\#521](https://github.com/fzaninotto/Faker/pull/521) ([SpaceK33z](https://github.com/SpaceK33z)) +* Fixed Russian (ru\_RU) Address provider [\#518](https://github.com/fzaninotto/Faker/pull/518) ([glagola](https://github.com/glagola)) +* Added Italian (it\_IT) Text provider [\#517](https://github.com/fzaninotto/Faker/pull/517) ([endelwar](https://github.com/endelwar)) +* Added Norwegian (no\_NO) locale [\#515](https://github.com/fzaninotto/Faker/pull/515) ([phaza](https://github.com/phaza)) +* Added VAT number to Bulgarian (bg\_BG) Payment provider [\#512](https://github.com/fzaninotto/Faker/pull/512) ([ronanguilloux](https://github.com/ronanguilloux)) +* Fixed UserAgent provider outdated user agents [\#511](https://github.com/fzaninotto/Faker/pull/511) ([ajbdev](https://github.com/ajbdev)) +* Fixed `image()` formatter to make it work with temp dir of any (decent) OS [\#507](https://github.com/fzaninotto/Faker/pull/507) ([ronanguilloux](https://github.com/ronanguilloux)) +* Added Persian (fa\_IR) locale [\#500](https://github.com/fzaninotto/Faker/pull/500) ([zoli](https://github.com/zoli)) +* Added Currency Code formatter [\#497](https://github.com/fzaninotto/Faker/pull/497) ([stelgenhof](https://github.com/stelgenhof)) +* Added VAT number to Belgium (be_BE) Payment provider [\#495](https://github.com/fzaninotto/Faker/pull/495) ([ronanguilloux](https://github.com/ronanguilloux)) +* Fixed `imageUrl` formatter bug where it would always return the same image [\#494](https://github.com/fzaninotto/Faker/pull/494) ([fzaninotto](https://github.com/fzaninotto)) +* Added more Indonesian (id\_ID) providers [\#493](https://github.com/fzaninotto/Faker/pull/493) ([deerawan](https://github.com/deerawan)) +* Added Indonesian (id\_ID) locale [\#492](https://github.com/fzaninotto/Faker/pull/492) ([stoutZero](https://github.com/stoutZero)) +* Fixed unique generator performance [\#491](https://github.com/fzaninotto/Faker/pull/491) ([ikwattro](https://github.com/ikwattro)) +* Added transliterator to `email` and `username` [\#490](https://github.com/fzaninotto/Faker/pull/490) ([fzaninotto](https://github.com/fzaninotto)) +* Added Hungarian (hu\_HU) Text provider [\#486](https://github.com/fzaninotto/Faker/pull/486) ([lintaba](https://github.com/lintaba)) +* Fixed CakePHP Entity Popolator (some cases where no entities prev. inserted) [\#483](https://github.com/fzaninotto/Faker/pull/483) ([jadb](https://github.com/jadb)) +* Added Color and DateTime Turkish (tr\_TR) Providers [\#481](https://github.com/fzaninotto/Faker/pull/481) ([behramcelen](https://github.com/behramcelen)) +* Added Latvian (lv\_LV) `personalIdentityNumber` formatter [\#472](https://github.com/fzaninotto/Faker/pull/472) ([MatissJanis](https://github.com/MatissJanis)) +* Added VAT number to Austrian (at_AT) Payment provider [\#470](https://github.com/fzaninotto/Faker/pull/470) ([ronanguilloux](https://github.com/ronanguilloux)) +* Fixed missing @return phpDoc in Payment provider [\#469](https://github.com/fzaninotto/Faker/pull/469) ([ronanguilloux](https://github.com/ronanguilloux)) +* Added SWIFT/BIC payment type formatter to the Payment provider [\#465](https://github.com/fzaninotto/Faker/pull/465) ([ronanguilloux](https://github.com/ronanguilloux)) +* Fixed small typo in Base provider exception [\#460](https://github.com/fzaninotto/Faker/pull/460) ([miclf](https://github.com/miclf)) +* Added Georgian (ka\_Ge) locale [\#457](https://github.com/fzaninotto/Faker/pull/457) ([lperto](https://github.com/lperto)) +* Added PSR-4 Autoloading [\#455](https://github.com/fzaninotto/Faker/pull/455) ([GrahamCampbell](https://github.com/GrahamCampbell)) +* Added Uganda (en_UG) locale [\#454](https://github.com/fzaninotto/Faker/pull/454) ([tharoldD](https://github.com/tharoldD)) +* Added `regexify` formatter, generating a random string based on a regular expression [\#453](https://github.com/fzaninotto/Faker/pull/453) ([fzaninotto](https://github.com/fzaninotto)) +* Added shuffle formatter, to shuffle an array or a string [\#452](https://github.com/fzaninotto/Faker/pull/452) ([fzaninotto](https://github.com/fzaninotto)) +* Added ISBN-10 & ISBN-13 codes formatters to Barcode provider [\#451](https://github.com/fzaninotto/Faker/pull/451) ([gietos](https://github.com/gietos)) +* Fixed Russian (ru\_RU) middle names (different for different genders) [\#450](https://github.com/fzaninotto/Faker/pull/450) ([gietos](https://github.com/gietos)) +* Fixed Ukranian (uk\_UA) Person provider [\#448](https://github.com/fzaninotto/Faker/pull/448) ([aivus](https://github.com/aivus)) +* Added Vietnamese (vi\_VN) locale [\#447](https://github.com/fzaninotto/Faker/pull/447) ([huy95](https://github.com/huy95)) +* Added type hint to the Documentor constructor [\#446](https://github.com/fzaninotto/Faker/pull/446) ([JeroenDeDauw](https://github.com/JeroenDeDauw)) +* Fixed Russian (ru\_RU) Person provider (joined names) [\#445](https://github.com/fzaninotto/Faker/pull/445) ([aivus](https://github.com/aivus)) +* Added English (en\_GB) `mobileNumber` methods [\#438](https://github.com/fzaninotto/Faker/pull/438) ([daveblake](https://github.com/daveblake)) +* Added Traditional Chinese (zh\_TW) Realtext provider [\#434](https://github.com/fzaninotto/Faker/pull/434) ([tzhuan](https://github.com/tzhuan)) +* Fixed first name in Spanish for Argentina (es\_AR) Person provider [\#433](https://github.com/fzaninotto/Faker/pull/433) ([fzaninotto](https://github.com/fzaninotto)) +* Fixed Canadian (en_CA) state abbreviation for Nunavut [\#430](https://github.com/fzaninotto/Faker/pull/430) ([julien-c](https://github.com/julien-c)) +* Added CakePHP ORM entity populator [\#428](https://github.com/fzaninotto/Faker/pull/428) ([jadb](https://github.com/jadb)) +* Added Traditional Chinese (zh\_TW) locale [\#427](https://github.com/fzaninotto/Faker/pull/427) ([tzhuan](https://github.com/tzhuan)) +* Fixed typo in Doctrine Populator phpDoc [\#425](https://github.com/fzaninotto/Faker/pull/425) ([ihsanudin](https://github.com/ihsanudin)) +* Added Chinese (zh_CN) Internet provider [\#424](https://github.com/fzaninotto/Faker/pull/424) ([Lisso-Me](https://github.com/Lisso-Me)) +* Added Country ISO 3166-1 alpha-3 code to the Miscellaneous provider[\#422](https://github.com/fzaninotto/Faker/pull/422) ([gido](https://github.com/gido)) +* Added English (en\_GB) Person provider [\#421](https://github.com/fzaninotto/Faker/pull/421) ([AlexCutts](https://github.com/AlexCutts)) +* Added missing tests for the Color Provider [\#420](https://github.com/fzaninotto/Faker/pull/420) ([bessl](https://github.com/bessl)) +* Added Nepali (ne\_NP) locale [\#419](https://github.com/fzaninotto/Faker/pull/419) ([ankitpokhrel](https://github.com/ankitpokhrel)) +* Fixed latitude and longitude formatters bug (numeric value out of range for 32bits) [\#416](https://github.com/fzaninotto/Faker/pull/416) ([fzaninotto](https://github.com/fzaninotto)) +* Added a dedicated calculator Luhn calculator service [\#414](https://github.com/fzaninotto/Faker/pull/414) ([fzaninotto](https://github.com/fzaninotto)) +* Fixed Russian (ru_RU) Person provider (removed lowercase duplications) [\#413](https://github.com/fzaninotto/Faker/pull/413) ([Ragazzo](https://github.com/Ragazzo)) +* Fixed barcode formatter (improved speed, added tests) [\#412](https://github.com/fzaninotto/Faker/pull/412) ([fzaninotto](https://github.com/fzaninotto)) +* Added ipv4 and barcode formatters tests [\#410](https://github.com/fzaninotto/Faker/pull/410) ([bessl](https://github.com/bessl)) +* Fixed typos in various comments blocks [\#409](https://github.com/fzaninotto/Faker/pull/409) ([bessl](https://github.com/bessl)) +* Fixed InternetTest (replaced regex with PHP filter) [\#406](https://github.com/fzaninotto/Faker/pull/406) ([bessl](https://github.com/bessl)) +* Added password formatter to the Internet provider[\#402](https://github.com/fzaninotto/Faker/pull/402) ([fzaninotto](https://github.com/fzaninotto)) +* Added Company and Internet Austrian (de\_AT) Providers [\#400](https://github.com/fzaninotto/Faker/pull/400) ([bessl](https://github.com/bessl)) +* Added third-party libraries section in README [\#399](https://github.com/fzaninotto/Faker/pull/399) ([fzaninotto](https://github.com/fzaninotto)) +* Added Spanish for Venezuela (es\_VE) locale [\#398](https://github.com/fzaninotto/Faker/pull/398) ([DIOHz0r](https://github.com/DIOHz0r)) +* Added PhoneNumber Autrian (de\_AT) Provider, and missing test for the 'locale' method. [\#395](https://github.com/fzaninotto/Faker/pull/395) ([bessl](https://github.com/bessl)) +* Removed wrongly localized Lorem provider [\#394](https://github.com/fzaninotto/Faker/pull/394) ([fzaninotto](https://github.com/fzaninotto)) +* Fixed Miscellaneous provider (made the `locale` formatter static) [\#390](https://github.com/fzaninotto/Faker/pull/390) ([bessl](https://github.com/bessl)) +* Added a unit test file for the Miscellaneous Provider [\#389](https://github.com/fzaninotto/Faker/pull/389) ([bessl](https://github.com/bessl)) +* Added warning in README about using `rand()`` and the seed functions [\#386](https://github.com/fzaninotto/Faker/pull/386) ([paulvalla](https://github.com/paulvalla)) +* Fixed French (fr\_FR) Person provider (Uppercased a first name) [\#385](https://github.com/fzaninotto/Faker/pull/385) ([netcarver](https://github.com/netcarver)) +* Added Russian (ru\_RU) and Ukrainian (uk\_UA) Text providers [\#383](https://github.com/fzaninotto/Faker/pull/383) ([terion-name](https://github.com/terion-name)) +* Added more street prefixes to French (fr\_FR) Address provider [\#381](https://github.com/fzaninotto/Faker/pull/381) ([ronanguilloux](https://github.com/ronanguilloux)) +* Added PHP 5.6 to CI targets [\#378](https://github.com/fzaninotto/Faker/pull/378) ([GrahamCampbell](https://github.com/GrahamCampbell)) +* Fixed spaces remaining at the end of liine in various files [\#377](https://github.com/fzaninotto/Faker/pull/377) ([GrahamCampbell](https://github.com/GrahamCampbell)) +* Fixed UserAgent provider (added space before processor on linux platform) [\#374](https://github.com/fzaninotto/Faker/pull/374) ([TomK](https://github.com/TomK)) +* Added Company generator for Russian (ru\_RU) locale [\#371](https://github.com/fzaninotto/Faker/pull/371) ([kix](https://github.com/kix)) +* Fixed Russian (ru\_RU) Color provider (uppercase letters) [\#370](https://github.com/fzaninotto/Faker/pull/370) ([semanser](https://github.com/semanser)) +* Added more Polish (pl\_PL) phone numbers [\#369](https://github.com/fzaninotto/Faker/pull/369) ([piotrantosik](https://github.com/piotrantosik)) +* Fixed Ruby Faker link in readme [\#368](https://github.com/fzaninotto/Faker/pull/368) ([philsturgeon](https://github.com/philsturgeon)) +* Added more Japanese (ja\_JP) names in Person provider [\#366](https://github.com/fzaninotto/Faker/pull/366) ([kumamidori](https://github.com/kumamidori)) +* Added Slovenian (sl\_SL) locale [\#363](https://github.com/fzaninotto/Faker/pull/363) ([alesf](https://github.com/alesf)) +* Fixed German (de\_DE) Person provider (first names) [\#362](https://github.com/fzaninotto/Faker/pull/362) ([mikehaertl](https://github.com/mikehaertl)) +* Fixed Ukrainian (uk\_UA) Person providr (there is no such letter "ы" in Ukrainian) [\#359](https://github.com/fzaninotto/Faker/pull/359) ([nazar-pc](https://github.com/nazar-pc)) +* Fixed Chinese (zh\_CN) PhoneNumber provider (the length of mobile phone number is 11) [\#358](https://github.com/fzaninotto/Faker/pull/358) ([byan](https://github.com/byan)) +* Added Arabic (ar_\JO) Locale [\#357](https://github.com/fzaninotto/Faker/pull/357) ([zrashwani](https://github.com/zrashwani)) +* Fixed Czech (cs\_CZ) Person provider (missing lowercase in last name) [\#355](https://github.com/fzaninotto/Faker/pull/355) ([halaxa](https://github.com/halaxa)) +* Fixed French for Belgium (fr\_BE) Address Provider (doubled city names) [\#354](https://github.com/fzaninotto/Faker/pull/354) ([miclf](https://github.com/miclf)) +* Added Biased Integer Provider [\#332](https://github.com/fzaninotto/Faker/pull/332) ([TimWolla](https://github.com/TimWolla)) +* Added Swedish (sv\_SE) locale [\#316](https://github.com/fzaninotto/Faker/pull/316) ([ulrikjohansson](https://github.com/ulrikjohansson)) +* Added English for New Zealand (en\_NZ) locale [\#283](https://github.com/fzaninotto/Faker/pull/283) ([JasonMortonNZ](https://github.com/JasonMortonNZ)) +* Added mention of external Provider for cron expressions to readme[\#498](https://github.com/fzaninotto/Faker/pull/498) ([swekaj](https://github.com/swekaj)) + +## 2014-06-04, v1.4.0 + +* Fixed typo in Slovak person names (cinan) +* Added tests for uk_UA providers (serge-kuharev) +* Fixed numerify() performance by making it 30% faster (fzaninotto) +* Added strict option to randomNumber to force number of digits (fzaninotto) +* Fixed randomNumber usage duplicating numberBetween (fzaninotto) +* Fixed address provider for latvian language (MatissJA) +* Added Czech Republic (cs_CZ) address, company, datetime and text providers (Mikulas) +* Fixed da_DK Person provider data containing an 'unnamed' person (tolnem) +* Added slug provider (fzaninotto) +* Fixed IDE insights for new local IP and MAC address providers (hugofonseca) +* Added firstname gender method to all Person providers (csanquer) +* Fixed tr_TR email service, city name, person, and phone number formats (ogunkarakus) +* Fixed US_en state list (fzaninotto) +* Fixed en_US address provider so state abbr are ISO 3166 codes (Garbee) +* Added local IP and MAC address providers (kielabokkie) +* Fixed typo in century list affecting the century provider (fzaninotto) +* Added default value to optional modifier (joshuajabbour) +* Fixed Portuguese phonenumbers have 9 digits (hugofonseca) +* Added fileCopy to File provider to simulate file upload (stefanosala) +* Added pt_PT providers (hugofonseca) +* Fixed dead code in text provider (hugofonseca) +* Fixed IDE insights for magic properties (hugofonseca) +* Added tin (NIF) generator for pt_PT provider (hugofonseca) +* Fixed numberBetween max default value handling (fzaninotto) +* Added pt_PT phone number provider (hugofonseca) +* Fixed PSR-2 standards and add make task to force it on Travis (terite) +* Added new ro_RO Personal Numerical Code (CNP) and phone number providers (avataru) +* Fixed Internet provider for sk_SK locale (cinan) +* Fixed typo in en_ZA Internet provider (bjorntheart) +* Fixed phpdoc for DateTime magic methods (stof) +* Added doc about seeding with maximum timestamp using dateTime formatters (fzaninotto) +* Added Maximum Timestamp option to get always same unix timestamp when using a fixed seed (csanquer) +* Added Montenegrian (me_ME) providers (ognjenm) +* Added ean barcode provider (nineinchnick) +* Added fullPath parameter to Image provider (stefanosala) +* Added more Polish company formats (nineinchnick) +* Added Polish realText provider (nineinchnick) +* Fixed remaining non-seedable random generators (terite) +* Added randomElements provider (terite) +* Added French realText provider (fzaninotto) +* Fixed realText provider bootstrap slowness (fzaninotto) +* Added realText provider for English and German, based on Markov Chains Generator (TimWolla) +* Fixed address format in nl_NL provider (doenietzomoeilijk) +* Fixed potentially offensive word from last name list (joshuajabbour) +* Fixed reamde documentation about the optional modifier (cryode) +* Fixed Image provider and documentor routine (fzaninotto) +* Fixed IDE insights for methods (PedroTroller) +* Fixed missing data in en_US Address provider (Garbee) +* Added Bengali (bn_BD) providers (masnun) +* Fixed warning on test file when short tags are on (bateller) +* Fixed Doctrine populator undefined index warning (dbojdo) +* Added French Canadian (fr_CA) Address and Person providers (marcaube) +* Fixed typo in NullGenerator (mhanson01) +* Fixed Doctrine populator issue with one-to-one nullable relationship (jpetitcolas) +* Added Canadian English (en_CA) address and phone number providers (cviebrock) +* Fixed duplicated Payment example in readme (Garbee) +* Fixed Polish (pl_PL) Person provider data (czogori) +* Added Hungarian (hu_HU) providers (sagikazarmark) +* Added 'kana' (ja_JP) name formatters (kzykhys) +* Added allow_failure for hhvm to travis-ci and test against php 5.5 (toin0u) + +## 2013-12-16, v1.3.0 + +* Fixed state generator in Australian (en_AU) provider (sebklaus) +* Fixed IDE insights for locale specific providers (ulrikjohansson) +* Added English (South Africa) (en_ZA) person, address, Internet and phone number providers (dmfaux) +* Fixed integer values overflowing on signed INTEGER columns on Doctrine populator (Thinkscape) +* Fixed spelling error in French (fr_FR) address provider (leihog) +* Added improvements based on SensioLabsInsights analysis +* Fixed Italian (it_IT) email provider (garak) +* Added Spanish (es_ES) Internet provider (eusonlito) +* Added English Philippines (en_PH) address provider (kamote) +* Added Brazilian (pt_BR) email provider data (KennedyTedesco) +* Fixed UK country code (pgscandeias) +* Added Peruvian (es_PE) person, address, phone number, and company providers (cslucano) +* Added Ukrainian (uk_UA) color provider (ruden) +* Fixed Ukrainian (uk_UA) namespace and email translitteration (ruden) +* Added Romanian (Moldova) (ro_MD) person, address, and phone number providers (AlexanderC) +* Added IBAN generator for every currently known locale that uses it (nineinchnick) +* Added Image generation powered by LoremPixel (weotch) +* Fixed missing timezone with dateTimeBetween (baldurrensch) +* Fixed call to undefined method cardType in Payment (WMeldon) +* Added Romanian (ro_RO) address and person providers (calina-c) +* Fixed Doctrine populator to use ObjectManager instead of EntityManagerInterface (mgiustiniani) +* Fixed docblock for Provider\Base::unique() (pschultz) +* Added Payment providers (creditCardType, creditCardNumber, creditCardExpirationDate, creditCardExpirationDateString) (pomaxa) +* Added unique() modifier +* Added IDE insights to allow better intellisense/phpStorm autocompletion (thallisphp) +* Added Polish (pl_PL) address provider, personal identity number and pesel number generator (nineinchnick) +* Added Turkish (tr_TR) address provider, and improved internet provider (hasandz) +* Fixed Propel column number guesser to use signed range of values (gunnarlium) +* Added Greek (el_GR) person, address, and phone number providers (georgeharito) +* Added Austrian (en_AU) address, Internet, and phone number providers (rcuddy) +* Fixed phpDoc in Doctrine Entity populator (rogamoore) +* Added French (fr_FR) phone number formats (vchabot) +* Added optional() modifier (weotch) +* Fixed typo in the Person provider documentation (jtreminio) +* Fixed Russian (ru_RU) person format (alexshadow007) +* Added Japanese (ja_JP) person, address, Internet, phone number, and company providers (kumamidori) +* Added color providers, driver license and passport number formats for the ru_RU locale (pomaxa) +* Added Latvian (lv_LV) person, address, Internet, and phone number providers (pomaxa) +* Added Brazilian (pt_BR) Internet provider (vjnrv) +* Added more Czech (cs_CZ) lastnames (petrkle) +* Added Chinese Simplified (zh_CN) person, address, Internet, and phone number providers (tlikai) +* Fixed Typos (pborelli) +* Added Color provider with hexColor, rgbColor, rgbColorAsArray, rgbCssColor, safeColorName, and colorName formatters (lsv) +* Added support for associative arrays in `randomElement` (aRn0D) + +## 2013-06-09, v1.2.0 + +* Added new provider for fr_BE locale (jflefebvre) +* Updated locale provider to use a static locale list (spawn-guy) +* Fixed invalid UTF-8 sequence in domain provider with the Bulgarian provider (Dynom) +* Fixed the nl_NL Person provider (Dynom) +* Removed all requires and added the autoload definition to composer (Dynom) +* Fixed encoding problems in nl_NL Address provider (Dynom) +* Added icelandic provider (is_IS) (birkir) +* Added en_CA address and phone numbers (cviebrock) +* Updated safeEmail provider to be really safe (TimWolla) +* Documented alternative randomNumber usage (Seldaek) +* Added basic file provider (anroots) +* Fixed use of fourth argument on Doctrine addEntity (ecentinela) +* Added nl_BE provider (wimvds) +* Added Random Float provider (csanquer) +* Fixed bug in Faker\ORM\Doctrine\Populator (mmf-amarcos) +* Updated ru_RU provider (rmrevin) +* Added safe email domain provider (csanquer) +* Fixed latitude provider (rumpl) +* Fixed unpredictability of fake data generated by Faker\Provider\Base::numberBetween() (goatherd) +* Added uuid provider (goatherd) +* Added possibility to call methods on Doctrine entities, possibility to generate unique id (nenadalm) +* Fixed prefixes typos in 'pl_PL' Person provider (krymen) +* Added more fake data to the Ukraininan providers (lysenkobv) +* Added more fake data to the Italian providers (EmanueleMinotto) +* Fixed spaces appearing in generated emails (alchy58) +* Added Armenian (hy_AM) provider (artash) +* Added Generation of valid SIREN & SIRET codes to French providers (alexsegura) +* Added Dutch (nl_NL) provider (WouterJ) +* Fixed missing typehint in Base::__construct() (benja-M-1) +* Fixed typo in README (benja-M-1) +* Added Ukrainian (ua_UA) provider (rsvasilyev) +* Added Turkish (tr_TR) Provider (faridmovsumov) +* Fixed executable bit in some PHP files (siwinski) +* Added Brazilian Portuguese (pt_BR) provider (oliveiraev) +* Added Spanish (es_ES) provider (ivannis) +* Fixed Doctrine populator to allow for the population of entity data that has associations to other entities (afishnamedsquish) +* Added Danish (da_DK) providers (toin0u) +* Cleaned up whitespaces (toin0u) +* Fixed utf-8 bug with lowercase generators (toin0u) +* Fixed composer.json (Seldaek) +* Fixed bug in Doctrine EntityPopulator (beberlei) +* Added Finnish (fi_FI) provider (drodil) + +## 2012-10-29, v1.1.0 + +* Updated text provider to return paragraphs as a string instead of array. Great for populating markdown textarea fields (Seldaek) +* Updated dateTimeBetween to accept DateTime instances (Seldaek) +* Added random number generator between a and b, simply like rand() (Seldaek) +* Fixed spaces in generated emails (blaugueux) +* Fixed Person provider in Russian locale (Isamashii) +* Added new UserAgent provider (blaugueux) +* Added locale generator to Miscellaneous provider (blaugueux) +* Added timezone generator to DateTime provider (blaugueux) +* Added new generators to French Address providers (departments, regions) (geoffrey-brier) +* Added new generators to French Company provider (catch phrase, SIREN, and SIRET numbers) (geoffrey-brier) +* Added state generator to German Address provider (Powerhamster) +* Added Slovak provider (bazo) +* Added latitude and longitude formatters to Address provider (fixe) +* Added Serbian provider (umpirsky) + +## 2012-07-10, v1.0.0 + +* Initial Version diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..99ed007 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2011 François Zaninotto +Portions Copyright (c) 2008 Caius Durling +Portions Copyright (c) 2008 Adam Royle +Portions Copyright (c) 2008 Fiona Burrows + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0353b31 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "siteworxpro/faker", + "type": "library", + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "faker", + "fixtures", + "data" + ], + "license": "MIT", + "authors": [ + { + "name": "François Zaninotto" + } + ], + "require": { + "php": "^8.1" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^9.5.10", + "squizlabs/php_codesniffer": "^3.6.1", + "slevomat/coding-standard": "^7.0.18" + }, + "autoload": { + "psr-4": { + "Siteworx\\Faker\\": "src/Faker/" + } + }, + "autoload-dev": { + "psr-4": { + "Siteworx\\Faker\\Test\\": "test/Faker/" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a1cca58 --- /dev/null +++ b/readme.md @@ -0,0 +1,1794 @@ +# Faker + +Faker is a PHP library that generates fake data for you. Whether you need to bootstrap your database, create good-looking XML documents, fill-in your persistence to stress test it, or anonymize data taken from a production service, Faker is for you. + +Faker is heavily inspired by Perl's [Data::Faker](http://search.cpan.org/~jasonk/Data-Faker-0.07/), and by ruby's [Faker](https://rubygems.org/gems/faker). + +Faker requires PHP >= 7.4 + +# Table of Contents + +- [Installation](#installation) +- [Basic Usage](#basic-usage) +- [Formatters](#formatters) + - [Base](#fakerproviderbase) + - [Lorem Ipsum Text](#fakerproviderlorem) + - [Person](#fakerprovideren_usperson) + - [Address](#fakerprovideren_usaddress) + - [Phone Number](#fakerprovideren_usphonenumber) + - [Company](#fakerprovideren_uscompany) + - [Real Text](#fakerprovideren_ustext) + - [Date and Time](#fakerproviderdatetime) + - [Internet](#fakerproviderinternet) + - [User Agent](#fakerprovideruseragent) + - [Payment](#fakerproviderpayment) + - [Color](#fakerprovidercolor) + - [File](#fakerproviderfile) + - [Image](#fakerproviderimage) + - [Uuid](#fakerprovideruuid) + - [Barcode](#fakerproviderbarcode) + - [Miscellaneous](#fakerprovidermiscellaneous) + - [Biased](#fakerproviderbiased) + - [Html Lorem](#fakerproviderhtmllorem) +- [Modifiers](#modifiers) +- [Localization](#localization) +- [Populating Entities Using an ORM or an ODM](#populating-entities-using-an-orm-or-an-odm) +- [Seeding the Generator](#seeding-the-generator) +- [Faker Internals: Understanding Providers](#faker-internals-understanding-providers) +- [Real Life Usage](#real-life-usage) +- [Language specific formatters](#language-specific-formatters) +- [Third-Party Libraries Extending/Based On Faker](#third-party-libraries-extendingbased-on-faker) +- [License](#license) + + +## Installation + +```sh +composer require fzaninotto/faker +``` + +## Basic Usage + +### Autoloading + +Faker supports both `PSR-0` as `PSR-4` autoloaders. +```php +name; + // 'Lucy Cechtelar'; +echo $faker->address; + // "426 Jordy Lodge + // Cartwrightshire, SC 88120-6700" +echo $faker->text; + // Dolores sit sint laboriosam dolorem culpa et autem. Beatae nam sunt fugit + // et sit et mollitia sed. + // Fuga deserunt tempora facere magni omnis. Omnis quia temporibus laudantium + // sit minima sint. +``` + +Even if this example shows a property access, each call to `$faker->name` yields a different (random) result. This is because Faker uses `__get()` magic, and forwards `Faker\Generator->$property` calls to `Faker\Generator->format($property)`. + +```php +name, "\n"; +} + // Adaline Reichel + // Dr. Santa Prosacco DVM + // Noemy Vandervort V + // Lexi O'Conner + // Gracie Weber + // Roscoe Johns + // Emmett Lebsack + // Keegan Thiel + // Wellington Koelpin II + // Ms. Karley Kiehn V +``` + +**Tip**: For a quick generation of fake data, you can also use Faker as a command line tool thanks to [faker-cli](https://github.com/bit3/faker-cli). + +## Formatters + +Each of the generator properties (like `name`, `address`, and `lorem`) are called "formatters". A faker generator has many of them, packaged in "providers". Here is a list of the bundled formatters in the default locale. + +### `Faker\Provider\Base` + + randomDigit // 7 + randomDigitNot(5) // 0, 1, 2, 3, 4, 6, 7, 8, or 9 + randomDigitNotNull // 5 + randomNumber($nbDigits = NULL, $strict = false) // 79907610 + randomFloat($nbMaxDecimals = NULL, $min = 0, $max = NULL) // 48.8932 + numberBetween($min = 1000, $max = 9000) // 8567 + randomLetter // 'b' + // returns randomly ordered subsequence of a provided array + randomElements($array = array ('a','b','c'), $count = 1) // array('c') + randomElement($array = array ('a','b','c')) // 'b' + shuffle('hello, world') // 'rlo,h eoldlw' + shuffle(array(1, 2, 3)) // array(2, 1, 3) + numerify('Hello ###') // 'Hello 609' + lexify('Hello ???') // 'Hello wgt' + bothify('Hello ##??') // 'Hello 42jz' + asciify('Hello ***') // 'Hello R6+' + regexify('[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'); // sm0@y8k96a.ej + +### `Faker\Provider\Lorem` + + word // 'aut' + words($nb = 3, $asText = false) // array('porro', 'sed', 'magni') + sentence($nbWords = 6, $variableNbWords = true) // 'Sit vitae voluptas sint non voluptates.' + sentences($nb = 3, $asText = false) // array('Optio quos qui illo error.', 'Laborum vero a officia id corporis.', 'Saepe provident esse hic eligendi.') + paragraph($nbSentences = 3, $variableNbSentences = true) // 'Ut ab voluptas sed a nam. Sint autem inventore aut officia aut aut blanditiis. Ducimus eos odit amet et est ut eum.' + paragraphs($nb = 3, $asText = false) // array('Quidem ut sunt et quidem est accusamus aut. Fuga est placeat rerum ut. Enim ex eveniet facere sunt.', 'Aut nam et eum architecto fugit repellendus illo. Qui ex esse veritatis.', 'Possimus omnis aut incidunt sunt. Asperiores incidunt iure sequi cum culpa rem. Rerum exercitationem est rem.') + text($maxNbChars = 200) // 'Fuga totam reiciendis qui architecto fugiat nemo. Consequatur recusandae qui cupiditate eos quod.' + +### `Faker\Provider\en_US\Person` + + title($gender = null|'male'|'female') // 'Ms.' + titleMale // 'Mr.' + titleFemale // 'Ms.' + suffix // 'Jr.' + name($gender = null|'male'|'female') // 'Dr. Zane Stroman' + firstName($gender = null|'male'|'female') // 'Maynard' + firstNameMale // 'Maynard' + firstNameFemale // 'Rachel' + lastName // 'Zulauf' + +### `Faker\Provider\en_US\Address` + + cityPrefix // 'Lake' + secondaryAddress // 'Suite 961' + state // 'NewMexico' + stateAbbr // 'OH' + citySuffix // 'borough' + streetSuffix // 'Keys' + buildingNumber // '484' + city // 'West Judge' + streetName // 'Keegan Trail' + streetAddress // '439 Karley Loaf Suite 897' + postcode // '17916' + address // '8888 Cummings Vista Apt. 101, Susanbury, NY 95473' + country // 'Falkland Islands (Malvinas)' + latitude($min = -90, $max = 90) // 77.147489 + longitude($min = -180, $max = 180) // 86.211205 + +### `Faker\Provider\en_US\PhoneNumber` + + phoneNumber // '201-886-0269 x3767' + tollFreePhoneNumber // '(888) 937-7238' + e164PhoneNumber // '+27113456789' + +### `Faker\Provider\en_US\Company` + + catchPhrase // 'Monitored regional contingency' + bs // 'e-enable robust architectures' + company // 'Bogan-Treutel' + companySuffix // 'and Sons' + jobTitle // 'Cashier' + +### `Faker\Provider\en_US\Text` + + realText($maxNbChars = 200, $indexSize = 2) // "And yet I wish you could manage it?) 'And what are they made of?' Alice asked in a shrill, passionate voice. 'Would YOU like cats if you were never even spoke to Time!' 'Perhaps not,' Alice replied." + +### `Faker\Provider\DateTime` + + unixTime($max = 'now') // 58781813 + dateTime($max = 'now', $timezone = null) // DateTime('2008-04-25 08:37:17', 'UTC') + dateTimeAD($max = 'now', $timezone = null) // DateTime('1800-04-29 20:38:49', 'Europe/Paris') + iso8601($max = 'now') // '1978-12-09T10:10:29+0000' + date($format = 'Y-m-d', $max = 'now') // '1979-06-09' + time($format = 'H:i:s', $max = 'now') // '20:49:42' + dateTimeBetween($startDate = '-30 years', $endDate = 'now', $timezone = null) // DateTime('2003-03-15 02:00:49', 'Africa/Lagos') + dateTimeInInterval($startDate = '-30 years', $interval = '+ 5 days', $timezone = null) // DateTime('2003-03-15 02:00:49', 'Antartica/Vostok') + dateTimeThisCentury($max = 'now', $timezone = null) // DateTime('1915-05-30 19:28:21', 'UTC') + dateTimeThisDecade($max = 'now', $timezone = null) // DateTime('2007-05-29 22:30:48', 'Europe/Paris') + dateTimeThisYear($max = 'now', $timezone = null) // DateTime('2011-02-27 20:52:14', 'Africa/Lagos') + dateTimeThisMonth($max = 'now', $timezone = null) // DateTime('2011-10-23 13:46:23', 'Antarctica/Vostok') + amPm($max = 'now') // 'pm' + dayOfMonth($max = 'now') // '04' + dayOfWeek($max = 'now') // 'Friday' + month($max = 'now') // '06' + monthName($max = 'now') // 'January' + year($max = 'now') // '1993' + century // 'VI' + timezone // 'Europe/Paris' + +Methods accepting a `$timezone` argument default to `date_default_timezone_get()`. You can pass a custom timezone string to each method, or define a custom timezone for all time methods at once using `$faker::setDefaultTimezone($timezone)`. + +### `Faker\Provider\Internet` + + email // 'tkshlerin@collins.com' + safeEmail // 'king.alford@example.org' + freeEmail // 'bradley72@gmail.com' + companyEmail // 'russel.durward@mcdermott.org' + freeEmailDomain // 'yahoo.com' + safeEmailDomain // 'example.org' + userName // 'wade55' + password // 'k&|X+a45*2[' + domainName // 'wolffdeckow.net' + domainWord // 'feeney' + tld // 'biz' + url // 'http://www.skilesdonnelly.biz/aut-accusantium-ut-architecto-sit-et.html' + slug // 'aut-repellat-commodi-vel-itaque-nihil-id-saepe-nostrum' + ipv4 // '109.133.32.252' + localIpv4 // '10.242.58.8' + ipv6 // '8e65:933d:22ee:a232:f1c1:2741:1f10:117c' + macAddress // '43:85:B7:08:10:CA' + +### `Faker\Provider\UserAgent` + + userAgent // 'Mozilla/5.0 (Windows CE) AppleWebKit/5350 (KHTML, like Gecko) Chrome/13.0.888.0 Safari/5350' + chrome // 'Mozilla/5.0 (Macintosh; PPC Mac OS X 10_6_5) AppleWebKit/5312 (KHTML, like Gecko) Chrome/14.0.894.0 Safari/5312' + firefox // 'Mozilla/5.0 (X11; Linuxi686; rv:7.0) Gecko/20101231 Firefox/3.6' + safari // 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_7_1 rv:3.0; en-US) AppleWebKit/534.11.3 (KHTML, like Gecko) Version/4.0 Safari/534.11.3' + opera // 'Opera/8.25 (Windows NT 5.1; en-US) Presto/2.9.188 Version/10.00' + internetExplorer // 'Mozilla/5.0 (compatible; MSIE 7.0; Windows 98; Win 9x 4.90; Trident/3.0)' + +### `Faker\Provider\Payment` + + creditCardType // 'MasterCard' + creditCardNumber // '4485480221084675' + creditCardExpirationDate // 04/13 + creditCardExpirationDateString // '04/13' + creditCardDetails // array('MasterCard', '4485480221084675', 'Aleksander Nowak', '04/13') + // Generates a random IBAN. Set $countryCode to null for a random country + iban($countryCode) // 'IT31A8497112740YZ575DJ28BP4' + swiftBicNumber // 'RZTIAT22263' + +### `Faker\Provider\Color` + + hexcolor // '#fa3cc2' + rgbcolor // '0,255,122' + rgbColorAsArray // array(0,255,122) + rgbCssColor // 'rgb(0,255,122)' + safeColorName // 'fuchsia' + colorName // 'Gainsbor' + hslColor // '340,50,20' + hslColorAsArray // array(340,50,20) + +### `Faker\Provider\File` + + fileExtension // 'avi' + mimeType // 'video/x-msvideo' + // Copy a random file from the source to the target directory and returns the fullpath or filename + file($sourceDir = '/tmp', $targetDir = '/tmp') // '/path/to/targetDir/13b73edae8443990be1aa8f1a483bc27.jpg' + file($sourceDir, $targetDir, false) // '13b73edae8443990be1aa8f1a483bc27.jpg' + +### `Faker\Provider\Image` + + // Image generation provided by LoremPixel (http://lorempixel.com/) + imageUrl($width = 640, $height = 480) // 'http://lorempixel.com/640/480/' + imageUrl($width, $height, 'cats') // 'http://lorempixel.com/800/600/cats/' + imageUrl($width, $height, 'cats', true, 'Faker') // 'http://lorempixel.com/800/400/cats/Faker' + imageUrl($width, $height, 'cats', true, 'Faker', true) // 'http://lorempixel.com/gray/800/400/cats/Faker/' Monochrome image + image($dir = '/tmp', $width = 640, $height = 480) // '/tmp/13b73edae8443990be1aa8f1a483bc27.jpg' + image($dir, $width, $height, 'cats') // 'tmp/13b73edae8443990be1aa8f1a483bc27.jpg' it's a cat! + image($dir, $width, $height, 'cats', false) // '13b73edae8443990be1aa8f1a483bc27.jpg' it's a filename without path + image($dir, $width, $height, 'cats', true, false) // it's a no randomize images (default: `true`) + image($dir, $width, $height, 'cats', true, true, 'Faker') // 'tmp/13b73edae8443990be1aa8f1a483bc27.jpg' it's a cat with 'Faker' text. Default, `null`. + +### `Faker\Provider\Uuid` + + uuid // '7e57d004-2b97-0e7a-b45f-5387367791cd' + +### `Faker\Provider\Barcode` + + ean13 // '4006381333931' + ean8 // '73513537' + isbn13 // '9790404436093' + isbn10 // '4881416324' + +### `Faker\Provider\Miscellaneous` + + boolean // false + boolean($chanceOfGettingTrue = 50) // true + md5 // 'de99a620c50f2990e87144735cd357e7' + sha1 // 'f08e7f04ca1a413807ebc47551a40a20a0b4de5c' + sha256 // '0061e4c60dac5c1d82db0135a42e00c89ae3a333e7c26485321f24348c7e98a5' + locale // en_UK + countryCode // UK + languageCode // en + currencyCode // EUR + emoji // 😁 + +### `Faker\Provider\Biased` + + // get a random number between 10 and 20, + // with more chances to be close to 20 + biasedNumberBetween($min = 10, $max = 20, $function = 'sqrt') + +### `Faker\Provider\HtmlLorem` + + //Generate HTML document which is no more than 2 levels deep, and no more than 3 elements wide at any level. + randomHtml(2,3) // Aut illo dolorem et accusantium eum.
Id aut saepe non mollitia voluptas voluptas.Non consequatur.Incidunt est.Aut voluptatem.Officia voluptas rerum quo.Asperiores similique.
Sapiente dolorum dolorem sint laboriosam commodi qui.Commodi nihil nesciunt eveniet quo repudiandae.Voluptates explicabo numquam distinctio necessitatibus repellat.Provident ut doloremque nam eum modi aspernatur.Iusto inventore.
Animi nihil ratione id mollitia libero ipsa quia tempore.Velit est officia et aut tenetur dolorem sed mollitia expedita.Modi modi repudiandae pariatur voluptas rerum ea incidunt non molestiae eligendi eos deleniti.Exercitationem voluptatibus dolor est iste quod molestiae.Quia reiciendis.
Inventore impedit exercitationem voluptatibus rerum cupiditate.Qui.Aliquam.Autem nihil aut et.Dolor ut quia error.
Enim facilis iusto earum et minus rerum assumenda quis quia.Reprehenderit ut sapiente occaecati voluptatum dolor voluptatem vitae qui velit.Quod fugiat non.Sunt nobis totam mollitia sed nesciunt est deleniti cumque.Repudiandae quo.
Modi dicta libero quisquam doloremque qui autem.Voluptatem aliquid saepe laudantium facere eos sunt dolor.Est eos quis laboriosam officia expedita repellendus quia natus.Et neque delectus quod fugit enim repudiandae qui.Fugit soluta sit facilis facere repellat culpa magni voluptatem maiores tempora.
Enim dolores doloremque.Assumenda voluptatem eum perferendis exercitationem.Quasi in fugit deserunt ea perferendis sunt nemo consequatur dolorum soluta.Maxime repellat qui numquam voluptatem est modi.Alias rerum rerum hic hic eveniet.
Tempore voluptatem.Eaque.Et sit quas fugit iusto.Nemo nihil rerum dignissimos et esse.Repudiandae ipsum numquam.
Nemo sunt quia.Sint tempore est neque ducimus harum sed.Dicta placeat atque libero nihil.Et qui aperiam temporibus facilis eum.Ut dolores qui enim et maiores nesciunt.
Dolorum totam sint debitis saepe laborum.Quidem corrupti ea.Cum voluptas quod.Possimus consequatur quasi dolorem ut et.Et velit non hic labore repudiandae quis.
+ +## Modifiers + +Faker provides three special providers, `unique()`, `optional()`, and `valid()`, to be called before any provider. + +```php +// unique() forces providers to return unique values +$values = array(); +for ($i = 0; $i < 10; $i++) { + // get a random digit, but always a new one, to avoid duplicates + $values []= $faker->unique()->randomDigit; +} +print_r($values); // [4, 1, 8, 5, 0, 2, 6, 9, 7, 3] + +// providers with a limited range will throw an exception when no new unique value can be generated +$values = array(); +try { + for ($i = 0; $i < 10; $i++) { + $values []= $faker->unique()->randomDigitNotNull; + } +} catch (\OverflowException $e) { + echo "There are only 9 unique digits not null, Faker can't generate 10 of them!"; +} + +// you can reset the unique modifier for all providers by passing true as first argument +$faker->unique($reset = true)->randomDigitNotNull; // will not throw OverflowException since unique() was reset +// tip: unique() keeps one array of values per provider + +// optional() sometimes bypasses the provider to return a default value instead (which defaults to NULL) +$values = array(); +for ($i = 0; $i < 10; $i++) { + // get a random digit, but also null sometimes + $values []= $faker->optional()->randomDigit; +} +print_r($values); // [1, 4, null, 9, 5, null, null, 4, 6, null] + +// optional() accepts a weight argument to specify the probability of receiving the default value. +// 0 will always return the default value; 1 will always return the provider. Default weight is 0.5 (50% chance). +$faker->optional($weight = 0.1)->randomDigit; // 90% chance of NULL +$faker->optional($weight = 0.9)->randomDigit; // 10% chance of NULL + +// optional() accepts a default argument to specify the default value to return. +// Defaults to NULL. +$faker->optional($weight = 0.5, $default = false)->randomDigit; // 50% chance of FALSE +$faker->optional($weight = 0.9, $default = 'abc')->word; // 10% chance of 'abc' + +// valid() only accepts valid values according to the passed validator functions +$values = array(); +$evenValidator = function($digit) { + return $digit % 2 === 0; +}; +for ($i = 0; $i < 10; $i++) { + $values []= $faker->valid($evenValidator)->randomDigit; +} +print_r($values); // [0, 4, 8, 4, 2, 6, 0, 8, 8, 6] + +// just like unique(), valid() throws an overflow exception when it can't generate a valid value +$values = array(); +try { + $faker->valid($evenValidator)->randomElement([1, 3, 5, 7, 9]); +} catch (\OverflowException $e) { + echo "Can't pick an even number in that set!"; +} +``` + +If you would like to use a modifier with a value not generated by Faker, use the `passthrough()` method. `passthrough()` simply returns whatever value it was given. + +```php +$faker->optional()->passthrough(mt_rand(5, 15)); +``` + +## Localization + +`Faker\Factory` can take a locale as an argument, to return localized data. If no localized provider is found, the factory fallbacks to the default locale (en_US). + +```php +name, "\n"; +} + // Luce du Coulon + // Auguste Dupont + // Roger Le Voisin + // Alexandre Lacroix + // Jacques Humbert-Roy + // Thérèse Guillet-Andre + // Gilles Gros-Bodin + // Amélie Pires + // Marcel Laporte + // Geneviève Marchal +``` + +You can check available Faker locales in the source code, [under the `Provider` directory](https://github.com/fzaninotto/Faker/tree/master/src/Faker/Provider). The localization of Faker is an ongoing process, for which we need your help. Don't hesitate to create localized providers to your own locale and submit a PR! + +## Populating Entities Using an ORM or an ODM + +Faker provides adapters for Object-Relational and Object-Document Mappers (currently, [Propel](http://www.propelorm.org), [Doctrine2](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/), [CakePHP](http://cakephp.org), [Spot2](https://github.com/vlucas/spot2), [Mandango](https://github.com/mandango/mandango) and [Eloquent](https://laravel.com/docs/master/eloquent) are supported). These adapters ease the population of databases through the Entity classes provided by an ORM library (or the population of document stores using Document classes provided by an ODM library). + +To populate entities, create a new populator class (using a generator instance as parameter), then list the class and number of all the entities that must be generated. To launch the actual data population, call the `execute()` method. + +Note that some of the `populators` could require additional parameters. As example the `doctrine` populator has an option to specify +its batchSize on how often it will flush the UnitOfWork to the database. + +Here is an example showing how to populate 5 `Author` and 10 `Book` objects: + +```php +addEntity('Author', 5); +$populator->addEntity('Book', 10); +$insertedPKs = $populator->execute(); +``` + +The populator uses name and column type guessers to populate each column with relevant data. For instance, Faker populates a column named `first_name` using the `firstName` formatter, and a column with a `TIMESTAMP` type using the `dateTime` formatter. The resulting entities are therefore coherent. If Faker misinterprets a column name, you can still specify a custom closure to be used for populating a particular column, using the third argument to `addEntity()`: + +```php +addEntity('Book', 5, array( + 'ISBN' => function() use ($generator) { return $generator->ean13(); } +)); +``` + +In this example, Faker will guess a formatter for all columns except `ISBN`, for which the given anonymous function will be used. + +**Tip**: To ignore some columns, specify `null` for the column names in the third argument of `addEntity()`. This is usually necessary for columns added by a behavior: + +```php +addEntity('Book', 5, array( + 'CreatedAt' => null, + 'UpdatedAt' => null, +)); +``` + +Of course, Faker does not populate autoincremented primary keys. In addition, `Faker\ORM\Propel\Populator::execute()` returns the list of inserted PKs, indexed by class: + +```php + (34, 35, 36, 37, 38), +// 'Book' => (456, 457, 458, 459, 470, 471, 472, 473, 474, 475) +// ) +``` + +**Note:** Due to the fact that `Faker` returns all the primary keys inserted, the memory consumption will go up drastically when you do batch inserts due to the big list of data. + +In the previous example, the `Book` and `Author` models share a relationship. Since `Author` entities are populated first, Faker is smart enough to relate the populated `Book` entities to one of the populated `Author` entities. + +Lastly, if you want to execute an arbitrary function on an entity before insertion, use the fourth argument of the `addEntity()` method: + +```php +addEntity('Book', 5, array(), array( + function($book) { $book->publish(); }, +)); +``` + +## Seeding the Generator + +You may want to get always the same generated data - for instance when using Faker for unit testing purposes. The generator offers a `seed()` method, which seeds the random number generator. Calling the same script twice with the same seed produces the same results. + +```php +seed(1234); + +echo $faker->name; // 'Jess Mraz I'; +``` + +> **Tip**: DateTime formatters won't reproduce the same fake data if you don't fix the `$max` value: +> +> ```php +> // even when seeded, this line will return different results because $max varies +> $faker->dateTime(); // equivalent to $faker->dateTime($max = 'now') +> // make sure you fix the $max parameter +> $faker->dateTime('2014-02-25 08:37:17'); // will return always the same date when seeded +> ``` +> +> **Tip**: Formatters won't reproduce the same fake data if you use the `rand()` php function. Use `$faker` or `mt_rand()` instead: +> +> ```php +> // bad +> $faker->realText(rand(10,20)); +> // good +> $faker->realText($faker->numberBetween(10,20)); +> ``` + + + +## Faker Internals: Understanding Providers + +A `Faker\Generator` alone can't do much generation. It needs `Faker\Provider` objects to delegate the data generation to them. `Faker\Factory::create()` actually creates a `Faker\Generator` bundled with the default providers. Here is what happens under the hood: + +```php +addProvider(new Siteworx\Faker\Provider\en_US\Person($faker)); +$faker->addProvider(new Siteworx\Faker\Provider\en_US\Address($faker)); +$faker->addProvider(new Siteworx\Faker\Provider\en_US\PhoneNumber($faker)); +$faker->addProvider(new Siteworx\Faker\Provider\en_US\Company($faker)); +$faker->addProvider(new Siteworx\Faker\Provider\Lorem($faker)); +$faker->addProvider(new Siteworx\Faker\Provider\Internet($faker)); +```` + +Whenever you try to access a property on the `$faker` object, the generator looks for a method with the same name in all the providers attached to it. For instance, calling `$faker->name` triggers a call to `Faker\Provider\Person::name()`. And since Faker starts with the last provider, you can easily override existing formatters: just add a provider containing methods named after the formatters you want to override. + +That means that you can easily add your own providers to a `Faker\Generator` instance. A provider is usually a class extending `\Faker\Provider\Base`. This parent class allows you to use methods like `lexify()` or `randomNumber()`; it also gives you access to formatters of other providers, through the protected `$generator` property. The new formatters are the public methods of the provider class. + +Here is an example provider for populating Book data: + +```php +generator->sentence($nbWords); + return substr($sentence, 0, strlen($sentence) - 1); + } + + public function ISBN() + { + return $this->generator->ean13(); + } +} +``` + +To register this provider, just add a new instance of `\Faker\Provider\Book` to an existing generator: + +```php +addProvider(new \Siteworx\Faker\Provider\Book($faker)); +``` + +Now you can use the two new formatters like any other Faker formatter: + +```php +setTitle($faker->title); +$book->setISBN($faker->ISBN); +$book->setSummary($faker->text); +$book->setPrice($faker->randomNumber(2)); +``` + +**Tip**: A provider can also be a Plain Old PHP Object. In that case, all the public methods of the provider become available to the generator. + +## Real Life Usage + +The following script generates a valid XML document: + +```php + + + + + + +boolean(25)): ?> + + +
+ streetAddress ?> + city ?> + postcode ?> + state ?> +
+ +boolean(33)): ?> + bs ?> + +boolean(33)): ?> + + + +boolean(15)): ?> +
+text(400) ?> +]]> +
+ +
+ +
+``` + +Running this script produces a document looking like: + +```xml + + + + +
+ 182 Harrison Cove + North Lloyd + 45577 + Alabama +
+ + orchestrate compelling web-readiness + +
+ +
+
+ + +
+ 90111 Hegmann Inlet + South Geovanymouth + 69961-9311 + Colorado +
+ + +
+ + +
+ 9791 Nona Corner + Harberhaven + 74062-8191 + RhodeIsland +
+ + +
+ + +
+ 11161 Schultz Via + Feilstad + 98019 + NewJersey +
+ + + +
+ +
+
+ + +
+ 6106 Nader Village Suite 753 + McLaughlinstad + 43189-8621 + Missouri +
+ + expedite viral synergies + + +
+ + +
+ 7546 Kuvalis Plaza + South Wilfrid + 77069 + Georgia +
+ + +
+ + + +
+ 478 Daisha Landing Apt. 510 + West Lizethhaven + 30566-5362 + WestVirginia +
+ + orchestrate dynamic networks + + +
+ +
+
+ + +
+ 1251 Koelpin Mission + North Revastad + 81620 + Maryland +
+ + +
+ + +
+ 6396 Langworth Hills Apt. 446 + New Carlos + 89399-0268 + Wyoming +
+ + + +
+ + + +
+ 2246 Kreiger Station Apt. 291 + Kaydenmouth + 11397-1072 + Wyoming +
+ + grow sticky portals + +
+ +
+
+
+``` + +## Language specific formatters + +### `Faker\Provider\ar_SA\Person` + +```php +idNumber; // ID number +echo $faker->nationalIdNumber // Citizen ID number +echo $faker->foreignerIdNumber // Foreigner ID number +echo $faker->companyIdNumber // Company ID number +``` + +### `Faker\Provider\ar_SA\Payment` + +```php +bankAccountNumber // "SA0218IBYZVZJSEC8536V4XC" +``` + +### `Faker\Provider\at_AT\Payment` + +```php +vat; // "AT U12345678" - Austrian Value Added Tax number +echo $faker->vat(false); // "ATU12345678" - unspaced Austrian Value Added Tax number +``` + +### `Faker\Provider\bg_BG\Payment` + +```php +vat; // "BG 0123456789" - Bulgarian Value Added Tax number +echo $faker->vat(false); // "BG0123456789" - unspaced Bulgarian Value Added Tax number +``` + +### `Faker\Provider\cs_CZ\Address` + +```php +region; // "Liberecký kraj" +``` + +### `Faker\Provider\cs_CZ\Company` + +```php +ico; // "69663963" +``` + +### `Faker\Provider\cs_CZ\DateTime` + +```php +monthNameGenitive; // "prosince" +echo $faker->formattedDate; // "12. listopadu 2015" +``` + +### `Faker\Provider\cs_CZ\Person` + +```php +birthNumber; // "7304243452" +``` + +### `Faker\Provider\da_DK\Person` + +```php +cpr; // "051280-2387" +``` + +### `Faker\Provider\da_DK\Address` + +```php +kommune; // "Frederiksberg" + +// Generates a random region name +echo $faker->region; // "Region Sjælland" +``` + +### `Faker\Provider\da_DK\Company` + +```php +cvr; // "32458723" + +// Generates a random P number +echo $faker->p; // "5398237590" +``` + +### `Faker\Provider\de_CH\Person` +```php +avs13; // "756.1234.5678.97" OR +echo $faker->ahv13; // "756.1234.5678.97" +``` + +### `Faker\Provider\de_DE\Payment` + +```php +bankAccountNumber; // "DE41849025553661169313" +echo $faker->bank; // "Volksbank Stuttgart" + +``` + +### `Faker\Provider\en_HK\Address` + +```php +town; // "Yuen Long" + +// Generates a fake village name based on the words commonly found in Hong Kong +echo $faker->village; // "O Tau" + +// Generates a fake estate name based on the words commonly found in Hong Kong +echo $faker->estate; // "Ching Lai Court" + +``` + +### `Faker\Provider\en_HK\Phone` + +```php +mobileNumber; // "92150087" + +// Generates a Hong Kong landline number (starting with 2 or 3) +echo $faker->landlineNumber; // "32750132" + +// Generates a Hong Kong fax number (starting with 7) +echo $faker->faxNumber; // "71937729" + +``` + +### `Faker\Provider\en_NG\Address` + +```php +region; // 'Katsina' +``` + +### `Faker\Provider\en_NG\Person` + +```php +name; // 'Oluwunmi Mayowa' +``` + +### `Faker\Provider\en_NZ\Phone` + +```php +mobileNumber; // "021 123 4567" + +// Generates a toll free number +echo $faker->tollFreeNumber; // "0800 123 456" + +// Area Code +echo $faker->areaCode; // "03" +``` + +### `Faker\Provider\en_US\Company` + +```php +ein; // '12-3456789' +``` + +### `Faker\Provider\en_US\Payment` + +```php +bankAccountNumber; // '51915734310' +echo $faker->bankRoutingNumber; // '212240302' +``` + +### `Faker\Provider\en_US\Person` + +```php +ssn; // '123-45-6789' +``` + +### `Faker\Provider\en_ZA\Company` + +```php +companyNumber; // 1999/789634/01 +``` + +### `Faker\Provider\en_ZA\Person` + +```php +idNumber; // 6606192211041 + +// Generates a random valid licence code +echo $faker->licenceCode; // EB +``` + +### `Faker\Provider\en_ZA\PhoneNumber` + +```php +tollFreeNumber; // 0800 555 5555 + +// Generates a mobile phone number +echo $faker->mobileNumber; // 082 123 5555 +``` + +### `Faker\Provider\es_ES\Person` + +```php +dni; // '77446565E' + +// Generates a random valid licence code +echo $faker->licenceCode; // B +``` + +### `Faker\Provider\es_ES\Payment` + +```php +vat; // "A35864370" +``` + +### `Faker\Provider\es_ES\PhoneNumber` + +```php +tollFreeNumber; // 900 123 456 + +// Generates a mobile phone number +echo $faker->mobileNumber; // +34 612 12 24 +``` + +### `Faker\Provider\es_PE\Person` + +```php +dni; // '83367512' +``` + +### `Faker\Provider\fa_IR\Person` + +```php +nationalCode; // "0078475759" +``` + +### `Faker\Provider\fa_IR\Address` + +```php +building; // "ساختمان آفتاب" + +// Returns a random city name +echo $faker->city // "استان زنجان" +``` + +### `Faker\Provider\fa_IR\Company` + +```php +contract; // "رسمی" +``` + +### `Faker\Provider\fi_FI\Payment` + +```php +bankAccountNumber; // "FI8350799879879616" +``` + +### `Faker\Provider\fi_FI\Person` + +```php +personalIdentityNumber() // '170974-007J' + +//Since the numbers are different for male and female persons, optionally you can specify gender. +echo $faker->personalIdentityNumber(\DateTime::createFromFormat('Y-m-d', '2015-12-14'), 'female') // '141215A520B' +``` + +### `Faker\Provider\fr_BE\Payment` + +```php +vat; // "BE 0123456789" - Belgian Value Added Tax number +echo $faker->vat(false); // "BE0123456789" - unspaced Belgian Value Added Tax number +``` + +### `Faker\Provider\es_VE\Person` + +```php +nationalId; // 'V11223344' +``` + +### `Faker\Provider\es_VE\Company` + +```php +taxpayerIdentificationNumber; // 'J1234567891' +``` + +### `Faker\Provider\fr_CH\Person` +```php +avs13; // "756.1234.5678.97" +``` + +### `Faker\Provider\fr_FR\Address` + +```php +departmentName; // "Haut-Rhin" + +// Generates a random department number +echo $faker->departmentNumber; // "2B" + +// Generates a random department info (department number => department name) +$faker->department; // array('18' => 'Cher'); + +// Generates a random region +echo $faker->region; // "Saint-Pierre-et-Miquelon" + +// Generates a random appartement,stair +echo $faker->secondaryAddress; // "Bat. 961" +``` + +### `Faker\Provider\fr_FR\Company` + +```php +siren; // 082 250 104 + +// Generates a random SIRET number +echo $faker->siret; // 347 355 708 00224 +``` + +### `Faker\Provider\fr_FR\Payment` + +```php +vat; // FR 12 123 456 789 +``` + +### `Faker\Provider\fr_FR\Person` + +```php +nir; // 1 88 07 35 127 571 - 19 +``` + +### `Faker\Provider\fr_FR\PhoneNumber` + +```php +phoneNumber; // +33 (0)1 67 97 01 31 +echo $faker->mobileNumber; // +33 6 21 12 72 84 +echo $faker->serviceNumber // 08 98 04 84 46 +``` + + +### `Faker\Provider\he_IL\Payment` + +```php +bankAccountNumber // "IL392237392219429527697" +``` + +### `Faker\Provider\hr_HR\Payment` + +```php +bankAccountNumber // "HR3789114847226078672" +``` + +### `Faker\Provider\hu_HU\Payment` + +```php +bankAccountNumber; // "HU09904437680048220079300783" +``` + +### `Faker\Provider\id_ID\Person` + +```php +nik(); // "8522246001570940" +``` + +### `Faker\Provider\it_CH\Person` +```php +avs13; // "756.1234.5678.97" +``` + +### `Faker\Provider\it_IT\Company` + +```php +vatId(); // "IT98746784967" +``` + +### `Faker\Provider\it_IT\Person` + +```php +taxId(); // "DIXDPZ44E08F367A" +``` + +### `Faker\Provider\ja_JP\Person` + +```php +kanaName($gender = null|'male'|'female') // "アオタ ミノル" + +// Generates a 'kana' first name +echo $faker->firstKanaName($gender = null|'male'|'female') // "ヒデキ" + +// Generates a 'kana' first name on the male +echo $faker->firstKanaNameMale // "ヒデキ" + +// Generates a 'kana' first name on the female +echo $faker->firstKanaNameFemale // "マアヤ" + +// Generates a 'kana' last name +echo $faker->lastKanaName; // "ナカジマ" +``` + +### `Faker\Provider\ka_GE\Payment` + +```php +bankAccountNumber; // "GE33ZV9773853617253389" +``` + +### `Faker\Provider\kk_KZ\Company` + +```php +businessIdentificationNumber; // "150140000019" +``` + +### `Faker\Provider\kk_KZ\Payment` + +```php +bank; // "Қазкоммерцбанк" + +// Generates a random bank account number +echo $faker->bankAccountNumber; // "KZ1076321LO4H6X41I37" +``` + +### `Faker\Provider\kk_KZ\Person` + +```php +individualIdentificationNumber; // "780322300455" + +// Generates an individual identification number based on his/her birth date +echo $faker->individualIdentificationNumber(new \DateTime('1999-03-01')); // "990301300455" +``` + +### `Faker\Provider\ko_KR\Address` + +```php +metropolitanCity; // "서울특별시" + +// Generates a borough +echo $faker->borough; // "강남구" +``` + +### `Faker\Provider\ko_KR\PhoneNumber` + +```php +localAreaPhoneNumber; // "02-1234-4567" + +// Generates a cell phone number +echo $faker->cellPhoneNumber; // "010-9876-5432" +``` + +### `Faker\Provider\lt_LT\Payment` + +```php +bankAccountNumber // "LT300848876740317118" +``` + +### `Faker\Provider\lv_LV\Person` + +```php +personalIdentityNumber; // "140190-12301" +``` + +### `Faker\Provider\ms_MY\Address` + +```php +township; // "Taman Bahagia" + +// Generates a random Malaysian town address with matching postcode and state +echo $faker->townState; // "55100 Bukit Bintang, Kuala Lumpur" +``` + +### `Faker\Provider\ms_MY\Miscellaneous` + +```php +jpjNumberPlate; // "WPL 5169" +``` + +### `Faker\Provider\ms_MY\Payment` + +```php +bank; // "Maybank" + +// Generates a random Malaysian bank account number (10-16 digits) +echo $faker->bankAccountNumber; // "1234567890123456" + +// Generates a random Malaysian insurance company +echo $faker->insurance; // "AIA Malaysia" + +// Generates a random Malaysian bank SWIFT Code +echo $faker->swiftCode; // "MBBEMYKLXXX" +``` + +### `Faker\Provider\ms_MY\Person` + +```php +myKadNumber($gender = null|'male'|'female', $hyphen = null|true|false); // "710703471796" +``` + +### `Faker\Provider\ms_MY\PhoneNumber` + +```php +mobileNumber($countryCodePrefix = null|true|false, $formatting = null|true|false); // "+6012-705 3767" + +// Generates a random Malaysian landline number +echo $faker->fixedLineNumber($countryCodePrefix = null|true|false, $formatting = null|true|false); // "03-7112 0455" + +// Generates a random Malaysian voip number +echo $faker->voipNumber($countryCodePrefix = null|true|false, $formatting = null|true|false); // "015-458 7099" +``` + +### `Faker\Provider\ne_NP\Address` + +```php +district; + +//Generates a Nepali city name +echo $faker->cityName; +``` + +### `Faker\Provider\nl_BE\Payment` + +```php +vat; // "BE 0123456789" - Belgian Value Added Tax number +echo $faker->vat(false); // "BE0123456789" - unspaced Belgian Value Added Tax number +``` + +### `Faker\Provider\nl_BE\Person` + +```php +rrn(); // "83051711784" - Belgian Rijksregisternummer +echo $faker->rrn('female'); // "50032089858" - Belgian Rijksregisternummer for a female +``` + +### `Faker\Provider\nl_NL\Company` + +```php +jobTitle; // "Houtbewerker" +echo $faker->vat; // "NL123456789B01" - Dutch Value Added Tax number +echo $faker->btw; // "NL123456789B01" - Dutch Value Added Tax number (alias) +``` + +### `Faker\Provider\nl_NL\Person` + +```php +idNumber; // "111222333" - Dutch Personal identification number (BSN) +``` + +### `Faker\Provider\nb_NO\MobileNumber` + +```php +mobileNumber; // "+4799988777" +echo $faker->mobileNumber; // "999 88 777" +echo $faker->mobileNumber; // "99988777" +``` + +### `Faker\Provider\nb_NO\Payment` + +```php +bankAccountNumber; // "NO3246764709816" +``` + +### `Faker\Provider\pl_PL\Person` + +```php +pesel; // "40061451555" +// Generates a random personal identity card number +echo $faker->personalIdentityNumber; // "AKX383360" +// Generates a random taxpayer identification number (NIP) +echo $faker->taxpayerIdentificationNumber; // '8211575109' +``` + +### `Faker\Provider\pl_PL\Company` + +```php +regon; // "714676680" +// Generates a random local REGON number +echo $faker->regonLocal; // "15346111382836" +``` + +### `Faker\Provider\pl_PL\Payment` + +```php +bank; // "Narodowy Bank Polski" +// Generates a random bank account number +echo $faker->bankAccountNumber; // "PL14968907563953822118075816" +``` + +### `Faker\Provider\pt_PT\Person` + +```php +taxpayerIdentificationNumber; // '165249277' +``` + +### `Faker\Provider\pt_BR\Address` + +```php +region; // 'Nordeste' + +// Generates a random region abbreviation +echo $faker->regionAbbr; // 'NE' +``` + +### `Faker\Provider\pt_BR\PhoneNumber` + +```php +areaCode; // 21 +echo $faker->cellphone; // 9432-5656 +echo $faker->landline; // 2654-3445 +echo $faker->phone; // random landline, 8-digit or 9-digit cellphone number + +// Using the phone functions with a false argument returns unformatted numbers +echo $faker->cellphone(false); // 74336667 + +// cellphone() has a special second argument to add the 9th digit. Ignored if generated a Radio number +echo $faker->cellphone(true, true); // 98983-3945 or 7343-1290 + +// Using the "Number" suffix adds area code to the phone +echo $faker->cellphoneNumber; // (11) 98309-2935 +echo $faker->landlineNumber(false); // 3522835934 +echo $faker->phoneNumber; // formatted, random landline or cellphone (obeying the 9th digit rule) +echo $faker->phoneNumberCleared; // not formatted, random landline or cellphone (obeying the 9th digit rule) +``` + +### `Faker\Provider\pt_BR\Person` + +```php +name; // 'Sr. Luis Adriano Sepúlveda Filho' + +// Valid document generators have a boolean argument to remove formatting +echo $faker->cpf; // '145.343.345-76' +echo $faker->cpf(false); // '45623467866' +echo $faker->rg; // '84.405.736-3' +echo $faker->rg(false); // '844057363' +``` + +### `Faker\Provider\pt_BR\Company` + +```php +cnpj; // '23.663.478/0001-24' +echo $faker->cnpj(false); // '23663478000124' +``` + +### `Faker\Provider\ro_MD\Payment` + +```php +bankAccountNumber; // "MD83BQW1CKMUW34HBESDP3A8" +``` + +### `Faker\Provider\ro_RO\Payment` + +```php +bankAccountNumber; // "RO55WRJE3OE8X3YQI7J26U1E" +``` + +### `Faker\Provider\ro_RO\Person` + +```php +prefixMale; // "ing." +// Generates a random female name prefix/title +echo $faker->prefixFemale; // "d-na." +// Generates a random male first name +echo $faker->firstNameMale; // "Adrian" +// Generates a random female first name +echo $faker->firstNameFemale; // "Miruna" + + +// Generates a random Personal Numerical Code (CNP) +echo $faker->cnp; // "2800523081231" +// Valid option values: +// $gender: null (random), male, female +// $dateOfBirth (1800+): null (random), Y-m-d, Y-m (random day), Y (random month and day) +// i.e. '1981-06-16', '2015-03', '1900' +// $county: 2 letter ISO 3166-2:RO county codes and B1, B2, B3, B4, B5, B6 for Bucharest's 6 sectors +// $isResident true/false flag if the person resides in Romania +echo $faker->cnp($gender = null, $dateOfBirth = null, $county = null, $isResident = true); + +``` + +### `Faker\Provider\ro_RO\PhoneNumber` + +```php +tollFreePhoneNumber; // "0800123456" +// Generates a random premium-rate phone number +echo $faker->premiumRatePhoneNumber; // "0900123456" +``` + +### `Faker\Provider\ru_RU\Payment` + +```php +bank; // "ОТП Банк" + +//Generate a Russian Tax Payment Number for Company +echo $faker->inn; // 7813540735 + +//Generate a Russian Tax Code for Company +echo $faker->kpp; // 781301001 +``` + +### `Faker\Provider\sv_SE\Payment` + +```php +bankAccountNumber; // "SE5018548608468284909192" +``` + +### `Faker\Provider\sv_SE\Person` + +```php +personalIdentityNumber() // '950910-0799' + +//Since the numbers are different for male and female persons, optionally you can specify gender. +echo $faker->personalIdentityNumber('female') // '950910-0781' +``` +### `Faker\Provider\tr_TR\Person` + +```php +tcNo // '55300634882' + +``` + + +### `Faker\Provider\zh_CN\Payment` + +```php +bank; // '中国建设银行' +``` + +### `Faker\Provider\uk_UA\Payment` + +```php +bank; // "Ощадбанк" +``` + +### `Faker\Provider\zh_TW\Person` + +```php +personalIdentityNumber; // A223456789 +``` + +### `Faker\Provider\zh_TW\Company` + +```php +VAT; //23456789 +``` + + +## Third-Party Libraries Extending/Based On Faker + +* Symfony bundles: + * [`willdurand/faker-bundle`](https://github.com/willdurand/BazingaFakerBundle): Put the awesome Faker library into the Symfony2 DIC and populate your database with fake data. + * [`hautelook/alice-bundle`](https://github.com/hautelook/AliceBundle), [`h4cc/alice-fixtures-bundle`](https://github.com/h4cc/AliceFixturesBundle): Bundles for using [`nelmio/alice`](https://packagist.org/packages/nelmio/alice) and Faker with data fixtures. Able to use Doctrine ORM as well as Doctrine MongoDB ODM. +* [`emanueleminotto/faker-service-provider`](https://github.com/EmanueleMinotto/FakerServiceProvider): Faker Service Provider for Silex +* [`bit3/faker-cli`](https://github.com/bit3/faker-cli): Command Line Tool for the Faker PHP library +* [`league/factory-muffin`](https://github.com/thephpleague/factory-muffin): enable the rapid creation of objects (PHP port of factory-girl) +* [`fzaninotto/company-name-generator`](https://github.com/fzaninotto/CompanyNameGenerator): Generate names for English tech companies with class +* [`emanueleminotto/faker-placehold-it-provider`](https://github.com/EmanueleMinotto/PlaceholdItProvider): Generate images using placehold.it +* [`spyrit/datalea`](https://github.com/spyrit/datalea) A highly customizable random test data generator web app +* [`frequenc1/newage-ipsum`](https://github.com/frequenc1/newage-ipsum): A new aged ipsum provider for the faker library inspired by http://sebpearce.com/bullshit/ +* [`prewk/xml-faker`](https://github.com/prewk/xml-faker): Create fake XML with Faker +* [`denheck/faker-context`](https://github.com/denheck/faker-context): Behat context using Faker to generate testdata +* [`swekaj/cron-expression-generator`](https://github.com/swekaj/CronExpressionGenerator): Faker provider for generating random, valid cron expressions. +* [`pragmafabrik/pomm-faker`](https://github.com/pragmafabrik/Pomm2Faker): Faker client for Pomm database framework (PostgreSQL) +* [`nelmio/alice`](https://github.com/nelmio/alice): Fixtures/object generator with a yaml DSL that can use Faker as data generator. +* [`ravage84/cakephp-fake-seeder`](https://github.com/ravage84/cakephp-fake-seeder) A CakePHP 2.x shell to seed your database with fake and/or fixed data. +* [`bheller/images-generator`](https://github.com/bruceheller/images-generator): An image generator provider using GD for placeholder type pictures +* [`pattern-lab/plugin-faker`](https://github.com/pattern-lab/plugin-php-faker): Pattern Lab is a Styleguide, Component Library, and Prototyping tool. This creates unique content each time Pattern Lab is generated. +* [`guidocella/eloquent-populator`](https://github.com/guidocella/eloquent-populator): Adapter for Laravel's Eloquent ORM. +* [`tamperdata/exiges`](https://github.com/tamperdata/exiges): Faker provider for generating random temperatures +* [`jzonta/faker-restaurant`](https://github.com/jzonta/FakerRestaurant): Faker for Food and Beverage names generate +* [`aalaap/faker-youtube`](https://github.com/aalaap/faker-youtube): Faker for YouTube URLs in various formats +* [`pelmered/fake-car`](https://github.com/pelmered/fake-car): Faker for cars and car data +* [`bluemmb/faker-picsum-photos-provider`](https://github.com/bluemmb/Faker-PicsumPhotos): Generate images using [picsum.photos](http://picsum.photos/) +* [`er1z/fakemock`](https://github.com/er1z/fakemock): Generate mocks using class-configuration and detection via Faker's guesser and Symfony asserts +* [`xvladqt/faker-lorem-flickr`](https://github.com/xvladxtremal/Faker-LoremFlickr): Generate images using [loremflickr.com](http://loremflickr.com/) +* [`metrakit/faker-eddy-malou`](https://github.com/Metrakit/faker-eddy-malou): Generate French Eddy Malou sentences & paragraphs +* [`drupol/belgian-national-number-faker`](https://github.com/drupol/belgian-national-number-faker): Generate fake Belgian national numbers +* [`elgentos/masquerade`](https://github.com/elgentos/masquerade): Configuration-based, platform-agnostic, locale-compatible data faker tool (out-of-the-box support for Magento 2) +* [`ottaviano/faker-gravatar`](https://github.com/ottaviano/faker-gravatar): Generate avatars using [Gravatar](https://en.gravatar.com/site/implement/images/) +* [`finwe/phpstan-faker`](https://github.com/finwe/phpstan-faker): PHPStan extension for Faker methods + +## License + +Faker is released under the MIT License. See the bundled LICENSE file for details. diff --git a/rules.xml b/rules.xml new file mode 100644 index 0000000..bc0e55c --- /dev/null +++ b/rules.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Faker/Calculator/Ean.php b/src/Faker/Calculator/Ean.php new file mode 100644 index 0000000..c0415fd --- /dev/null +++ b/src/Faker/Calculator/Ean.php @@ -0,0 +1,55 @@ += 0; $i -= 2) { + $even += $digits[$i]; + } + + $odd = 0; + for ($i = $length - 2; $i >= 0; $i -= 2) { + $odd += $digits[$i]; + } + + return (10 - ((3 * $even + $odd) % 10)) % 10; + } + + /** + * Checks whether the provided number is an EAN compliant number and that + * the checksum is correct. + * + * @param string $ean An EAN number + * @return boolean + */ + public static function isValid($ean) + { + if (!preg_match(self::PATTERN, $ean)) { + return false; + } + + return self::checksum(substr($ean, 0, -1)) === intval(substr($ean, -1)); + } +} diff --git a/src/Faker/Calculator/Iban.php b/src/Faker/Calculator/Iban.php new file mode 100644 index 0000000..f6df9f0 --- /dev/null +++ b/src/Faker/Calculator/Iban.php @@ -0,0 +1,73 @@ + 2, 2 => 4, 3 => 10, 4 => 3, 5 => 5, 6 => 9, 7 => 4, 8 => 6, 9 => 8); + $sum = 0; + for ($i = 1; $i <= 9; $i++) { + $sum += intval(substr($inn, $i - 1, 1)) * $multipliers[$i]; + } + return strval($sum % 11 % 10); + } + + /** + * Checks whether an INN has a valid checksum + * + * @param string $inn + * @return boolean + */ + public static function isValid($inn) + { + return self::checksum(substr($inn, 0, -1)) === substr($inn, -1, 1); + } +} diff --git a/src/Faker/Calculator/Luhn.php b/src/Faker/Calculator/Luhn.php new file mode 100644 index 0000000..8a74bb0 --- /dev/null +++ b/src/Faker/Calculator/Luhn.php @@ -0,0 +1,75 @@ += 0; $i -= 2) { + $sum += $number[$i]; + } + for ($i = $length - 2; $i >= 0; $i -= 2) { + $sum += array_sum(str_split($number[$i] * 2)); + } + + return $sum % 10; + } + + /** + * @param $partialNumber + * @return string + */ + public static function computeCheckDigit($partialNumber) + { + $checkDigit = self::checksum($partialNumber . '0'); + if ($checkDigit === 0) { + return 0; + } + + return (string) (10 - $checkDigit); + } + + /** + * Checks whether a number (partial number + check digit) is Luhn compliant + * + * @param string $number + * @return bool + */ + public static function isValid($number) + { + return self::checksum($number) === 0; + } + + /** + * Generate a Luhn compliant number. + * + * @param string $partialValue + * + * @return string + */ + public static function generateLuhnNumber($partialValue) + { + if (!preg_match('/^\d+$/', $partialValue)) { + throw new InvalidArgumentException('Argument should be an integer.'); + } + return $partialValue . Luhn::computeCheckDigit($partialValue); + } +} diff --git a/src/Faker/Calculator/TCNo.php b/src/Faker/Calculator/TCNo.php new file mode 100644 index 0000000..dd81097 --- /dev/null +++ b/src/Faker/Calculator/TCNo.php @@ -0,0 +1,52 @@ + $digit) { + if ($index % 2 == 0) { + $evenSum += $digit; + } else { + $oddSum += $digit; + } + } + + $tenthDigit = (7 * $evenSum - $oddSum) % 10; + $eleventhDigit = ($evenSum + $oddSum + $tenthDigit) % 10; + + return $tenthDigit . $eleventhDigit; + } + + /** + * Checks whether a TCNo has a valid checksum + * + * @param string $tcNo + * @return boolean + */ + public static function isValid($tcNo) + { + return self::checksum(substr($tcNo, 0, -2)) === substr($tcNo, -2, 2); + } +} diff --git a/src/Faker/DefaultGenerator.php b/src/Faker/DefaultGenerator.php new file mode 100644 index 0000000..8397f4e --- /dev/null +++ b/src/Faker/DefaultGenerator.php @@ -0,0 +1,39 @@ +optional(). + */ +class DefaultGenerator +{ + + protected $default; + + public function __construct($default = null) + { + $this->default = $default; + } + + /** + * @param string $attribute + * + * @return mixed + */ + public function __get(string $attribute) + { + return $this->default; + } + + /** + * @param string $method + * @param array $attributes + * + * @return mixed + */ + public function __call(string $method, array $attributes) + { + return $this->default; + } +} diff --git a/src/Faker/Documentor.php b/src/Faker/Documentor.php new file mode 100644 index 0000000..5f7a1c8 --- /dev/null +++ b/src/Faker/Documentor.php @@ -0,0 +1,69 @@ +generator = $generator; + } + + /** + * @return array + */ + public function getFormatters(): array + { + $formatters = array(); + $providers = array_reverse($this->generator->getProviders()); + $providers[] = new Provider\Base($this->generator); + foreach ($providers as $provider) { + $providerClass = get_class($provider); + $formatters[$providerClass] = array(); + $refl = new \ReflectionObject($provider); + foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflmethod) { + if ($providerClass !== Base::class && $reflmethod->getDeclaringClass()->getName() === 'Faker\Provider\Base') { + continue; + } + $methodName = $reflmethod->name; + if ($reflmethod->isConstructor()) { + continue; + } + $parameters = array(); + foreach ($reflmethod->getParameters() as $reflparameter) { + $parameter = '$' . $reflparameter->getName(); + if ($reflparameter->isDefaultValueAvailable()) { + $parameter .= ' = ' . var_export($reflparameter->getDefaultValue(), true); + } + $parameters [] = $parameter; + } + $parameters = $parameters ? '(' . join(', ', $parameters) . ')' : ''; + try { + $example = $this->generator->format($methodName); + } catch (\InvalidArgumentException $e) { + $example = ''; + } + if (is_array($example)) { + $example = "array('" . join("', '", $example) . "')"; + } elseif ($example instanceof \DateTime) { + $example = "DateTime('" . $example->format('Y-m-d H:i:s') . "')"; + } elseif ($example instanceof Generator || $example instanceof UniqueGenerator) { // modifier + $example = ''; + } else { + $example = var_export($example, true); + } + $formatters[$providerClass][$methodName . $parameters] = $example; + } + } + + return $formatters; + } +} diff --git a/src/Faker/Factory.php b/src/Faker/Factory.php new file mode 100644 index 0000000..f64f136 --- /dev/null +++ b/src/Faker/Factory.php @@ -0,0 +1,68 @@ +addProvider(new $providerClassName($generator)); + } + + return $generator; + } + + /** + * @param string $provider + * @param string $locale + * @return string + */ + protected static function getProviderClassname(string $provider, string $locale = ''): string + { + $providerClass = self::findProviderClassname($provider, $locale); + if ($providerClass) { + return $providerClass; + } + // fallback to default locale + $providerClass = self::findProviderClassname($provider, static::DEFAULT_LOCALE); + if ($providerClass) { + return $providerClass; + } + + // fallback to no locale + $providerClass = self::findProviderClassname($provider); + if ($providerClass) { + return $providerClass; + } + throw new \InvalidArgumentException(sprintf('Unable to find provider "%s" with locale "%s"', $provider, $locale)); + } + + /** + * @param string $provider + * @param string $locale + * @return string + */ + protected static function findProviderClassname(string $provider, string $locale = ''): ?string + { + $providerClass = 'Siteworx\\Faker\\' . ($locale ? sprintf('Provider\%s\%s', $locale, $provider) : sprintf('Provider\%s', $provider)); + + if (class_exists($providerClass, true)) { + return $providerClass; + } + + return null; + } +} diff --git a/src/Faker/Generator.php b/src/Faker/Generator.php new file mode 100644 index 0000000..58444f5 --- /dev/null +++ b/src/Faker/Generator.php @@ -0,0 +1,319 @@ +providers, $provider); + } + + public function getProviders(): array + { + return $this->providers; + } + + public function seed($seed = null): void + { + if ($seed === null) { + mt_srand(); + } elseif (PHP_VERSION_ID < 70100) { + mt_srand((int) $seed); + } else { + mt_srand((int) $seed, MT_RAND_PHP); + } + } + + /** + * @param array $providers + * @return Generator + */ + public function setProviders(array $providers): Generator + { + $this->providers = $providers; + + return $this; + } + + public function format($formatter, $arguments = array()) + { + return call_user_func_array($this->getFormatter($formatter), $arguments); + } + + /** + * @param string $formatter + * + * @return Callable + */ + public function getFormatter(string $formatter) + { + if (isset($this->formatters[$formatter])) { + return $this->formatters[$formatter]; + } + foreach ($this->providers as $provider) { + if (method_exists($provider, $formatter)) { + $this->formatters[$formatter] = array($provider, $formatter); + + return $this->formatters[$formatter]; + } + } + throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter)); + } + + /** + * Replaces tokens ('{{ tokenName }}') with the result from the token method call + * + * @param string $string String that needs to bet parsed + * @return string + */ + public function parse(string $string): string + { + return preg_replace_callback('/{{\s?(\w+)\s?}}/u', array($this, 'callFormatWithMatches'), $string); + } + + protected function callFormatWithMatches($matches) + { + return $this->format($matches[1]); + } + + /** + * @param string $attribute + * + * @return mixed + */ + public function __get(string $attribute) + { + return $this->format($attribute); + } + + public function __isset($name) + { + } + + public function __set(string $key, $value) + { + throw new \RuntimeException('Faker is immutable.'); + } + + /** + * @param string $method + * @param array $attributes + * + * @return mixed + */ + public function __call(string $method, array $attributes) + { + return $this->format($method, $attributes); + } + + public function __destruct() + { + $this->seed(); + } + + public function __wakeup() + { + $this->formatters = []; + } +} diff --git a/src/Faker/Guesser/Name.php b/src/Faker/Guesser/Name.php new file mode 100644 index 0000000..6af3da7 --- /dev/null +++ b/src/Faker/Guesser/Name.php @@ -0,0 +1,158 @@ +generator = $generator; + } + + /** + * @param string $name + * @param int|null $size Length of field, if known + * @return callable + */ + public function guessFormat(string $name, int $size = null): ?callable + { + $name = Base::toLower($name); + $generator = $this->generator; + if (preg_match('/^is[_A-Z]/', $name)) { + return static function () use ($generator) { + return $generator->boolean; + }; + } + if (preg_match('/(_a|A)t$/', $name)) { + return static function () use ($generator) { + return $generator->dateTime; + }; + } + switch (str_replace('_', '', $name)) { + case 'firstname': + return static function () use ($generator) { + return $generator->firstName; + }; + case 'lastname': + return static function () use ($generator) { + return $generator->lastName; + }; + case 'username': + case 'login': + return static function () use ($generator) { + return $generator->userName; + }; + case 'email': + case 'emailaddress': + return function () use ($generator) { + return $generator->email; + }; + case 'phonenumber': + case 'phone': + case 'telephone': + case 'telnumber': + return function () use ($generator) { + return $generator->phoneNumber; + }; + case 'address': + return function () use ($generator) { + return $generator->address; + }; + case 'city': + case 'town': + return function () use ($generator) { + return $generator->city; + }; + case 'streetaddress': + return function () use ($generator) { + return $generator->streetAddress; + }; + case 'postcode': + case 'zipcode': + return function () use ($generator) { + return $generator->postcode; + }; + case 'state': + return function () use ($generator) { + return $generator->state; + }; + case 'county': + if ($this->generator->locale == 'en_US') { + return function () use ($generator) { + return sprintf('%s County', $generator->city); + }; + } + + return function () use ($generator) { + return $generator->state; + }; + case 'country': + switch ($size) { + case 2: + return function () use ($generator) { + return $generator->countryCode; + }; + case 3: + return function () use ($generator) { + return $generator->countryISOAlpha3; + }; + case 5: + case 6: + return function () use ($generator) { + return $generator->locale; + }; + default: + return function () use ($generator) { + return $generator->country; + }; + } + break; + case 'locale': + return function () use ($generator) { + return $generator->locale; + }; + case 'currency': + case 'currencycode': + return function () use ($generator) { + return $generator->currencyCode; + }; + case 'url': + case 'website': + return function () use ($generator) { + return $generator->url; + }; + case 'company': + case 'companyname': + case 'employer': + return function () use ($generator) { + return $generator->company; + }; + case 'title': + if ($size !== null && $size <= 10) { + return function () use ($generator) { + return $generator->title; + }; + } + + return function () use ($generator) { + return $generator->sentence; + }; + case 'body': + case 'summary': + case 'article': + case 'description': + return function () use ($generator) { + return $generator->text; + }; + } + } +} diff --git a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php new file mode 100644 index 0000000..07e7063 --- /dev/null +++ b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php @@ -0,0 +1,72 @@ +generator = $generator; + } + + /** + * @return \Closure|null + */ + public function guessFormat($column, $table) + { + $generator = $this->generator; + $schema = $table->schema(); + + switch ($schema->columnType($column)) { + case 'boolean': + return function () use ($generator) { + return $generator->boolean; + }; + case 'integer': + return function () { + return mt_rand(0, intval('2147483647')); + }; + case 'biginteger': + return function () { + return mt_rand(0, intval('9223372036854775807')); + }; + case 'decimal': + case 'float': + return function () use ($generator) { + return $generator->randomFloat(); + }; + case 'uuid': + return function () use ($generator) { + return $generator->uuid(); + }; + case 'string': + if (method_exists($schema, 'getColumn')) { + $columnData = $schema->getColumn($column); + } else { + $columnData = $schema->column($column); + } + $length = $columnData['length']; + return function () use ($generator, $length) { + return $generator->text($length); + }; + case 'text': + return function () use ($generator) { + return $generator->text(); + }; + case 'date': + case 'datetime': + case 'timestamp': + case 'time': + return function () use ($generator) { + return $generator->datetime(); + }; + + case 'binary': + default: + return null; + } + } +} diff --git a/src/Faker/ORM/CakePHP/EntityPopulator.php b/src/Faker/ORM/CakePHP/EntityPopulator.php new file mode 100644 index 0000000..25d82c9 --- /dev/null +++ b/src/Faker/ORM/CakePHP/EntityPopulator.php @@ -0,0 +1,172 @@ +class = $class; + } + + /** + * @param string $name + */ + public function __get($name) + { + return $this->{$name}; + } + + /** + * @param string $name + */ + public function __set($name, $value) + { + $this->{$name} = $value; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + public function mergeModifiersWith($modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + /** + * @return array + */ + public function guessColumnFormatters($populator) + { + $formatters = []; + $class = $this->class; + $table = $this->getTable($class); + $schema = $table->schema(); + $pk = $schema->primaryKey(); + $guessers = $populator->getGuessers() + ['ColumnTypeGuesser' => new ColumnTypeGuesser($populator->getGenerator())]; + $isForeignKey = function ($column) use ($table) { + foreach ($table->associations()->type('BelongsTo') as $assoc) { + if ($column == $assoc->foreignKey()) { + return true; + } + } + return false; + }; + + + foreach ($schema->columns() as $column) { + if ($column == $pk[0] || $isForeignKey($column)) { + continue; + } + + foreach ($guessers as $guesser) { + if ($formatter = $guesser->guessFormat($column, $table)) { + $formatters[$column] = $formatter; + break; + } + } + } + + return $formatters; + } + + /** + * @return array + */ + public function guessModifiers() + { + $modifiers = []; + $table = $this->getTable($this->class); + + $belongsTo = $table->associations()->type('BelongsTo'); + foreach ($belongsTo as $assoc) { + $modifiers['belongsTo' . $assoc->name()] = function ($data, $insertedEntities) use ($assoc) { + $table = $assoc->target(); + $foreignModel = $table->alias(); + + $foreignKeys = []; + if (!empty($insertedEntities[$foreignModel])) { + $foreignKeys = $insertedEntities[$foreignModel]; + } else { + $foreignKeys = $table->find('all') + ->select(['id']) + ->map( + function ($row) { + return $row->id; + } + ) + ->toArray(); + } + + if (empty($foreignKeys)) { + throw new \Exception(sprintf('%s belongsTo %s, which seems empty at this point.', $this->getTable($this->class)->table(), $assoc->table())); + } + + $foreignKey = $foreignKeys[array_rand($foreignKeys)]; + $data[$assoc->foreignKey()] = $foreignKey; + return $data; + }; + } + + // TODO check if TreeBehavior attached to modify lft/rgt cols + + return $modifiers; + } + + /** + * @param array $options + */ + public function execute($class, $insertedEntities, $options = []) + { + $table = $this->getTable($class); + $entity = $table->newEntity(); + + foreach ($this->columnFormatters as $column => $format) { + if (!is_null($format)) { + $entity->{$column} = is_callable($format) ? $format($insertedEntities, $table) : $format; + } + } + + foreach ($this->modifiers as $modifier) { + $entity = $modifier($entity, $insertedEntities); + } + + if (!$entity = $table->save($entity, $options)) { + throw new \RuntimeException("Failed saving $class record"); + } + + $pk = $table->primaryKey(); + if (is_string($pk)) { + return $entity->{$pk}; + } + + return $entity->{$pk[0]}; + } + + public function setConnection($name) + { + $this->connectionName = $name; + } + + protected function getTable($class) + { + $options = []; + if (!empty($this->connectionName)) { + $options['connection'] = $this->connectionName; + } + return TableRegistry::get($class, $options); + } +} diff --git a/src/Faker/ORM/CakePHP/Populator.php b/src/Faker/ORM/CakePHP/Populator.php new file mode 100644 index 0000000..1469f05 --- /dev/null +++ b/src/Faker/ORM/CakePHP/Populator.php @@ -0,0 +1,112 @@ +generator = $generator; + } + + /** + * @return \Siteworx\Faker\Generator + */ + public function getGenerator() + { + return $this->generator; + } + + /** + * @return array + */ + public function getGuessers() + { + return $this->guessers; + } + + /** + * @return $this + */ + public function removeGuesser($name) + { + if ($this->guessers[$name]) { + unset($this->guessers[$name]); + } + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function addGuesser($class) + { + if (!is_object($class)) { + $class = new $class($this->generator); + } + + if (!method_exists($class, 'guessFormat')) { + throw new \Exception('Missing required custom guesser method: ' . get_class($class) . '::guessFormat()'); + } + + $this->guessers[get_class($class)] = $class; + return $this; + } + + /** + * @param array $customColumnFormatters + * @param array $customModifiers + * @return $this + */ + public function addEntity($entity, $number, $customColumnFormatters = [], $customModifiers = []) + { + if (!$entity instanceof EntityPopulator) { + $entity = new EntityPopulator($entity); + } + + $entity->columnFormatters = $entity->guessColumnFormatters($this); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + + $entity->modifiers = $entity->guessModifiers($this); + if ($customModifiers) { + $entity->mergeModifiersWith($customModifiers); + } + + $class = $entity->class; + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + return $this; + } + + /** + * @param array $options + * @return array + */ + public function execute($options = []) + { + $insertedEntities = []; + + foreach ($this->quantities as $class => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute($class, $insertedEntities, $options); + } + } + + return $insertedEntities; + } +} diff --git a/src/Faker/ORM/Doctrine/ColumnTypeGuesser.php b/src/Faker/ORM/Doctrine/ColumnTypeGuesser.php new file mode 100644 index 0000000..4baafff --- /dev/null +++ b/src/Faker/ORM/Doctrine/ColumnTypeGuesser.php @@ -0,0 +1,82 @@ +generator = $generator; + } + + /** + * @param ClassMetadata $class + * @return \Closure|null + */ + public function guessFormat($fieldName, ClassMetadata $class) + { + $generator = $this->generator; + $type = $class->getTypeOfField($fieldName); + switch ($type) { + case 'boolean': + return function () use ($generator) { + return $generator->boolean; + }; + case 'decimal': + $size = isset($class->fieldMappings[$fieldName]['precision']) ? $class->fieldMappings[$fieldName]['precision'] : 2; + + return function () use ($generator, $size) { + return $generator->randomNumber($size + 2) / 100; + }; + case 'smallint': + return function () { + return mt_rand(0, 65535); + }; + case 'integer': + return function () { + return mt_rand(0, intval('2147483647')); + }; + case 'bigint': + return function () { + return mt_rand(0, intval('18446744073709551615')); + }; + case 'float': + return function () { + return mt_rand(0, intval('4294967295')) / mt_rand(1, intval('4294967295')); + }; + case 'string': + $size = isset($class->fieldMappings[$fieldName]['length']) ? $class->fieldMappings[$fieldName]['length'] : 255; + + return function () use ($generator, $size) { + return $generator->text($size); + }; + case 'text': + return function () use ($generator) { + return $generator->text; + }; + case 'datetime': + case 'date': + case 'time': + return function () use ($generator) { + return $generator->datetime; + }; + case 'datetime_immutable': + case 'date_immutable': + case 'time_immutable': + return function () use ($generator) { + return \DateTimeImmutable::createFromMutable($generator->datetime); + }; + default: + // no smart way to guess what the user expects here + return null; + } + } +} diff --git a/src/Faker/ORM/Doctrine/EntityPopulator.php b/src/Faker/ORM/Doctrine/EntityPopulator.php new file mode 100644 index 0000000..95c09eb --- /dev/null +++ b/src/Faker/ORM/Doctrine/EntityPopulator.php @@ -0,0 +1,256 @@ +class = $class; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class->getName(); + } + + /** + * @param $columnFormatters + */ + public function setColumnFormatters($columnFormatters) + { + $this->columnFormatters = $columnFormatters; + } + + /** + * @return array + */ + public function getColumnFormatters() + { + return $this->columnFormatters; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + /** + * @param array $modifiers + */ + public function setModifiers(array $modifiers) + { + $this->modifiers = $modifiers; + } + + /** + * @return array + */ + public function getModifiers() + { + return $this->modifiers; + } + + /** + * @param array $modifiers + */ + public function mergeModifiersWith(array $modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @return array + */ + public function guessColumnFormatters(\Siteworx\Faker\Generator $generator) + { + $formatters = array(); + $nameGuesser = new \Siteworx\Faker\Guesser\Name($generator); + $columnTypeGuesser = new ColumnTypeGuesser($generator); + foreach ($this->class->getFieldNames() as $fieldName) { + if ($this->class->isIdentifier($fieldName) || !$this->class->hasField($fieldName)) { + continue; + } + + $size = isset($this->class->fieldMappings[$fieldName]['length']) ? $this->class->fieldMappings[$fieldName]['length'] : null; + if ($formatter = $nameGuesser->guessFormat($fieldName, $size)) { + $formatters[$fieldName] = $formatter; + continue; + } + if ($formatter = $columnTypeGuesser->guessFormat($fieldName, $this->class)) { + $formatters[$fieldName] = $formatter; + continue; + } + } + + foreach ($this->class->getAssociationNames() as $assocName) { + if ($this->class->isCollectionValuedAssociation($assocName)) { + continue; + } + + $relatedClass = $this->class->getAssociationTargetClass($assocName); + + $unique = $optional = false; + if ($this->class instanceof \Doctrine\ORM\Mapping\ClassMetadata) { + $mappings = $this->class->getAssociationMappings(); + foreach ($mappings as $mapping) { + if ($mapping['targetEntity'] == $relatedClass) { + if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_ONE) { + $unique = true; + $optional = isset($mapping['joinColumns'][0]['nullable']) ? $mapping['joinColumns'][0]['nullable'] : false; + break; + } + } + } + } elseif ($this->class instanceof \Doctrine\ODM\MongoDB\Mapping\ClassMetadata) { + $mappings = $this->class->associationMappings; + foreach ($mappings as $mapping) { + if ($mapping['targetDocument'] == $relatedClass) { + if ($mapping['type'] == \Doctrine\ODM\MongoDB\Mapping\ClassMetadata::ONE && $mapping['association'] == \Doctrine\ODM\MongoDB\Mapping\ClassMetadata::REFERENCE_ONE) { + $unique = true; + $optional = isset($mapping['nullable']) ? $mapping['nullable'] : false; + break; + } + } + } + } + + $index = 0; + $formatters[$assocName] = function ($inserted) use ($relatedClass, &$index, $unique, $optional) { + + if (isset($inserted[$relatedClass])) { + if ($unique) { + $related = null; + if (isset($inserted[$relatedClass][$index]) || !$optional) { + $related = $inserted[$relatedClass][$index]; + } + + $index++; + + return $related; + } + + return $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)]; + } + + return null; + }; + } + + return $formatters; + } + + /** + * Insert one new record using the Entity class. + * @param ObjectManager $manager + * @param bool $generateId + * @return EntityPopulator + */ + public function execute(ObjectManager $manager, $insertedEntities, $generateId = false) + { + $obj = $this->class->newInstance(); + + $this->fillColumns($obj, $insertedEntities); + $this->callMethods($obj, $insertedEntities); + + if ($generateId) { + $idsName = $this->class->getIdentifier(); + foreach ($idsName as $idName) { + $id = $this->generateId($obj, $idName, $manager); + $this->class->reflFields[$idName]->setValue($obj, $id); + } + } + + $manager->persist($obj); + + return $obj; + } + + private function fillColumns($obj, $insertedEntities) + { + foreach ($this->columnFormatters as $field => $format) { + if ($format !== null) { + // Add some extended debugging information to any errors thrown by the formatter + try { + $value = is_callable($format) ? $format($insertedEntities, $obj) : $format; + } catch (\InvalidArgumentException $ex) { + throw new \InvalidArgumentException( + sprintf( + "Failed to generate a value for %s::%s: %s", + get_class($obj), + $field, + $ex->getMessage() + ) + ); + } + // Try a standard setter if it's available, otherwise fall back on reflection + $setter = sprintf("set%s", ucfirst($field)); + if (is_callable(array($obj, $setter))) { + $obj->$setter($value); + } else { + $this->class->reflFields[$field]->setValue($obj, $value); + } + } + } + } + + private function callMethods($obj, $insertedEntities) + { + foreach ($this->getModifiers() as $modifier) { + $modifier($obj, $insertedEntities); + } + } + + /** + * @param ObjectManager $manager + * @return int|null + */ + private function generateId($obj, $column, ObjectManager $manager) + { + /* @var $repository \Doctrine\Common\Persistence\ObjectRepository */ + $repository = $manager->getRepository(get_class($obj)); + $result = $repository->createQueryBuilder('e') + ->select(sprintf('e.%s', $column)) + ->getQuery() + ->execute(); + $ids = array_map('current', $result->toArray()); + + $id = null; + do { + $id = mt_rand(); + } while (in_array($id, $ids)); + + return $id; + } +} diff --git a/src/Faker/ORM/Doctrine/Populator.php b/src/Faker/ORM/Doctrine/Populator.php new file mode 100644 index 0000000..5f516cf --- /dev/null +++ b/src/Faker/ORM/Doctrine/Populator.php @@ -0,0 +1,109 @@ +generator = $generator; + $this->manager = $manager; + $this->batchSize = $batchSize; + } + + /** + * Add an order for the generation of $number records for $entity. + * + * @param mixed $entity A Doctrine classname, or a \Faker\ORM\Doctrine\EntityPopulator instance + * @param int $number The number of entities to populate + */ + public function addEntity($entity, $number, $customColumnFormatters = array(), $customModifiers = array(), $generateId = false) + { + if (!$entity instanceof \Siteworx\Faker\ORM\Doctrine\EntityPopulator) { + if ($this->manager === null) { + throw new \InvalidArgumentException("No entity manager passed to Doctrine Populator."); + } + $entity = new \Siteworx\Faker\ORM\Doctrine\EntityPopulator($this->manager->getClassMetadata($entity)); + } + $entity->setColumnFormatters($entity->guessColumnFormatters($this->generator)); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + $entity->mergeModifiersWith($customModifiers); + $this->generateId[$entity->getClass()] = $generateId; + + $class = $entity->getClass(); + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + } + + /** + * Populate the database using all the Entity classes previously added. + * + * Please note that large amounts of data will result in more memory usage since the the Populator will return + * all newly created primary keys after executing. + * + * @param null|EntityManager $entityManager A Doctrine connection object + * + * @return array A list of the inserted PKs + */ + public function execute($entityManager = null) + { + if ($entityManager === null) { + $entityManager = $this->manager; + } + if ($entityManager === null) { + throw new \InvalidArgumentException("No entity manager passed to Doctrine Populator."); + } + + $insertedEntities = array(); + foreach ($this->quantities as $class => $number) { + $generateId = $this->generateId[$class]; + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute( + $entityManager, + $insertedEntities, + $generateId + ); + if (count($insertedEntities) % $this->batchSize === 0) { + $entityManager->flush(); + } + } + $entityManager->flush(); + } + + return $insertedEntities; + } +} diff --git a/src/Faker/ORM/Mandango/ColumnTypeGuesser.php b/src/Faker/ORM/Mandango/ColumnTypeGuesser.php new file mode 100644 index 0000000..226d757 --- /dev/null +++ b/src/Faker/ORM/Mandango/ColumnTypeGuesser.php @@ -0,0 +1,50 @@ +generator = $generator; + } + + /** + * @return \Closure|null + */ + public function guessFormat($field) + { + $generator = $this->generator; + switch ($field['type']) { + case 'boolean': + return function () use ($generator) { + return $generator->boolean; + }; + case 'integer': + return function () { + return mt_rand(0, intval('4294967295')); + }; + case 'float': + return function () { + return mt_rand(0, intval('4294967295')) / mt_rand(1, intval('4294967295')); + }; + case 'string': + return function () use ($generator) { + return $generator->text(255); + }; + case 'date': + return function () use ($generator) { + return $generator->datetime; + }; + default: + // no smart way to guess what the user expects here + return null; + } + } +} diff --git a/src/Faker/ORM/Mandango/EntityPopulator.php b/src/Faker/ORM/Mandango/EntityPopulator.php new file mode 100644 index 0000000..45d3c63 --- /dev/null +++ b/src/Faker/ORM/Mandango/EntityPopulator.php @@ -0,0 +1,126 @@ +class = $class; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + public function setColumnFormatters($columnFormatters) + { + $this->columnFormatters = $columnFormatters; + } + + /** + * @return array + */ + public function getColumnFormatters() + { + return $this->columnFormatters; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @param Mandango $mandango + * @return array + */ + public function guessColumnFormatters(\Siteworx\Faker\Generator $generator, Mandango $mandango) + { + $formatters = array(); + $nameGuesser = new \Siteworx\Faker\Guesser\Name($generator); + $columnTypeGuesser = new \Siteworx\Faker\ORM\Mandango\ColumnTypeGuesser($generator); + + $metadata = $mandango->getMetadata($this->class); + + // fields + foreach ($metadata['fields'] as $fieldName => $field) { + if ($formatter = $nameGuesser->guessFormat($fieldName)) { + $formatters[$fieldName] = $formatter; + continue; + } + if ($formatter = $columnTypeGuesser->guessFormat($field)) { + $formatters[$fieldName] = $formatter; + continue; + } + } + + // references + foreach (array_merge($metadata['referencesOne'], $metadata['referencesMany']) as $referenceName => $reference) { + if (!isset($reference['class'])) { + continue; + } + $referenceClass = $reference['class']; + + $formatters[$referenceName] = function ($insertedEntities) use ($referenceClass) { + if (isset($insertedEntities[$referenceClass])) { + return Base::randomElement($insertedEntities[$referenceClass]); + } + }; + } + + return $formatters; + } + + /** + * Insert one new record using the Entity class. + * @param Mandango $mandango + */ + public function execute(Mandango $mandango, $insertedEntities) + { + $metadata = $mandango->getMetadata($this->class); + + $obj = $mandango->create($this->class); + foreach ($this->columnFormatters as $column => $format) { + if ($format !== null) { + $value = is_callable($format) ? $format($insertedEntities, $obj) : $format; + + if ( + isset($metadata['fields'][$column]) || + isset($metadata['referencesOne'][$column]) + ) { + $obj->set($column, $value); + } + + if (isset($metadata['referencesMany'][$column])) { + $adder = 'add' . ucfirst($column); + $obj->$adder($value); + } + } + } + $mandango->persist($obj); + + return $obj; + } +} diff --git a/src/Faker/ORM/Mandango/Populator.php b/src/Faker/ORM/Mandango/Populator.php new file mode 100644 index 0000000..ceeb988 --- /dev/null +++ b/src/Faker/ORM/Mandango/Populator.php @@ -0,0 +1,69 @@ +generator = $generator; + $this->mandango = $mandango; + } + + /** + * Add an order for the generation of $number records for $entity. + * + * @param mixed $entity A Propel ActiveRecord classname, or a \Faker\ORM\Propel\EntityPopulator instance + * @param int $number The number of entities to populate + */ + public function addEntity($entity, $number, $customColumnFormatters = array()) + { + if (!$entity instanceof \Siteworx\Faker\ORM\Mandango\EntityPopulator) { + $entity = new \Siteworx\Faker\ORM\Mandango\EntityPopulator($entity); + } + $entity->setColumnFormatters($entity->guessColumnFormatters($this->generator, $this->mandango)); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + $class = $entity->getClass(); + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + } + + /** + * Populate the database using all the Entity classes previously added. + * + * @return array A list of the inserted entities. + */ + public function execute() + { + $insertedEntities = array(); + foreach ($this->quantities as $class => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute($this->mandango, $insertedEntities); + } + } + $this->mandango->flush(); + + return $insertedEntities; + } +} diff --git a/src/Faker/ORM/Propel/ColumnTypeGuesser.php b/src/Faker/ORM/Propel/ColumnTypeGuesser.php new file mode 100644 index 0000000..2fd9c95 --- /dev/null +++ b/src/Faker/ORM/Propel/ColumnTypeGuesser.php @@ -0,0 +1,108 @@ +generator = $generator; + } + + /** + * @param ColumnMap $column + * @return \Closure|null + */ + public function guessFormat(ColumnMap $column) + { + $generator = $this->generator; + if ($column->isTemporal()) { + if ($column->isEpochTemporal()) { + return function () use ($generator) { + return $generator->dateTime; + }; + } + + return function () use ($generator) { + return $generator->dateTimeAD; + }; + } + $type = $column->getType(); + switch ($type) { + case PropelColumnTypes::BOOLEAN: + case PropelColumnTypes::BOOLEAN_EMU: + return function () use ($generator) { + return $generator->boolean; + }; + case PropelColumnTypes::NUMERIC: + case PropelColumnTypes::DECIMAL: + $size = $column->getSize(); + + return function () use ($generator, $size) { + return $generator->randomNumber($size + 2) / 100; + }; + case PropelColumnTypes::TINYINT: + return function () { + return mt_rand(0, 127); + }; + case PropelColumnTypes::SMALLINT: + return function () { + return mt_rand(0, 32767); + }; + case PropelColumnTypes::INTEGER: + return function () { + return mt_rand(0, intval('2147483647')); + }; + case PropelColumnTypes::BIGINT: + return function () { + return mt_rand(0, intval('9223372036854775807')); + }; + case PropelColumnTypes::FLOAT: + return function () { + return mt_rand(0, intval('2147483647')) / mt_rand(1, intval('2147483647')); + }; + case PropelColumnTypes::DOUBLE: + case PropelColumnTypes::REAL: + return function () { + return mt_rand(0, intval('9223372036854775807')) / mt_rand(1, intval('9223372036854775807')); + }; + case PropelColumnTypes::CHAR: + case PropelColumnTypes::VARCHAR: + case PropelColumnTypes::BINARY: + case PropelColumnTypes::VARBINARY: + $size = $column->getSize(); + + return function () use ($generator, $size) { + return $generator->text($size); + }; + case PropelColumnTypes::LONGVARCHAR: + case PropelColumnTypes::LONGVARBINARY: + case PropelColumnTypes::CLOB: + case PropelColumnTypes::CLOB_EMU: + case PropelColumnTypes::BLOB: + return function () use ($generator) { + return $generator->text; + }; + case PropelColumnTypes::ENUM: + $valueSet = $column->getValueSet(); + + return function () use ($generator, $valueSet) { + return $generator->randomElement($valueSet); + }; + case PropelColumnTypes::OBJECT: + case PropelColumnTypes::PHP_ARRAY: + default: + // no smart way to guess what the user expects here + return null; + } + } +} diff --git a/src/Faker/ORM/Propel/EntityPopulator.php b/src/Faker/ORM/Propel/EntityPopulator.php new file mode 100644 index 0000000..56289da --- /dev/null +++ b/src/Faker/ORM/Propel/EntityPopulator.php @@ -0,0 +1,194 @@ +class = $class; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + public function setColumnFormatters($columnFormatters) + { + $this->columnFormatters = $columnFormatters; + } + + /** + * @return array + */ + public function getColumnFormatters() + { + return $this->columnFormatters; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @return array + */ + public function guessColumnFormatters(\Siteworx\Faker\Generator $generator) + { + $formatters = array(); + $class = $this->class; + $peerClass = $class::PEER; + $tableMap = $peerClass::getTableMap(); + $nameGuesser = new \Siteworx\Faker\Guesser\Name($generator); + $columnTypeGuesser = new \Siteworx\Faker\ORM\Propel\ColumnTypeGuesser($generator); + foreach ($tableMap->getColumns() as $columnMap) { + // skip behavior columns, handled by modifiers + if ($this->isColumnBehavior($columnMap)) { + continue; + } + if ($columnMap->isForeignKey()) { + $relatedClass = $columnMap->getRelation()->getForeignTable()->getClassname(); + $formatters[$columnMap->getPhpName()] = function ($inserted) use ($relatedClass) { + return isset($inserted[$relatedClass]) ? $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)] : null; + }; + continue; + } + if ($columnMap->isPrimaryKey()) { + continue; + } + if ($formatter = $nameGuesser->guessFormat($columnMap->getPhpName(), $columnMap->getSize())) { + $formatters[$columnMap->getPhpName()] = $formatter; + continue; + } + if ($formatter = $columnTypeGuesser->guessFormat($columnMap)) { + $formatters[$columnMap->getPhpName()] = $formatter; + continue; + } + } + + return $formatters; + } + + /** + * @param ColumnMap $columnMap + * @return bool + */ + protected function isColumnBehavior(ColumnMap $columnMap) + { + foreach ($columnMap->getTable()->getBehaviors() as $name => $params) { + $columnName = Base::toLower($columnMap->getName()); + switch ($name) { + case 'nested_set': + $columnNames = array($params['left_column'], $params['right_column'], $params['level_column']); + if (in_array($columnName, $columnNames)) { + return true; + } + break; + case 'timestampable': + $columnNames = array($params['create_column'], $params['update_column']); + if (in_array($columnName, $columnNames)) { + return true; + } + break; + } + } + + return false; + } + + public function setModifiers($modifiers) + { + $this->modifiers = $modifiers; + } + + /** + * @return array + */ + public function getModifiers() + { + return $this->modifiers; + } + + public function mergeModifiersWith($modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @return array + */ + public function guessModifiers(\Siteworx\Faker\Generator $generator) + { + $modifiers = array(); + $class = $this->class; + $peerClass = $class::PEER; + $tableMap = $peerClass::getTableMap(); + foreach ($tableMap->getBehaviors() as $name => $params) { + switch ($name) { + case 'nested_set': + $modifiers['nested_set'] = function ($obj, $inserted) use ($class, $generator) { + if (isset($inserted[$class])) { + $queryClass = $class . 'Query'; + $parent = $queryClass::create()->findPk($generator->randomElement($inserted[$class])); + $obj->insertAsLastChildOf($parent); + } else { + $obj->makeRoot(); + } + }; + break; + case 'sortable': + $modifiers['sortable'] = function ($obj, $inserted) use ($class) { + $maxRank = isset($inserted[$class]) ? count($inserted[$class]) : 0; + $obj->insertAtRank(mt_rand(1, $maxRank + 1)); + }; + break; + } + } + + return $modifiers; + } + + /** + * Insert one new record using the Entity class. + */ + public function execute($con, $insertedEntities) + { + $obj = new $this->class(); + foreach ($this->getColumnFormatters() as $column => $format) { + if ($format !== null) { + $obj->setByName($column, is_callable($format) ? $format($insertedEntities, $obj) : $format); + } + } + foreach ($this->getModifiers() as $modifier) { + $modifier($obj, $insertedEntities); + } + $obj->save($con); + + return $obj->getPrimaryKey(); + } +} diff --git a/src/Faker/ORM/Propel/Populator.php b/src/Faker/ORM/Propel/Populator.php new file mode 100644 index 0000000..02eb107 --- /dev/null +++ b/src/Faker/ORM/Propel/Populator.php @@ -0,0 +1,92 @@ +generator = $generator; + } + + /** + * Add an order for the generation of $number records for $entity. + * + * @param mixed $entity A Propel ActiveRecord classname, or a \Faker\ORM\Propel\EntityPopulator instance + * @param int $number The number of entities to populate + */ + public function addEntity($entity, $number, $customColumnFormatters = array(), $customModifiers = array()) + { + if (!$entity instanceof \Siteworx\Faker\ORM\Propel\EntityPopulator) { + $entity = new \Siteworx\Faker\ORM\Propel\EntityPopulator($entity); + } + $entity->setColumnFormatters($entity->guessColumnFormatters($this->generator)); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + $entity->setModifiers($entity->guessModifiers($this->generator)); + if ($customModifiers) { + $entity->mergeModifiersWith($customModifiers); + } + $class = $entity->getClass(); + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + } + + /** + * Populate the database using all the Entity classes previously added. + * + * @param PropelPDO $con A Propel connection object + * + * @return array A list of the inserted PKs + */ + public function execute($con = null) + { + if ($con === null) { + $con = $this->getConnection(); + } + $isInstancePoolingEnabled = \Propel::isInstancePoolingEnabled(); + \Propel::disableInstancePooling(); + $insertedEntities = array(); + $con->beginTransaction(); + foreach ($this->quantities as $class => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute($con, $insertedEntities); + } + } + $con->commit(); + if ($isInstancePoolingEnabled) { + \Propel::enableInstancePooling(); + } + + return $insertedEntities; + } + + protected function getConnection() + { + // use the first connection available + $class = key($this->entities); + + if (!$class) { + throw new \RuntimeException('No class found from entities. Did you add entities to the Populator ?'); + } + + $peer = $class::PEER; + + return \Propel::getConnection($peer::DATABASE_NAME, \Propel::CONNECTION_WRITE); + } +} diff --git a/src/Faker/ORM/Propel2/ColumnTypeGuesser.php b/src/Faker/ORM/Propel2/ColumnTypeGuesser.php new file mode 100644 index 0000000..2b348a4 --- /dev/null +++ b/src/Faker/ORM/Propel2/ColumnTypeGuesser.php @@ -0,0 +1,108 @@ +generator = $generator; + } + + /** + * @param ColumnMap $column + * @return \Closure|null + */ + public function guessFormat(ColumnMap $column) + { + $generator = $this->generator; + if ($column->isTemporal()) { + if ($column->getType() == PropelTypes::BU_DATE || $column->getType() == PropelTypes::BU_TIMESTAMP) { + return function () use ($generator) { + return $generator->dateTime; + }; + } + + return function () use ($generator) { + return $generator->dateTimeAD; + }; + } + $type = $column->getType(); + switch ($type) { + case PropelTypes::BOOLEAN: + case PropelTypes::BOOLEAN_EMU: + return function () use ($generator) { + return $generator->boolean; + }; + case PropelTypes::NUMERIC: + case PropelTypes::DECIMAL: + $size = $column->getSize(); + + return function () use ($generator, $size) { + return $generator->randomNumber($size + 2) / 100; + }; + case PropelTypes::TINYINT: + return function () { + return mt_rand(0, 127); + }; + case PropelTypes::SMALLINT: + return function () { + return mt_rand(0, 32767); + }; + case PropelTypes::INTEGER: + return function () { + return mt_rand(0, intval('2147483647')); + }; + case PropelTypes::BIGINT: + return function () { + return mt_rand(0, intval('9223372036854775807')); + }; + case PropelTypes::FLOAT: + return function () { + return mt_rand(0, intval('2147483647')) / mt_rand(1, intval('2147483647')); + }; + case PropelTypes::DOUBLE: + case PropelTypes::REAL: + return function () { + return mt_rand(0, intval('9223372036854775807')) / mt_rand(1, intval('9223372036854775807')); + }; + case PropelTypes::CHAR: + case PropelTypes::VARCHAR: + case PropelTypes::BINARY: + case PropelTypes::VARBINARY: + $size = $column->getSize(); + + return function () use ($generator, $size) { + return $generator->text($size); + }; + case PropelTypes::LONGVARCHAR: + case PropelTypes::LONGVARBINARY: + case PropelTypes::CLOB: + case PropelTypes::CLOB_EMU: + case PropelTypes::BLOB: + return function () use ($generator) { + return $generator->text; + }; + case PropelTypes::ENUM: + $valueSet = $column->getValueSet(); + + return function () use ($generator, $valueSet) { + return $generator->randomElement($valueSet); + }; + case PropelTypes::OBJECT: + case PropelTypes::PHP_ARRAY: + default: + // no smart way to guess what the user expects here + return null; + } + } +} diff --git a/src/Faker/ORM/Propel2/EntityPopulator.php b/src/Faker/ORM/Propel2/EntityPopulator.php new file mode 100644 index 0000000..657998f --- /dev/null +++ b/src/Faker/ORM/Propel2/EntityPopulator.php @@ -0,0 +1,195 @@ +class = $class; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + public function setColumnFormatters($columnFormatters) + { + $this->columnFormatters = $columnFormatters; + } + + /** + * @return array + */ + public function getColumnFormatters() + { + return $this->columnFormatters; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @return array + */ + public function guessColumnFormatters(\Siteworx\Faker\Generator $generator) + { + $formatters = array(); + $class = $this->class; + $peerClass = $class::TABLE_MAP; + $tableMap = $peerClass::getTableMap(); + $nameGuesser = new \Siteworx\Faker\Guesser\Name($generator); + $columnTypeGuesser = new \Siteworx\Faker\ORM\Propel2\ColumnTypeGuesser($generator); + foreach ($tableMap->getColumns() as $columnMap) { + // skip behavior columns, handled by modifiers + if ($this->isColumnBehavior($columnMap)) { + continue; + } + if ($columnMap->isForeignKey()) { + $relatedClass = $columnMap->getRelation()->getForeignTable()->getClassname(); + $formatters[$columnMap->getPhpName()] = function ($inserted) use ($relatedClass) { + $relatedClass = trim($relatedClass, "\\"); + return isset($inserted[$relatedClass]) ? $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)] : null; + }; + continue; + } + if ($columnMap->isPrimaryKey()) { + continue; + } + if ($formatter = $nameGuesser->guessFormat($columnMap->getPhpName(), $columnMap->getSize())) { + $formatters[$columnMap->getPhpName()] = $formatter; + continue; + } + if ($formatter = $columnTypeGuesser->guessFormat($columnMap)) { + $formatters[$columnMap->getPhpName()] = $formatter; + continue; + } + } + + return $formatters; + } + + /** + * @param ColumnMap $columnMap + * @return bool + */ + protected function isColumnBehavior(ColumnMap $columnMap) + { + foreach ($columnMap->getTable()->getBehaviors() as $name => $params) { + $columnName = Base::toLower($columnMap->getName()); + switch ($name) { + case 'nested_set': + $columnNames = array($params['left_column'], $params['right_column'], $params['level_column']); + if (in_array($columnName, $columnNames)) { + return true; + } + break; + case 'timestampable': + $columnNames = array($params['create_column'], $params['update_column']); + if (in_array($columnName, $columnNames)) { + return true; + } + break; + } + } + + return false; + } + + public function setModifiers($modifiers) + { + $this->modifiers = $modifiers; + } + + /** + * @return array + */ + public function getModifiers() + { + return $this->modifiers; + } + + public function mergeModifiersWith($modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + /** + * @param \Siteworx\Faker\Generator $generator + * @return array + */ + public function guessModifiers(\Siteworx\Faker\Generator $generator) + { + $modifiers = array(); + $class = $this->class; + $peerClass = $class::TABLE_MAP; + $tableMap = $peerClass::getTableMap(); + foreach ($tableMap->getBehaviors() as $name => $params) { + switch ($name) { + case 'nested_set': + $modifiers['nested_set'] = function ($obj, $inserted) use ($class, $generator) { + if (isset($inserted[$class])) { + $queryClass = $class . 'Query'; + $parent = $queryClass::create()->findPk($generator->randomElement($inserted[$class])); + $obj->insertAsLastChildOf($parent); + } else { + $obj->makeRoot(); + } + }; + break; + case 'sortable': + $modifiers['sortable'] = function ($obj, $inserted) use ($class) { + $maxRank = isset($inserted[$class]) ? count($inserted[$class]) : 0; + $obj->insertAtRank(mt_rand(1, $maxRank + 1)); + }; + break; + } + } + + return $modifiers; + } + + /** + * Insert one new record using the Entity class. + */ + public function execute($con, $insertedEntities) + { + $obj = new $this->class(); + foreach ($this->getColumnFormatters() as $column => $format) { + if ($format !== null) { + $obj->setByName($column, is_callable($format) ? $format($insertedEntities, $obj) : $format); + } + } + foreach ($this->getModifiers() as $modifier) { + $modifier($obj, $insertedEntities); + } + $obj->save($con); + + return $obj->getPrimaryKey(); + } +} diff --git a/src/Faker/ORM/Propel2/Populator.php b/src/Faker/ORM/Propel2/Populator.php new file mode 100644 index 0000000..dcff147 --- /dev/null +++ b/src/Faker/ORM/Propel2/Populator.php @@ -0,0 +1,95 @@ +generator = $generator; + } + + /** + * Add an order for the generation of $number records for $entity. + * + * @param mixed $entity A Propel ActiveRecord classname, or a \Faker\ORM\Propel2\EntityPopulator instance + * @param int $number The number of entities to populate + */ + public function addEntity($entity, $number, $customColumnFormatters = array(), $customModifiers = array()) + { + if (!$entity instanceof \Siteworx\Faker\ORM\Propel2\EntityPopulator) { + $entity = new \Siteworx\Faker\ORM\Propel2\EntityPopulator($entity); + } + $entity->setColumnFormatters($entity->guessColumnFormatters($this->generator)); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + $entity->setModifiers($entity->guessModifiers($this->generator)); + if ($customModifiers) { + $entity->mergeModifiersWith($customModifiers); + } + $class = $entity->getClass(); + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + } + + /** + * Populate the database using all the Entity classes previously added. + * + * @param PropelPDO $con A Propel connection object + * + * @return array A list of the inserted PKs + */ + public function execute($con = null) + { + if ($con === null) { + $con = $this->getConnection(); + } + $isInstancePoolingEnabled = Propel::isInstancePoolingEnabled(); + Propel::disableInstancePooling(); + $insertedEntities = array(); + $con->beginTransaction(); + foreach ($this->quantities as $class => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute($con, $insertedEntities); + } + } + $con->commit(); + if ($isInstancePoolingEnabled) { + Propel::enableInstancePooling(); + } + + return $insertedEntities; + } + + protected function getConnection() + { + // use the first connection available + $class = key($this->entities); + + if (!$class) { + throw new \RuntimeException('No class found from entities. Did you add entities to the Populator ?'); + } + + $peer = $class::TABLE_MAP; + + return Propel::getConnection($peer::DATABASE_NAME, ServiceContainerInterface::CONNECTION_WRITE); + } +} diff --git a/src/Faker/ORM/Spot/ColumnTypeGuesser.php b/src/Faker/ORM/Spot/ColumnTypeGuesser.php new file mode 100644 index 0000000..7e5b3b9 --- /dev/null +++ b/src/Faker/ORM/Spot/ColumnTypeGuesser.php @@ -0,0 +1,78 @@ +generator = $generator; + } + + /** + * @param array $field + * @return \Closure|null + */ + public function guessFormat(array $field) + { + $generator = $this->generator; + $type = $field['type']; + switch ($type) { + case 'boolean': + return function () use ($generator) { + return $generator->boolean; + }; + case 'decimal': + $size = isset($field['precision']) ? $field['precision'] : 2; + + return function () use ($generator, $size) { + return $generator->randomNumber($size + 2) / 100; + }; + case 'smallint': + return function () use ($generator) { + return $generator->numberBetween(0, 65535); + }; + case 'integer': + return function () use ($generator) { + return $generator->numberBetween(0, intval('2147483647')); + }; + case 'bigint': + return function () use ($generator) { + return $generator->numberBetween(0, intval('18446744073709551615')); + }; + case 'float': + return function () use ($generator) { + return $generator->randomFloat(null, 0, intval('4294967295')); + }; + case 'string': + $size = isset($field['length']) ? $field['length'] : 255; + + return function () use ($generator, $size) { + return $generator->text($size); + }; + case 'text': + return function () use ($generator) { + return $generator->text; + }; + case 'datetime': + case 'date': + case 'time': + return function () use ($generator) { + return $generator->datetime; + }; + default: + // no smart way to guess what the user expects here + return null; + } + } +} diff --git a/src/Faker/ORM/Spot/EntityPopulator.php b/src/Faker/ORM/Spot/EntityPopulator.php new file mode 100644 index 0000000..7fc01c4 --- /dev/null +++ b/src/Faker/ORM/Spot/EntityPopulator.php @@ -0,0 +1,220 @@ +mapper = $mapper; + $this->locator = $locator; + $this->useExistingData = $useExistingData; + } + + /** + * @return string + */ + public function getMapper() + { + return $this->mapper; + } + + /** + * @param $columnFormatters + */ + public function setColumnFormatters($columnFormatters) + { + $this->columnFormatters = $columnFormatters; + } + + /** + * @return array + */ + public function getColumnFormatters() + { + return $this->columnFormatters; + } + + /** + * @param $columnFormatters + */ + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + /** + * @param array $modifiers + */ + public function setModifiers(array $modifiers) + { + $this->modifiers = $modifiers; + } + + /** + * @return array + */ + public function getModifiers() + { + return $this->modifiers; + } + + /** + * @param array $modifiers + */ + public function mergeModifiersWith(array $modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + /** + * @param Generator $generator + * @return array + */ + public function guessColumnFormatters(Generator $generator) + { + $formatters = array(); + $nameGuesser = new Name($generator); + $columnTypeGuesser = new ColumnTypeGuesser($generator); + $fields = $this->mapper->fields(); + foreach ($fields as $fieldName => $field) { + if ($field['primary'] === true) { + continue; + } + if ($formatter = $nameGuesser->guessFormat($fieldName)) { + $formatters[$fieldName] = $formatter; + continue; + } + if ($formatter = $columnTypeGuesser->guessFormat($field)) { + $formatters[$fieldName] = $formatter; + continue; + } + } + $entityName = $this->mapper->entity(); + $entity = $this->mapper->build([]); + $relations = $entityName::relations($this->mapper, $entity); + foreach ($relations as $relation) { + // We don't need any other relation here. + if ($relation instanceof BelongsTo) { + $fieldName = $relation->localKey(); + $entityName = $relation->entityName(); + $field = $fields[$fieldName]; + $required = $field['required']; + + $locator = $this->locator; + + $formatters[$fieldName] = function ($inserted) use ($required, $entityName, $locator) { + if (!empty($inserted[$entityName])) { + return $inserted[$entityName][mt_rand(0, count($inserted[$entityName]) - 1)]->get('id'); + } + + if ($required && $this->useExistingData) { + // We did not add anything like this, but it's required, + // So let's find something existing in DB. + $mapper = $locator->mapper($entityName); + $records = $mapper->all()->limit(self::RELATED_FETCH_COUNT)->toArray(); + if (empty($records)) { + return null; + } + + return $records[mt_rand(0, count($records) - 1)]['id']; + } + + return null; + }; + } + } + + return $formatters; + } + + /** + * Insert one new record using the Entity class. + * + * @param $insertedEntities + * @return string + */ + public function execute($insertedEntities) + { + $obj = $this->mapper->build([]); + + $this->fillColumns($obj, $insertedEntities); + $this->callMethods($obj, $insertedEntities); + + $this->mapper->insert($obj); + + + return $obj; + } + + /** + * @param $obj + * @param $insertedEntities + */ + private function fillColumns($obj, $insertedEntities) + { + foreach ($this->columnFormatters as $field => $format) { + if ($format !== null) { + $value = is_callable($format) ? $format($insertedEntities, $obj) : $format; + $obj->set($field, $value); + } + } + } + + /** + * @param $obj + * @param $insertedEntities + */ + private function callMethods($obj, $insertedEntities) + { + foreach ($this->getModifiers() as $modifier) { + $modifier($obj, $insertedEntities); + } + } +} diff --git a/src/Faker/ORM/Spot/Populator.php b/src/Faker/ORM/Spot/Populator.php new file mode 100644 index 0000000..9573284 --- /dev/null +++ b/src/Faker/ORM/Spot/Populator.php @@ -0,0 +1,92 @@ +generator = $generator; + $this->locator = $locator; + } + + /** + * Add an order for the generation of $number records for $entity. + * + * @param $entityName string Name of Entity object to generate + * @param $number int The number of entities to populate + * @param $customColumnFormatters array + * @param $customModifiers array + * @param $useExistingData bool Should we use existing rows (e.g. roles) to populate relations? + */ + public function addEntity( + $entityName, + $number, + $customColumnFormatters = array(), + $customModifiers = array(), + $useExistingData = false + ) + { + $mapper = $this->locator->mapper($entityName); + if ($mapper === null) { + throw new \InvalidArgumentException("No mapper can be found for entity " . $entityName); + } + $entity = new EntityPopulator($mapper, $this->locator, $useExistingData); + + $entity->setColumnFormatters($entity->guessColumnFormatters($this->generator)); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + $entity->mergeModifiersWith($customModifiers); + + $this->entities[$entityName] = $entity; + $this->quantities[$entityName] = $number; + } + + /** + * Populate the database using all the Entity classes previously added. + * + * @param Locator $locator A Spot locator + * + * @return array A list of the inserted PKs + */ + public function execute($locator = null) + { + if ($locator === null) { + $locator = $this->locator; + } + if ($locator === null) { + throw new \InvalidArgumentException("No entity manager passed to Spot Populator."); + } + + $insertedEntities = array(); + foreach ($this->quantities as $entityName => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$entityName][] = $this->entities[$entityName]->execute( + $insertedEntities + ); + } + } + + return $insertedEntities; + } +} diff --git a/src/Faker/Provider/Address.php b/src/Faker/Provider/Address.php new file mode 100644 index 0000000..9f369f1 --- /dev/null +++ b/src/Faker/Provider/Address.php @@ -0,0 +1,147 @@ +generator->parse($format); + } + + /** + * @example 'Crist Parks' + */ + public function streetName() + { + $format = static::randomElement(static::$streetNameFormats); + + return $this->generator->parse($format); + } + + /** + * @example '791 Crist Parks' + */ + public function streetAddress() + { + $format = static::randomElement(static::$streetAddressFormats); + + return $this->generator->parse($format); + } + + /** + * @example 86039-9874 + */ + public static function postcode() + { + return static::toUpper(static::bothify(static::randomElement(static::$postcode))); + } + + /** + * @example '791 Crist Parks, Sashabury, IL 86039-9874' + */ + public function address() + { + $format = static::randomElement(static::$addressFormats); + + return $this->generator->parse($format); + } + + /** + * @example 'Japan' + */ + public static function country() + { + return static::randomElement(static::$country); + } + + /** + * @example '77.147489' + * @param float|int $min + * @param float|int $max + * @return float Uses signed degrees format (returns a float number between -90 and 90) + */ + public static function latitude($min = -90, $max = 90) + { + return static::randomFloat(6, $min, $max); + } + + /** + * @example '86.211205' + * @param float|int $min + * @param float|int $max + * @return float Uses signed degrees format (returns a float number between -180 and 180) + */ + public static function longitude($min = -180, $max = 180) + { + return static::randomFloat(6, $min, $max); + } + + /** + * @example array('77.147489', '86.211205') + * @return array | latitude, longitude + */ + public static function localCoordinates() + { + return array( + 'latitude' => static::latitude(), + 'longitude' => static::longitude() + ); + } +} diff --git a/src/Faker/Provider/Barcode.php b/src/Faker/Provider/Barcode.php new file mode 100644 index 0000000..5150355 --- /dev/null +++ b/src/Faker/Provider/Barcode.php @@ -0,0 +1,114 @@ + $digit) { + $sums += $digit * $sequence[$n % 2]; + } + return (10 - $sums % 10) % 10; + } + + /** + * ISBN-10 check digit + * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digits + * + * @param string $input ISBN without check-digit + * @throws \LengthException When wrong input length passed + * + * @return integer Check digit + */ + protected static function isbnChecksum($input) + { + // We're calculating check digit for ISBN-10 + // so, the length of the input should be 9 + $length = 9; + + if (strlen($input) !== $length) { + throw new \LengthException(sprintf('Input length should be equal to %d', $length)); + } + + $digits = str_split($input); + array_walk( + $digits, + function (&$digit, $position) { + $digit = (10 - $position) * $digit; + } + ); + $result = (11 - array_sum($digits) % 11) % 11; + + // 10 is replaced by X + return $result < 10 ? $result : 'X'; + } + + /** + * Get a random EAN13 barcode. + * @return string + * @example '4006381333931' + */ + public function ean13() + { + return $this->ean(13); + } + + /** + * Get a random EAN8 barcode. + * @return string + * @example '73513537' + */ + public function ean8() + { + return $this->ean(8); + } + + /** + * Get a random ISBN-10 code + * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number + * + * @return string + * @example '4881416324' + */ + public function isbn10() + { + $code = static::numerify(str_repeat('#', 9)); + + return $code . static::isbnChecksum($code); + } + + /** + * Get a random ISBN-13 code + * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number + * + * @return string + * @example '9790404436093' + */ + public function isbn13() + { + $code = '97' . static::numberBetween(8, 9) . static::numerify(str_repeat('#', 9)); + + return $code . static::eanChecksum($code); + } +} diff --git a/src/Faker/Provider/Base.php b/src/Faker/Provider/Base.php new file mode 100644 index 0000000..e049a90 --- /dev/null +++ b/src/Faker/Provider/Base.php @@ -0,0 +1,643 @@ +generator = $generator; + } + + /** + * Returns a random number between 0 and 9 + * + * @return integer + */ + public static function randomDigit() + { + return mt_rand(0, 9); + } + + /** + * Returns a random number between 1 and 9 + * + * @return integer + */ + public static function randomDigitNotNull() + { + return mt_rand(1, 9); + } + + /** + * Generates a random digit, which cannot be $except + * + * @param int $except + * @return int + */ + public static function randomDigitNot($except) + { + $result = self::numberBetween(0, 8); + if ($result >= $except) { + $result++; + } + return $result; + } + + /** + * Returns a random integer with 0 to $nbDigits digits. + * + * The maximum value returned is mt_getrandmax() + * + * @param integer $nbDigits Defaults to a random number between 1 and 9 + * @param boolean $strict Whether the returned number should have exactly $nbDigits + * @example 79907610 + * + * @return integer + */ + public static function randomNumber($nbDigits = null, $strict = false) + { + if (!is_bool($strict)) { + throw new \InvalidArgumentException('randomNumber() generates numbers of fixed width. To generate numbers between two boundaries, use numberBetween() instead.'); + } + if ($nbDigits === null) { + $nbDigits = static::randomDigitNotNull(); + } + $max = pow(10, $nbDigits) - 1; + if ($max > mt_getrandmax()) { + throw new \InvalidArgumentException('randomNumber() can only generate numbers up to mt_getrandmax()'); + } + if ($strict) { + return mt_rand(pow(10, $nbDigits - 1), $max); + } + + return mt_rand(0, $max); + } + + /** + * Return a random float number + * + * @param int $nbMaxDecimals + * @param int|float $min + * @param int|float $max + * @example 48.8932 + * + * @return float + */ + public static function randomFloat($nbMaxDecimals = null, $min = 0, $max = null) + { + if ($nbMaxDecimals === null) { + $nbMaxDecimals = static::randomDigit(); + } + + if ($max === null) { + $max = static::randomNumber(); + if ($min > $max) { + $max = $min; + } + } + + if ($min > $max) { + $tmp = $min; + $min = $max; + $max = $tmp; + } + + return round($min + mt_rand() / mt_getrandmax() * ($max - $min), $nbMaxDecimals); + } + + /** + * Returns a random number between $int1 and $int2 (any order) + * + * @param integer $int1 default to 0 + * @param integer $int2 defaults to 32 bit max integer, ie 2147483647 + * @example 79907610 + * + * @return integer + */ + public static function numberBetween($int1 = 0, $int2 = 2147483647) + { + $min = $int1 < $int2 ? $int1 : $int2; + $max = $int1 < $int2 ? $int2 : $int1; + return mt_rand($min, $max); + } + + /** + * Returns the passed value + * + * @param mixed $value + * + * @return mixed + */ + public static function passthrough($value) + { + return $value; + } + + /** + * Returns a random letter from a to z + * + * @return string + */ + public static function randomLetter() + { + return chr(mt_rand(97, 122)); + } + + /** + * Returns a random ASCII character (excluding accents and special chars) + */ + public static function randomAscii() + { + return chr(mt_rand(33, 126)); + } + + /** + * Returns randomly ordered subsequence of $count elements from a provided array + * + * @param array $array Array to take elements from. Defaults to a-c + * @param integer $count Number of elements to take. + * @param boolean $allowDuplicates Allow elements to be picked several times. Defaults to false + * @throws \LengthException When requesting more elements than provided + * + * @return array New array with $count elements from $array + */ + public static function randomElements($array = array('a', 'b', 'c'), $count = 1, $allowDuplicates = false) + { + $traversables = array(); + + if ($array instanceof \Traversable) { + foreach ($array as $element) { + $traversables[] = $element; + } + } + + $arr = count($traversables) ? $traversables : $array; + + $allKeys = array_keys($arr); + $numKeys = count($allKeys); + + if (!$allowDuplicates && $numKeys < $count) { + throw new \LengthException(sprintf('Cannot get %d elements, only %d in array', $count, $numKeys)); + } + + $highKey = $numKeys - 1; + $keys = $elements = array(); + $numElements = 0; + + while ($numElements < $count) { + $num = mt_rand(0, $highKey); + + if (!$allowDuplicates) { + if (isset($keys[$num])) { + continue; + } + $keys[$num] = true; + } + + $elements[] = $arr[$allKeys[$num]]; + $numElements++; + } + + return $elements; + } + + /** + * Returns a random element from a passed array + * + * @param array $array + * @return mixed + */ + public static function randomElement($array = array('a', 'b', 'c')) + { + if (!$array || ($array instanceof \Traversable && !count($array))) { + return null; + } + $elements = static::randomElements($array, 1); + + return $elements[0]; + } + + /** + * Returns a random key from a passed associative array + * + * @param array $array + * @return int|string|null + */ + public static function randomKey($array = array()) + { + if (!$array) { + return null; + } + $keys = array_keys($array); + return $keys[mt_rand(0, count($keys) - 1)]; + } + + /** + * Returns a shuffled version of the argument. + * + * This function accepts either an array, or a string. + * + * @example $faker->shuffle([1, 2, 3]); // [2, 1, 3] + * @example $faker->shuffle('hello, world'); // 'rlo,h eold!lw' + * + * @see shuffleArray() + * @see shuffleString() + * + * @param array|string $arg The set to shuffle + * @return array|string The shuffled set + */ + public static function shuffle($arg = '') + { + if (is_array($arg)) { + return static::shuffleArray($arg); + } + if (is_string($arg)) { + return static::shuffleString($arg); + } + throw new \InvalidArgumentException('shuffle() only supports strings or arrays'); + } + + /** + * Returns a shuffled version of the array. + * + * This function does not mutate the original array. It uses the + * Fisher–Yates algorithm, which is unbiased, together with a Mersenne + * twister random generator. This function is therefore more random than + * PHP's shuffle() function, and it is seedable. + * + * @link http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + * + * @example $faker->shuffleArray([1, 2, 3]); // [2, 1, 3] + * + * @param array $array The set to shuffle + * @return array The shuffled set + */ + public static function shuffleArray($array = array()) + { + $shuffledArray = array(); + $i = 0; + reset($array); + foreach ($array as $key => $value) { + if ($i == 0) { + $j = 0; + } else { + $j = mt_rand(0, $i); + } + if ($j == $i) { + $shuffledArray[] = $value; + } else { + $shuffledArray[] = $shuffledArray[$j]; + $shuffledArray[$j] = $value; + } + $i++; + } + return $shuffledArray; + } + + /** + * Returns a shuffled version of the string. + * + * This function does not mutate the original string. It uses the + * Fisher–Yates algorithm, which is unbiased, together with a Mersenne + * twister random generator. This function is therefore more random than + * PHP's shuffle() function, and it is seedable. Additionally, it is + * UTF8 safe if the mb extension is available. + * + * @link http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + * + * @example $faker->shuffleString('hello, world'); // 'rlo,h eold!lw' + * + * @param string $string The set to shuffle + * @param string $encoding The string encoding (defaults to UTF-8) + * @return string The shuffled set + */ + public static function shuffleString($string = '', $encoding = 'UTF-8') + { + if (function_exists('mb_strlen')) { + // UTF8-safe str_split() + $array = array(); + $strlen = mb_strlen($string, $encoding); + for ($i = 0; $i < $strlen; $i++) { + $array [] = mb_substr($string, $i, 1, $encoding); + } + } else { + $array = str_split($string, 1); + } + return implode('', static::shuffleArray($array)); + } + + private static function replaceWildcard($string, $wildcard = '#', $callback = 'static::randomDigit') + { + if (($pos = strpos($string, $wildcard)) === false) { + return $string; + } + for ($i = $pos, $last = strrpos($string, $wildcard, $pos) + 1; $i < $last; $i++) { + if ($string[$i] === $wildcard) { + $string[$i] = call_user_func($callback); + } + } + return $string; + } + + /** + * Replaces all hash sign ('#') occurrences with a random number + * Replaces all percentage sign ('%') occurrences with a not null number + * + * @param string $string String that needs to bet parsed + * @return string + */ + public static function numerify($string = '###') + { + // instead of using randomDigit() several times, which is slow, + // count the number of hashes and generate once a large number + $toReplace = array(); + if (($pos = strpos($string, '#')) !== false) { + for ($i = $pos, $last = strrpos($string, '#', $pos) + 1; $i < $last; $i++) { + if ($string[$i] === '#') { + $toReplace[] = $i; + } + } + } + if ($nbReplacements = count($toReplace)) { + $maxAtOnce = strlen((string) mt_getrandmax()) - 1; + $numbers = ''; + $i = 0; + while ($i < $nbReplacements) { + $size = min($nbReplacements - $i, $maxAtOnce); + $numbers .= str_pad(static::randomNumber($size), $size, '0', STR_PAD_LEFT); + $i += $size; + } + for ($i = 0; $i < $nbReplacements; $i++) { + $string[$toReplace[$i]] = $numbers[$i]; + } + } + $string = self::replaceWildcard($string, '%', 'static::randomDigitNotNull'); + + return $string; + } + + /** + * Replaces all question mark ('?') occurrences with a random letter + * + * @param string $string String that needs to bet parsed + * @return string + */ + public static function lexify($string = '????') + { + return self::replaceWildcard($string, '?', 'static::randomLetter'); + } + + /** + * Replaces hash signs ('#') and question marks ('?') with random numbers and letters + * An asterisk ('*') is replaced with either a random number or a random letter + * + * @param string $string String that needs to bet parsed + * @return string + */ + public static function bothify($string = '## ??') + { + $string = self::replaceWildcard( + $string, + '*', + function () { + return mt_rand(0, 1) ? '#' : '?'; + } + ); + return static::lexify(static::numerify($string)); + } + + /** + * Replaces * signs with random numbers and letters and special characters + * + * @example $faker->asciify(''********'); // "s5'G!uC3" + * + * @param string $string String that needs to bet parsed + * @return string + */ + public static function asciify($string = '****') + { + return preg_replace_callback('/\*/u', 'static::randomAscii', $string); + } + + /** + * Transforms a basic regular expression into a random string satisfying the expression. + * + * @example $faker->regexify('[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'); // sm0@y8k96a.ej + * + * Regex delimiters '/.../' and begin/end markers '^...$' are ignored. + * + * Only supports a small subset of the regex syntax. For instance, + * unicode, negated classes, unbounded ranges, subpatterns, back references, + * assertions, recursive patterns, and comments are not supported. Escaping + * support is extremely fragile. + * + * This method is also VERY slow. Use it only when no other formatter + * can generate the fake data you want. For instance, prefer calling + * `$faker->email` rather than `regexify` with the previous regular + * expression. + * + * Also note than `bothify` can probably do most of what this method does, + * but much faster. For instance, for a dummy email generation, try + * `$faker->bothify('?????????@???.???')`. + * + * @see https://github.com/icomefromthenet/ReverseRegex for a more robust implementation + * + * @param string $regex A regular expression (delimiters are optional) + * @return string + */ + public static function regexify($regex = '') + { + // ditch the anchors + $regex = preg_replace('/^\/?\^?/', '', $regex); + $regex = preg_replace('/\$?\/?$/', '', $regex); + // All {2} become {2,2} + $regex = preg_replace('/\{(\d+)\}/', '{\1,\1}', $regex); + // Single-letter quantifiers (?, *, +) become bracket quantifiers ({0,1}, {0,rand}, {1, rand}) + $regex = preg_replace('/(? 0 && $weight < 1 && mt_rand() / mt_getrandmax() <= $weight) { + return $this->generator; + } + + // new system with percentage + if (is_int($weight) && mt_rand(1, 100) <= $weight) { + return $this->generator; + } + + return new DefaultGenerator($default); + } + + /** + * Chainable method for making any formatter unique. + * + * + * // will never return twice the same value + * $faker->unique()->randomElement(array(1, 2, 3)); + * + * + * @param boolean $reset If set to true, resets the list of existing values + * @param integer $maxRetries Maximum number of retries to find a unique value, + * After which an OverflowException is thrown. + * @throws \OverflowException When no unique value can be found by iterating $maxRetries times + * + * @return UniqueGenerator A proxy class returning only non-existing values + */ + public function unique($reset = false, $maxRetries = 10000) + { + if ($reset || !$this->unique) { + $this->unique = new UniqueGenerator($this->generator, $maxRetries); + } + + return $this->unique; + } + + /** + * Chainable method for forcing any formatter to return only valid values. + * + * The value validity is determined by a function passed as first argument. + * + * + * $values = array(); + * $evenValidator = function ($digit) { + * return $digit % 2 === 0; + * }; + * for ($i=0; $i < 10; $i++) { + * $values []= $faker->valid($evenValidator)->randomDigit; + * } + * print_r($values); // [0, 4, 8, 4, 2, 6, 0, 8, 8, 6] + * + * + * @param Closure $validator A function returning true for valid values + * @param integer $maxRetries Maximum number of retries to find a unique value, + * After which an OverflowException is thrown. + * @throws \OverflowException When no valid value can be found by iterating $maxRetries times + * + * @return ValidGenerator A proxy class returning only valid values + */ + public function valid($validator = null, $maxRetries = 10000) + { + return new ValidGenerator($this->generator, $validator, $maxRetries); + } +} diff --git a/src/Faker/Provider/Biased.php b/src/Faker/Provider/Biased.php new file mode 100644 index 0000000..f716a71 --- /dev/null +++ b/src/Faker/Provider/Biased.php @@ -0,0 +1,64 @@ +generator->parse($format); + } + + /** + * @example 'Ltd' + * + * @return string + */ + public static function companySuffix() + { + return static::randomElement(static::$companySuffix); + } + + /** + * @example 'Job' + * + * @return string + */ + public function jobTitle() + { + $format = static::randomElement(static::$jobTitleFormat); + + return $this->generator->parse($format); + } +} diff --git a/src/Faker/Provider/DateTime.php b/src/Faker/Provider/DateTime.php new file mode 100644 index 0000000..c98abb1 --- /dev/null +++ b/src/Faker/Provider/DateTime.php @@ -0,0 +1,341 @@ +getTimestamp(); + } + + return strtotime(empty($max) ? 'now' : $max); + } + + /** + * Get a timestamp between January 1, 1970 and now + * + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return int + * + * @example 1061306726 + */ + public static function unixTime($max = 'now') + { + return mt_rand(0, static::getMaxTimestamp($max)); + } + + /** + * Get a datetime object for a date between January 1, 1970 and now + * + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('2005-08-16 20:39:21') + * @return \DateTime + * @see http://php.net/manual/en/timezones.php + * @see http://php.net/manual/en/function.date-default-timezone-get.php + */ + public static function dateTime($max = 'now', $timezone = null) + { + return static::setTimezone( + new \DateTime('@' . static::unixTime($max)), + $timezone + ); + } + + /** + * Get a datetime object for a date between January 1, 001 and now + * + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('1265-03-22 21:15:52') + * @return \DateTime + * @see http://php.net/manual/en/timezones.php + * @see http://php.net/manual/en/function.date-default-timezone-get.php + */ + public static function dateTimeAD($max = 'now', $timezone = null) + { + $min = (PHP_INT_SIZE > 4 ? -62135597361 : -PHP_INT_MAX); + return static::setTimezone( + new \DateTime('@' . mt_rand($min, static::getMaxTimestamp($max))), + $timezone + ); + } + + /** + * get a date string formatted with ISO8601 + * + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '2003-10-21T16:05:52+0000' + */ + public static function iso8601($max = 'now') + { + return static::date(\DateTime::ISO8601, $max); + } + + /** + * Get a date string between January 1, 1970 and now + * + * @param string $format + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '2008-11-27' + */ + public static function date($format = 'Y-m-d', $max = 'now') + { + return static::dateTime($max)->format($format); + } + + /** + * Get a time string (24h format by default) + * + * @param string $format + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '15:02:34' + */ + public static function time($format = 'H:i:s', $max = 'now') + { + return static::dateTime($max)->format($format); + } + + /** + * Get a DateTime object based on a random date between two given dates. + * Accepts date strings that can be recognized by strtotime(). + * + * @param \DateTime|string $startDate Defaults to 30 years ago + * @param \DateTime|string $endDate Defaults to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('1999-02-02 11:42:52') + * @return \DateTime + * @see http://php.net/manual/en/timezones.php + * @see http://php.net/manual/en/function.date-default-timezone-get.php + */ + public static function dateTimeBetween($startDate = '-30 years', $endDate = 'now', $timezone = null) + { + $startTimestamp = $startDate instanceof \DateTime ? $startDate->getTimestamp() : strtotime($startDate); + $endTimestamp = static::getMaxTimestamp($endDate); + + if ($startTimestamp > $endTimestamp) { + throw new \InvalidArgumentException('Start date must be anterior to end date.'); + } + + $timestamp = mt_rand($startTimestamp, $endTimestamp); + + return static::setTimezone( + new \DateTime('@' . $timestamp), + $timezone + ); + } + + /** + * Get a DateTime object based on a random date between one given date and + * an interval + * Accepts date string that can be recognized by strtotime(). + * + * @param \DateTime|string $date Defaults to 30 years ago + * @param string $interval Defaults to 5 days after + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example dateTimeInInterval('1999-02-02 11:42:52', '+ 5 days') + * @return \DateTime + * @see http://php.net/manual/en/timezones.php + * @see http://php.net/manual/en/function.date-default-timezone-get.php + */ + public static function dateTimeInInterval($date = '-30 years', $interval = '+5 days', $timezone = null) + { + $intervalObject = \DateInterval::createFromDateString($interval); + $datetime = $date instanceof \DateTime ? $date : new \DateTime($date); + $otherDatetime = clone $datetime; + $otherDatetime->add($intervalObject); + + $begin = $datetime > $otherDatetime ? $otherDatetime : $datetime; + $end = $datetime === $begin ? $otherDatetime : $datetime; + + return static::dateTimeBetween( + $begin, + $end, + $timezone + ); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('1964-04-04 11:02:02') + * @return \DateTime + */ + public static function dateTimeThisCentury($max = 'now', $timezone = null) + { + return static::dateTimeBetween('-100 year', $max, $timezone); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('2010-03-10 05:18:58') + * @return \DateTime + */ + public static function dateTimeThisDecade($max = 'now', $timezone = null) + { + return static::dateTimeBetween('-10 year', $max, $timezone); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('2011-09-19 09:24:37') + * @return \DateTime + */ + public static function dateTimeThisYear($max = 'now', $timezone = null) + { + return static::dateTimeBetween('first day of january this year', $max, $timezone); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @param string|null $timezone time zone in which the date time should be set, default to DateTime::$defaultTimezone, if set, otherwise the result of `date_default_timezone_get` + * @example DateTime('2011-10-05 12:51:46') + * @return \DateTime + */ + public static function dateTimeThisMonth($max = 'now', $timezone = null) + { + return static::dateTimeBetween('-1 month', $max, $timezone); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example 'am' + */ + public static function amPm($max = 'now') + { + return static::dateTime($max)->format('a'); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '22' + */ + public static function dayOfMonth($max = 'now') + { + return static::dateTime($max)->format('d'); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example 'Tuesday' + */ + public static function dayOfWeek($max = 'now') + { + return static::dateTime($max)->format('l'); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '7' + */ + public static function month($max = 'now') + { + return static::dateTime($max)->format('m'); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example 'September' + */ + public static function monthName($max = 'now') + { + return static::dateTime($max)->format('F'); + } + + /** + * @param \DateTime|int|string $max maximum timestamp used as random end limit, default to "now" + * @return string + * @example '1673' + */ + public static function year($max = 'now') + { + return static::dateTime($max)->format('Y'); + } + + /** + * @return string + * @example 'XVII' + */ + public static function century() + { + return static::randomElement(static::$century); + } + + /** + * @return string + * @example 'Europe/Paris' + */ + public static function timezone() + { + return static::randomElement(\DateTimeZone::listIdentifiers()); + } + + /** + * Internal method to set the time zone on a DateTime. + * + * @param \DateTime $dt + * @param string|null $timezone + * + * @return \DateTime + */ + private static function setTimezone(\DateTime $dt, $timezone) + { + return $dt->setTimezone(new \DateTimeZone(static::resolveTimezone($timezone))); + } + + /** + * Sets default time zone. + * + * @param string $timezone + * + * @return void + */ + public static function setDefaultTimezone($timezone = null) + { + static::$defaultTimezone = $timezone; + } + + /** + * Gets default time zone. + * + * @return string|null + */ + public static function getDefaultTimezone() + { + return static::$defaultTimezone; + } + + /** + * @param string|null $timezone + * @return null|string + */ + private static function resolveTimezone($timezone) + { + return $timezone === null ? (static::$defaultTimezone === null ? date_default_timezone_get() : static::$defaultTimezone) : $timezone; + } +} diff --git a/src/Faker/Provider/File.php b/src/Faker/Provider/File.php new file mode 100644 index 0000000..161371e --- /dev/null +++ b/src/Faker/Provider/File.php @@ -0,0 +1,610 @@ + file extension(s) + * @link http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + */ + protected static $mimeTypes = array( + 'application/atom+xml' => 'atom', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/java-archive' => 'jar', + 'application/java-vm' => 'class', + 'application/javascript' => 'js', + 'application/json' => 'json', + 'application/jsonml+json' => 'jsonml', + 'application/lost+xml' => 'lostxml', + 'application/mathml+xml' => 'mathml', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp4' => 'mp4s', + 'application/msword' => array('doc', 'dot'), + 'application/octet-stream' => array( + 'bin', + 'dms', + 'lrf', + 'mar', + 'so', + 'dist', + 'distz', + 'pkg', + 'bpk', + 'dump', + 'elc', + 'deploy' + ), + 'application/ogg' => 'ogx', + 'application/omdoc+xml' => 'omdoc', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => array('asc', 'sig'), + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => array('ai', 'eps', 'ps'), + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + 'application/reginfo+xml' => 'rif', + 'application/rss+xml' => 'rss', + 'application/rtf' => 'rtf', + 'application/sbml+xml' => 'sbml', + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.xdp+xml' => 'xdp', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.ahead.space' => 'ahead', + 'application/vnd.dart' => 'dart', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dece.data' => array('uvf', 'uvvf', 'uvd', 'uvvd'), + 'application/vnd.dece.ttml+xml' => array('uvt', 'uvvt'), + 'application/vnd.dece.unspecified' => array('uvx', 'uvvx'), + 'application/vnd.dece.zip' => array('uvz', 'uvvz'), + 'application/vnd.denovo.fcselayout-link' => 'fe_launch', + 'application/vnd.dna' => 'dna', + 'application/vnd.dolby.mlp' => 'mlp', + 'application/vnd.dpgraph' => 'dpg', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.ds-keypoint' => 'kpxx', + 'application/vnd.dvb.ait' => 'ait', + 'application/vnd.dvb.service' => 'svc', + 'application/vnd.dynageo' => 'geo', + 'application/vnd.ecowin.chart' => 'mag', + 'application/vnd.enliven' => 'nml', + 'application/vnd.epson.esf' => 'esf', + 'application/vnd.epson.msf' => 'msf', + 'application/vnd.epson.quickanime' => 'qam', + 'application/vnd.epson.salt' => 'slt', + 'application/vnd.epson.ssf' => 'ssf', + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => array('seed', 'dataless'), + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.hal+xml' => 'hal', + 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', + 'application/vnd.ibm.minipay' => 'mpy', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => array('icc', 'icm'), + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => array('kpr', 'kpt'), + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => array('kwd', 'kwt'), + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => array('kne', 'knp'), + 'application/vnd.koan' => array('skp', 'skd', 'skt', 'skm'), + 'application/vnd.kodak-descriptor' => 'sse', + 'application/vnd.las.las+xml' => 'lasxml', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => '123', + 'application/vnd.lotus-approach' => 'apr', + 'application/vnd.lotus-freelance' => 'pre', + 'application/vnd.lotus-notes' => 'nsf', + 'application/vnd.lotus-organizer' => 'org', + 'application/vnd.lotus-screencam' => 'scm', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => array( + 'xls', + 'xlm', + 'xla', + 'xlc', + 'xlt', + 'xlw' + ), + 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', + 'application/vnd.ms-fontobject' => 'eot', + 'application/vnd.ms-htmlhelp' => 'chm', + 'application/vnd.ms-ims' => 'ims', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-officetheme' => 'thmx', + 'application/vnd.ms-pki.seccat' => 'cat', + 'application/vnd.ms-pki.stl' => 'stl', + 'application/vnd.ms-powerpoint' => array('ppt', 'pps', 'pot'), + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', + 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', + 'application/vnd.ms-project' => array('mpp', 'mpt'), + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => array('wps', 'wks', 'wcm', 'wdb'), + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.chart-template' => 'otc', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.formula-template' => 'odft', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.oasis.opendocument.image-template' => 'oti', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.olpc-sugar' => 'xo', + 'application/vnd.oma.dd2+xml' => 'dd2', + 'application/vnd.openofficeorg.extension' => 'oxt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', + 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.pvi.ptid1' => 'ptid', + 'application/vnd.quark.quarkxpress' => array( + 'qxd', + 'qxt', + 'qwd', + 'qwt', + 'qxl', + 'qxb' + ), + 'application/vnd.realvnc.bed' => 'bed', + 'application/vnd.recordare.musicxml' => 'mxl', + 'application/vnd.recordare.musicxml+xml' => 'musicxml', + 'application/vnd.rig.cryptonote' => 'cryptonote', + 'application/vnd.rim.cod' => 'cod', + 'application/vnd.rn-realmedia' => 'rm', + 'application/vnd.rn-realmedia-vbr' => 'rmvb', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + 'application/vnd.seemail' => 'see', + 'application/vnd.sema' => 'sema', + 'application/vnd.semd' => 'semd', + 'application/vnd.semf' => 'semf', + 'application/vnd.shana.informed.formdata' => 'ifm', + 'application/vnd.shana.informed.formtemplate' => 'itp', + 'application/vnd.shana.informed.interchange' => 'iif', + 'application/vnd.shana.informed.package' => 'ipk', + 'application/vnd.simtech-mindmapper' => array('twd', 'twds'), + 'application/vnd.smaf' => 'mmf', + 'application/vnd.stepmania.stepchart' => 'sm', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => array('sus', 'susp'), + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => array('sis', 'sisx'), + 'application/vnd.syncml+xml' => 'xsm', + 'application/vnd.syncml.dm+wbxml' => 'bdm', + 'application/vnd.syncml.dm+xml' => 'xdm', + 'application/vnd.tao.intent-module-archive' => 'tao', + 'application/vnd.tcpdump.pcap' => array('pcap', 'cap', 'dmp'), + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => array('ufd', 'ufdl'), + 'application/vnd.uiq.theme' => 'utz', + 'application/vnd.umajin' => 'umj', + 'application/vnd.unity' => 'unityweb', + 'application/vnd.uoml+xml' => 'uoml', + 'application/vnd.vcx' => 'vcx', + 'application/vnd.visio' => array('vsd', 'vst', 'vss', 'vsw'), + 'application/vnd.visionary' => 'vis', + 'application/vnd.vsf' => 'vsf', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wolfram.player' => 'nbp', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + 'application/vnd.xfdl' => 'xfdl', + 'application/voicexml+xml' => 'vxml', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-bittorrent' => 'torrent', + 'application/x-blorb' => array('blb', 'blorb'), + 'application/x-bzip' => 'bz', + 'application/x-cdlink' => 'vcd', + 'application/x-cfs-compressed' => 'cfs', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-conference' => 'nsc', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => array('deb', 'udeb'), + 'application/x-dgc-compressed' => 'dgc', + 'application/x-director' => array( + 'dir', + 'dcr', + 'dxr', + 'cst', + 'cct', + 'cxt', + 'w3d', + 'fgd', + 'swa' + ), + 'application/x-font-ttf' => array('ttf', 'ttc'), + 'application/x-font-type1' => array('pfa', 'pfb', 'pfm', 'afm'), + 'application/x-font-woff' => 'woff', + 'application/x-freearc' => 'arc', + 'application/x-futuresplash' => 'spl', + 'application/x-gca-compressed' => 'gca', + 'application/x-glulx' => 'ulx', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gramps-xml' => 'gramps', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-install-instructions' => 'install', + 'application/x-iso9660-image' => 'iso', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-lzh-compressed' => array('lzh', 'lha'), + 'application/x-mie' => 'mie', + 'application/x-mobipocket-ebook' => array('prc', 'mobi'), + 'application/x-ms-application' => 'application', + 'application/x-ms-shortcut' => 'lnk', + 'application/x-ms-wmd' => 'wmd', + 'application/x-ms-wmz' => 'wmz', + 'application/x-ms-xbap' => 'xbap', + 'application/x-msaccess' => 'mdb', + 'application/x-msbinder' => 'obd', + 'application/x-mscardfile' => 'crd', + 'application/x-msclip' => 'clp', + 'application/x-msdownload' => array('exe', 'dll', 'com', 'bat', 'msi'), + 'application/x-msmediaview' => array( + 'mvb', + 'm13', + 'm14' + ), + 'application/x-msmetafile' => array('wmf', 'wmz', 'emf', 'emz'), + 'application/x-rar-compressed' => 'rar', + 'application/x-research-info-systems' => 'ris', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-sql' => 'sql', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-subrip' => 'srt', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-t3vm-image' => 't3', + 'application/x-tads' => 'gam', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => array('texinfo', 'texi'), + 'application/x-tgif' => 'obj', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => array('der', 'crt'), + 'application/x-xfig' => 'fig', + 'application/x-xliff+xml' => 'xlf', + 'application/x-xpinstall' => 'xpi', + 'application/x-xz' => 'xz', + 'application/x-zmachine' => 'z1', + 'application/xaml+xml' => 'xaml', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => array('xhtml', 'xht'), + 'application/xml' => array('xml', 'xsl'), + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xproc+xml' => 'xpl', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => array('mxml', 'xhvml', 'xvml', 'xvm'), + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => array('au', 'snd'), + 'audio/midi' => array('mid', 'midi', 'kar', 'rmi'), + 'audio/mp4' => 'mp4a', + 'audio/mpeg' => array( + 'mpga', + 'mp2', + 'mp2a', + 'mp3', + 'm2a', + 'm3a' + ), + 'audio/ogg' => array('oga', 'ogg', 'spx'), + 'audio/vnd.dece.audio' => array('uva', 'uvva'), + 'audio/vnd.rip' => 'rip', + 'audio/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => array('aif', 'aiff', 'aifc'), + 'audio/x-caf' => 'caf', + 'audio/x-flac' => 'flac', + 'audio/x-matroska' => 'mka', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => array('ram', 'ra'), + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'audio/xm' => 'xm', + 'image/bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => array('jpeg', 'jpg', 'jpe'), + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/sgi' => 'sgi', + 'image/svg+xml' => array('svg', 'svgz'), + 'image/tiff' => array('tiff', 'tif'), + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => array('uvi', 'uvvi', 'uvg', 'uvvg'), + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.djvu' => array('djvu', 'djv'), + 'image/vnd.dwg' => 'dwg', + 'image/vnd.dxf' => 'dxf', + 'image/vnd.fastbidsheet' => 'fbs', + 'image/vnd.fpx' => 'fpx', + 'image/vnd.fst' => 'fst', + 'image/vnd.fujixerox.edmics-mmr' => 'mmr', + 'image/vnd.fujixerox.edmics-rlc' => 'rlc', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.ms-photo' => 'wdp', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-3ds' => '3ds', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => array('fh', 'fhc', 'fh4', 'fh5', 'fh7'), + 'image/x-icon' => 'ico', + 'image/x-mrsid-image' => 'sid', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => array('pic', 'pct'), + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-tga' => 'tga', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => array('eml', 'mime'), + 'model/iges' => array('igs', 'iges'), + 'model/mesh' => array('msh', 'mesh', 'silo'), + 'model/vnd.collada+xml' => 'dae', + 'model/vnd.dwf' => 'dwf', + 'model/vnd.gdl' => 'gdl', + 'model/vnd.gtw' => 'gtw', + 'model/vnd.mts' => 'mts', + 'model/vnd.vtu' => 'vtu', + 'model/vrml' => array('wrl', 'vrml'), + 'model/x3d+binary' => 'x3db', + 'model/x3d+vrml' => 'x3dv', + 'model/x3d+xml' => 'x3d', + 'text/cache-manifest' => 'appcache', + 'text/calendar' => array('ics', 'ifb'), + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => array('html', 'htm'), + 'text/n3' => 'n3', + 'text/plain' => array( + 'txt', + 'text', + 'conf', + 'def', + 'list', + 'log', + 'in' + ), + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/sgml' => array('sgml', 'sgm'), + 'text/tab-separated-values' => 'tsv', + 'text/troff' => array( + 't', + 'tr', + 'roff', + 'man', + 'me', + 'ms' + ), + 'text/turtle' => 'ttl', + 'text/uri-list' => array('uri', 'uris', 'urls'), + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-asm' => array('s', 'asm'), + 'text/x-fortran' => array('f', 'for', 'f77', 'f90'), + 'text/x-java-source' => 'java', + 'text/x-opml' => 'opml', + 'text/x-pascal' => array('p', 'pas'), + 'text/x-nfo' => 'nfo', + 'text/x-setext' => 'etx', + 'text/x-sfv' => 'sfv', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => array('jpm', 'jpgm'), + 'video/mj2' => 'mj2', + 'video/mp4' => 'mp4', + 'video/mpeg' => array('mpeg', 'mpg', 'mpe', 'm1v', 'm2v'), + 'video/ogg' => 'ogv', + 'video/quicktime' => array('qt', 'mov'), + 'video/vnd.dece.hd' => array('uvh', 'uvvh'), + 'video/vnd.dece.mobile' => array('uvm', 'uvvm'), + 'video/vnd.dece.pd' => array('uvp', 'uvvp'), + 'video/vnd.dece.sd' => array('uvs', 'uvvs'), + 'video/vnd.dece.video' => array('uvv', 'uvvv'), + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => array('mxu', 'm4u'), + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => array('uvu', 'uvvu'), + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-matroska' => array('mkv', 'mk3d', 'mks'), + 'video/x-mng' => 'mng', + 'video/x-ms-asf' => array('asf', 'asx'), + 'video/x-ms-vob' => 'vob', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + ); + + /** + * Get a random MIME type + * + * @return string + * @example 'video/avi' + */ + public static function mimeType() + { + return static::randomElement(array_keys(static::$mimeTypes)); + } + + /** + * Get a random file extension (without a dot) + * + * @example avi + * @return string + */ + public static function fileExtension() + { + $random_extension = static::randomElement(array_values(static::$mimeTypes)); + + return is_array($random_extension) ? static::randomElement($random_extension) : $random_extension; + } + + /** + * Copy a random file from the source directory to the target directory and returns the filename/fullpath + * + * @param string $sourceDirectory The directory to look for random file taking + * @param string $targetDirectory + * @param boolean $fullPath Whether to have the full path or just the filename + * @return string + */ + public static function file($sourceDirectory = '/tmp', $targetDirectory = '/tmp', $fullPath = true) + { + if (!is_dir($sourceDirectory)) { + throw new \InvalidArgumentException(sprintf('Source directory %s does not exist or is not a directory.', $sourceDirectory)); + } + + if (!is_dir($targetDirectory)) { + throw new \InvalidArgumentException(sprintf('Target directory %s does not exist or is not a directory.', $targetDirectory)); + } + + if ($sourceDirectory == $targetDirectory) { + throw new \InvalidArgumentException('Source and target directories must differ.'); + } + + // Drop . and .. and reset array keys + $files = array_filter( + array_values(array_diff(scandir($sourceDirectory), array('.', '..'))), + function ($file) use ($sourceDirectory) { + return is_file($sourceDirectory . DIRECTORY_SEPARATOR . $file) && is_readable($sourceDirectory . DIRECTORY_SEPARATOR . $file); + } + ); + + if (empty($files)) { + throw new \InvalidArgumentException(sprintf('Source directory %s is empty.', $sourceDirectory)); + } + + $sourceFullPath = $sourceDirectory . DIRECTORY_SEPARATOR . static::randomElement($files); + + $destinationFile = Uuid::uuid() . '.' . pathinfo($sourceFullPath, PATHINFO_EXTENSION); + $destinationFullPath = $targetDirectory . DIRECTORY_SEPARATOR . $destinationFile; + + if (copy($sourceFullPath, $destinationFullPath) === false) { + return false; + } + + return $fullPath ? $destinationFullPath : $destinationFile; + } +} diff --git a/src/Faker/Provider/HtmlLorem.php b/src/Faker/Provider/HtmlLorem.php new file mode 100644 index 0000000..1a8f8be --- /dev/null +++ b/src/Faker/Provider/HtmlLorem.php @@ -0,0 +1,276 @@ +addProvider(new Lorem($generator)); + $generator->addProvider(new Internet($generator)); + } + + /** + * @param integer $maxDepth + * @param integer $maxWidth + * + * @return string + */ + public function randomHtml($maxDepth = 4, $maxWidth = 4) + { + $document = new \DOMDocument(); + $this->idGenerator = new UniqueGenerator($this->generator); + + $head = $document->createElement("head"); + $this->addRandomTitle($head); + + $body = $document->createElement("body"); + $this->addLoginForm($body); + $this->addRandomSubTree($body, $maxDepth, $maxWidth); + + $html = $document->createElement("html"); + $html->appendChild($head); + $html->appendChild($body); + + $document->appendChild($html); + return $document->saveHTML(); + } + + private function addRandomSubTree(\DOMElement $root, $maxDepth, $maxWidth) + { + $maxDepth--; + if ($maxDepth <= 0) { + return $root; + } + + $siblings = mt_rand(1, $maxWidth); + for ($i = 0; $i < $siblings; $i++) { + if ($maxDepth == 1) { + $this->addRandomLeaf($root); + } else { + $sibling = $root->ownerDocument->createElement("div"); + $root->appendChild($sibling); + $this->addRandomAttribute($sibling); + $this->addRandomSubTree($sibling, mt_rand(0, $maxDepth), $maxWidth); + } + } + return $root; + } + + private function addRandomLeaf(\DOMElement $node) + { + $rand = mt_rand(1, 10); + switch ($rand) { + case 1: + $this->addRandomP($node); + break; + case 2: + $this->addRandomA($node); + break; + case 3: + $this->addRandomSpan($node); + break; + case 4: + $this->addRandomUL($node); + break; + case 5: + $this->addRandomH($node); + break; + case 6: + $this->addRandomB($node); + break; + case 7: + $this->addRandomI($node); + break; + case 8: + $this->addRandomTable($node); + break; + default: + $this->addRandomText($node); + break; + } + } + + private function addRandomAttribute(\DOMElement $node) + { + $rand = mt_rand(1, 2); + switch ($rand) { + case 1: + $node->setAttribute("class", $this->generator->word); + break; + case 2: + $node->setAttribute("id", (string) $this->idGenerator->randomNumber(5)); + break; + } + } + + private function addRandomP(\DOMElement $element, $maxLength = 10) + { + + $node = $element->ownerDocument->createElement(static::P_TAG); + $node->textContent = $this->generator->sentence(mt_rand(1, $maxLength)); + $element->appendChild($node); + } + + private function addRandomText(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $element->appendChild($text); + } + + private function addRandomA(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement(static::A_TAG); + $node->setAttribute("href", $this->generator->safeEmailDomain); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addRandomTitle(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement(static::TITLE_TAG); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addRandomH(\DOMElement $element, $maxLength = 10) + { + $h = static::H_TAG . (string) mt_rand(1, 3); + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement($h); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addRandomB(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement(static::B_TAG); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addRandomI(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement(static::I_TAG); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addRandomSpan(\DOMElement $element, $maxLength = 10) + { + $text = $element->ownerDocument->createTextNode($this->generator->sentence(mt_rand(1, $maxLength))); + $node = $element->ownerDocument->createElement(static::SPAN_TAG); + $node->appendChild($text); + $element->appendChild($node); + } + + private function addLoginForm(\DOMElement $element) + { + + $textInput = $element->ownerDocument->createElement(static::INPUT_TAG); + $textInput->setAttribute("type", "text"); + $textInput->setAttribute("id", "username"); + + $textLabel = $element->ownerDocument->createElement(static::LABEL_TAG); + $textLabel->setAttribute("for", "username"); + $textLabel->textContent = $this->generator->word; + + $passwordInput = $element->ownerDocument->createElement(static::INPUT_TAG); + $passwordInput->setAttribute("type", "password"); + $passwordInput->setAttribute("id", "password"); + + $passwordLabel = $element->ownerDocument->createElement(static::LABEL_TAG); + $passwordLabel->setAttribute("for", "password"); + $passwordLabel->textContent = $this->generator->word; + + $submit = $element->ownerDocument->createElement(static::INPUT_TAG); + $submit->setAttribute("type", "submit"); + $submit->setAttribute("value", $this->generator->word); + + $submit = $element->ownerDocument->createElement(static::FORM_TAG); + $submit->setAttribute("action", $this->generator->safeEmailDomain); + $submit->setAttribute("method", "POST"); + $submit->appendChild($textLabel); + $submit->appendChild($textInput); + $submit->appendChild($passwordLabel); + $submit->appendChild($passwordInput); + $element->appendChild($submit); + } + + private function addRandomTable(\DOMElement $element, $maxRows = 10, $maxCols = 6, $maxTitle = 4, $maxLength = 10) + { + $rows = mt_rand(1, $maxRows); + $cols = mt_rand(1, $maxCols); + + $table = $element->ownerDocument->createElement(static::TABLE_TAG); + $thead = $element->ownerDocument->createElement(static::THEAD_TAG); + $tbody = $element->ownerDocument->createElement(static::TBODY_TAG); + + $table->appendChild($thead); + $table->appendChild($tbody); + + $tr = $element->ownerDocument->createElement(static::TR_TAG); + $thead->appendChild($tr); + for ($i = 0; $i < $cols; $i++) { + $th = $element->ownerDocument->createElement(static::TH_TAG); + $th->textContent = $this->generator->sentence(mt_rand(1, $maxTitle)); + $tr->appendChild($th); + } + for ($i = 0; $i < $rows; $i++) { + $tr = $element->ownerDocument->createElement(static::TR_TAG); + $tbody->appendChild($tr); + for ($j = 0; $j < $cols; $j++) { + $th = $element->ownerDocument->createElement(static::TD_TAG); + $th->textContent = $this->generator->sentence(mt_rand(1, $maxLength)); + $tr->appendChild($th); + } + } + $element->appendChild($table); + } + + private function addRandomUL(\DOMElement $element, $maxItems = 11, $maxLength = 4) + { + $num = mt_rand(1, $maxItems); + $ul = $element->ownerDocument->createElement(static::UL_TAG); + for ($i = 0; $i < $num; $i++) { + $li = $element->ownerDocument->createElement(static::LI_TAG); + $li->textContent = $this->generator->sentence(mt_rand(1, $maxLength)); + $ul->appendChild($li); + } + $element->appendChild($ul); + } +} diff --git a/src/Faker/Provider/Image.php b/src/Faker/Provider/Image.php new file mode 100644 index 0000000..de152c9 --- /dev/null +++ b/src/Faker/Provider/Image.php @@ -0,0 +1,106 @@ +generator->parse($format); + } + + /** + * @example 'jdoe@example.com' + */ + final public function safeEmail() + { + return preg_replace('/\s/u', '', $this->userName() . '@' . static::safeEmailDomain()); + } + + /** + * @example 'jdoe@gmail.com' + */ + public function freeEmail() + { + return preg_replace('/\s/u', '', $this->userName() . '@' . static::freeEmailDomain()); + } + + /** + * @example 'jdoe@dawson.com' + */ + public function companyEmail() + { + return preg_replace('/\s/u', '', $this->userName() . '@' . $this->domainName()); + } + + /** + * @example 'gmail.com' + */ + public static function freeEmailDomain() + { + return static::randomElement(static::$freeEmailDomain); + } + + /** + * @example 'example.org' + */ + final public static function safeEmailDomain() + { + $domains = array( + 'example.com', + 'example.org', + 'example.net' + ); + + return static::randomElement($domains); + } + /** + * @example 'jdoe' + */ + public function userName() + { + $format = static::randomElement(static::$userNameFormats); + $username = static::bothify($this->generator->parse($format)); + + $username = strtolower(static::transliterate($username)); + + // check if transliterate() didn't support the language and removed all letters + if (trim($username, '._') === '') { + throw new \Exception('userName failed with the selected locale. Try a different locale or activate the "intl" PHP extension.'); + } + + // clean possible trailing dots from first/last names + $username = str_replace('..', '.', $username); + $username = rtrim($username, '.'); + + return $username; + } + /** + * @example 'fY4èHdZv68' + */ + public function password($minLength = 6, $maxLength = 20) + { + $pattern = str_repeat('*', $this->numberBetween($minLength, $maxLength)); + + return $this->asciify($pattern); + } + + /** + * @example 'tiramisu.com' + */ + public function domainName() + { + return $this->domainWord() . '.' . $this->tld(); + } + + /** + * @example 'faber' + */ + public function domainWord() + { + $lastName = $this->generator->format('lastName'); + + $lastName = strtolower(static::transliterate($lastName)); + + // check if transliterate() didn't support the language and removed all letters + if (trim($lastName, '._') === '') { + throw new \Exception('domainWord failed with the selected locale. Try a different locale or activate the "intl" PHP extension.'); + } + + // clean possible trailing dot from last name + $lastName = rtrim($lastName, '.'); + + return $lastName; + } + + /** + * @example 'com' + */ + public function tld() + { + return static::randomElement(static::$tld); + } + + /** + * @example 'http://www.runolfsdottir.com/' + */ + public function url() + { + $format = static::randomElement(static::$urlFormats); + + return $this->generator->parse($format); + } + + /** + * @example 'aut-repellat-commodi-vel-itaque-nihil-id-saepe-nostrum' + */ + public function slug($nbWords = 6, $variableNbWords = true) + { + if ($nbWords <= 0) { + return ''; + } + if ($variableNbWords) { + $nbWords = (int) ($nbWords * mt_rand(60, 140) / 100) + 1; + } + $words = $this->generator->words($nbWords); + + return join('-', $words); + } + + /** + * @example '237.149.115.38' + */ + public function ipv4() + { + return long2ip(mt_rand(0, 1) == 0 ? mt_rand(-2147483648, -2) : mt_rand(16777216, 2147483647)); + } + + /** + * @example '35cd:186d:3e23:2986:ef9f:5b41:42a4:e6f1' + */ + public function ipv6() + { + $res = array(); + for ($i = 0; $i < 8; $i++) { + $res [] = dechex(mt_rand(0, "65535")); + } + + return join(':', $res); + } + + /** + * @example '10.1.1.17' + */ + public static function localIpv4() + { + if (static::numberBetween(0, 1) === 0) { + // 10.x.x.x range + return long2ip(static::numberBetween(ip2long("10.0.0.0"), ip2long("10.255.255.255"))); + } + + // 192.168.x.x range + return long2ip(static::numberBetween(ip2long("192.168.0.0"), ip2long("192.168.255.255"))); + } + + /** + * @example '32:F1:39:2F:D6:18' + */ + public static function macAddress() + { + for ($i = 0; $i < 6; $i++) { + $mac[] = sprintf('%02X', static::numberBetween(0, 0xff)); + } + $mac = implode(':', $mac); + + return $mac; + } + + protected static function transliterate($string) + { + if (preg_match('/[^A-Za-z0-9_.]/', $string) === 0) { + return $string; + } + + $transId = 'Any-Latin; Latin-ASCII; NFD; [:Nonspacing Mark:] Remove; NFC;'; + if (class_exists('Transliterator', false) && $transliterator = \Transliterator::create($transId)) { + $transString = $transliterator->transliterate($string); + } else { + $transString = static::toAscii($string); + } + + return preg_replace('/[^A-Za-z0-9_.]/u', '', $transString); + } + + protected static function toAscii($string) + { + static $arrayFrom, $arrayTo; + + if (empty($arrayFrom)) { + $transliterationTable = array( + 'IJ' => 'I', 'Ö' => 'O', 'Œ' => 'O', 'Ü' => 'U', 'ä' => 'a', 'æ' => 'a', + 'ij' => 'i', 'ö' => 'o', 'œ' => 'o', 'ü' => 'u', 'ß' => 's', 'ſ' => 's', + 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', + 'Æ' => 'A', 'Ā' => 'A', 'Ą' => 'A', 'Ă' => 'A', 'Ç' => 'C', 'Ć' => 'C', + 'Č' => 'C', 'Ĉ' => 'C', 'Ċ' => 'C', 'Ď' => 'D', 'Đ' => 'D', 'È' => 'E', + 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ē' => 'E', 'Ę' => 'E', 'Ě' => 'E', + 'Ĕ' => 'E', 'Ė' => 'E', 'Ĝ' => 'G', 'Ğ' => 'G', 'Ġ' => 'G', 'Ģ' => 'G', + 'Ĥ' => 'H', 'Ħ' => 'H', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', + 'Ī' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Į' => 'I', 'İ' => 'I', 'Ĵ' => 'J', + 'Ķ' => 'K', 'Ľ' => 'K', 'Ĺ' => 'K', 'Ļ' => 'K', 'Ŀ' => 'K', 'Ł' => 'L', + 'Ñ' => 'N', 'Ń' => 'N', 'Ň' => 'N', 'Ņ' => 'N', 'Ŋ' => 'N', 'Ò' => 'O', + 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Ō' => 'O', 'Ő' => 'O', + 'Ŏ' => 'O', 'Ŕ' => 'R', 'Ř' => 'R', 'Ŗ' => 'R', 'Ś' => 'S', 'Ş' => 'S', + 'Ŝ' => 'S', 'Ș' => 'S', 'Š' => 'S', 'Ť' => 'T', 'Ţ' => 'T', 'Ŧ' => 'T', + 'Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ū' => 'U', 'Ů' => 'U', + 'Ű' => 'U', 'Ŭ' => 'U', 'Ũ' => 'U', 'Ų' => 'U', 'Ŵ' => 'W', 'Ŷ' => 'Y', + 'Ÿ' => 'Y', 'Ý' => 'Y', 'Ź' => 'Z', 'Ż' => 'Z', 'Ž' => 'Z', 'à' => 'a', + 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ā' => 'a', 'ą' => 'a', 'ă' => 'a', + 'å' => 'a', 'ç' => 'c', 'ć' => 'c', 'č' => 'c', 'ĉ' => 'c', 'ċ' => 'c', + 'ď' => 'd', 'đ' => 'd', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', + 'ē' => 'e', 'ę' => 'e', 'ě' => 'e', 'ĕ' => 'e', 'ė' => 'e', 'ƒ' => 'f', + 'ĝ' => 'g', 'ğ' => 'g', 'ġ' => 'g', 'ģ' => 'g', 'ĥ' => 'h', 'ħ' => 'h', + 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ī' => 'i', 'ĩ' => 'i', + 'ĭ' => 'i', 'į' => 'i', 'ı' => 'i', 'ĵ' => 'j', 'ķ' => 'k', 'ĸ' => 'k', + 'ł' => 'l', 'ľ' => 'l', 'ĺ' => 'l', 'ļ' => 'l', 'ŀ' => 'l', 'ñ' => 'n', + 'ń' => 'n', 'ň' => 'n', 'ņ' => 'n', 'ʼn' => 'n', 'ŋ' => 'n', 'ò' => 'o', + 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ø' => 'o', 'ō' => 'o', 'ő' => 'o', + 'ŏ' => 'o', 'ŕ' => 'r', 'ř' => 'r', 'ŗ' => 'r', 'ś' => 's', 'š' => 's', + 'ť' => 't', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ū' => 'u', 'ů' => 'u', + 'ű' => 'u', 'ŭ' => 'u', 'ũ' => 'u', 'ų' => 'u', 'ŵ' => 'w', 'ÿ' => 'y', + 'ý' => 'y', 'ŷ' => 'y', 'ż' => 'z', 'ź' => 'z', 'ž' => 'z', 'Α' => 'A', + 'Ά' => 'A', 'Ἀ' => 'A', 'Ἁ' => 'A', 'Ἂ' => 'A', 'Ἃ' => 'A', 'Ἄ' => 'A', + 'Ἅ' => 'A', 'Ἆ' => 'A', 'Ἇ' => 'A', 'ᾈ' => 'A', 'ᾉ' => 'A', 'ᾊ' => 'A', + 'ᾋ' => 'A', 'ᾌ' => 'A', 'ᾍ' => 'A', 'ᾎ' => 'A', 'ᾏ' => 'A', 'Ᾰ' => 'A', + 'Ᾱ' => 'A', 'Ὰ' => 'A', 'ᾼ' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', + 'Ε' => 'E', 'Έ' => 'E', 'Ἐ' => 'E', 'Ἑ' => 'E', 'Ἒ' => 'E', 'Ἓ' => 'E', + 'Ἔ' => 'E', 'Ἕ' => 'E', 'Ὲ' => 'E', 'Ζ' => 'Z', 'Η' => 'I', 'Ή' => 'I', + 'Ἠ' => 'I', 'Ἡ' => 'I', 'Ἢ' => 'I', 'Ἣ' => 'I', 'Ἤ' => 'I', 'Ἥ' => 'I', + 'Ἦ' => 'I', 'Ἧ' => 'I', 'ᾘ' => 'I', 'ᾙ' => 'I', 'ᾚ' => 'I', 'ᾛ' => 'I', + 'ᾜ' => 'I', 'ᾝ' => 'I', 'ᾞ' => 'I', 'ᾟ' => 'I', 'Ὴ' => 'I', 'ῌ' => 'I', + 'Θ' => 'T', 'Ι' => 'I', 'Ί' => 'I', 'Ϊ' => 'I', 'Ἰ' => 'I', 'Ἱ' => 'I', + 'Ἲ' => 'I', 'Ἳ' => 'I', 'Ἴ' => 'I', 'Ἵ' => 'I', 'Ἶ' => 'I', 'Ἷ' => 'I', + 'Ῐ' => 'I', 'Ῑ' => 'I', 'Ὶ' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', + 'Ν' => 'N', 'Ξ' => 'K', 'Ο' => 'O', 'Ό' => 'O', 'Ὀ' => 'O', 'Ὁ' => 'O', + 'Ὂ' => 'O', 'Ὃ' => 'O', 'Ὄ' => 'O', 'Ὅ' => 'O', 'Ὸ' => 'O', 'Π' => 'P', + 'Ρ' => 'R', 'Ῥ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Ύ' => 'Y', + 'Ϋ' => 'Y', 'Ὑ' => 'Y', 'Ὓ' => 'Y', 'Ὕ' => 'Y', 'Ὗ' => 'Y', 'Ῠ' => 'Y', + 'Ῡ' => 'Y', 'Ὺ' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'P', 'Ω' => 'O', + 'Ώ' => 'O', 'Ὠ' => 'O', 'Ὡ' => 'O', 'Ὢ' => 'O', 'Ὣ' => 'O', 'Ὤ' => 'O', + 'Ὥ' => 'O', 'Ὦ' => 'O', 'Ὧ' => 'O', 'ᾨ' => 'O', 'ᾩ' => 'O', 'ᾪ' => 'O', + 'ᾫ' => 'O', 'ᾬ' => 'O', 'ᾭ' => 'O', 'ᾮ' => 'O', 'ᾯ' => 'O', 'Ὼ' => 'O', + 'ῼ' => 'O', 'α' => 'a', 'ά' => 'a', 'ἀ' => 'a', 'ἁ' => 'a', 'ἂ' => 'a', + 'ἃ' => 'a', 'ἄ' => 'a', 'ἅ' => 'a', 'ἆ' => 'a', 'ἇ' => 'a', 'ᾀ' => 'a', + 'ᾁ' => 'a', 'ᾂ' => 'a', 'ᾃ' => 'a', 'ᾄ' => 'a', 'ᾅ' => 'a', 'ᾆ' => 'a', + 'ᾇ' => 'a', 'ὰ' => 'a', 'ᾰ' => 'a', 'ᾱ' => 'a', 'ᾲ' => 'a', 'ᾳ' => 'a', + 'ᾴ' => 'a', 'ᾶ' => 'a', 'ᾷ' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', + 'ε' => 'e', 'έ' => 'e', 'ἐ' => 'e', 'ἑ' => 'e', 'ἒ' => 'e', 'ἓ' => 'e', + 'ἔ' => 'e', 'ἕ' => 'e', 'ὲ' => 'e', 'ζ' => 'z', 'η' => 'i', 'ή' => 'i', + 'ἠ' => 'i', 'ἡ' => 'i', 'ἢ' => 'i', 'ἣ' => 'i', 'ἤ' => 'i', 'ἥ' => 'i', + 'ἦ' => 'i', 'ἧ' => 'i', 'ᾐ' => 'i', 'ᾑ' => 'i', 'ᾒ' => 'i', 'ᾓ' => 'i', + 'ᾔ' => 'i', 'ᾕ' => 'i', 'ᾖ' => 'i', 'ᾗ' => 'i', 'ὴ' => 'i', 'ῂ' => 'i', + 'ῃ' => 'i', 'ῄ' => 'i', 'ῆ' => 'i', 'ῇ' => 'i', 'θ' => 't', 'ι' => 'i', + 'ί' => 'i', 'ϊ' => 'i', 'ΐ' => 'i', 'ἰ' => 'i', 'ἱ' => 'i', 'ἲ' => 'i', + 'ἳ' => 'i', 'ἴ' => 'i', 'ἵ' => 'i', 'ἶ' => 'i', 'ἷ' => 'i', 'ὶ' => 'i', + 'ῐ' => 'i', 'ῑ' => 'i', 'ῒ' => 'i', 'ῖ' => 'i', 'ῗ' => 'i', 'κ' => 'k', + 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => 'k', 'ο' => 'o', 'ό' => 'o', + 'ὀ' => 'o', 'ὁ' => 'o', 'ὂ' => 'o', 'ὃ' => 'o', 'ὄ' => 'o', 'ὅ' => 'o', + 'ὸ' => 'o', 'π' => 'p', 'ρ' => 'r', 'ῤ' => 'r', 'ῥ' => 'r', 'σ' => 's', + 'ς' => 's', 'τ' => 't', 'υ' => 'y', 'ύ' => 'y', 'ϋ' => 'y', 'ΰ' => 'y', + 'ὐ' => 'y', 'ὑ' => 'y', 'ὒ' => 'y', 'ὓ' => 'y', 'ὔ' => 'y', 'ὕ' => 'y', + 'ὖ' => 'y', 'ὗ' => 'y', 'ὺ' => 'y', 'ῠ' => 'y', 'ῡ' => 'y', 'ῢ' => 'y', + 'ῦ' => 'y', 'ῧ' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'p', 'ω' => 'o', + 'ώ' => 'o', 'ὠ' => 'o', 'ὡ' => 'o', 'ὢ' => 'o', 'ὣ' => 'o', 'ὤ' => 'o', + 'ὥ' => 'o', 'ὦ' => 'o', 'ὧ' => 'o', 'ᾠ' => 'o', 'ᾡ' => 'o', 'ᾢ' => 'o', + 'ᾣ' => 'o', 'ᾤ' => 'o', 'ᾥ' => 'o', 'ᾦ' => 'o', 'ᾧ' => 'o', 'ὼ' => 'o', + 'ῲ' => 'o', 'ῳ' => 'o', 'ῴ' => 'o', 'ῶ' => 'o', 'ῷ' => 'o', 'А' => 'A', + 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'E', + 'Ж' => 'Z', 'З' => 'Z', 'И' => 'I', 'Й' => 'I', 'К' => 'K', 'Л' => 'L', + 'М' => 'M', 'Н' => 'N', 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', + 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'K', 'Ц' => 'T', 'Ч' => 'C', + 'Ш' => 'S', 'Щ' => 'S', 'Ы' => 'Y', 'Э' => 'E', 'Ю' => 'Y', 'Я' => 'Y', + 'а' => 'A', 'б' => 'B', 'в' => 'V', 'г' => 'G', 'д' => 'D', 'е' => 'E', + 'ё' => 'E', 'ж' => 'Z', 'з' => 'Z', 'и' => 'I', 'й' => 'I', 'к' => 'K', + 'л' => 'L', 'м' => 'M', 'н' => 'N', 'о' => 'O', 'п' => 'P', 'р' => 'R', + 'с' => 'S', 'т' => 'T', 'у' => 'U', 'ф' => 'F', 'х' => 'K', 'ц' => 'T', + 'ч' => 'C', 'ш' => 'S', 'щ' => 'S', 'ы' => 'Y', 'э' => 'E', 'ю' => 'Y', + 'я' => 'Y', 'ð' => 'd', 'Ð' => 'D', 'þ' => 't', 'Þ' => 'T', 'ა' => 'a', + 'ბ' => 'b', 'გ' => 'g', 'დ' => 'd', 'ე' => 'e', 'ვ' => 'v', 'ზ' => 'z', + 'თ' => 't', 'ი' => 'i', 'კ' => 'k', 'ლ' => 'l', 'მ' => 'm', 'ნ' => 'n', + 'ო' => 'o', 'პ' => 'p', 'ჟ' => 'z', 'რ' => 'r', 'ს' => 's', 'ტ' => 't', + 'უ' => 'u', 'ფ' => 'p', 'ქ' => 'k', 'ღ' => 'g', 'ყ' => 'q', 'შ' => 's', + 'ჩ' => 'c', 'ც' => 't', 'ძ' => 'd', 'წ' => 't', 'ჭ' => 'c', 'ხ' => 'k', + 'ჯ' => 'j', 'ჰ' => 'h', 'ţ' => 't', 'ʼ' => "'", '̧' => '', 'ḩ' => 'h', + '‘' => "'", '’' => "'", 'ừ' => 'u', '/' => '', 'ế' => 'e', 'ả' => 'a', + 'ị' => 'i', 'ậ' => 'a', 'ệ' => 'e', 'ỉ' => 'i', 'ồ' => 'o', 'ề' => 'e', + 'ơ' => 'o', 'ạ' => 'a', 'ẵ' => 'a', 'ư' => 'u', 'ằ' => 'a', 'ầ' => 'a', + 'ḑ' => 'd', 'Ḩ' => 'H', 'Ḑ' => 'D', 'ș' => 's', 'ț' => 't', 'ộ' => 'o', + 'ắ' => 'a', 'ş' => 's', "'" => '', 'ու' => 'u', 'ա' => 'a', 'բ' => 'b', + 'գ' => 'g', 'դ' => 'd', 'ե' => 'e', 'զ' => 'z', 'է' => 'e', 'ը' => 'y', + 'թ' => 't', 'ժ' => 'zh', 'ի' => 'i', 'լ' => 'l', 'խ' => 'kh', 'ծ' => 'ts', + 'կ' => 'k', 'հ' => 'h', 'ձ' => 'dz', 'ղ' => 'gh', 'ճ' => 'ch', 'մ' => 'm', + 'յ' => 'y', 'ն' => 'n', 'շ' => 'sh', 'ո' => 'o', 'չ' => 'ch', 'պ' => 'p', + 'ջ' => 'j', 'ռ' => 'r', 'ս' => 's', 'վ' => 'v', 'տ' => 't', 'ր' => 'r', + 'ց' => 'ts', 'փ' => 'p', 'ք' => 'q', 'և' => 'ev', 'օ' => 'o', 'ֆ' => 'f', + ); + $arrayFrom = array_keys($transliterationTable); + $arrayTo = array_values($transliterationTable); + } + + return str_replace($arrayFrom, $arrayTo, $string); + } +} diff --git a/src/Faker/Provider/Lorem.php b/src/Faker/Provider/Lorem.php new file mode 100644 index 0000000..458237f --- /dev/null +++ b/src/Faker/Provider/Lorem.php @@ -0,0 +1,204 @@ + array( + "4539###########", + "4556###########", + "4916###########", + "4532###########", + "4929###########", + "40240071#######", + "4485###########", + "4716###########", + "4##############" + ), + 'Visa Retired' => array( + "4539########", + "4556########", + "4916########", + "4532########", + "4929########", + "40240071####", + "4485########", + "4716########", + "4###########", + ), + 'MasterCard' => array( + "2221###########", + "23#############", + "24#############", + "25#############", + "26#############", + "2720###########", + "51#############", + "52#############", + "53#############", + "54#############", + "55#############" + ), + 'American Express' => array( + "34############", + "37############" + ), + 'Discover Card' => array( + "6011###########" + ), + ); + + /** + * @var array list of IBAN formats, source: @link https://www.swift.com/standards/data-standards/iban + */ + protected static $ibanFormats = array( + 'AD' => array(array('n', 4), array('n', 4), array('c', 12)), + 'AE' => array(array('n', 3), array('n', 16)), + 'AL' => array(array('n', 8), array('c', 16)), + 'AT' => array(array('n', 5), array('n', 11)), + 'AZ' => array(array('a', 4), array('c', 20)), + 'BA' => array(array('n', 3), array('n', 3), array('n', 8), array('n', 2)), + 'BE' => array(array('n', 3), array('n', 7), array('n', 2)), + 'BG' => array(array('a', 4), array('n', 4), array('n', 2), array('c', 8)), + 'BH' => array(array('a', 4), array('c', 14)), + 'BR' => array(array('n', 8), array('n', 5), array('n', 10), array('a', 1), array('c', 1)), + 'CH' => array(array('n', 5), array('c', 12)), + 'CR' => array(array('n', 4), array('n', 14)), + 'CY' => array(array('n', 3), array('n', 5), array('c', 16)), + 'CZ' => array(array('n', 4), array('n', 6), array('n', 10)), + 'DE' => array(array('n', 8), array('n', 10)), + 'DK' => array(array('n', 4), array('n', 9), array('n', 1)), + 'DO' => array(array('c', 4), array('n', 20)), + 'EE' => array(array('n', 2), array('n', 2), array('n', 11), array('n', 1)), + 'ES' => array(array('n', 4), array('n', 4), array('n', 1), array('n', 1), array('n', 10)), + 'FI' => array(array('n', 6), array('n', 7), array('n', 1)), + 'FR' => array(array('n', 5), array('n', 5), array('c', 11), array('n', 2)), + 'GB' => array(array('a', 4), array('n', 6), array('n', 8)), + 'GE' => array(array('a', 2), array('n', 16)), + 'GI' => array(array('a', 4), array('c', 15)), + 'GR' => array(array('n', 3), array('n', 4), array('c', 16)), + 'GT' => array(array('c', 4), array('c', 20)), + 'HR' => array(array('n', 7), array('n', 10)), + 'HU' => array(array('n', 3), array('n', 4), array('n', 1), array('n', 15), array('n', 1)), + 'IE' => array(array('a', 4), array('n', 6), array('n', 8)), + 'IL' => array(array('n', 3), array('n', 3), array('n', 13)), + 'IS' => array(array('n', 4), array('n', 2), array('n', 6), array('n', 10)), + 'IT' => array(array('a', 1), array('n', 5), array('n', 5), array('c', 12)), + 'KW' => array(array('a', 4), array('n', 22)), + 'KZ' => array(array('n', 3), array('c', 13)), + 'LB' => array(array('n', 4), array('c', 20)), + 'LI' => array(array('n', 5), array('c', 12)), + 'LT' => array(array('n', 5), array('n', 11)), + 'LU' => array(array('n', 3), array('c', 13)), + 'LV' => array(array('a', 4), array('c', 13)), + 'MC' => array(array('n', 5), array('n', 5), array('c', 11), array('n', 2)), + 'MD' => array(array('c', 2), array('c', 18)), + 'ME' => array(array('n', 3), array('n', 13), array('n', 2)), + 'MK' => array(array('n', 3), array('c', 10), array('n', 2)), + 'MR' => array(array('n', 5), array('n', 5), array('n', 11), array('n', 2)), + 'MT' => array(array('a', 4), array('n', 5), array('c', 18)), + 'MU' => array(array('a', 4), array('n', 2), array('n', 2), array('n', 12), array('n', 3), array('a', 3)), + 'NL' => array(array('a', 4), array('n', 10)), + 'NO' => array(array('n', 4), array('n', 6), array('n', 1)), + 'PK' => array(array('a', 4), array('c', 16)), + 'PL' => array(array('n', 8), array('n', 16)), + 'PS' => array(array('a', 4), array('c', 21)), + 'PT' => array(array('n', 4), array('n', 4), array('n', 11), array('n', 2)), + 'RO' => array(array('a', 4), array('c', 16)), + 'RS' => array(array('n', 3), array('n', 13), array('n', 2)), + 'SA' => array(array('n', 2), array('c', 18)), + 'SE' => array(array('n', 3), array('n', 16), array('n', 1)), + 'SI' => array(array('n', 5), array('n', 8), array('n', 2)), + 'SK' => array(array('n', 4), array('n', 6), array('n', 10)), + 'SM' => array(array('a', 1), array('n', 5), array('n', 5), array('c', 12)), + 'TN' => array(array('n', 2), array('n', 3), array('n', 13), array('n', 2)), + 'TR' => array(array('n', 5), array('n', 1), array('c', 16)), + 'VG' => array(array('a', 4), array('n', 16)), + ); + + /** + * @return string Returns a credit card vendor name + * + * @example 'MasterCard' + */ + public static function creditCardType() + { + return static::randomElement(static::$cardVendors); + } + + /** + * Returns the String of a credit card number. + * + * @param string $type Supporting any of 'Visa', 'MasterCard', 'American Express', and 'Discover' + * @param boolean $formatted Set to true if the output string should contain one separator every 4 digits + * @param string $separator Separator string for formatting card number. Defaults to dash (-). + * @return string + * + * @example '4485480221084675' + */ + public static function creditCardNumber($type = null, $formatted = false, $separator = '-') + { + if (is_null($type)) { + $type = static::creditCardType(); + } + $mask = static::randomElement(static::$cardParams[$type]); + + $number = static::numerify($mask); + $number .= Luhn::computeCheckDigit($number); + + if ($formatted) { + $p1 = substr($number, 0, 4); + $p2 = substr($number, 4, 4); + $p3 = substr($number, 8, 4); + $p4 = substr($number, 12); + $number = $p1 . $separator . $p2 . $separator . $p3 . $separator . $p4; + } + + return $number; + } + + /** + * @param boolean $valid True (by default) to get a valid expiration date, false to get a maybe valid date + * @return \DateTime + * @example 04/13 + */ + public function creditCardExpirationDate($valid = true) + { + if ($valid) { + return $this->generator->dateTimeBetween('now', '36 months'); + } + + return $this->generator->dateTimeBetween('-36 months', '36 months'); + } + + /** + * @param boolean $valid True (by default) to get a valid expiration date, false to get a maybe valid date + * @param string $expirationDateFormat + * @return string + * @example '04/13' + */ + public function creditCardExpirationDateString($valid = true, $expirationDateFormat = null) + { + return $this->creditCardExpirationDate($valid)->format(is_null($expirationDateFormat) ? static::$expirationDateFormat : $expirationDateFormat); + } + + /** + * @param boolean $valid True (by default) to get a valid expiration date, false to get a maybe valid date + * @return array + */ + public function creditCardDetails($valid = true) + { + $type = static::creditCardType(); + + return array( + 'type' => $type, + 'number' => static::creditCardNumber($type), + 'name' => $this->generator->name(), + 'expirationDate' => $this->creditCardExpirationDateString($valid) + ); + } + + /** + * International Bank Account Number (IBAN) + * + * @link http://en.wikipedia.org/wiki/International_Bank_Account_Number + * @param string $countryCode ISO 3166-1 alpha-2 country code + * @param string $prefix for generating bank account number of a specific bank + * @param integer $length total length without country code and 2 check digits + * @return string + */ + public static function iban($countryCode = null, $prefix = '', $length = null) + { + $countryCode = is_null($countryCode) ? self::randomKey(self::$ibanFormats) : strtoupper($countryCode); + + $format = !isset(static::$ibanFormats[$countryCode]) ? null : static::$ibanFormats[$countryCode]; + if ($length === null) { + if ($format === null) { + $length = 24; + } else { + $length = 0; + foreach ($format as $part) { + list($class, $groupCount) = $part; + $length += $groupCount; + } + } + } + if ($format === null) { + $format = array(array('n', $length)); + } + + $expandedFormat = ''; + foreach ($format as $item) { + list($class, $length) = $item; + $expandedFormat .= str_repeat($class, $length); + } + + $result = $prefix; + $expandedFormat = substr($expandedFormat, strlen($result)); + foreach (str_split($expandedFormat) as $class) { + switch ($class) { + default: + case 'c': + $result .= mt_rand(0, 100) <= 50 ? static::randomDigit() : strtoupper(static::randomLetter()); + break; + case 'a': + $result .= strtoupper(static::randomLetter()); + break; + case 'n': + $result .= static::randomDigit(); + break; + } + } + + $checksum = Iban::checksum($countryCode . '00' . $result); + + return $countryCode . $checksum . $result; + } + + /** + * Return the String of a SWIFT/BIC number + * + * @example 'RZTIAT22263' + * @link http://en.wikipedia.org/wiki/ISO_9362 + * @return string Swift/Bic number + */ + public static function swiftBicNumber() + { + return self::regexify("^([A-Z]){4}([A-Z]){2}([0-9A-Z]){2}([0-9A-Z]{3})?$"); + } +} diff --git a/src/Faker/Provider/Person.php b/src/Faker/Provider/Person.php new file mode 100644 index 0000000..91dbcc3 --- /dev/null +++ b/src/Faker/Provider/Person.php @@ -0,0 +1,130 @@ +generator->parse($format); + } + + /** + * @param string|null $gender 'male', 'female' or null for any + * @return string + * @example 'John' + */ + public function firstName(string $gender = null): string + { + if ($gender === static::GENDER_MALE) { + return static::firstNameMale(); + } + + if ($gender === static::GENDER_FEMALE) { + return static::firstNameFemale(); + } + + return $this->generator->parse(static::randomElement(static::$firstNameFormat)); + } + + public static function firstNameMale() + { + return static::randomElement(static::$firstNameMale); + } + + public static function firstNameFemale() + { + return static::randomElement(static::$firstNameFemale); + } + + /** + * @example 'Doe' + * @return string + */ + public function lastName(): string + { + return static::randomElement(static::$lastName); + } + + /** + * @param string|null $gender 'male', 'female' or null for any + * @return string + *@example 'Mrs.' + */ + public function title(string $gender = null): string + { + if ($gender === static::GENDER_MALE) { + return static::titleMale(); + } + + if ($gender === static::GENDER_FEMALE) { + return static::titleFemale(); + } + + return $this->generator->parse(static::randomElement(static::$titleFormat)); + } + + /** + * @example 'Mr.' + */ + public static function titleMale() + { + return static::randomElement(static::$titleMale); + } + + /** + * @example 'Mrs.' + */ + public static function titleFemale() + { + return static::randomElement(static::$titleFemale); + } +} diff --git a/src/Faker/Provider/PhoneNumber.php b/src/Faker/Provider/PhoneNumber.php new file mode 100644 index 0000000..d80171a --- /dev/null +++ b/src/Faker/Provider/PhoneNumber.php @@ -0,0 +1,44 @@ +generator->parse(static::randomElement(static::$formats))); + } + + /** + * @example +27113456789 + * @return string + */ + public function e164PhoneNumber() + { + $formats = array('+%############'); + return static::numerify($this->generator->parse(static::randomElement($formats))); + } + + /** + * International Mobile Equipment Identity (IMEI) + * + * @link http://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity + * @link http://imei-number.com/imei-validation-check/ + * @example '720084494799532' + * @return int $imei + */ + public function imei() + { + $imei = (string) static::numerify('##############'); + $imei .= Luhn::computeCheckDigit($imei); + return $imei; + } +} diff --git a/src/Faker/Provider/Text.php b/src/Faker/Provider/Text.php new file mode 100644 index 0000000..9a1d28e --- /dev/null +++ b/src/Faker/Provider/Text.php @@ -0,0 +1,147 @@ + 5) { + throw new \InvalidArgumentException('indexSize must be at most 5'); + } + + $words = $this->getConsecutiveWords($indexSize); + $result = array(); + $resultLength = 0; + // take a random starting point + $next = static::randomKey($words); + while ($resultLength < $maxNbChars && isset($words[$next])) { + // fetch a random word to append + $word = static::randomElement($words[$next]); + + // calculate next index + $currentWords = static::explode($next); + $currentWords[] = $word; + array_shift($currentWords); + $next = static::implode($currentWords); + + // ensure text starts with an uppercase letter + if ($resultLength == 0 && !static::validStart($word)) { + continue; + } + + // append the element + $result[] = $word; + $resultLength += static::strlen($word) + static::$separatorLen; + } + + // remove the element that caused the text to overflow + array_pop($result); + + // build result + $result = static::implode($result); + + return static::appendEnd($result); + } + + protected function getConsecutiveWords($indexSize) + { + if (!isset($this->consecutiveWords[$indexSize])) { + $parts = $this->getExplodedText(); + $words = array(); + $index = array(); + for ($i = 0; $i < $indexSize; $i++) { + $index[] = array_shift($parts); + } + + for ($i = 0, $count = count($parts); $i < $count; $i++) { + $stringIndex = static::implode($index); + if (!isset($words[$stringIndex])) { + $words[$stringIndex] = array(); + } + $word = $parts[$i]; + $words[$stringIndex][] = $word; + array_shift($index); + $index[] = $word; + } + // cache look up words for performance + $this->consecutiveWords[$indexSize] = $words; + } + + return $this->consecutiveWords[$indexSize]; + } + + protected function getExplodedText() + { + if ($this->explodedText === null) { + $this->explodedText = static::explode(preg_replace('/\s+/u', ' ', static::$baseText)); + } + + return $this->explodedText; + } + + protected static function explode($text) + { + return explode(static::$separator, $text); + } + + protected static function implode($words) + { + return implode(static::$separator, $words); + } + + protected static function strlen($text) + { + return function_exists('mb_strlen') ? mb_strlen($text, 'UTF-8') : strlen($text); + } + + protected static function validStart($word) + { + $isValid = true; + if (static::$textStartsWithUppercase) { + $isValid = preg_match('/^\p{Lu}/u', $word); + } + return $isValid; + } + + protected static function appendEnd($text) + { + return preg_replace("/([ ,-:;\x{2013}\x{2014}]+$)/us", '', $text) . '.'; + } +} diff --git a/src/Faker/Provider/UserAgent.php b/src/Faker/Provider/UserAgent.php new file mode 100644 index 0000000..1783f8e --- /dev/null +++ b/src/Faker/Provider/UserAgent.php @@ -0,0 +1,166 @@ +> 8) | (($tLo & 0xff000000) >> 24); + $tMi = (($tMi & 0x00ff) << 8) | (($tMi & 0xff00) >> 8); + $tHi = (($tHi & 0x00ff) << 8) | (($tHi & 0xff00) >> 8); + } + + // apply version number + $tHi &= 0x0fff; + $tHi |= (3 << 12); + + // cast to string + return sprintf( + '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x', + $tLo, + $tMi, + $tHi, + $csHi, + $csLo, + $byte[10], + $byte[11], + $byte[12], + $byte[13], + $byte[14], + $byte[15] + ); + } +} diff --git a/src/Faker/Provider/en_US/Address.php b/src/Faker/Provider/en_US/Address.php new file mode 100644 index 0000000..15683bc --- /dev/null +++ b/src/Faker/Provider/en_US/Address.php @@ -0,0 +1,110 @@ +generator->parse($format)); + } + + /** + * NPA-format area code + * + * @see https://en.wikipedia.org/wiki/North_American_Numbering_Plan#Numbering_system + * + * @return string + */ + public static function areaCode() + { + $digits = []; + $digits[] = self::numberBetween(2, 9); + $digits[] = self::randomDigit(); + $digits[] = self::randomDigitNot($digits[1]); + + return join('', $digits); + } + + /** + * NXX-format central office exchange code + * + * @see https://en.wikipedia.org/wiki/North_American_Numbering_Plan#Numbering_system + * + * @return string + */ + public static function exchangeCode() + { + $digits = []; + $digits[] = self::numberBetween(2, 9); + $digits[] = self::randomDigit(); + + if ($digits[1] === 1) { + $digits[] = self::randomDigitNot(1); + } else { + $digits[] = self::randomDigit(); + } + + return implode('', $digits); + } +} diff --git a/src/Faker/Provider/en_US/Text.php b/src/Faker/Provider/en_US/Text.php new file mode 100644 index 0000000..624564c --- /dev/null +++ b/src/Faker/Provider/en_US/Text.php @@ -0,0 +1,3721 @@ +unique() + */ +class UniqueGenerator +{ + + protected Generator $generator; + + protected int $maxRetries; + + protected array $uniques = []; + + /** + * @param Generator $generator + * @param integer $maxRetries + */ + public function __construct(Generator $generator, int $maxRetries = 10000) + { + $this->generator = $generator; + $this->maxRetries = $maxRetries; + } + + /** + * Catch and proxy all generator calls but return only unique values + * @param string $attribute + * @return mixed + */ + public function __get(string $attribute) + { + return $this->__call($attribute, array()); + } + + public function __isset($name) + { + } + + public function __set(string $key, $value) + { + throw new \RuntimeException('Faker is immutable'); + } + + /** + * Catch and proxy all generator calls with arguments but return only unique values + * @param string $name + * @param array $arguments + * @return mixed + */ + public function __call(string $name, array $arguments) + { + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } + $i = 0; + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; + if ($i > $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); + } + } while (array_key_exists(serialize($res), $this->uniques[$name])); + $this->uniques[$name][serialize($res)] = null; + + return $res; + } +} diff --git a/src/Faker/ValidGenerator.php b/src/Faker/ValidGenerator.php new file mode 100644 index 0000000..24633b9 --- /dev/null +++ b/src/Faker/ValidGenerator.php @@ -0,0 +1,77 @@ +valid() + */ +class ValidGenerator +{ + + protected Generator $generator; + + protected $validator; + + protected int $maxRetries; + + /** + * @param Generator $generator + * @param callable|null $validator + * @param integer $maxRetries + */ + public function __construct(Generator $generator, callable $validator = null, int $maxRetries = 10000) + { + if (is_null($validator)) { + $validator = static function () { + return true; + }; + } elseif (!is_callable($validator)) { + throw new \InvalidArgumentException('valid() only accepts callables as first argument'); + } + $this->generator = $generator; + $this->validator = $validator; + $this->maxRetries = $maxRetries; + } + + /** + * Catch and proxy all generator calls but return only valid values + * @param string $attribute + * + * @return mixed + */ + public function __get(string $attribute) + { + return $this->__call($attribute, array()); + } + + public function __isset($name) + { + } + + public function __set($name, $value) + { + throw new \RuntimeException('Faker is immutable'); + } + + /** + * Catch and proxy all generator calls with arguments but return only valid values + * @param string $name + * @param array $arguments + * + * @return mixed + */ + public function __call(string $name, array $arguments) + { + $i = 0; + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; + if ($i > $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries)); + } + } while (!call_user_func($this->validator, $res)); + + return $res; + } +}