Просмотр файла vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php

Размер файла: 7.99Kb
  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. }