Skip to content

Commit fbdca79

Browse files
committed
Version 2.7.4
2 parents f6a8c92 + 4748ee8 commit fbdca79

File tree

6 files changed

+85
-15
lines changed

6 files changed

+85
-15
lines changed

docs/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
- Remove `static` methods from `\CfdiUtils\CfdiVersion`, instead create an instance of the class
1212
- Remove `static` methods from `\CfdiUtils\TimbreFiscalDigital\TfdVersion`, instead create an instance of the class
1313
- Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments.
14+
- Refactor `\CfdiUtils\Certificado\SerialNumber` to be immutable, this change will remove `loadHexadecimal`,
15+
`loadDecimal` and `loadAscii`.
16+
17+
## Version 2.7.4 2018-12-12
18+
19+
- Add `CfdiUtils\Certificado\Certificado::getSerialObject` to return **a copy** of the instance
20+
of `CfdiUtils\Certificado\SerialNumber` in case is required.
21+
- Add `CfdiUtils\Certificado\SerialNumber::loadAscii` helper function
22+
- Improve tests on `SerialNumberTest` to include `loadAscii`
23+
- Improve tests on `CertificadoTest` to include `getSerialObject`
1424

1525

1626
## Version 2.7.3 2018-12-05

docs/componentes/certificado.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,31 @@ var_dump($certificate->getRfc()); // algo como COSC8001137NA
6262
```
6363

6464

65+
## Números de serie del certificado
66+
67+
En el número de serie requerido en los CFDI se utiliza una representación ASCII y no hexadecimal, sin embargo
68+
en algunas ocasiones se podría necesitar el número en formato hexadecimal de dos dígitos o la representación decimal.
69+
70+
El objeto `Certificado` contiene internamente un objeto de tipo `SerialNumber` que del que se puede obtener **una copia**
71+
por el método `getSerialObject(): SerialNumber` y dicho objeto puede devolver el número de serie en tres diferentes formatos:
72+
73+
- `SerialNumber::asAscii()`: `30001000000300023708` el mismo que se devuelve en `Certificado::getSerial()`
74+
- `SerialNumber::getHexadecimal()`: `3330303031303030303030333030303233373038`
75+
- `SerialNumber::getDecimal()`: `292233162870206001759766198425879490508935868472`
76+
77+
!!! note ""
78+
Se obtiene una copia del objeto y no la misma instancia porque el `SerialNumber` es mutable, a partir de la
79+
versión 3 el objeto será inmutable y se podrá obtener el objeto de la propia instancia.
80+
81+
6582
## Acerca de los formatos de archivo
6683

6784
El certificado (el archivo extensión CER) puede ser leído directamente.
6885

6986
La llave privada (el archivo extensión KEY) debe ser convertido a tipo PEM
7087
para ser correctamente interpretado por esta clase (en realidad por PHP).
7188

89+
7290
## Comandos útiles de openssl
7391

7492
- Obtener información del certificado:

src/CfdiUtils/Certificado/Certificado.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Certificado
1212
/** @var string */
1313
private $name;
1414

15-
/** @var string */
15+
/** @var SerialNumber */
1616
private $serial;
1717

1818
/** @var int */
@@ -76,7 +76,7 @@ public function __construct(string $filename)
7676
} else {
7777
throw new \RuntimeException("Cannot get serialNumberHex or serialNumber from certificate file $filename");
7878
}
79-
$this->serial = $serial->asAscii();
79+
$this->serial = $serial;
8080
$this->validFrom = $data['validFrom_time_t'] ?? 0;
8181
$this->validTo = $data['validTo_time_t'] ?? 0;
8282
$this->pubkey = $pubKey;
@@ -143,7 +143,12 @@ public function getName(): string
143143
*/
144144
public function getSerial(): string
145145
{
146-
return $this->serial;
146+
return $this->serial->asAscii();
147+
}
148+
149+
public function getSerialObject(): SerialNumber
150+
{
151+
return clone $this->serial;
147152
}
148153

149154
/**

src/CfdiUtils/Certificado/SerialNumber.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public function loadDecimal(string $decString)
3434
$this->loadHexadecimal($hexString);
3535
}
3636

37+
public function loadAscii(string $input)
38+
{
39+
$this->loadHexadecimal($this->asciiToHex($input));
40+
}
41+
3742
public function getHexadecimal(): string
3843
{
3944
return $this->hexString;
@@ -56,6 +61,13 @@ protected function hexToAscii(string $input): string
5661
}, '');
5762
}
5863

64+
protected function asciiToHex(string $input): string
65+
{
66+
return array_reduce(str_split($input, 1), function (string $carry, string $value): string {
67+
return $carry . dechex(ord($value));
68+
}, '');
69+
}
70+
5971
/**
6072
* Converts any string of any base to any other base without
6173
* PHP native method base_convert's double and float limitations.

tests/CfdiUtilsTests/Certificado/CertificadoTest.php

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function testConstructWithValidExample()
2727

2828
$certificado = new Certificado($cerfile);
2929

30-
$this->assertEquals($cerfile, $certificado->getFilename());
30+
$this->assertSame($cerfile, $certificado->getFilename());
3131
$certificateName = implode('', [
3232
'/CN=ACCEM SERVICIOS EMPRESARIALES SC',
3333
'/name=ACCEM SERVICIOS EMPRESARIALES SC',
@@ -36,13 +36,17 @@ public function testConstructWithValidExample()
3636
'/serialNumber= / HEGT761003MDFRNN09',
3737
'/OU=CSD01_AAA010101AAA',
3838
]);
39-
$this->assertEquals($certificateName, $certificado->getCertificateName());
40-
$this->assertEquals('ACCEM SERVICIOS EMPRESARIALES SC', $certificado->getName());
41-
$this->assertEquals('AAA010101AAA', $certificado->getRfc());
42-
$this->assertEquals('30001000000300023708', $certificado->getSerial());
43-
$this->assertEquals(strtotime('2017-05-18T03:54:56+00:00'), $certificado->getValidFrom());
44-
$this->assertEquals(strtotime('2021-05-18T03:54:56+00:00'), $certificado->getValidTo());
45-
$this->assertEquals($expectedPublicKey, $certificado->getPubkey());
39+
$this->assertSame($certificateName, $certificado->getCertificateName());
40+
$this->assertSame('ACCEM SERVICIOS EMPRESARIALES SC', $certificado->getName());
41+
$this->assertSame('AAA010101AAA', $certificado->getRfc());
42+
$this->assertSame('30001000000300023708', $certificado->getSerial());
43+
$this->assertSame(
44+
'3330303031303030303030333030303233373038',
45+
$certificado->getSerialObject()->getHexadecimal()
46+
);
47+
$this->assertSame(strtotime('2017-05-18T03:54:56+00:00'), $certificado->getValidFrom());
48+
$this->assertSame(strtotime('2021-05-18T03:54:56+00:00'), $certificado->getValidTo());
49+
$this->assertSame($expectedPublicKey, $certificado->getPubkey());
4650
}
4751

4852
public function testVerifyWithKnownData()
@@ -171,4 +175,16 @@ public function testCanReadRfcFromCertificateWhenX500UniqueIdentifierOnlyContain
171175
$certificate = new Certificado($certificateFile);
172176
$this->assertEquals('SOMG790807J57', $certificate->getRfc());
173177
}
178+
179+
public function testGetSerialObjectReturnsACopyOfTheObjectInsteadTheSameObject()
180+
{
181+
// remove this test on version 3 when the object SerialNumber is immutable
182+
$certificateFile = $this->utilAsset('certs/00001000000301246267.cer');
183+
$certificate = new Certificado($certificateFile);
184+
185+
$first = $certificate->getSerialObject();
186+
$second = $certificate->getSerialObject();
187+
$this->assertEquals($first, $second);
188+
$this->assertNotSame($first, $second);
189+
}
174190
}

tests/CfdiUtilsTests/Certificado/SerialNumberTest.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ public function testAsDecimalAsAscii()
1212
$expectedDecimal = '292233162870206001759766198425879490508935868472';
1313
$expectedAscii = '30001000000300023708';
1414
$serial = new SerialNumber($input);
15+
$this->assertSame($input, $serial->getHexadecimal());
1516
$this->assertSame($expectedDecimal, $serial->asDecimal());
1617
$this->assertSame($expectedAscii, $serial->asAscii());
1718
}
1819

1920
/**
20-
* This test extends the base class to expose the visibility of serialHexToAscii method.
21-
*
2221
* @param string $input
2322
* @param string $expected
2423
* @testWith ["3330303031303030303030333030303233373038", "30001000000300023708"]
@@ -32,8 +31,6 @@ public function testLoadHexadecimal(string $input, string $expected)
3231
}
3332

3433
/**
35-
* This test extends the base class to expose the visibility of serialHexToAscii method.
36-
*
3734
* @param string $input
3835
* @param string $expected
3936
* @testWith ["0x3330303031303030303030333030303233373038", "30001000000300023708"]
@@ -45,4 +42,16 @@ public function testLoadDecimal(string $input, string $expected)
4542
$serial->loadDecimal($input);
4643
$this->assertSame($expected, $serial->asAscii());
4744
}
45+
46+
/**
47+
* @param string $input
48+
* @param string $expected
49+
* @testWith ["30001000000300023708", "3330303031303030303030333030303233373038"]
50+
*/
51+
public function testLoadAscii(string $input, string $expected)
52+
{
53+
$serial = new SerialNumber('');
54+
$serial->loadAscii($input);
55+
$this->assertSame($expected, $serial->getHexadecimal());
56+
}
4857
}

0 commit comments

Comments
 (0)