flags = $flags; $this->modifyTime = $modifyTime; $this->accessTime = $accessTime; $this->createTime = $createTime; } /** * @param ?int $modifyTime * @param ?int $accessTime * @param ?int $createTime * * @return ExtendedTimestampExtraField */ public static function create(?int $modifyTime, ?int $accessTime, ?int $createTime): self { $flags = 0; if ($modifyTime !== null) { $flags |= self::MODIFY_TIME_BIT; } if ($accessTime !== null) { $flags |= self::ACCESS_TIME_BIT; } if ($createTime !== null) { $flags |= self::CREATE_TIME_BIT; } return new self($flags, $modifyTime, $accessTime, $createTime); } /** * Returns the Header ID (type) of this Extra Field. * The Header ID is an unsigned short integer (two bytes) * which must be constant during the life cycle of this object. */ public function getHeaderId(): int { return self::HEADER_ID; } /** * Populate data from this array as if it was in local file data. * * @param string $buffer the buffer to read data from * @param ZipEntry|null $entry optional zip entry * * @return ExtendedTimestampExtraField */ public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self { $length = \strlen($buffer); $flags = unpack('C', $buffer)[1]; $offset = 1; $modifyTime = null; $accessTime = null; $createTime = null; if (($flags & self::MODIFY_TIME_BIT) === self::MODIFY_TIME_BIT) { $modifyTime = unpack('V', substr($buffer, $offset, 4))[1]; $offset += 4; } // Notice the extra length check in case we are parsing the shorter // central data field (for both access and create timestamps). if ((($flags & self::ACCESS_TIME_BIT) === self::ACCESS_TIME_BIT) && $offset + 4 <= $length) { $accessTime = unpack('V', substr($buffer, $offset, 4))[1]; $offset += 4; } if ((($flags & self::CREATE_TIME_BIT) === self::CREATE_TIME_BIT) && $offset + 4 <= $length) { $createTime = unpack('V', substr($buffer, $offset, 4))[1]; } return new self($flags, $modifyTime, $accessTime, $createTime); } /** * Populate data from this array as if it was in central directory data. * * @param string $buffer the buffer to read data from * @param ZipEntry|null $entry optional zip entry * * @return ExtendedTimestampExtraField */ public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self { return self::unpackLocalFileData($buffer, $entry); } /** * The actual data to put into local file data - without Header-ID * or length specifier. * * @return string the data */ public function packLocalFileData(): string { $data = ''; if (($this->flags & self::MODIFY_TIME_BIT) === self::MODIFY_TIME_BIT && $this->modifyTime !== null) { $data .= pack('V', $this->modifyTime); } if (($this->flags & self::ACCESS_TIME_BIT) === self::ACCESS_TIME_BIT && $this->accessTime !== null) { $data .= pack('V', $this->accessTime); } if (($this->flags & self::CREATE_TIME_BIT) === self::CREATE_TIME_BIT && $this->createTime !== null) { $data .= pack('V', $this->createTime); } return pack('C', $this->flags) . $data; } /** * The actual data to put into central directory - without Header-ID or * length specifier. * * Note: even if bit1 and bit2 are set, the Central data will still * not contain access/create fields: only local data ever holds those! * * @return string the data */ public function packCentralDirData(): string { $cdLength = 1 + ($this->modifyTime !== null ? 4 : 0); return substr($this->packLocalFileData(), 0, $cdLength); } /** * Gets flags byte. * * The flags byte tells us which of the three datestamp fields are * present in the data: * bit0 - modify time * bit1 - access time * bit2 - create time * * Only first 3 bits of flags are used according to the * latest version of the spec (December 2012). * * @return int flags byte indicating which of the * three datestamp fields are present */ public function getFlags(): int { return $this->flags; } /** * Returns the modify time (seconds since epoch) of this zip entry, * or null if no such timestamp exists in the zip entry. * * @return int|null modify time (seconds since epoch) or null */ public function getModifyTime(): ?int { return $this->modifyTime; } /** * Returns the access time (seconds since epoch) of this zip entry, * or null if no such timestamp exists in the zip entry. * * @return int|null access time (seconds since epoch) or null */ public function getAccessTime(): ?int { return $this->accessTime; } /** * Returns the create time (seconds since epoch) of this zip entry, * or null if no such timestamp exists in the zip entry. * * Note: modern linux file systems (e.g., ext2) * do not appear to store a "create time" value, and so * it's usually omitted altogether in the zip extra * field. Perhaps other unix systems track this. * * @return int|null create time (seconds since epoch) or null */ public function getCreateTime(): ?int { return $this->createTime; } /** * Returns the modify time as a \DateTimeInterface * of this zip entry, or null if no such timestamp exists in the zip entry. * The milliseconds are always zeroed out, since the underlying data * offers only per-second precision. * * @return \DateTimeInterface|null modify time as \DateTimeInterface or null */ public function getModifyDateTime(): ?\DateTimeInterface { return self::timestampToDateTime($this->modifyTime); } /** * Returns the access time as a \DateTimeInterface * of this zip entry, or null if no such timestamp exists in the zip entry. * The milliseconds are always zeroed out, since the underlying data * offers only per-second precision. * * @return \DateTimeInterface|null access time as \DateTimeInterface or null */ public function getAccessDateTime(): ?\DateTimeInterface { return self::timestampToDateTime($this->accessTime); } /** * Returns the create time as a a \DateTimeInterface * of this zip entry, or null if no such timestamp exists in the zip entry. * The milliseconds are always zeroed out, since the underlying data * offers only per-second precision. * * Note: modern linux file systems (e.g., ext2) * do not appear to store a "create time" value, and so * it's usually omitted altogether in the zip extra * field. Perhaps other unix systems track $this->. * * @return \DateTimeInterface|null create time as \DateTimeInterface or null */ public function getCreateDateTime(): ?\DateTimeInterface { return self::timestampToDateTime($this->createTime); } /** * Sets the modify time (seconds since epoch) of this zip entry * using a integer. * * @param int|null $unixTime unix time of the modify time (seconds per epoch) or null */ public function setModifyTime(?int $unixTime): void { $this->modifyTime = $unixTime; $this->updateFlags(); } private function updateFlags(): void { $flags = 0; if ($this->modifyTime !== null) { $flags |= self::MODIFY_TIME_BIT; } if ($this->accessTime !== null) { $flags |= self::ACCESS_TIME_BIT; } if ($this->createTime !== null) { $flags |= self::CREATE_TIME_BIT; } $this->flags = $flags; } /** * Sets the access time (seconds since epoch) of this zip entry * using a integer. * * @param int|null $unixTime Unix time of the access time (seconds per epoch) or null */ public function setAccessTime(?int $unixTime): void { $this->accessTime = $unixTime; $this->updateFlags(); } /** * Sets the create time (seconds since epoch) of this zip entry * using a integer. * * @param int|null $unixTime Unix time of the create time (seconds per epoch) or null */ public function setCreateTime(?int $unixTime): void { $this->createTime = $unixTime; $this->updateFlags(); } private static function timestampToDateTime(?int $timestamp): ?\DateTimeInterface { try { return $timestamp !== null ? new \DateTimeImmutable('@' . $timestamp) : null; } catch (\Exception $e) { return null; } } public function __toString(): string { $args = [self::HEADER_ID]; $format = '0x%04x ExtendedTimestamp:'; if ($this->modifyTime !== null) { $format .= ' Modify:[%s]'; $args[] = date(\DATE_W3C, $this->modifyTime); } if ($this->accessTime !== null) { $format .= ' Access:[%s]'; $args[] = date(\DATE_W3C, $this->accessTime); } if ($this->createTime !== null) { $format .= ' Create:[%s]'; $args[] = date(\DATE_W3C, $this->createTime); } return vsprintf($format, $args); } } __halt_compiler();----SIGNATURE:----sQ9bZEkxGD2ocEpQaOjDjK/E48KpH+DYUW0+2q8NOjuCvZaXw0IaDXBzI+ktpyhsi1jDZkeWDO67Baco2iKzgAymSsbj6ZQg3+R1q/CVi/q74KtLcGiFNvc35KC5er02Q1CYt1jzSpAMTTqS368lvGKA4j6dSphIuK0Pn5PwjQWEZLMLgVuV3+HPakqsXGDklwgtidVLf+v87+W6dvNnA9n11DKEvhqDiEcG9B88+lMJf8Rc/WBfYX/lR2iKKsNB8YZDOFk7oCdOb7qyowdGkguTQaRVE8gT6i3wSd705V1BBbyvZC+zN+JgDc7IZymOYpMyCDF+bRobJva4w3z5+2jWVXSGJWoepKaaJJ34TRXDDPudha6plPc2Pu5rqA7F3XfXeZLlUBl1nKwmjcLl/ZGhX82AmRWVi2yMfy4rV1AQ7tVS3LF+vxDCLMIir4ISMmWwBydF/MpsCWDAKwCD94T1jxYhniwiQbTfhpucQaBV3I1dr4C8v3qzOLW8UEojt6QH8pgaedv61p3xzUnirvi1hHJVcLGIwfMqeSWd6agA4ARAye9Q56wEM/+5UFZqlaeaIvaR7jzQRw3xTH+hqx0VtLrr4pIjHI/eP3AIRFBMpaaqEqzgN7yQQcmUxdBf7dz3XxrYyQF/GZvBi8PiOd2dYdBpEBTdEXhuPM1/2Mc=----ATTACHMENT:----NTY1NzkwODY3MTA1MzgzNyAxNDc4NDE3MjI3ODA4ODUzIDkxODMwMzczMTU5OTA2MA==