diff --git a/example/benchmark.php b/example/benchmark.php index fe21db4..5bcfef0 100755 --- a/example/benchmark.php +++ b/example/benchmark.php @@ -1,8 +1,8 @@ type; } } \ No newline at end of file diff --git a/src/MySQLReplication/Event/DTO/GTIDLogDTO.php b/src/MySQLReplication/Event/DTO/GTIDLogDTO.php index d4ba500..205f588 100755 --- a/src/MySQLReplication/Event/DTO/GTIDLogDTO.php +++ b/src/MySQLReplication/Event/DTO/GTIDLogDTO.php @@ -19,6 +19,10 @@ class GTIDLogDTO extends EventDTO implements \JsonSerializable * @var string */ private $gtid; + /** + * @var string + */ + private $type = ConstEventsNames::GTID; /** * GTIDLogEventDTO constructor. @@ -58,7 +62,7 @@ public function getGtid() */ public function getType() { - return ConstEventsNames::GTID; + return $this->type; } /** diff --git a/src/MySQLReplication/Event/DTO/QueryDTO.php b/src/MySQLReplication/Event/DTO/QueryDTO.php index 341df3d..cd0db36 100755 --- a/src/MySQLReplication/Event/DTO/QueryDTO.php +++ b/src/MySQLReplication/Event/DTO/QueryDTO.php @@ -20,9 +20,13 @@ class QueryDTO extends EventDTO implements \JsonSerializable */ private $query; /** - * @var + * @var string */ private $database; + /** + * @var string + */ + private $type = ConstEventsNames::QUERY; /** * QueryEventDTO constructor. @@ -73,7 +77,7 @@ public function getQuery() */ public function getType() { - return ConstEventsNames::QUERY; + return $this->type; } /** diff --git a/src/MySQLReplication/Event/DTO/RotateDTO.php b/src/MySQLReplication/Event/DTO/RotateDTO.php index 6678b7d..92b403d 100755 --- a/src/MySQLReplication/Event/DTO/RotateDTO.php +++ b/src/MySQLReplication/Event/DTO/RotateDTO.php @@ -19,6 +19,10 @@ class RotateDTO extends EventDTO implements \JsonSerializable * @var string */ private $next_binlog; + /** + * @var string + */ + private $type = ConstEventsNames::ROTATE; /** * RotateDTO constructor. @@ -58,7 +62,7 @@ public function getNextBinlog() */ public function getType() { - return ConstEventsNames::ROTATE; + return $this->type; } /** diff --git a/src/MySQLReplication/Event/DTO/RowsDTO.php b/src/MySQLReplication/Event/DTO/RowsDTO.php index 9038810..113105d 100755 --- a/src/MySQLReplication/Event/DTO/RowsDTO.php +++ b/src/MySQLReplication/Event/DTO/RowsDTO.php @@ -3,90 +3,53 @@ namespace MySQLReplication\Event\DTO; use MySQLReplication\Event\EventInfo; +use MySQLReplication\Event\RowEvent\TableMap; /** * Class RowsDTO * @package MySQLReplication\DTO */ -class RowsDTO extends EventDTO implements \JsonSerializable +abstract class RowsDTO extends EventDTO implements \JsonSerializable { /** * @var array */ private $values; - /** - * @var string - */ - private $database; - /** - * @var string - */ - private $table; /** * @var int */ - private $affected; + private $changedRows; /** - * @var int + * @var TableMap */ - private $changedRows; + private $tableMap; /** * GTIDLogEventDTO constructor. * @param EventInfo $eventInfo - * @param $database - * @param $table - * @param $affected + * @param TableMap $tableMap * @param $changedRows * @param array $values */ public function __construct( EventInfo $eventInfo, - $database, - $table, - $affected, + TableMap $tableMap, $changedRows, array $values ) { parent::__construct($eventInfo); - $this->database = $database; - $this->table = $table; - $this->affected = $affected; $this->changedRows = $changedRows; $this->values = $values; + $this->tableMap = $tableMap; } /** - * @return array - */ - public function getValues() - { - return $this->values; - } - - /** - * @return string - */ - public function getDatabase() - { - return $this->database; - } - - /** - * @return string - */ - public function getTable() - { - return $this->table; - } - - /** - * @return int + * @return TableMap */ - public function getAffected() + public function getTableMap() { - return $this->affected; + return $this->tableMap; } /** @@ -98,11 +61,11 @@ public function getChangedRows() } /** - * @return string + * @return array */ - public function getType() + public function getValues() { - return ''; + return $this->values; } /** @@ -115,8 +78,8 @@ public function __toString() 'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL . 'Log position: ' . $this->eventInfo->getPos() . PHP_EOL . 'Event size: ' . $this->eventInfo->getSize() . PHP_EOL . - 'Table: ' . $this->table . PHP_EOL . - 'Affected columns: ' . $this->affected . PHP_EOL . + 'Table: ' . $this->tableMap->getTable() . PHP_EOL . + 'Affected columns: ' . $this->tableMap->getColumnsAmount() . PHP_EOL . 'Changed rows: ' . $this->changedRows . PHP_EOL . 'Values: ' . print_r($this->values, true) . PHP_EOL; } diff --git a/src/MySQLReplication/Event/DTO/TableMapDTO.php b/src/MySQLReplication/Event/DTO/TableMapDTO.php index 92ebbac..a0b6c9f 100755 --- a/src/MySQLReplication/Event/DTO/TableMapDTO.php +++ b/src/MySQLReplication/Event/DTO/TableMapDTO.php @@ -4,6 +4,7 @@ use MySQLReplication\Definitions\ConstEventsNames; use MySQLReplication\Event\EventInfo; +use MySQLReplication\Event\RowEvent\TableMap; /** * Class TableMapDTO @@ -11,76 +12,43 @@ */ class TableMapDTO extends EventDTO implements \JsonSerializable { - /** - * @var int - */ - private $tableId; - /** - * @var string - */ - private $database; /** * @var string */ - private $table; + private $type = ConstEventsNames::TABLE_MAP; /** - * @var + * @var TableMap */ - private $columns; + private $tableMap; /** * TableMapDTO constructor. * @param EventInfo $eventInfo - * @param $tableId - * @param $database - * @param $table - * @param $columns + * @param TableMap $tableMap */ public function __construct( EventInfo $eventInfo, - $tableId, - $database, - $table, - $columns + TableMap $tableMap ) { parent::__construct($eventInfo); - $this->tableId = $tableId; - $this->database = $database; - $this->table = $table; - $this->columns = $columns; - } - - /** - * @return int - */ - public function getTableId() - { - return $this->tableId; - } - - /** - * @return string - */ - public function getDatabase() - { - return $this->database; + $this->tableMap = $tableMap; } /** * @return string */ - public function getTable() - { - return $this->table; - } - - /** - * @return int - */ - public function getColumns() + public function __toString() { - return $this->columns; + return PHP_EOL . + '=== Event ' . $this->getType() . ' === ' . PHP_EOL . + 'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL . + 'Log position: ' . $this->eventInfo->getPos() . PHP_EOL . + 'Event size: ' . $this->eventInfo->getSize() . PHP_EOL . + 'Table: ' . $this->tableMap->getTable() . PHP_EOL . + 'Database: ' . $this->tableMap->getDatabase() . PHP_EOL . + 'Table Id: ' . $this->tableMap->getTableId() . PHP_EOL . + 'Columns amount: ' . $this->tableMap->getColumnsAmount() . PHP_EOL; } /** @@ -88,23 +56,7 @@ public function getColumns() */ public function getType() { - return ConstEventsNames::TABLE_MAP; - } - - /** - * @return string - */ - public function __toString() - { - return PHP_EOL . - '=== Event ' . $this->getType() . ' === ' . PHP_EOL . - 'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL . - 'Log position: ' . $this->eventInfo->getPos() . PHP_EOL . - 'Event size: ' . $this->eventInfo->getSize() . PHP_EOL . - 'Table: ' . $this->table . PHP_EOL . - 'Database: ' . $this->database . PHP_EOL . - 'Table Id: ' . $this->tableId . PHP_EOL . - 'Columns: ' . print_r($this->columns, true) . PHP_EOL; + return $this->type; } /** @@ -118,4 +70,12 @@ public function jsonSerialize() { return get_object_vars($this); } + + /** + * @return TableMap + */ + public function getTableMap() + { + return $this->tableMap; + } } \ No newline at end of file diff --git a/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php b/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php index b0190bf..ac40f22 100755 --- a/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php +++ b/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php @@ -10,11 +10,16 @@ */ class UpdateRowsDTO extends RowsDTO { + /** + * @var string + */ + protected $type = ConstEventsNames::UPDATE; + /** * @return string */ public function getType() { - return ConstEventsNames::UPDATE; + return $this->type; } } \ No newline at end of file diff --git a/src/MySQLReplication/Event/DTO/WriteRowsDTO.php b/src/MySQLReplication/Event/DTO/WriteRowsDTO.php index 2d42a92..4e542a2 100755 --- a/src/MySQLReplication/Event/DTO/WriteRowsDTO.php +++ b/src/MySQLReplication/Event/DTO/WriteRowsDTO.php @@ -10,11 +10,15 @@ */ class WriteRowsDTO extends RowsDTO { + /** + * @var string + */ + protected $type = ConstEventsNames::WRITE; /** * @return string */ public function getType() { - return ConstEventsNames::WRITE; + return $this->type; } } \ No newline at end of file diff --git a/src/MySQLReplication/Event/DTO/XidDTO.php b/src/MySQLReplication/Event/DTO/XidDTO.php index 181d4f8..ab60800 100755 --- a/src/MySQLReplication/Event/DTO/XidDTO.php +++ b/src/MySQLReplication/Event/DTO/XidDTO.php @@ -11,6 +11,10 @@ */ class XidDTO extends EventDTO implements \JsonSerializable { + /** + * @var string + */ + private $type = ConstEventsNames::XID; /** * @var */ @@ -43,7 +47,7 @@ public function getXid() */ public function getType() { - return ConstEventsNames::XID; + return $this->type; } /** diff --git a/src/MySQLReplication/Event/EventInfo.php b/src/MySQLReplication/Event/EventInfo.php index bbe24e3..510694e 100755 --- a/src/MySQLReplication/Event/EventInfo.php +++ b/src/MySQLReplication/Event/EventInfo.php @@ -6,7 +6,7 @@ * Class EventInfo * @package MySQLReplication\BinLog */ -class EventInfo +class EventInfo implements \JsonSerializable { /** * @var int @@ -128,4 +128,16 @@ public function getFlag() { return $this->flag; } + + /** + * Specify data which should be serialized to JSON + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + * @since 5.4.0 + */ + public function jsonSerialize() + { + return get_object_vars($this); + } } \ No newline at end of file diff --git a/src/MySQLReplication/Event/RowEvent/RowEvent.php b/src/MySQLReplication/Event/RowEvent/RowEvent.php index 6d5b2d7..407c661 100755 --- a/src/MySQLReplication/Event/RowEvent/RowEvent.php +++ b/src/MySQLReplication/Event/RowEvent/RowEvent.php @@ -44,29 +44,9 @@ class RowEvent extends EventCommon 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, ]; /** - * @var array + * @var TableMap[] */ private static $tableMapCache; - /** - * @var string - */ - private $tableMapTableName; - /** - * @var string - */ - private $tableMapDatabase; - /** - * @var int - */ - private $tableMapColumnsAmount; - /** - * @var array - */ - private $tableMapFields; - /** - * @var bool - */ - private $process = true; /** * @var MySQLRepository */ @@ -75,6 +55,10 @@ class RowEvent extends EventCommon * @var Config */ private $config; + /** + * @var TableMap + */ + private $currentTableMap; /** * RowEvent constructor. @@ -100,78 +84,88 @@ public function __construct(Config $config, MySQLRepository $MySQLRepository, Bi */ public function makeTableMapDTO() { - $tableId = $this->binaryDataReader->readTableId(); - $this->binaryDataReader->advance(2); + // automatically clear table cache to save memory + if (count(self::$tableMapCache) >= 128) + { + self::$tableMapCache = array_slice(self::$tableMapCache, 128, -1, true); + } $data = []; + $data['table_id'] = $this->binaryDataReader->readTableId(); + $this->binaryDataReader->advance(2); $data['schema_length'] = $this->binaryDataReader->readUInt8(); - $data['schema_name'] = $this->tableMapDatabase = $this->binaryDataReader->read($data['schema_length']); + $data['schema_name'] = $this->binaryDataReader->read($data['schema_length']); $this->binaryDataReader->advance(1); - $data['table_length'] = $this->binaryDataReader->readUInt8(); - $data['table_name'] = $this->tableMapTableName = $this->binaryDataReader->read($data['table_length']); + $data['table_name'] = $this->binaryDataReader->read($data['table_length']); $this->binaryDataReader->advance(1); + $data['columns_amount'] = $this->binaryDataReader->readCodedBinary(); + $data['column_types'] = $this->binaryDataReader->read($data['columns_amount']); - $this->tableMapColumnsAmount = $this->binaryDataReader->readCodedBinary(); - - $column_type_def = $this->binaryDataReader->read($this->tableMapColumnsAmount); - - // automatically clear array to save memory - if (count(self::$tableMapCache) >= 200) + if ([] !== $this->config->getTablesOnly() && !in_array($data['table_name'], $this->config->getTablesOnly())) { - self::$tableMapCache = array_slice(self::$tableMapCache, 100, -1, true); + return null; } - $tableMapDTO = new TableMapDTO( - $this->eventInfo, - $tableId, - $data['schema_name'], - $data['table_name'], - $this->tableMapColumnsAmount - ); + if ([] !== $this->config->getDatabasesOnly() && !in_array($data['schema_name'], $this->config->getDatabasesOnly())) + { + return null; + } - if (isset(self::$tableMapCache[$tableId])) + // already in cache don't parse + if (isset(self::$tableMapCache[$data['table_id']])) { - return $tableMapDTO; + return new TableMapDTO( + $this->eventInfo, + self::$tableMapCache[$data['table_id']] + ); } $this->binaryDataReader->readCodedBinary(); - self::$tableMapCache[$tableId]['fields'] = []; - self::$tableMapCache[$tableId]['database'] = $data['schema_name']; - self::$tableMapCache[$tableId]['table_name'] = $data['table_name']; - $columns = $this->MySQLRepository->getFields($data['schema_name'], $data['table_name']); - // if you drop tables and parse of logs you will get empty scheme - if (empty($columns)) - { - return $tableMapDTO; - } - for ($i = 0; $i < strlen($column_type_def); $i++) + $fields = []; + // if you drop tables and parse of logs you will get empty scheme + if (!empty($columns)) { - // this a dirty hack to prevent row events containing columns which have been dropped - if (!isset($columns[$i])) - { - $columns[$i] = [ - 'COLUMN_NAME' => 'DROPPED_COLUMN_' . $i, - 'COLLATION_NAME' => null, - 'CHARACTER_SET_NAME' => null, - 'COLUMN_COMMENT' => null, - 'COLUMN_TYPE' => 'BLOB', - 'COLUMN_KEY' => '', - ]; - $type = ConstFieldType::IGNORE; - } - else + for ($i = 0; $i < strlen($data['column_types']); $i++) { - $type = ord($column_type_def[$i]); - } + // this a dirty hack to prevent row events containing columns which have been dropped + if (!isset($columns[$i])) + { + $columns[$i] = [ + 'COLUMN_NAME' => 'DROPPED_COLUMN_' . $i, + 'COLLATION_NAME' => null, + 'CHARACTER_SET_NAME' => null, + 'COLUMN_COMMENT' => null, + 'COLUMN_TYPE' => 'BLOB', + 'COLUMN_KEY' => '', + ]; + $type = ConstFieldType::IGNORE; + } + else + { + $type = ord($data['column_types'][$i]); + } - self::$tableMapCache[$tableId]['fields'][$i] = BinLogColumns::parse($type, $columns[$i], $this->binaryDataReader); + $fields[$i] = BinLogColumns::parse($type, $columns[$i], $this->binaryDataReader); + } } - return $tableMapDTO; + // save to cache + self::$tableMapCache[$data['table_id']] = new TableMap( + $data['schema_name'], + $data['table_name'], + $data['table_id'], + $data['columns_amount'], + $fields + ); + + return new TableMapDTO( + $this->eventInfo, + self::$tableMapCache[$data['table_id']] + ); } /** @@ -179,9 +173,7 @@ public function makeTableMapDTO() */ public function makeWriteRowsDTO() { - $this->rowInit(); - - if (false === $this->process) + if (false === $this->rowInit()) { return null; } @@ -190,21 +182,17 @@ public function makeWriteRowsDTO() return new WriteRowsDTO( $this->eventInfo, - $this->tableMapDatabase, - $this->tableMapTableName, - $this->tableMapColumnsAmount, + $this->currentTableMap, count($values), $values ); } /** - * + * @return bool */ private function rowInit() { - $this->process = true; - $tableId = $this->binaryDataReader->readTableId(); $this->binaryDataReader->advance(2); @@ -217,30 +205,17 @@ private function rowInit() $this->binaryDataReader->read($this->binaryDataReader->readUInt16() / 8); } - $this->tableMapColumnsAmount = $this->binaryDataReader->readCodedBinary(); - + $this->binaryDataReader->readCodedBinary(); - $this->tableMapFields = []; - if (isset(self::$tableMapCache[$tableId])) + if (isset(self::$tableMapCache[$tableId]) && [] !== self::$tableMapCache[$tableId]->getFields()) { - $this->tableMapFields = self::$tableMapCache[$tableId]['fields']; - - if ([] !== $this->config->getTablesOnly() && !in_array(self::$tableMapCache[$tableId]['table_name'], $this->config->getTablesOnly())) - { - $this->process = false; - } + $this->currentTableMap = self::$tableMapCache[$tableId]; - if ([] !== $this->config->getDatabasesOnly() && !in_array(self::$tableMapCache[$tableId]['database'], $this->config->getDatabasesOnly())) - { - $this->process = false; - } - } - if ([] == $this->tableMapFields) - { - //remove cache can be empty (drop table) - unset(self::$tableMapCache[$tableId]); - $this->process = false; + return true; } + unset(self::$tableMapCache[$tableId]); + + return false; } /** @@ -252,17 +227,16 @@ private function getColumnData($colsBitmap) { $values = []; - $l = $this->getColumnsBinarySize($this->bitCount($colsBitmap)); + $length = $this->getColumnsBinarySize($this->bitCount($colsBitmap)); // null bitmap length = (bits set in 'columns-present-bitmap'+7)/8 // see http://dev.mysql.com/doc/internals/en/rows-event.html - $null_bitmap = $this->binaryDataReader->read($l); + $null_bitmap = $this->binaryDataReader->read($length); $nullBitmapIndex = 0; - foreach ($this->tableMapFields as $i => $column) + foreach ($this->currentTableMap->getFields() as $i => $column) { $name = $column['name']; - $unsigned = $column['unsigned']; if ($this->bitGet($colsBitmap, $i) == 0) { @@ -280,7 +254,7 @@ private function getColumnData($colsBitmap) } elseif ($column['type'] == ConstFieldType::TINY) { - if ($unsigned) + if (true === $column['unsigned']) { $values[$name] = $this->binaryDataReader->readUInt8(); } @@ -291,7 +265,7 @@ private function getColumnData($colsBitmap) } elseif ($column['type'] == ConstFieldType::SHORT) { - if ($unsigned) + if (true === $column['unsigned']) { $values[$name] = $this->binaryDataReader->readUInt16(); } @@ -302,7 +276,7 @@ private function getColumnData($colsBitmap) } elseif ($column['type'] == ConstFieldType::LONG) { - if ($unsigned) + if (true === $column['unsigned']) { $values[$name] = $this->binaryDataReader->readUInt32(); } @@ -313,7 +287,7 @@ private function getColumnData($colsBitmap) } elseif ($column['type'] == ConstFieldType::LONGLONG) { - if ($unsigned) + if (true === $column['unsigned']) { $values[$name] = $this->binaryDataReader->readUInt64(); } @@ -324,7 +298,7 @@ private function getColumnData($colsBitmap) } elseif ($column['type'] == ConstFieldType::INT24) { - if ($unsigned) + if (true === $column['unsigned']) { $values[$name] = $this->binaryDataReader->readUInt24(); } @@ -413,12 +387,12 @@ private function getColumnData($colsBitmap) } /** - * @param int $columns + * @param int $columnsAmount * @return int */ - private function getColumnsBinarySize($columns) + private function getColumnsBinarySize($columnsAmount) { - return (int)(($columns + 7) / 8); + return (int)(($columnsAmount + 7) / 8); } /** @@ -808,9 +782,7 @@ private function getBit(array $column) */ public function makeDeleteRowsDTO() { - $this->rowInit(); - - if (false === $this->process) + if (false === $this->rowInit()) { return null; } @@ -819,9 +791,7 @@ public function makeDeleteRowsDTO() return new DeleteRowsDTO( $this->eventInfo, - $this->tableMapDatabase, - $this->tableMapTableName, - $this->tableMapColumnsAmount, + $this->currentTableMap, count($values), $values ); @@ -832,14 +802,12 @@ public function makeDeleteRowsDTO() */ public function makeUpdateRowsDTO() { - $this->rowInit(); - - if (false === $this->process) + if (false === $this->rowInit()) { return null; } - $columnsBinarySize = $this->getColumnsBinarySize($this->tableMapColumnsAmount); + $columnsBinarySize = $this->getColumnsBinarySize($this->currentTableMap->getColumnsAmount()); $beforeBinaryData = $this->binaryDataReader->read($columnsBinarySize); $afterBinaryData = $this->binaryDataReader->read($columnsBinarySize); @@ -854,9 +822,7 @@ public function makeUpdateRowsDTO() return new UpdateRowsDTO( $this->eventInfo, - $this->tableMapDatabase, - $this->tableMapTableName, - $this->tableMapColumnsAmount, + $this->currentTableMap, count($values), $values ); @@ -868,7 +834,7 @@ public function makeUpdateRowsDTO() */ private function getValues() { - $columnsBinarySize = $this->getColumnsBinarySize($this->tableMapColumnsAmount); + $columnsBinarySize = $this->getColumnsBinarySize($this->currentTableMap->getColumnsAmount()); $binaryData = $this->binaryDataReader->read($columnsBinarySize); $values = []; diff --git a/src/MySQLReplication/Event/RowEvent/TableMap.php b/src/MySQLReplication/Event/RowEvent/TableMap.php new file mode 100755 index 0000000..f3a2645 --- /dev/null +++ b/src/MySQLReplication/Event/RowEvent/TableMap.php @@ -0,0 +1,105 @@ +database = $database; + $this->table = $table; + $this->tableId = $tableId; + $this->columnsAmount = $columnsAmount; + $this->fields = $fields; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->database; + } + + /** + * @return string + */ + public function getTable() + { + return $this->table; + } + + /** + * @return int + */ + public function getTableId() + { + return $this->tableId; + } + + /** + * @return int + */ + public function getColumnsAmount() + { + return $this->columnsAmount; + } + + /** + * @return array + */ + public function getFields() + { + return $this->fields; + } + + /** + * Specify data which should be serialized to JSON + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + * @since 5.4.0 + */ + public function jsonSerialize() + { + return get_object_vars($this); + } +} \ No newline at end of file