diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df101e0..1b89dea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,59 +12,78 @@ on: jobs: + composer-normalize: + name: Composer normalization + runs-on: "ubuntu-latest" + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + tools: composer-normalize + env: + fail-fast: true + - name: Composer normalize + run: composer-normalize + phpcs: name: Code style (phpcs) runs-on: "ubuntu-latest" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: none - tools: composer:v2, cs2pr, phpcs + tools: cs2pr, phpcs env: fail-fast: true - name: Code style (phpcs) - run: phpcs -q --report=checkstyle src/ tests/ | cs2pr + run: phpcs -q --report=checkstyle | cs2pr php-cs-fixer: name: Code style (php-cs-fixer) runs-on: "ubuntu-latest" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: none - tools: composer:v2, cs2pr, php-cs-fixer + tools: cs2pr, php-cs-fixer env: fail-fast: true - name: Code style (php-cs-fixer) run: php-cs-fixer fix --dry-run --format=checkstyle | cs2pr + env: + PHP_CS_FIXER_IGNORE_ENV: 1 phpstan: name: Code analysis (phpstan) runs-on: "ubuntu-latest" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: none tools: composer:v2, phpstan env: fail-fast: true - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -79,10 +98,10 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - php-versions: ['7.3', '7.4', '8.0', '8.1'] + php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -93,9 +112,9 @@ jobs: fail-fast: true - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8a40ddf..09687f8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -14,20 +14,20 @@ jobs: runs-on: "ubuntu-latest" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: xdebug tools: composer:v2 env: fail-fast: true - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -37,7 +37,7 @@ jobs: - name: Create code coverage run: vendor/bin/phpunit --testdox --verbose --coverage-xml=build/coverage --coverage-clover=build/coverage/clover.xml --log-junit=build/coverage/junit.xml - name: Store code coverage - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: code-coverage path: build/coverage @@ -53,15 +53,15 @@ jobs: id: check-secrets run: | if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then - echo "::set-output name=github::yes" + echo "github=yes" >> $GITHUB_OUTPUT else - echo "::set-output name=github::no" + echo "github=no" >> $GITHUB_OUTPUT echo "::warning ::GITHUB_TOKEN non set" fi if [ -n "${{ secrets.SONAR_TOKEN }}" ]; then - echo "::set-output name=sonar::yes" + echo "sonar=yes" >> $GITHUB_OUTPUT else - echo "::set-output name=sonar::no" + echo "sonar=no" >> $GITHUB_OUTPUT echo "::warning ::SONAR_TOKEN non set" fi @@ -72,20 +72,20 @@ jobs: runs-on: "ubuntu-latest" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Unshallow clone to provide blame information run: git fetch --unshallow - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: none tools: composer:v2 - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -93,7 +93,7 @@ jobs: - name: Install project dependencies run: composer upgrade --no-interaction --no-progress --prefer-dist - name: Obtain code coverage - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: code-coverage path: build/coverage diff --git a/.phive/phars.xml b/.phive/phars.xml index a8c442c..c05fe29 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,7 +1,8 @@ - - - - + + + + + diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index fab2854..bfdf895 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -11,25 +11,24 @@ return (new PhpCsFixer\Config()) ->setRiskyAllowed(true) - ->setCacheFile(__DIR__ . '/build/php_cs.cache') + ->setCacheFile(__DIR__ . '/build/php-cs-fixer.cache') ->setRules([ '@PSR12' => true, '@PSR12:risky' => true, '@PHP71Migration:risky' => true, '@PHP73Migration' => true, - // PSR12 (remove when php-cs-fixer reaches ^3.1.1) - 'class_definition' => ['space_before_parenthesis' => true], // symfony - // 'class_attributes_separation' => true, // conflict with PSR12 + 'class_attributes_separation' => true, 'whitespace_after_comma_in_array' => true, 'no_empty_statement' => true, 'no_extra_blank_lines' => true, 'function_typehint_space' => true, + 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arrays']], 'no_blank_lines_after_phpdoc' => true, 'object_operator_without_whitespace' => true, 'binary_operator_spaces' => true, 'phpdoc_scalar' => true, - 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_comma_in_singleline' => true, 'single_quote' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_unused_imports' => true, @@ -37,16 +36,18 @@ 'standardize_not_equals' => true, 'concat_space' => ['spacing' => 'one'], 'linebreak_after_opening_tag' => true, + 'fully_qualified_strict_types' => true, // symfony:risky 'no_alias_functions' => true, 'self_accessor' => true, // contrib 'not_operator_with_successor_space' => true, + 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], // @PSR12 sort_algorithm: none ]) ->setFinder( PhpCsFixer\Finder::create() ->in(__DIR__) ->append([__FILE__]) - ->exclude(['vendor', 'build']) + ->exclude(['vendor', 'tools', 'build']), ) ; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2d96576..2b9f998 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contribuciones -Las contribuciones son bienvenidas. Aceptamos *Pull Requests* en el [repositorio GitHub][homepage]. +Las contribuciones son bienvenidas. Aceptamos *Pull Requests* en el [repositorio GitHub][project]. Este proyecto se apega al siguiente [Código de Conducta][coc]. Al participar en este proyecto y en su comunidad, deberás seguir este código. diff --git a/LICENSE b/LICENSE index 27397b2..97083c0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 - 2022 PhpCfdi https://www.phpcfdi.com/ +Copyright (c) 2019 - 2023 PhpCfdi https://www.phpcfdi.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e391d02..821b721 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ and licensed for use under the MIT License (MIT). Please see [LICENSE][] for mor [badge-php-version]: https://img.shields.io/packagist/php-v/phpcfdi/credentials?logo=php [badge-release]: https://img.shields.io/github/release/phpcfdi/credentials?logo=git [badge-license]: https://img.shields.io/github/license/phpcfdi/credentials?logo=open-source-initiative -[badge-build]: https://img.shields.io/github/workflow/status/phpcfdi/credentials/build/main?logo=github-actions +[badge-build]: https://img.shields.io/github/actions/workflow/status/phpcfdi/credentials/build.yml?branch=main&logo=github-actions [badge-reliability]: https://sonarcloud.io/api/project_badges/measure?project=phpcfdi_credentials&metric=reliability_rating [badge-maintainability]: https://sonarcloud.io/api/project_badges/measure?project=phpcfdi_credentials&metric=sqale_rating [badge-coverage]: https://img.shields.io/sonar/coverage/phpcfdi_credentials/main?logo=sonarcloud&server=https%3A%2F%2Fsonarcloud.io diff --git a/composer.json b/composer.json index fff7c5b..4e4eeb8 100644 --- a/composer.json +++ b/composer.json @@ -1,25 +1,25 @@ { "name": "phpcfdi/credentials", "description": "Library to use eFirma (fiel) and CSD (sellos) from SAT", - "keywords": ["efirma", "fiel", "sat", "cfdi", "sello", "certificado"], - "homepage": "https://github.com/phpcfdi/credentials", "license": "MIT", + "keywords": [ + "efirma", + "fiel", + "sat", + "cfdi", + "sello", + "certificado" + ], "authors": [ { "name": "Carlos C Soto", "email": "eclipxe13@gmail.com" } ], + "homepage": "https://github.com/phpcfdi/credentials", "support": { - "source": "https://github.com/phpcfdi/credentials", - "issues": "https://github.com/phpcfdi/credentials/issues" - }, - "prefer-stable": true, - "config": { - "optimize-autoloader": true, - "preferred-install": { - "*": "dist" - } + "issues": "https://github.com/phpcfdi/credentials/issues", + "source": "https://github.com/phpcfdi/credentials" }, "require": { "php": ">=7.3", @@ -31,6 +31,7 @@ "ext-json": "*", "phpunit/phpunit": "^9.5" }, + "prefer-stable": true, "autoload": { "psr-4": { "PhpCfdi\\Credentials\\": "src/" @@ -41,30 +42,41 @@ "PhpCfdi\\Credentials\\Tests\\": "tests/" } }, + "config": { + "optimize-autoloader": true, + "preferred-install": { + "*": "dist" + } + }, "scripts": { - "dev:build": ["@dev:fix-style", "@dev:test"], + "dev:build": [ + "@dev:fix-style", + "@dev:check-style", + "@dev:test" + ], "dev:check-style": [ + "@php tools/composer-normalize normalize --dry-run", "@php tools/php-cs-fixer fix --dry-run --verbose", "@php tools/phpcs --colors -sp" ], + "dev:coverage": [ + "@php -dzend_extension=xdebug.so -dxdebug.mode=coverage vendor/bin/phpunit --verbose --coverage-html build/coverage/html/" + ], "dev:fix-style": [ + "@php tools/composer-normalize normalize", "@php tools/php-cs-fixer fix --verbose", "@php tools/phpcbf --colors -sp" ], "dev:test": [ - "@dev:check-style", "@php vendor/bin/phpunit --testdox --verbose --stop-on-failure", - "@php tools/phpstan analyse --no-progress" - ], - "dev:coverage": [ - "@php -dzend_extension=xdebug.so -dxdebug.mode=coverage vendor/bin/phpunit --verbose --coverage-html build/coverage/html/" + "@php tools/phpstan analyse --no-progress --verbose" ] }, "scripts-descriptions": { - "dev:build": "DEV: run dev:fix-style and dev:tests, run before pull request", - "dev:check-style": "DEV: search for code style errors using php-cs-fixer and phpcs", - "dev:fix-style": "DEV: fix code style errors using php-cs-fixer and phpcbf", - "dev:test": "DEV: run dev:check-style, phpunit and phpstan", - "dev:coverage": "DEV: run phpunit with xdebug and storage coverage in build/coverage/html/" + "dev:build": "DEV: run dev:fix-style dev:check-style and dev:tests, run before pull request", + "dev:check-style": "DEV: search for code style errors using composer-normalize, php-cs-fixer and phpcs", + "dev:coverage": "DEV: run phpunit with xdebug and storage coverage in build/coverage/html/", + "dev:fix-style": "DEV: fix code style errors using composer-normalize, php-cs-fixer and phpcbf", + "dev:test": "DEV: run phpunit and phpstan" } } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ba44a4d..811474a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,7 +7,26 @@ Usamos [Versionado Semántico 2.0.0](SEMVER.md) por lo que puedes usar esta libr ## Cambios no liberados en una versión Pueden aparecer cambios no liberados que se integran a la rama principal, pero no ameritan una nueva liberación de -versión, aunque sí su incorporación en la rama principal de trabajo. Generalmente se tratan de cambios en el desarrollo. +versión, aunque sí su incorporación en la rama principal de trabajo. Generalmente, se tratan de cambios en el desarrollo. + +### Mantenimiento 2023-02-22 + +Los siguientes cambios son de mantenimiento: + +- Se actualiza el año en el archivo de licencia. +- Se agrega una prueba para comprobar certificados *Teletex*. + Ver https://github.com/nodecfdi/credentials/commit/cd8f1827e06a5917c41940e82b8d696379362d5d. +- Se agrega un archivo de documentación: *Ejemplo de creación de una credencial con verificaciones previas*. +- Se corrige la insignia de construcción del proyecto `[bagde-build]`. +- Se sustituye la referencia `[homepage]` a `[project]` en el archivo `CONTRIBUTING.md`. +- Se actualizan los archivos de configuración de estilo de código. +- Se actualizan los flujos de trabajo de GitHub: + - Los trabajos de PHP se ejecutan en la versión 8.2. + - Se actualizan las acciones de GitHub a la versión 3. + - Se agrega PHP 8.2 a la matriz de pruebas. + - Se cambia la directiva `::set-output` a `$GITHUB_OUTPUT`. + - Se corrige el trabajo `phpcs` eliminando las rutas fijas. +- Se actualizan las versiones de las herramientas de desarrollo. ## Listado de cambios diff --git a/docs/EjemploCrearCredencialVerificacion.md b/docs/EjemploCrearCredencialVerificacion.md new file mode 100644 index 0000000..51885d6 --- /dev/null +++ b/docs/EjemploCrearCredencialVerificacion.md @@ -0,0 +1,55 @@ +# Ejemplo de creación de una credencial con verificaciones previas + +Al momento de crear una *credencial* (`Credential`), es posible que queramos verificar la creación con +una lista detallada de errores. Si bien esto puede significar una doble verificación, es posible implementarlo +con el siguiente código de ejemplo: + +```php +rfc() !== $expectedRfc) { + throw new Exception(sprintf('El certificado no pertenece al RFC %s.', $expectedRfc)); + } + if ($certificate->validOn()) { + throw new Exception('El certificado no es vigente en este momento.'); + } + if ($expectedType->isFiel() && ! $certificate->satType()->isFiel()) { + throw new Exception('El certificado no corresponde a una eFirma/FIEL.'); + } + if ($expectedType->isCsd() && ! $certificate->satType()->isCsd()) { + throw new Exception('El certificado no corresponde a un CSD.'); + } + + try { + $privateKey = PrivateKey::openFile($privateKeyFile, $passPhrase); + } catch (Throwable $exception) { + throw new Exception('El archivo de llave privada no se pudo abrir, el archivo o la contraseña son incorrectos.', 0, $exception); + } + if (! $privateKey->belongsTo($certificate)) { + throw new Exception('La llave privada no es par del certificado.'); + } + + return new Credential($certificate, $privateKey); +} +``` diff --git a/phpcs.xml.dist b/phpcs.xml.dist index e7c3269..e7a9c2a 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -1,4 +1,4 @@ - + The EngineWorks (PSR-2 based) coding standard. diff --git a/tests/Unit/CertificateTest.php b/tests/Unit/CertificateTest.php index be30835..cd5fb56 100644 --- a/tests/Unit/CertificateTest.php +++ b/tests/Unit/CertificateTest.php @@ -213,4 +213,14 @@ public function testCreateSerialNumber(): void $this->assertStringContainsString('Certificate does not contain a serial number', $exception->getMessage()); } } + + public function testReadCertificateTeletex(): void + { + /** @see https://github.com/nodecfdi/credentials/commit/cd8f1827e06a5917c41940e82b8d696379362d5d */ + $teletexCertificate = $this->fileContents('00001000000413053762.cer'); + $certificate = new Certificate($teletexCertificate); + + $this->assertSame('SMA0112284B2', $certificate->rfc()); + $this->assertSame('COMPAÑIA SANTA MARIA SA DE CV', $certificate->legalName()); + } } diff --git a/tests/_files/00001000000413053762.cer b/tests/_files/00001000000413053762.cer new file mode 100644 index 0000000..2fa4f2f Binary files /dev/null and b/tests/_files/00001000000413053762.cer differ