PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php

https://github.com/outlawscumbag/symfony
PHP | 223 lines | 123 code | 38 blank | 62 comment | 18 complexity | 7a85f29541a1083e7f844614dea15a15 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\Profiler;
  11. /**
  12. * Storage for profiler using files.
  13. *
  14. * @author Alexandre Salomé <alexandre.salome@gmail.com>
  15. */
  16. class FileProfilerStorage implements ProfilerStorageInterface
  17. {
  18. /**
  19. * Folder where profiler data are stored.
  20. *
  21. * @var string
  22. */
  23. private $folder;
  24. /**
  25. * Constructs the file storage using a "dsn-like" path.
  26. *
  27. * Example : "file:/path/to/the/storage/folder"
  28. *
  29. * @param string $dsn The DSN
  30. */
  31. public function __construct($dsn)
  32. {
  33. if (0 !== strpos($dsn, 'file:')) {
  34. throw new \InvalidArgumentException("FileStorage DSN must start with file:");
  35. }
  36. $this->folder = substr($dsn, 5);
  37. if (!is_dir($this->folder)) {
  38. mkdir($this->folder);
  39. }
  40. }
  41. /**
  42. * {@inheritdoc}
  43. */
  44. public function find($ip, $url, $limit)
  45. {
  46. $file = $this->getIndexFilename();
  47. if (!file_exists($file)) {
  48. return array();
  49. }
  50. $file = fopen($file, 'r');
  51. fseek($file, 0, SEEK_END);
  52. $result = array();
  53. while ($limit > 0) {
  54. $line = $this->readLineFromFile($file);
  55. if (false === $line) {
  56. break;
  57. }
  58. if ($line === "") {
  59. continue;
  60. }
  61. list($csvToken, $csvIp, $csvUrl, $csvTime, $csvParent) = str_getcsv($line);
  62. if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url)) {
  63. continue;
  64. }
  65. $row = array(
  66. 'token' => $csvToken,
  67. 'ip' => $csvIp,
  68. 'url' => $csvUrl,
  69. 'time' => $csvTime,
  70. 'parent' => $csvParent
  71. );
  72. $result[] = $row;
  73. $limit--;
  74. }
  75. fclose($file);
  76. return $result;
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. public function purge()
  82. {
  83. $flags = \FilesystemIterator::SKIP_DOTS;
  84. $iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
  85. $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
  86. foreach ($iterator as $file) {
  87. if (is_file($file)) {
  88. unlink($file);
  89. } else {
  90. rmdir($file);
  91. }
  92. }
  93. }
  94. /**
  95. * {@inheritdoc}
  96. */
  97. public function read($token)
  98. {
  99. $file = $this->getFilename($token);
  100. if (!file_exists($file)) {
  101. return null;
  102. }
  103. return unserialize(file_get_contents($file));
  104. }
  105. /**
  106. * {@inheritdoc}
  107. */
  108. public function write(Profile $profile)
  109. {
  110. $file = $this->getFilename($profile->getToken());
  111. $exists = file_exists($file);
  112. // Create directory
  113. $dir = dirname($file);
  114. if (!is_dir($dir)) {
  115. mkdir($dir, 0777, true);
  116. }
  117. // Store profile
  118. file_put_contents($file, serialize($profile));
  119. // Add to index
  120. $file = fopen($this->getIndexFilename(), 'a');
  121. fputcsv($file, array(
  122. $profile->getToken(),
  123. $profile->getIp(),
  124. $profile->getUrl(),
  125. $profile->getTime(),
  126. $profile->getParent() ? $profile->getParent()->getToken() : null
  127. ));
  128. fclose($file);
  129. return ! $exists;
  130. }
  131. /**
  132. * Gets filename to store data, associated to the token.
  133. *
  134. * @return string The profile filename
  135. */
  136. protected function getFilename($token)
  137. {
  138. // Uses 4 last characters, because first are mostly the same.
  139. $folderA = substr($token, -2, 2);
  140. $folderB = substr($token, -4, 2);
  141. return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token;
  142. }
  143. /**
  144. * Gets the index filename.
  145. *
  146. * @return string The index filename
  147. */
  148. protected function getIndexFilename()
  149. {
  150. return $this->folder.'/'.'index.csv';
  151. }
  152. /**
  153. * Reads a line in the file, ending with the current position.
  154. *
  155. * This function automatically skips the empty lines and do not include the line return in result value.
  156. *
  157. * @param resource $file The file resource, with the pointer placed at the end of the line to read
  158. *
  159. * @return mixed A string representating the line or FALSE if beginning of file is reached
  160. */
  161. protected function readLineFromFile($file)
  162. {
  163. if (ftell($file) === 0) {
  164. return false;
  165. }
  166. fseek($file, -1, SEEK_CUR);
  167. $str = '';
  168. while (true) {
  169. $char = fgetc($file);
  170. if ($char === "\n") {
  171. // Leave the file with cursor before the line return
  172. fseek($file, -1, SEEK_CUR);
  173. break;
  174. }
  175. $str = $char . $str;
  176. if (ftell($file) === 1) {
  177. // All file is read, so we move cursor to the position 0
  178. fseek($file, -1, SEEK_CUR);
  179. break;
  180. }
  181. fseek($file, -2, SEEK_CUR);
  182. }
  183. return $str === "" ? $this->readLineFromFile($file) : $str;
  184. }
  185. }