This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php

252 lines
6.3 KiB
PHP
Raw Normal View History

2011-07-22 17:33:22 +01:00
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
2011-07-22 17:33:22 +01:00
namespace Symfony\Component\HttpKernel\Profiler;
/**
* Storage for profiler using files.
2011-07-22 17:33:22 +01:00
*
* @author Alexandre Salomé <alexandre.salome@gmail.com>
*/
class FileProfilerStorage implements ProfilerStorageInterface
{
/**
* Folder where profiler data are stored.
*
* @var string
*/
private $folder;
2011-07-22 17:33:22 +01:00
/**
* Constructs the file storage using a "dsn-like" path.
*
* Example : "file:/path/to/the/storage/folder"
*
* @param string $dsn The DSN
*/
2011-07-22 17:33:22 +01:00
public function __construct($dsn)
{
if (0 !== strpos($dsn, 'file:')) {
throw new \InvalidArgumentException("FileStorage DSN must start with file:");
}
$this->folder = substr($dsn, 5);
if (!is_dir($this->folder)) {
mkdir($this->folder);
}
}
/**
* {@inheritdoc}
*/
2011-07-22 17:33:22 +01:00
public function find($ip, $url, $limit)
{
$file = $this->getIndexFilename();
if (!file_exists($file)) {
return array();
}
$file = fopen($file, 'r');
fseek($file, 0, SEEK_END);
$result = array();
while ($limit > 0) {
$line = $this->readLineFromFile($file);
if (false === $line) {
break;
}
if ($line === "") {
continue;
}
list($csvToken, $csvIp, $csvUrl, $csvTime, $csvParent) = str_getcsv($line);
if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url)) {
continue;
}
$row = array(
'token' => $csvToken,
'ip' => $csvIp,
'url' => $csvUrl,
'time' => $csvTime,
'parent' => $csvParent
);
$result[] = $row;
$limit--;
}
fclose($file);
return $result;
2011-07-22 17:33:22 +01:00
}
/**
* {@inheritdoc}
*/
2011-07-22 17:33:22 +01:00
public function purge()
{
$flags = \FilesystemIterator::SKIP_DOTS;
$iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
2011-07-22 17:33:22 +01:00
2011-07-24 10:58:14 +01:00
foreach ($iterator as $file) {
if (is_file($file)) {
unlink($file);
} else {
rmdir($file);
}
2011-07-22 17:33:22 +01:00
}
}
/**
* {@inheritdoc}
*/
2011-07-22 17:33:22 +01:00
public function read($token)
{
$file = $this->getFilename($token);
if (!file_exists($file)) {
return null;
}
$profile = unserialize(file_get_contents($file));
$childrenFile = $this->getChildrenFilename($token);
if (file_exists($childrenFile)) {
$childrenTokens = explode(',', file_get_contents($childrenFile));
foreach ($childrenTokens as $childToken) {
$profile->addChild($this->read($childToken));
}
}
return $profile;
2011-07-22 17:33:22 +01:00
}
/**
* {@inheritdoc}
*/
2011-07-22 17:33:22 +01:00
public function write(Profile $profile)
{
$file = $this->getFilename($profile->getToken());
$exists = file_exists($file);
// Create directory
$dir = dirname($file);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
// Store profile
file_put_contents($file, serialize($profile));
// Add to index
$file = fopen($this->getIndexFilename(), 'a');
fputcsv($file, array(
$profile->getToken(),
$profile->getIp(),
$profile->getUrl(),
$profile->getTime(),
$profile->getParent() ? $profile->getParent()->getToken() : null
));
fclose($file);
if ($profile->getParent()) {
$childrenFile = $this->getChildrenFilename($profile->getParent()->getToken());
$toWrite = file_exists($childrenFile) ? ','.$profile->getToken() : $profile->getToken();
$fp = fopen($childrenFile, 'a');
fputs($fp, $toWrite);
fclose($fp);
}
return ! $exists;
2011-07-22 17:33:22 +01:00
}
/**
* Gets filename to store data, associated to the token.
*
* @return string The profile filename
*/
2011-07-22 17:33:22 +01:00
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;
2011-07-22 17:33:22 +01:00
}
/**
* Gets filename to store children tokens.
*
* @return string The tokens filename
*/
protected function getChildrenFilename($token)
{
return $this->getFilename($token).'.children';
}
/**
* Gets the index filename.
*
* @return string The index filename
*/
protected function getIndexFilename()
{
return $this->folder.'/'.'index.csv';
}
/**
* Reads a line in the file, ending with the current position.
*
* 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 representating the line or FALSE if beginning of file is reached
*/
protected function readLineFromFile($file)
{
if (ftell($file) === 0) {
return false;
}
fseek($file, -1, SEEK_CUR);
$str = '';
while (true) {
$char = fgetc($file);
if ($char === "\n") {
// Leave the file with cursor before the line return
fseek($file, -1, SEEK_CUR);
break;
}
$str = $char . $str;
if (ftell($file) === 1) {
// All file is read, so we move cursor to the position 0
fseek($file, -1, SEEK_CUR);
break;
}
fseek($file, -2, SEEK_CUR);
}
return $str === "" ? $this->readLineFromFile($file) : $str;
}
2011-07-22 17:33:22 +01:00
}