ApkAlignmentExtraField.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of the nelexa/zip package.
  5. * (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. namespace PhpZip\Model\Extra\Fields;
  10. use PhpZip\Exception\ZipException;
  11. use PhpZip\Model\Extra\ZipExtraField;
  12. use PhpZip\Model\ZipEntry;
  13. /**
  14. * Apk Alignment Extra Field.
  15. *
  16. * @see https://android.googlesource.com/platform/tools/apksig/+/master/src/main/java/com/android/apksig/ApkSigner.java
  17. * @see https://developer.android.com/studio/command-line/zipalign
  18. */
  19. final class ApkAlignmentExtraField implements ZipExtraField
  20. {
  21. /**
  22. * @var int Extensible data block/field header ID used for storing
  23. * information about alignment of uncompressed entries as
  24. * well as for aligning the entries's data. See ZIP
  25. * appnote.txt section 4.5 Extensible data fields.
  26. */
  27. public const HEADER_ID = 0xd935;
  28. /** @var int */
  29. public const ALIGNMENT_BYTES = 4;
  30. /** @var int */
  31. public const COMMON_PAGE_ALIGNMENT_BYTES = 4096;
  32. private int $multiple;
  33. private int $padding;
  34. public function __construct(int $multiple, int $padding)
  35. {
  36. $this->multiple = $multiple;
  37. $this->padding = $padding;
  38. }
  39. /**
  40. * Returns the Header ID (type) of this Extra Field.
  41. * The Header ID is an unsigned short integer (two bytes)
  42. * which must be constant during the life cycle of this object.
  43. */
  44. public function getHeaderId(): int
  45. {
  46. return self::HEADER_ID;
  47. }
  48. public function getMultiple(): int
  49. {
  50. return $this->multiple;
  51. }
  52. public function getPadding(): int
  53. {
  54. return $this->padding;
  55. }
  56. public function setMultiple(int $multiple): void
  57. {
  58. $this->multiple = $multiple;
  59. }
  60. public function setPadding(int $padding): void
  61. {
  62. $this->padding = $padding;
  63. }
  64. /**
  65. * Populate data from this array as if it was in local file data.
  66. *
  67. * @param string $buffer the buffer to read data from
  68. * @param ZipEntry|null $entry optional zip entry
  69. *
  70. * @throws ZipException
  71. *
  72. * @return ApkAlignmentExtraField
  73. */
  74. public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
  75. {
  76. $length = \strlen($buffer);
  77. if ($length < 2) {
  78. // This is APK alignment field.
  79. // FORMAT:
  80. // * uint16 alignment multiple (in bytes)
  81. // * remaining bytes -- padding to achieve alignment of data which starts after
  82. // the extra field
  83. throw new ZipException(
  84. 'Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.'
  85. );
  86. }
  87. $multiple = unpack('v', $buffer)[1];
  88. $padding = $length - 2;
  89. return new self($multiple, $padding);
  90. }
  91. /**
  92. * Populate data from this array as if it was in central directory data.
  93. *
  94. * @param string $buffer the buffer to read data from
  95. * @param ZipEntry|null $entry optional zip entry
  96. *
  97. * @throws ZipException on error
  98. *
  99. * @return ApkAlignmentExtraField
  100. */
  101. public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
  102. {
  103. return self::unpackLocalFileData($buffer, $entry);
  104. }
  105. /**
  106. * The actual data to put into local file data - without Header-ID
  107. * or length specifier.
  108. *
  109. * @return string the data
  110. */
  111. public function packLocalFileData(): string
  112. {
  113. return pack('vx' . $this->padding, $this->multiple);
  114. }
  115. /**
  116. * The actual data to put into central directory - without Header-ID or
  117. * length specifier.
  118. *
  119. * @return string the data
  120. */
  121. public function packCentralDirData(): string
  122. {
  123. return $this->packLocalFileData();
  124. }
  125. public function __toString(): string
  126. {
  127. return sprintf(
  128. '0x%04x APK Alignment: Multiple=%d Padding=%d',
  129. self::HEADER_ID,
  130. $this->multiple,
  131. $this->padding
  132. );
  133. }
  134. }