/** * Test requesting an invalid output format. * @expectedException TimestampException * @covers ConvertibleTimestamp::getTimestamp */ public function testInvalidOutput() { $timestamp = new ConvertibleTimestamp('1343761268'); $timestamp->getTimestamp(98); }
/** * Set the cached stat info for a file path. * Negatives (404s) are not cached. By not caching negatives, we can skip cache * salting for the case when a file is created at a path were there was none before. * * @param string $path Storage path * @param array $val Stat information to cache */ protected final function setFileCache($path, array $val) { $path = FileBackend::normalizeStoragePath($path); if ($path === null) { return; // invalid storage path } $mtime = ConvertibleTimestamp::convert(TS_UNIX, $val['mtime']); $ttl = $this->memCache->adaptiveTTL($mtime, 7 * 86400, 300, 0.1); $key = $this->fileCacheKey($path); // Set the cache unless it is currently salted. $this->memCache->set($key, $val, $ttl); }
public function timestamp($ts = 0) { $ct = new ConvertibleTimestamp($ts); return $ct->getTimestamp(TS_POSTGRES); }
public function decodeExpiry($expiry, $format = TS_MW) { if ($expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity()) { return 'infinity'; } return ConvertibleTimestamp::convert($format, $expiry); }
protected function doGetFileStat(array $params) { $source = $this->resolveToFSPath($params['src']); if ($source === null) { return false; // invalid storage path } $this->trapWarnings(); // don't trust 'false' if there were errors $stat = is_file($source) ? stat($source) : false; // regular files only $hadError = $this->untrapWarnings(); if ($stat) { $ct = new ConvertibleTimestamp($stat['mtime']); return ['mtime' => $ct->getTimestamp(TS_MW), 'size' => $stat['size']]; } elseif (!$hadError) { return false; // file does not exist } else { return null; // failure } }
/** * Stream a file to the browser, adding all the headings and fun stuff. * Headers sent include: Content-type, Content-Length, Last-Modified, * and Content-Disposition. * * @param array $headers Any additional headers to send if the file exists * @param bool $sendErrors Send error messages if errors occur (like 404) * @param array $optHeaders HTTP request header map (e.g. "range") (use lowercase keys) * @param integer $flags Bitfield of STREAM_* constants * @throws MWException * @return bool Success */ public function stream($headers = [], $sendErrors = true, $optHeaders = [], $flags = 0) { // Don't stream it out as text/html if there was a PHP error if ((($flags & self::STREAM_HEADLESS) == 0 || $headers) && headers_sent()) { echo "Headers already sent, terminating.\n"; return false; } $headerFunc = $flags & self::STREAM_HEADLESS ? function ($header) { // no-op } : function ($header) { is_int($header) ? HttpStatus::header($header) : header($header); }; MediaWiki\suppressWarnings(); $info = stat($this->path); MediaWiki\restoreWarnings(); if (!is_array($info)) { if ($sendErrors) { self::send404Message($this->path, $flags); } return false; } // Send Last-Modified HTTP header for client-side caching $mtimeCT = new ConvertibleTimestamp($info['mtime']); $headerFunc('Last-Modified: ' . $mtimeCT->getTimestamp(TS_RFC2822)); if (($flags & self::STREAM_ALLOW_OB) == 0) { call_user_func($this->obResetFunc); } $type = call_user_func($this->streamMimeFunc, $this->path); if ($type && $type != 'unknown/unknown') { $headerFunc("Content-type: {$type}"); } else { // Send a content type which is not known to Internet Explorer, to // avoid triggering IE's content type detection. Sending a standard // unknown content type here essentially gives IE license to apply // whatever content type it likes. $headerFunc('Content-type: application/x-wiki'); } // Don't send if client has up to date cache if (isset($optHeaders['if-modified-since'])) { $modsince = preg_replace('/;.*$/', '', $optHeaders['if-modified-since']); if ($mtimeCT->getTimestamp(TS_UNIX) <= strtotime($modsince)) { ini_set('zlib.output_compression', 0); $headerFunc(304); return true; // ok } } // Send additional headers foreach ($headers as $header) { header($header); // always use header(); specifically requested } if (isset($optHeaders['range'])) { $range = self::parseRange($optHeaders['range'], $info['size']); if (is_array($range)) { $headerFunc(206); $headerFunc('Content-Length: ' . $range[2]); $headerFunc("Content-Range: bytes {$range[0]}-{$range[1]}/{$info['size']}"); } elseif ($range === 'invalid') { if ($sendErrors) { $headerFunc(416); $headerFunc('Cache-Control: no-cache'); $headerFunc('Content-Type: text/html; charset=utf-8'); $headerFunc('Content-Range: bytes */' . $info['size']); } return false; } else { // unsupported Range request (e.g. multiple ranges) $range = null; $headerFunc('Content-Length: ' . $info['size']); } } else { $range = null; $headerFunc('Content-Length: ' . $info['size']); } if (is_array($range)) { $handle = fopen($this->path, 'rb'); if ($handle) { $ok = true; fseek($handle, $range[0]); $remaining = $range[2]; while ($remaining > 0 && $ok) { $bytes = min($remaining, 8 * 1024); $data = fread($handle, $bytes); $remaining -= $bytes; $ok = $data !== false; print $data; } } else { return false; } } else { return readfile($this->path) !== false; // faster } return true; }
/** * function to validate date properties, and convert to (partial) Exif format. * * Dates can be one of the following formats: * YYYY * YYYY-MM * YYYY-MM-DD * YYYY-MM-DDThh:mmTZD * YYYY-MM-DDThh:mm:ssTZD * YYYY-MM-DDThh:mm:ss.sTZD * * @param array $info Information about current property * @param mixed &$val Current value to validate. Converts to TS_EXIF as a side-effect. * in cases where there's only a partial date, it will give things like * 2011:04. * @param bool $standalone If this is a simple property or array */ public function validateDate($info, &$val, $standalone) { if (!$standalone) { // this only validates standalone properties, not arrays, etc return; } $res = []; // @codingStandardsIgnoreStart Long line that cannot be broken if (!preg_match('/^([0-3]\\d{3})(?:-([01]\\d)(?:-([0-3]\\d)(?:T([0-2]\\d):([0-6]\\d)(?::([0-6]\\d)(?:\\.\\d+)?)?([-+]\\d{2}:\\d{2}|Z)?)?)?)?$/D', $val, $res)) { // @codingStandardsIgnoreEnd $this->logger->info(__METHOD__ . " Expected date but got {$val}"); $val = null; } else { /* * $res is formatted as follows: * 0 -> full date. * 1 -> year, 2-> month, 3-> day, 4-> hour, 5-> minute, 6->second * 7-> Timezone specifier (Z or something like +12:30 ) * many parts are optional, some aren't. For example if you specify * minute, you must specify hour, day, month, and year but not second or TZ. */ /* * First of all, if year = 0000, Something is wrongish, * so don't extract. This seems to happen when * some programs convert between metadata formats. */ if ($res[1] === '0000') { $this->logger->info(__METHOD__ . " Invalid date (year 0): {$val}"); $val = null; return; } if (!isset($res[4])) { // hour // just have the year month day (if that) $val = $res[1]; if (isset($res[2])) { $val .= ':' . $res[2]; } if (isset($res[3])) { $val .= ':' . $res[3]; } return; } if (!isset($res[7]) || $res[7] === 'Z') { // if hour is set, then minute must also be or regex above will fail. $val = $res[1] . ':' . $res[2] . ':' . $res[3] . ' ' . $res[4] . ':' . $res[5]; if (isset($res[6]) && $res[6] !== '') { $val .= ':' . $res[6]; } return; } // Extra check for empty string necessary due to TZ but no second case. $stripSeconds = false; if (!isset($res[6]) || $res[6] === '') { $res[6] = '00'; $stripSeconds = true; } // Do timezone processing. We've already done the case that tz = Z. // We know that if we got to this step, year, month day hour and min must be set // by virtue of regex not failing. $unix = ConvertibleTimestamp::convert(TS_UNIX, $res[1] . $res[2] . $res[3] . $res[4] . $res[5] . $res[6]); $offset = intval(substr($res[7], 1, 2)) * 60 * 60; $offset += intval(substr($res[7], 4, 2)) * 60; if (substr($res[7], 0, 1) === '-') { $offset = -$offset; } $val = ConvertibleTimestamp::convert(TS_EXIF, $unix + $offset); if ($stripSeconds) { // If seconds weren't specified, remove the trailing ':00'. $val = substr($val, 0, -3); } } }