├── .gitignore ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── php.yml ├── src ├── Exceptions │ └── InvalidRutException.php ├── HasCallbacks.php ├── HasHelpers.php ├── SerializesToJson.php ├── HasCase.php ├── BuildsGenerator.php ├── HasFormats.php ├── RutHelper.php ├── RutGenerator.php └── Rut.php ├── helpers └── helpers.php ├── tests ├── HelpersTest.php ├── RutSerializesToJsonTest.php ├── RutHasHelpersTest.php ├── RutHasFormatsTest.php ├── RutHasCaseTest.php ├── RutCallbacksTest.php ├── RutGeneratorTest.php ├── RutTest.php └── RutHelperTest.php ├── phpunit.xml ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /build 4 | .DS_Store 5 | composer.lock -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Help me support this package 2 | 3 | ko_fi: DarkGhostHunter 4 | custom: ['https://paypal.me/darkghosthunter'] 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "09:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidRutException.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | src 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "darkghosthunter/rut-utils", 3 | "description": "A complete library for handling chilean RUTs and RUNs.", 4 | "license": "MIT", 5 | "minimum-stability": "dev", 6 | "prefer-stable": true, 7 | "authors": [ 8 | { 9 | "name": "Italo Baeza C.", 10 | "email": "darkghosthunter@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.4||^8.0", 15 | "ext-json": "*" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^9.5.3", 19 | "mockery/mockery": "^1.4.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "DarkGhostHunter\\RutUtils\\": "src" 24 | }, 25 | "files": [ 26 | "helpers/helpers.php" 27 | ] 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Tests\\": "tests/" 32 | } 33 | }, 34 | "scripts": { 35 | "test": "vendor/bin/phpunit --coverage-clover build/logs/clover.xml", 36 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Italo Israel Baeza Cabrera] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/RutSerializesToJsonTest.php: -------------------------------------------------------------------------------- 1 | shouldJsonAsArray()); 15 | static::assertEquals('"18.300.252-K"', $rut->toJson()); 16 | } 17 | 18 | public function testSerializesAllJson() 19 | { 20 | Rut::allJsonAsString(); 21 | 22 | $rut = new Rut(18300252, 'k'); 23 | 24 | Rut::allJsonAsArray(); 25 | 26 | static::assertJson($rut->toJson()); 27 | static::assertEquals('{"num":18300252,"vd":"K"}', $rut->toJson()); 28 | 29 | Rut::allJsonAsString(); 30 | 31 | static::assertJson($rut->toJson()); 32 | static::assertEquals('"18.300.252-K"', $rut->toJson()); 33 | } 34 | 35 | public function testSerializesJson() 36 | { 37 | Rut::allJsonAsString(); 38 | 39 | $rut = new Rut(18300252, 'k'); 40 | 41 | $rut->jsonAsArray(); 42 | 43 | static::assertJson($rut->toJson()); 44 | static::assertEquals('{"num":18300252,"vd":"K"}', $rut->toJson()); 45 | 46 | $rut->jsonAsString(); 47 | 48 | static::assertJson($rut->toJson()); 49 | static::assertEquals('"18.300.252-K"', $rut->toJson()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/HasCallbacks.php: -------------------------------------------------------------------------------- 1 | isValid(); 25 | } 26 | 27 | /** 28 | * Return if the RUT is for a Person 29 | * 30 | * @return bool 31 | */ 32 | public function isPerson(): bool 33 | { 34 | return RutHelper::isPerson($this); 35 | } 36 | 37 | /** 38 | * Return if the RUT is for a Company 39 | * 40 | * @return bool 41 | */ 42 | public function isCompany(): bool 43 | { 44 | return !$this->isPerson(); 45 | } 46 | 47 | /** 48 | * Return if the RUT is equal to another RUT 49 | * 50 | * @param mixed ...$ruts 51 | * @return bool 52 | */ 53 | public function isEqual(...$ruts): bool 54 | { 55 | $ruts = array_filter(array_merge(RutHelper::unpack($ruts), [$this])); 56 | 57 | // Bail if after filtering RUTs we end up with only this instance. 58 | if (count($ruts) < 2) { 59 | return false; 60 | } 61 | 62 | return RutHelper::isEqual($ruts); 63 | } 64 | } -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | php: [8.0, 7.4] 15 | dependency-version: [prefer-lowest, prefer-stable] 16 | 17 | name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Setup PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: ${{ matrix.php }} 27 | extensions: mbstring, intl 28 | coverage: xdebug 29 | 30 | - name: Cache dependencies 31 | uses: actions/cache@v2 32 | with: 33 | path: ~/.composer/cache/files 34 | key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 35 | restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer- 36 | 37 | - name: Install dependencies 38 | run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest 39 | 40 | - name: Run Tests 41 | run: composer run-script test 42 | 43 | - name: Upload Coverage to Coveralls 44 | env: 45 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | COVERALLS_SERVICE_NAME: github 47 | run: | 48 | rm -rf composer.* vendor/ 49 | composer require php-coveralls/php-coveralls 50 | vendor/bin/php-coveralls 51 | -------------------------------------------------------------------------------- /tests/RutHasHelpersTest.php: -------------------------------------------------------------------------------- 1 | isValid()); 14 | static::assertFalse($rut->isInvalid()); 15 | } 16 | 17 | public function testIsInvalid() 18 | { 19 | $rut = new Rut(0, 'bar'); 20 | static::assertFalse($rut->isValid()); 21 | static::assertTrue($rut->isInvalid()); 22 | 23 | $rut = new Rut(1000, 0); 24 | static::assertFalse($rut->isValid()); 25 | static::assertTrue($rut->isInvalid()); 26 | } 27 | 28 | public function testIsPersonOrCompany() 29 | { 30 | $rut = new Rut(18300252, 'k'); 31 | 32 | static::assertTrue($rut->isPerson()); 33 | static::assertFalse($rut->isCompany()); 34 | 35 | $rut = new Rut(50000000, '7'); 36 | 37 | static::assertFalse($rut->isPerson()); 38 | static::assertTrue($rut->isCompany()); 39 | } 40 | 41 | public function testIsEqualTo() 42 | { 43 | $rut = new Rut(18300252, 'k'); 44 | 45 | static::assertTrue($rut->isEqual('18300252K')); 46 | static::assertTrue($rut->isEqual(new Rut(18300252, 'K'))); 47 | static::assertTrue($rut->isEqual(new Rut(18300252, 'k')), '18300252K'); 48 | static::assertTrue($rut->isEqual([new Rut(18300252, 'K'), '18300252K', [18300252, 'K']])); 49 | 50 | static::assertFalse($rut->isEqual(null)); 51 | static::assertFalse($rut->isEqual(new Rut(1000, 0))); 52 | static::assertFalse($rut->isEqual(new Rut(1000, 0), '18300252k')); 53 | static::assertFalse($rut->isEqual([new Rut(18300252, 'K'), '18300252K', [null, 'K']])); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/SerializesToJson.php: -------------------------------------------------------------------------------- 1 | jsonAsArray = true; 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Forces this instance to transform as a JSON single string. 55 | * 56 | * @return $this 57 | */ 58 | public function jsonAsString(): Rut 59 | { 60 | $this->jsonAsArray = false; 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * If the instance should be casted as a single JSON string or multiple 67 | * 68 | * @return null|bool 69 | */ 70 | public function shouldJsonAsArray(): ?bool 71 | { 72 | return $this->jsonAsArray ?? static::$globalJsonFromArray; 73 | } 74 | 75 | /** 76 | * Specify data which should be serialized to JSON 77 | * 78 | * @return string|array 79 | */ 80 | public function jsonSerialize() 81 | { 82 | return $this->shouldJsonAsArray() ? $this->toArray() : (string)$this; 83 | } 84 | } -------------------------------------------------------------------------------- /tests/RutHasFormatsTest.php: -------------------------------------------------------------------------------- 1 | getFormat()); 17 | } 18 | 19 | public function testChangesGlobalFormats() 20 | { 21 | $rut = new Rut(18300252, 'k'); 22 | 23 | static::assertEquals('strict', Rut::getGlobalFormat()); 24 | static::assertEquals('18.300.252-K', (string)$rut); 25 | 26 | Rut::allFormatRaw(); 27 | static::assertEquals('raw', Rut::getGlobalFormat()); 28 | static::assertEquals('18300252K', (string)$rut); 29 | 30 | Rut::allFormatBasic(); 31 | static::assertEquals('basic', Rut::getGlobalFormat()); 32 | static::assertEquals('18300252-K', (string)$rut); 33 | 34 | Rut::allFormatStrict(); 35 | static::assertEquals('strict', Rut::getGlobalFormat()); 36 | static::assertEquals('18.300.252-K', (string)$rut); 37 | } 38 | 39 | public function testChangesInstanceFormat() 40 | { 41 | $rut = new Rut(18300252, 'k'); 42 | static::assertEquals('strict', $rut->getFormat()); 43 | 44 | $rut->setFormat('raw'); 45 | static::assertEquals('raw', $rut->getFormat()); 46 | static::assertEquals('18300252K', (string)$rut); 47 | static::assertEquals('18300252K', $rut->toFormattedString()); 48 | 49 | $rut->setFormat('basic'); 50 | static::assertEquals('basic', $rut->getFormat()); 51 | static::assertEquals('18300252-K', (string)$rut); 52 | static::assertEquals('18300252-K', $rut->toFormattedString()); 53 | 54 | $rut->setFormat('strict'); 55 | static::assertEquals('strict', $rut->getFormat()); 56 | static::assertEquals('18.300.252-K', (string)$rut); 57 | static::assertEquals('18.300.252-K', $rut->toFormattedString()); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/HasCase.php: -------------------------------------------------------------------------------- 1 | uppercase ?? self::$globalUppercase; 59 | } 60 | 61 | /** 62 | * Case the verification digit to uppercase or lowercase 63 | * 64 | * @param string $vd 65 | * @return string 66 | */ 67 | protected function case(string $vd): string 68 | { 69 | return $this->shouldUppercase() ? strtoupper($vd) : strtolower($vd); 70 | } 71 | 72 | /** 73 | * Set all RUT to use lowercase `K` 74 | * 75 | * @return static 76 | */ 77 | public function lowercase(): Rut 78 | { 79 | $this->uppercase = false; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Set all RUT to use uppercase `K` 86 | * 87 | * @return static 88 | */ 89 | public function uppercase(): Rut 90 | { 91 | $this->uppercase = true; 92 | 93 | return $this; 94 | } 95 | } -------------------------------------------------------------------------------- /tests/RutHasCaseTest.php: -------------------------------------------------------------------------------- 1 | vd); 24 | static::assertEquals('K', $rutA['vd']); 25 | static::assertEquals('K', $rutB->vd); 26 | static::assertEquals('K', $rutB['vd']); 27 | 28 | static::assertEquals('15.518.258-K', (string)$rutA); 29 | static::assertEquals('21.644.289-K', (string)$rutB); 30 | } 31 | 32 | public function testGlobalLowercase() 33 | { 34 | $rutA = new Rut('15518258', 'K'); 35 | 36 | Rut::allLowercase(); 37 | 38 | $rutB = new Rut('21644289', 'K'); 39 | 40 | static::assertEquals('k', $rutA->vd); 41 | static::assertEquals('k', $rutA['vd']); 42 | static::assertEquals('k', $rutB->vd); 43 | static::assertEquals('k', $rutB['vd']); 44 | 45 | static::assertEquals('15.518.258-k', (string)$rutA); 46 | static::assertEquals('21.644.289-k', (string)$rutB); 47 | } 48 | 49 | public function testInstanceUppercase() 50 | { 51 | $rutA = new Rut('15518258', 'k'); 52 | 53 | Rut::allLowercase(); 54 | 55 | $rutB = new Rut('21644289', 'k'); 56 | 57 | $rutA->uppercase(); 58 | $rutB->uppercase(); 59 | 60 | static::assertEquals('K', $rutA->vd); 61 | static::assertEquals('K', $rutA['vd']); 62 | static::assertEquals('K', $rutB->vd); 63 | static::assertEquals('K', $rutB['vd']); 64 | 65 | static::assertEquals('15.518.258-K', (string)$rutA); 66 | static::assertEquals('21.644.289-K', (string)$rutB); 67 | } 68 | 69 | public function testInstanceLowercase() 70 | { 71 | $rutA = new Rut('15518258', 'K'); 72 | 73 | Rut::allUppercase(); 74 | 75 | $rutB = new Rut('21644289', 'K'); 76 | 77 | $rutA->lowercase(); 78 | $rutB->lowercase(); 79 | 80 | static::assertEquals('k', $rutA->vd); 81 | static::assertEquals('k', $rutA['vd']); 82 | static::assertEquals('k', $rutB->vd); 83 | static::assertEquals('k', $rutB['vd']); 84 | 85 | static::assertEquals('15.518.258-k', (string)$rutA); 86 | static::assertEquals('21.644.289-k', (string)$rutB); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/BuildsGenerator.php: -------------------------------------------------------------------------------- 1 | unique = true; 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * Generate RUTs that may be duplicated 42 | * 43 | * @return static 44 | */ 45 | public function withDuplicates(): RutGenerator 46 | { 47 | $this->unique = false; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Return companies RUTs 54 | * 55 | * @return static 56 | */ 57 | public function asCompany(): RutGenerator 58 | { 59 | $this->person = false; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Return Persons RUTs 66 | * 67 | * @return static 68 | */ 69 | public function asPerson(): RutGenerator 70 | { 71 | $this->person = true; 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Return RUTs as strict strings 78 | * 79 | * @return static 80 | * @example '22.605.071-K' 81 | */ 82 | public function asStrict(): RutGenerator 83 | { 84 | $this->output = Rut::FORMAT_STRICT; 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * Return RUTs as basic strings 91 | * 92 | * @return static 93 | * @example '22605071-K' 94 | */ 95 | public function asBasic(): RutGenerator 96 | { 97 | $this->output = Rut::FORMAT_BASIC; 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Return RUTs as raw strings 104 | * 105 | * @return static 106 | * @example '22605071K' 107 | */ 108 | public function asRaw(): RutGenerator 109 | { 110 | $this->output = Rut::FORMAT_RAW; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Return RUTs as Rut object instances 117 | * 118 | * @return static 119 | */ 120 | public function asObject(): RutGenerator 121 | { 122 | $this->output = 'object'; 123 | 124 | return $this; 125 | } 126 | } -------------------------------------------------------------------------------- /tests/RutCallbacksTest.php: -------------------------------------------------------------------------------- 1 | format ?? static::$globalFormat; 70 | } 71 | 72 | /** 73 | * Sets the format to use for the current Rut instance. 74 | * 75 | * @param string $format 76 | */ 77 | public function setFormat(string $format): void 78 | { 79 | $this->format = $format; 80 | } 81 | 82 | /** 83 | * Returns the RUT as an strictly formatted string 84 | * 85 | * @return string 86 | * @example 18.765.432-1 87 | */ 88 | public function toStrictString(): string 89 | { 90 | $num = number_format((int)$this->rut['num'], 0, ',', '.'); 91 | $vd = $this->shouldUppercase() ? strtoupper($this->rut['vd']) : strtolower($this->rut['vd']); 92 | 93 | return $num . '-' .$vd; 94 | } 95 | 96 | /** 97 | * Returns the RUT as a basic formatted string 98 | * 99 | * @return string 100 | * @example 18765432-1 101 | */ 102 | public function toBasicString(): string 103 | { 104 | $vd = $this->shouldUppercase() ? strtoupper($this->rut['vd']) : strtolower($this->rut['vd']); 105 | 106 | return (int)$this->rut['num'] . '-' . $vd; 107 | } 108 | 109 | /** 110 | * Returns the RUT as a raw formatted string 111 | * 112 | * @return string 113 | * @example 187654321 114 | */ 115 | public function toRawString(): string 116 | { 117 | $vd = $this->shouldUppercase() ? strtoupper($this->rut['vd']) : strtolower($this->rut['vd']); 118 | 119 | return (int)$this->rut['num'] . $vd; 120 | } 121 | 122 | /** 123 | * Returns a formatted RUT string 124 | * 125 | * @return string 126 | */ 127 | public function toFormattedString(): string 128 | { 129 | switch ($this->format ?? static::$globalFormat) { 130 | case static::FORMAT_STRICT: 131 | return $this->toStrictString(); 132 | case static::FORMAT_BASIC: 133 | return $this->toBasicString(); 134 | case static::FORMAT_RAW: 135 | default: 136 | return $this->toRawString(); 137 | } 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /tests/RutGeneratorTest.php: -------------------------------------------------------------------------------- 1 | generator = new RutGenerator(); 17 | } 18 | 19 | public function testMake() 20 | { 21 | static::assertInstanceOf(RutGenerator::class, RutGenerator::make()); 22 | } 23 | 24 | public function testGenerateOne() 25 | { 26 | $rut = $this->generator->generate(1); 27 | 28 | static::assertInstanceOf(Rut::class, $rut); 29 | static::assertIsInt($rut->num); 30 | static::assertIsString($rut->vd); 31 | } 32 | 33 | public function testGenerateOneInArray() 34 | { 35 | $rut = $this->generator->generate(1, false); 36 | 37 | static::assertIsArray($rut); 38 | static::assertInstanceOf(Rut::class, $rut[0]); 39 | static::assertIsInt($rut[0]->num); 40 | static::assertIsString($rut[0]->vd); 41 | } 42 | 43 | public function testGenerateMany() 44 | { 45 | $ruts = $this->generator->generate($rand = rand(10, 20)); 46 | 47 | static::assertIsArray($ruts); 48 | static::assertCount($rand, $ruts); 49 | 50 | foreach ($ruts as $rut) { 51 | static::assertInstanceOf(Rut::class, $rut); 52 | } 53 | } 54 | 55 | public function testGenerateStatic() 56 | { 57 | $generator = new class extends RutGenerator { 58 | public static $runs = 0; 59 | public static $results = [0,1,1,2,3,3,4,4,4,5,6,7,8,9]; 60 | 61 | public function generate(int $iterations = 1, bool $unwrapSingle = true) 62 | { 63 | $result = static::$results[static::$runs]; 64 | 65 | ++static::$runs; 66 | 67 | return $result; 68 | } 69 | 70 | public function getStatic() 71 | { 72 | return static::$static; 73 | } 74 | }; 75 | 76 | $results = []; 77 | 78 | for ($i = 0; $i < 10; ++$i) { 79 | $results[] = $generator->generateStatic(); 80 | } 81 | 82 | static::assertEquals([0,1,2,3,4,5,6,7,8,9], $results); 83 | static::assertEquals([0,1,2,3,4,5,6,7,8,9], $generator->getStatic()); 84 | 85 | $generator->flushStatic(); 86 | 87 | static::assertEmpty($generator->getStatic()); 88 | } 89 | 90 | public function testAsCompany() 91 | { 92 | $ruts = $this->generator->asCompany()->generate($rand = rand(10, 20)); 93 | 94 | static::assertIsArray($ruts); 95 | static::assertCount($rand, $ruts); 96 | 97 | foreach ($ruts as $rut) { 98 | static::assertTrue($rut->num >= Rut::COMPANY_RUT_BASE && $rut->num < RutGenerator::MAXIMUM_NUMBER); 99 | } 100 | } 101 | 102 | public function testAsPerson() 103 | { 104 | $ruts = $this->generator->asPerson()->generate($rand = rand(10, 20)); 105 | 106 | static::assertIsArray($ruts); 107 | static::assertCount($rand, $ruts); 108 | 109 | foreach ($ruts as $rut) { 110 | static::assertTrue($rut->num >= RutGenerator::MINIMUM_NUMBER && $rut->num < Rut::COMPANY_RUT_BASE); 111 | } 112 | } 113 | 114 | public function testAsRutObject() 115 | { 116 | $rut = $this->generator->asObject()->generate(); 117 | 118 | static::assertInstanceOf(Rut::class, $rut); 119 | } 120 | 121 | public function testAsStrict() 122 | { 123 | $ruts = $this->generator->asStrict()->generate($rand = rand(10, 20)); 124 | 125 | static::assertIsArray($ruts); 126 | static::assertCount($rand, $ruts); 127 | 128 | foreach ($ruts as $rut) { 129 | static::assertIsString($rut); 130 | static::assertStringContainsString('.', $rut); 131 | static::assertStringContainsString('-', $rut); 132 | } 133 | } 134 | 135 | public function testAsBasic() 136 | { 137 | $ruts = $this->generator->asBasic()->generate($rand = rand(10, 20)); 138 | 139 | static::assertIsArray($ruts); 140 | static::assertCount($rand, $ruts); 141 | 142 | foreach ($ruts as $rut) { 143 | static::assertIsString($rut); 144 | static::assertStringNotContainsString('.', $rut); 145 | static::assertStringContainsString('-', $rut); 146 | } 147 | } 148 | 149 | public function testAsRaw() 150 | { 151 | $ruts = $this->generator->asRaw()->generate($rand = rand(10, 20)); 152 | 153 | static::assertIsArray($ruts); 154 | static::assertCount($rand, $ruts); 155 | 156 | foreach ($ruts as $rut) { 157 | static::assertIsString($rut); 158 | static::assertStringNotContainsString('.', $rut); 159 | static::assertStringNotContainsString('-', $rut); 160 | } 161 | } 162 | 163 | public function testWithoutDuplicates() 164 | { 165 | $this->generator = new class extends RutGenerator { 166 | protected function performGenerate(int $iterations): array 167 | { 168 | if ($iterations < 5) return ['four', 'five']; 169 | 170 | return ['one', 'one', 'two', 'two', 'three',]; 171 | } 172 | }; 173 | 174 | $ruts = $this->generator->withoutDuplicates()->asRaw()->generate($rand = 5); 175 | 176 | static::assertCount($rand, $ruts); 177 | static::assertEquals(['one', 'two', 'three', 'four', 'five'], $ruts); 178 | } 179 | 180 | public function testNotUnique() 181 | { 182 | $this->generator = new class extends RutGenerator { 183 | protected function performGenerate(int $iterations): array 184 | { 185 | return ['one', 'one', 'two', 'two', 'three']; 186 | } 187 | }; 188 | 189 | $ruts = $this->generator->withDuplicates()->asRaw()->generate($rand = 5); 190 | 191 | static::assertCount($rand, $ruts); 192 | 193 | static::assertEquals(['one', 'one', 'two', 'two', 'three'], $ruts); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/RutHelper.php: -------------------------------------------------------------------------------- 1 | $value) { 184 | $ruts[$key] = static::cleanRut(is_array($value) ? implode('', $value) : (string)$value); 185 | } 186 | 187 | // To see if all the ruts are equal we will remove the duplicates values. 188 | // Doing this should reduce the array to only 1 non-empty item, which 189 | // means that all the ruts are equal. Otherwise, they're not equal. 190 | $ruts = array_unique($ruts); 191 | 192 | return count($ruts) === 1 && ! empty($ruts[0]); 193 | } 194 | 195 | /** 196 | * Unpacks an array of RUTs if it's a single multidimensional array 197 | * 198 | * @param array $ruts 199 | * 200 | * @return array 201 | */ 202 | public static function unpack(array $ruts): array 203 | { 204 | // If the array contains only one entry, and that entry is an array: unpack. 205 | if (count($ruts) === 1 && is_array($ruts[0])) { 206 | $ruts = $ruts[0]; 207 | } 208 | 209 | return $ruts; 210 | } 211 | 212 | /** 213 | * Get the Verification Digit from a given number 214 | * 215 | * @param int $num 216 | * @return int|string 217 | * @internal This is the main logic to create a valid rut from a number. 218 | */ 219 | public static function getVd(int $num) 220 | { 221 | $i = 2; 222 | $sum = 0; 223 | 224 | foreach (array_reverse(str_split($num)) as $v) { 225 | if ($i === 8) { 226 | $i = 2; 227 | } 228 | $sum += $v * $i; 229 | ++$i; 230 | } 231 | 232 | $dig = 11 - ($sum % 11); 233 | 234 | switch ($dig) { 235 | case 11: 236 | return 0; 237 | case 10: 238 | return 'K'; 239 | default: 240 | return $dig; 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /src/RutGenerator.php: -------------------------------------------------------------------------------- 1 | min, $this->max] = $this->prepareMinMax(); 61 | 62 | $this->iterations = $iterations = max(1, $iterations); 63 | 64 | $array = $this->performGenerate($iterations); 65 | 66 | if ($this->unique) { 67 | $array = $this->fillNonUniqueIterations($array); 68 | } 69 | 70 | return $iterations === 1 && $unwrapSingle ? $array[0] : $array; 71 | } 72 | 73 | /** 74 | * Generates one unique result by checking an internal static array 75 | * 76 | * @return array|\DarkGhostHunter\RutUtils\Rut 77 | */ 78 | public function generateStatic() 79 | { 80 | do { 81 | $result = $this->generate(); 82 | } while (in_array($result, static::$static, true)); 83 | 84 | static::$static[] = $result; 85 | 86 | return $result; 87 | } 88 | 89 | /** 90 | * Flushes the static Ruts saved for generation 91 | * 92 | * @return $this 93 | */ 94 | public function flushStatic(): RutGenerator 95 | { 96 | static::$static = []; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Performs the random generation of RUTs by the given iterations. 103 | * 104 | * @param int $iterations 105 | * @return array 106 | */ 107 | protected function performGenerate(int $iterations): array 108 | { 109 | switch ($this->output) { 110 | case Rut::FORMAT_RAW: 111 | $array = $this->generateRaw($iterations, $this->min, $this->max); 112 | break; 113 | case Rut::FORMAT_BASIC: 114 | $array = $this->generateBasic($iterations, $this->min, $this->max); 115 | break; 116 | case Rut::FORMAT_STRICT: 117 | $array = $this->generateStrict($iterations, $this->min, $this->max); 118 | break; 119 | case 'object': 120 | default: 121 | $array = $this->generateObjects($iterations, $this->min, $this->max); 122 | break; 123 | } 124 | 125 | return $array; 126 | } 127 | 128 | /** 129 | * Prepare the minimum and maximum RUT numbers to generate. 130 | * 131 | * @return array 132 | */ 133 | protected function prepareMinMax(): array 134 | { 135 | return $this->person 136 | ? [ static::MINIMUM_NUMBER, Rut::COMPANY_RUT_BASE ] 137 | : [ Rut::COMPANY_RUT_BASE, static::MAXIMUM_NUMBER]; 138 | } 139 | 140 | /** 141 | * Generates a given number of random RUTs as strictly formatted strings. 142 | * 143 | * @param int $iterations 144 | * @param int $min 145 | * @param int $max 146 | * @return array 147 | */ 148 | protected function generateStrict(int $iterations, int $min, int $max): array 149 | { 150 | $array = []; 151 | 152 | for ($i = 0; $i < $iterations; ++$i) { 153 | $rut = rand($min, $max); 154 | $array[] = number_format($rut, 0, ',', '.') . '-' . RutHelper::getVd($rut); 155 | } 156 | 157 | return $array; 158 | } 159 | 160 | /** 161 | * Generates a given number of random RUTs as basic formatted strings. 162 | * 163 | * @param int $iterations 164 | * @param int $min 165 | * @param int $max 166 | * @return array 167 | */ 168 | protected function generateBasic(int $iterations, int $min, int $max): array 169 | { 170 | $array = []; 171 | 172 | for ($i = 0; $i < $iterations; ++$i) { 173 | $rut = rand($min, $max); 174 | $array[] = $rut . '-' . RutHelper::getVd($rut); 175 | } 176 | 177 | return $array; 178 | } 179 | 180 | /** 181 | * Generate a given number of random RUTs as raw strings. 182 | * 183 | * @param int $iterations 184 | * @param int $min 185 | * @param int $max 186 | * @return array 187 | */ 188 | protected function generateRaw(int $iterations, int $min, int $max): array 189 | { 190 | $array = []; 191 | 192 | for ($i = 0; $i < $iterations; ++$i) { 193 | $array[] = ($rut = rand($min, $max)) . RutHelper::getVd($rut); 194 | } 195 | 196 | return $array; 197 | } 198 | 199 | /** 200 | * Generate a given number of random RUTs as Rut instances. 201 | * 202 | * @param int $iterations 203 | * @param int $min 204 | * @param int $max 205 | * @return array 206 | */ 207 | protected function generateObjects(int $iterations, int $min, int $max): array 208 | { 209 | $array = []; 210 | 211 | for ($i = 0; $i < $iterations; ++$i) { 212 | $array[] = RutHelper::rectify(rand($min, $max)); 213 | } 214 | 215 | return $array; 216 | } 217 | 218 | /** 219 | * Remove non unique values and replaces them with new ones 220 | * 221 | * We use this method because it's less resource-heavy to make 100 RUTs, 222 | * filter the ones repeated, count how many are left to reach the quota, 223 | * generate the remaining, and repeat until there is nothing to generate, 224 | * instead of checking each one if it's repeated on the resulting array. 225 | * 226 | * @param array $array 227 | * @return array 228 | */ 229 | protected function fillNonUniqueIterations(array &$array): array 230 | { 231 | $array = array_unique($array, SORT_REGULAR); 232 | 233 | while ($this->iterations > ($total = count($array))) { 234 | 235 | array_push($array, ...$this->performGenerate($this->iterations - $total)); 236 | 237 | $array = array_unique($array, SORT_REGULAR); 238 | 239 | } 240 | 241 | return array_values($array); 242 | } 243 | 244 | /** 245 | * Creates a new Rut Generator instance 246 | * 247 | * @return static 248 | */ 249 | public static function make(): RutGenerator 250 | { 251 | return new static; 252 | } 253 | } -------------------------------------------------------------------------------- /src/Rut.php: -------------------------------------------------------------------------------- 1 | rut = [ 56 | 'num' => $num, 57 | 'vd' => $this->case($vd), 58 | ]; 59 | } 60 | 61 | /** 62 | * Clones this Rut instance into a new instance 63 | * 64 | * @return \DarkGhostHunter\RutUtils\Rut 65 | */ 66 | public function clone(): Rut 67 | { 68 | return clone $this; 69 | } 70 | 71 | /** 72 | * Returns the RUT as an array 73 | * 74 | * @return array 75 | */ 76 | public function getRut(): array 77 | { 78 | return $this->rut; 79 | } 80 | 81 | /** 82 | * Makes one Rut instance, or null if its malformed. No validation is done. 83 | * 84 | * @param mixed $rut 85 | * @param null|int|string|callable $vd 86 | * @param null|callable|mixed $default 87 | * @return null|\DarkGhostHunter\RutUtils\Rut 88 | */ 89 | public static function make($rut, $vd = null, $default = null) 90 | { 91 | if ($rut instanceof static) { 92 | return $rut; 93 | } 94 | 95 | if (is_callable($vd) && $default === null) { 96 | $default = $vd; 97 | $vd = null; 98 | } 99 | 100 | if (is_array($rut)) { 101 | [$rut, $vd, $default] = array_pad($rut, 3, null); 102 | } 103 | elseif ($vd === null) { 104 | [$rut, $vd] = RutHelper::separateRut($rut); 105 | } 106 | 107 | // Create a new instance of a Rut if both parameters are correct. 108 | if ($rut && $vd !== null) { 109 | $rut = new static((int)$rut, $vd); 110 | 111 | if ($rut->isValid()) { 112 | return $rut; 113 | } 114 | } 115 | 116 | return is_callable($default) ? $default() : $default; 117 | } 118 | 119 | /** 120 | * Makes a single Rut instance, or throws an exception when its malformed or invalid 121 | * 122 | * @param string|int $rut 123 | * @param null $vd 124 | * @return \DarkGhostHunter\RutUtils\Rut 125 | * @throws \DarkGhostHunter\RutUtils\Exceptions\InvalidRutException 126 | */ 127 | public static function makeOrThrow($rut, $vd = null): Rut 128 | { 129 | if ($instance = self::make($rut, $vd)) { 130 | return $instance; 131 | } 132 | 133 | throw new InvalidRutException($rut); 134 | } 135 | 136 | /** 137 | * Makes many Rut instances from a given array, discarding malformed ones. 138 | * 139 | * @param array $ruts 140 | * @return array|mixed 141 | */ 142 | public static function many(...$ruts) 143 | { 144 | $ruts = RutHelper::unpack($ruts); 145 | 146 | foreach ($ruts as $key => $value) { 147 | $ruts[$key] = static::make($value); 148 | } 149 | 150 | $ruts = array_filter($ruts); 151 | 152 | foreach (static::$after as $callback) { 153 | $ruts = $callback($ruts); 154 | } 155 | 156 | return $ruts; 157 | } 158 | 159 | /** 160 | * Creates only valid RUTs, or throw an exception if at least one is malformed or invalid 161 | * 162 | * @param array $ruts 163 | * @return array|mixed 164 | * @throws \DarkGhostHunter\RutUtils\Exceptions\InvalidRutException 165 | */ 166 | public static function manyOrThrow(...$ruts) 167 | { 168 | $ruts = RutHelper::unpack($ruts); 169 | 170 | foreach ($ruts as $key => $value) { 171 | $ruts[$key] = static::makeOrThrow($value); 172 | } 173 | 174 | foreach (static::$after as $callback) { 175 | $ruts = $callback($ruts); 176 | } 177 | 178 | return $ruts; 179 | } 180 | 181 | /** 182 | * Dynamically manage getting the RUT attributes 183 | * 184 | * @param $name 185 | * @return string|int|null 186 | */ 187 | public function __get($name) 188 | { 189 | if ($name === 'vd' && $this->rut['vd']) { 190 | return $this->shouldUppercase() 191 | ? strtoupper($this->rut['vd']) 192 | : strtolower($this->rut['vd']); 193 | } 194 | 195 | return $this->rut[$name] ?? null; 196 | } 197 | 198 | /** 199 | * Dynamically set an attribute 200 | * 201 | * @param $name 202 | * @param $value 203 | */ 204 | public function __set($name, $value) 205 | { 206 | // Don't allow to set attributes 207 | } 208 | 209 | /** 210 | * Returns if an attribute is set 211 | * 212 | * @param $name 213 | * @return bool 214 | */ 215 | public function __isset($name) 216 | { 217 | return isset($this->rut[$name]); 218 | } 219 | 220 | /** 221 | * Unset a property or attribute 222 | * 223 | * @param $name 224 | */ 225 | public function __unset($name) 226 | { 227 | // Dont' allow to unset attributes Ruts 228 | } 229 | 230 | /** 231 | * Whether a offset exists 232 | * 233 | * @param $offset 234 | * @return bool 235 | */ 236 | public function offsetExists($offset): bool 237 | { 238 | return $this->__isset($offset); 239 | } 240 | 241 | /** 242 | * Offset to retrieve 243 | * 244 | * @param $offset 245 | * @return mixed|null 246 | */ 247 | public function offsetGet($offset) 248 | { 249 | return $this->__get($offset); 250 | } 251 | 252 | /** 253 | * Offset to set 254 | * 255 | * @param $offset 256 | * @param $value 257 | */ 258 | public function offsetSet($offset, $value) 259 | { 260 | // Dont' allow to set attributes 261 | } 262 | 263 | /** 264 | * Offset to unset 265 | * 266 | * @param $offset 267 | */ 268 | public function offsetUnset($offset) 269 | { 270 | // Dont' allow to unset attributes Ruts 271 | } 272 | 273 | /** 274 | * Returns the RUT as a JSON string 275 | * 276 | * @return false|string 277 | * @throws \JsonException 278 | */ 279 | public function toJson() 280 | { 281 | return json_encode($this->jsonSerialize(), JSON_THROW_ON_ERROR); 282 | } 283 | 284 | /** 285 | * Return the RUT as a string 286 | * 287 | * @return string 288 | */ 289 | public function __toString() 290 | { 291 | return $this->toFormattedString(); 292 | } 293 | 294 | /** 295 | * Return an array representation of the Rut instance 296 | * 297 | * @return array 298 | */ 299 | public function toArray(): array 300 | { 301 | return $this->rut; 302 | } 303 | 304 | /** 305 | * String representation of object 306 | * 307 | * @return string 308 | */ 309 | public function serialize(): string 310 | { 311 | return $this->toRawString(); 312 | } 313 | 314 | /** 315 | * Constructs the object 316 | * 317 | * @param string $data 318 | * 319 | * @return void 320 | * @since 5.1.0 321 | */ 322 | public function unserialize($data): void 323 | { 324 | [$num, $vd] = str_split($data, strlen($data) - 1); 325 | 326 | $this->rut = ['num' => (int)$num, 'vd' => $vd]; 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /tests/RutTest.php: -------------------------------------------------------------------------------- 1 | num); 16 | static::assertEquals('K', $rut->vd); 17 | 18 | $rut = new Rut(1000, 'moUse'); 19 | 20 | static::assertEquals(1000, $rut->num); 21 | static::assertEquals('MOUSE', $rut->vd); 22 | } 23 | 24 | public function testMake() 25 | { 26 | $rut = Rut::make('foo'); 27 | 28 | static::assertNull($rut); 29 | 30 | $rut = Rut::make('10.666.309-2'); 31 | 32 | static::assertEquals(10666309, $rut->num); 33 | static::assertEquals(2, $rut->vd); 34 | 35 | $rut = Rut::make(18300252, 'k'); 36 | 37 | static::assertEquals(18300252, $rut->num); 38 | static::assertEquals('K', $rut->vd); 39 | 40 | $rut = Rut::make('1d2a9w0!3@9g1=9-1'); 41 | 42 | static::assertEquals(12903919, $rut->num); 43 | static::assertEquals(1, $rut->vd); 44 | 45 | $rut = Rut::make('foo', 'bar'); 46 | static::assertNull($rut); 47 | 48 | $rut = Rut::make('1d2a9w0!3@9g1=9-1'); 49 | $rut = Rut::make($rut); 50 | 51 | static::assertEquals(12903919, $rut->num); 52 | static::assertEquals(1, $rut->vd); 53 | 54 | $rut = Rut::make('foo', 'bar', function () { 55 | return 'foo'; 56 | }); 57 | static::assertEquals('foo', $rut); 58 | 59 | $rut = Rut::make('foo', null, function () { 60 | return 'foo'; 61 | }); 62 | static::assertEquals('foo', $rut); 63 | 64 | $rut = Rut::make('foo', function () { 65 | return 'foo'; 66 | }); 67 | static::assertEquals('foo', $rut); 68 | } 69 | 70 | public function testClone() 71 | { 72 | $rutA = Rut::make('10.666.309-2'); 73 | $rutB = $rutA->clone(); 74 | 75 | static::assertEquals($rutA, $rutB); 76 | } 77 | 78 | public function testMakeMany() 79 | { 80 | $ruts = Rut::many([ 81 | '18.300.252-k', 82 | [9306036, 9], 83 | '210668594', 84 | '1d2a9w0!3@9g1=9-1', 85 | 'foo', 86 | ['foo', 'bar'] 87 | ]); 88 | 89 | static::assertIsArray($ruts); 90 | static::assertCount(4, $ruts); 91 | 92 | static::assertEquals(18300252, $ruts[0]->num); 93 | static::assertEquals('K', $ruts[0]->vd); 94 | 95 | static::assertEquals(9306036, $ruts[1]->num); 96 | static::assertEquals(9, $ruts[1]->vd); 97 | 98 | static::assertEquals(21066859, $ruts[2]->num); 99 | static::assertEquals(4, $ruts[2]->vd); 100 | 101 | static::assertEquals(12903919, $ruts[3]->num); 102 | static::assertEquals(1, $ruts[3]->vd); 103 | } 104 | 105 | public function testMakeManyUnpacks() 106 | { 107 | $ruts = Rut::many(...[ 108 | '18.300.252-k', 109 | [9306036, 9], 110 | '210668594', 111 | '1d2a9w0!3@9g1=9-1', 112 | 'foo', 113 | ['foo', 'bar'] 114 | ]); 115 | 116 | static::assertIsArray($ruts); 117 | static::assertCount(4, $ruts); 118 | 119 | static::assertEquals(18300252, $ruts[0]->num); 120 | static::assertEquals('K', $ruts[0]->vd); 121 | 122 | static::assertEquals(9306036, $ruts[1]->num); 123 | static::assertEquals(9, $ruts[1]->vd); 124 | 125 | static::assertEquals(21066859, $ruts[2]->num); 126 | static::assertEquals(4, $ruts[2]->vd); 127 | 128 | static::assertEquals(12903919, $ruts[3]->num); 129 | static::assertEquals(1, $ruts[3]->vd); 130 | } 131 | 132 | public function testMakeManyReturnsEmptyArrayWhenNoArguments() 133 | { 134 | $ruts = Rut::many(); 135 | 136 | static::assertIsArray($ruts); 137 | static::assertEmpty($ruts); 138 | } 139 | 140 | public function testManyOrThrow() 141 | { 142 | $this->expectException(InvalidRutException::class); 143 | $this->expectExceptionMessage('The given RUT [foo] is invalid'); 144 | 145 | Rut::manyOrThrow([ 146 | '18.300.252-k', 147 | [9306036, 9], 148 | '210668594', 149 | '1d2a9w0!3@9g1=9-1', 150 | 'foo', 151 | ['foo', 'bar'], 152 | 187654321 153 | ]); 154 | } 155 | 156 | public function testManyReturnsEmptyArray() 157 | { 158 | $ruts = Rut::many(); 159 | 160 | static::assertIsArray($ruts); 161 | static::assertEmpty($ruts); 162 | } 163 | 164 | public function testMakeOrThrowFailsWhenInvalidRut() 165 | { 166 | $this->expectException(InvalidRutException::class); 167 | $this->expectExceptionMessage('The given RUT [12345678-k] is invalid'); 168 | 169 | Rut::makeOrThrow('12345678-k'); 170 | } 171 | 172 | public function testMakeOrThrowFailsWhenInvalidRuts() 173 | { 174 | $this->expectException(InvalidRutException::class); 175 | $this->expectExceptionMessage('The given RUT is invalid'); 176 | 177 | Rut::manyOrThrow([ 178 | '18.300.252-k', 179 | ['foo'], 180 | 'bar' 181 | ]); 182 | } 183 | 184 | public function testGetRut() 185 | { 186 | $rut = new Rut(10666309, 2); 187 | 188 | static::assertEquals(['num' => 10666309, 'vd' => 2], $rut->getRut()); 189 | } 190 | 191 | public function testGetAndSet() 192 | { 193 | $rut = new Rut(10666309, 2); 194 | 195 | static::assertEquals(10666309, $rut->num); 196 | static::assertEquals(2, $rut->vd); 197 | 198 | static::assertEquals(10666309, $rut['num']); 199 | static::assertEquals(2, $rut['vd']); 200 | 201 | $rut->num = 'foo'; 202 | $rut->vd = 'bar'; 203 | 204 | static::assertEquals(10666309, $rut->num); 205 | static::assertEquals(2, $rut->vd); 206 | 207 | static::assertEquals(10666309, $rut['num']); 208 | static::assertEquals(2, $rut['vd']); 209 | 210 | $rut['num'] = 'foo'; 211 | $rut['vd'] = 'bar'; 212 | 213 | static::assertEquals(10666309, $rut->num); 214 | static::assertEquals(2, $rut->vd); 215 | 216 | static::assertEquals(10666309, $rut['num']); 217 | static::assertEquals(2, $rut['vd']); 218 | } 219 | 220 | public function testIssetAndUnset() 221 | { 222 | $rut = new Rut(10666309, 2); 223 | 224 | static::assertTrue(isset($rut->vd)); 225 | static::assertTrue(isset($rut['vd'])); 226 | static::assertTrue(isset($rut->num)); 227 | static::assertTrue(isset($rut['num'])); 228 | 229 | unset($rut->vd, $rut['vd'], $rut->num, $rut['num']); 230 | 231 | static::assertTrue(isset($rut->vd)); 232 | static::assertTrue(isset($rut['vd'])); 233 | static::assertTrue(isset($rut->num)); 234 | static::assertTrue(isset($rut['num'])); 235 | } 236 | 237 | public function testToJson() 238 | { 239 | $rut = new Rut(10666309, 2); 240 | 241 | static::assertJson($rut->toJson()); 242 | static::assertJson(json_encode($rut)); 243 | 244 | static::assertEquals('"10.666.309-2"', $rut->toJson()); 245 | static::assertEquals('"10.666.309-2"', json_encode($rut)); 246 | } 247 | 248 | public function testToString() 249 | { 250 | $rut = new Rut(10666309, 2); 251 | 252 | static::assertIsString($rut->__toString()); 253 | static::assertEquals('10.666.309-2', $rut->__toString()); 254 | static::assertIsString((string)$rut); 255 | static::assertEquals('10.666.309-2', (string)$rut); 256 | } 257 | 258 | public function testToArray() 259 | { 260 | $rut = new Rut(10666309, 2); 261 | 262 | static::assertIsArray($rut->toArray()); 263 | 264 | static::assertEquals($array = [ 265 | 'num' => 10666309, 266 | 'vd' => '2' 267 | ], $rut->toArray()); 268 | } 269 | 270 | public function testSerialization() 271 | { 272 | $unserialized = unserialize(serialize(new Rut(10666309, 2))); 273 | 274 | static::assertInstanceOf(Rut::class, $unserialized); 275 | static::assertEquals(10666309, $unserialized->num); 276 | static::assertEquals(2, $unserialized->vd); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /tests/RutHelperTest.php: -------------------------------------------------------------------------------- 1 | 917q!$/%/e0K', 42 | ]; 43 | 44 | public function testCleansRut() 45 | { 46 | foreach ($this->malformedRuts as $key => $rut) { 47 | static::assertEquals(strtolower($this->ruts[$key]), RutHelper::cleanRut($rut, false)); 48 | } 49 | } 50 | 51 | public function testNullOnEmptyCleanedRut() 52 | { 53 | static::assertNull(RutHelper::cleanRut('asdasdasdasd', false)); 54 | static::assertNull(RutHelper::cleanRut('asdasdasdasd', true)); 55 | } 56 | 57 | public function testSeparatesRut() 58 | { 59 | foreach ($this->ruts as $rut) { 60 | 61 | static::assertEquals( 62 | strtoupper(substr($rut, 0, -1)), 63 | RutHelper::separateRut($rut)[0] 64 | ); 65 | static::assertEquals( 66 | strtoupper(substr($rut, -1, 1)), 67 | RutHelper::separateRut($rut)[1] 68 | ); 69 | } 70 | } 71 | 72 | public function testSeparatesMalformedRut() 73 | { 74 | foreach ($this->malformedRuts as $key => $rut) { 75 | static::assertEquals( 76 | strtoupper(substr($this->ruts[$key], 0, -1)), 77 | RutHelper::separateRut($rut)[0] 78 | ); 79 | static::assertEquals( 80 | strtoupper(substr($this->ruts[$key], -1, 1)), 81 | RutHelper::separateRut($rut)[1] 82 | ); 83 | } 84 | } 85 | 86 | public function testNullOnCleanedRut() 87 | { 88 | static::assertEquals([null, null], RutHelper::separateRut('this-is-no-a-rut')); 89 | } 90 | 91 | public function testValidate() 92 | { 93 | static::assertTrue(RutHelper::validate($this->ruts)); 94 | 95 | foreach ($this->ruts as $rut) { 96 | static::assertTrue(RutHelper::validate($rut)); 97 | } 98 | } 99 | 100 | public function testDoesntValidates() 101 | { 102 | foreach ($this->invalidRuts as $rut) { 103 | static::assertFalse(RutHelper::validate($rut)); 104 | } 105 | 106 | static::assertFalse(RutHelper::validate('asdasdasd')); 107 | 108 | } 109 | 110 | public function testValidateOnMalformedRut() 111 | { 112 | foreach ($this->malformedRuts as $rut) { 113 | static::assertTrue(RutHelper::validate($rut)); 114 | } 115 | } 116 | 117 | public function testDoesntValidateOnMalformedRut() 118 | { 119 | $malformed = [ 120 | '8498dfadf4616', 121 | 247009091, 122 | '58685f66*^Ǩ_:;>¿??="·$&""8', 123 | 13666578, 124 | '22^¨*¨"$&=(%605071J', 125 | '1437*^¨_:;·"$SDFPW9ASDW171K', 126 | ]; 127 | 128 | foreach ($malformed as $rut) { 129 | static::assertFalse(RutHelper::validate($rut)); 130 | } 131 | } 132 | 133 | public function testDoesntValidateOnInvalidType() 134 | { 135 | $malformed = [ 136 | '8.495.461-6', 137 | '24.700.909-4', 138 | ['5.868.566-6'], 139 | '13.666.578-2', 140 | '22.605.071-k', 141 | '14.379.170-K', 142 | ]; 143 | 144 | static::assertFalse(RutHelper::validate($malformed)); 145 | } 146 | 147 | public function testValidateStrict() 148 | { 149 | $ruts = [ 150 | '8.495.461-6', 151 | '24.700.909-4', 152 | '5.868.566-6', 153 | '13.666.578-2', 154 | '22.605.071-k', 155 | '14.379.170-K', 156 | ]; 157 | 158 | static::assertTrue(RutHelper::validateStrict($ruts)); 159 | 160 | foreach ($ruts as $rut) { 161 | static::assertTrue(RutHelper::validateStrict($rut)); 162 | } 163 | } 164 | 165 | public function testDoesntValidateStrict() 166 | { 167 | foreach ($this->ruts as $rut) { 168 | static::assertFalse(RutHelper::validateStrict($rut)); 169 | } 170 | } 171 | 172 | public function testDoesntValidateStrictWhenInvalidType() 173 | { 174 | $ruts = [ 175 | '8.495.461-6', 176 | ['24.700.909-4'], 177 | '5.868.566-6', 178 | '13.666.578-2', 179 | '14.379.170-K', 180 | ]; 181 | 182 | static::assertFalse(RutHelper::validateStrict($ruts)); 183 | 184 | $ruts = [ 185 | new Rut(8495461, '6'), 186 | new Rut(13666578, '6'), 187 | ]; 188 | 189 | static::assertFalse(RutHelper::validateStrict($ruts)); 190 | } 191 | 192 | public function testAreTwoEqual() 193 | { 194 | static::assertTrue(RutHelper::isEqual(247009094, '2470!!!###0909-4')); 195 | static::assertTrue(RutHelper::isEqual(247009094, '2470!!!###0909-4', '24.700.909-4')); 196 | } 197 | 198 | public function testAreTwoNotEqual() 199 | { 200 | static::assertFalse(RutHelper::isEqual(247009091, '2470!!!###0909-4')); 201 | static::assertTrue(RutHelper::isEqual(247009094, '2470!!!###0909-4', '24.700.9094')); 202 | } 203 | 204 | public function testAreEqualWithSingleArray() 205 | { 206 | static::assertTrue(RutHelper::isEqual([ 207 | 247009094, '2470!!!###0909-4', 208 | ])); 209 | 210 | static::assertFalse(RutHelper::isEqual([ 211 | 247009091, '2470!!!###0909-4', 212 | ])); 213 | } 214 | 215 | public function testFilter() 216 | { 217 | static::assertEquals(['247009094'], RutHelper::filter(247009094)); 218 | static::assertEquals(['247009094'], RutHelper::filter('247009094')); 219 | 220 | static::assertEquals($this->ruts, RutHelper::filter($this->ruts)); 221 | 222 | $malformedRuts = array_map(function ($rut) { 223 | return (string)$rut; 224 | }, $this->malformedRuts); 225 | 226 | static::assertEquals($malformedRuts, RutHelper::filter($this->malformedRuts)); 227 | } 228 | 229 | public function testDoesntFilter() 230 | { 231 | static::assertEquals([], RutHelper::filter(247009091)); 232 | static::assertEquals([], RutHelper::filter('247009091')); 233 | static::assertEquals([], RutHelper::filter($this->invalidRuts)); 234 | } 235 | 236 | public function testRectify() 237 | { 238 | $rutA = 24700909; 239 | $rutAVd = 4; 240 | $rutB = '22605071'; 241 | $rutBVd = 'k'; 242 | 243 | $rutARectified = RutHelper::rectify($rutA); 244 | $rutBRectified = RutHelper::rectify($rutB); 245 | 246 | static::assertInstanceOf(Rut::class, $rutARectified); 247 | static::assertInstanceOf(Rut::class, $rutBRectified); 248 | 249 | static::assertEquals((string)$rutA . $rutAVd, $rutARectified->toRawString()); 250 | static::assertEquals(strtoupper($rutB . $rutBVd), $rutBRectified->toRawString()); 251 | } 252 | 253 | public function testDoesntRectify() 254 | { 255 | $this->expectException(TypeError::class); 256 | 257 | RutHelper::rectify('NotARut'); 258 | } 259 | 260 | public function testIsPerson() 261 | { 262 | static::assertTrue(RutHelper::isPerson(136665782)); 263 | static::assertTrue(RutHelper::isPerson('22605071k')); 264 | 265 | static::assertFalse(RutHelper::isPerson(761231235)); 266 | static::assertFalse(RutHelper::isPerson('66123136K')); 267 | } 268 | 269 | public function testIsCompany() 270 | { 271 | static::assertFalse(RutHelper::isCompany(136665782)); 272 | static::assertFalse(RutHelper::isCompany('22605071k')); 273 | 274 | static::assertTrue(RutHelper::isCompany(761231235)); 275 | static::assertTrue(RutHelper::isCompany('66123136K')); 276 | } 277 | 278 | public function testGetVd() 279 | { 280 | $ruts = [ 281 | '6' => '8495461', 282 | '4' => '24700909', 283 | '2' => '13666578', 284 | 'K' => '22605071', 285 | ]; 286 | 287 | foreach ($ruts as $key => $rut) { 288 | static::assertEquals($key, RutHelper::getVd($rut)); 289 | } 290 | 291 | } 292 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Pablo García Saldaña - Unsplash (UL) #Y-MGVIkpyFw](https://images.unsplash.com/photo-1441484295955-db07de1fdbad?ixlib=rb-1.2.1&auto=format&fit=crop&w=1280&h=400&q=80) 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/darkghosthunter/rut-utils/v/stable)](https://packagist.org/packages/darkghosthunter/rut-utils) [![License](https://poser.pugx.org/darkghosthunter/rut-utils/license)](https://packagist.org/packages/darkghosthunter/rut-utils) 4 | ![](https://img.shields.io/packagist/php-v/darkghosthunter/rut-utils.svg) [![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/RutUtils/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/RutUtils?branch=master) [![Maintainability](https://api.codeclimate.com/v1/badges/7142cecb93e555cd7028/maintainability)](https://codeclimate.com/github/DarkGhostHunter/RutUtils/maintainability) 5 | 6 | # RUT Utilities 7 | 8 | A complete library for creating, manipulating and generating chilean RUTs or RUNs. 9 | 10 | This package allows you to: 11 | 12 | - **Create** a RUT object to conveniently hold the RUT information. 13 | - **Validate**, clean and rectify RUTs. 14 | - **Generate** random RUTs in a flexible manner. 15 | 16 | While this package works as a fire-and-forget utility for your project, ensure you read the documentation so you don't repeat yourself. 17 | 18 | ## Requirements 19 | 20 | This package only needs PHP 7.3 and later. 21 | 22 | It may work on older versions, but it will only support active PHP releases. 23 | 24 | > Optional: Know what *el weón weón weón* means. 25 | 26 | ## Installation 27 | 28 | Just fire Composer and require it into your project: 29 | 30 | ```bash 31 | composer require darkghosthunter/rut-utils 32 | ``` 33 | 34 | If you don't have Composer in your project, ~~you should be ashamed~~ just [install it](https://getcomposer.org/download/) . 35 | 36 | ## Usage 37 | 38 | * [What is a RUT or RUN?](#what-is-a-rut-or-run) 39 | * [Creating a RUT](#creating-a-rut) 40 | * [Retrieving a RUT](#retrieving-a-rut) 41 | * [Generating RUTs](#generating-ruts) 42 | * [Helpers](#helpers) 43 | * [Make Callbacks](#make-callbacks) 44 | * [Serialization](#serialization) 45 | * [Global helper](#global-helper) 46 | 47 | ### What is a RUT or RUN? 48 | 49 | A RUT (or RUN for people) is a string of numbers which identify a person or company. They're unique for each one, and they're never re-assigned to an individual, so the registry of RUTs is always growing. 50 | 51 | The RUT its comprised of a random Number, like `18.765.432`, and a Verification Digit, which is the result of a mathematical algorithm [(Modulo 11)](https://www.google.com/search?q=modulo+11+algorithm) over that number. This Verification Digit vary between `0` and `9`, or a `K`. In the end. you get this: 52 | 53 | ``` 54 | 18.765.432-1 55 | ``` 56 | 57 | This identification information is a safe bet for chilean companies. It allows to attach one account to one individual (person or company), and can be cross-referenced with other data the user may have available through other services. 58 | 59 | > What's the difference between RUTs and RUNs? RUT are meant for identifying a person or company taxes, RUNs are to identify single persons. For both cases, **they're practically the same**. 60 | 61 | ### Creating a RUT 62 | 63 | There are two ways to create a RUT: manual instancing, which is strict, and using the `make()` static helper. 64 | 65 | #### Manual instancing 66 | 67 | Using manual instantiation allows you to create a RUT by the given number and verification digit quickly. For example, with data coming from a Database or any other trustful source. 68 | 69 | ```php 70 | num; // 14328145 188 | echo $rut->vd; // 0 189 | ``` 190 | 191 | > For safety reasons, you cannot set the `num` and `vd` in the instance. 192 | 193 | #### Lowercase or Uppercase `K` 194 | 195 | A RUT can have the `K` character as verification *digit*. The Rut object doesn't discerns between lowercase `k` or uppercase `K` when creating one, but **it always stores uppercase as default**. 196 | 197 | You can change this behaviour for all Rut instances using the `allUppercase()` or `allLowercase()` methods: 198 | 199 | ```php 200 | vd; // "k" 207 | 208 | Rut::allUppercase(); 209 | 210 | echo Rut::make('12343580', 'K')->vd; // "K" 211 | ``` 212 | 213 | Additionally, you can change this configuration for a single instance by using `uppercase()` and `lowercase()`. 214 | 215 | ```php 216 | lowercase(); 223 | 224 | echo $rut->vd; // "K" 225 | 226 | $rut->uppercase(); 227 | 228 | echo $rut->vd; // "k" 229 | ``` 230 | 231 | This may come in handy when your source of truth manages lowercase `k` and you need strict comparison for storing, or normalize it. 232 | 233 | ### Generating RUTs 234 | 235 | Sometimes it's handy to create a RUT on the fly, usually for testing purposes when seeding and mocking. 236 | 237 | You can do that using the `RutGenerator` class and use the methods to build how you want to generate your RUTs. The methods are fluent, meaning, you can chain them until you use the `generate()` method. 238 | 239 | ```php 240 | generate(); 245 | 246 | echo $rut; // "7.976.228-8" 247 | ``` 248 | 249 | The default mode makes a RUT for normal people, which are bound between 1.000.000 and 50.000.000. You can use the `forCompany()` method, which will vary the result randomly between 50.000.000 and 100.000.000. 250 | 251 | ```php 252 | asPerson()->generate(); 257 | // "15.846.327-K" 258 | 259 | echo $company = RutGenerator::make()->asCompany()->generate(); 260 | // "54.029.467-4" 261 | ``` 262 | 263 | Of course one may be not enough. You can add a parameter to these methods with the number of RUTs you want to make. The result will be returned as an `array`. 264 | 265 | ```php 266 | asPerson()->generate(10); 271 | $companyRuts = RutGenerator::make()->asCompany()->generate(35); 272 | ``` 273 | 274 | If for some reason you need them as raw strings instead of Rut instances, which is very good when generating thousands of them on strict memory usage, use the `asBasic()` and `asRaw()` method. 275 | 276 | This will output the random strings like `22605071K`. 277 | 278 | ```php 279 | asRaw()->generate(10); 284 | $basic = RutGenerator::make()->asBasic()->generate(20); 285 | $strict = RutGenerator::make()->asStrict()->generate(30); 286 | ``` 287 | 288 | #### Generating random unique RUTs 289 | 290 | If you need to create more than thousands of RUTs without the risk of having them duplicated, use the `withoutDuplicates()` method. 291 | 292 | ```php 293 | withoutDuplicates()->generate(100000); 298 | ``` 299 | 300 | ##### Unique results between calls 301 | 302 | You may have a custom seeder in your application that may call `generate()` every single time, increasing risk of collisions with each generation. Fear not! Using the `generateStatic()` you are guaranteed to get unique results during a single application lifecycle. 303 | 304 | ```php 305 | 'John'], 311 | ['name' => 'Clara'], 312 | ['name' => 'Mark'], 313 | // ... and other 99.997 records 314 | ]; 315 | 316 | $seeder = function ($user) { 317 | return array_merge($user, [ 318 | 'rut' => RutGenerator::make()->generateStatic() 319 | ]); 320 | }; 321 | 322 | // Call the seeder 323 | foreach ($users as $key => $user) { 324 | $users[$key] = $seeder($user); 325 | } 326 | 327 | // Flush the static array for the next seeder call 328 | RutGenerator::make()->flushStatic(); 329 | ``` 330 | 331 | ### Helpers 332 | 333 | You can manipulate and check strings quickly using the `RutHelper` class, which contains a wide variety of handy static methods you can use. 334 | 335 | * [`cleanRut`](#cleanrut) 336 | * [`separateRut`](#separaterut) 337 | * [`validate`](#validate) 338 | * [`validateStrict`](#validatestrict) 339 | * [`filter`](#filter) 340 | * [`rectify`](#rectify) 341 | * [`isPerson`](#isperson) 342 | * [`isCompany`](#iscompany) 343 | * [`isEqual`](#isequal) 344 | * [`getVd`](#getvd) 345 | 346 | #### `cleanRut` 347 | 348 | Clears a RUT string from invalid characters. Additionally, you can set if you want the `K` verification character as uppercase or lowercase. 349 | 350 | ```php 351 | 18290743, 372 | // 1 => 'k', 373 | // ] 374 | ``` 375 | 376 | #### `validate` 377 | 378 | Checks if the RUTs issued are valid. 379 | 380 | If there are more than one RUT, it will return `true` if all the RUTs are valid, and `false` if at least one is invalid. 381 | 382 | ```php 383 | isValid(); // true 403 | echo Rut::make('94.328.145-0')->isValid(); // false 404 | echo Rut::make('cleanthis14328145-0-andthistoo')->isValid(); // true 405 | ``` 406 | 407 | #### `validateStrict` 408 | 409 | You can strictly validate a RUT. The RUT value being passed must be a string with thousand separator and hyphen preceding the RUT verification digit. 410 | 411 | If there are more than one RUT, it will return `true` if all the RUTs are valid, and `false` if at least one is invalid. 412 | 413 | ```php 414 | 14328145-0 448 | // [1] => 12343580-K 449 | // } 450 | 451 | $rutsB = RutHelper::filter([ 452 | '14328145-0', 453 | '12343580-K', 454 | '94.328.145-0', 455 | 'not-a-rut' 456 | ]); 457 | 458 | var_dump($rutsB); 459 | 460 | // array(1) { 461 | // [0] => 14328145-0 462 | // [1] => 12343580-K 463 | // } 464 | ``` 465 | #### `rectify` 466 | 467 | Receives only the RUT number and returns a valid `Rut` instance with the corresponding verification digit. 468 | 469 | ```php 470 | num; // "18765432" 477 | echo $rut->vd; // "7" 478 | ``` 479 | 480 | > If you pass down a whole RUT, you may get a new RUT with an appended Verification Digit. Ensure you pass down only the RUT number. 481 | 482 | #### `isPerson` 483 | 484 | Checks if the RUT below 50.000.000, which are usually used for normal people. 485 | 486 | ```php 487 | isPerson(); // true 502 | ``` 503 | 504 | #### `isCompany` 505 | 506 | Checks if the RUT is over or equal 50.000.000, which are usually used for companies. 507 | 508 | ```php 509 | isCompany(); // true 524 | ``` 525 | 526 | #### `isEqual` 527 | 528 | Returns if two or more RUTs are equal, independently of how these are formatted, **even if these are invalid**. 529 | 530 | ```php 531 | isEqual('thisisARut12343580-K', '12343580-k'); // true 556 | ``` 557 | 558 | #### `getVd` 559 | 560 | Returns the verification digit for a given number. 561 | 562 | ```php 563 | count($ruts), 604 | ]); 605 | }); 606 | 607 | $ruts = Rut::many([ 608 | // ... 609 | ]); 610 | 611 | var_dump($ruts); 612 | // array(100) [ 613 | // // ... 614 | // 'count' => 100, 615 | // ] 616 | ``` 617 | 618 | > If you register multiple callbacks, these will be executed in the order they were registered. 619 | 620 | ### Serialization 621 | 622 | Sometimes you may want to store your Rut instance somewhere, or serialize it to JSON, or a string. With this package you're covered from all angles. 623 | 624 | #### Serialize / Unserialize 625 | 626 | By default, a Rut instance is serialized as a raw string, which is latter reconstructed quickly by just dividing the string into the number and verification digit: 627 | 628 | ```php 629 | toStrictString(); // "22.605.071-K" 671 | echo $rut->toBasicString(); // "22605071-K" 672 | echo $rut->toRawString(); // "22605071K" 673 | ``` 674 | 675 | #### JSON 676 | 677 | By default, when casting to JSON, the result is a string. You can change this to be an array of the number and verification digit using static methods for all instances, or a per-instance case: 678 | 679 | ```php 680 | jsonAsArray(); 695 | 696 | echo json_encode($rut); // {"num":"22605071","vd":"K"} 697 | 698 | $rut->jsonAsString(); 699 | 700 | echo json_encode($rut); // "22.605.071-K" 701 | ``` 702 | 703 | ## Global helper 704 | 705 | In version 2.0, all helpers have been killed and now you have only one called `rut()`. It works as a proxy for `Rut::make`, but accepts a default in case of invalid ruts. If no parameter is issued, an instance of the Rut Generator is returned. 706 | 707 | ```php 708 | generate(); 719 | 720 | echo $rut; // '20.750.456-4' 721 | ``` 722 | 723 | ## License 724 | 725 | This package is licenced by the [MIT License](LICENSE). 726 | --------------------------------------------------------------------------------