Completed
Push — master ( 3061fb...bc1b17 )
by Joas
20:34
created
lib/private/Profiler/FileProfilerStorage.php 2 patches
Indentation   +261 added lines, -261 removed lines patch added patch discarded remove patch
@@ -14,265 +14,265 @@
 block discarded – undo
14 14
  * Storage for profiler using files.
15 15
  */
16 16
 class FileProfilerStorage {
17
-	// Folder where profiler data are stored.
18
-	private string $folder;
19
-
20
-	/**
21
-	 * Constructs the file storage using a "dsn-like" path.
22
-	 *
23
-	 * Example : "file:/path/to/the/storage/folder"
24
-	 *
25
-	 * @throws \RuntimeException
26
-	 */
27
-	public function __construct(string $folder) {
28
-		$this->folder = $folder;
29
-
30
-		if (!is_dir($this->folder) && @mkdir($this->folder, 0777, true) === false && !is_dir($this->folder)) {
31
-			throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder));
32
-		}
33
-	}
34
-
35
-	public function find(?string $url, ?int $limit, ?string $method, ?int $start = null, ?int $end = null, ?string $statusCode = null): array {
36
-		$file = $this->getIndexFilename();
37
-
38
-		if (!file_exists($file)) {
39
-			return [];
40
-		}
41
-
42
-		$file = fopen($file, 'r');
43
-		fseek($file, 0, \SEEK_END);
44
-
45
-		$result = [];
46
-		while (\count($result) < $limit && $line = $this->readLineFromFile($file)) {
47
-			$values = str_getcsv($line);
48
-			[$csvToken, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode] = $values;
49
-			$csvTime = (int)$csvTime;
50
-
51
-			if (($url && !str_contains($csvUrl, $url))
52
-				|| ($method && !str_contains($csvMethod, $method))
53
-				|| ($statusCode && !str_contains($csvStatusCode, $statusCode))) {
54
-				continue;
55
-			}
56
-
57
-			if ($start !== null && $csvTime < $start) {
58
-				continue;
59
-			}
60
-
61
-			if ($end !== null && $csvTime > $end) {
62
-				continue;
63
-			}
64
-
65
-			$result[$csvToken] = [
66
-				'token' => $csvToken,
67
-				'method' => $csvMethod,
68
-				'url' => $csvUrl,
69
-				'time' => $csvTime,
70
-				'parent' => $csvParent,
71
-				'status_code' => $csvStatusCode,
72
-			];
73
-		}
74
-
75
-		fclose($file);
76
-
77
-		return array_values($result);
78
-	}
79
-
80
-	public function purge(): void {
81
-		$flags = \FilesystemIterator::SKIP_DOTS;
82
-		$iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
83
-		$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
84
-
85
-		foreach ($iterator as $file) {
86
-			$path = $file->getPathname();
87
-			if (is_file($path)) {
88
-				unlink($path);
89
-			} else {
90
-				rmdir($path);
91
-			}
92
-		}
93
-	}
94
-
95
-	public function read(string $token): ?IProfile {
96
-		if (!$token || !file_exists($file = $this->getFilename($token))) {
97
-			return null;
98
-		}
99
-
100
-		if (\function_exists('gzcompress')) {
101
-			$file = 'compress.zlib://' . $file;
102
-		}
103
-
104
-		return $this->createProfileFromData($token, unserialize(file_get_contents($file)));
105
-	}
106
-
107
-	/**
108
-	 * @throws \RuntimeException
109
-	 */
110
-	public function write(IProfile $profile): bool {
111
-		$file = $this->getFilename($profile->getToken());
112
-
113
-		$profileIndexed = is_file($file);
114
-		if (!$profileIndexed) {
115
-			// Create directory
116
-			$dir = \dirname($file);
117
-			if (!is_dir($dir) && @mkdir($dir, 0777, true) === false && !is_dir($dir)) {
118
-				throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir));
119
-			}
120
-		}
121
-
122
-		$profileToken = $profile->getToken();
123
-		// when there are errors in sub-requests, the parent and/or children tokens
124
-		// may equal the profile token, resulting in infinite loops
125
-		$parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null;
126
-		$childrenToken = array_filter(array_map(function (IProfile $p) use ($profileToken) {
127
-			return $profileToken !== $p->getToken() ? $p->getToken() : null;
128
-		}, $profile->getChildren()));
129
-
130
-		// Store profile
131
-		$data = [
132
-			'token' => $profileToken,
133
-			'parent' => $parentToken,
134
-			'children' => $childrenToken,
135
-			'data' => $profile->getCollectors(),
136
-			'method' => $profile->getMethod(),
137
-			'url' => $profile->getUrl(),
138
-			'time' => $profile->getTime(),
139
-			'status_code' => $profile->getStatusCode(),
140
-		];
141
-
142
-		$context = stream_context_create();
143
-
144
-		if (\function_exists('gzcompress')) {
145
-			$file = 'compress.zlib://' . $file;
146
-			stream_context_set_option($context, 'zlib', 'level', 3);
147
-		}
148
-
149
-		if (file_put_contents($file, serialize($data), 0, $context) === false) {
150
-			return false;
151
-		}
152
-
153
-		if (!$profileIndexed) {
154
-			// Add to index
155
-			if (false === $file = fopen($this->getIndexFilename(), 'a')) {
156
-				return false;
157
-			}
158
-
159
-			fputcsv($file, array_map([$this, 'escapeFormulae'], [
160
-				$profile->getToken(),
161
-				$profile->getMethod(),
162
-				$profile->getUrl(),
163
-				$profile->getTime(),
164
-				$profile->getParentToken(),
165
-				$profile->getStatusCode(),
166
-			]), escape: '');
167
-			fclose($file);
168
-		}
169
-
170
-		return true;
171
-	}
172
-
173
-	protected function escapeFormulae(?string $value): ?string {
174
-		if ($value !== null && preg_match('/^[=+\-@\t\r]/', $value)) {
175
-			return "'" . $value;
176
-		}
177
-		return $value;
178
-	}
179
-
180
-	/**
181
-	 * Gets filename to store data, associated to the token.
182
-	 *
183
-	 * @return string The profile filename
184
-	 */
185
-	protected function getFilename(string $token): string {
186
-		// Uses 4 last characters, because first are mostly the same.
187
-		$folderA = substr($token, -2, 2);
188
-		$folderB = substr($token, -4, 2);
189
-
190
-		return $this->folder . '/' . $folderA . '/' . $folderB . '/' . $token;
191
-	}
192
-
193
-	/**
194
-	 * Gets the index filename.
195
-	 *
196
-	 * @return string The index filename
197
-	 */
198
-	protected function getIndexFilename(): string {
199
-		return $this->folder . '/index.csv';
200
-	}
201
-
202
-	/**
203
-	 * Reads a line in the file, backward.
204
-	 *
205
-	 * This function automatically skips the empty lines and do not include the line return in result value.
206
-	 *
207
-	 * @param resource $file The file resource, with the pointer placed at the end of the line to read
208
-	 *
209
-	 * @return ?string A string representing the line or null if beginning of file is reached
210
-	 */
211
-	protected function readLineFromFile($file): ?string {
212
-		$line = '';
213
-		$position = ftell($file);
214
-
215
-		if ($position === 0) {
216
-			return null;
217
-		}
218
-
219
-		while (true) {
220
-			$chunkSize = min($position, 1024);
221
-			$position -= $chunkSize;
222
-			fseek($file, $position);
223
-
224
-			if ($chunkSize === 0) {
225
-				// bof reached
226
-				break;
227
-			}
228
-
229
-			$buffer = fread($file, $chunkSize);
230
-
231
-			if (false === ($upTo = strrpos($buffer, "\n"))) {
232
-				$line = $buffer . $line;
233
-				continue;
234
-			}
235
-
236
-			$position += $upTo;
237
-			$line = substr($buffer, $upTo + 1) . $line;
238
-			fseek($file, max(0, $position), \SEEK_SET);
239
-
240
-			if ($line !== '') {
241
-				break;
242
-			}
243
-		}
244
-
245
-		return $line === '' ? null : $line;
246
-	}
247
-
248
-	protected function createProfileFromData(string $token, array $data, ?IProfile $parent = null): IProfile {
249
-		$profile = new Profile($token);
250
-		$profile->setMethod($data['method']);
251
-		$profile->setUrl($data['url']);
252
-		$profile->setTime($data['time']);
253
-		$profile->setStatusCode($data['status_code']);
254
-		$profile->setCollectors($data['data']);
255
-
256
-		if (!$parent && $data['parent']) {
257
-			$parent = $this->read($data['parent']);
258
-		}
259
-
260
-		if ($parent) {
261
-			$profile->setParent($parent);
262
-		}
263
-
264
-		foreach ($data['children'] as $token) {
265
-			if (!$token || !file_exists($file = $this->getFilename($token))) {
266
-				continue;
267
-			}
268
-
269
-			if (\function_exists('gzcompress')) {
270
-				$file = 'compress.zlib://' . $file;
271
-			}
272
-
273
-			$profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
274
-		}
275
-
276
-		return $profile;
277
-	}
17
+    // Folder where profiler data are stored.
18
+    private string $folder;
19
+
20
+    /**
21
+     * Constructs the file storage using a "dsn-like" path.
22
+     *
23
+     * Example : "file:/path/to/the/storage/folder"
24
+     *
25
+     * @throws \RuntimeException
26
+     */
27
+    public function __construct(string $folder) {
28
+        $this->folder = $folder;
29
+
30
+        if (!is_dir($this->folder) && @mkdir($this->folder, 0777, true) === false && !is_dir($this->folder)) {
31
+            throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder));
32
+        }
33
+    }
34
+
35
+    public function find(?string $url, ?int $limit, ?string $method, ?int $start = null, ?int $end = null, ?string $statusCode = null): array {
36
+        $file = $this->getIndexFilename();
37
+
38
+        if (!file_exists($file)) {
39
+            return [];
40
+        }
41
+
42
+        $file = fopen($file, 'r');
43
+        fseek($file, 0, \SEEK_END);
44
+
45
+        $result = [];
46
+        while (\count($result) < $limit && $line = $this->readLineFromFile($file)) {
47
+            $values = str_getcsv($line);
48
+            [$csvToken, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode] = $values;
49
+            $csvTime = (int)$csvTime;
50
+
51
+            if (($url && !str_contains($csvUrl, $url))
52
+                || ($method && !str_contains($csvMethod, $method))
53
+                || ($statusCode && !str_contains($csvStatusCode, $statusCode))) {
54
+                continue;
55
+            }
56
+
57
+            if ($start !== null && $csvTime < $start) {
58
+                continue;
59
+            }
60
+
61
+            if ($end !== null && $csvTime > $end) {
62
+                continue;
63
+            }
64
+
65
+            $result[$csvToken] = [
66
+                'token' => $csvToken,
67
+                'method' => $csvMethod,
68
+                'url' => $csvUrl,
69
+                'time' => $csvTime,
70
+                'parent' => $csvParent,
71
+                'status_code' => $csvStatusCode,
72
+            ];
73
+        }
74
+
75
+        fclose($file);
76
+
77
+        return array_values($result);
78
+    }
79
+
80
+    public function purge(): void {
81
+        $flags = \FilesystemIterator::SKIP_DOTS;
82
+        $iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
83
+        $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
84
+
85
+        foreach ($iterator as $file) {
86
+            $path = $file->getPathname();
87
+            if (is_file($path)) {
88
+                unlink($path);
89
+            } else {
90
+                rmdir($path);
91
+            }
92
+        }
93
+    }
94
+
95
+    public function read(string $token): ?IProfile {
96
+        if (!$token || !file_exists($file = $this->getFilename($token))) {
97
+            return null;
98
+        }
99
+
100
+        if (\function_exists('gzcompress')) {
101
+            $file = 'compress.zlib://' . $file;
102
+        }
103
+
104
+        return $this->createProfileFromData($token, unserialize(file_get_contents($file)));
105
+    }
106
+
107
+    /**
108
+     * @throws \RuntimeException
109
+     */
110
+    public function write(IProfile $profile): bool {
111
+        $file = $this->getFilename($profile->getToken());
112
+
113
+        $profileIndexed = is_file($file);
114
+        if (!$profileIndexed) {
115
+            // Create directory
116
+            $dir = \dirname($file);
117
+            if (!is_dir($dir) && @mkdir($dir, 0777, true) === false && !is_dir($dir)) {
118
+                throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir));
119
+            }
120
+        }
121
+
122
+        $profileToken = $profile->getToken();
123
+        // when there are errors in sub-requests, the parent and/or children tokens
124
+        // may equal the profile token, resulting in infinite loops
125
+        $parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null;
126
+        $childrenToken = array_filter(array_map(function (IProfile $p) use ($profileToken) {
127
+            return $profileToken !== $p->getToken() ? $p->getToken() : null;
128
+        }, $profile->getChildren()));
129
+
130
+        // Store profile
131
+        $data = [
132
+            'token' => $profileToken,
133
+            'parent' => $parentToken,
134
+            'children' => $childrenToken,
135
+            'data' => $profile->getCollectors(),
136
+            'method' => $profile->getMethod(),
137
+            'url' => $profile->getUrl(),
138
+            'time' => $profile->getTime(),
139
+            'status_code' => $profile->getStatusCode(),
140
+        ];
141
+
142
+        $context = stream_context_create();
143
+
144
+        if (\function_exists('gzcompress')) {
145
+            $file = 'compress.zlib://' . $file;
146
+            stream_context_set_option($context, 'zlib', 'level', 3);
147
+        }
148
+
149
+        if (file_put_contents($file, serialize($data), 0, $context) === false) {
150
+            return false;
151
+        }
152
+
153
+        if (!$profileIndexed) {
154
+            // Add to index
155
+            if (false === $file = fopen($this->getIndexFilename(), 'a')) {
156
+                return false;
157
+            }
158
+
159
+            fputcsv($file, array_map([$this, 'escapeFormulae'], [
160
+                $profile->getToken(),
161
+                $profile->getMethod(),
162
+                $profile->getUrl(),
163
+                $profile->getTime(),
164
+                $profile->getParentToken(),
165
+                $profile->getStatusCode(),
166
+            ]), escape: '');
167
+            fclose($file);
168
+        }
169
+
170
+        return true;
171
+    }
172
+
173
+    protected function escapeFormulae(?string $value): ?string {
174
+        if ($value !== null && preg_match('/^[=+\-@\t\r]/', $value)) {
175
+            return "'" . $value;
176
+        }
177
+        return $value;
178
+    }
179
+
180
+    /**
181
+     * Gets filename to store data, associated to the token.
182
+     *
183
+     * @return string The profile filename
184
+     */
185
+    protected function getFilename(string $token): string {
186
+        // Uses 4 last characters, because first are mostly the same.
187
+        $folderA = substr($token, -2, 2);
188
+        $folderB = substr($token, -4, 2);
189
+
190
+        return $this->folder . '/' . $folderA . '/' . $folderB . '/' . $token;
191
+    }
192
+
193
+    /**
194
+     * Gets the index filename.
195
+     *
196
+     * @return string The index filename
197
+     */
198
+    protected function getIndexFilename(): string {
199
+        return $this->folder . '/index.csv';
200
+    }
201
+
202
+    /**
203
+     * Reads a line in the file, backward.
204
+     *
205
+     * This function automatically skips the empty lines and do not include the line return in result value.
206
+     *
207
+     * @param resource $file The file resource, with the pointer placed at the end of the line to read
208
+     *
209
+     * @return ?string A string representing the line or null if beginning of file is reached
210
+     */
211
+    protected function readLineFromFile($file): ?string {
212
+        $line = '';
213
+        $position = ftell($file);
214
+
215
+        if ($position === 0) {
216
+            return null;
217
+        }
218
+
219
+        while (true) {
220
+            $chunkSize = min($position, 1024);
221
+            $position -= $chunkSize;
222
+            fseek($file, $position);
223
+
224
+            if ($chunkSize === 0) {
225
+                // bof reached
226
+                break;
227
+            }
228
+
229
+            $buffer = fread($file, $chunkSize);
230
+
231
+            if (false === ($upTo = strrpos($buffer, "\n"))) {
232
+                $line = $buffer . $line;
233
+                continue;
234
+            }
235
+
236
+            $position += $upTo;
237
+            $line = substr($buffer, $upTo + 1) . $line;
238
+            fseek($file, max(0, $position), \SEEK_SET);
239
+
240
+            if ($line !== '') {
241
+                break;
242
+            }
243
+        }
244
+
245
+        return $line === '' ? null : $line;
246
+    }
247
+
248
+    protected function createProfileFromData(string $token, array $data, ?IProfile $parent = null): IProfile {
249
+        $profile = new Profile($token);
250
+        $profile->setMethod($data['method']);
251
+        $profile->setUrl($data['url']);
252
+        $profile->setTime($data['time']);
253
+        $profile->setStatusCode($data['status_code']);
254
+        $profile->setCollectors($data['data']);
255
+
256
+        if (!$parent && $data['parent']) {
257
+            $parent = $this->read($data['parent']);
258
+        }
259
+
260
+        if ($parent) {
261
+            $profile->setParent($parent);
262
+        }
263
+
264
+        foreach ($data['children'] as $token) {
265
+            if (!$token || !file_exists($file = $this->getFilename($token))) {
266
+                continue;
267
+            }
268
+
269
+            if (\function_exists('gzcompress')) {
270
+                $file = 'compress.zlib://' . $file;
271
+            }
272
+
273
+            $profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
274
+        }
275
+
276
+        return $profile;
277
+    }
278 278
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -1,6 +1,6 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 
3
-declare(strict_types = 1);
3
+declare(strict_types=1);
4 4
 /**
5 5
  * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
6 6
  * SPDX-License-Identifier: AGPL-3.0-or-later
@@ -46,7 +46,7 @@  discard block
 block discarded – undo
46 46
 		while (\count($result) < $limit && $line = $this->readLineFromFile($file)) {
47 47
 			$values = str_getcsv($line);
48 48
 			[$csvToken, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode] = $values;
49
-			$csvTime = (int)$csvTime;
49
+			$csvTime = (int) $csvTime;
50 50
 
51 51
 			if (($url && !str_contains($csvUrl, $url))
52 52
 				|| ($method && !str_contains($csvMethod, $method))
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 		}
99 99
 
100 100
 		if (\function_exists('gzcompress')) {
101
-			$file = 'compress.zlib://' . $file;
101
+			$file = 'compress.zlib://'.$file;
102 102
 		}
103 103
 
104 104
 		return $this->createProfileFromData($token, unserialize(file_get_contents($file)));
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
 		// when there are errors in sub-requests, the parent and/or children tokens
124 124
 		// may equal the profile token, resulting in infinite loops
125 125
 		$parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null;
126
-		$childrenToken = array_filter(array_map(function (IProfile $p) use ($profileToken) {
126
+		$childrenToken = array_filter(array_map(function(IProfile $p) use ($profileToken) {
127 127
 			return $profileToken !== $p->getToken() ? $p->getToken() : null;
128 128
 		}, $profile->getChildren()));
129 129
 
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
 		$context = stream_context_create();
143 143
 
144 144
 		if (\function_exists('gzcompress')) {
145
-			$file = 'compress.zlib://' . $file;
145
+			$file = 'compress.zlib://'.$file;
146 146
 			stream_context_set_option($context, 'zlib', 'level', 3);
147 147
 		}
148 148
 
@@ -172,7 +172,7 @@  discard block
 block discarded – undo
172 172
 
173 173
 	protected function escapeFormulae(?string $value): ?string {
174 174
 		if ($value !== null && preg_match('/^[=+\-@\t\r]/', $value)) {
175
-			return "'" . $value;
175
+			return "'".$value;
176 176
 		}
177 177
 		return $value;
178 178
 	}
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
 		$folderA = substr($token, -2, 2);
188 188
 		$folderB = substr($token, -4, 2);
189 189
 
190
-		return $this->folder . '/' . $folderA . '/' . $folderB . '/' . $token;
190
+		return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token;
191 191
 	}
192 192
 
193 193
 	/**
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 	 * @return string The index filename
197 197
 	 */
198 198
 	protected function getIndexFilename(): string {
199
-		return $this->folder . '/index.csv';
199
+		return $this->folder.'/index.csv';
200 200
 	}
201 201
 
202 202
 	/**
@@ -229,12 +229,12 @@  discard block
 block discarded – undo
229 229
 			$buffer = fread($file, $chunkSize);
230 230
 
231 231
 			if (false === ($upTo = strrpos($buffer, "\n"))) {
232
-				$line = $buffer . $line;
232
+				$line = $buffer.$line;
233 233
 				continue;
234 234
 			}
235 235
 
236 236
 			$position += $upTo;
237
-			$line = substr($buffer, $upTo + 1) . $line;
237
+			$line = substr($buffer, $upTo + 1).$line;
238 238
 			fseek($file, max(0, $position), \SEEK_SET);
239 239
 
240 240
 			if ($line !== '') {
@@ -267,7 +267,7 @@  discard block
 block discarded – undo
267 267
 			}
268 268
 
269 269
 			if (\function_exists('gzcompress')) {
270
-				$file = 'compress.zlib://' . $file;
270
+				$file = 'compress.zlib://'.$file;
271 271
 			}
272 272
 
273 273
 			$profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
Please login to merge, or discard this patch.