From 464800a3c5b986cd14b7a881346beab100c64130 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 1 Apr 2024 15:02:20 +0200 Subject: [PATCH] Modernize --- .github/workflows/main.yml | 1 + composer.json | 5 +- psalm-baseline.xml | 8 + psalm.xml | 1 + src/FileId.php | 75 +++--- src/FileIdType.php | 132 ++++++++++ src/PhotoSizeSource.php | 18 +- .../PhotoSizeSourceDialogPhoto.php | 10 +- src/PhotoSizeSource/PhotoSizeSourceLegacy.php | 4 +- .../PhotoSizeSourceStickersetThumbnail.php | 4 +- ...toSizeSourceStickersetThumbnailVersion.php | 4 +- .../PhotoSizeSourceThumbnail.php | 26 +- src/PhotoSizeSourceType.php | 34 +++ src/UniqueFileId.php | 37 ++- src/UniqueFileIdType.php | 29 +++ src/{type.php => functions.php} | 239 ++---------------- 16 files changed, 313 insertions(+), 314 deletions(-) create mode 100644 psalm-baseline.xml create mode 100644 src/FileIdType.php create mode 100644 src/PhotoSizeSourceType.php create mode 100644 src/UniqueFileIdType.php rename src/{type.php => functions.php} (66%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 106b943..1cafb13 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,5 +44,6 @@ jobs: TOKEN: ${{ secrets.TOKEN }} DEST: ${{ secrets.DEST }} run: | + vendor/bin/psalm --no-cache vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml vendor/bin/php-cs-fixer --diff --dry-run -v fix diff --git a/composer.json b/composer.json index 53f63a9..ac41d7c 100644 --- a/composer.json +++ b/composer.json @@ -15,14 +15,15 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "amphp/php-cs-fixer-config": "^2" + "amphp/php-cs-fixer-config": "^2", + "vimeo/psalm": "dev-master" }, "autoload": { "psr-4": { "danog\\Decoder\\": "src/" }, "files": [ - "src/type.php" + "src/functions.php" ] }, "autoload-dev": { diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..a0e23a0 --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/psalm.xml b/psalm.xml index 8dc065a..839a987 100644 --- a/psalm.xml +++ b/psalm.xml @@ -5,6 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + errorBaseline="psalm-baseline.xml" > diff --git a/src/FileId.php b/src/FileId.php index 9fac9d3..3ddb30b 100644 --- a/src/FileId.php +++ b/src/FileId.php @@ -26,8 +26,10 @@ /** * Represents decoded bot API file ID. + * + * @api */ -class FileId +final class FileId { /** * Bot API file ID version. @@ -51,7 +53,7 @@ class FileId * File type. * */ - private int $type = 0; + private FileIdType $type = FileIdType::NONE; /** * File reference. @@ -123,7 +125,7 @@ public static function fromBotAPI(string $fileId): self } $result->setId($resultArray['id']); - if ($result->getType() <= PHOTO) { + if ($result->getType()->value <= FileIdType::PHOTO->value) { if (isset($resultArray['volume_id'])) { $result->setVolumeId($resultArray['volume_id']); } @@ -131,31 +133,31 @@ public static function fromBotAPI(string $fileId): self $result->setLocalId($resultArray['local_id']); } switch ($resultArray['photosize_source']) { - case PHOTOSIZE_SOURCE_LEGACY: - case PHOTOSIZE_SOURCE_FULL_LEGACY: + case PhotoSizeSourceType::LEGACY: + case PhotoSizeSourceType::FULL_LEGACY: $photoSizeSource = new PhotoSizeSourceLegacy($resultArray['photosize_source']); $photoSizeSource->setSecret($resultArray['secret']); break; - case PHOTOSIZE_SOURCE_THUMBNAIL: + case PhotoSizeSourceType::THUMBNAIL: $photoSizeSource = new PhotoSizeSourceThumbnail($resultArray['photosize_source']); $photoSizeSource->setThumbType($resultArray['thumbnail_type']); $photoSizeSource->setThumbFileType($resultArray['file_type']); break; - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG_LEGACY: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL: + case PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY: + case PhotoSizeSourceType::DIALOGPHOTO_BIG: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL: $photoSizeSource = new PhotoSizeSourceDialogPhoto($resultArray['photosize_source']); $photoSizeSource->setDialogId($resultArray['dialog_id']); $photoSizeSource->setDialogAccessHash($resultArray['dialog_access_hash']); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL: - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_LEGACY: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY: $photoSizeSource = new PhotoSizeSourceStickersetThumbnail($resultArray['photosize_source']); $photoSizeSource->setStickerSetId($resultArray['sticker_set_id']); $photoSizeSource->setStickerSetAccessHash($resultArray['sticker_set_access_hash']); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_VERSION: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION: $photoSizeSource = new PhotoSizeSourceStickersetThumbnailVersion($resultArray['photosize_source']); $photoSizeSource->setStickerSetId($resultArray['sticker_set_id']); $photoSizeSource->setStickerSetAccessHash($resultArray['sticker_set_access_hash']); @@ -174,7 +176,7 @@ public static function fromBotAPI(string $fileId): self */ public function getBotAPI(): string { - $type = $this->getType(); + $type = $this->getType()->value; if ($this->hasFileReference()) { $type |= FILE_REFERENCE_FLAG; } @@ -195,40 +197,46 @@ public function getBotAPI(): string $fileId .= packLong($this->getId()); $fileId .= packLong($this->getAccessHash()); - if ($this->getType() <= PHOTO) { + if ($this->getType()->value <= FileIdType::PHOTO->value) { $photoSize = $this->getPhotoSizeSource(); $fileId .= \pack('V', $photoSize->getType()); switch ($photoSize->getType()) { - case PHOTOSIZE_SOURCE_LEGACY: + case PhotoSizeSourceType::LEGACY: + assert($photoSize instanceof PhotoSizeSourceLegacy); $fileId .= packLong($photoSize->getSecret()); break; - case PHOTOSIZE_SOURCE_FULL_LEGACY: + case PhotoSizeSourceType::FULL_LEGACY: + assert($photoSize instanceof PhotoSizeSourceLegacy); $fileId .= packLong($this->getVolumeId()); $fileId .= packLong($photoSize->getSecret()); $fileId .= \pack('l', $this->getLocalId()); break; - case PHOTOSIZE_SOURCE_THUMBNAIL: + case PhotoSizeSourceType::THUMBNAIL: + assert($photoSize instanceof PhotoSizeSourceThumbnail); $fileId .= \pack('Va4', $photoSize->getThumbFileType(), $photoSize->getThumbType()); break; - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG_LEGACY: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY: + case PhotoSizeSourceType::DIALOGPHOTO_BIG: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL: + case PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY: + assert($photoSize instanceof PhotoSizeSourceDialogPhoto); $fileId .= packLongBig($photoSize->getDialogId()); $fileId .= packLong($photoSize->getDialogAccessHash()); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL: - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_LEGACY: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY: + assert($photoSize instanceof PhotoSizeSourceStickersetThumbnail); $fileId .= packLong($photoSize->getStickerSetId()); $fileId .= packLong($photoSize->getStickerSetAccessHash()); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_VERSION: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION: + assert($photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion); $fileId .= packLong($photoSize->getStickerSetId()); $fileId .= packLong($photoSize->getStickerSetAccessHash()); $fileId .= \pack('l', $photoSize->getStickerSetVersion()); break; } - if ($photoSize->getType() >= PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY && $photoSize->getType() <= PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_LEGACY) { + if ($photoSize->getType() >= PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY && $photoSize->getType()->value <= PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY->value) { $fileId .= packLong($this->getVolumeId()); $fileId .= \pack('l', $this->getLocalId()); } @@ -314,27 +322,18 @@ public function setSubVersion(int $subVersion): self * Get file type. * */ - public function getType(): int + public function getType(): FileIdType { return $this->type; } - /** - * Get file type as string. - * - */ - public function getTypeName(): string - { - return TYPES[$this->type]; - } - /** * Set file type. * - * @param int $type File type. + * @param FileIdType $type File type. * */ - public function setType(int $type): self + public function setType(FileIdType $type): self { $this->type = $type; diff --git a/src/FileIdType.php b/src/FileIdType.php new file mode 100644 index 0000000..49089eb --- /dev/null +++ b/src/FileIdType.php @@ -0,0 +1,132 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://github.com/tg-file-decoder Documentation + */ + +namespace danog\Decoder; + +use AssertionError; + +enum FileIdType: int +{ + /** + * Thumbnail. + */ + case THUMBNAIL = 0; + /** + * Profile photo. + * Used for users and channels, chat photos are normal PHOTOs. + */ + case PROFILE_PHOTO = 1; + /** + * Normal photos. + */ + case PHOTO = 2; + + /** + * Voice messages. + */ + case VOICE = 3; + /** + * Video. + */ + case VIDEO = 4; + /** + * Document. + */ + case DOCUMENT = 5; + /** + * Secret chat document. + */ + case ENCRYPTED = 6; + /** + * Temporary document. + */ + case TEMP = 7; + /** + * Sticker. + */ + case STICKER = 8; + /** + * Music. + */ + case AUDIO = 9; + /** + * GIF. + */ + case ANIMATION = 10; + /** + * Encrypted thumbnail. + */ + case ENCRYPTED_THUMBNAIL = 11; + /** + * Wallpaper. + */ + case WALLPAPER = 12; + /** + * Round video. + */ + case VIDEO_NOTE = 13; + /** + * Passport raw file. + */ + case SECURE_RAW = 14; + /** + * Passport file. + */ + case SECURE = 15; + /** + * Background. + */ + case BACKGROUND = 16; + /** + * Size. + */ + case SIZE = 17; + case NONE = 18; + + /** + * Convert file ID type to unique file ID type. + */ + public function toUnique(): UniqueFileIdType + { + return match ($this) { + self::PHOTO => UniqueFileIdType::PHOTO, + self::PROFILE_PHOTO => UniqueFileIdType::PHOTO, + self::THUMBNAIL => UniqueFileIdType::PHOTO, + self::ENCRYPTED_THUMBNAIL => UniqueFileIdType::PHOTO, + self::WALLPAPER => UniqueFileIdType::PHOTO, + + self::VIDEO => UniqueFileIdType::DOCUMENT, + self::VOICE => UniqueFileIdType::DOCUMENT, + self::DOCUMENT => UniqueFileIdType::DOCUMENT, + self::STICKER => UniqueFileIdType::DOCUMENT, + self::AUDIO => UniqueFileIdType::DOCUMENT, + self::ANIMATION => UniqueFileIdType::DOCUMENT, + self::VIDEO_NOTE => UniqueFileIdType::DOCUMENT, + self::BACKGROUND => UniqueFileIdType::DOCUMENT, + + self::SECURE => UniqueFileIdType::SECURE, + self::SECURE_RAW => UniqueFileIdType::SECURE, + + self::ENCRYPTED => UniqueFileIdType::ENCRYPTED, + + self::TEMP => UniqueFileIdType::TEMP, + + default => throw new AssertionError("Cannot convert file ID of type ".$this->name." to a unique file ID!") + }; + } +} diff --git a/src/PhotoSizeSource.php b/src/PhotoSizeSource.php index 2a12949..e839828 100644 --- a/src/PhotoSizeSource.php +++ b/src/PhotoSizeSource.php @@ -31,18 +31,16 @@ abstract class PhotoSizeSource * Source type. * */ - private int $type; + private PhotoSizeSourceType $type; /** * Set photosize source type. * - * @param integer $type Type + * @param PhotoSizeSourceType $type Type */ - public function __construct(int $type) + public function __construct(PhotoSizeSourceType $type) { $this->type = $type; - - return $this; } /** * Get photosize source type. @@ -51,18 +49,18 @@ public function __construct(int $type) * * @psalm-return ( * T is PhotoSizeSourceLegacy ? - * ? \danog\Decoder\PHOTOSIZE_SOURCE_LEGACY + * ? \danog\Decoder\PhotoSizeSourceType::LEGACY * : (T is PhotoSizeSourceDialogPhoto - * ? \danog\Decoder\PHOTOSIZE_SOURCE_DIALOGPHOTO_* + * ? \danog\Decoder\PhotoSizeSourceType::DIALOGPHOTO_* * (T is PhotoSizeSourceStickersetThumbnail - * ? \danog\Decoder\PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL - * : \danog\Decoder\PHOTOSIZE_SOURCE_THUMBNAIL + * ? \danog\Decoder\PhotoSizeSourceType::STICKERSET_THUMBNAIL + * : \danog\Decoder\PhotoSizeSourceType::THUMBNAIL * ) * ) * * @internal Internal use */ - public function getType(): int + public function getType(): PhotoSizeSourceType { return $this->type; } diff --git a/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php b/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php index 97f78f5..2e26520 100644 --- a/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php +++ b/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php @@ -19,16 +19,16 @@ namespace danog\Decoder\PhotoSizeSource; use danog\Decoder\PhotoSizeSource; - -use const danog\Decoder\PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL; -use const danog\Decoder\PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY; +use danog\Decoder\PhotoSizeSourceType; /** * Represents source of photosize. * + * @api + * * @extends PhotoSizeSource */ -class PhotoSizeSourceDialogPhoto extends PhotoSizeSource +final class PhotoSizeSourceDialogPhoto extends PhotoSizeSource { /** * ID of dialog. @@ -90,6 +90,6 @@ public function setDialogAccessHash(int $dialogAccessHash): self */ public function isSmallDialogPhoto(): bool { - return \in_array($this->getType(), [PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY, PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL]); + return \in_array($this->getType(), [PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY, PhotoSizeSourceType::DIALOGPHOTO_SMALL], true); } } diff --git a/src/PhotoSizeSource/PhotoSizeSourceLegacy.php b/src/PhotoSizeSource/PhotoSizeSourceLegacy.php index 1a16c50..9ed8948 100644 --- a/src/PhotoSizeSource/PhotoSizeSourceLegacy.php +++ b/src/PhotoSizeSource/PhotoSizeSourceLegacy.php @@ -23,9 +23,11 @@ /** * Represents source of photosize. * + * @api + * * @extends PhotoSizeSource */ -class PhotoSizeSourceLegacy extends PhotoSizeSource +final class PhotoSizeSourceLegacy extends PhotoSizeSource { /** * Secret legacy ID. diff --git a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php index af48b47..c3f48f3 100644 --- a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php +++ b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php @@ -23,9 +23,11 @@ /** * Represents source of photosize. * + * @api + * * @extends PhotoSizeSource */ -class PhotoSizeSourceStickersetThumbnail extends PhotoSizeSource +final class PhotoSizeSourceStickersetThumbnail extends PhotoSizeSource { /** * Stickerset ID. diff --git a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php index b06f02d..a0c1eb6 100644 --- a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php +++ b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php @@ -23,9 +23,11 @@ /** * Represents source of photosize. * + * @api + * * @extends PhotoSizeSource */ -class PhotoSizeSourceStickersetThumbnailVersion extends PhotoSizeSource +final class PhotoSizeSourceStickersetThumbnailVersion extends PhotoSizeSource { /** * Stickerset ID. diff --git a/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php b/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php index e81b2b0..aa7d594 100644 --- a/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php +++ b/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php @@ -18,20 +18,23 @@ namespace danog\Decoder\PhotoSizeSource; +use danog\Decoder\FileIdType; use danog\Decoder\PhotoSizeSource; -use const danog\Decoder\TYPES; - /** * Represents source of photosize. + * + * @api + * + * @extends PhotoSizeSource */ -class PhotoSizeSourceThumbnail extends PhotoSizeSource +final class PhotoSizeSourceThumbnail extends PhotoSizeSource { /** * File type of original file. * */ - private int $thumbFileType; + private FileIdType $thumbFileType; /** * Thumbnail size. * @@ -42,26 +45,17 @@ class PhotoSizeSourceThumbnail extends PhotoSizeSource * Get file type of original file. * */ - public function getThumbFileType(): int + public function getThumbFileType(): FileIdType { return $this->thumbFileType; } - /** - * Get file type of original file as string. - * - */ - public function getThumbFileTypeString(): string - { - return TYPES[$this->thumbFileType]; - } - /** * Set file type of original file. * - * @param int $thumbFileType File type of original file + * @param FileIdType $thumbFileType File type of original file * */ - public function setThumbFileType(int $thumbFileType): self + public function setThumbFileType(FileIdType $thumbFileType): self { $this->thumbFileType = $thumbFileType; diff --git a/src/PhotoSizeSourceType.php b/src/PhotoSizeSourceType.php new file mode 100644 index 0000000..0128637 --- /dev/null +++ b/src/PhotoSizeSourceType.php @@ -0,0 +1,34 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://github.com/tg-file-decoder Documentation + */ + +namespace danog\Decoder; + +enum PhotoSizeSourceType: int +{ + case LEGACY = 0; + case THUMBNAIL = 1; + case DIALOGPHOTO_SMALL = 2; + case DIALOGPHOTO_BIG = 3; + case STICKERSET_THUMBNAIL = 4; + case FULL_LEGACY = 5; + case DIALOGPHOTO_SMALL_LEGACY = 6; + case DIALOGPHOTO_BIG_LEGACY = 7; + case STICKERSET_THUMBNAIL_LEGACY = 8; + case STICKERSET_THUMBNAIL_VERSION = 9; +} diff --git a/src/UniqueFileId.php b/src/UniqueFileId.php index 979cf77..709475f 100644 --- a/src/UniqueFileId.php +++ b/src/UniqueFileId.php @@ -24,14 +24,16 @@ /** * Represents decoded unique bot API file ID. + * + * @api */ -class UniqueFileId +final class UniqueFileId { /** * File type. * */ - private int $type = NONE; + private UniqueFileIdType $type; /** * File ID. * @@ -90,9 +92,9 @@ public function __toString(): string public function getUniqueBotAPI(): string { $fileId = \pack('V', $this->getType()); - if ($this->getType() === UNIQUE_WEB) { + if ($this->getType() === UniqueFileIdType::WEB) { $fileId .= packTLString($this->getUrl()); - } elseif ($this->getType() === UNIQUE_PHOTO) { + } elseif ($this->getType() === UniqueFileIdType::PHOTO) { if ($this->hasVolumeId()) { $fileId .= packLong($this->getVolumeId()); $fileId .= \pack('l', $this->getLocalId()); @@ -122,9 +124,9 @@ public static function fromUniqueBotAPI(string $fileId): self $result = new self(); $resultArray = internalDecodeUnique($fileId); $result->setType($resultArray['typeId']); - if ($result->getType() === UNIQUE_WEB) { + if ($result->getType() === UniqueFileIdType::WEB) { $result->setUrl($resultArray['url']); - } elseif ($result->getType() === UNIQUE_PHOTO) { + } elseif ($result->getType() === UniqueFileIdType::PHOTO) { if (isset($resultArray['volume_id'])) { $result->setVolumeId($resultArray['volume_id']); $result->setLocalId($resultArray['local_id']); @@ -163,13 +165,13 @@ public static function fromBotAPI(string $fileId): self public static function fromFileId(FileId $fileId): self { $result = new self(); - $result->setType(FULL_UNIQUE_MAP[$fileId->getType()]); + $result->setType($fileId->getType()->toUnique()); if ($result->hasUrl()) { - $result->setType(UNIQUE_WEB); + $result->setType(UniqueFileIdType::WEB); } - if ($result->getType() === UNIQUE_WEB) { + if ($result->getType() === UniqueFileIdType::WEB) { $result->setUrl($fileId->getUrl()); - } elseif ($result->getType() === UNIQUE_PHOTO) { + } elseif ($result->getType() === UniqueFileIdType::PHOTO) { if ($fileId->hasVolumeId()) { $result->setVolumeId($fileId->getVolumeId()); $result->setLocalId($fileId->getLocalId()); @@ -201,20 +203,11 @@ public static function fromFileId(FileId $fileId): self return $result; } - /** - * Get unique file type as string. - * - */ - public function getTypeName(): string - { - return UNIQUE_TYPES[$this->type]; - } - /** * Get unique file type. * */ - public function getType(): int + public function getType(): UniqueFileIdType { return $this->type; } @@ -222,10 +215,10 @@ public function getType(): int /** * Set file type. * - * @param int $type File type. + * @param UniqueFileIdType $type File type. * */ - public function setType(int $type): self + public function setType(UniqueFileIdType $type): self { $this->type = $type; diff --git a/src/UniqueFileIdType.php b/src/UniqueFileIdType.php new file mode 100644 index 0000000..09f199d --- /dev/null +++ b/src/UniqueFileIdType.php @@ -0,0 +1,29 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://github.com/tg-file-decoder Documentation + */ + +namespace danog\Decoder; + +enum UniqueFileIdType: int +{ + case WEB = 0; + case PHOTO = 1; + case DOCUMENT = 2; + case SECURE = 3; + case ENCRYPTED = 4; + case TEMP = 5; +} diff --git a/src/type.php b/src/functions.php similarity index 66% rename from src/type.php rename to src/functions.php index 9d48d15..97a1828 100644 --- a/src/type.php +++ b/src/functions.php @@ -1,204 +1,12 @@ . - * - * @author Daniil Gentili - * @copyright 2016-2019 Daniil Gentili - * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 - * - * @link https://github.com/tg-file-decoder Documentation - */ namespace danog\Decoder; -/** - * Thumbnail. - */ -const THUMBNAIL = 0; -/** - * Profile photo. - * Used for users and channels, chat photos are normal PHOTOs. - */ -const PROFILE_PHOTO = 1; -/** - * Normal photos. - */ -const PHOTO = 2; - -/** - * Voice messages. - */ -const VOICE = 3; -/** - * Video. - */ -const VIDEO = 4; -/** - * Document. - */ -const DOCUMENT = 5; -/** - * Secret chat document. - */ -const ENCRYPTED = 6; -/** - * Temporary document. - */ -const TEMP = 7; -/** - * Sticker. - */ -const STICKER = 8; -/** - * Music. - */ -const AUDIO = 9; -/** - * GIF. - */ -const ANIMATION = 10; -/** - * Encrypted thumbnail. - */ -const ENCRYPTED_THUMBNAIL = 11; -/** - * Wallpaper. - */ -const WALLPAPER = 12; -/** - * Round video. - */ -const VIDEO_NOTE = 13; -/** - * Passport raw file. - */ -const SECURE_RAW = 14; -/** - * Passport file. - */ -const SECURE = 15; -/** - * Background. - */ -const BACKGROUND = 16; -/** - * Size. - */ -const SIZE = 17; -const NONE = 18; - -const TYPES = [ - THUMBNAIL => 'thumbnail', - PROFILE_PHOTO => 'profile_photo', - PHOTO => 'photo', - VOICE => 'voice', - VIDEO => 'video', - DOCUMENT => 'document', - ENCRYPTED => 'encrypted', - TEMP => 'temp', - STICKER => 'sticker', - AUDIO => 'audio', - ANIMATION => 'animation', - ENCRYPTED_THUMBNAIL => 'encrypted_thumbnail', - WALLPAPER => 'wallpaper', - VIDEO_NOTE => 'video_note', - SECURE_RAW => 'secure_raw', - SECURE => 'secure', - BACKGROUND => 'background', - SIZE => 'size' -]; -const TYPES_IDS = [ - 'thumbnail' => THUMBNAIL, - 'profile_photo' => PROFILE_PHOTO, - 'photo' => PHOTO, - 'voice' => VOICE, - 'video' => VIDEO, - 'document' => DOCUMENT, - 'encrypted' => ENCRYPTED, - 'temp' => TEMP, - 'sticker' => STICKER, - 'audio' => AUDIO, - 'animation' => ANIMATION, - 'encrypted_thumbnail' => ENCRYPTED_THUMBNAIL, - 'wallpaper' => WALLPAPER, - 'video_note' => VIDEO_NOTE, - 'secure_raw' => SECURE_RAW, - 'secure' => SECURE, - 'background' => BACKGROUND, - 'size' => SIZE -]; - -const UNIQUE_WEB = 0; -const UNIQUE_PHOTO = 1; -const UNIQUE_DOCUMENT = 2; -const UNIQUE_SECURE = 3; -const UNIQUE_ENCRYPTED = 4; -const UNIQUE_TEMP = 5; - -const UNIQUE_TYPES = [ - UNIQUE_WEB => 'web', - UNIQUE_PHOTO => 'photo', - UNIQUE_DOCUMENT => 'document', - UNIQUE_SECURE => 'secure', - UNIQUE_ENCRYPTED => 'encrypted', - UNIQUE_TEMP => 'temp' -]; - -const UNIQUE_TYPES_IDS = [ - 'web' => UNIQUE_WEB, - 'photo' => UNIQUE_PHOTO, - 'document' => UNIQUE_DOCUMENT, - 'secure' => UNIQUE_SECURE, - 'encrypted' => UNIQUE_ENCRYPTED, - 'temp' => UNIQUE_TEMP -]; - -const FULL_UNIQUE_MAP = [ - PHOTO => UNIQUE_PHOTO, - PROFILE_PHOTO => UNIQUE_PHOTO, - THUMBNAIL => UNIQUE_PHOTO, - ENCRYPTED_THUMBNAIL => UNIQUE_PHOTO, - WALLPAPER => UNIQUE_PHOTO, - - VIDEO => UNIQUE_DOCUMENT, - VOICE => UNIQUE_DOCUMENT, - DOCUMENT => UNIQUE_DOCUMENT, - STICKER => UNIQUE_DOCUMENT, - AUDIO => UNIQUE_DOCUMENT, - ANIMATION => UNIQUE_DOCUMENT, - VIDEO_NOTE => UNIQUE_DOCUMENT, - BACKGROUND => UNIQUE_DOCUMENT, - - SECURE => UNIQUE_SECURE, - SECURE_RAW => UNIQUE_SECURE, - - ENCRYPTED => UNIQUE_ENCRYPTED, - - TEMP => UNIQUE_TEMP -]; - -const PHOTOSIZE_SOURCE_LEGACY = 0; -const PHOTOSIZE_SOURCE_THUMBNAIL = 1; -const PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL = 2; -const PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG = 3; -const PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL = 4; -const PHOTOSIZE_SOURCE_FULL_LEGACY = 5; -const PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY = 6; -const PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG_LEGACY = 7; -const PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_LEGACY = 8; -const PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_VERSION = 9; - const WEB_LOCATION_FLAG = 1 << 24; const FILE_REFERENCE_FLAG = 1 << 25; const LONG = PHP_INT_SIZE === 8 ? 'Q' : 'l2'; +/** @psalm-suppress UnusedVariable */ $BIG_ENDIAN = \pack('L', 1) === \pack('N', 1); /** @@ -361,7 +169,6 @@ function rleEncode(string $string): string } if ($count > 0) { $new .= $null.\chr($count); - $count = 0; } return $new; @@ -404,15 +211,16 @@ function readTLString(mixed $stream): string $x = \stream_get_contents($stream, $long_len); $resto = posmod(-$long_len, 4); if ($resto > 0) { - \stream_get_contents($stream, $resto); + \fseek($stream, $resto, SEEK_CUR); } } else { $x = $l ? \stream_get_contents($stream, $l) : ''; $resto = posmod(-($l + 1), 4); if ($resto > 0) { - \stream_get_contents($stream, $resto); + \fseek($stream, $resto, SEEK_CUR); } } + \assert($x !== false); return $x; } @@ -462,10 +270,7 @@ function internalDecode(string $fileId): array $result['hasWebLocation'] = (bool) ($result['typeId'] & WEB_LOCATION_FLAG); $result['typeId'] &= ~FILE_REFERENCE_FLAG; $result['typeId'] &= ~WEB_LOCATION_FLAG; - if (!isset(TYPES[$result['typeId']])) { - throw new \InvalidArgumentException("Invalid file type provided: {$result['typeId']}"); - } - $result['type'] = TYPES[$result['typeId']]; + $result['type'] = FileIdType::from($result['typeId']); $res = \fopen('php://memory', 'rw+b'); \fwrite($res, \substr($fileId, 8)); \fseek($res, 0); @@ -485,38 +290,38 @@ function internalDecode(string $fileId): array fixLong($result, 'id'); fixLong($result, 'access_hash'); - if ($result['typeId'] <= PHOTO) { + if ($result['typeId'] <= FileIdType::PHOTO->value) { $parsePhotoSize = function () use (&$result, &$fileId) { $result['photosize_source'] = $result['subVersion'] >= 4 ? \unpack('V', \stream_get_contents($fileId, 4))[1] : 0; switch ($result['photosize_source']) { - case PHOTOSIZE_SOURCE_LEGACY: + case PhotoSizeSourceType::LEGACY: $result += \unpack(LONG.'secret', \stream_get_contents($fileId, 8)); fixLong($result, 'secret'); break; - case PHOTOSIZE_SOURCE_THUMBNAIL: + case PhotoSizeSourceType::THUMBNAIL: $result += \unpack('Vfile_type/athumbnail_type', \stream_get_contents($fileId, 8)); break; - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL: - $result['photo_size'] = $result['photosize_source'] === PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL ? 'photo_small' : 'photo_big'; + case PhotoSizeSourceType::DIALOGPHOTO_BIG: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL: + $result['photo_size'] = $result['photosize_source'] === PhotoSizeSourceType::DIALOGPHOTO_SMALL ? 'photo_small' : 'photo_big'; $result['dialog_id'] = unpackLong(\stream_get_contents($fileId, 8)); $result['dialog_access_hash'] = \unpack(LONG, \stream_get_contents($fileId, 8))[1]; fixLong($result, 'dialog_access_hash'); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL: $result += \unpack(LONG.'sticker_set_id/'.LONG.'sticker_set_access_hash', \stream_get_contents($fileId, 16)); fixLong($result, 'sticker_set_id'); fixLong($result, 'sticker_set_access_hash'); break; - case PHOTOSIZE_SOURCE_FULL_LEGACY: + case PhotoSizeSourceType::FULL_LEGACY: $result += \unpack(LONG.'volume_id/'.LONG.'secret/llocal_id', \stream_get_contents($fileId, 20)); fixLong($result, 'volume_id'); fixLong($result, 'secret'); break; - case PHOTOSIZE_SOURCE_DIALOGPHOTO_BIG_LEGACY: - case PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY: - $result['photo_size'] = $result['photosize_source'] === PHOTOSIZE_SOURCE_DIALOGPHOTO_SMALL_LEGACY ? 'photo_small' : 'photo_big'; + case PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY: + case PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY: + $result['photo_size'] = $result['photosize_source'] === PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY ? 'photo_small' : 'photo_big'; $result['dialog_id'] = unpackLong(\stream_get_contents($fileId, 8)); $result['dialog_access_hash'] = \unpack(LONG, \stream_get_contents($fileId, 8))[1]; fixLong($result, 'dialog_access_hash'); @@ -524,7 +329,7 @@ function internalDecode(string $fileId): array $result += \unpack(LONG.'volume_id/llocal_id', \stream_get_contents($fileId, 12)); fixLong($result, 'volume_id'); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_LEGACY: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY: $result += \unpack(LONG.'sticker_set_id/'.LONG.'sticker_set_access_hash', \stream_get_contents($fileId, 16)); fixLong($result, 'sticker_set_id'); fixLong($result, 'sticker_set_access_hash'); @@ -533,7 +338,7 @@ function internalDecode(string $fileId): array fixLong($result, 'volume_id'); break; - case PHOTOSIZE_SOURCE_STICKERSET_THUMBNAIL_VERSION: + case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION: $result += \unpack(LONG.'sticker_set_id/'.LONG.'sticker_set_access_hash/lsticker_version', \stream_get_contents($fileId, 20)); fixLong($result, 'sticker_set_id'); fixLong($result, 'sticker_set_access_hash'); @@ -579,13 +384,10 @@ function internalDecodeUnique(string $fileId): array $fileId = rleDecode(base64urlDecode($fileId)); $result = \unpack('VtypeId', $fileId); - if (!isset(UNIQUE_TYPES[$result['typeId']])) { - throw new \InvalidArgumentException("Invalid file type provided: {$result['typeId']}"); - } - $result['type'] = UNIQUE_TYPES[$result['typeId']]; + $result['type'] = UniqueFileIdType::from($result['typeId']); $fileId = \substr($fileId, 4); - if ($result['typeId'] === UNIQUE_WEB) { + if ($result['typeId'] === UniqueFileIdType::WEB) { $res = \fopen('php://memory', 'rw+b'); \fwrite($res, $fileId); \fseek($res, 0); @@ -624,5 +426,6 @@ function internalDecodeUnique(string $fileId): array \trigger_error("Unique file ID $orig has $l bytes of leftover data"); } + \assert($result !== false); return $result; }