diff --git a/component/Abstract/Value/Boolean.php b/component/Abstract/Value/Boolean.php
index b887865..de2d236 100644
--- a/component/Abstract/Value/Boolean.php
+++ b/component/Abstract/Value/Boolean.php
@@ -4,10 +4,10 @@
namespace Phant\DataStructure\Abstract\Value;
-abstract class Boolean
+abstract readonly class Boolean
{
public function __construct(
- public readonly bool $value
+ public bool $value
) {
}
}
diff --git a/component/Abstract/Value/Decimal.php b/component/Abstract/Value/Decimal.php
index b114eb1..5b4586d 100644
--- a/component/Abstract/Value/Decimal.php
+++ b/component/Abstract/Value/Decimal.php
@@ -4,10 +4,10 @@
namespace Phant\DataStructure\Abstract\Value;
-abstract class Decimal
+abstract readonly class Decimal
{
public function __construct(
- public readonly float $value
+ public float $value
) {
}
}
diff --git a/component/Abstract/Value/Integer.php b/component/Abstract/Value/Integer.php
index 00484f2..527740a 100644
--- a/component/Abstract/Value/Integer.php
+++ b/component/Abstract/Value/Integer.php
@@ -4,10 +4,10 @@
namespace Phant\DataStructure\Abstract\Value;
-abstract class Integer
+abstract readonly class Integer
{
public function __construct(
- public readonly int $value
+ public int $value
) {
}
}
diff --git a/component/Abstract/Value/Varchar.php b/component/Abstract/Value/Varchar.php
index d94d37c..a045b8d 100644
--- a/component/Abstract/Value/Varchar.php
+++ b/component/Abstract/Value/Varchar.php
@@ -6,12 +6,12 @@
use Phant\Error\NotCompliant;
-abstract class Varchar
+abstract readonly class Varchar
{
public const PATTERN = null;
public function __construct(
- public readonly string $value
+ public string $value
) {
if (defined(get_class($this) . '::PATTERN') && static::PATTERN && !preg_match(static::PATTERN, $value)) {
throw new NotCompliant('Value : ' . $value);
diff --git a/component/Color/Hexadecimal.php b/component/Color/Hexadecimal.php
index cba442e..688932c 100644
--- a/component/Color/Hexadecimal.php
+++ b/component/Color/Hexadecimal.php
@@ -4,9 +4,7 @@
namespace Phant\DataStructure\Color;
-use Phant\Error\NotCompliant;
-
-class Hexadecimal extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Hexadecimal extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^#[0-9a-fA-F]{3}$|#[0-9a-fA-F]{6}$|#[0-9a-fA-F]{8}$/';
}
diff --git a/component/Company/Euid.php b/component/Company/Euid.php
new file mode 100755
index 0000000..5b42652
--- /dev/null
+++ b/component/Company/Euid.php
@@ -0,0 +1,79 @@
+extractParts();
+
+ if (!$parts->countryCode || !in_array($parts->countryCode, self::COUNTRY_CODES)) {
+ throw new NotCompliant('Not compliant country code');
+ }
+
+ if ($check) {
+ match ($parts->countryCode) {
+ 'FR' => new Siren($parts->companyCode),
+ default => null,
+ };
+ }
+ }
+
+ public function getFormatted(
+ bool $nonBreakingSpace = true
+ ): string {
+ $parts = $this->extractParts();
+
+ $euid = implode(' ', [
+ $parts->countryCode,
+ $parts->registryCode,
+ $parts->separator,
+ self::formatCompanyCode($parts->companyCode),
+ ]);
+
+ if ($nonBreakingSpace) {
+ $euid = str_replace(' ', "\xC2\xA0", $euid); // non breaking space
+ }
+
+ return $euid;
+ }
+
+ public function extractParts(
+ ): object {
+ preg_match('/^(\D{2})(\w+)(\.)(\w+)$/', $this->value, $matches);
+
+ return (object) [
+ 'countryCode' => $matches[1] ?? null,
+ 'registryCode' => $matches[2] ?? null,
+ 'separator' => $matches[3] ?? null,
+ 'companyCode' => $matches[4] ?? null,
+ ];
+ }
+
+ private static function formatCompanyCode(
+ string $companyCode
+ ): string {
+ return match (strlen($companyCode)) {
+ 5 => preg_replace('/^(\d{2})(\d{3})$/', '$1 $2', $companyCode),
+ 6 => preg_replace('/^(\d{3})(\d{3})$/', '$1 $2', $companyCode),
+ 7 => preg_replace('/^(\d{3})(\d{4})$/', '$1 $2', $companyCode),
+ 8 => preg_replace('/^(\d{2})(\d{3})(\d{3})$/', '$1 $2 $3', $companyCode),
+ 9 => preg_replace('/^(\d{3})(\d{3})(\d{3})$/', '$1 $2 $3', $companyCode),
+ default => $companyCode,
+ };
+ }
+}
diff --git a/component/Company/Fr/CodeActivite.php b/component/Company/Fr/CodeActivite.php
index f954059..f417879 100755
--- a/component/Company/Fr/CodeActivite.php
+++ b/component/Company/Fr/CodeActivite.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Company\Fr;
-class CodeActivite extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class CodeActivite extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^\d{2}\.?\d{1,2}?\w{1}?$/';
diff --git a/component/Company/Fr/Siren.php b/component/Company/Fr/Siren.php
index c0744b8..147d980 100755
--- a/component/Company/Fr/Siren.php
+++ b/component/Company/Fr/Siren.php
@@ -6,7 +6,7 @@
use Phant\Error\NotCompliant;
-class Siren extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Siren extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^\d{9}$/';
@@ -39,12 +39,12 @@ public static function luhnCheck(
}
public function getFormatted(
- bool $espaceInsecable = true
+ bool $nonBreakingSpace = true
): string {
$siren = $this->value;
$siren = preg_replace('/^(\d{3})(\d{3})(\d{3})$/', '$1 $2 $3', $siren);
- if ($espaceInsecable) {
- $siren = str_replace(' ', "\xC2\xA0", $siren); // Espace insécable
+ if ($nonBreakingSpace) {
+ $siren = str_replace(' ', "\xC2\xA0", $siren); // non breaking space
}
return $siren;
diff --git a/component/Company/Fr/Siret.php b/component/Company/Fr/Siret.php
index 7d39479..251189d 100755
--- a/component/Company/Fr/Siret.php
+++ b/component/Company/Fr/Siret.php
@@ -7,7 +7,7 @@
use Phant\DataStructure\Company\Fr\Siren;
use Phant\Error\NotCompliant;
-class Siret extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Siret extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^\d{14}$/';
public const SIREN_LA_POSTE = '356000000';
@@ -31,12 +31,12 @@ public function getSiren(
}
public function getFormatted(
- bool $espaceInsecable = true
+ bool $nonBreakingSpace = true
): string {
$siret = $this->value;
$siret = preg_replace('/^(\d{3})(\d{3})(\d{3})(\d{5})$/', '$1 $2 $3 $4', $siret);
- if ($espaceInsecable) {
- $siret = str_replace(' ', "\xC2\xA0", $siret); // Espace insécable
+ if ($nonBreakingSpace) {
+ $siret = str_replace(' ', "\xC2\xA0", $siret); // non breaking space
}
return $siret;
diff --git a/component/Company/Name.php b/component/Company/Name.php
index 803f479..70ebbdf 100644
--- a/component/Company/Name.php
+++ b/component/Company/Name.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Company;
-class Name extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Name extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^.{1,}$/';
diff --git a/component/Geography/Fr/CodeCommune.php b/component/Geography/Fr/CodeCommune.php
index 46ebd28..8a7bd50 100755
--- a/component/Geography/Fr/CodeCommune.php
+++ b/component/Geography/Fr/CodeCommune.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Geography\Fr;
-class CodeCommune extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class CodeCommune extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^[0-9][0-9AB][0-9]{3}$/';
diff --git a/component/Geography/Fr/CodePostal.php b/component/Geography/Fr/CodePostal.php
index f0983d1..1be0998 100755
--- a/component/Geography/Fr/CodePostal.php
+++ b/component/Geography/Fr/CodePostal.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Geography\Fr;
-class CodePostal extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class CodePostal extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^\d{5}$/';
diff --git a/component/Geography/Fr/NumeroDepartement.php b/component/Geography/Fr/NumeroDepartement.php
index 97f49a1..a9dd982 100755
--- a/component/Geography/Fr/NumeroDepartement.php
+++ b/component/Geography/Fr/NumeroDepartement.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Geography\Fr;
-class NumeroDepartement extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class NumeroDepartement extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^(9[7-8][0-9])|([0-9]{2})|(2[AB])$/';
diff --git a/component/Geography/GpsCoordinates.php b/component/Geography/GpsCoordinates.php
index da2fb14..8ce2408 100644
--- a/component/Geography/GpsCoordinates.php
+++ b/component/Geography/GpsCoordinates.php
@@ -6,12 +6,12 @@
use Phant\Error\NotCompliant;
-class GpsCoordinates
+readonly class GpsCoordinates
{
// Format : WGS84 (https://en.wikipedia.org/wiki/World_Geodetic_System)
final public function __construct(
- public readonly float $latitude,
- public readonly float $longitude
+ public float $latitude,
+ public float $longitude
) {
if ($latitude > 90 || $latitude < -90 || $longitude > 180 || $longitude < -180) {
throw new NotCompliant('GPS coordinates: ' . $latitude . ';' . $longitude);
diff --git a/component/Id/Uuid.php b/component/Id/Uuid.php
index f0a2fa6..7f8944a 100644
--- a/component/Id/Uuid.php
+++ b/component/Id/Uuid.php
@@ -8,7 +8,7 @@
use Ramsey\Uuid\Uuid as UuidBuilder;
use Ramsey\Uuid\Exception\InvalidUuidStringException;
-class Uuid extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Uuid extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}/';
diff --git a/component/Key/Ssl.php b/component/Key/Ssl.php
index 17f7bbf..118b702 100644
--- a/component/Key/Ssl.php
+++ b/component/Key/Ssl.php
@@ -6,11 +6,11 @@
use Phant\Error\NotCompliant;
-final class Ssl
+final readonly class Ssl
{
public function __construct(
- public readonly string $private,
- public readonly string $public
+ public string $private,
+ public string $public
) {
}
diff --git a/component/Money/Price.php b/component/Money/Price.php
index 8c6597b..e4aa454 100644
--- a/component/Money/Price.php
+++ b/component/Money/Price.php
@@ -4,17 +4,17 @@
namespace Phant\DataStructure\Money;
-class Price
+readonly class Price
{
public function __construct(
- public readonly float $amount,
- public readonly ?Currency $currency,
- public readonly ?string $unit
+ public float $amount,
+ public ?Currency $currency,
+ public ?string $unit
) {
}
public function getFormatted(
- bool $espaceInsecable = true
+ bool $nonBreakingSpace = true
): string {
$price = number_format($this->amount, 2, ',', ' ');
@@ -26,8 +26,8 @@ public function getFormatted(
$price .= '/' . $this->unit;
}
- if ($espaceInsecable) {
- $price = str_replace(' ', "\xC2\xA0", $price); // Espace insécable
+ if ($nonBreakingSpace) {
+ $price = str_replace(' ', "\xC2\xA0", $price); // non breaking space
}
return $price;
diff --git a/component/Number/Grade.php b/component/Number/Grade.php
index 5fddfc2..91ed931 100644
--- a/component/Number/Grade.php
+++ b/component/Number/Grade.php
@@ -6,11 +6,11 @@
use Phant\Error\NotCompliant;
-class Grade
+readonly class Grade
{
public function __construct(
- public readonly int $position,
- public readonly int $scale
+ public int $position,
+ public int $scale
) {
if ($position < 0) {
throw new NotCompliant('Note : ' . $position);
diff --git a/component/Number/Rate.php b/component/Number/Rate.php
index c44bdc5..2080ddc 100644
--- a/component/Number/Rate.php
+++ b/component/Number/Rate.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Number;
-class Rate extends \Phant\DataStructure\Abstract\Value\Decimal
+readonly class Rate extends \Phant\DataStructure\Abstract\Value\Decimal
{
public function __toString(
) {
diff --git a/component/Person/Birthday.php b/component/Person/Birthday.php
index 62780ea..b1fba70 100644
--- a/component/Person/Birthday.php
+++ b/component/Person/Birthday.php
@@ -4,6 +4,6 @@
namespace Phant\DataStructure\Person;
-class Birthday extends \Phant\DataStructure\Time\Date
+readonly class Birthday extends \Phant\DataStructure\Time\Date
{
}
diff --git a/component/Person/Firstname.php b/component/Person/Firstname.php
index 007702d..b056585 100644
--- a/component/Person/Firstname.php
+++ b/component/Person/Firstname.php
@@ -6,7 +6,7 @@
use Phant\Error\NotCompliant;
-class Firstname extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Firstname extends \Phant\DataStructure\Abstract\Value\Varchar
{
public function __construct(
string $firstname
diff --git a/component/Person/Lastname.php b/component/Person/Lastname.php
index 8e018af..a2bc5b6 100644
--- a/component/Person/Lastname.php
+++ b/component/Person/Lastname.php
@@ -6,7 +6,7 @@
use Phant\Error\NotCompliant;
-class Lastname extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Lastname extends \Phant\DataStructure\Abstract\Value\Varchar
{
public function __construct(
string $lastname
diff --git a/component/Time/Date.php b/component/Time/Date.php
index 92ea771..c79b681 100644
--- a/component/Time/Date.php
+++ b/component/Time/Date.php
@@ -6,11 +6,11 @@
use Phant\Error\NotCompliant;
-class Date
+readonly class Date
{
- public readonly int $time;
- public readonly string $date;
- public readonly string $format;
+ public int $time;
+ public string $date;
+ public string $format;
public function __construct(
int|string $date,
diff --git a/component/Time/DateInterval.php b/component/Time/DateInterval.php
index 5efbb27..36e8773 100644
--- a/component/Time/DateInterval.php
+++ b/component/Time/DateInterval.php
@@ -8,19 +8,19 @@
use Phant\DataStructure\Time\Duration;
use Phant\Error\NotCompliant;
-class DateInterval
+readonly class DateInterval
{
- public readonly ?Duration $duration;
+ public ?Duration $duration;
public function __construct(
- public readonly ?Date $from,
- public readonly ?Date $to
+ public ?Date $from,
+ public ?Date $to
) {
if (!$from && !$to) {
- throw new NotCompliant('Date intervals: from ' . $from . ' to' . $to);
+ throw new NotCompliant('A date interval must contain at least one date');
}
if ($from && $to && $from->time > $to->time) {
- throw new NotCompliant('From can be after To : ' . $from . '/' . $to);
+ throw new NotCompliant('Invalid date interval: from date (' . $from . ') cannot be after to date (' . $to . ')');
}
$this->duration = ($this->from && $this->to) ? new Duration(($this->to->time + Duration::DAY - 1) - $this->from->time) : null;
diff --git a/component/Time/DateTime.php b/component/Time/DateTime.php
index 33af05d..2491607 100644
--- a/component/Time/DateTime.php
+++ b/component/Time/DateTime.php
@@ -4,9 +4,7 @@
namespace Phant\DataStructure\Time;
-use Phant\Error\NotCompliant;
-
-class DateTime extends \Phant\DataStructure\Time\Date
+readonly class DateTime extends \Phant\DataStructure\Time\Date
{
public function __construct(
int|string $date,
diff --git a/component/Time/DateTimeInterval.php b/component/Time/DateTimeInterval.php
index 83a3a78..b90f08f 100644
--- a/component/Time/DateTimeInterval.php
+++ b/component/Time/DateTimeInterval.php
@@ -8,19 +8,19 @@
use Phant\DataStructure\Time\Duration;
use Phant\Error\NotCompliant;
-class DateTimeInterval
+readonly class DateTimeInterval
{
- public readonly ?Duration $duration;
+ public ?Duration $duration;
public function __construct(
- public readonly ?DateTime $from,
- public readonly ?DateTime $to
+ public ?DateTime $from,
+ public ?DateTime $to
) {
if (!$from && !$to) {
- throw new NotCompliant('Date time intervals: from ' . $from . ' to' . $to);
+ throw new NotCompliant('A datetime interval must contain at least one date');
}
if ($from && $to && $from->time > $to->time) {
- throw new NotCompliant('From can be after To : ' . $from . '/' . $to);
+ throw new NotCompliant('Invalid datetime interval: from datetime (' . $from . ') cannot be after to datetime (' . $to . ')');
}
$this->duration = ($this->from && $this->to) ? new Duration($this->to->time - $this->from->time) : null;
diff --git a/component/Time/Duration.php b/component/Time/Duration.php
index 794500f..e83329d 100644
--- a/component/Time/Duration.php
+++ b/component/Time/Duration.php
@@ -4,32 +4,19 @@
namespace Phant\DataStructure\Time;
-class Duration
+readonly class Duration
{
- // Duration in secondes
- public const MINUTE = 60;
- public const HOUR = 3600;
- public const DAY = 86400;
- public const MONTH = 2628000;
- public const YEAR = 31536000;
-
- public const SECOND_LABEL = 's';
- public const SECOND_LABEL_PLURAL = 's';
- public const MINUTE_LABEL = 'min';
- public const MINUTE_LABEL_PLURAL = 'min';
- public const HOUR_LABEL = 'h';
- public const HOUR_LABEL_PLURAL = 'h';
- public const DAY_LABEL = 'day';
- public const DAY_LABEL_PLURAL = 'days';
- public const MONTH_LABEL = 'month';
- public const MONTH_LABEL_PLURAL = 'months';
- public const YEAR_LABEL = 'year';
- public const YEAR_LABEL_PLURAL = 'years';
-
- public readonly string $label;
+ // Duration in seconds
+ public const MINUTE = 60;
+ public const HOUR = 3600;
+ public const DAY = 86400;
+ public const MONTH = 2628000;
+ public const YEAR = 31536000;
+
+ public string $label;
public function __construct(
- public readonly int $value
+ public int $value
) {
$this->label = $this->buildLabel();
}
@@ -48,7 +35,7 @@ protected function buildLabel(
if ($remainingTime >= self::YEAR) {
$years = intval($remainingTime / self::YEAR);
if ($years) {
- $labels[] = $years . ' ' . ($years > 1 ? self::YEAR_LABEL_PLURAL : self::YEAR_LABEL);
+ $labels[] = $years . ' ' . ($years > 1 ? Unit::Year->getLabelPlural() : Unit::Year->getLabel());
$remainingTime = $remainingTime % self::YEAR;
}
}
@@ -56,7 +43,7 @@ protected function buildLabel(
if ($remainingTime >= self::MONTH) {
$months = intval($remainingTime / self::MONTH);
if ($months) {
- $labels[] = $months . ' ' . ($months > 1 ? self::MONTH_LABEL_PLURAL : self::MONTH_LABEL);
+ $labels[] = $months . ' ' . ($months > 1 ? Unit::Month->getLabelPlural() : Unit::Month->getLabel());
$remainingTime = $remainingTime % self::MONTH;
}
}
@@ -64,7 +51,7 @@ protected function buildLabel(
if ($remainingTime >= self::DAY) {
$days = intval($remainingTime / self::DAY);
if ($days) {
- $labels[] = $days . ' ' . ($days > 1 ? self::DAY_LABEL_PLURAL : self::DAY_LABEL);
+ $labels[] = $days . ' ' . ($days > 1 ? Unit::Day->getLabelPlural() : Unit::Day->getLabel());
$remainingTime = $remainingTime % self::DAY;
}
}
@@ -72,7 +59,7 @@ protected function buildLabel(
if ($remainingTime >= self::HOUR) {
$hours = intval($remainingTime / self::HOUR);
if ($hours) {
- $labels[] = $hours . ' ' . ($hours > 1 ? self::HOUR_LABEL_PLURAL : self::HOUR_LABEL);
+ $labels[] = $hours . ' ' . ($hours > 1 ? Unit::Hour->getLabelPlural() : Unit::Hour->getLabel());
$remainingTime = $remainingTime % self::HOUR;
}
}
@@ -80,7 +67,7 @@ protected function buildLabel(
if ($remainingTime >= self::MINUTE) {
$minutes = intval($remainingTime / self::MINUTE);
if ($minutes) {
- $labels[] = $minutes . ' ' . ($minutes > 1 ? self::MINUTE_LABEL_PLURAL : self::MINUTE_LABEL);
+ $labels[] = $minutes . ' ' . ($minutes > 1 ? Unit::Minute->getLabelPlural() : Unit::Minute->getLabel());
$remainingTime = $remainingTime % self::MINUTE;
}
}
@@ -88,7 +75,7 @@ protected function buildLabel(
if ($remainingTime > 0) {
$secondes = intval($remainingTime);
if ($secondes) {
- $labels[] = $secondes . ' ' . ($secondes > 1 ? self::SECOND_LABEL_PLURAL : self::SECOND_LABEL);
+ $labels[] = $secondes . ' ' . ($secondes > 1 ? Unit::Second->getLabelPlural() : Unit::Second->getLabel());
}
}
diff --git a/component/Time/Unit.php b/component/Time/Unit.php
new file mode 100644
index 0000000..7186d24
--- /dev/null
+++ b/component/Time/Unit.php
@@ -0,0 +1,39 @@
+ 's',
+ self::Minute => 'min',
+ self::Hour => 'h',
+ self::Day => 'day',
+ self::Month => 'month',
+ self::Year => 'year',
+ };
+ }
+
+ public function getLabelPlural(
+ ): string {
+ return match ($this) {
+ self::Second => 's',
+ self::Minute => 'min',
+ self::Hour => 'h',
+ self::Day => 'days',
+ self::Month => 'months',
+ self::Year => 'years',
+ };
+ }
+}
diff --git a/component/Token/ApiKey.php b/component/Token/ApiKey.php
index 0ac00fd..e2079d2 100644
--- a/component/Token/ApiKey.php
+++ b/component/Token/ApiKey.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Token;
-class ApiKey extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class ApiKey extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^[0-9a-zA-Z]{8}\.[0-9a-zA-Z]{64}$/';
diff --git a/component/Token/Jwt.php b/component/Token/Jwt.php
index 9bf3f0f..ebce335 100644
--- a/component/Token/Jwt.php
+++ b/component/Token/Jwt.php
@@ -10,7 +10,7 @@
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
-class Jwt extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Jwt extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PAYLOAD_CREATION_TIME = 'iat';
public const PAYLOAD_LIFE_TIME = 'exp';
diff --git a/component/Web/DomainName.php b/component/Web/DomainName.php
index 60dda6e..c57b0dd 100644
--- a/component/Web/DomainName.php
+++ b/component/Web/DomainName.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Web;
-class DomainName extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class DomainName extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})|localhost$/';
diff --git a/component/Web/Email.php b/component/Web/Email.php
index 5553bc6..50d62db 100644
--- a/component/Web/Email.php
+++ b/component/Web/Email.php
@@ -7,7 +7,7 @@
use Phant\DataStructure\Web\EmailAddressAndName;
use Phant\DataStructure\Web\EmailAttachmentList;
-class Email
+readonly class Email
{
final public function __construct(
public string $subject,
diff --git a/component/Web/EmailAddress.php b/component/Web/EmailAddress.php
index 83fd623..eac373b 100644
--- a/component/Web/EmailAddress.php
+++ b/component/Web/EmailAddress.php
@@ -7,7 +7,7 @@
use Phant\DataStructure\Web\DomainName;
use Phant\DataStructure\Web\UserName;
-class EmailAddress extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class EmailAddress extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/';
diff --git a/component/Web/EmailAddressAndName.php b/component/Web/EmailAddressAndName.php
index a421165..9f39df4 100644
--- a/component/Web/EmailAddressAndName.php
+++ b/component/Web/EmailAddressAndName.php
@@ -4,11 +4,11 @@
namespace Phant\DataStructure\Web;
-class EmailAddressAndName
+readonly class EmailAddressAndName
{
public function __construct(
- public readonly EmailAddress $emailAddress,
- public readonly ?string $name
+ public EmailAddress $emailAddress,
+ public ?string $name
) {
}
diff --git a/component/Web/EmailAttachment.php b/component/Web/EmailAttachment.php
index 01342f8..acfc15a 100644
--- a/component/Web/EmailAttachment.php
+++ b/component/Web/EmailAttachment.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Web;
-class EmailAttachment
+readonly class EmailAttachment
{
final public function __construct(
public string $name,
diff --git a/component/Web/Url.php b/component/Web/Url.php
index 4426527..dae6afa 100644
--- a/component/Web/Url.php
+++ b/component/Web/Url.php
@@ -4,7 +4,9 @@
namespace Phant\DataStructure\Web;
-class Url extends \Phant\DataStructure\Abstract\Value\Varchar
+use Phant\Error\NotCompliant;
+
+class Url
{
public const PATTERN = '%\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))%s';
@@ -18,13 +20,20 @@ class Url extends \Phant\DataStructure\Abstract\Value\Varchar
protected ?string $fragment;
public function __construct(
- string $url
+ public readonly string $value
) {
- parent::__construct($url);
+ if (defined(get_class($this) . '::PATTERN') && static::PATTERN && !preg_match(static::PATTERN, $value)) {
+ throw new NotCompliant('Value : ' . $value);
+ }
$this->decompose();
}
+ public function __toString(
+ ): string {
+ return $this->value;
+ }
+
public function getScheme(
): ?string {
return $this->scheme;
diff --git a/component/Web/UserAgent/Browser.php b/component/Web/UserAgent/Browser.php
index 0d929a8..de49fb2 100644
--- a/component/Web/UserAgent/Browser.php
+++ b/component/Web/UserAgent/Browser.php
@@ -4,11 +4,11 @@
namespace Phant\DataStructure\Web\UserAgent;
-class Browser
+readonly class Browser
{
public function __construct(
- public readonly BrowserFamily $family,
- public readonly Version $version
+ public BrowserFamily $family,
+ public Version $version
) {
}
}
diff --git a/component/Web/UserAgent/OperatingSystem.php b/component/Web/UserAgent/OperatingSystem.php
index 9b96749..3118d0a 100644
--- a/component/Web/UserAgent/OperatingSystem.php
+++ b/component/Web/UserAgent/OperatingSystem.php
@@ -4,11 +4,11 @@
namespace Phant\DataStructure\Web\UserAgent;
-class OperatingSystem
+readonly class OperatingSystem
{
public function __construct(
- public readonly OperatingSystemFamily $family,
- public readonly Version $version
+ public OperatingSystemFamily $family,
+ public Version $version
) {
}
}
diff --git a/component/Web/UserAgent/OperatingSystemFamily.php b/component/Web/UserAgent/OperatingSystemFamily.php
index bc77d4a..1dde805 100644
--- a/component/Web/UserAgent/OperatingSystemFamily.php
+++ b/component/Web/UserAgent/OperatingSystemFamily.php
@@ -12,6 +12,5 @@ enum OperatingSystemFamily: string
case Android = 'Android';
case iOS = 'iOS';
case iPadOS = 'iPadOS';
-
case Other = 'Other';
}
diff --git a/component/Web/UserAgent/Version.php b/component/Web/UserAgent/Version.php
index 39af951..4f65c53 100644
--- a/component/Web/UserAgent/Version.php
+++ b/component/Web/UserAgent/Version.php
@@ -6,6 +6,6 @@
use Phant\DataStructure\Abstract\Value\Varchar;
-class Version extends Varchar
+readonly class Version extends Varchar
{
}
diff --git a/component/Web/UserName.php b/component/Web/UserName.php
index 5056328..6c08337 100644
--- a/component/Web/UserName.php
+++ b/component/Web/UserName.php
@@ -4,7 +4,7 @@
namespace Phant\DataStructure\Web;
-class UserName extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class UserName extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/i';
diff --git a/composer.json b/composer.json
index 1652a81..b9a2f94 100644
--- a/composer.json
+++ b/composer.json
@@ -10,14 +10,14 @@
}
],
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"phant/error": "1.*",
"ramsey/uuid": "4.*",
- "firebase/php-jwt": "6.*"
+ "firebase/php-jwt": "7.*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.*",
- "phpstan/phpstan": "1.*",
+ "phpstan/phpstan": "2.*",
"phpunit/phpunit": "12.*"
},
"scripts": {
diff --git a/phpunit.xml b/phpunit.xml
index 23e2dfb..6fd795e 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,12 +1,13 @@
-
-
-
- test
-
-
-
-
- component
-
-
-
\ No newline at end of file
+
+
+
+
+ test
+
+
+
+
+ component
+
+
+
diff --git a/test/Abstract/Fixture/Value/Boolean.php b/test/Abstract/Fixture/Value/Boolean.php
index 3d06cac..c159d33 100644
--- a/test/Abstract/Fixture/Value/Boolean.php
+++ b/test/Abstract/Fixture/Value/Boolean.php
@@ -4,6 +4,6 @@
namespace Test\Abstract\Fixture\Value;
-class Boolean extends \Phant\DataStructure\Abstract\Value\Boolean
+readonly class Boolean extends \Phant\DataStructure\Abstract\Value\Boolean
{
}
diff --git a/test/Abstract/Fixture/Value/Decimal.php b/test/Abstract/Fixture/Value/Decimal.php
index 52ee0f3..9e13e5a 100644
--- a/test/Abstract/Fixture/Value/Decimal.php
+++ b/test/Abstract/Fixture/Value/Decimal.php
@@ -4,6 +4,6 @@
namespace Test\Abstract\Fixture\Value;
-class Decimal extends \Phant\DataStructure\Abstract\Value\Decimal
+readonly class Decimal extends \Phant\DataStructure\Abstract\Value\Decimal
{
}
diff --git a/test/Abstract/Fixture/Value/Integer.php b/test/Abstract/Fixture/Value/Integer.php
index 2dcc301..3089c15 100644
--- a/test/Abstract/Fixture/Value/Integer.php
+++ b/test/Abstract/Fixture/Value/Integer.php
@@ -4,6 +4,6 @@
namespace Test\Abstract\Fixture\Value;
-class Integer extends \Phant\DataStructure\Abstract\Value\Integer
+readonly class Integer extends \Phant\DataStructure\Abstract\Value\Integer
{
}
diff --git a/test/Abstract/Fixture/Value/Varchar.php b/test/Abstract/Fixture/Value/Varchar.php
index a3fb3a2..0ab30cd 100644
--- a/test/Abstract/Fixture/Value/Varchar.php
+++ b/test/Abstract/Fixture/Value/Varchar.php
@@ -4,7 +4,7 @@
namespace Test\Abstract\Fixture\Value;
-class Varchar extends \Phant\DataStructure\Abstract\Value\Varchar
+readonly class Varchar extends \Phant\DataStructure\Abstract\Value\Varchar
{
public const PATTERN = '/^[a-zA-Z !]{2,}$/';
}
diff --git a/test/Company/EuidTest.php b/test/Company/EuidTest.php
new file mode 100644
index 0000000..623bf2d
--- /dev/null
+++ b/test/Company/EuidTest.php
@@ -0,0 +1,137 @@
+assertEquals('FR001.512747395', (string)$euid);
+
+ $this->assertIsString($euid->value);
+ $this->assertEquals('FR001.512747395', $euid->value);
+
+ $this->assertIsString($euid->getFormatted());
+ $this->assertEquals('FR 001 . 512 747 395', $euid->getFormatted(false));
+ }
+
+ public function testFormattedWithoutNonBreakingSpace(): void
+ {
+ $euid = new Euid('FR001.512747395');
+
+ $formatted = $euid->getFormatted(false);
+ $this->assertEquals('FR 001 . 512 747 395', $formatted);
+ $this->assertStringNotContainsString("\xC2\xA0", $formatted);
+ }
+
+ public function testFormattedWithNonBreakingSpace(): void
+ {
+ $euid = new Euid('FR001.512747395');
+
+ $formatted = $euid->getFormatted(true);
+ $this->assertEquals("FR\xC2\xA0001\xC2\xA0.\xC2\xA0512\xC2\xA0747\xC2\xA0395", $formatted);
+ }
+
+ public function testExtractParts(): void
+ {
+ $euid = new Euid('FR001.512747395');
+ $parts = $euid->extractParts();
+
+ $this->assertEquals('FR', $parts->countryCode);
+ $this->assertEquals('001', $parts->registryCode);
+ $this->assertEquals('.', $parts->separator);
+ $this->assertEquals('512747395', $parts->companyCode);
+ }
+
+ public function testConstructorWithNonComplianceCheck(): void
+ {
+ // Test avec un SIREN valide français
+ $euid = new Euid('FR001.512747395', true);
+ $this->assertEquals('FR001.512747395', (string)$euid);
+
+ // Test sans vérification de compliance
+ $euid = new Euid('FR001.123456789', false);
+ $this->assertEquals('FR001.123456789', (string)$euid);
+ }
+
+ public function testInvalidCountryCode(): void
+ {
+ $this->expectException(NotCompliant::class);
+ $this->expectExceptionMessage('Not compliant country code');
+
+ new Euid('XX001.512747395');
+ }
+
+ public function testInvalidFrenchSiren(): void
+ {
+ $this->expectException(NotCompliant::class);
+
+ // SIREN invalide avec vérification activée
+ new Euid('FR001.123456789', true);
+ }
+
+ public function testValidEuropeanCountryCodes(): void
+ {
+ $validCountryCodes = ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK'];
+
+ foreach ($validCountryCodes as $countryCode) {
+ $euid = new Euid("{$countryCode}001.123456789", false);
+ $this->assertEquals("{$countryCode}001.123456789", (string)$euid);
+ }
+ }
+
+ public function testCompanyCodeFormatting(): void
+ {
+ // Test avec différentes longueurs de codes entreprise
+ $testCases = [
+ ['FR001.12345', '12 345'], // 5 digits
+ ['FR001.123456', '123 456'], // 6 digits
+ ['FR001.1234567', '123 4567'], // 7 digits
+ ['FR001.12345678', '12 345 678'], // 8 digits
+ ['FR001.123456789', '123 456 789'], // 9 digits
+ ];
+
+ foreach ($testCases as [$input, $expectedFormatting]) {
+ $euid = new Euid($input, false);
+ $formatted = $euid->getFormatted(false);
+ $this->assertStringContainsString($expectedFormatting, $formatted);
+ }
+ }
+
+ public function testPatternConformity(): void
+ {
+ $euid = new Euid('FR001.512747395');
+
+ $this->assertMatchesRegularExpression(Euid::PATTERN, $euid->value);
+ }
+
+ public function testStringRepresentation(): void
+ {
+ $euid = new Euid('DE002.987654321', false);
+
+ // Test du cast en string
+ $this->assertEquals('DE002.987654321', (string)$euid);
+
+ // Test de la propriété value
+ $this->assertEquals('DE002.987654321', $euid->value);
+ }
+
+ public function testWithDifferentRegistryCodes(): void
+ {
+ $euid1 = new Euid('FR001.512747395');
+ $euid2 = new Euid('FR999.512747395');
+
+ $parts1 = $euid1->extractParts();
+ $parts2 = $euid2->extractParts();
+
+ $this->assertEquals('001', $parts1->registryCode);
+ $this->assertEquals('999', $parts2->registryCode);
+ }
+}
diff --git a/test/Key/SslTest.php b/test/Key/SslTest.php
index a0d5376..fd2eae9 100644
--- a/test/Key/SslTest.php
+++ b/test/Key/SslTest.php
@@ -103,14 +103,38 @@ public function testEncryptInvalid(): void
{
$this->expectException(NotCompliant::class);
- $result = $this->fixtureInvalid->encrypt('Foo bar');
+ // Set error handler to suppress expected openssl warnings
+ set_error_handler(function ($severity, $message, $filename, $lineno) {
+ if (strpos($message, 'openssl_private_encrypt') !== false) {
+ return true; // Suppress this warning
+ }
+ return false; // Let other errors/warnings through
+ });
+
+ try {
+ $result = $this->fixtureInvalid->encrypt('Foo bar');
+ } finally {
+ restore_error_handler();
+ }
}
public function testEncryptInvalidBis(): void
{
$this->expectException(NotCompliant::class);
- $result = $this->fixtureInvalid->encrypt('');
+ // Set error handler to suppress expected openssl warnings
+ set_error_handler(function ($severity, $message, $filename, $lineno) {
+ if (strpos($message, 'openssl_private_encrypt') !== false) {
+ return true; // Suppress this warning
+ }
+ return false; // Let other errors/warnings through
+ });
+
+ try {
+ $result = $this->fixtureInvalid->encrypt('');
+ } finally {
+ restore_error_handler();
+ }
}
public function testDecrypt(): void
@@ -126,8 +150,20 @@ public function testDecryptInvalid(): void
{
$this->expectException(NotCompliant::class);
- $result = $this->fixtureInvalid->decrypt(
- $this->fixture->encrypt('Foo bar')
- );
+ // Set error handler to suppress expected openssl warnings
+ set_error_handler(function ($severity, $message, $filename, $lineno) {
+ if (strpos($message, 'openssl_public_decrypt') !== false) {
+ return true; // Suppress this warning
+ }
+ return false; // Let other errors/warnings through
+ });
+
+ try {
+ $result = $this->fixtureInvalid->decrypt(
+ $this->fixture->encrypt('Foo bar')
+ );
+ } finally {
+ restore_error_handler();
+ }
}
}
diff --git a/test/Time/UnitTest.php b/test/Time/UnitTest.php
new file mode 100644
index 0000000..5034886
--- /dev/null
+++ b/test/Time/UnitTest.php
@@ -0,0 +1,43 @@
+assertCount(6, $cases);
+ $this->assertContains(Unit::Second, $cases);
+ $this->assertContains(Unit::Minute, $cases);
+ $this->assertContains(Unit::Hour, $cases);
+ $this->assertContains(Unit::Day, $cases);
+ $this->assertContains(Unit::Month, $cases);
+ $this->assertContains(Unit::Year, $cases);
+ }
+
+ public function testGetLabelSingular(): void
+ {
+ $this->assertEquals('s', Unit::Second->getLabel());
+ $this->assertEquals('min', Unit::Minute->getLabel());
+ $this->assertEquals('h', Unit::Hour->getLabel());
+ $this->assertEquals('day', Unit::Day->getLabel());
+ $this->assertEquals('month', Unit::Month->getLabel());
+ $this->assertEquals('year', Unit::Year->getLabel());
+ }
+
+ public function testGetLabelPlural(): void
+ {
+ $this->assertEquals('s', Unit::Second->getLabelPlural());
+ $this->assertEquals('min', Unit::Minute->getLabelPlural());
+ $this->assertEquals('h', Unit::Hour->getLabelPlural());
+ $this->assertEquals('days', Unit::Day->getLabelPlural());
+ $this->assertEquals('months', Unit::Month->getLabelPlural());
+ $this->assertEquals('years', Unit::Year->getLabelPlural());
+ }
+}
diff --git a/test/Token/JwtTest.php b/test/Token/JwtTest.php
index 9091f52..305374e 100644
--- a/test/Token/JwtTest.php
+++ b/test/Token/JwtTest.php
@@ -11,28 +11,43 @@ final class JwtTest extends \PHPUnit\Framework\TestCase
{
public const PRIVATE_KEY = <<