comparison vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\HttpKernel\Profiler;
13
14 /**
15 * Storage for profiler using files.
16 *
17 * @author Alexandre Salomé <alexandre.salome@gmail.com>
18 */
19 class FileProfilerStorage implements ProfilerStorageInterface
20 {
21 /**
22 * Folder where profiler data are stored.
23 *
24 * @var string
25 */
26 private $folder;
27
28 /**
29 * Constructs the file storage using a "dsn-like" path.
30 *
31 * Example : "file:/path/to/the/storage/folder"
32 *
33 * @param string $dsn The DSN
34 *
35 * @throws \RuntimeException
36 */
37 public function __construct($dsn)
38 {
39 if (0 !== strpos($dsn, 'file:')) {
40 throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use FileStorage with an invalid dsn "%s". The expected format is "file:/path/to/the/storage/folder".', $dsn));
41 }
42 $this->folder = substr($dsn, 5);
43
44 if (!is_dir($this->folder) && false === @mkdir($this->folder, 0777, true) && !is_dir($this->folder)) {
45 throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder));
46 }
47 }
48
49 /**
50 * {@inheritdoc}
51 */
52 public function find($ip, $url, $limit, $method, $start = null, $end = null, $statusCode = null)
53 {
54 $file = $this->getIndexFilename();
55
56 if (!file_exists($file)) {
57 return array();
58 }
59
60 $file = fopen($file, 'r');
61 fseek($file, 0, SEEK_END);
62
63 $result = array();
64 while (count($result) < $limit && $line = $this->readLineFromFile($file)) {
65 $values = str_getcsv($line);
66 list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode) = $values;
67 $csvTime = (int) $csvTime;
68
69 if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method) || $statusCode && false === strpos($csvStatusCode, $statusCode)) {
70 continue;
71 }
72
73 if (!empty($start) && $csvTime < $start) {
74 continue;
75 }
76
77 if (!empty($end) && $csvTime > $end) {
78 continue;
79 }
80
81 $result[$csvToken] = array(
82 'token' => $csvToken,
83 'ip' => $csvIp,
84 'method' => $csvMethod,
85 'url' => $csvUrl,
86 'time' => $csvTime,
87 'parent' => $csvParent,
88 'status_code' => $csvStatusCode,
89 );
90 }
91
92 fclose($file);
93
94 return array_values($result);
95 }
96
97 /**
98 * {@inheritdoc}
99 */
100 public function purge()
101 {
102 $flags = \FilesystemIterator::SKIP_DOTS;
103 $iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
104 $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
105
106 foreach ($iterator as $file) {
107 if (is_file($file)) {
108 unlink($file);
109 } else {
110 rmdir($file);
111 }
112 }
113 }
114
115 /**
116 * {@inheritdoc}
117 */
118 public function read($token)
119 {
120 if (!$token || !file_exists($file = $this->getFilename($token))) {
121 return;
122 }
123
124 return $this->createProfileFromData($token, unserialize(file_get_contents($file)));
125 }
126
127 /**
128 * {@inheritdoc}
129 *
130 * @throws \RuntimeException
131 */
132 public function write(Profile $profile)
133 {
134 $file = $this->getFilename($profile->getToken());
135
136 $profileIndexed = is_file($file);
137 if (!$profileIndexed) {
138 // Create directory
139 $dir = dirname($file);
140 if (!is_dir($dir) && false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
141 throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir));
142 }
143 }
144
145 // Store profile
146 $data = array(
147 'token' => $profile->getToken(),
148 'parent' => $profile->getParentToken(),
149 'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()),
150 'data' => $profile->getCollectors(),
151 'ip' => $profile->getIp(),
152 'method' => $profile->getMethod(),
153 'url' => $profile->getUrl(),
154 'time' => $profile->getTime(),
155 'status_code' => $profile->getStatusCode(),
156 );
157
158 if (false === file_put_contents($file, serialize($data))) {
159 return false;
160 }
161
162 if (!$profileIndexed) {
163 // Add to index
164 if (false === $file = fopen($this->getIndexFilename(), 'a')) {
165 return false;
166 }
167
168 fputcsv($file, array(
169 $profile->getToken(),
170 $profile->getIp(),
171 $profile->getMethod(),
172 $profile->getUrl(),
173 $profile->getTime(),
174 $profile->getParentToken(),
175 $profile->getStatusCode(),
176 ));
177 fclose($file);
178 }
179
180 return true;
181 }
182
183 /**
184 * Gets filename to store data, associated to the token.
185 *
186 * @param string $token
187 *
188 * @return string The profile filename
189 */
190 protected function getFilename($token)
191 {
192 // Uses 4 last characters, because first are mostly the same.
193 $folderA = substr($token, -2, 2);
194 $folderB = substr($token, -4, 2);
195
196 return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token;
197 }
198
199 /**
200 * Gets the index filename.
201 *
202 * @return string The index filename
203 */
204 protected function getIndexFilename()
205 {
206 return $this->folder.'/index.csv';
207 }
208
209 /**
210 * Reads a line in the file, backward.
211 *
212 * This function automatically skips the empty lines and do not include the line return in result value.
213 *
214 * @param resource $file The file resource, with the pointer placed at the end of the line to read
215 *
216 * @return mixed A string representing the line or null if beginning of file is reached
217 */
218 protected function readLineFromFile($file)
219 {
220 $line = '';
221 $position = ftell($file);
222
223 if (0 === $position) {
224 return;
225 }
226
227 while (true) {
228 $chunkSize = min($position, 1024);
229 $position -= $chunkSize;
230 fseek($file, $position);
231
232 if (0 === $chunkSize) {
233 // bof reached
234 break;
235 }
236
237 $buffer = fread($file, $chunkSize);
238
239 if (false === ($upTo = strrpos($buffer, "\n"))) {
240 $line = $buffer.$line;
241 continue;
242 }
243
244 $position += $upTo;
245 $line = substr($buffer, $upTo + 1).$line;
246 fseek($file, max(0, $position), SEEK_SET);
247
248 if ('' !== $line) {
249 break;
250 }
251 }
252
253 return '' === $line ? null : $line;
254 }
255
256 protected function createProfileFromData($token, $data, $parent = null)
257 {
258 $profile = new Profile($token);
259 $profile->setIp($data['ip']);
260 $profile->setMethod($data['method']);
261 $profile->setUrl($data['url']);
262 $profile->setTime($data['time']);
263 $profile->setStatusCode($data['status_code']);
264 $profile->setCollectors($data['data']);
265
266 if (!$parent && $data['parent']) {
267 $parent = $this->read($data['parent']);
268 }
269
270 if ($parent) {
271 $profile->setParent($parent);
272 }
273
274 foreach ($data['children'] as $token) {
275 if (!$token || !file_exists($file = $this->getFilename($token))) {
276 continue;
277 }
278
279 $profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
280 }
281
282 return $profile;
283 }
284 }