[BC] New Version

This version simplifies the api a lot, eliminating unnecesary complexity and reducing the library to a few classes only.
This commit is contained in:
2020-11-20 11:51:33 +00:00
parent 0ad94c4944
commit b4f4fe25b9
32 changed files with 847 additions and 1061 deletions

122
.github/workflows/pr.yml vendored Normal file
View File

@@ -0,0 +1,122 @@
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 --ignore-platform-reqs"
- name: "Install highest dependencies"
if: ${{ matrix.dependencies == 'highest' }}
run: "composer install ${{ env.COMPOSER_FLAGS }} --ignore-platform-reqs"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit --testdox --coverage-text"

2
.gitignore vendored
View File

@@ -2,4 +2,4 @@ vendor
composer.lock composer.lock
.idea .idea
.php_cs.cache .php_cs.cache
build .phpunit.result.cache

View File

@@ -1,26 +0,0 @@
image: alpine:3.8
# This configures the whole environment
before_script:
# Install git, make, zip and php + extensions
- ping -c 3 alpinelinux.org
- apk add --no-cache git curl make gcc unzip php7 php7-mcrypt php7-intl php7-gettext php7-ctype php7-curl php7-dom php7-fileinfo php7-ftp php7-iconv php7-json php7-mbstring php7-mysqlnd php7-openssl php7-pdo php7-pdo_sqlite php7-pear php7-phar php7-posix php7-session php7-simplexml php7-sqlite3 php7-tokenizer php7-xml php7-xmlreader php7-xmlwriter php7-zlib php7-xdebug
# Enable xDebug for coverage report
- echo "zend_extension=/usr/lib/php7/modules/xdebug.so" >> /etc/php7/php.ini
- echo "xdebug.coverage_enable=1" >> /etc/php7/php.ini
# Display php version
- php -v
# Install and run composer
- curl --location --output /usr/local/bin/composer https://getcomposer.org/download/1.7.3/composer.phar
- chmod +x /usr/local/bin/composer
- composer install --ansi
test:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/
script:
- php vendor/bin/phpunit --coverage-text --colors=never
only:
- branches

View File

@@ -1,32 +1,35 @@
<?php <?php
$header = <<<EOF $header = <<<EOF
This file is part of the MNC\ChileanRut library. @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
(c) Matías Navarro Carter <mnavarrocarter@gmail.com>
For the full copyright and license information, please view the LICENSE For the full copyright and license information, please view the LICENSE
file that was distributed with this source code. file that was distributed with this source code.
EOF; EOF;
return PhpCsFixer\Config::create() return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([ ->setRules([
'@Symfony' => true, '@Symfony' => true,
'array_syntax' => ['syntax' => 'short'], 'array_syntax' => ['syntax' => 'short'],
'combine_consecutive_unsets' => true, 'declare_strict_types' => true,
'header_comment' => ['header' => $header], 'strict_comparison' => true,
'linebreak_after_opening_tag' => true, 'phpdoc_no_empty_return' => true,
'no_php4_constructor' => true, 'header_comment' => ['header' => $header, 'comment_type' => 'PHPDoc'],
'no_useless_else' => true, 'yoda_style' => [
'ordered_class_elements' => true, 'equal' => false,
'ordered_imports' => true, 'identical' => false,
'php_unit_construct' => true, 'less_and_greater' => false,
'php_unit_strict' => true, 'always_move_variable' => true
'phpdoc_no_empty_return' => false, ],
]) ])
->setUsingCache(true)
->setRiskyAllowed(true)
->setFinder( ->setFinder(
PhpCsFixer\Finder::create() PhpCsFixer\Finder::create()
->in(__DIR__) ->in(__DIR__.'/src')
) )
; ;

View File

@@ -1,18 +0,0 @@
# CHANGELOG
## v1.0.0 (08.08.2018)
- Create Initial Classes (Rut, Validator, ChainRutValidator)
- Tested Rut
- Tested SimpleRutValidator
- Tested Symfony Form Type
## v2.0.0 (12.10.2018)
This is a major version because api changes and class renaming took place.
Also, all features were properly tested with CI/CD pipelines.
- Changed ChainRutValidator constructor signature. Now uses argument spreading.
- Added tests for ChainRutValidator
- SimpleRutValidator renamed to Module11RutValidator
- Now class Rut always validates itself with the SimpleRutValidator if no validator
is passed. This ensures object consistency.
- Added test coverage reports, code quality and CI testing

View File

@@ -1,4 +1,4 @@
Copyright (c) 2018 Matías Navarro Carter Copyright (c) 2020 Matías Navarro Carter
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,8 +0,0 @@
coverage:
vendor/bin/phpunit --coverage-text
cs:
vendor/bin/php-cs-fixer fix --diff --verbose
commit: cs coverage
git add . && git commit

218
README.md
View File

@@ -1,13 +1,9 @@
Rut Chileno Rut Chileno
=========== ===========
Esta librería implementa una clase Rut como un *value object* inmutable, incluyendo Esta librería implementa una clase Rut como un sencillo *value object* inmutable.
una api de validación flexible y extendible.
Además, posee un validador para `symfony/validator`, un *form type* para `symfony/form` Además, posee dos *types* para `doctrine/dbal`.
y un *type* para `doctrine/dbal`.
Sólo es compatible con PHP 7.1 o superior.
## Instalación ## Instalación
@@ -18,195 +14,101 @@ composer require mnavarrocarter/chilean-rut
``` ```
## Uso ## Uso
Simplemente instancia una nueva clase con un rut en cualquier formato:
### 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.
```php ```php
<?php <?php
use MNC\ChileanRut\Rut; use MNC\ChileanRut\Rut;
$rut = new Rut('23.546.565-4'); $rut = Rut::parse('23.546.565-4');
// Si prefieres, puedes usar el factory method
$rut = Rut::fromString('23546565-4');
``` ```
Por defecto, la clase Rut se valida usando el `Module11RutValidator` si no se pasa ### Validando el Rut
un validador personalizado al momento de instanciación. Esto es para asegurar la
integridad del objeto.
Si quieres, por alguna extraña razón, deshacerte de esa validación, puedes crear > TLDR: Un objeto `Rut` siempre será valido.
un `AlwaysValidRutValidator` implementando la interfaz `RutValidator`. El método
validate estaría en blanco, lo que haría pasar la validación sin problema.
```php Si tu RUT no es valido, el método `parse` lanzara una excepción de tipo
<?php `MNC\ChileanRut\InvalidRut`. Esto es para seguir buenos principios de *objects
use MNC\ChileanRut\Rut; calisthenics*: un objeto de valor siempre se crea en un estado valido, y se mantiene
use MNC\ChileanRut\Validator\RutValidator; valido a través de todo su ciclo de vida. No se permiten mutaciones que dejen el
objeto en un estado invalido.
class AlwaysValidRutValidator implements RutValidator Por esta razón el objecto `$rut` es completamente inmutable. Esto quiere decir
{ que una vez creado no puedes cambiar su estado interno: solo puedes leer información.
public function validate(Rut $rut) : void Estos son los unicos métodos que puedes usar:
{
// Vacío a propósito
}
}
// Asi, la validación pasa sin problema.
$rut = new Rut('23.546.565-4', new AlwaysValidRutValidator());
```
### Validación Personalizada de Rut
El `Module11RutValidator` no es más que la implementación del validador clásico de Rut,
el algoritmo de módulo 11. Esto verifica que un Rut es algoritmicamente correcto, pero
no valida que es real.
Por ello, proveemos la interfaz `RutValidator`. Con ella, puedes crear tus propias
reglas de validación, como llamar a un web service o consultar una base de datos
para verificar si un Rut es real o no. Te recomiendo mirar la interfaz para
implementarla correctamente.
De todas formas, aquí hay un ejemplo que va a buscar un rut a un web service.
```php
<?php
use MNC\ChileanRut\Validator\RutValidator;
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Exception\InvalidRutException;
use App\Rut\WebServiceRutChecker;
class MyCustomRutValidator implements RutValidator
{
private $rutChecker;
/**
* MyCustomRutValidator constructor.
* @param WebServiceRutChecker $rutChecker
*/
public function __construct(WebServiceRutChecker $rutChecker)
{
$this->rutChecker = $rutChecker;
}
/**
* @param Rut $rut
*/
public function validate(Rut $rut) : void
{
// Por debajo, esta clase ficticia haría una llamada a un web service preguntando
// si el Rut existe.
if ($this->rutChecker->doesRutExist($rut->format())) {
return;
}
throw new InvalidRutException($rut, 'This rut does not exist');
}
}
```
> NOTA: La implementación de cualquier validador DEBE arrojar un InvalidRutException cuando
el Rut no es válido. De lo contrario, el Rut se toma como válido.
### Usando múltiples validadores
Proveemos un `ChainRutValidator` que puedes usar para validar un rut contra múltiples
validadores. Esto permite ejecutar cadenas de validación, como ver primero si un rut es
válido algorítmicamente antes de verificarlo contra un web service.
Usarlo es simple:
```php
<?php
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Validator\ChainRutValidator;
use MNC\ChileanRut\Validator\SimpleRutValidator;
use App\Rut\DatabaseRutValidator;
$chainValidator = new ChainRutValidator(
new SimpleRutValidator(),
new DatabaseRutValidator()
);
$rut = new Rut('14.245.245-2');
$chainValidator->validate($rut);
```
### Formateando Ruts a String
Una vez creado el objeto Rut, puedes formatearlo a string en el formato que tu quieras.
Esto se hace a través del método format y cómo parámetro acepta el valor
de una de las constantes `FORMAT_` de la clase Rut.
```php ```php
<?php <?php
use MNC\ChileanRut\Rut; use MNC\ChileanRut\Rut;
$rut = new Rut('34244223-4'); $rut = Rut::parse('23.546.565-4');
echo $rut->format(Rut::FORMAT_CLEAR); // Va a imprimir 342442234 $rut->getNumber(); // (int) 23546565
echo $rut->format(Rut::FORMAT_READABLE); // Va a imprimir 34.244.223-4 $rut->getVerifier(); // (string) 4
echo $rut->format(Rut::FORMAT_HYPHENED); // Va a imprimir 34244223-4
echo $rut->format(Rut::FORMAT_HIDDEN); // Va a imprimir 34.***.***-4
``` ```
### Utilidades ### Formateando el Rut
Esta librería provee una clase llamada `CorrelativeUtils` que tiene algunas utilidades
interesantes. Posee tres métodos: 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.
```php ```php
<?php <?php
use MNC\ChileanRut\Util\CorrelativeUtils; use MNC\ChileanRut\Rut;
// Este método devuelve el digito verificador de un correlativo. $rut = Rut::parse('23.546.565-4');
CorrelativeUtils::findVerifierDigit('34525252');
// Este método devuelve una instancia de Rut válida, sólo con el correlativo. echo $rut->format()->hyphened(); // 23546565-4
CorrelativeUtils::createValidRutOnlyFromCorrelative('34525252'); echo $rut->format()->dotted()->hyphened(); // 23.546.565-2
echo $rut->format()->dotted()->hyphened()->obfuscated(); // **.***.565-2
// Este método devuelve instancia de Rut autogenerada algoritmicamente válida. echo $rut->format()->obfuscated()->hyphened(); // *****565-2
CorrelativeUtils::autoGenerateValidRut(); echo $rut->format()->obfuscated(); // *****5652
``` ```
## Integraciones con Liberías de Terceros ## Integraciones con Librerías de Terceros
### Doctrine DBAL ### Doctrine DBAL
Esta libería provee un custom type para doctrine llamado `RutType`. Puedes registrarla Esta librería provee dos [*Custom Types*](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/custom-mapping-types.html)
en el Dbal para usarla en tus mappings de doctrine y automáticamente mappear tu para Doctrine, con el objetivo de que puedas mapear tus objetos `Rut` fácilmente
el valor de tu db a un objeto rut. a una base de datos relacional.
### Symfony Validator El `MNC\ChileanRut\Doctrine\RutType` mapeara tu RUT a una columna VARCHAR.
Además, esta libería cuenta con un validador para Symfony Validator, que te El string se guarda con puntos y guion. Ex: `16.894.365-2`. Es una forma no tan
permite beneficiarte de las anotaciones del componente de validación de Symfony. eficiente de guardar los RUTS (en términos de espacio), pero ayuda mucho cuando
Como dependencia opcional necesita una instancia de `RutValidator`. Si ninguna es proveída, se visualiza o exporta la base de datos a otras fuentes.
se utiliza el `SimpleRutValidator`. Solo puedes usar el validador contra una instancia de `Rut`.
### Symfony Form Type El `MNC\ChileanRut\Doctrine\NumericRutType` mapeara tu RUT a una columna INTEGER.
Por último, esta libería cuenta con un Symfony Form Type que puedes añadir en tus El numero se guarda sin digito verificador y es recalculado cuando la columna
formularios HTML, para que puedas autoinstanciar la clase y poner lógica de es transformada a un valor PHP. Esta forma de guardar ruts es muy eficiente (en
validación en ella sin problema, y añadirla a tus otros tipos. términos de espacio), pero cuesta comparar y leer los números si visualizas o
exportas los registros en la base de datos.
Por supuesto, puedes elegir el `Type` que más se ajuste a tus necesidades.
## FAQ ## FAQ
### ¿Cómo nació y por qué esta librería? ### ¿Cómo nació y por qué esta librería?
Esta libería nace de la necesidad de estandarizar una clase Rut común para todos mis proyectos Esta librería nace de la necesidad de estandarizar una clase Rut común para todos
PHP. mis proyectos PHP.
Si bien es cierto, hay muchas liberías con implementaciones de Rut chilenos en PHP,
Si bien es cierto, hay muchas librerías con implementaciones de Rut chilenos en PHP,
muchas de ellas tienen notorias deficiencias: muchas de ellas tienen notorias deficiencias:
1. No están testeadas unitariamente, 1. No están testeadas unitariamente,
2. No separan bien responsabilidades, como la lógica de validación con la de instanciación. 2. No tienen un buen diseño y sus apis tienen efectos secundarios.
3. No proveen validación extensible por medio de interfaces, limitando la validación 3. Están acopladas a un framework (Laravel Rut y otras hierbas)
solo a ser algorítmica. 4. No proveen herramientas ni integraciones con librerías de terceros.
4. Están acopladas a un framework
5. No proveen herramientas ni integraciones con librerías de terceros.
### ¿Por qué PHP 7.1?
El fin del soporte de PHP 5.6 será a fines de 2018. PHP 7.1 es una de las últimas
versiones estables, y me beneficio mucho de su sistema de tipado estricto en esta libería.
### ¿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.

View File

@@ -11,15 +11,14 @@
], ],
"minimum-stability": "stable", "minimum-stability": "stable",
"require": { "require": {
"php": "^7.1" "php": "^7.4"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.12", "friendsofphp/php-cs-fixer": "^2.16",
"phpunit/phpunit": "^7.3", "phpunit/phpunit": "^9.0",
"doctrine/dbal": "^2.5", "doctrine/dbal": "^2.5",
"symfony/form": "^3.4|^4.0", "symfony/var-dumper": "^5.1",
"symfony/validator": "^3.4|^4.0", "vimeo/psalm": "^4.2"
"symfony/var-dumper": "^4.1"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -28,7 +27,15 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"MNC\\ChileanRut\\Tests\\": "tests" "MNC\\ChileanRut\\": "tests"
} }
},
"scripts": {
"lint": "php-cs-fixer fix --allow-risky=yes",
"pr": [
"php-cs-fixer fix --dry-run -vvv",
"phpunit --testdox --coverage-text",
"psalm --stats"
]
} }
} }

View File

@@ -1,25 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false"
<phpunit backupGlobals="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true"
backupStaticAttributes="false" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false"
colors="true" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
convertErrorsToExceptions="true" <coverage>
convertNoticesToExceptions="true" <include>
convertWarningsToExceptions="true" <directory>./src</directory>
processIsolation="false" </include>
stopOnFailure="false" </coverage>
bootstrap="vendor/autoload.php"
>
<testsuites> <testsuites>
<testsuite name="Test Suite"> <testsuite name="Test Suite">
<directory suffix="Test.php">./tests</directory> <directory suffix="Test.php">./tests</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit> </phpunit>

54
psalm.xml.dist Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0"?>
<psalm
totallyTyped="false"
resolveFromConfigFile="true"
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"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<LessSpecificReturnType errorLevel="info" />
<!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
<DeprecatedMethod errorLevel="info" />
<DeprecatedProperty errorLevel="info" />
<DeprecatedClass errorLevel="info" />
<DeprecatedConstant errorLevel="info" />
<DeprecatedFunction errorLevel="info" />
<DeprecatedInterface errorLevel="info" />
<DeprecatedTrait errorLevel="info" />
<InternalMethod errorLevel="info" />
<InternalProperty errorLevel="info" />
<InternalClass errorLevel="info" />
<MissingClosureReturnType errorLevel="info" />
<MissingReturnType errorLevel="info" />
<MissingPropertyType errorLevel="info" />
<InvalidDocblock errorLevel="info" />
<PropertyNotSetInConstructor errorLevel="info" />
<MissingConstructor errorLevel="info" />
<MissingClosureParamType errorLevel="info" />
<MissingParamType errorLevel="info" />
<RedundantCondition errorLevel="info" />
<DocblockTypeContradiction errorLevel="info" />
<RedundantConditionGivenDocblockType errorLevel="info" />
<UnresolvableInclude errorLevel="info" />
<RawObjectIteration errorLevel="info" />
<InvalidStringClass errorLevel="info" />
</issueHandlers>
</psalm>

View File

@@ -1,75 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Bridge\Symfony\Form;
use MNC\ChileanRut\Rut;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class RutType.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class RutType extends AbstractType implements DataTransformerInterface
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->addModelTransformer($this);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'compound' => false,
'data_class' => Rut::class,
'empty_data' => function (FormInterface $form) {
return new Rut($form->getData());
},
]);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix(): ?string
{
return 'rut';
}
/**
* @param mixed $value
*
* @return mixed|string
*/
public function transform($value)
{
if ($value instanceof Rut) {
return $value->format(Rut::FORMAT_READABLE);
}
}
/**
* @param mixed $value
*
* @return mixed|Rut
*/
public function reverseTransform($value)
{
return new Rut((string) $value);
}
}

View File

@@ -1,21 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Bridge\Symfony\Validator;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class IsValidRut extends Constraint
{
public $message = '"{{value}}" is not a valid Rut.';
}

View File

@@ -1,65 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Bridge\Symfony\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Validator\Module11RutValidator;
use MNC\ChileanRut\Validator\RutValidator;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Class IsValidRutValidator.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class IsValidRutValidator extends ConstraintValidator
{
/**
* @var RutValidator
*/
private $validator;
/**
* IsValidRutValidator constructor.
*
* @param RutValidator|null $validator
*/
public function __construct(RutValidator $validator = null)
{
$this->validator = $validator ?? new Module11RutValidator();
}
/**
* @param mixed $value
* @param Constraint $constraint
*/
public function validate($value, Constraint $constraint): void
{
if (null === $value || '' === $value) {
return;
}
if (!$value instanceof Rut) {
throw new UnexpectedTypeException($value, Rut::class);
}
try {
$this->validator->validate($value);
} catch (InvalidRutException $exception) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $value->format(Rut::FORMAT_CLEAR))
->addViolation();
}
}
}

View File

@@ -0,0 +1,74 @@
<?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\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\IntegerType;
use MNC\ChileanRut\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.
*/
class NumericRutType extends IntegerType
{
public const NAME = 'numeric-rut';
public function getName(): string
{
return self::NAME;
}
/**
* @param mixed $value
*
* @return mixed
*
* @throws ConversionException
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return $value;
}
if ($value instanceof Rut) {
return (string) $value->getNumber();
}
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', Rut::class]);
}
/**
* @param mixed $value
*
* @return mixed
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
$value = parent::convertToPHPValue($value, $platform);
if ($value === null) {
return $value;
}
return Rut::create($value);
}
}

View File

@@ -1,14 +1,20 @@
<?php <?php
/* declare(strict_types=1);
* This file is part of the MNC\ChileanRut library.
/**
* @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
* *
* (c) Matías Navarro Carter <mnavarrocarter@gmail.com>
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace MNC\ChileanRut\Bridge\Doctrine\DBAL\Types; namespace MNC\ChileanRut\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\ConversionException;
@@ -16,17 +22,15 @@ use Doctrine\DBAL\Types\StringType;
use MNC\ChileanRut\Rut; use MNC\ChileanRut\Rut;
/** /**
* Class RutType. * Class RutTextType.
* *
* @author Matías Navarro Carter <mnavarro@option.cl> * This type maps the rut to a string and stores it with the verifier number,
* including dots and the hyphen.
*/ */
class RutType extends StringType class RutType extends StringType
{ {
public const NAME = 'rut'; public const NAME = 'rut';
/**
* @return string
*/
public function getName(): string public function getName(): string
{ {
return self::NAME; return self::NAME;
@@ -34,7 +38,6 @@ class RutType extends StringType
/** /**
* @param mixed $value * @param mixed $value
* @param AbstractPlatform $platform
* *
* @return mixed * @return mixed
* *
@@ -42,12 +45,12 @@ class RutType extends StringType
*/ */
public function convertToDatabaseValue($value, AbstractPlatform $platform) public function convertToDatabaseValue($value, AbstractPlatform $platform)
{ {
if (null === $value) { if ($value === null) {
return $value; return $value;
} }
if ($value instanceof Rut) { if ($value instanceof Rut) {
return $value->format(Rut::FORMAT_HYPHENED); return (string) $value->format()->hyphened()->dotted();
} }
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', Rut::class]); throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', Rut::class]);
@@ -55,16 +58,21 @@ class RutType extends StringType
/** /**
* @param mixed $value * @param mixed $value
* @param AbstractPlatform $platform
* *
* @return mixed * @return mixed
*
* @throws ConversionException
*/ */
public function convertToPHPValue($value, AbstractPlatform $platform) public function convertToPHPValue($value, AbstractPlatform $platform)
{ {
if (null === $value || $value instanceof Rut) { if ($value === null) {
return $value; return $value;
} }
return new Rut($value); if (is_string($value)) {
return Rut::parse($value);
}
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string']);
} }
} }

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Exception;
use MNC\ChileanRut\Rut;
/**
* Class InvalidRutException.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class InvalidRutException extends \LogicException
{
/**
* @var Rut
*/
private $rut;
/**
* InvalidRutException constructor.
*
* @param Rut $rut
* @param string|null $message
*/
public function __construct(Rut $rut, string $message = null)
{
if (null === $message) {
$message = sprintf('Rut %s is not a valid rut.', $rut->format(Rut::FORMAT_READABLE));
}
$this->rut = $rut;
parent::__construct($message);
}
/**
* @return Rut
*/
public function getRut(): Rut
{
return $this->rut;
}
}

87
src/FormattedRut.php Normal file
View File

@@ -0,0 +1,87 @@
<?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;
}
}

35
src/InvalidRut.php Normal file
View File

@@ -0,0 +1,35 @@
<?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));
}
}

View File

@@ -1,193 +1,139 @@
<?php <?php
/* declare(strict_types=1);
* This file is part of the MNC\ChileanRut library.
/**
* @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
* *
* (c) Matías Navarro Carter <mnavarrocarter@gmail.com>
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace MNC\ChileanRut; namespace MNC\ChileanRut;
use MNC\ChileanRut\Validator\Module11RutValidator;
use MNC\ChileanRut\Validator\RutValidator;
/** /**
* Rut represents a the Chilean National ID Number. * Rut represents a the Chilean National ID Number.
* *
* All residents of Chile are uniquely identified by one of these. * All residents of Chile are uniquely identified by one of these and it is
* * mainly used for tax purposes.
* @author Matías Navarro Carter <mnavarro@option.cl>
*/ */
class Rut class Rut
{ {
public const FORMAT_HYPHENED = 0; // 14533535-5 private const VALID_VERIFIERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'K'];
public const FORMAT_CLEAR = 1; // 145335355
public const FORMAT_READABLE = 2; // 14.533.535-5
public const FORMAT_HIDDEN = 3; // 17.***.***-5
/** /**
* @var string * The actual RUT number.
*/ */
private $value; private int $number;
/** /**
* @var string * The RUT verifier digit.
*/ */
private $dv; private string $verifier;
public static function parse(string $rut): Rut
{
// Remove space, dots and hyphens
$rut = str_replace([' ', '.', '-'], '', $rut);
return new self(
(int) substr($rut, 0, -1),
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. * Rut constructor.
* *
* @param string $rut * @throws InvalidRut if the verifier digit is invalid
* @param RutValidator|null $validator if provided validates the Rut
*/ */
public function __construct(string $rut, RutValidator $validator = null) public function __construct(int $number, string $verifier)
{ {
$sanitized = $this->sanitize($rut); $this->number = $number;
$this->value = substr($sanitized, 0, -1); $this->verifier = strtoupper($verifier);
$this->dv = $sanitized[\strlen($sanitized) - 1]; $this->guard();
if (!$validator instanceof RutValidator) {
$validator = new Module11RutValidator();
}
$validator->validate($this);
} }
/** public function getNumber(): int
* Casts the Rut object into a string.
*
* @return string
*/
public function __toString(): string
{ {
return $this->format(self::FORMAT_READABLE); return $this->number;
} }
/** public function getVerifier(): string
* Creates a new Rut instance from the correlative and the verifier digit.
*
* @param string $correlative
* @param string $verifierDigit
* @param RutValidator|null $validator
*
* @return Rut
*/
public static function fromParts(string $correlative, string $verifierDigit, RutValidator $validator = null): Rut
{ {
return new self($correlative.$verifierDigit, $validator); return $this->verifier;
}
/**
* Creates a new instance of Rut from a string.
*
* @param string $rut
* @param RutValidator|null $validator
*
* @return Rut
*/
public static function fromString(string $rut, RutValidator $validator = null): Rut
{
return new self($rut, $validator);
} }
/** /**
* Compares whether a Rut is equal to another or not. * Compares whether a Rut is equal to another or not.
*
* @param Rut $rut
*
* @return bool
*/ */
public function isEqualTo(Rut $rut): bool public function equals(Rut $rut): bool
{ {
return $this->format() === $rut->format(); return $this->number === $rut->number;
} }
/** /**
* Formats a Rut to a string. * Formats a Rut to a string.
*
* @param int $format one of the FORMAT_ constants
*
* @return string
*/ */
public function format(int $format = 0): string public function format(): FormattedRut
{ {
switch ($format) { return new FormattedRut($this);
case self::FORMAT_HYPHENED: }
return $this->value.'-'.$this->dv;
break; private function guard(): void
case self::FORMAT_CLEAR: {
return $this->value.$this->dv; // Check if rut is between zero and 999.999.999
break; if ($this->number < 0 || $this->number > 999_999_999) {
case self::FORMAT_READABLE: throw InvalidRut::number();
return $this->formatReadable(); }
break; // Check if the verifier digit is in the range of valid ones
case self::FORMAT_HIDDEN: if (!in_array($this->verifier, self::VALID_VERIFIERS, true)) {
return $this->formatHidden(); throw InvalidRut::digit($this->verifier);
break; }
default: // Check the verifier is algorithmically correct
throw new \InvalidArgumentException( if ($this->verifier !== self::calculateVerifier($this->number)) {
sprintf( throw InvalidRut::digit($this->verifier);
'Argument provided for %s method of class %s is invalid.',
__METHOD__,
__CLASS__
)
);
} }
} }
/** /**
* Returns the correlative number of the Rut. * Calculates a verifier digit from a number.
*
* @return string
*/ */
public function getCorrelative(): string private static function calculateVerifier(int $number): string
{ {
return $this->value; /** @var list<int> $sequence */
$sequence = array_filter(array_reverse(str_split((string) $number)), 'intval');
$x = 2;
$s = 0;
foreach ($sequence as $digit) {
if ($x > 7) {
$x = 2;
}
$s += $digit * $x;
++$x;
}
$dv = 11 - ($s % 11);
if ($dv === 10) {
$dv = 'K';
}
if ($dv === 11) {
$dv = '0';
} }
/** return (string) $dv;
* Returns the verifier digit of the Rut.
*
* @return string
*/
public function getVerifierDigit(): string
{
return $this->dv;
}
/**
* Sanitizes a Rut string.
*
* @param string $value
*
* @return string
*/
private function sanitize(string $value): string
{
return str_replace(['.', ',', '-'], '', strtoupper(trim($value)));
}
/**
* Helper to format the Rut as FORMAT_READABLE.
*
* @return string
*/
private function formatReadable(): string
{
return sprintf('%s-%s', number_format($this->value, 0, '', '.'), $this->dv);
}
/**
* Helper to format the Rut as FORMAT_HIDDEN.
*
* @return string
*/
private function formatHidden(): string
{
$readable = $this->formatReadable();
$exploded = explode('.', $readable);
return sprintf('%s.***.***-%s', $exploded[0], $this->dv);
} }
} }

View File

@@ -1,81 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Util;
use MNC\ChileanRut\Rut;
/**
* This class provides utils for a Rut correlative.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class CorrelativeUtils
{
/**
* Finds the verifier digit of a correlative.
*
* @param string $correlative
*
* @return string
*/
public static function findVerifierDigit(string $correlative): string
{
$x = 2;
$s = 0;
for ($i = \strlen($correlative) - 1; $i >= 0; --$i) {
if ($x > 7) {
$x = 2;
}
$s += $correlative[$i] * $x;
++$x;
}
$dv = 11 - ($s % 11);
if (10 === $dv) {
$dv = 'K';
}
if (11 === $dv) {
$dv = '0';
}
return (string) $dv;
}
/**
* Instantiates a valid Rut object just providing a correlative.
*
* @param string $correlative
*
* @return Rut
*/
public static function createValidRutOnlyFromCorrelative(string $correlative): Rut
{
return Rut::fromParts($correlative, static::findVerifierDigit($correlative));
}
/**
* Auto-generates an algorithmically valid Rut, because why not.
*
* @noinspection PhpDocMissingThrowsInspection
*
* @return Rut
*/
public static function autoGenerateValidRut(): Rut
{
/** @noinspection PhpUnhandledExceptionInspection */
$correlative = \random_int(1000000, 40000000);
return static::createValidRutOnlyFromCorrelative($correlative);
}
}

View File

@@ -1,52 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
/**
* A ChainRutValidator.
*
* Use this implementation when you want to validate a Rut against multiple
* validators. Add the validators in order by calling addValidator().
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class ChainRutValidator implements RutValidator
{
/**
* @var RutValidator[]
*/
private $validators;
/**
* ChainRutValidator constructor.
*
* @param RutValidator[] $validators
*/
public function __construct(RutValidator ...$validators)
{
$this->validators = $validators;
}
/**
* @param Rut $rut
*
* @throws InvalidRutException on invalid Rut
*/
public function validate(Rut $rut): void
{
foreach ($this->validators as $validator) {
$validator->validate($rut);
}
}
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Util\CorrelativeUtils;
/**
* Validates the Rut using the Module 11 algorithm.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
class Module11RutValidator implements RutValidator
{
/**
* @param Rut $rut
*
* @throws InvalidRutException
*/
public function validate(Rut $rut): void
{
$digit = CorrelativeUtils::findVerifierDigit($rut->getCorrelative());
if ($digit !== $rut->getVerifierDigit()) {
throw new InvalidRutException($rut);
}
}
}

View File

@@ -1,43 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
/**
* This is the base contract for a Rut validator.
*
* You can implement any logic here that you can use to validate a Rut.
* For example, the Module11RutValidator only validates that a Rut is algorithmically
* correct, but not that it actually exists.
*
* You could create a HTTPRutValidator that performs a request to validate that a
* Rut exists against a Rest Api or a third party service.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
*/
interface RutValidator
{
/**
* Validates a Rut.
*
* The implementation MUST throw an InvalidRutException if validation fails.
*
* The different clients CAN catch that exception and handle the validation
* error according to their business rules.
*
* @param Rut $rut
*
* @throws InvalidRutException on invalid Rut
*/
public function validate(Rut $rut): void;
}

View File

@@ -1,41 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Tests\Bridge\Symfony\Form;
use MNC\ChileanRut\Bridge\Symfony\Form\RutType;
use MNC\ChileanRut\Rut;
use Symfony\Component\Form\Test\TypeTestCase;
class RutTypeTest extends TypeTestCase
{
public function testSubmitValidData()
{
$objectToCompare = new Rut('16.894.365-2');
// $objectToCompare will retrieve data from the form submission; pass it as the second argument
$form = $this->factory->create(RutType::class);
// submit the data to the form directly
$form->submit('16.894.365-2');
$this->assertTrue($form->isSynchronized());
$formData = $form->getData();
// check that $objectToCompare was modified as expected when the form was submitted
$this->assertInstanceOf(Rut::class, $formData);
$this->assertTrue($formData->isEqualTo($objectToCompare));
$view = $form->createView();
$this->assertSame('16.894.365-2', $view->vars['value']);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace MNC\ChileanRut\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use MNC\ChileanRut\Rut;
use PHPUnit\Framework\TestCase;
class NumericRutTypeTest extends TestCase
{
public function testItConvertsNullFromDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new NumericRutType();
$result = $type->convertToPHPValue(null, $platform);
self::assertNull($result);
}
public function testItConvertsStringFromDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new NumericRutType();
$result = $type->convertToPHPValue(16894365, $platform);
self::assertInstanceOf(Rut::class, $result);
}
public function testItConvertsNullToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new NumericRutType();
$result = $type->convertToDatabaseValue(null, $platform);
self::assertNull($result);
}
public function testItConvertsRutToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new NumericRutType();
$result = $type->convertToDatabaseValue(Rut::parse('168943652'), $platform);
self::assertSame('16894365', $result);
}
public function testItCannotConvertToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new NumericRutType();
$this->expectException(ConversionException::class);
$type->convertToDatabaseValue(new \DateTime(), $platform);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace MNC\ChileanRut\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use MNC\ChileanRut\Rut;
use PHPUnit\Framework\TestCase;
class RutTypeTest extends TestCase
{
public function testItConvertsNullFromDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$result = $type->convertToPHPValue(null, $platform);
self::assertNull($result);
}
public function testItConvertsStringFromDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$result = $type->convertToPHPValue('16.894.365-2', $platform);
self::assertInstanceOf(Rut::class, $result);
}
public function testItCannotConvertFromDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$this->expectException(ConversionException::class);
$type->convertToPHPValue(true, $platform);
}
public function testItConvertsNullToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$result = $type->convertToDatabaseValue(null, $platform);
self::assertNull($result);
}
public function testItConvertsRutToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$result = $type->convertToDatabaseValue(Rut::parse('168943652'), $platform);
self::assertSame('16.894.365-2', $result);
}
public function testItCannotConvertToDatabaseValue(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$type = new RutType();
$this->expectException(ConversionException::class);
$type->convertToDatabaseValue(new \DateTime(), $platform);
}
}

View File

@@ -0,0 +1,54 @@
<?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);
}
}

View File

@@ -1,82 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Tests\Rut;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Validator\Module11RutValidator;
use PHPUnit\Framework\TestCase;
class RutTest extends TestCase
{
public function testThatRutIsSanitizedProperlyOnInstantiation()
{
$rut = Rut::fromString('16.894.365-2');
$this->assertSame('16894365', $rut->getCorrelative());
$this->assertSame('2', $rut->getVerifierDigit());
}
public function testThatRutsInstantiatedDifferentFormatButWithEqualValueAreIndeedEqual()
{
$rut1 = new Rut('16.894.365-2');
$rut2 = new Rut('16894365-2');
$this->assertTrue($rut1->isEqualTo($rut2));
}
public function testThatFormatClearWorks()
{
$rut = new Rut('16.894.365-2');
$this->assertSame('168943652', $rut->format(Rut::FORMAT_CLEAR));
}
public function testThatFormatWithHyphenWorks()
{
$rut = new Rut('16.894.365-2');
$this->assertSame('16894365-2', $rut->format(Rut::FORMAT_HYPHENED));
}
public function testThatFormatReadableWorks()
{
$rut = new Rut('168943652');
$this->assertSame('16.894.365-2', $rut->format(Rut::FORMAT_READABLE));
}
public function testThatFormatHiddenWorks()
{
$rut = new Rut('168943652');
$this->assertSame('16.***.***-2', $rut->format(Rut::FORMAT_HIDDEN));
}
public function testThatIntegratedValidationThrowsExceptionOnInvalidRut()
{
$this->expectException(InvalidRutException::class);
$validator = new Module11RutValidator();
$rut = new Rut('4444444-2', $validator);
}
public function testThatIntegratedValidationDoesNotThrowExceptionOnValidRut()
{
$validator = new Module11RutValidator();
$rut = new Rut('16.894.365-2', $validator);
$this->assertInstanceOf(Rut::class, $rut);
}
public function testInvalidFormatValueRaisesException()
{
$rut = new Rut('16.894.365-2');
$this->expectException(\InvalidArgumentException::class);
$rut->format(23);
}
}

86
tests/RutTest.php Normal file
View File

@@ -0,0 +1,86 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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;
use PHPUnit\Framework\TestCase;
/**
* Class RutTest
* @package MNC\ChileanRut\Tests\Rut
*/
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
{
$rut = Rut::parse($raw);
self::assertSame($expectedNumber, $rut->getNumber());
self::assertSame($expectedVerifier, $rut->getVerifier());
}
public function testItDetectsOutOfRangeVerifier(): void
{
$this->expectException(InvalidRut::class);
Rut::parse('16894365F');
}
public function testItDetectsInvalidVerifier(): void
{
$this->expectException(InvalidRut::class);
Rut::parse('16894365K');
}
public function testItDetectsRutTooBig(): void
{
$this->expectException(InvalidRut::class);
Rut::create(3_355_535_353);
}
public function testItComparesToEqual(): 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));
}
public static function testItCreatesARut(): void
{
$rut = Rut::create(22_457_309);
self::assertSame(22_457_309, $rut->getNumber());
}
public static function testItCanFormatRut(): void
{
$rut = (string) Rut::create(22_457_309)->format();
self::assertSame('22457309K', $rut);
}
/**
* @return array[]
*/
public function getRutDataset(): 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']
];
}
}

View File

@@ -1,56 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Tests\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Util\CorrelativeUtils;
use MNC\ChileanRut\Validator\ChainRutValidator;
use MNC\ChileanRut\Validator\Module11RutValidator;
use MNC\ChileanRut\Validator\RutValidator;
use PHPUnit\Framework\TestCase;
/**
* Class ChainRutValidatorTest.
*/
class ChainRutValidatorTest extends TestCase
{
public function testThatChainValidatorFails(): void
{
$rut = CorrelativeUtils::autoGenerateValidRut();
$normalValidator = new Module11RutValidator();
$mockValidator = $this->createMock(RutValidator::class);
$mockValidator->expects($this->once())
->method('validate')
->willThrowException(new InvalidRutException($rut));
$chainValidator = new ChainRutValidator($normalValidator, $mockValidator);
$this->expectException(InvalidRutException::class);
$chainValidator->validate($rut);
}
public function testThatChainValidatorPasses(): void
{
$rut = CorrelativeUtils::autoGenerateValidRut();
$normalValidator = new Module11RutValidator();
$mockValidator = $this->createMock(RutValidator::class);
$mockValidator->expects($this->once())
->method('validate')
->willReturn(null);
$chainValidator = new ChainRutValidator($normalValidator, $mockValidator);
$chainValidator->validate($rut);
$this->assertTrue(true);
}
}

View File

@@ -1,39 +0,0 @@
<?php
/*
* This file is part of the MNC\ChileanRut library.
*
* (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\Tests\Validator;
use MNC\ChileanRut\Exception\InvalidRutException;
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Validator\Module11RutValidator;
use PHPUnit\Framework\TestCase;
class SimpleRutValidatorTest extends TestCase
{
public function testValidationPassesOnValidRut()
{
$rut = new Rut('16.894.365-2');
$validator = new Module11RutValidator();
$validator->validate($rut);
$this->assertInstanceOf(Rut::class, $rut);
}
public function testValidationFailsOnInvalidRut()
{
$this->expectException(InvalidRutException::class);
$rut = new Rut('34.4534.353-1');
$validator = new Module11RutValidator();
$validator->validate($rut);
}
}