store = Store::getInstance(); $this->session = Session::getInstance($phpUnit); } /** * Renders some JS to monitor a file on the server. * Report: $str: 'file:typo3conf/sql.log|tail:1000|append:1|interval:1000|htmlId:monitor1' * Fetch: download.php?s= with sip: file=&tail= * * @param array $vars * @return string * @throws \CodeException * @throws \UserFormException * @throws \UserReportException */ public function process(array $vars) { if (empty($vars[TOKEN_L_FILE])) { throw new \UserReportException("Monitor: empty or missing parameter 'file'", ERROR_MISSING_PARAMETER_FILE); } // Set defaults Support::setIfNotSet($vars, TOKEN_L_TAIL, MONITOR_TAIL_DEFAULT); $isContinuous = Support::setIfNotSet($vars, TOKEN_L_APPEND, MONITOR_APPEND_DEFAULT) == MONITOR_MODE_APPEND_1 ? 'true' : 'false'; $interval = Support::setIfNotSet($vars, TOKEN_L_INTERVAL, MONITOR_INTERVAL_DEFAULT); $htmlId = Support::setIfNotSet($vars, TOKEN_L_HTML_ID, MONITOR_HTML_ID_DEFAULT); // Setup query to generate SIP $queryString = DOWNLOAD_MODE . '=' . DOWNLOAD_MODE_MONITOR . '&' . TOKEN_L_FILE . '=' . $vars[TOKEN_L_FILE] . '&' . TOKEN_L_TAIL . '=' . $vars[TOKEN_L_TAIL] . '&' . TOKEN_L_APPEND . '=' . $vars[TOKEN_L_APPEND]; // $url = store::getSipInstance()->queryStringToSip(API_DIR . '/' . API_DOWNLOAD_PHP . '?' . $queryString, RETURN_URL); $arr = store::getSipInstance()->queryStringToSip(Path::join(Path::JAVASCRIPT_TO_EXT, Path::EXT_TO_API, API_DOWNLOAD_PHP) . '?' . $queryString, RETURN_ARRAY); $url = $arr[SIP_SIP_URL]; // On page reload, take care to remove optional existing old seek position. $key = $this->getSeekSessionKey($arr[CLIENT_SIP]); $this->session::unsetItem($key); $webworker = Path::urlExt(Path::EXT_TO_JAVASCRIPT, 'GetFileContent.js'); $code = << $(document).ready(function () { var getFile = new QfqNS.DisplayFile({ webworker: "$webworker", filePath: "$url", interval: $interval, targetId: "$htmlId", isContinuous: $isContinuous }); getFile.show(); }); EOF; return $code; } /** * @param $filepath * @param int $lines * @param $modeAppend * @return string * @throws \CodeException * @throws \DownloadException * @throws \UserFormException */ public function dump($filepath, $lines, $modeAppend) { // Retrieve always the last $lines if ($modeAppend == MONITOR_MODE_APPEND_0) { return $this->tailCustom($filepath, $lines); } // First call to this file in this session. $f = $this->open($filepath); // Check if there is already a seek position. $key = $this->getSeekSessionKey($this->store->getVar(SIP_SIP, STORE_SIP)); $seek = $this->session::get($key); if ($seek === false) { // Get seek position EOF fseek($f, 0, SEEK_END); $seek = ftell($f); // Store the position in session. $this->session::set($key, $seek); // On initial call, get the specified last $lines. return $this->tailCustom($filepath, $lines, true, $f); } fseek($f, $seek, SEEK_SET); $output = fread($f, 8192); $this->session::set($key, ftell($f)); return $output; } /** * @param $sip * @return string */ private function getSeekSessionKey($sip){ return MONITOR_SESSION_FILE_SEEK . '-' . $sip; } /** * @param string $filepath * @param string $mode * @return bool|resource * @throws \DownloadException */ private function open($filepath, $mode = 'rb') { // Open file $f = @fopen($filepath, $mode); if ($f === false) { $err = error_get_last(); $msg = $err['message'] ?? ''; throw new \DownloadException ("Error open '" . $filepath . "': " . $msg, ERROR_IO_FILE_NOT_FOUND); } return $f; } /** * Slightly modified version of http://www.geekality.net/2011/05/28/php-tail-tackling-large-files/ * @author Torleif Berger, Lorenzo Stanco * @link http://stackoverflow.com/a/15025877/995958 * @license http://creativecommons.org/licenses/by/3.0/ * * @param string $filepath * @param int $lines * @param bool $adaptive * @param null $f * @return string * @throws \DownloadException */ private function tailCustom($filepath, $lines = 1, $adaptive = true, $f = null) { // Open file if ($f === null) { $f = $this->open($filepath); } // Sets buffer size, according to the number of lines to retrieve. // This gives a performance boost when reading a few lines from the file. if (!$adaptive) $buffer = 4096; else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096)); // Jump to last character fseek($f, -1, SEEK_END); // Read it and adjust line number if necessary // (Otherwise the result would be wrong if file doesn't end with a blank line) if (fread($f, 1) != "\n") $lines -= 1; // Start reading $output = ''; $chunk = ''; // While we would like more while (ftell($f) > 0 && $lines >= 0) { // Figure out how far back we should jump $seek = min(ftell($f), $buffer); // Do the jump (backwards, relative to where we are) fseek($f, -$seek, SEEK_CUR); // Read a chunk and prepend it to our output $output = ($chunk = fread($f, $seek)) . $output; // Jump back to where we started reading fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR); // Decrease our line counter $lines -= substr_count($chunk, "\n"); } // While we have too many lines // (Because of buffer size we might have read too many) while ($lines++ < 0) { // Find first newline and remove all text before that $output = substr($output, strpos($output, "\n") + 1); } // Close file and return fclose($f); return trim($output); } }