HashWrapper.php 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Files\Stream;
  8. use Icewind\Streams\Wrapper;
  9. class HashWrapper extends Wrapper {
  10. protected $callback;
  11. protected $hash;
  12. public static function wrap($source, string $algo, callable $callback) {
  13. $hash = hash_init($algo);
  14. $context = stream_context_create([
  15. 'hash' => [
  16. 'source' => $source,
  17. 'callback' => $callback,
  18. 'hash' => $hash,
  19. ],
  20. ]);
  21. return Wrapper::wrapSource($source, $context, 'hash', self::class);
  22. }
  23. protected function open() {
  24. $context = $this->loadContext('hash');
  25. $this->callback = $context['callback'];
  26. $this->hash = $context['hash'];
  27. return true;
  28. }
  29. public function dir_opendir($path, $options) {
  30. return $this->open();
  31. }
  32. public function stream_open($path, $mode, $options, &$opened_path) {
  33. return $this->open();
  34. }
  35. public function stream_read($count) {
  36. $result = parent::stream_read($count);
  37. hash_update($this->hash, $result);
  38. return $result;
  39. }
  40. public function stream_close() {
  41. if (is_callable($this->callback)) {
  42. // if the stream is closed as a result of the end-of-request GC, the hash context might be cleaned up before this stream
  43. if ($this->hash instanceof \HashContext) {
  44. try {
  45. $hash = @hash_final($this->hash);
  46. if ($hash) {
  47. call_user_func($this->callback, $hash);
  48. }
  49. } catch (\Throwable $e) {
  50. }
  51. }
  52. // prevent further calls by potential PHP 7 GC ghosts
  53. $this->callback = null;
  54. }
  55. return parent::stream_close();
  56. }
  57. }