diff --git a/composer.json b/composer.json
index ac41d7c..dacd64a 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
}
],
"require": {
- "php": ">=8.1"
+ "php-64bit": ">=8.1"
},
"require-dev": {
"phpunit/phpunit": "^9",
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
deleted file mode 100644
index a0e23a0..0000000
--- a/psalm-baseline.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/psalm.xml b/psalm.xml
index 839a987..8ecbfb5 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -1,11 +1,13 @@
diff --git a/src/FileId.php b/src/FileId.php
index 3ddb30b..dd2d84c 100644
--- a/src/FileId.php
+++ b/src/FileId.php
@@ -19,6 +19,8 @@
namespace danog\Decoder;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
+use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhotoBig;
+use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhotoSmall;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceLegacy;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnail;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnailVersion;
@@ -31,73 +33,73 @@ use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;
*/
final class FileId
{
- /**
- * Bot API file ID version.
- *
- */
- private int $version = 4;
-
- /**
- * Bot API file ID subversion.
- *
- */
- private int $subVersion = 47;
-
- /**
- * DC ID.
- *
- */
- private int $dcId = 0;
-
- /**
- * File type.
- *
- */
- private FileIdType $type = FileIdType::NONE;
-
- /**
- * File reference.
- *
- */
- private string $fileReference = '';
- /**
- * File URL for weblocation.
- *
- */
- private string $url = '';
-
- /**
- * File id.
- *
- */
- private int $id;
- /**
- * File access hash.
- *
- */
- private int $accessHash;
-
- /**
- * Photo volume ID.
- *
- */
- private int $volumeId;
- /**
- * Photo local ID.
- *
- */
- private int $localId;
-
- /**
- * Photo size source.
- *
- */
- private PhotoSizeSource $photoSizeSource;
/**
* Basic constructor function.
*/
- public function __construct()
- {
+ public function __construct(
+ /**
+ * DC ID.
+ *
+ */
+ public readonly int $dcId,
+
+ /**
+ * File type.
+ *
+ */
+ public readonly FileIdType $type,
+
+ /**
+ * File id.
+ *
+ */
+ public readonly ?int $id,
+ /**
+ * File access hash.
+ *
+ */
+ public readonly int $accessHash,
+
+ /**
+ * Photo size source.
+ *
+ */
+ public readonly ?PhotoSizeSource $photoSizeSource = null,
+
+ /**
+ * Photo volume ID.
+ *
+ */
+ public readonly ?int $volumeId = null,
+ /**
+ * Photo local ID.
+ *
+ */
+ public readonly ?int $localId = null,
+
+ /**
+ * File reference.
+ *
+ */
+ public readonly ?string $fileReference = null,
+ /**
+ * File URL for weblocation.
+ *
+ */
+ public readonly ?string $url = null,
+
+ /**
+ * Bot API file ID version.
+ *
+ */
+ public readonly int $version = 4,
+
+ /**
+ * Bot API file ID subversion.
+ *
+ */
+ public readonly int $subVersion = 47,
+ ) {
}
/**
@@ -108,66 +110,139 @@ final class FileId
*/
public static function fromBotAPI(string $fileId): self
{
- $result = new self;
- $resultArray = internalDecode($fileId);
- $result->setVersion($resultArray['version']);
- $result->setSubVersion($resultArray['subVersion']);
- $result->setType($resultArray['typeId']);
- $result->setDcId($resultArray['dc_id']);
- $result->setAccessHash($resultArray['access_hash']);
+ $orig = $fileId;
+ $fileId = rleDecode(base64urlDecode($fileId));
+ $version = \ord($fileId[\strlen($fileId) - 1]);
+ $subVersion = $version === 4 ? \ord($fileId[\strlen($fileId) - 2]) : 0;
- if ($resultArray['hasReference']) {
- $result->setFileReference($resultArray['fileReference']);
- }
- if ($resultArray['hasWebLocation']) {
- $result->setUrl($resultArray['url']);
- return $result;
- }
- $result->setId($resultArray['id']);
+ $res = \fopen('php://memory', 'rw+b');
+ \assert($res !== false);
+ \fwrite($res, $fileId);
+ \fseek($res, 0);
+ $fileId = $res;
+ $read = function (int $length) use (&$fileId): string {
+ $res = \stream_get_contents($fileId, $length);
+ \assert($res !== false);
+ return $res;
+ };
- if ($result->getType()->value <= FileIdType::PHOTO->value) {
- if (isset($resultArray['volume_id'])) {
- $result->setVolumeId($resultArray['volume_id']);
+ $typeId = unpackInt($read(4));
+ $dc_id = unpackInt($read(4));
+ $fileReference = $typeId & FILE_REFERENCE_FLAG ? readTLString($fileId) : null;
+ $hasWebLocation = (bool) ($typeId & WEB_LOCATION_FLAG);
+ $typeId &= ~FILE_REFERENCE_FLAG;
+ $typeId &= ~WEB_LOCATION_FLAG;
+
+ if ($hasWebLocation) {
+ $url = readTLString($fileId);
+ $access_hash = unpackLong($read(8));
+ return new self(
+ $dc_id,
+ FileIdType::from($typeId),
+ null,
+ $access_hash,
+ fileReference: $fileReference,
+ url: $url,
+ version: $version,
+ subVersion: $subVersion
+ );
+ }
+ $id = unpackLong($read(8));
+ $access_hash = unpackLong($read(8));
+
+ $volume_id = null;
+ $local_id = null;
+ $photoSizeSource = null;
+ if ($typeId <= FileIdType::PHOTO->value) {
+ if ($subVersion < 32) {
+ $volume_id = unpackLong($read(8));
+ $local_id = unpackInt($read(4));
}
- if (isset($resultArray['local_id'])) {
- $result->setLocalId($resultArray['local_id']);
- }
- switch ($resultArray['photosize_source']) {
+
+ /** @psalm-suppress MixedArgument */
+ $photosize_source = PhotoSizeSourceType::from($subVersion >= 4 ? \unpack('V', $read(4))[1] : 0);
+ switch ($photosize_source) {
case PhotoSizeSourceType::LEGACY:
+ $photoSizeSource = new PhotoSizeSourceLegacy(unpackLong($read(8)));
+ break;
case PhotoSizeSourceType::FULL_LEGACY:
- $photoSizeSource = new PhotoSizeSourceLegacy($resultArray['photosize_source']);
- $photoSizeSource->setSecret($resultArray['secret']);
+ $volume_id = unpackLong($read(8));
+ $photoSizeSource = new PhotoSizeSourceLegacy(unpackLong($read(8)));
+ $local_id = unpackInt($read(4));
break;
case PhotoSizeSourceType::THUMBNAIL:
- $photoSizeSource = new PhotoSizeSourceThumbnail($resultArray['photosize_source']);
- $photoSizeSource->setThumbType($resultArray['thumbnail_type']);
- $photoSizeSource->setThumbFileType($resultArray['file_type']);
+ /** @var array{file_type: int, thumbnail_type: string} */
+ $result = \unpack('Vfile_type/athumbnail_type', $read(8));
+ $photoSizeSource = new PhotoSizeSourceThumbnail(
+ FileIdType::from($result['file_type']),
+ $result['thumbnail_type']
+ );
+ break;
+ case PhotoSizeSourceType::DIALOGPHOTO_BIG:
+ case PhotoSizeSourceType::DIALOGPHOTO_SMALL:
+ $clazz = $photosize_source === PhotoSizeSourceType::DIALOGPHOTO_SMALL
+ ? PhotoSizeSourceDialogPhotoSmall::class
+ : PhotoSizeSourceDialogPhotoBig::class;
+ $photoSizeSource = new $clazz(
+ unpackLong($read(8)),
+ unpackLong($read(8)),
+ );
+ break;
+ case PhotoSizeSourceType::STICKERSET_THUMBNAIL:
+ $photoSizeSource = new PhotoSizeSourceStickersetThumbnail(
+ unpackLong($read(8)),
+ unpackLong($read(8))
+ );
break;
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']);
+ $clazz = $photosize_source === PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY
+ ? PhotoSizeSourceDialogPhotoSmall::class
+ : PhotoSizeSourceDialogPhotoBig::class;
+ $photoSizeSource = new $clazz(
+ unpackLong($read(8)),
+ unpackLong($read(8))
+ );
+
+ $volume_id = unpackLong($read(8));
+ $local_id = unpackInt($read(4));
break;
- 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']);
+ $photoSizeSource = new PhotoSizeSourceStickersetThumbnail(
+ unpackLong($read(8)),
+ unpackLong($read(8)),
+ );
+
+ $volume_id = unpackLong($read(8));
+ $local_id = unpackInt($read(4));
break;
case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION:
- $photoSizeSource = new PhotoSizeSourceStickersetThumbnailVersion($resultArray['photosize_source']);
- $photoSizeSource->setStickerSetId($resultArray['sticker_set_id']);
- $photoSizeSource->setStickerSetAccessHash($resultArray['sticker_set_access_hash']);
- $photoSizeSource->setStickerSetVersion($resultArray['sticker_version']);
+ $photoSizeSource = new PhotoSizeSourceStickersetThumbnailVersion(
+ unpackLong($read(8)),
+ unpackLong($read(8)),
+ unpackInt($read(4))
+ );
break;
}
- $result->setPhotoSizeSource($photoSizeSource);
+ }
+ $l = \fstat($fileId)['size'] - \ftell($fileId);
+ $l -= $version >= 4 ? 2 : 1;
+ if ($l > 0) {
+ \trigger_error("File ID $orig has $l bytes of leftover data");
}
- return $result;
+ return new self(
+ dcId: $dc_id,
+ type: FileIdType::from($typeId),
+ id: $id,
+ accessHash: $access_hash,
+ volumeId: $volume_id,
+ localId: $local_id,
+ fileReference: $fileReference,
+ version: $version,
+ subVersion: $subVersion,
+ photoSizeSource: $photoSizeSource,
+ );
}
/**
@@ -176,76 +251,86 @@ final class FileId
*/
public function getBotAPI(): string
{
- $type = $this->getType()->value;
- if ($this->hasFileReference()) {
+ $type = $this->type->value;
+ if ($this->fileReference !== null) {
$type |= FILE_REFERENCE_FLAG;
}
- if ($this->hasUrl()) {
+ if ($this->url !== null) {
$type |= WEB_LOCATION_FLAG;
}
- $fileId = \pack('VV', $type, $this->getDcId());
- if ($this->hasFileReference()) {
- $fileId .= packTLString($this->getFileReference());
+ $fileId = \pack('VV', $type, $this->dcId);
+ if ($this->fileReference !== null) {
+ $fileId .= packTLString($this->fileReference);
}
- if ($this->hasUrl()) {
- $fileId .= packTLString($this->getUrl());
- $fileId .= packLong($this->getAccessHash());
+ if ($this->url !== null) {
+ $fileId .= packTLString($this->url);
+ $fileId .= packLong($this->accessHash);
return base64urlEncode(rleEncode($fileId));
}
- $fileId .= packLong($this->getId());
- $fileId .= packLong($this->getAccessHash());
+ \assert($this->id !== null);
+ $fileId .= packLong($this->id);
+ $fileId .= packLong($this->accessHash);
- if ($this->getType()->value <= FileIdType::PHOTO->value) {
- $photoSize = $this->getPhotoSizeSource();
- $fileId .= \pack('V', $photoSize->getType());
- switch ($photoSize->getType()) {
- case PhotoSizeSourceType::LEGACY:
- assert($photoSize instanceof PhotoSizeSourceLegacy);
- $fileId .= packLong($photoSize->getSecret());
+ if ($this->photoSizeSource !== null) {
+ $photoSize = $this->photoSizeSource;
+ $writeExtra = false;
+ switch (true) {
+ case $photoSize instanceof PhotoSizeSourceLegacy:
+ if ($this->volumeId === null) {
+ $writeExtra = true;
+ $fileId .= \pack('V', PhotoSizeSourceType::LEGACY->value);
+ $fileId .= packLong($photoSize->secret);
+ } else {
+ $fileId .= \pack('V', PhotoSizeSourceType::FULL_LEGACY->value);
+ $fileId .= packLong($this->volumeId);
+ $fileId .= packLong($photoSize->secret);
+ $fileId .= \pack('l', $this->localId);
+ }
break;
- case PhotoSizeSourceType::FULL_LEGACY:
- assert($photoSize instanceof PhotoSizeSourceLegacy);
- $fileId .= packLong($this->getVolumeId());
- $fileId .= packLong($photoSize->getSecret());
- $fileId .= \pack('l', $this->getLocalId());
+ case $photoSize instanceof PhotoSizeSourceThumbnail:
+ $fileId .= \pack('V', PhotoSizeSourceType::THUMBNAIL->value);
+ $fileId .= \pack('Va4', $photoSize->thumbFileType->value, $photoSize->thumbType);
break;
- case PhotoSizeSourceType::THUMBNAIL:
- assert($photoSize instanceof PhotoSizeSourceThumbnail);
- $fileId .= \pack('Va4', $photoSize->getThumbFileType(), $photoSize->getThumbType());
+ case $photoSize instanceof PhotoSizeSourceDialogPhoto:
+ $fileId .= \pack(
+ 'V',
+ ($writeExtra = $this->volumeId !== null) ?
+ (
+ $photoSize->isSmallDialogPhoto()
+ ? PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY->value
+ : PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY->value
+ ) : (
+ $photoSize->isSmallDialogPhoto()
+ ? PhotoSizeSourceType::DIALOGPHOTO_SMALL->value
+ : PhotoSizeSourceType::DIALOGPHOTO_BIG->value
+ )
+ );
+ $fileId .= packLong($photoSize->dialogId);
+ $fileId .= packLong($photoSize->dialogAccessHash);
break;
- 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());
+ case $photoSize instanceof PhotoSizeSourceStickersetThumbnail:
+ $writeExtra = $this->volumeId !== null;
+ $fileId .= packLong($photoSize->stickerSetId);
+ $fileId .= packLong($photoSize->stickerSetAccessHash);
break;
- case PhotoSizeSourceType::STICKERSET_THUMBNAIL:
- case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY:
- assert($photoSize instanceof PhotoSizeSourceStickersetThumbnail);
- $fileId .= packLong($photoSize->getStickerSetId());
- $fileId .= packLong($photoSize->getStickerSetAccessHash());
- break;
- case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION:
- assert($photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion);
- $fileId .= packLong($photoSize->getStickerSetId());
- $fileId .= packLong($photoSize->getStickerSetAccessHash());
- $fileId .= \pack('l', $photoSize->getStickerSetVersion());
+ case $photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion:
+ $fileId .= packLong($photoSize->stickerSetId);
+ $fileId .= packLong($photoSize->stickerSetAccessHash);
+ $fileId .= \pack('l', $photoSize->stickerSetVersion);
break;
}
- if ($photoSize->getType() >= PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY && $photoSize->getType()->value <= PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY->value) {
- $fileId .= packLong($this->getVolumeId());
- $fileId .= \pack('l', $this->getLocalId());
+ if ($writeExtra && $this->volumeId !== null && $this->localId !== null) {
+ $fileId .= packLong($this->volumeId);
+ $fileId .= \pack('l', $this->localId);
}
}
- if ($this->getVersion() >= 4) {
- $fileId .= \chr($this->getSubVersion());
+ if ($this->version >= 4) {
+ $fileId .= \chr($this->subVersion);
}
- $fileId .= \chr($this->getVersion());
+ $fileId .= \chr($this->version);
return base64urlEncode(rleEncode($fileId));
}
@@ -274,306 +359,4 @@ final class FileId
{
return $this->getBotAPI();
}
- /**
- * Get bot API file ID version.
- *
- */
- public function getVersion(): int
- {
- return $this->version;
- }
-
- /**
- * Set bot API file ID version.
- *
- * @param int $version Bot API file ID version.
- *
- */
- public function setVersion(int $version): self
- {
- $this->version = $version;
-
- return $this;
- }
-
- /**
- * Get bot API file ID subversion.
- *
- */
- public function getSubVersion(): int
- {
- return $this->subVersion;
- }
-
- /**
- * Set bot API file ID subversion.
- *
- * @param int $subVersion Bot API file ID subversion.
- *
- */
- public function setSubVersion(int $subVersion): self
- {
- $this->subVersion = $subVersion;
-
- return $this;
- }
-
- /**
- * Get file type.
- *
- */
- public function getType(): FileIdType
- {
- return $this->type;
- }
-
- /**
- * Set file type.
- *
- * @param FileIdType $type File type.
- *
- */
- public function setType(FileIdType $type): self
- {
- $this->type = $type;
-
- return $this;
- }
-
- /**
- * Get file reference.
- *
- */
- public function getFileReference(): string
- {
- return $this->fileReference;
- }
-
- /**
- * Set file reference.
- *
- * @param string $fileReference File reference.
- *
- */
- public function setFileReference(string $fileReference): self
- {
- $this->fileReference = $fileReference;
-
- return $this;
- }
-
- /**
- * Check if has file reference.
- *
- * @return boolean
- */
- public function hasFileReference(): bool
- {
- return !empty($this->fileReference);
- }
-
- /**
- * Get file URL for weblocation.
- *
- */
- public function getUrl(): string
- {
- return $this->url;
- }
-
- /**
- * Check if has file URL.
- *
- * @return boolean
- */
- public function hasUrl(): bool
- {
- return !empty($this->url);
- }
-
- /**
- * Set file URL for weblocation.
- *
- * @param string $url File URL for weblocation.
- *
- */
- public function setUrl(string $url): self
- {
- $this->url = $url;
-
- return $this;
- }
-
- /**
- * Get file id.
- *
- * @return int
- */
- public function getId()
- {
- return $this->id;
- }
-
- /**
- * Set file id.
- *
- * @param int $id File id.
- *
- */
- public function setId(int $id): self
- {
- $this->id = $id;
-
- return $this;
- }
-
- /**
- * Check if has file id.
- *
- */
- public function hasId(): bool
- {
- return isset($this->id);
- }
-
- /**
- * Get file access hash.
- *
- * @return int
- */
- public function getAccessHash()
- {
- return $this->accessHash;
- }
-
- /**
- * Set file access hash.
- *
- * @param int $accessHash File access hash.
- *
- */
- public function setAccessHash(int $accessHash): self
- {
- $this->accessHash = $accessHash;
-
- return $this;
- }
-
- /**
- * Get photo volume ID.
- *
- * @return int
- */
- public function getVolumeId()
- {
- return $this->volumeId;
- }
-
- /**
- * Set photo volume ID.
- *
- * @param int $volumeId Photo volume ID.
- *
- */
- public function setVolumeId(int $volumeId): self
- {
- $this->volumeId = $volumeId;
-
- return $this;
- }
- /**
- * Check if has volume ID.
- *
- * @return boolean
- */
- public function hasVolumeId(): bool
- {
- return isset($this->volumeId);
- }
-
- /**
- * Get photo local ID.
- *
- */
- public function getLocalId(): int
- {
- return $this->localId;
- }
-
- /**
- * Set photo local ID.
- *
- * @param int $localId Photo local ID.
- *
- */
- public function setLocalId(int $localId): self
- {
- $this->localId = $localId;
-
- return $this;
- }
-
- /**
- * Check if has local ID.
- *
- * @return boolean
- */
- public function hasLocalId(): bool
- {
- return isset($this->localId);
- }
-
- /**
- * Get photo size source.
- *
- */
- public function getPhotoSizeSource(): PhotoSizeSource
- {
- return $this->photoSizeSource;
- }
-
- /**
- * Set photo size source.
- *
- * @param PhotoSizeSource $photoSizeSource Photo size source.
- *
- */
- public function setPhotoSizeSource(PhotoSizeSource $photoSizeSource): self
- {
- $this->photoSizeSource = $photoSizeSource;
-
- return $this;
- }
-
- /**
- * Check if has photo size source.
- *
- * @return boolean
- */
- public function hasPhotoSizeSource(): bool
- {
- return isset($this->photoSizeSource);
- }
-
- /**
- * Get dC ID.
- *
- */
- public function getDcId(): int
- {
- return $this->dcId;
- }
-
- /**
- * Set dC ID.
- *
- * @param int $dcId DC ID.
- *
- */
- public function setDcId(int $dcId): self
- {
- $this->dcId = $dcId;
-
- return $this;
- }
}
diff --git a/src/PhotoSizeSource.php b/src/PhotoSizeSource.php
index e839828..c253f55 100644
--- a/src/PhotoSizeSource.php
+++ b/src/PhotoSizeSource.php
@@ -18,50 +18,9 @@
namespace danog\Decoder;
-use danog\Decoder\PhotoSizeSource\PhotoSizeSourceLegacy;
-
/**
* Represents source of photosize.
- *
- * @template T
*/
abstract class PhotoSizeSource
{
- /**
- * Source type.
- *
- */
- private PhotoSizeSourceType $type;
-
- /**
- * Set photosize source type.
- *
- * @param PhotoSizeSourceType $type Type
- */
- public function __construct(PhotoSizeSourceType $type)
- {
- $this->type = $type;
- }
- /**
- * Get photosize source type.
- *
- * @return integer
- *
- * @psalm-return (
- * T is PhotoSizeSourceLegacy ?
- * ? \danog\Decoder\PhotoSizeSourceType::LEGACY
- * : (T is PhotoSizeSourceDialogPhoto
- * ? \danog\Decoder\PhotoSizeSourceType::DIALOGPHOTO_*
- * (T is PhotoSizeSourceStickersetThumbnail
- * ? \danog\Decoder\PhotoSizeSourceType::STICKERSET_THUMBNAIL
- * : \danog\Decoder\PhotoSizeSourceType::THUMBNAIL
- * )
- * )
- *
- * @internal Internal use
- */
- public function getType(): PhotoSizeSourceType
- {
- return $this->type;
- }
}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php b/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php
index 2e26520..9e94dbd 100644
--- a/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php
+++ b/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php
@@ -19,77 +19,22 @@
namespace danog\Decoder\PhotoSizeSource;
use danog\Decoder\PhotoSizeSource;
-use danog\Decoder\PhotoSizeSourceType;
/**
* Represents source of photosize.
*
* @api
- *
- * @extends PhotoSizeSource
*/
-final class PhotoSizeSourceDialogPhoto extends PhotoSizeSource
+abstract class PhotoSizeSourceDialogPhoto extends PhotoSizeSource
{
- /**
- * ID of dialog.
- *
- */
- private int $dialogId;
- /**
- * Access hash of dialog.
- *
- */
- private int $dialogAccessHash;
-
- /**
- * Get dialog ID.
- *
- * @return int
- */
- public function getDialogId()
- {
- return $this->dialogId;
+ public function __construct(
+ public readonly int $dialogId,
+ public readonly int $dialogAccessHash,
+ ) {
}
- /**
- * Set dialog ID.
- *
- * @param int $id Dialog ID
- *
- */
- public function setDialogId(int $id): self
- {
- $this->dialogId = $id;
- return $this;
- }
- /**
- * Get access hash of dialog.
- *
- * @return int
- */
- public function getDialogAccessHash()
- {
- return $this->dialogAccessHash;
- }
-
- /**
- * Set access hash of dialog.
- *
- * @param int $dialogAccessHash Access hash of dialog
- *
- */
- public function setDialogAccessHash(int $dialogAccessHash): self
- {
- $this->dialogAccessHash = $dialogAccessHash;
-
- return $this;
- }
-
/**
* Get whether the big or small version of the photo is being used.
*
*/
- public function isSmallDialogPhoto(): bool
- {
- return \in_array($this->getType(), [PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY, PhotoSizeSourceType::DIALOGPHOTO_SMALL], true);
- }
+ abstract public function isSmallDialogPhoto(): bool;
}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php b/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php
new file mode 100644
index 0000000..137e328
--- /dev/null
+++ b/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php
@@ -0,0 +1,36 @@
+.
+ *
+ * @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\PhotoSizeSource;
+
+/**
+ * Represents source of photosize.
+ *
+ * @api
+ */
+final class PhotoSizeSourceDialogPhotoBig extends PhotoSizeSourceDialogPhoto
+{
+ /**
+ * Get whether the big or small version of the photo is being used.
+ *
+ */
+ public function isSmallDialogPhoto(): bool
+ {
+ return false;
+ }
+}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php b/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php
new file mode 100644
index 0000000..ecbd82a
--- /dev/null
+++ b/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php
@@ -0,0 +1,36 @@
+.
+ *
+ * @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\PhotoSizeSource;
+
+/**
+ * Represents source of photosize.
+ *
+ * @api
+ */
+final class PhotoSizeSourceDialogPhotoSmall extends PhotoSizeSourceDialogPhoto
+{
+ /**
+ * Get whether the big or small version of the photo is being used.
+ *
+ */
+ public function isSmallDialogPhoto(): bool
+ {
+ return true;
+ }
+}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceLegacy.php b/src/PhotoSizeSource/PhotoSizeSourceLegacy.php
index 9ed8948..56ae157 100644
--- a/src/PhotoSizeSource/PhotoSizeSourceLegacy.php
+++ b/src/PhotoSizeSource/PhotoSizeSourceLegacy.php
@@ -24,37 +24,11 @@ use danog\Decoder\PhotoSizeSource;
* Represents source of photosize.
*
* @api
- *
- * @extends PhotoSizeSource
*/
final class PhotoSizeSourceLegacy extends PhotoSizeSource
{
- /**
- * Secret legacy ID.
- *
- */
- private int $secret;
-
- /**
- * Get secret legacy ID.
- *
- * @return int
- */
- public function getSecret()
- {
- return $this->secret;
- }
-
- /**
- * Set secret legacy ID.
- *
- * @param int $secret Secret legacy ID
- *
- */
- public function setSecret(int $secret): self
- {
- $this->secret = $secret;
-
- return $this;
+ public function __construct(
+ public readonly int $secret,
+ ) {
}
}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php
index c3f48f3..eb055e3 100644
--- a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php
+++ b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php
@@ -24,65 +24,21 @@ use danog\Decoder\PhotoSizeSource;
* Represents source of photosize.
*
* @api
- *
- * @extends PhotoSizeSource
*/
final class PhotoSizeSourceStickersetThumbnail extends PhotoSizeSource
{
- /**
- * Stickerset ID.
- *
- */
- private int $stickerSetId;
- /**
- * Stickerset access hash.
- *
- */
- private int $stickerSetAccessHash;
+ public function __construct(
+ /**
+ * Stickerset ID.
+ *
+ */
+ public readonly int $stickerSetId,
+ /**
+ * Stickerset access hash.
+ *
+ */
+ public readonly int $stickerSetAccessHash
+ ) {
- /**
- * Get stickerset ID.
- *
- * @return int
- */
- public function getStickerSetId()
- {
- return $this->stickerSetId;
- }
-
- /**
- * Set stickerset ID.
- *
- * @param int $stickerSetId Stickerset ID
- *
- */
- public function setStickerSetId(int $stickerSetId): self
- {
- $this->stickerSetId = $stickerSetId;
-
- return $this;
- }
-
- /**
- * Get stickerset access hash.
- *
- * @return int
- */
- public function getStickerSetAccessHash()
- {
- return $this->stickerSetAccessHash;
- }
-
- /**
- * Set stickerset access hash.
- *
- * @param int $stickerSetAccessHash Stickerset access hash
- *
- */
- public function setStickerSetAccessHash(int $stickerSetAccessHash): self
- {
- $this->stickerSetAccessHash = $stickerSetAccessHash;
-
- return $this;
}
}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php
index a0c1eb6..7ff55f9 100644
--- a/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php
+++ b/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php
@@ -24,92 +24,26 @@ use danog\Decoder\PhotoSizeSource;
* Represents source of photosize.
*
* @api
- *
- * @extends PhotoSizeSource
*/
final class PhotoSizeSourceStickersetThumbnailVersion extends PhotoSizeSource
{
- /**
- * Stickerset ID.
- *
- */
- private int $stickerSetId;
- /**
- * Stickerset access hash.
- *
- */
- private int $stickerSetAccessHash;
- /**
- * Stickerset version.
- *
- */
- private int $stickerSetVersion;
+ public function __construct(
+ /**
+ * Stickerset ID.
+ *
+ */
+ public readonly int $stickerSetId,
+ /**
+ * Stickerset access hash.
+ *
+ */
+ public readonly int $stickerSetAccessHash,
+ /**
+ * Stickerset version.
+ *
+ */
+ public readonly int $stickerSetVersion
+ ) {
- /**
- * Get stickerset ID.
- *
- * @return int
- */
- public function getStickerSetId()
- {
- return $this->stickerSetId;
- }
-
- /**
- * Set stickerset ID.
- *
- * @param int $stickerSetId Stickerset ID
- *
- */
- public function setStickerSetId(int $stickerSetId): self
- {
- $this->stickerSetId = $stickerSetId;
-
- return $this;
- }
-
- /**
- * Get stickerset access hash.
- *
- * @return int
- */
- public function getStickerSetAccessHash()
- {
- return $this->stickerSetAccessHash;
- }
-
- /**
- * Set stickerset access hash.
- *
- * @param int $stickerSetAccessHash Stickerset access hash
- *
- */
- public function setStickerSetAccessHash(int $stickerSetAccessHash): self
- {
- $this->stickerSetAccessHash = $stickerSetAccessHash;
-
- return $this;
- }
-
- /**
- * Get stickerset version.
- *
- */
- public function getStickerSetVersion(): int
- {
- return $this->stickerSetVersion;
- }
-
- /**
- * Set stickerset version.
- *
- * @param int $stickerSetVersion Stickerset version.
- *
- */
- public function setStickerSetVersion(int $stickerSetVersion): self
- {
- $this->stickerSetVersion = $stickerSetVersion;
-
- return $this;
}
}
diff --git a/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php b/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php
index aa7d594..348151f 100644
--- a/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php
+++ b/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php
@@ -25,62 +25,20 @@ use danog\Decoder\PhotoSizeSource;
* Represents source of photosize.
*
* @api
- *
- * @extends PhotoSizeSource
*/
final class PhotoSizeSourceThumbnail extends PhotoSizeSource
{
- /**
- * File type of original file.
- *
- */
- private FileIdType $thumbFileType;
- /**
- * Thumbnail size.
- *
- */
- private string $thumbType;
-
- /**
- * Get file type of original file.
- *
- */
- public function getThumbFileType(): FileIdType
- {
- return $this->thumbFileType;
- }
- /**
- * Set file type of original file.
- *
- * @param FileIdType $thumbFileType File type of original file
- *
- */
- public function setThumbFileType(FileIdType $thumbFileType): self
- {
- $this->thumbFileType = $thumbFileType;
-
- return $this;
- }
-
- /**
- * Get thumbnail size.
- *
- */
- public function getThumbType(): string
- {
- return $this->thumbType;
- }
-
- /**
- * Set thumbnail size.
- *
- * @param string $thumbType Thumbnail size
- *
- */
- public function setThumbType(string $thumbType): self
- {
- $this->thumbType = $thumbType;
-
- return $this;
+ public function __construct(
+ /**
+ * File type of original file.
+ *
+ */
+ public readonly FileIdType $thumbFileType,
+ /**
+ * Thumbnail size.
+ *
+ */
+ public readonly string $thumbType,
+ ) {
}
}
diff --git a/src/PhotoSizeSourceType.php b/src/PhotoSizeSourceType.php
index 0128637..aac18d9 100644
--- a/src/PhotoSizeSourceType.php
+++ b/src/PhotoSizeSourceType.php
@@ -19,6 +19,9 @@
namespace danog\Decoder;
+/**
+ * @internal Not for public use
+ */
enum PhotoSizeSourceType: int
{
case LEGACY = 0;
diff --git a/src/UniqueFileId.php b/src/UniqueFileId.php
index 709475f..086071c 100644
--- a/src/UniqueFileId.php
+++ b/src/UniqueFileId.php
@@ -29,51 +29,51 @@ use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;
*/
final class UniqueFileId
{
- /**
- * File type.
- *
- */
- private UniqueFileIdType $type;
- /**
- * File ID.
- *
- */
- private int $id;
- /**
- * Photo volume ID.
- *
- */
- private int $volumeId;
- /**
- * Photo local ID.
- *
- */
- private int $localId;
- /**
- * Photo subtype.
- *
- */
- private int $subType;
- /**
- * Sticker set ID.
- *
- */
- private int $stickerSetId;
- /**
- * Sticker set version.
- *
- */
- private int $stickerSetVersion;
- /**
- * Weblocation URL.
- *
- */
- private string $url;
/**
* Basic constructor function.
*/
- public function __construct()
- {
+ public function __construct(
+ /**
+ * File type.
+ *
+ */
+ public readonly UniqueFileIdType $type,
+ /**
+ * File ID.
+ *
+ */
+ public readonly ?int $id = null,
+ /**
+ * Photo subtype.
+ *
+ */
+ public readonly ?int $subType = null,
+ /**
+ * Photo volume ID.
+ *
+ */
+ public readonly ?int $volumeId = null,
+ /**
+ * Photo local ID.
+ *
+ */
+ public readonly ?int $localId = null,
+ /**
+ * Sticker set ID.
+ *
+ */
+ public readonly ?int $stickerSetId = null,
+ /**
+ * Sticker set version.
+ *
+ */
+ public readonly ?int $stickerSetVersion = null,
+ /**
+ * Weblocation URL.
+ *
+ */
+ public readonly ?string $url= null,
+ ) {
}
/**
@@ -91,23 +91,25 @@ final class UniqueFileId
*/
public function getUniqueBotAPI(): string
{
- $fileId = \pack('V', $this->getType());
- if ($this->getType() === UniqueFileIdType::WEB) {
- $fileId .= packTLString($this->getUrl());
- } elseif ($this->getType() === UniqueFileIdType::PHOTO) {
- if ($this->hasVolumeId()) {
- $fileId .= packLong($this->getVolumeId());
- $fileId .= \pack('l', $this->getLocalId());
- } elseif ($this->hasStickerSetId()) {
- $fileId .= \chr($this->getSubType());
- $fileId .= packLong($this->getStickerSetId());
- $fileId .= \pack('l', $this->getStickerSetVersion());
+ $fileId = \pack('V', $this->type->value);
+ if ($this->url !== null) {
+ $fileId .= packTLString($this->url);
+ } elseif ($this->type === UniqueFileIdType::PHOTO) {
+ if ($this->volumeId !== null) {
+ $fileId .= packLong($this->volumeId);
+ $fileId .= \pack('l', $this->localId);
+ } elseif ($this->stickerSetId !== null) {
+ \assert($this->subType !== null);
+ $fileId .= \chr($this->subType);
+ $fileId .= packLong($this->stickerSetId);
+ $fileId .= \pack('l', $this->stickerSetVersion);
} else {
- $fileId .= packLong($this->getId());
- $fileId .= \chr($this->getSubType());
+ \assert($this->subType !== null && $this->id !== null);
+ $fileId .= packLong($this->id);
+ $fileId .= \chr($this->subType);
}
- } elseif ($this->hasId()) {
- $fileId .= packLong($this->getId());
+ } elseif ($this->id !== null) {
+ $fileId .= packLong($this->id);
}
return base64urlEncode(rleEncode($fileId));
@@ -121,28 +123,61 @@ final class UniqueFileId
*/
public static function fromUniqueBotAPI(string $fileId): self
{
- $result = new self();
- $resultArray = internalDecodeUnique($fileId);
- $result->setType($resultArray['typeId']);
- if ($result->getType() === UniqueFileIdType::WEB) {
- $result->setUrl($resultArray['url']);
- } elseif ($result->getType() === UniqueFileIdType::PHOTO) {
- if (isset($resultArray['volume_id'])) {
- $result->setVolumeId($resultArray['volume_id']);
- $result->setLocalId($resultArray['local_id']);
- } elseif (isset($resultArray['id'])) {
- $result->setId($resultArray['id']);
- $result->setSubType($resultArray['subType']);
- } elseif (isset($resultArray['sticker_set_id'])) {
- $result->setStickerSetId($resultArray['sticker_set_id']);
- $result->setStickerSetVersion($resultArray['sticker_set_version']);
- $result->setSubType($resultArray['subType']);
- }
- } elseif (isset($resultArray['id'])) {
- $result->setId($resultArray['id']);
- }
+ $orig = $fileId;
+ $fileId = rleDecode(base64urlDecode($fileId));
- return $result;
+ /** @var int */
+ $typeId = \unpack('V', $fileId)[1];
+ $type = UniqueFileIdType::from($typeId);
+ $url = null;
+
+ $subType = null;
+ $id = null;
+ $fileId = \substr($fileId, 4);
+ $volume_id = null;
+ $local_id = null;
+ $sticker_set_id = null;
+ $sticker_set_version = null;
+ if ($type === UniqueFileIdType::WEB) {
+ $res = \fopen('php://memory', 'rw+b');
+ \assert($res !== false);
+ \fwrite($res, $fileId);
+ \fseek($res, 0);
+ $fileId = $res;
+ $url = readTLString($fileId);
+
+ $l = \fstat($fileId)['size'] - \ftell($fileId);
+ \trigger_error("Unique file ID $orig has $l bytes of leftover data");
+ } elseif (\strlen($fileId) === 12) {
+ // Legacy photos
+ $volume_id = unpackLong(\substr($fileId, 0, 8));
+ $local_id = unpackInt(\substr($fileId, 8));
+ } elseif (\strlen($fileId) === 9) {
+ // Dialog photos/thumbnails
+ $id = unpackLong($fileId);
+ $subType = \ord($fileId[8]);
+ } elseif (\strlen($fileId) === 13) {
+ // Stickerset ID/version
+ $subType = \ord($fileId[0]);
+ $sticker_set_id = unpackLong(\substr($fileId, 1, 8));
+ $sticker_set_version = unpackInt(\substr($fileId, 9));
+ } elseif (\strlen($fileId) === 8) {
+ // Any other document
+ $id = unpackLong($fileId);
+ } else {
+ $l = \strlen($fileId);
+ \trigger_error("Unique file ID $orig has $l bytes of leftover data");
+ }
+ return new self(
+ type: $type,
+ id: $id,
+ subType: $subType,
+ volumeId: $volume_id,
+ localId: $local_id,
+ stickerSetId: $sticker_set_id,
+ stickerSetVersion: $sticker_set_version,
+ url: $url
+ );
}
/**
@@ -164,287 +199,45 @@ final class UniqueFileId
*/
public static function fromFileId(FileId $fileId): self
{
- $result = new self();
- $result->setType($fileId->getType()->toUnique());
- if ($result->hasUrl()) {
- $result->setType(UniqueFileIdType::WEB);
+ if ($fileId->url !== null) {
+ return new self(
+ UniqueFileIdType::WEB,
+ url: $fileId->url
+ );
}
- if ($result->getType() === UniqueFileIdType::WEB) {
- $result->setUrl($fileId->getUrl());
- } elseif ($result->getType() === UniqueFileIdType::PHOTO) {
- if ($fileId->hasVolumeId()) {
- $result->setVolumeId($fileId->getVolumeId());
- $result->setLocalId($fileId->getLocalId());
- } elseif ($fileId->hasId()) {
- $result->setId($fileId->getId());
- $photoSize = $fileId->getPhotoSizeSource();
- if ($photoSize instanceof PhotoSizeSourceThumbnail) {
- $type = $photoSize->getThumbType();
- if ($type === 'a') {
- $type = \chr(0);
- } elseif ($type === 'c') {
- $type = \chr(1);
- } else {
- $type = \chr(\ord($type)+5);
- }
- $result->setSubType(\ord($type));
- } elseif ($photoSize instanceof PhotoSizeSourceDialogPhoto) {
- $result->setSubType($photoSize->isSmallDialogPhoto() ? 0 : 1);
- } elseif ($photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion) {
- $result->setSubType(2);
- $result->setStickerSetId($photoSize->getStickerSetId());
- $result->setStickerSetVersion($photoSize->getStickerSetVersion());
+ $type = $fileId->type->toUnique();
+ if ($type === UniqueFileIdType::PHOTO) {
+ $photoSize = $fileId->photoSizeSource;
+ $subType = null;
+ if ($photoSize instanceof PhotoSizeSourceThumbnail) {
+ $subType = \ord($photoSize->thumbType);
+ if ($subType === 97) {
+ $subType = 0;
+ } elseif ($subType === 99) {
+ $subType = 1;
+ } else {
+ $subType = $subType+5;
}
+ $subType = $subType;
+ } elseif ($photoSize instanceof PhotoSizeSourceDialogPhoto) {
+ $subType = $photoSize->isSmallDialogPhoto() ? 0 : 1;
+ } elseif ($photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion) {
+ return new self(
+ $type,
+ $fileId->id,
+ 2,
+ stickerSetId: $photoSize->stickerSetId,
+ stickerSetVersion: $photoSize->stickerSetVersion,
+ );
}
- } elseif ($fileId->hasId()) {
- $result->setId($fileId->getId());
+ return new self(
+ $type,
+ $fileId->id,
+ $subType,
+ $fileId->volumeId,
+ $fileId->localId,
+ );
}
-
- return $result;
- }
-
- /**
- * Get unique file type.
- *
- */
- public function getType(): UniqueFileIdType
- {
- return $this->type;
- }
-
- /**
- * Set file type.
- *
- * @param UniqueFileIdType $type File type.
- *
- */
- public function setType(UniqueFileIdType $type): self
- {
- $this->type = $type;
-
- return $this;
- }
-
- /**
- * Get file ID.
- *
- * @return int
- */
- public function getId()
- {
- return $this->id;
- }
-
- /**
- * Set file ID.
- *
- * @param int $id File ID.
- *
- */
- public function setId(int $id): self
- {
- $this->id = $id;
-
- return $this;
- }
-
- /**
- * Check if has ID.
- *
- * @return boolean
- */
- public function hasId(): bool
- {
- return isset($this->id);
- }
-
- /**
- * Get photo volume ID.
- *
- * @return int
- */
- public function getVolumeId()
- {
- return $this->volumeId;
- }
-
- /**
- * Set photo volume ID.
- *
- * @param int $volumeId Photo volume ID.
- *
- */
- public function setVolumeId(int $volumeId): self
- {
- $this->volumeId = $volumeId;
-
- return $this;
- }
- /**
- * Check if has volume ID.
- *
- * @return boolean
- */
- public function hasVolumeId(): bool
- {
- return isset($this->volumeId);
- }
-
- /**
- * Get photo local ID.
- *
- */
- public function getLocalId(): int
- {
- return $this->localId;
- }
-
- /**
- * Set photo local ID.
- *
- * @param int $localId Photo local ID.
- *
- */
- public function setLocalId(int $localId): self
- {
- $this->localId = $localId;
-
- return $this;
- }
-
- /**
- * Check if has local ID.
- *
- * @return boolean
- */
- public function hasLocalId(): bool
- {
- return isset($this->localId);
- }
-
- /**
- * Get weblocation URL.
- *
- */
- public function getUrl(): string
- {
- return $this->url;
- }
-
- /**
- * Set weblocation URL.
- *
- * @param string $url Weblocation URL
- *
- */
- public function setUrl(string $url): self
- {
- $this->url = $url;
-
- return $this;
- }
-
- /**
- * Check if has weblocation URL.
- *
- * @return boolean
- */
- public function hasUrl(): bool
- {
- return isset($this->url);
- }
-
- /**
- * Get photo subtype.
- *
- */
- public function getSubType(): int
- {
- return $this->subType;
- }
-
- /**
- * Has photo subtype?
- *
- */
- public function hasSubType(): bool
- {
- return isset($this->subType);
- }
-
- /**
- * Set photo subtype.
- *
- * @param int $subType Photo subtype
- *
- */
- public function setSubType(int $subType): self
- {
- $this->subType = $subType;
-
- return $this;
- }
-
- /**
- * Get sticker set ID.
- *
- * @return int
- */
- public function getStickerSetId()
- {
- return $this->stickerSetId;
- }
-
- /**
- * Has sticker set ID?
- *
- */
- public function hasStickerSetId(): bool
- {
- return isset($this->stickerSetId);
- }
-
- /**
- * Set sticker set ID.
- *
- * @param int $stickerSetId Sticker set ID
- *
- */
- public function setStickerSetId(int $stickerSetId): self
- {
- $this->stickerSetId = $stickerSetId;
-
- return $this;
- }
-
- /**
- * Get sticker set version.
- *
- */
- public function getStickerSetVersion(): int
- {
- return $this->stickerSetVersion;
- }
-
- /**
- * Has sticker set version.
- *
- */
- public function hasStickerSetVersion(): bool
- {
- return isset($this->stickerSetVersion);
- }
-
- /**
- * Set sticker set version.
- *
- * @param int $stickerSetVersion Sticker set version
- *
- */
- public function setStickerSetVersion(int $stickerSetVersion): self
- {
- $this->stickerSetVersion = $stickerSetVersion;
-
- return $this;
+ return new self($type, $fileId->id);
}
}
diff --git a/src/functions.php b/src/functions.php
index 97a1828..6faecf8 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -4,92 +4,45 @@ namespace danog\Decoder;
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);
/**
* Unpack long properly, returns an actual number in any case.
*
- * @param string $field Field to unpack
+ * @internal
*
- * @return string|int
+ * @param string $field Field to unpack
*/
-function unpackLong(string $field)
+function unpackLong(string $field): int
{
- if (PHP_INT_SIZE === 8) {
- /** @psalm-suppress InvalidGlobal */
- global $BIG_ENDIAN; // Evil
- return \unpack('q', $BIG_ENDIAN ? \strrev($field) : $field)[1];
- }
- if (\class_exists(\tgseclib\Math\BigInteger::class)) {
- return (string) new \tgseclib\Math\BigInteger(\strrev($field), -256);
- }
- if (\class_exists(\phpseclib\Math\BigInteger::class)) {
- return (string) new \phpseclib\Math\BigInteger(\strrev($field), -256);
- }
- throw new \Error('Please install phpseclib to unpack bot API file IDs');
+ global $BIG_ENDIAN; // Evil
+ /** @psalm-suppress MixedReturnStatement */
+ return \unpack('q', $BIG_ENDIAN ? \strrev($field) : $field)[1];
+}
+/**
+ * Unpack integer.
+ * @internal
+ *
+ * @param string $field Field to unpack
+ */
+function unpackInt(string $field): int
+{
+ /** @psalm-suppress MixedReturnStatement */
+ return \unpack('l', $field)[1];
}
/**
* Pack string long.
*
+ * @internal
*
*/
-function packLongBig(string|int $field): string
+function packLong(int $field): string
{
- if (PHP_INT_SIZE === 8) {
- /** @psalm-suppress InvalidGlobal */
- global $BIG_ENDIAN; // Evil
- $res = \pack('q', $field);
- return $BIG_ENDIAN ? \strrev($res) : $res;
- }
-
- if (\class_exists(\tgseclib\Math\BigInteger::class)) {
- return (new \tgseclib\Math\BigInteger($field))->toBytes();
- }
- if (\class_exists(\phpseclib\Math\BigInteger::class)) {
- return (new \phpseclib\Math\BigInteger($field))->toBytes();
- }
- throw new \Error('Please install phpseclib to unpack bot API file IDs');
+ global $BIG_ENDIAN; // Evil
+ $res = \pack('q', $field);
+ return $BIG_ENDIAN ? \strrev($res) : $res;
}
-/**
- * Fix long parameters in case of 32 bit systems.
- *
- * @param array $params Parameters
- * @param string $field 64-bit field
- *
- * @return void
- */
-function fixLong(array &$params, string $field)
-{
- if (PHP_INT_SIZE === 8) {
- return;
- }
- $params[$field] = [
- $params[$field.'1'],
- $params[$field.'2'],
- ];
- unset($params[$field.'1'], $params[$field.'2']);
-}
-
-/**
- * Encode long to string.
- *
- * @param string|int|int[] $fields Fields to encode
- *
- */
-function packLong(string|int|array $fields): string
-{
- if (\is_string($fields)) { // Already encoded, we hope
- return $fields;
- }
- if (PHP_INT_SIZE === 8) {
- return \pack(LONG, $fields);
- }
- return \pack(LONG, ...$fields);
-}
-
/**
* Base64URL decode.
*
@@ -195,7 +148,7 @@ function posmod(int $a, int $b): int
/**
* Read TL string.
*
- * @param mixed $stream Byte stream
+ * @param resource $stream Byte stream
*
* @internal
*
@@ -207,6 +160,7 @@ function readTLString(mixed $stream): string
throw new \InvalidArgumentException("Length too big!");
}
if ($l === 254) {
+ /** @var int */
$long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1];
$x = \stream_get_contents($stream, $long_len);
$resto = posmod(-$long_len, 4);
@@ -229,6 +183,7 @@ function readTLString(mixed $stream): string
*
* @param string $string String
*
+ * @internal
*/
function packTLString(string $string): string
{
@@ -246,186 +201,3 @@ function packTLString(string $string): string
}
return $concat;
}
-
-/**
- * Internal decode function.
- *
- * I know that you will use this directly giuseppe
- *
- * @param string $fileId Bot API file ID
- *
- * @internal
- *
- */
-function internalDecode(string $fileId): array
-{
- $orig = $fileId;
- $fileId = rleDecode(base64urlDecode($fileId));
- $result = [];
- $result['version'] = \ord($fileId[\strlen($fileId) - 1]);
- $result['subVersion'] = $result['version'] === 4 ? \ord($fileId[\strlen($fileId) - 2]) : 0;
-
- $result += \unpack('VtypeId/Vdc_id', $fileId);
- $result['hasReference'] = (bool) ($result['typeId'] & FILE_REFERENCE_FLAG);
- $result['hasWebLocation'] = (bool) ($result['typeId'] & WEB_LOCATION_FLAG);
- $result['typeId'] &= ~FILE_REFERENCE_FLAG;
- $result['typeId'] &= ~WEB_LOCATION_FLAG;
- $result['type'] = FileIdType::from($result['typeId']);
- $res = \fopen('php://memory', 'rw+b');
- \fwrite($res, \substr($fileId, 8));
- \fseek($res, 0);
- $fileId = $res;
-
- if ($result['hasReference']) {
- $result['fileReference'] = readTLString($fileId);
- }
- if ($result['hasWebLocation']) {
- $result['url'] = readTLString($fileId);
- $result['access_hash'] = \unpack(LONG.'access_hash', \stream_get_contents($fileId, 8));
- fixLong($result, 'access_hash');
- return $result;
- }
-
- $result += \unpack(LONG.'id/'.LONG.'access_hash', \stream_get_contents($fileId, 16));
- fixLong($result, 'id');
- fixLong($result, 'access_hash');
-
- 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 PhotoSizeSourceType::LEGACY:
- $result += \unpack(LONG.'secret', \stream_get_contents($fileId, 8));
- fixLong($result, 'secret');
- break;
- case PhotoSizeSourceType::THUMBNAIL:
- $result += \unpack('Vfile_type/athumbnail_type', \stream_get_contents($fileId, 8));
- break;
- 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 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 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 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');
-
- $result += \unpack(LONG.'volume_id/llocal_id', \stream_get_contents($fileId, 12));
- fixLong($result, 'volume_id');
- break;
- 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');
-
- $result += \unpack(LONG.'volume_id/llocal_id', \stream_get_contents($fileId, 12));
- fixLong($result, 'volume_id');
- break;
-
- 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');
- break;
- }
- };
- if ($result['subVersion'] >= 32) {
- $parsePhotoSize();
- } else {
- $result += \unpack(LONG.'volume_id', \stream_get_contents($fileId, 8));
- fixLong($result, 'volume_id');
-
- if ($result['subVersion'] >= 22) {
- $parsePhotoSize();
- $result += \unpack('llocal_id', \stream_get_contents($fileId, 4));
- } else {
- $result += \unpack(LONG.'secret/llocal_id', \stream_get_contents($fileId, 12));
- fixLong($result, 'volume_id');
- fixLong($result, 'secret');
- }
- }
- }
- $l = \fstat($fileId)['size'] - \ftell($fileId);
- $l -= $result['version'] >= 4 ? 2 : 1;
- if ($l > 0) {
- \trigger_error("File ID $orig has $l bytes of leftover data");
- }
- return $result;
-}
-/**
- * Internal decode function.
- *
- * I know that you will use this directly giuseppe
- *
- * @param string $fileId Bot API file ID
- *
- * @internal
- *
- */
-function internalDecodeUnique(string $fileId): array
-{
- $orig = $fileId;
- $fileId = rleDecode(base64urlDecode($fileId));
-
- $result = \unpack('VtypeId', $fileId);
- $result['type'] = UniqueFileIdType::from($result['typeId']);
-
- $fileId = \substr($fileId, 4);
- if ($result['typeId'] === UniqueFileIdType::WEB) {
- $res = \fopen('php://memory', 'rw+b');
- \fwrite($res, $fileId);
- \fseek($res, 0);
- $fileId = $res;
- $result['url'] = readTLString($fileId);
-
- $l = \fstat($fileId)['size'] - \ftell($fileId);
- } elseif (\strlen($fileId) === 12) {
- // Legacy photos
- $result += \unpack(LONG.'volume_id/llocal_id', $fileId);
- fixLong($result, 'volume_id');
-
- $l = 0;
- } elseif (\strlen($fileId) === 9) {
- // Dialog photos/thumbnails
- $result += \unpack(LONG.'id/CsubType', $fileId);
- fixLong($result, 'id');
-
- $l = 0;
- } elseif (\strlen($fileId) === 13) {
- // Stickerset ID/version
- $result += \unpack('CsubType/'.LONG.'sticker_set_id/lsticker_set_version', $fileId);
- fixLong($result, 'sticker_set_id');
-
- $l = 0;
- } elseif (\strlen($fileId) === 8) {
- // Any other document
- $result += \unpack(LONG.'id', $fileId);
- fixLong($result, 'id');
-
- $l = 0;
- } else {
- $l = \strlen($fileId);
- }
- if ($l > 0) {
- \trigger_error("Unique file ID $orig has $l bytes of leftover data");
- }
-
- \assert($result !== false);
- return $result;
-}