diff --git a/.gitignore b/.gitignore index 4320fb8..f3cfc11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /nbproject /.idea + composer.lock + +/vendor \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1398a1c..2e1418a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1 @@ language: php -php: - - '5.4' - - '5.5' - - '5.6' - - '7.0' - - '7.1' - -script: phpunit diff --git a/composer.json b/composer.json index 4f045ea..6ae18d2 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,16 @@ { - "name": "vitalyart/hltv-demo-parser", - "description": "Парсер данных с демки HLTV", - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "4.8.*" - }, - "autoload": { - "psr-4": { - "VitalyArt\\DemoParser\\": "src/" - } - } + "name": "vitalyart/hltv-demo-parser", + "description": "Парсер данных с демки HLTV", + "require": { + "php": ">=7.1", + "ext-mbstring": "*" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "autoload": { + "psr-4": { + "VitalyArt\\DemoParser\\": "src/" + } + } } diff --git a/src/Demo.php b/src/Demo.php index 87ef98f..2b4e2b6 100644 --- a/src/Demo.php +++ b/src/Demo.php @@ -2,6 +2,8 @@ namespace VitalyArt\DemoParser; +use DateTime; + class Demo { private $demoProtocol; @@ -11,8 +13,16 @@ class Demo private $entries; private $startTime; private $endTime; - - public function __construct($demoProtocol, $netProtocol, $mapName, $clientName, $entries, $startTime, $endTime) + + public function __construct( + int $demoProtocol, + int $netProtocol, + string $mapName, + string $clientName, + array $entries, + ?DateTime $startTime, + ?DateTime $endTime + ) { $this->demoProtocol = $demoProtocol; $this->netProtocol = $netProtocol; @@ -23,37 +33,37 @@ public function __construct($demoProtocol, $netProtocol, $mapName, $clientName, $this->endTime = $endTime; } - public function getDemoProtocol() + public function getDemoProtocol(): int { return $this->demoProtocol; } - public function getNetProtocol() + public function getNetProtocol(): int { return $this->netProtocol; } - public function getMapName() + public function getMapName(): string { return $this->mapName; } - public function getClientName() + public function getClientName(): string { return $this->clientName; } - public function getEntries() + public function getEntries(): array { return $this->entries; } - public function getStartTime() + public function getStartTime(): ?DateTime { return $this->startTime; } - public function getEndTime() + public function getEndTime(): ?DateTime { return $this->endTime; } diff --git a/src/Entry.php b/src/Entry.php index fe76608..5559987 100644 --- a/src/Entry.php +++ b/src/Entry.php @@ -13,7 +13,7 @@ class Entry private $frames; private $offset; private $fileLength; - + /** * @param string $typeString * @param integer $type @@ -25,7 +25,17 @@ class Entry * @param integer $offset * @param integer $fileLength */ - public function __construct($typeString, $type, $description, $flags, $CDTrack, $trackTime, $frames, $offset, $fileLength) + public function __construct( + string $typeString, + int $type, + string $description, + int $flags, + string $CDTrack, + float $trackTime, + int $frames, + int $offset, + int $fileLength + ) { $this->typeString = $typeString; $this->type = $type; @@ -42,16 +52,16 @@ public function __construct($typeString, $type, $description, $flags, $CDTrack, * Entry type * @return string */ - public function getTypeString() + public function getTypeString(): string { return $this->typeString; } /** * Integer entry type - * @return integer + * @return int */ - public function getType() + public function getType(): int { return $this->type; } @@ -60,16 +70,16 @@ public function getType() * Description * @return string */ - public function getDescription() + public function getDescription(): string { return $this->description; } /** * Flags - * @return integer + * @return int */ - public function getFlags() + public function getFlags(): int { return $this->flags; } @@ -78,7 +88,7 @@ public function getFlags() * CD track * @return string */ - public function getCDTrack() + public function getCDTrack(): string { return $this->CDTrack; } @@ -87,34 +97,34 @@ public function getCDTrack() * Track time * @return float */ - public function getTrackTime() + public function getTrackTime(): float { return $this->trackTime; } /** * Frames - * @return integer + * @return int */ - public function getFrames() + public function getFrames(): int { return $this->frames; } /** * Offset - * @return integer + * @return int */ - public function getOffset() + public function getOffset(): int { return $this->offset; } /** * File length - * @return integer + * @return int */ - public function getFileLength() + public function getFileLength(): int { return $this->fileLength; } diff --git a/src/Parser.php b/src/Parser.php index de49dc4..1de7313 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,6 +2,7 @@ namespace VitalyArt\DemoParser; +use DateTime; use VitalyArt\DemoParser\exceptions\FileNotExistsException; use VitalyArt\DemoParser\exceptions\FileNotSpecifiedException; use VitalyArt\DemoParser\exceptions\IsNotADemoException; @@ -20,26 +21,26 @@ class Parser * @var string Name of demo file without extension */ private $fileName; - + /** * @var resource */ private $handle; - + /** * @var Entry[] */ private $entries; - + /** - * @var string Path to demo file + * @var string Path to demo file */ private $demoFile; /** * Process */ - private function bootstrap() + private function bootstrap(): void { $this->fileName = pathinfo($this->demoFile, PATHINFO_FILENAME); $this->checkFile(); @@ -52,15 +53,15 @@ private function bootstrap() * Set demo file * @param string $demoFile Path to demo file */ - public function setDemoFile($demoFile) + public function setDemoFile(string $demoFile): void { $this->demoFile = $demoFile; } - + /** * Fill demo from data */ - private function fillDemo() + private function fillDemo(): void { $this->demo = new Demo( $this->readInt(8), @@ -72,16 +73,17 @@ private function fillDemo() $this->getEndTime() ); } - + /** * @return Demo * @throws FileNotSpecifiedException */ - public function getDemo() + public function getDemo(): Demo { - if(!$this->demoFile) { + if (!$this->demoFile) { throw new FileNotSpecifiedException('No demo file specified'); } + $this->bootstrap(); return $this->demo; } @@ -91,27 +93,30 @@ public function getDemo() * @throws FileNotExistsException If file not found on file system * @throws WrongExtensionException If file extension is not a DEM */ - private function checkFile() + private function checkFile(): void { - if(!is_file($this->demoFile)) { + if (!is_file($this->demoFile)) { throw new FileNotExistsException("Demo file not found in path {$this->demoFile}"); } - if(pathinfo($this->demoFile, PATHINFO_EXTENSION) !== 'dem') { + + if (pathinfo($this->demoFile, PATHINFO_EXTENSION) !== 'dem') { throw new WrongExtensionException("Extension of file {$this->demoFile} is not DEM"); } } - + /** * @throws NotReadableException If file is not readable * @throws IsNotADemoException IF file is a not demo */ - private function handle() + private function handle(): void { - $this->handle = fopen($this->demoFile, "r"); + $this->handle = fopen($this->demoFile, 'r'); + if (!$this->handle) { throw new NotReadableException("File {$this->demoFile} is not readable"); } - if ($this->readData(0, 8) !== "HLDEMO") { + + if ($this->readData(0, 8) !== 'HLDEMO') { throw new IsNotADemoException("File {$this->demoFile} is not a demo"); } } @@ -119,17 +124,20 @@ private function handle() /** * @return Entry[] */ - private function getEntries() + private function getEntries(): array { - if($this->entries === null) { + if ($this->entries === null) { $entriesOffset = $this->readInt(540); - $entriesCount = $this->readInt($entriesOffset); + $entriesCount = $this->readInt($entriesOffset); + $this->entries = []; + for ($i = 0; $i < $entriesCount; $i++) { if ($this->readData($entriesOffset + 96 * $i + 4, 64)) { $key = trim($this->readData($entriesOffset + 96 * $i + 4, 64)); - $key = mb_convert_case($key, MB_CASE_LOWER, "UTF-8"); - $entry = new \VitalyArt\DemoParser\Entry( + $key = mb_convert_case($key, MB_CASE_LOWER, 'UTF-8'); + + $entry = new Entry( $key, $this->readInt($entriesOffset + 96 * $i), $this->readData($entriesOffset + 96 * $i + 4, 64), @@ -140,115 +148,105 @@ private function getEntries() $this->readInt($entriesOffset + 96 * $i + 84), $this->readInt($entriesOffset + 96 * $i + 88) ); - if($this->isValidEntry($entry)) { + + if ($this->isValidEntry($entry)) { $this->entries[$key] = $entry; } } } } + return $this->entries; } - + /** * Start time - * @return null|\DateTime + * @return null|DateTime */ - private function getStartDate() + private function getStartDate(): ?DateTime { - if (preg_match("/.+-(\d+)-.+/", $this->fileName, $matches)) { - return \DateTime::createFromFormat('ymdHi', $matches[1]); + if (preg_match('/.+-(\d+)-.+/', $this->fileName, $matches)) { + return DateTime::createFromFormat('ymdHi', $matches[1]); } + return null; } /** * End time - * @return null|\DateTime + * @return null|DateTime */ - private function getEndTime() + private function getEndTime(): ?DateTime { $startTime = $this->getStartDate(); + if (!$startTime) { return null; } + $playbackTime = null; - foreach($this->getEntries() as $entry) { - if($entry->getTypeString() === 'playback') { + + foreach ($this->getEntries() as $entry) { + if ($entry->getTypeString() === 'playback') { $playbackTime = intval($entry->getTrackTime()); break; } } + if ($playbackTime === null) { return null; } + return $startTime->modify("+ {$playbackTime} seconds"); } - - /** - * Check entry - * @param Entry $entry - * @return boolean - */ - private function isValidEntry($entry) + + private function isValidEntry(Entry $entry): bool { foreach ($entry as $value) { if ($value === false) { return false; } } + return true; } - /** - * @param integer $offset - * @return bool - */ - private function readInt($offset) + private function readInt(int $offset): int { if (fseek($this->handle, $offset) == -1) { return false; } - $data = unpack("i", fread($this->handle, 4)); + + $data = unpack('i', fread($this->handle, 4)); return $data[1]; } - /** - * @param resource $file - * @param integer $offset - * @return bool - */ - private function readUint($file, $offset) + private function readUint(int $offset): int { - if (fseek($file, $offset) == -1) { + if (fseek($this->handle, $offset) == -1) { return false; } - $data = unpack("I", fread($file, 4)); + + $data = unpack('I', fread($this->handle, 4)); return $data[1]; } - /** - * @param integer $offset - * @return bool - */ - private function readFloat($offset) + private function readFloat(int $offset): float { if (fseek($this->handle, $offset) == -1) { return false; } - $data = unpack("f", fread($this->handle, 4)); + + $data = unpack('f', fread($this->handle, 4)); return $data[1]; } - /** - * @param integer $offset - * @param integer $Len - * @return string - */ - private function readData($offset, $Len) + private function readData(int $offset, int $Len): string { if (fseek($this->handle, $offset) == -1) { return false; } + return trim(fread($this->handle, $Len)); } } diff --git a/tests/test.php b/tests/test.php index 0fb4fbe..ea1c8c6 100644 --- a/tests/test.php +++ b/tests/test.php @@ -1,87 +1,86 @@ parser = new \VitalyArt\DemoParser\Parser; + $this->parser = new Parser(); } - + /** - * @expectedException \VitalyArt\DemoParser\exceptions\FileNotSpecifiedException + * @expectedException VitalyArt\DemoParser\exceptions\FileNotSpecifiedException */ - public function testNoDemoFileSpecified() + public function testNoDemoFileSpecified(): void { $this->parser->getDemo(); } - + /** * @expectedException VitalyArt\DemoParser\exceptions\FileNotExistsException */ - public function testDemoFileNotExists() + public function testDemoFileNotExists(): void { $this->parser->setDemoFile('/invalid/path'); $this->parser->getDemo(); } - + /** * @expectedException VitalyArt\DemoParser\exceptions\IsNotADemoException */ - public function testDemoFileIsNotDemo() + public function testDemoFileIsNotDemo(): void { $this->parser->setDemoFile(__DIR__ . '/demos/no-demo-file.dem'); $this->parser->getDemo(); } - + /** - * @expectedException \VitalyArt\DemoParser\exceptions\WrongExtensionException + * @expectedException VitalyArt\DemoParser\exceptions\WrongExtensionException */ - public function testDemoFileWrongExtension() + public function testDemoFileWrongExtension(): void { $this->parser->setDemoFile(__DIR__ . '/demos/demo-file-with-wrong-extension.txt'); $this->parser->getDemo(); } - public function testDemoInstanceofDemo() + public function testDemoInstanceofDemo(): void { $this->setValidDemo(); $demo = $this->parser->getDemo(); - $this->assertInstanceOf(\VitalyArt\DemoParser\Demo::class, $demo); + $this->assertInstanceOf(Demo::class, $demo); } - public function testStarttimeInstanceofDatetime() + public function testStarttimeInstanceofDatetime(): void { $this->setValidDemo(); $demo = $this->parser->getDemo(); $this->assertInstanceOf(DateTime::class, $demo->getStartTime()); } - - public function testEntriesCount() + + public function testEntriesCount(): void { $this->setValidDemo(); $demo = $this->parser->getDemo(); $this->assertEquals(count($demo->getEntries()), 2); } - - public function testEntriesInstanceofEntry() + + public function testEntriesInstanceofEntry(): void { $this->setValidDemo(); $demo = $this->parser->getDemo(); foreach ($demo->getEntries() as $entry) { - $this->assertInstanceOf(\VitalyArt\DemoParser\Entry::class, $entry); + $this->assertInstanceOf(Entry::class, $entry); } } - - protected function setValidDemo() + + protected function setValidDemo(): void { $this->parser->setDemoFile(__DIR__ . '/demos/navi-vs-fx-iem5eu-third-1101231211-de_dust2.dem'); } -} \ No newline at end of file +}