feat: new v4 version (#5)
This commit is contained in:
1
.dev/.gitignore
vendored
Normal file
1
.dev/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
coverage
|
||||
50
.dev/docker/php/Dockerfile
Normal file
50
.dev/docker/php/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
FROM alpine:3.18 as base
|
||||
|
||||
ARG UID=1000
|
||||
ARG USER=mnavarro
|
||||
ARG COMPOSER_VERSION=2.7.0
|
||||
|
||||
# Create a development user
|
||||
RUN adduser $USER --disabled-password --home /$USER --uid $UID
|
||||
|
||||
# Install PHP Extensions
|
||||
RUN apk add --no-cache \
|
||||
php82-cli \
|
||||
php82-phar \
|
||||
php82-mbstring \
|
||||
php82-json \
|
||||
php82-zip \
|
||||
php82-openssl \
|
||||
php82-dom \
|
||||
php82-xml \
|
||||
php82-soap \
|
||||
php82-session \
|
||||
php82-xmlwriter \
|
||||
php82-sockets \
|
||||
php82-simplexml \
|
||||
php82-bcmath \
|
||||
php82-xmlreader \
|
||||
php82-tokenizer \
|
||||
php82-iconv \
|
||||
php82-sodium \
|
||||
php82-fileinfo \
|
||||
php82-curl \
|
||||
php82-ctype \
|
||||
php82-pcntl \
|
||||
php82-posix
|
||||
|
||||
# Link php82 to php
|
||||
RUN ln -s /usr/bin/php82 /usr/bin/php
|
||||
|
||||
# Add wget to make requests
|
||||
RUN apk add --no-cache wget
|
||||
|
||||
# Download and install composer
|
||||
RUN wget -O /usr/bin/composer https://github.com/composer/composer/releases/download/$COMPOSER_VERSION/composer.phar && \
|
||||
chmod +x /usr/bin/composer
|
||||
|
||||
FROM base as dev
|
||||
|
||||
# Install and Configure XDebug
|
||||
RUN apk add --no-cache php82-pecl-xdebug
|
||||
COPY ./xdebug.ini /etc/php82/conf.d/60_xdebug.ini
|
||||
6
.dev/docker/php/xdebug.ini
Normal file
6
.dev/docker/php/xdebug.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
zend_extension=xdebug.so
|
||||
|
||||
[xdebug]
|
||||
xdebug.mode=debug,develop,coverage
|
||||
xdebug.client_host=host.docker.internal
|
||||
xdebug.output_dir=/castor/context/.dev/debug
|
||||
7
.dev/init
Executable file
7
.dev/init
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
mkdir -p .dev/coverage
|
||||
|
||||
composer install
|
||||
12
.gitattributes
vendored
Normal file
12
.gitattributes
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/.idea export-ignore
|
||||
/vendor export-ignore
|
||||
/tests export-ignore
|
||||
/.dev export-ignore
|
||||
/.php-cs-fixer.dist.php export-ignore
|
||||
/compose.yml export-ignore
|
||||
/composer.lock export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
/psalm.xml export-ignore
|
||||
/psalm-baseline.xml export-ignore
|
||||
/.gitignore export-ignore
|
||||
/README.md export-ignore
|
||||
39
.github/workflows/ci.yml
vendored
Normal file
39
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: "Chilean Rut CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- '**.php'
|
||||
- 'composer.json'
|
||||
- '.github/workflows/ci.yml'
|
||||
- '.github/workflows/php.yml'
|
||||
push:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- '**.php'
|
||||
- 'composer.json'
|
||||
- '.github/workflows/ci.yml'
|
||||
- '.github/workflows/php.yml'
|
||||
jobs:
|
||||
ci:
|
||||
name: 'CI'
|
||||
uses: ./.github/workflows/php.yml
|
||||
with:
|
||||
php-version: 8.2
|
||||
composer-cmd: install --ansi --no-interaction --no-progress --no-suggest --prefer-dist
|
||||
secrets: inherit
|
||||
|
||||
# Publishes code coverage report
|
||||
publish-coverage:
|
||||
name: "Publish Coverage"
|
||||
needs: ['ci']
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
51
.github/workflows/php.yml
vendored
Normal file
51
.github/workflows/php.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: "PHP Checks"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
php-version:
|
||||
description: 'The version of PHP to use'
|
||||
default: '8.2'
|
||||
type: string
|
||||
composer-cmd:
|
||||
description: 'Command to install dependencies'
|
||||
type: string
|
||||
default: 'install --ansi --no-interaction --no-progress --no-suggest --prefer-dist'
|
||||
|
||||
jobs:
|
||||
php-checks:
|
||||
name: "PHP Checks"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "pcov"
|
||||
php-version: "${{ inputs.php-version }}"
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
- name: "Cache dependencies"
|
||||
uses: "actions/cache@v2"
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: "${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}"
|
||||
restore-keys: "${{ runner.os }}-composer-"
|
||||
- name: "Install dependencies"
|
||||
run: "composer ${{ inputs.composer-cmd }}"
|
||||
- name: "Check Code Style"
|
||||
run: "composer fmt:check"
|
||||
- name: "Run Psalm"
|
||||
run: "composer psalm:gh"
|
||||
- name: "Run Test Suite"
|
||||
run: "composer test"
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: '.dev/coverage'
|
||||
122
.github/workflows/pr.yml
vendored
122
.github/workflows/pr.yml
vendored
@@ -1,122 +0,0 @@
|
||||
name: "Review PR"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
|
||||
env:
|
||||
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist"
|
||||
|
||||
jobs:
|
||||
|
||||
# This job ensures the coding standard is followed
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
operating-system:
|
||||
- "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "pcov"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
- name: "Cache dependencies"
|
||||
uses: "actions/cache@v2"
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: "composer-cache"
|
||||
restore-keys: "composer-cache"
|
||||
- name: "Install dependencies"
|
||||
run: "composer install ${{ env.COMPOSER_FLAGS }}"
|
||||
- name: "Coding Standard"
|
||||
run: "vendor/bin/php-cs-fixer fix --dry-run -vvv"
|
||||
|
||||
type-analysis:
|
||||
name: "Type Coverage"
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
operating-system:
|
||||
- "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "pcov"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
- name: "Cache dependencies"
|
||||
uses: "actions/cache@v2"
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: "composer-cache"
|
||||
restore-keys: "composer-cache"
|
||||
- name: "Install dependencies"
|
||||
run: "composer install ${{ env.COMPOSER_FLAGS }}"
|
||||
- name: "Run Psalm"
|
||||
run: "vendor/bin/psalm --output-format=github --shepherd --stats"
|
||||
continue-on-error: true
|
||||
|
||||
unit-test:
|
||||
name: "Unit Testing"
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
dependencies:
|
||||
- "lowest"
|
||||
- "highest"
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
operating-system:
|
||||
- "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: "actions/checkout@v2"
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "pcov"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
- name: "Cache dependencies"
|
||||
uses: "actions/cache@v2"
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: "composer-cache"
|
||||
restore-keys: "composer-cache"
|
||||
- name: "Install lowest dependencies"
|
||||
if: ${{ matrix.dependencies == 'lowest' }}
|
||||
run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest"
|
||||
- name: "Install highest dependencies"
|
||||
if: ${{ matrix.dependencies == 'highest' }}
|
||||
run: "composer install ${{ env.COMPOSER_FLAGS }}"
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit --testdox --coverage-text"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
vendor
|
||||
composer.lock
|
||||
.idea
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
.env
|
||||
compose.override.yml
|
||||
36
.php-cs-fixer.dist.php
Normal file
36
.php-cs-fixer.dist.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use PhpCsFixer\Fixer\FunctionNotation\NativeFunctionInvocationFixer;
|
||||
|
||||
$header = <<<EOF
|
||||
@project Chilean RUT
|
||||
@link https://github.com/mnavarrocarter/chilean-rut
|
||||
@package castor/log
|
||||
@author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
@license MIT
|
||||
@copyright 2024 Matias Navarro-Carter
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
EOF;
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setCacheFile('/tmp/php-cs-fixer')
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PhpCsFixer' => true,
|
||||
'declare_strict_types' => true,
|
||||
'header_comment' => ['header' => $header, 'comment_type' => 'PHPDoc'],
|
||||
'yoda_style' => false,
|
||||
'php_unit_internal_class' => false,
|
||||
'php_unit_test_class_requires_covers' => false,
|
||||
'native_function_invocation' => [
|
||||
'include' => [NativeFunctionInvocationFixer::SET_ALL],
|
||||
'scope' => 'namespaced',
|
||||
]
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(["src", "tests"])
|
||||
)
|
||||
;
|
||||
71
README.md
71
README.md
@@ -7,7 +7,7 @@ Además, posee dos *types* para `doctrine/dbal`.
|
||||
|
||||
## Instalación
|
||||
|
||||
Esta libería funciona con composer:
|
||||
Esta puede ser instalada mediante composer:
|
||||
|
||||
```
|
||||
composer require mnavarrocarter/chilean-rut
|
||||
@@ -18,8 +18,11 @@ composer require mnavarrocarter/chilean-rut
|
||||
### Parseando un Rut
|
||||
|
||||
La clase Rut es capaz de parsear cualquier tipo de rut sin importar el formato usando
|
||||
el método `Rut::parse()`. Confiadamente puedes poner el valor directamente de un
|
||||
formulario web y `parse` se encargara de sanitizar el string y ver si el RUT es valido.
|
||||
el método `Rut::parse()`. Confiadamente, puedes poner el valor directamente de un
|
||||
formulario web y `parse` se encargará de sanitizar el string y ver si el RUT es válido.
|
||||
|
||||
> NOTA: Un Rut se considera válido cuando su dígito verificador es algorítmicamente válido
|
||||
> para el número. Esta libreria no puede validar que el Rut existe realmente.
|
||||
|
||||
```php
|
||||
<?php
|
||||
@@ -29,50 +32,53 @@ use MNC\ChileanRut\Rut;
|
||||
$rut = Rut::parse('23.546.565-4');
|
||||
```
|
||||
|
||||
### Validando el Rut
|
||||
### Validando el RUT
|
||||
|
||||
> TLDR: Un objeto `Rut` siempre será valido.
|
||||
|
||||
Si tu RUT no es valido, el método `parse` lanzara una excepción de tipo
|
||||
`MNC\ChileanRut\InvalidRut`. Esto es para seguir buenos principios de *objects
|
||||
calisthenics*: un objeto de valor siempre se crea en un estado valido, y se mantiene
|
||||
valido a través de todo su ciclo de vida. No se permiten mutaciones que dejen el
|
||||
objeto en un estado invalido.
|
||||
Si tu RUT no es válido, el método `parse` lanzara una excepción de tipo
|
||||
`MNC\Rut\IsInvalid`. Esto es para seguir buenos principios de *objects
|
||||
calisthenics*: un objeto de valor siempre se crea en un estado válido, y se mantiene
|
||||
válido a través de todo su ciclo de vida. No se permiten mutaciones que dejen el
|
||||
objeto en un estado inválido.
|
||||
|
||||
Por esta razón el objecto `$rut` es completamente inmutable. Esto quiere decir
|
||||
Por esta razón el objeto `MNC\Rut\IsInvalid` es completamente inmutable. Esto quiere decir
|
||||
que una vez creado no puedes cambiar su estado interno: solo puedes leer información.
|
||||
Estos son los unicos métodos que puedes usar:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
|
||||
$rut = Rut::parse('23.546.565-4');
|
||||
|
||||
$rut->getNumber(); // (int) 23546565
|
||||
$rut->getVerifier(); // (string) 4
|
||||
$rut->number; // (int) 23546565
|
||||
$rut->verifier; // (MNC\Rut\Verifier::Four) 4
|
||||
```
|
||||
|
||||
### Formateando el Rut
|
||||
|
||||
Existen muchas formas distintas de formatear un rut y esta librería soporta muchas
|
||||
Existen muchas formas distintas de formatear un RUT y esta librería soporta muchas
|
||||
de ellas. El método format devuelve un objeto al cual puedes encadenar llamadas para
|
||||
formatear el rut y luego castearlo a un string. La interfaz es encadenable para que
|
||||
puedas combinar las opciones de formato como quieras.
|
||||
formatear el RUT y extraer su información de diversas maneras.
|
||||
|
||||
He aquí algunos ejemplos:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
|
||||
$rut = Rut::parse('23.546.565-4');
|
||||
|
||||
echo $rut->format()->hyphened(); // 23546565-4
|
||||
echo $rut->format()->dotted()->hyphened(); // 23.546.565-2
|
||||
echo $rut->format()->dotted()->hyphened()->obfuscated(); // **.***.565-2
|
||||
echo $rut->format()->obfuscated()->hyphened(); // *****565-2
|
||||
echo $rut->format()->obfuscated(); // *****5652
|
||||
echo $rut->toString(); // 235465654
|
||||
echo $rut->toSimple(); // 23546565-4
|
||||
echo $rut->toHuman(); // 23.546.565-4
|
||||
echo $rut->last(4); // 6565
|
||||
echo $rut->last(4, pad: '*'); // ****6565
|
||||
echo $rut->first(4); // 2354
|
||||
echo $rut->first(4, pad: '*'); // 2354****
|
||||
```
|
||||
|
||||
## Integraciones con Librerías de Terceros
|
||||
@@ -82,13 +88,13 @@ Esta librería provee dos [*Custom Types*](https://www.doctrine-project.org/proj
|
||||
para Doctrine, con el objetivo de que puedas mapear tus objetos `Rut` fácilmente
|
||||
a una base de datos relacional.
|
||||
|
||||
El `MNC\ChileanRut\Doctrine\RutType` mapeara tu RUT a una columna VARCHAR.
|
||||
`MNC\Rut\Doctrine\RutType` mapea tu RUT a una columna VARCHAR.
|
||||
El string se guarda con puntos y guion. Ex: `16.894.365-2`. Es una forma no tan
|
||||
eficiente de guardar los RUTS (en términos de espacio), pero ayuda mucho cuando
|
||||
se visualiza o exporta la base de datos a otras fuentes.
|
||||
|
||||
El `MNC\ChileanRut\Doctrine\NumericRutType` mapeara tu RUT a una columna INTEGER.
|
||||
El numero se guarda sin digito verificador y es recalculado cuando la columna
|
||||
`MNC\Rut\Doctrine\NumericRutType` mapea tu RUT a una columna INTEGER.
|
||||
El número se guarda sin dígito verificador y es recalculado cuando la columna
|
||||
es transformada a un valor PHP. Esta forma de guardar ruts es muy eficiente (en
|
||||
términos de espacio), pero cuesta comparar y leer los números si visualizas o
|
||||
exportas los registros en la base de datos.
|
||||
@@ -97,18 +103,15 @@ Por supuesto, puedes elegir el `Type` que más se ajuste a tus necesidades.
|
||||
|
||||
## FAQ
|
||||
|
||||
### ¿Cómo nació y por qué esta librería?
|
||||
### ¿Por qué esta librería?
|
||||
Esta librería nace de la necesidad de estandarizar una clase Rut común para todos
|
||||
mis proyectos PHP.
|
||||
|
||||
Si bien es cierto, hay muchas librerías con implementaciones de Rut chilenos en PHP,
|
||||
muchas de ellas tienen notorias deficiencias:
|
||||
|
||||
1. No están testeadas unitariamente,
|
||||
2. No tienen un buen diseño y sus apis tienen efectos secundarios.
|
||||
3. Están acopladas a un framework (Laravel Rut y otras hierbas)
|
||||
4. No proveen herramientas ni integraciones con librerías de terceros.
|
||||
|
||||
### ¿Por qué PHP 7.4?
|
||||
PHP 7.3 ya no tiene mucho tiempo de soporte y 8.0 esta ad portas de ser lanzado. Hay que
|
||||
empujar el ecosistema hacia adelante.
|
||||
1. No están testeadas unitariamente.
|
||||
2. No estan tipadas apropiadamente
|
||||
3. No tienen un buen diseño y sus apis tienen efectos secundarios.
|
||||
4. Están acopladas a un framework (Laravel Rut y otras hierbas)
|
||||
5. No proveen herramientas ni integraciones con librerías de terceros, como Doctrine.
|
||||
|
||||
27
compose.yml
Normal file
27
compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
name: "mnavarro"
|
||||
|
||||
services:
|
||||
|
||||
chilean-rut: &php
|
||||
image: mnavarro/chilean-rut:dev
|
||||
build:
|
||||
context: .dev/docker/php
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
args:
|
||||
UID: ${UID:-1000}
|
||||
user: ${UID:-1000}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
chilean-rut-init:
|
||||
condition: service_completed_successfully
|
||||
working_dir: /mnavarro/chilean-rut
|
||||
volumes:
|
||||
- ./:/mnavarro/chilean-rut
|
||||
command: ["php", "-S", "0.0.0.0:8000", "-t", ".dev/coverage"]
|
||||
|
||||
chilean-rut-init:
|
||||
<<: *php
|
||||
depends_on: []
|
||||
restart: no
|
||||
command: [".dev/init"]
|
||||
@@ -3,39 +3,46 @@
|
||||
"description": "PHP Rut Value Object with validation utilities, doctrine type, and other cool features.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matias Navarro Carter",
|
||||
"email": "mnavarro@option.cl"
|
||||
"name": "Matias Navarro-Carter",
|
||||
"email": "mnavarrocarter@gmail.com",
|
||||
"role": "Lead Maintainer"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"doctrine/dbal": "^2.10.1|^3.0",
|
||||
"symfony/var-dumper": "^5.1",
|
||||
"vimeo/psalm": "^4.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MNC\\ChileanRut\\": "src/"
|
||||
"MNC\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"MNC\\ChileanRut\\": "tests"
|
||||
"MNC\\": "tests"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"friendsofphp/php-cs-fixer": "^3.49",
|
||||
"vimeo/psalm": "^5.22",
|
||||
"doctrine/dbal": "^2.10.1|^3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "php-cs-fixer fix --allow-risky=yes",
|
||||
"pr": [
|
||||
"php-cs-fixer fix --dry-run -vvv",
|
||||
"phpunit --testdox --coverage-text",
|
||||
"psalm --stats"
|
||||
]
|
||||
"pr": ["@fmt", "@psalm", "@test"],
|
||||
"ci": ["@fmt:check", "@psalm:gh", "@test"],
|
||||
"fmt": "php-cs-fixer fix --diff --ansi",
|
||||
"fmt:check": "php-cs-fixer fix --dry-run --diff --ansi",
|
||||
"test": ["phpunit --colors"],
|
||||
"test:unit": "phpunit --colors --exclude-group=integration --exclude-group=e2e",
|
||||
"test:e2e": "phpunit --colors --group=e2e",
|
||||
"test:integration": "phpunit --colors --group=integration",
|
||||
"psalm": "psalm --no-cache --threads=5 --use-baseline",
|
||||
"psalm:gh": "psalm --no-cache --threads=5 --long-progress --output-format=github --use-baseline",
|
||||
"psalm:fix": "psalm --update-baseline",
|
||||
"psalm:allow": "psalm --set-baseline=psalm-baseline.xml"
|
||||
}
|
||||
}
|
||||
|
||||
4635
composer.lock
generated
Normal file
4635
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
phpunit.xml
Normal file
29
phpunit.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
executionOrder="depends,defects"
|
||||
requireCoverageMetadata="true"
|
||||
beStrictAboutCoverageMetadata="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
failOnRisky="true"
|
||||
cacheResultFile="/tmp/phpunit.result.cache"
|
||||
failOnWarning="true">
|
||||
<testsuites>
|
||||
<testsuite name="main">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
|
||||
<include>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<coverage>
|
||||
<report>
|
||||
<html outputDirectory=".dev/coverage"/>
|
||||
</report>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
8
psalm-baseline.xml
Normal file
8
psalm-baseline.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="5.22.2@d768d914152dbbf3486c36398802f74e80cfde48">
|
||||
<file src="src/Rut/Doctrine/NumericRutType.php">
|
||||
<ImplementedReturnTypeMismatch>
|
||||
<code><![CDATA[?Rut]]></code>
|
||||
</ImplementedReturnTypeMismatch>
|
||||
</file>
|
||||
</files>
|
||||
17
psalm.xml
Normal file
17
psalm.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
findUnusedBaselineEntry="true"
|
||||
findUnusedCode="false"
|
||||
errorBaseline="psalm-baseline.xml"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean Rut
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package mnavarrocarter/chilean-rut
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2020 Matias Navarro Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut;
|
||||
|
||||
/**
|
||||
* A FormattedRut encapsulates logic for formatting a Rut.
|
||||
*/
|
||||
class FormattedRut
|
||||
{
|
||||
private const HYPHENED = 1;
|
||||
private const DOTTED = 2;
|
||||
private const OBFUSCATED = 4;
|
||||
|
||||
private Rut $rut;
|
||||
private int $flags;
|
||||
|
||||
/**
|
||||
* FormattedRut constructor.
|
||||
*/
|
||||
public function __construct(Rut $rut)
|
||||
{
|
||||
$this->rut = $rut;
|
||||
$this->flags = 0;
|
||||
}
|
||||
|
||||
public function hyphened(): FormattedRut
|
||||
{
|
||||
return $this->add(self::HYPHENED);
|
||||
}
|
||||
|
||||
public function dotted(): FormattedRut
|
||||
{
|
||||
return $this->add(self::DOTTED);
|
||||
}
|
||||
|
||||
public function obfuscated(): FormattedRut
|
||||
{
|
||||
return $this->add(self::OBFUSCATED);
|
||||
}
|
||||
|
||||
private function add(int $flag): FormattedRut
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->flags += $flag;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$number = (string) $this->rut->getNumber();
|
||||
$verifier = $this->rut->getVerifier();
|
||||
if ($this->has(self::OBFUSCATED)) {
|
||||
$replace = str_repeat('*', strlen($number) - 3);
|
||||
$number = substr_replace($number, $replace, 0, -3);
|
||||
}
|
||||
|
||||
if ($this->has(self::DOTTED)) {
|
||||
$number = substr_replace($number, '.', -3, 0);
|
||||
$number = substr_replace($number, '.', -7, 0);
|
||||
}
|
||||
if ($this->has(self::HYPHENED)) {
|
||||
return $number.'-'.$verifier;
|
||||
}
|
||||
|
||||
return $number.$verifier;
|
||||
}
|
||||
|
||||
private function has(int $flag): bool
|
||||
{
|
||||
return ($this->flags & $flag) !== 0;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean Rut
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package mnavarrocarter/chilean-rut
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2020 Matias Navarro Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class InvalidRut.
|
||||
*/
|
||||
class InvalidRut extends InvalidArgumentException
|
||||
{
|
||||
public static function number(): InvalidRut
|
||||
{
|
||||
return new self('Rut number cannot be greater than 999.999.999 and smaller than zero');
|
||||
}
|
||||
|
||||
public static function digit(string $digit): InvalidRut
|
||||
{
|
||||
return new self(sprintf('The verifier digit "%s" is not valid', $digit));
|
||||
}
|
||||
}
|
||||
190
src/Rut.php
190
src/Rut.php
@@ -3,83 +3,91 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean Rut
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2020 Matias Navarro Carter
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut;
|
||||
namespace MNC;
|
||||
|
||||
use MNC\Rut\Verifier;
|
||||
|
||||
/**
|
||||
* Rut represents a the Chilean National ID Number.
|
||||
* Esta clase representa un RUT.
|
||||
*
|
||||
* All residents of Chile are uniquely identified by one of these and it is
|
||||
* mainly used for tax purposes.
|
||||
* Una vez creado, el RUT es siempre valido
|
||||
*/
|
||||
class Rut
|
||||
final readonly class Rut
|
||||
{
|
||||
private const VALID_VERIFIERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'K'];
|
||||
private const MAX_NUMBER = 999_999_999;
|
||||
private const MIN_NUMBER = 0;
|
||||
|
||||
private function __construct(
|
||||
public int $number,
|
||||
public Verifier $verifier
|
||||
) {}
|
||||
|
||||
/**
|
||||
* The actual RUT number.
|
||||
* Parsea un objeto RUT a partir de una cadena de texto.
|
||||
*
|
||||
* El RUT DEBE contener el digito verificador.
|
||||
*
|
||||
* Si no se cuenta con el verificador, el metodo create debe ser usado.
|
||||
*
|
||||
* @see Rut::create
|
||||
*
|
||||
* @throws Rut\IsInvalid si el RUT no es valido
|
||||
*/
|
||||
private int $number;
|
||||
/**
|
||||
* The RUT verifier digit.
|
||||
*/
|
||||
private string $verifier;
|
||||
|
||||
public static function parse(string $rut): Rut
|
||||
{
|
||||
// Remove space, dots and hyphens
|
||||
$rut = str_replace([' ', '.', '-'], '', $rut);
|
||||
$rut = \str_replace([' ', '.', '-'], '', $rut);
|
||||
|
||||
return new self(
|
||||
(int) substr($rut, 0, -1),
|
||||
substr($rut, -1)
|
||||
return self::create(
|
||||
(int) \substr($rut, 0, -1),
|
||||
Verifier::fromString(\substr($rut, -1))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a valid rut out of a number.
|
||||
*/
|
||||
public static function create(int $number): Rut
|
||||
{
|
||||
$verifier = self::calculateVerifier($number);
|
||||
|
||||
return new Rut($number, $verifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rut constructor.
|
||||
* Crea un objeto RUT.
|
||||
*
|
||||
* @throws InvalidRut if the verifier digit is invalid
|
||||
* El digito verificador es opcional. Cuando es recibido, es validado.
|
||||
*
|
||||
* Si el digito verificador no es provisto, es generado automáticamente.
|
||||
*
|
||||
* @throws Rut\IsInvalid si el Rut es invalido
|
||||
*/
|
||||
public function __construct(int $number, string $verifier)
|
||||
public static function create(int $number, ?Verifier $verifier = null): self
|
||||
{
|
||||
$this->number = $number;
|
||||
$this->verifier = strtoupper($verifier);
|
||||
$this->guard();
|
||||
}
|
||||
if ($number < self::MIN_NUMBER) {
|
||||
throw Rut\IsInvalid::numberTooSmall($number);
|
||||
}
|
||||
|
||||
public function getNumber(): int
|
||||
{
|
||||
return $this->number;
|
||||
}
|
||||
if ($number > self::MAX_NUMBER) {
|
||||
throw Rut\IsInvalid::numberTooBig($number);
|
||||
}
|
||||
|
||||
public function getVerifier(): string
|
||||
{
|
||||
return $this->verifier;
|
||||
$computed = self::computeVerifier($number);
|
||||
if ($verifier === null) {
|
||||
return new self($number, $computed);
|
||||
}
|
||||
|
||||
if ($computed !== $verifier) {
|
||||
throw Rut\IsInvalid::rut($number, $verifier);
|
||||
}
|
||||
|
||||
return new self($number, $verifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares whether a Rut is equal to another or not.
|
||||
* Compara si un RUT es igual a otro o no.
|
||||
*/
|
||||
public function equals(Rut $rut): bool
|
||||
{
|
||||
@@ -87,38 +95,71 @@ class Rut
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Rut to a string.
|
||||
* Retorna el RUT en formato "12345678K".
|
||||
*/
|
||||
public function format(): FormattedRut
|
||||
public function toString(): string
|
||||
{
|
||||
return new FormattedRut($this);
|
||||
}
|
||||
|
||||
private function guard(): void
|
||||
{
|
||||
// Check if rut is between zero and 999.999.999
|
||||
if ($this->number < 0 || $this->number > 999_999_999) {
|
||||
throw InvalidRut::number();
|
||||
}
|
||||
// Check if the verifier digit is in the range of valid ones
|
||||
if (!in_array($this->verifier, self::VALID_VERIFIERS, true)) {
|
||||
throw InvalidRut::digit($this->verifier);
|
||||
}
|
||||
// Check the verifier is algorithmically correct
|
||||
if ($this->verifier !== self::calculateVerifier($this->number)) {
|
||||
throw InvalidRut::digit($this->verifier);
|
||||
}
|
||||
return $this->number.$this->verifier->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a verifier digit from a number.
|
||||
* Retorna el RUT en formato "12345678-K".
|
||||
*/
|
||||
private static function calculateVerifier(int $number): string
|
||||
public function toSimple(): string
|
||||
{
|
||||
/** @var list<int> $sequence */
|
||||
$sequence = array_filter(array_reverse(str_split((string) $number)), function ($d) {
|
||||
return preg_match('/\d/', $d);
|
||||
});
|
||||
return $this->number.'-'.$this->verifier->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna el RUT en formato "12.345.678-K".
|
||||
*/
|
||||
public function toHuman(): string
|
||||
{
|
||||
return \number_format($this->number, 0, ',', '.').'-'.$this->verifier->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna los $n ultimos numeros del RUT.
|
||||
*
|
||||
* El digito verificador no es considerado.
|
||||
*/
|
||||
public function last(int $n, string $pad = ''): string
|
||||
{
|
||||
$number = (string) $this->number;
|
||||
$last = \substr($number, -$n);
|
||||
if ($pad !== '') {
|
||||
$last = \str_repeat($pad, \strlen($number) - $n).$last;
|
||||
}
|
||||
|
||||
return $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna los $n primeros numeros del RUT.
|
||||
*
|
||||
* El digito verificador no es considerado.
|
||||
*/
|
||||
public function first(int $n, string $pad = ''): string
|
||||
{
|
||||
$number = (string) $this->number;
|
||||
$first = \substr($number, 0, $n);
|
||||
if ($pad !== '') {
|
||||
$first .= \str_repeat($pad, \strlen($number) - $n);
|
||||
}
|
||||
|
||||
return $first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computa el digito verificador de un RUT a partir del numero.
|
||||
*/
|
||||
public static function computeVerifier(int $number): Verifier
|
||||
{
|
||||
$sequence = \array_reverse(\array_map(
|
||||
static fn (string $d): int => (int) $d,
|
||||
\str_split((string) $number)
|
||||
));
|
||||
|
||||
$x = 2;
|
||||
$s = 0;
|
||||
foreach ($sequence as $digit) {
|
||||
@@ -128,14 +169,7 @@ class Rut
|
||||
$s += $digit * $x;
|
||||
++$x;
|
||||
}
|
||||
$dv = 11 - ($s % 11);
|
||||
if ($dv === 10) {
|
||||
$dv = 'K';
|
||||
}
|
||||
if ($dv === 11) {
|
||||
$dv = '0';
|
||||
}
|
||||
|
||||
return (string) $dv;
|
||||
return Verifier::from(11 - ($s % 11));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,29 +3,26 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean Rut
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2020 Matias Navarro Carter
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut\Doctrine;
|
||||
namespace MNC\Rut\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\IntegerType;
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
|
||||
/**
|
||||
* Class NumericRutType.
|
||||
*
|
||||
* This type maps the rut to a number table and stores it without the verifier
|
||||
* digit. This is because the digit is derived from the number.
|
||||
* Mapea el Rut a una columna integer.
|
||||
*/
|
||||
class NumericRutType extends IntegerType
|
||||
{
|
||||
@@ -39,18 +36,16 @@ class NumericRutType extends IntegerType
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof Rut) {
|
||||
return (string) $value->getNumber();
|
||||
return (string) $value->number;
|
||||
}
|
||||
|
||||
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', Rut::class]);
|
||||
@@ -59,16 +54,21 @@ class NumericRutType extends IntegerType
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform): ?Rut
|
||||
{
|
||||
$value = parent::convertToPHPValue($value, $platform);
|
||||
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
return null;
|
||||
}
|
||||
|
||||
return Rut::create($value);
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform): true
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,29 +3,26 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean Rut
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2020 Matias Navarro Carter
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut\Doctrine;
|
||||
namespace MNC\Rut\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
|
||||
/**
|
||||
* Class RutTextType.
|
||||
*
|
||||
* This type maps the rut to a string and stores it with the verifier number,
|
||||
* including dots and the hyphen.
|
||||
* Mapea el RUT a una columna VARCHAR.
|
||||
*/
|
||||
class RutType extends StringType
|
||||
{
|
||||
@@ -39,18 +36,16 @@ class RutType extends StringType
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof Rut) {
|
||||
return (string) $value->format()->hyphened()->dotted();
|
||||
return $value->toHuman();
|
||||
}
|
||||
|
||||
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', Rut::class]);
|
||||
@@ -59,20 +54,23 @@ class RutType extends StringType
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform): ?Rut
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
if (\is_string($value)) {
|
||||
return Rut::parse($value);
|
||||
}
|
||||
|
||||
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string']);
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform): true
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
53
src/Rut/IsInvalid.php
Normal file
53
src/Rut/IsInvalid.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\Rut;
|
||||
|
||||
class IsInvalid extends \InvalidArgumentException
|
||||
{
|
||||
public static function numberTooBig(int $number): IsInvalid
|
||||
{
|
||||
return new self(\sprintf(
|
||||
'El RUT numero %d es mayor a 99.999.999',
|
||||
$number
|
||||
));
|
||||
}
|
||||
|
||||
public static function numberTooSmall(int $number): IsInvalid
|
||||
{
|
||||
return new self(\sprintf(
|
||||
'El RUT numero %d es menor a cero',
|
||||
$number
|
||||
));
|
||||
}
|
||||
|
||||
public static function rut(int $number, Verifier $verifier): IsInvalid
|
||||
{
|
||||
return new self(\sprintf(
|
||||
'El digito verificador %s no es valido para el rut %d',
|
||||
$verifier->toString(),
|
||||
$number
|
||||
));
|
||||
}
|
||||
|
||||
public static function verifier(string $verifier): IsInvalid
|
||||
{
|
||||
return new self(\sprintf(
|
||||
'Encontrado un digito verificador invalido con valor %s',
|
||||
$verifier,
|
||||
));
|
||||
}
|
||||
}
|
||||
78
src/Rut/Verifier.php
Normal file
78
src/Rut/Verifier.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\Rut;
|
||||
|
||||
/**
|
||||
* Representa el digito verificador de un RUT.
|
||||
*
|
||||
* Internamente, se guarda el modulo del RUT, y no su valor como texto.
|
||||
*/
|
||||
enum Verifier: int
|
||||
{
|
||||
case One = 1;
|
||||
case Two = 2;
|
||||
case Three = 3;
|
||||
case Four = 4;
|
||||
case Five = 5;
|
||||
case Six = 6;
|
||||
case Seven = 7;
|
||||
case Eight = 8;
|
||||
case Nine = 9;
|
||||
case K = 10;
|
||||
case Zero = 11;
|
||||
|
||||
/**
|
||||
* @throws IsInvalid si el verificador es invalido
|
||||
*/
|
||||
public static function fromString(string $v): Verifier
|
||||
{
|
||||
return match ($v) {
|
||||
'1' => self::One,
|
||||
'2' => self::Two,
|
||||
'3' => self::Three,
|
||||
'4' => self::Four,
|
||||
'5' => self::Five,
|
||||
'6' => self::Six,
|
||||
'7' => self::Seven,
|
||||
'8' => self::Eight,
|
||||
'9' => self::Nine,
|
||||
'0' => self::Zero,
|
||||
'K' => self::K,
|
||||
default => throw IsInvalid::verifier($v)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la representacion textual del digito verificador.
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::One => '1',
|
||||
self::Two => '2',
|
||||
self::Three => '3',
|
||||
self::Four => '4',
|
||||
self::Five => '5',
|
||||
self::Six => '6',
|
||||
self::Seven => '7',
|
||||
self::Eight => '8',
|
||||
self::Nine => '9',
|
||||
self::K => 'K',
|
||||
self::Zero => '0',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Class FormattedRutTest
|
||||
* @package MNC\ChileanRut
|
||||
*/
|
||||
class FormattedRutTest extends TestCase
|
||||
{
|
||||
public function testItFormatsHyphened(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->hyphened();
|
||||
self::assertSame('16894365-2', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsDotted(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->dotted();
|
||||
self::assertSame('16.894.3652', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsHyphenedAndDotted(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->hyphened()->dotted();
|
||||
self::assertSame('16.894.365-2', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsObfuscated(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->obfuscated();
|
||||
self::assertSame('*****3652', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsObfuscatedAndHyphened(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->hyphened()->obfuscated();
|
||||
self::assertSame('*****365-2', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsObfuscatedAndDotted(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->obfuscated()->dotted();
|
||||
self::assertSame('**.***.3652', $formatted);
|
||||
}
|
||||
|
||||
public function testItFormatsWithAll(): void
|
||||
{
|
||||
$formatted = (string) Rut::parse('168943652')->format()->hyphened()->dotted()->obfuscated();
|
||||
self::assertSame('**.***.365-2', $formatted);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Doctrine;
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\Rut\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(NumericRutType::class)]
|
||||
#[CoversClass(Rut::class)]
|
||||
#[CoversClass(Rut\Verifier::class)]
|
||||
class NumericRutTypeTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
@@ -54,4 +73,11 @@ class NumericRutTypeTest extends TestCase
|
||||
$this->expectException(ConversionException::class);
|
||||
$type->convertToDatabaseValue(new \DateTime(), $platform);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_returns_true_for_comment(): void
|
||||
{
|
||||
$platform = $this->createMock(AbstractPlatform::class);
|
||||
$this->assertTrue(Type::getType(NumericRutType::NAME)->requiresSQLCommentHint($platform));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Doctrine;
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\Rut\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use MNC\ChileanRut\Rut;
|
||||
use MNC\Rut;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(RutType::class)]
|
||||
#[CoversClass(Rut::class)]
|
||||
#[CoversClass(Rut\Verifier::class)]
|
||||
class RutTypeTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
@@ -62,4 +81,11 @@ class RutTypeTest extends TestCase
|
||||
$this->expectException(ConversionException::class);
|
||||
$type->convertToDatabaseValue(new \DateTime(), $platform);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_returns_true_for_comment(): void
|
||||
{
|
||||
$platform = $this->createMock(AbstractPlatform::class);
|
||||
$this->assertTrue(Type::getType(RutType::NAME)->requiresSQLCommentHint($platform));
|
||||
}
|
||||
}
|
||||
@@ -1,87 +1,107 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MNC\ChileanRut library.
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @project Chilean RUT
|
||||
* @link https://github.com/mnavarrocarter/chilean-rut
|
||||
* @package castor/log
|
||||
* @author Matias Navarro-Carter mnavarrocarter@gmail.com
|
||||
* @license MIT
|
||||
* @copyright 2024 Matias Navarro-Carter
|
||||
*
|
||||
* (c) Matías Navarro Carter <mnavarrocarter@gmail.com>
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MNC\ChileanRut;
|
||||
namespace MNC;
|
||||
|
||||
use MNC\Rut\IsInvalid;
|
||||
use MNC\Rut\Verifier;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Class RutTest
|
||||
* @package MNC\ChileanRut\Tests\Rut
|
||||
*/
|
||||
#[CoversClass(Rut::class)]
|
||||
#[CoversClass(IsInvalid::class)]
|
||||
#[CoversClass(Verifier::class)]
|
||||
class RutTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getRutDataset
|
||||
* @param string $raw
|
||||
* @param int $expectedNumber
|
||||
* @param string $expectedVerifier
|
||||
*/
|
||||
public function testItParsesRuts(string $raw, int $expectedNumber, string $expectedVerifier): void
|
||||
#[Test]
|
||||
#[DataProvider('getParseData')]
|
||||
public function it_parses_ruts(string $raw, int $expectedNumber, Verifier $expectedVerifier): void
|
||||
{
|
||||
$rut = Rut::parse($raw);
|
||||
self::assertSame($expectedNumber, $rut->getNumber());
|
||||
self::assertSame($expectedVerifier, $rut->getVerifier());
|
||||
$this->assertSame($expectedNumber, $rut->number);
|
||||
$this->assertSame($expectedVerifier, $rut->verifier);
|
||||
}
|
||||
|
||||
public function testItDetectsOutOfRangeVerifier(): void
|
||||
#[Test]
|
||||
#[DataProvider('getParseWithErrorData')]
|
||||
public function it_parses_with_error(string $raw, string $expectedError): void
|
||||
{
|
||||
$this->expectException(InvalidRut::class);
|
||||
Rut::parse('16894365F');
|
||||
$this->expectException(IsInvalid::class);
|
||||
$this->expectExceptionMessage($expectedError);
|
||||
Rut::parse($raw);
|
||||
}
|
||||
|
||||
public function testItDetectsInvalidVerifier(): void
|
||||
#[Test]
|
||||
public function it_cannot_be_negative(): void
|
||||
{
|
||||
$this->expectException(InvalidRut::class);
|
||||
Rut::parse('16894365K');
|
||||
$this->expectException(IsInvalid::class);
|
||||
$this->expectExceptionMessage('El RUT numero -22224525 es menor a cero');
|
||||
Rut::create(-22_224_525);
|
||||
}
|
||||
|
||||
public function testItDetectsRutTooBig(): void
|
||||
{
|
||||
$this->expectException(InvalidRut::class);
|
||||
Rut::create(3_355_535_353);
|
||||
}
|
||||
|
||||
public function testItComparesToEqual(): void
|
||||
#[Test]
|
||||
public function it_checks_for_equality(): void
|
||||
{
|
||||
$rut1 = Rut::parse('168943652');
|
||||
$rut2 = Rut::parse('16.894.365-2');
|
||||
$rut3 = Rut::create(22_224_525);
|
||||
self::assertTrue($rut1->equals($rut2));
|
||||
self::assertFalse($rut1->equals($rut3));
|
||||
$this->assertTrue($rut1->equals($rut2));
|
||||
$this->assertFalse($rut1->equals($rut3));
|
||||
}
|
||||
|
||||
public static function testItCreatesARut(): void
|
||||
#[Test]
|
||||
public function it_creates_with_no_verifier(): void
|
||||
{
|
||||
$rut = Rut::create(22_457_309);
|
||||
self::assertSame(22_457_309, $rut->getNumber());
|
||||
$this->assertSame(22_457_309, $rut->number);
|
||||
}
|
||||
|
||||
public static function testItCanFormatRut(): void
|
||||
#[Test]
|
||||
public function it_formats(): void
|
||||
{
|
||||
$rut = (string) Rut::create(22_457_309)->format();
|
||||
self::assertSame('22457309K', $rut);
|
||||
$rut = Rut::create(22_457_309);
|
||||
$this->assertSame('22457309K', $rut->toString());
|
||||
$this->assertSame('22457309-K', $rut->toSimple());
|
||||
$this->assertSame('22.457.309-K', $rut->toHuman());
|
||||
$this->assertSame('7309', $rut->last(4));
|
||||
$this->assertSame('****7309', $rut->last(4, '*'));
|
||||
$this->assertSame('2245', $rut->first(4));
|
||||
$this->assertSame('2245****', $rut->first(4, '*'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function getRutDataset(): array
|
||||
public static function getParseData(): array
|
||||
{
|
||||
return [
|
||||
['16.894.365-2', 16_894_365, '2'],
|
||||
['24 736.7322', 24_736_732, '2'],
|
||||
[' 24 232.. 442 -- 0', 24_232_442, '0'],
|
||||
['35323325', 3_532_332, '5'],
|
||||
['22.457.309K', 22_457_309, 'K'],
|
||||
['15450088K', 15450088, 'K']
|
||||
['16.894.365-2', 16_894_365, Verifier::Two],
|
||||
['24 736.7322', 24_736_732, Verifier::Two],
|
||||
[' 24 232.. 442 -- 0', 24_232_442, Verifier::Zero],
|
||||
['35323325', 3_532_332, Verifier::Five],
|
||||
['22.457.309K', 22_457_309, Verifier::K],
|
||||
['15450088K', 15_450_088, Verifier::K],
|
||||
];
|
||||
}
|
||||
|
||||
public static function getParseWithErrorData(): array
|
||||
{
|
||||
return [
|
||||
['212321312321-1', 'El RUT numero 212321312321 es mayor a 99.999.999'],
|
||||
['23.232.123-K', 'El digito verificador K no es valido para el rut 23232123'],
|
||||
['12.2324.232-P', 'Encontrado un digito verificador invalido con valor P'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user