annotate vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php @ 2:92f882872392

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