diff --git a/examples/ajax-fetch.php b/examples/ajax-fetch.php index f6ed451f0..d53884113 100644 --- a/examples/ajax-fetch.php +++ b/examples/ajax-fetch.php @@ -6,9 +6,6 @@ use Tracy\Debugger; -// session is required for this functionality -session_start(); - // For security reasons, Tracy is visible only on localhost. // You may force Tracy to run in development mode by passing the Debugger::DEVELOPMENT instead of Debugger::DETECT. Debugger::enable(Debugger::DETECT, __DIR__ . '/log'); diff --git a/examples/ajax-jquery.php b/examples/ajax-jquery.php index fc1d41ad3..8d941f6bb 100644 --- a/examples/ajax-jquery.php +++ b/examples/ajax-jquery.php @@ -6,9 +6,6 @@ use Tracy\Debugger; -// session is required for this functionality -session_start(); - // For security reasons, Tracy is visible only on localhost. // You may force Tracy to run in development mode by passing the Debugger::DEVELOPMENT instead of Debugger::DETECT. Debugger::enable(Debugger::DETECT, __DIR__ . '/log'); diff --git a/examples/preloading.php b/examples/preloading.php index 154918ca3..6ac06a9a4 100644 --- a/examples/preloading.php +++ b/examples/preloading.php @@ -6,9 +6,6 @@ use Tracy\Debugger; -// session is required for this functionality -session_start(); - // For security reasons, Tracy is visible only on localhost. // You may force Tracy to run in development mode by passing the Debugger::DEVELOPMENT instead of Debugger::DETECT. Debugger::enable(Debugger::DETECT, __DIR__ . '/log'); diff --git a/examples/redirect.php b/examples/redirect.php index 4ec6b6cac..d12794f66 100644 --- a/examples/redirect.php +++ b/examples/redirect.php @@ -6,9 +6,6 @@ use Tracy\Debugger; -// session is required for this functionality -session_start(); - // For security reasons, Tracy is visible only on localhost. // You may force Tracy to run in development mode by passing the Debugger::DEVELOPMENT instead of Debugger::DETECT. Debugger::enable(Debugger::DETECT, __DIR__ . '/log'); diff --git a/src/Bridges/Nette/TracyExtension.php b/src/Bridges/Nette/TracyExtension.php index c3bce8809..059eaf6eb 100644 --- a/src/Bridges/Nette/TracyExtension.php +++ b/src/Bridges/Nette/TracyExtension.php @@ -40,6 +40,7 @@ public function getConfigSchema(): Nette\Schema\Schema 'fromEmail' => Expect::email()->dynamic(), 'emailSnooze' => Expect::string()->dynamic(), 'logSeverity' => Expect::anyOf(Expect::scalar(), Expect::listOf('scalar')), + 'storage' => Expect::string(), 'editor' => Expect::string()->dynamic(), 'browser' => Expect::string()->dynamic(), 'errorTemplate' => Expect::string()->dynamic(), @@ -83,7 +84,7 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class) $builder = $this->getContainerBuilder(); $options = (array) $this->config; - unset($options['bar'], $options['blueScreen'], $options['netteMailer']); + unset($options['bar'], $options['blueScreen'], $options['netteMailer'], $options['storage']); if (isset($options['logSeverity'])) { $res = 0; foreach ((array) $options['logSeverity'] as $level) { @@ -135,9 +136,18 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class) )); } - if (!$this->cliMode && ($name = $builder->getByType(Nette\Http\Session::class))) { - $initialize->addBody('$this->getService(?)->start();', [$name]); - $initialize->addBody('Tracy\Debugger::dispatch();'); + if (!$this->cliMode) { + if ($this->config->storage === 'session') { + if ($name = $builder->getByType(Nette\Http\Session::class)) { + $initialize->addBody('$this->getService(?)->start();', [$name]); + } + + $initialize->addBody('Tracy\Debugger::dispatch();'); + + } elseif ($this->config->storage !== null) { + $initialize->addBody('Tracy\Debugger::setStorage(new Tracy\FileSession(?));', [$this->config->storage]); + $initialize->addBody('Tracy\Debugger::dispatch();'); + } } } diff --git a/src/Tracy/Debugger/Debugger.php b/src/Tracy/Debugger/Debugger.php index 1c6b6c039..b8759f86b 100644 --- a/src/Tracy/Debugger/Debugger.php +++ b/src/Tracy/Debugger/Debugger.php @@ -233,6 +233,7 @@ public static function enable($mode = null, ?string $logDirectory = null, $email 'Logger/FireLogger', 'Logger/Logger', 'Session/SessionHandler', + 'Session/FileSession', 'Session/NativeSession', 'Helpers', ] as $path) { @@ -484,7 +485,8 @@ public static function setSessionHandler(SessionHandler $storage): void public static function getSessionHandler(): SessionHandler { if (!self::$sessionHandler) { - self::$sessionHandler = new NativeSession; + $dir = session_save_path() ?: ini_get('upload_tmp_dir') ?: sys_get_temp_dir() ?: self::$logDirectory; + self::$sessionHandler = $dir ? new FileSession($dir) : new NativeSession; } return self::$sessionHandler; diff --git a/src/Tracy/Session/FileSession.php b/src/Tracy/Session/FileSession.php new file mode 100644 index 000000000..66c196657 --- /dev/null +++ b/src/Tracy/Session/FileSession.php @@ -0,0 +1,109 @@ +dir = $dir; + } + + + public function isAvailable(): bool + { + if (!$this->file) { + $this->open(); + } + + return (bool) $this->file; + } + + + private function open(): void + { + $id = $_COOKIE[$this->cookieName] ?? null; + if ( + !is_string($id) + || !preg_match('#^\w{10}\z#i', $id) + || !($file = @fopen($path = $this->dir . '/' . self::FILE_PREFIX . $id, 'r+')) // intentionally @ + ) { + $id = Helpers::createId(); + setcookie($this->cookieName, $id, time() + self::COOKIE_LIFETIME, '/', '', false, true); + + $file = @fopen($path = $this->dir . '/' . self::FILE_PREFIX . $id, 'c+'); // intentionally @ + if ($file === false) { + throw new \RuntimeException("Unable to create file '$path'. " . error_get_last()['message']); + } + } + + if (!@flock($file, LOCK_EX)) { // intentionally @ + throw new \RuntimeException("Unable to acquire exclusive lock on '$path'. ", error_get_last()['message']); + } + + $this->file = $file; + $this->data = @unserialize(stream_get_contents($this->file)) ?: []; // @ - file may be empty + + if (mt_rand() / mt_getrandmax() < $this->gcProbability) { + $this->clean(); + } + } + + + public function &getData(): array + { + return $this->data; + } + + + public function clean(): void + { + $old = strtotime('-1 week'); + foreach (glob($this->dir . '/' . self::FILE_PREFIX . '*') as $file) { + if (filemtime($file) < $old) { + unlink($file); + } + } + } + + + public function __destruct() + { + if (!$this->file) { + return; + } + + ftruncate($this->file, 0); + fseek($this->file, 0); + fwrite($this->file, serialize($this->data)); + fclose($this->file); + $this->file = null; + } +} diff --git a/src/tracy.php b/src/tracy.php index bdd53abab..1937062fe 100644 --- a/src/tracy.php +++ b/src/tracy.php @@ -27,5 +27,6 @@ require __DIR__ . '/Tracy/OutputDebugger/OutputDebugger.php'; require __DIR__ . '/Tracy/Session/SessionHandler.php'; require __DIR__ . '/Tracy/Session/NativeSession.php'; +require __DIR__ . '/Tracy/Session/FileSession.php'; require __DIR__ . '/Tracy/Helpers.php'; require __DIR__ . '/Tracy/functions.php';