diff --git a/composer.json b/composer.json index cae9b1c..99c1f74 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "ext-json": "*", "ext-sockets": "*", "doctrine/collections": "^2.1", - "doctrine/dbal": "^3.8", + "doctrine/dbal": "^4.0", "psr/log": "^3.0", "psr/simple-cache": "^3.0", "symfony/event-dispatcher": "^6.0|^7.0" @@ -26,7 +26,7 @@ "kubawerlos/php-cs-fixer-custom-fixers": "^3.19", "monolog/monolog": "^3.5", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", + "phpunit/phpunit": "^11.0", "symplify/easy-coding-standard": "^12.1" }, "license": "MIT", diff --git a/src/MySQLReplication/BinLog/BinLogSocketConnect.php b/src/MySQLReplication/BinLog/BinLogSocketConnect.php index 9b097b9..7755f38 100644 --- a/src/MySQLReplication/BinLog/BinLogSocketConnect.php +++ b/src/MySQLReplication/BinLog/BinLogSocketConnect.php @@ -192,9 +192,14 @@ private function getBinlogStream(): void $this->executeSQL('SET @master_binlog_checksum = @@global.binlog_checksum'); } + if ($this->config->heartbeatPeriod > 0.00) { // master_heartbeat_period is in nanoseconds - $this->executeSQL('SET @master_heartbeat_period = ' . $this->config->heartbeatPeriod * 1000000000); + if (version_compare($this->repository->getVersion(), '8.4.0') >= 0) { + $this->executeSQL('SET @source_heartbeat_period = ' . $this->config->heartbeatPeriod * 1000000000); + } else { + $this->executeSQL('SET @master_heartbeat_period = ' . $this->config->heartbeatPeriod * 1000000000); + } $this->logger->debug('Heartbeat period set to ' . $this->config->heartbeatPeriod . ' seconds'); } diff --git a/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php b/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php index 3a8119f..a709767 100644 --- a/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php +++ b/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php @@ -286,7 +286,7 @@ public function readDouble(): float public function readTableId(): string { - return $this->unpackUInt64($this->read(self::UNSIGNED_INT48_LENGTH) . chr(0) . chr(0)); + return (string)$this->unpackUInt64($this->read(self::UNSIGNED_INT48_LENGTH) . chr(0) . chr(0)); } public function isComplete(int $size): bool diff --git a/src/MySQLReplication/Config/Config.php b/src/MySQLReplication/Config/Config.php index 8542531..916f434 100644 --- a/src/MySQLReplication/Config/Config.php +++ b/src/MySQLReplication/Config/Config.php @@ -106,7 +106,7 @@ public function validate(): void public function checkDataBasesOnly(string $database): bool { return ($this->databasesOnly !== [] && !in_array($database, $this->databasesOnly, true)) - || ($this->databasesRegex !== [] && !self::matchNames($database, $this->databasesRegex)); + || ($this->databasesRegex !== [] && !self::matchNames($database, $this->databasesRegex)); } @@ -116,17 +116,6 @@ public function checkTablesOnly(string $table): bool || ($this->tablesRegex !== [] && !self::matchNames($table, $this->tablesRegex)); } - private static function matchNames(string $name, array $patterns): bool - { - foreach ($patterns as $pattern) { - if (preg_match($pattern, $name)) { - return true; - } - } - - return false; - } - public function checkEvent(int $type): bool { if ($this->eventsOnly !== [] && !in_array($type, $this->eventsOnly, true)) { @@ -144,4 +133,15 @@ 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; + } } diff --git a/src/MySQLReplication/Event/RotateEvent.php b/src/MySQLReplication/Event/RotateEvent.php index 0e0d2b9..58f3118 100644 --- a/src/MySQLReplication/Event/RotateEvent.php +++ b/src/MySQLReplication/Event/RotateEvent.php @@ -13,7 +13,7 @@ class RotateEvent extends EventCommon { public function makeRotateEventDTO(): RotateDTO { - $binFilePos = $this->binaryDataReader->readUInt64(); + $binFilePos = (string)$this->binaryDataReader->readUInt64(); $binFileName = $this->binaryDataReader->read( $this->eventInfo->getSizeNoHeader() - $this->getSizeToRemoveByVersion() ); diff --git a/src/MySQLReplication/Event/RowEvent/RowEvent.php b/src/MySQLReplication/Event/RowEvent/RowEvent.php index e2f27ee..d5cbf77 100644 --- a/src/MySQLReplication/Event/RowEvent/RowEvent.php +++ b/src/MySQLReplication/Event/RowEvent/RowEvent.php @@ -416,7 +416,7 @@ public function makeUpdateRowsDTO(): ?UpdateRowsDTO protected function findTableMap(): ?TableMap { - $tableId = $this->binaryDataReader->readTableId(); + $tableId = (string)$this->binaryDataReader->readTableId(); $this->binaryDataReader->advance(2); if (in_array( diff --git a/src/MySQLReplication/Event/XidEvent.php b/src/MySQLReplication/Event/XidEvent.php index 39611fa..a59f981 100644 --- a/src/MySQLReplication/Event/XidEvent.php +++ b/src/MySQLReplication/Event/XidEvent.php @@ -13,6 +13,6 @@ class XidEvent extends EventCommon { public function makeXidDTO(): XidDTO { - return new XidDTO($this->eventInfo, $this->binaryDataReader->readUInt64()); + return new XidDTO($this->eventInfo, (string)$this->binaryDataReader->readUInt64()); } } diff --git a/src/MySQLReplication/Repository/MySQLRepository.php b/src/MySQLReplication/Repository/MySQLRepository.php index 19cfdf6..b38c994 100644 --- a/src/MySQLReplication/Repository/MySQLRepository.php +++ b/src/MySQLReplication/Repository/MySQLRepository.php @@ -37,8 +37,8 @@ public function getFields(string $database, string $table): FieldDTOCollection `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? - ORDER BY - ORDINAL_POSITION + ORDER BY + ORDINAL_POSITION '; return FieldDTOCollection::makeFromArray( @@ -57,15 +57,10 @@ public function isCheckSum(): bool public function getVersion(): string { - $r = ''; - $versions = $this->getConnection() - ->fetchAllAssociative('SHOW VARIABLES LIKE "version%"'); - - foreach ($versions as $version) { - $r .= $version['Value']; - } + $res = $this->getConnection() + ->fetchAssociative('SHOW VARIABLES LIKE "version"'); - return $r; + return $res['Value'] ?? ''; } public function getMasterStatus(): MasterStatusDTO @@ -100,11 +95,8 @@ public function ping(Connection $connection): bool private function getConnection(): Connection { - if ($this->ping($this->connection) === false) { - $this->connection->close(); - $this->connection->connect(); - } - + // In DBAL 4.x, connections handle reconnection automatically + // No need for manual ping/reconnect logic return $this->connection; } } diff --git a/tests/Unit/Repository/MySQLRepositoryTest.php b/tests/Unit/Repository/MySQLRepositoryTest.php index 5ad88ac..1a9d9da 100644 --- a/tests/Unit/Repository/MySQLRepositoryTest.php +++ b/tests/Unit/Repository/MySQLRepositoryTest.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Platforms\MySQLPlatform; use MySQLReplication\Repository\FieldDTOCollection; use MySQLReplication\Repository\MasterStatusDTO; @@ -71,21 +72,13 @@ public function testShouldIsCheckSum(): void public function testShouldGetVersion(): void { $expected = [ - [ - 'Value' => 'foo', - ], - [ - 'Value' => 'bar', - ], - [ - 'Value' => '123', - ], + 'Value' => 'version', ]; - $this->connection->method('fetchAllAssociative') - ->willReturn($expected); + $this->connection->method('fetchAssociative') + ->willReturn($expected); - self::assertEquals('foobar123', $this->mySQLRepositoryTest->getVersion()); + self::assertEquals('version', $this->mySQLRepositoryTest->getVersion()); } public function testShouldGetMasterStatus(): void @@ -104,13 +97,17 @@ public function testShouldGetMasterStatus(): void self::assertEquals(MasterStatusDTO::makeFromArray($expected), $this->mySQLRepositoryTest->getMasterStatus()); } - public function testShouldReconnect(): void + public function testShouldReconnect(): void { // just to cover private getConnection + $exception = $this->createMock(ConnectionException::class); + $this->connection->method('executeQuery') - ->willReturnCallback(static function () { - throw new Exception(''); - }); + ->willThrowException($exception); + + $this->connection->method('fetchAssociative') + ->willReturn(['Value' => 'NONE']); + $this->mySQLRepositoryTest->isCheckSum(); self::assertTrue(true); }