| 
<?php
 declare(strict_types=1);
 
 namespace MySQLReplication\Config;
 
 use JsonSerializable;
 
 readonly class Config implements JsonSerializable
 {
 public function __construct(
 public string $user,
 public string $host,
 public int $port,
 public string $password,
 public string $charset,
 public string $gtid,
 public string $mariaDbGtid,
 public int $slaveId,
 public string $binLogFileName,
 public string $binLogPosition,
 public array $eventsOnly,
 public array $eventsIgnore,
 public array $tablesOnly,
 public array $databasesOnly,
 public int $tableCacheSize,
 public array $custom,
 public float $heartbeatPeriod,
 public string $slaveUuid,
 private array $tablesRegex = [],
 private array $databasesRegex = [],
 ) {
 }
 
 public function validate(): void
 {
 if (!empty($this->host)) {
 $ip = gethostbyname($this->host);
 if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
 throw new ConfigException(ConfigException::IP_ERROR_MESSAGE, ConfigException::IP_ERROR_CODE);
 }
 }
 if (!empty($this->port) && filter_var(
 $this->port,
 FILTER_VALIDATE_INT,
 [
 'options' => [
 'min_range' => 0,
 ],
 ]
 ) === false) {
 throw new ConfigException(ConfigException::PORT_ERROR_MESSAGE, ConfigException::PORT_ERROR_CODE);
 }
 if (!empty($this->gtid)) {
 foreach (explode(',', $this->gtid) as $gtid) {
 if (!(bool)preg_match(
 '/^([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})((?::[0-9-]+)+)$/',
 $gtid,
 $matches
 )) {
 throw new ConfigException(ConfigException::GTID_ERROR_MESSAGE, ConfigException::GTID_ERROR_CODE);
 }
 }
 }
 if (!empty($this->slaveId) && filter_var(
 $this->slaveId,
 FILTER_VALIDATE_INT,
 [
 'options' => [
 'min_range' => 0,
 ],
 ]
 ) === false) {
 throw new ConfigException(
 ConfigException::SLAVE_ID_ERROR_MESSAGE,
 ConfigException::SLAVE_ID_ERROR_CODE
 );
 }
 if (bccomp($this->binLogPosition, '0') === -1) {
 throw new ConfigException(
 ConfigException::BIN_LOG_FILE_POSITION_ERROR_MESSAGE,
 ConfigException::BIN_LOG_FILE_POSITION_ERROR_CODE
 );
 }
 if (filter_var($this->tableCacheSize, FILTER_VALIDATE_INT, [
 'options' => [
 'min_range' => 0,
 ],
 ]) === false) {
 throw new ConfigException(
 ConfigException::TABLE_CACHE_SIZE_ERROR_MESSAGE,
 ConfigException::TABLE_CACHE_SIZE_ERROR_CODE
 );
 }
 if ($this->heartbeatPeriod !== 0.0 && false === (
 $this->heartbeatPeriod >= 0.001 && $this->heartbeatPeriod <= 4294967.0
 )) {
 throw new ConfigException(
 ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE,
 ConfigException::HEARTBEAT_PERIOD_ERROR_CODE
 );
 }
 }
 
 
 public function checkDataBasesOnly(string $database): bool
 {
 return ($this->databasesOnly !== [] && !in_array($database, $this->databasesOnly, true))
 || ($this->databasesRegex !== [] && !self::matchNames($database, $this->databasesRegex));
 }
 
 
 public function checkTablesOnly(string $table): bool
 {
 return ($this->tablesOnly !== [] && !in_array($table, $this->tablesOnly, true))
 || ($this->tablesRegex !== [] && !self::matchNames($table, $this->tablesRegex));
 }
 
 public function checkEvent(int $type): bool
 {
 if ($this->eventsOnly !== [] && !in_array($type, $this->eventsOnly, true)) {
 return false;
 }
 
 if (in_array($type, $this->eventsIgnore, true)) {
 return false;
 }
 
 return true;
 }
 
 public function jsonSerialize(): array
 {
 return get_class_vars(self::class);
 }
 
 private static function matchNames(string $name, array $patterns): bool
 {
 foreach ($patterns as $pattern) {
 if (preg_match($pattern, $name)) {
 return true;
 }
 }
 
 return false;
 }
 }
 
 |