/src/LibtorrentPHP/File/File.php

https://gitlab.com/hwmack/LibtorrentPHP · PHP · 201 lines · 95 code · 26 blank · 80 comment · 8 complexity · f7c48f6e3b448fa1d28bdf600fc074a5 MD5 · raw file

  1. <?php
  2. namespace CWE\Libraries\LibtorrentPHP\File;
  3. use CWE\Libraries\LibtorrentPHP\File\Error\UnreadableError;
  4. use CWE\Libraries\LibtorrentPHP\File\Error\NotExistsError;
  5. /**
  6. * Object to process to a physical file in the file system in regards og
  7. * torrent file generation and serving.
  8. *
  9. * @package PHPTracker
  10. * @subpackage File
  11. */
  12. class File
  13. {
  14. /**
  15. * Full path of the file on the disk.
  16. *
  17. * @var string
  18. */
  19. private $path;
  20. /**
  21. * If the file os opened for reading, this contains its read handle.
  22. *
  23. * @var resource
  24. */
  25. private $read_handle;
  26. /**
  27. * Initializing the object with the file full path.
  28. *
  29. * @throws NotExitsError If the file does not exists.
  30. * @param string $path
  31. */
  32. public function __construct( $path )
  33. {
  34. $this->path = (string) $path;
  35. $this->shouldExist();
  36. $this->path = realpath( $this->path );
  37. }
  38. /**
  39. * Return the file full path is the object is used as string.
  40. *
  41. * @return string
  42. */
  43. public function __toString()
  44. {
  45. return $this->path;
  46. }
  47. /**
  48. * If the file is open for reading, it's properly closed while destructing.
  49. */
  50. public function __destruct()
  51. {
  52. if ( isset( $this->read_handle ) )
  53. {
  54. fclose( $this->read_handle );
  55. }
  56. }
  57. /**
  58. * Tells if the file exists.
  59. *
  60. * @return boolean
  61. */
  62. private function exists()
  63. {
  64. return file_exists( $this->path );
  65. }
  66. /**
  67. * Tells the size of the file in bytes.
  68. *
  69. * @return integer
  70. */
  71. public function size()
  72. {
  73. if ( false === ( $size = @filesize( $this->path ) ) )
  74. {
  75. throw new UnreadableError( "File $this is unreadable." );
  76. }
  77. return $size;
  78. }
  79. /**
  80. * Returns the basename of the file.
  81. *
  82. * @return string
  83. */
  84. public function basename()
  85. {
  86. return basename( $this->path );
  87. }
  88. /**
  89. * Generates SHA1 hashes of each piece of the file.
  90. *
  91. * @param integer $size_piece Size of one piece of a file on bytes.
  92. * @return string Byte string of the concatenated SHA1 hashes of each pieces.
  93. */
  94. public function getHashesForPieces( $size_piece )
  95. {
  96. $size_piece = intval( $size_piece );
  97. if ( $size_piece <= 0 )
  98. {
  99. // TODO: Throwing exception?
  100. return null;
  101. }
  102. $c_pieces = ceil( $this->size() / $size_piece );
  103. $hashes = '';
  104. for ( $n_piece = 0; $n_piece < $c_pieces; ++$n_piece )
  105. {
  106. $hashes .= $this->hashPiece( $n_piece, $size_piece );
  107. }
  108. return $hashes;
  109. }
  110. /**
  111. * Reads one arbitrary length chunk of a file beginning from a byte index.
  112. *
  113. * @param integer $begin Where to start reading (bytes).
  114. * @param integer $length How many bytes to read.
  115. * @return string Binary string with the read data.
  116. */
  117. public function readBlock( $begin, $length )
  118. {
  119. $file_handle = $this->getReadHandle();
  120. fseek( $file_handle, $begin );
  121. if ( false === $buffer = @fread( $file_handle, $length ) )
  122. {
  123. throw new UnreadableError( "File $this is unreadable." );
  124. }
  125. // TODO: Check if we could read enough data.
  126. return $buffer;
  127. }
  128. /**
  129. * Lazy-opens a file for reading and returns its resource.
  130. *
  131. * @throws UnreadableError If the file can't be read.
  132. * @return resource
  133. */
  134. private function getReadHandle()
  135. {
  136. if ( !isset( $this->read_handle ) )
  137. {
  138. $this->read_handle = @fopen( $this->path, 'rb' );
  139. if ( false === $this->read_handle )
  140. {
  141. unset( $this->read_handle );
  142. throw new UnreadableError( "File $this is unreadable." );
  143. }
  144. }
  145. return $this->read_handle;
  146. }
  147. /**
  148. * Gets SHA1 hash of a piece of a file in raw format.
  149. *
  150. * @param integer $n_piece 0 bases index of the current peice.
  151. * @param integer $size_piece Generic piece size of the file in bytes.
  152. * @return string Byte string of the SHA1 hash of this piece.
  153. */
  154. private function hashPiece( $n_piece, $size_piece )
  155. {
  156. $file_handle = $this->getReadHandle();
  157. $hash_handle = hash_init( 'sha1' );
  158. fseek( $file_handle, $n_piece * $size_piece );
  159. hash_update_stream( $hash_handle, $file_handle, $size_piece );
  160. // Getting hash of the piece as raw binary.
  161. return hash_final( $hash_handle, true );
  162. }
  163. /**
  164. * Throws exception if the file does not exist.
  165. *
  166. * @throws NotExitsError
  167. */
  168. private function shouldExist()
  169. {
  170. if ( !$this->exists() )
  171. {
  172. throw new NotExistsError( "File $this does not exist." );
  173. }
  174. }
  175. }