You are here

class FileProfilerStorage in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php \Symfony\Component\HttpKernel\Profiler\FileProfilerStorage

Storage for profiler using files.

@author Alexandre Salomé <alexandre.salome@gmail.com>

Hierarchy

Expanded class hierarchy of FileProfilerStorage

1 file declares its use of FileProfilerStorage
FileProfilerStorageTest.php in vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php

File

vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php, line 19

Namespace

Symfony\Component\HttpKernel\Profiler
View source
class FileProfilerStorage implements ProfilerStorageInterface {

  /**
   * Folder where profiler data are stored.
   *
   * @var string
   */
  private $folder;

  /**
   * Constructs the file storage using a "dsn-like" path.
   *
   * Example : "file:/path/to/the/storage/folder"
   *
   * @param string $dsn The DSN
   *
   * @throws \RuntimeException
   */
  public function __construct($dsn) {
    if (0 !== strpos($dsn, 'file:')) {
      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));
    }
    $this->folder = substr($dsn, 5);
    if (!is_dir($this->folder)) {
      mkdir($this->folder, 0777, true);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function find($ip, $url, $limit, $method, $start = null, $end = null) {
    $file = $this
      ->getIndexFilename();
    if (!file_exists($file)) {
      return array();
    }
    $file = fopen($file, 'r');
    fseek($file, 0, SEEK_END);
    $result = array();
    while (count($result) < $limit && ($line = $this
      ->readLineFromFile($file))) {
      $values = str_getcsv($line);
      list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent) = $values;
      $csvStatusCode = isset($values[6]) ? $values[6] : null;
      $csvTime = (int) $csvTime;
      if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method)) {
        continue;
      }
      if (!empty($start) && $csvTime < $start) {
        continue;
      }
      if (!empty($end) && $csvTime > $end) {
        continue;
      }
      $result[$csvToken] = array(
        'token' => $csvToken,
        'ip' => $csvIp,
        'method' => $csvMethod,
        'url' => $csvUrl,
        'time' => $csvTime,
        'parent' => $csvParent,
        'status_code' => $csvStatusCode,
      );
    }
    fclose($file);
    return array_values($result);
  }

  /**
   * {@inheritdoc}
   */
  public function purge() {
    $flags = \FilesystemIterator::SKIP_DOTS;
    $iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
    $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
    foreach ($iterator as $file) {
      if (is_file($file)) {
        unlink($file);
      }
      else {
        rmdir($file);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function read($token) {
    if (!$token || !file_exists($file = $this
      ->getFilename($token))) {
      return;
    }
    return $this
      ->createProfileFromData($token, unserialize(file_get_contents($file)));
  }

  /**
   * {@inheritdoc}
   */
  public function write(Profile $profile) {
    $file = $this
      ->getFilename($profile
      ->getToken());
    $profileIndexed = is_file($file);
    if (!$profileIndexed) {

      // Create directory
      $dir = dirname($file);
      if (!is_dir($dir)) {
        mkdir($dir, 0777, true);
      }
    }

    // Store profile
    $data = array(
      'token' => $profile
        ->getToken(),
      'parent' => $profile
        ->getParentToken(),
      'children' => array_map(function ($p) {
        return $p
          ->getToken();
      }, $profile
        ->getChildren()),
      'data' => $profile
        ->getCollectors(),
      'ip' => $profile
        ->getIp(),
      'method' => $profile
        ->getMethod(),
      'url' => $profile
        ->getUrl(),
      'time' => $profile
        ->getTime(),
    );
    if (false === file_put_contents($file, serialize($data))) {
      return false;
    }
    if (!$profileIndexed) {

      // Add to index
      if (false === ($file = fopen($this
        ->getIndexFilename(), 'a'))) {
        return false;
      }
      fputcsv($file, array(
        $profile
          ->getToken(),
        $profile
          ->getIp(),
        $profile
          ->getMethod(),
        $profile
          ->getUrl(),
        $profile
          ->getTime(),
        $profile
          ->getParentToken(),
        $profile
          ->getStatusCode(),
      ));
      fclose($file);
    }
    return true;
  }

  /**
   * Gets filename to store data, associated to the token.
   *
   * @param string $token
   *
   * @return string The profile filename
   */
  protected function getFilename($token) {

    // Uses 4 last characters, because first are mostly the same.
    $folderA = substr($token, -2, 2);
    $folderB = substr($token, -4, 2);
    return $this->folder . '/' . $folderA . '/' . $folderB . '/' . $token;
  }

  /**
   * Gets the index filename.
   *
   * @return string The index filename
   */
  protected function getIndexFilename() {
    return $this->folder . '/index.csv';
  }

  /**
   * Reads a line in the file, backward.
   *
   * This function automatically skips the empty lines and do not include the line return in result value.
   *
   * @param resource $file The file resource, with the pointer placed at the end of the line to read
   *
   * @return mixed A string representing the line or null if beginning of file is reached
   */
  protected function readLineFromFile($file) {
    $line = '';
    $position = ftell($file);
    if (0 === $position) {
      return;
    }
    while (true) {
      $chunkSize = min($position, 1024);
      $position -= $chunkSize;
      fseek($file, $position);
      if (0 === $chunkSize) {

        // bof reached
        break;
      }
      $buffer = fread($file, $chunkSize);
      if (false === ($upTo = strrpos($buffer, "\n"))) {
        $line = $buffer . $line;
        continue;
      }
      $position += $upTo;
      $line = substr($buffer, $upTo + 1) . $line;
      fseek($file, max(0, $position), SEEK_SET);
      if ('' !== $line) {
        break;
      }
    }
    return '' === $line ? null : $line;
  }
  protected function createProfileFromData($token, $data, $parent = null) {
    $profile = new Profile($token);
    $profile
      ->setIp($data['ip']);
    $profile
      ->setMethod($data['method']);
    $profile
      ->setUrl($data['url']);
    $profile
      ->setTime($data['time']);
    $profile
      ->setCollectors($data['data']);
    if (!$parent && $data['parent']) {
      $parent = $this
        ->read($data['parent']);
    }
    if ($parent) {
      $profile
        ->setParent($parent);
    }
    foreach ($data['children'] as $token) {
      if (!$token || !file_exists($file = $this
        ->getFilename($token))) {
        continue;
      }
      $profile
        ->addChild($this
        ->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
    }
    return $profile;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FileProfilerStorage::$folder private property Folder where profiler data are stored.
FileProfilerStorage::createProfileFromData protected function
FileProfilerStorage::find public function Finds profiler tokens for the given criteria. Overrides ProfilerStorageInterface::find
FileProfilerStorage::getFilename protected function Gets filename to store data, associated to the token.
FileProfilerStorage::getIndexFilename protected function Gets the index filename.
FileProfilerStorage::purge public function Purges all data from the database. Overrides ProfilerStorageInterface::purge
FileProfilerStorage::read public function Reads data associated with the given token. Overrides ProfilerStorageInterface::read
FileProfilerStorage::readLineFromFile protected function Reads a line in the file, backward.
FileProfilerStorage::write public function Saves a Profile. Overrides ProfilerStorageInterface::write
FileProfilerStorage::__construct public function Constructs the file storage using a "dsn-like" path.