[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
.idea
.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
$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
file that was distributed with this source code.
EOF;
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'combine_consecutive_unsets' => true,
'header_comment' => ['header' => $header],
'linebreak_after_opening_tag' => true,
'no_php4_constructor' => true,
'no_useless_else' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'php_unit_strict' => true,
'phpdoc_no_empty_return' => false,
'declare_strict_types' => true,
'strict_comparison' => true,
'phpdoc_no_empty_return' => true,
'header_comment' => ['header' => $header, 'comment_type' => 'PHPDoc'],
'yoda_style' => [
'equal' => false,
'identical' => false,
'less_and_greater' => false,
'always_move_variable' => true
],
])
->setUsingCache(true)
->setRiskyAllowed(true)
->setFinder(
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
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
===========
Esta librería implementa una clase Rut como un *value object* inmutable, incluyendo
una api de validación flexible y extendible.
Esta librería implementa una clase Rut como un sencillo *value object* inmutable.
Además, posee un validador para `symfony/validator`, un *form type* para `symfony/form`
y un *type* para `doctrine/dbal`.
Sólo es compatible con PHP 7.1 o superior.
Además, posee dos *types* para `doctrine/dbal`.
## Instalación
@@ -18,195 +14,101 @@ composer require mnavarrocarter/chilean-rut
```
## 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
use MNC\ChileanRut\Rut;
$rut = new Rut('23.546.565-4');
// Si prefieres, puedes usar el factory method
$rut = Rut::fromString('23546565-4');
$rut = Rut::parse('23.546.565-4');
```
Por defecto, la clase Rut se valida usando el `Module11RutValidator` si no se pasa
un validador personalizado al momento de instanciación. Esto es para asegurar la
integridad del objeto.
### Validando el Rut
Si quieres, por alguna extraña razón, deshacerte de esa validación, puedes crear
un `AlwaysValidRutValidator` implementando la interfaz `RutValidator`. El método
validate estaría en blanco, lo que haría pasar la validación sin problema.
> TLDR: Un objeto `Rut` siempre será valido.
```php
<?php
use MNC\ChileanRut\Rut;
use MNC\ChileanRut\Validator\RutValidator;
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.
class AlwaysValidRutValidator implements RutValidator
{
public function validate(Rut $rut) : void
{
// 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.
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.
Estos son los unicos métodos que puedes usar:
```php
<?php
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
echo $rut->format(Rut::FORMAT_READABLE); // Va a imprimir 34.244.223-4
echo $rut->format(Rut::FORMAT_HYPHENED); // Va a imprimir 34244223-4
echo $rut->format(Rut::FORMAT_HIDDEN); // Va a imprimir 34.***.***-4
$rut->getNumber(); // (int) 23546565
$rut->getVerifier(); // (string) 4
```
### Utilidades
Esta librería provee una clase llamada `CorrelativeUtils` que tiene algunas utilidades
interesantes. Posee tres métodos:
### Formateando el Rut
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
use MNC\ChileanRut\Util\CorrelativeUtils;
use MNC\ChileanRut\Rut;
// Este método devuelve el digito verificador de un correlativo.
CorrelativeUtils::findVerifierDigit('34525252');
$rut = Rut::parse('23.546.565-4');
// Este método devuelve una instancia de Rut válida, sólo con el correlativo.
CorrelativeUtils::createValidRutOnlyFromCorrelative('34525252');
// Este método devuelve instancia de Rut autogenerada algoritmicamente válida.
CorrelativeUtils::autoGenerateValidRut();
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
```
## Integraciones con Liberías de Terceros
## Integraciones con Librerías de Terceros
### Doctrine DBAL
Esta libería provee un custom type para doctrine llamado `RutType`. Puedes registrarla
en el Dbal para usarla en tus mappings de doctrine y automáticamente mappear tu
el valor de tu db a un objeto rut.
Esta librería provee dos [*Custom Types*](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/custom-mapping-types.html)
para Doctrine, con el objetivo de que puedas mapear tus objetos `Rut` fácilmente
a una base de datos relacional.
### Symfony Validator
Además, esta libería cuenta con un validador para Symfony Validator, que te
permite beneficiarte de las anotaciones del componente de validación de Symfony.
Como dependencia opcional necesita una instancia de `RutValidator`. Si ninguna es proveída,
se utiliza el `SimpleRutValidator`. Solo puedes usar el validador contra una instancia de `Rut`.
El `MNC\ChileanRut\Doctrine\RutType` mapeara 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.
### Symfony Form Type
Por último, esta libería cuenta con un Symfony Form Type que puedes añadir en tus
formularios HTML, para que puedas autoinstanciar la clase y poner lógica de
validación en ella sin problema, y añadirla a tus otros tipos.
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
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.
Por supuesto, puedes elegir el `Type` que más se ajuste a tus necesidades.
## FAQ
### ¿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
PHP.
Si bien es cierto, hay muchas liberías con implementaciones de Rut chilenos en PHP,
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 separan bien responsabilidades, como la lógica de validación con la de instanciación.
3. No proveen validación extensible por medio de interfaces, limitando la validación
solo a ser algorítmica.
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.
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.

View File

@@ -11,15 +11,14 @@
],
"minimum-stability": "stable",
"require": {
"php": "^7.1"
"php": "^7.4"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.12",
"phpunit/phpunit": "^7.3",
"friendsofphp/php-cs-fixer": "^2.16",
"phpunit/phpunit": "^9.0",
"doctrine/dbal": "^2.5",
"symfony/form": "^3.4|^4.0",
"symfony/validator": "^3.4|^4.0",
"symfony/var-dumper": "^4.1"
"symfony/var-dumper": "^5.1",
"vimeo/psalm": "^4.2"
},
"autoload": {
"psr-4": {
@@ -28,7 +27,15 @@
},
"autoload-dev": {
"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"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="vendor/autoload.php"
>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false"
colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true"
convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false"
bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory>./src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</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
/*
* This file is part of the MNC\ChileanRut library.
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
*
* (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\Doctrine\DBAL\Types;
namespace MNC\ChileanRut\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
@@ -16,17 +22,15 @@ use Doctrine\DBAL\Types\StringType;
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
{
public const NAME = 'rut';
/**
* @return string
*/
public function getName(): string
{
return self::NAME;
@@ -34,7 +38,6 @@ class RutType extends StringType
/**
* @param mixed $value
* @param AbstractPlatform $platform
*
* @return mixed
*
@@ -42,12 +45,12 @@ class RutType extends StringType
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (null === $value) {
if ($value === null) {
return $value;
}
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]);
@@ -55,16 +58,21 @@ class RutType extends StringType
/**
* @param mixed $value
* @param AbstractPlatform $platform
*
* @return mixed
*
* @throws ConversionException
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if (null === $value || $value instanceof Rut) {
if ($value === null) {
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
/*
* This file is part of the MNC\ChileanRut library.
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
*
* (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 MNC\ChileanRut\Validator\Module11RutValidator;
use MNC\ChileanRut\Validator\RutValidator;
/**
* Rut represents a the Chilean National ID Number.
*
* All residents of Chile are uniquely identified by one of these.
*
* @author Matías Navarro Carter <mnavarro@option.cl>
* All residents of Chile are uniquely identified by one of these and it is
* mainly used for tax purposes.
*/
class Rut
{
public const FORMAT_HYPHENED = 0; // 14533535-5
public const FORMAT_CLEAR = 1; // 145335355
public const FORMAT_READABLE = 2; // 14.533.535-5
public const FORMAT_HIDDEN = 3; // 17.***.***-5
private const VALID_VERIFIERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'K'];
/**
* @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.
*
* @param string $rut
* @param RutValidator|null $validator if provided validates the Rut
* @throws InvalidRut if the verifier digit is invalid
*/
public function __construct(string $rut, RutValidator $validator = null)
public function __construct(int $number, string $verifier)
{
$sanitized = $this->sanitize($rut);
$this->value = substr($sanitized, 0, -1);
$this->dv = $sanitized[\strlen($sanitized) - 1];
if (!$validator instanceof RutValidator) {
$validator = new Module11RutValidator();
}
$validator->validate($this);
$this->number = $number;
$this->verifier = strtoupper($verifier);
$this->guard();
}
/**
* Casts the Rut object into a string.
*
* @return string
*/
public function __toString(): string
public function getNumber(): int
{
return $this->format(self::FORMAT_READABLE);
return $this->number;
}
/**
* 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
public function getVerifier(): string
{
return new self($correlative.$verifierDigit, $validator);
}
/**
* 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);
return $this->verifier;
}
/**
* 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.
*
* @param int $format one of the FORMAT_ constants
*
* @return string
*/
public function format(int $format = 0): string
public function format(): FormattedRut
{
switch ($format) {
case self::FORMAT_HYPHENED:
return $this->value.'-'.$this->dv;
break;
case self::FORMAT_CLEAR:
return $this->value.$this->dv;
break;
case self::FORMAT_READABLE:
return $this->formatReadable();
break;
case self::FORMAT_HIDDEN:
return $this->formatHidden();
break;
default:
throw new \InvalidArgumentException(
sprintf(
'Argument provided for %s method of class %s is invalid.',
__METHOD__,
__CLASS__
)
);
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);
}
}
/**
* Returns the correlative number of the Rut.
*
* @return string
* Calculates a verifier digit from a number.
*/
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';
}
/**
* 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);
return (string) $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);
}
}