1<?php
2/**
3 * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
4 *
5 * @license GNU AGPL version 3 or any later version
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22namespace Icewind\Streams;
23
24/**
25 * Wrapper that counts the amount of data read and written
26 *
27 * The following options should be passed in the context when opening the stream
28 * [
29 *     'callback' => [
30 *        'source'  => resource
31 *        'callback'    => function($readCount, $writeCount){}
32 *     ]
33 * ]
34 *
35 * The callback will be called when the stream is closed
36 */
37class CountWrapper extends Wrapper {
38	/**
39	 * @var int
40	 */
41	protected $readCount = 0;
42
43	/**
44	 * @var int
45	 */
46	protected $writeCount = 0;
47
48	/**
49	 * @var callable
50	 */
51	protected $callback;
52
53	/**
54	 * Wraps a stream with the provided callbacks
55	 *
56	 * @param resource $source
57	 * @param callable $callback
58	 * @return resource|bool
59	 *
60	 * @throws \BadMethodCallException
61	 */
62	public static function wrap($source, $callback) {
63		if (!is_callable($callback)) {
64			throw new \InvalidArgumentException('Invalid or missing callback');
65		}
66		return self::wrapSource($source, [
67			'source'   => $source,
68			'callback' => $callback
69		]);
70	}
71
72	protected function open() {
73		$context = $this->loadContext();
74		$this->callback = $context['callback'];
75		return true;
76	}
77
78	public function dir_opendir($path, $options) {
79		return $this->open();
80	}
81
82	public function stream_open($path, $mode, $options, &$opened_path) {
83		return $this->open();
84	}
85
86	public function stream_read($count) {
87		$result = parent::stream_read($count);
88		$this->readCount += strlen($result);
89		return $result;
90	}
91
92	public function stream_write($data) {
93		$result = parent::stream_write($data);
94		$this->writeCount += strlen($data);
95		return $result;
96	}
97
98	public function stream_close() {
99		$result = parent::stream_close();
100		call_user_func($this->callback, $this->readCount, $this->writeCount);
101		return $result;
102	}
103}
104