You are here

class FeedsFetcherResult in Feeds 7.2

Base class for all fetcher results.

Hierarchy

Expanded class hierarchy of FeedsFetcherResult

File

plugins/FeedsFetcher.inc, line 11
Contains the FeedsFetcher and related classes.

View source
class FeedsFetcherResult extends FeedsResult {

  /**
   * The raw fetched data.
   *
   * @var string
   */
  protected $raw;

  /**
   * The path to a file where the raw data is stored.
   *
   * @var string
   */
  protected $file_path;

  /**
   * Constructor.
   */
  public function __construct($raw) {
    $this->raw = $raw;
  }

  /**
   * Prevent saving the raw result when serializing object.
   */
  public function __sleep() {
    if (!empty($this->raw)) {

      // Save contents of raw to a file for later use.
      $this
        ->saveRawToFile();
    }

    // Save anything but 'raw'.
    unset($this->raw);
    return array_keys(get_object_vars($this));
  }

  /**
   * Returns the raw content.
   *
   * @return string
   *   The raw content from the source as a string.
   *
   * @throws Exception
   *   Extending classes MAY throw an exception if a problem occurred.
   */
  public function getRaw() {
    if (empty($this->raw)) {

      // Return raw contents from file.
      return $this
        ->getFileContents();
    }
    return $this
      ->sanitizeRawOptimized($this->raw);
  }

  /**
   * Get a path to a temporary file containing the resource provided by the
   * fetcher.
   *
   * File will be deleted after DRUPAL_MAXIMUM_TEMP_FILE_AGE.
   *
   * @return string
   *   A path to a file containing the raw content as a source.
   *
   * @throws Exception
   *   If an unexpected problem occurred.
   */
  public function getFilePath() {
    if (empty($this->file_path)) {

      // No file exists yet. Save any raw data that we got.
      $this
        ->saveRawToFile();
    }

    // Check if given file exists now.
    $this
      ->checkFile();

    // Return file path.
    return $this
      ->sanitizeFile($this->file_path);
  }

  /**
   * Returns directory for storing files that are in progress of import.
   *
   * @return string
   *   The cache dir to use.
   */
  public function getFeedsInProgressDir() {
    $dir = variable_get('feeds_in_progress_dir', NULL);
    if ($dir) {
      return $dir;
    }
    else {
      $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
      $scheme = isset($schemes['private']) ? 'private' : 'public';
      return $scheme . '://feeds/in_progress';
    }
  }

  /**
   * Constructs file name for saving the raw data.
   */
  public function constructFilePath() {
    return $this
      ->getFeedsInProgressDir() . '/' . get_class($this) . REQUEST_TIME;
  }

  /**
   * Returns if raw data exists.
   *
   * This checks if either $this->raw is set or if the raw data exists in a
   * file. This is better then calling just ::getRaw() as that would return a
   * copy of all raw data which may lead to memory issues if the data is very
   * large.
   *
   * @return bool
   *   True if the raw data exists.
   *   False otherwise.
   */
  public function rawExists() {
    return !empty($this->raw) || $this
      ->fileExists();
  }

  /**
   * Returns if the file to parse exists.
   *
   * @return bool
   *   True if the file exists.
   *   False otherwise.
   */
  public function fileExists() {
    if (!empty($this->file_path) && is_readable($this->file_path)) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Returns the contents of a file, if it exists.
   *
   * @return string
   *   The file contents.
   */
  public function getFileContents() {
    if ($this
      ->fileExists()) {
      $this
        ->sanitizeFile($this->file_path);
      return file_get_contents($this->file_path);
    }
  }

  /**
   * Checks that a file exists and is readable.
   *
   * @throws RuntimeException
   *   Thrown if the file isn't readable or writable.
   */
  protected function checkFile() {
    if (!file_exists($this->file_path)) {
      throw new RuntimeException(t('File %filepath does not exist.', array(
        '%filepath' => $this->file_path,
      )));
    }
    if (!is_readable($this->file_path)) {
      throw new RuntimeException(t('File %filepath is not readable.', array(
        '%filepath' => $this->file_path,
      )));
    }
  }

  /**
   * Saves the raw fetcher result to a file.
   *
   * @throws RuntimeException
   *   In case the destination wasn't writable.
   */
  public function saveRawToFile() {
    $file_in_progress_dir = $this
      ->getFeedsInProgressDir();
    if (!file_prepare_directory($file_in_progress_dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
      throw new RuntimeException(t('Feeds directory either cannot be created or is not writable.'));
    }
    $this->file_path = FALSE;
    if ($file = file_save_data($this
      ->getRaw(), $this
      ->constructFilePath())) {
      $file->status = 0;
      file_save($file);
      $this->file_path = $file->uri;

      // Clear raw data to save memory, but also to prevent saving the same raw data
      // to a file again in the same request.
      $this->raw = NULL;
    }
    else {
      throw new RuntimeException(t('Cannot write content to %dest', array(
        '%dest' => $destination,
      )));
    }
  }

  /**
   * Sanitize the raw content string.
   *
   * Currently supported sanitizations:
   * - Remove BOM header from UTF-8 files.
   *
   * Consider using ::sanitizeRawOptimized() instead that receives the variable
   * by reference and thus saves memory.
   *
   * @param string $raw
   *   The raw content string to be sanitized.
   *
   * @return string
   *   The sanitized content as a string.
   */
  public function sanitizeRaw($raw) {
    if (substr($raw, 0, 3) == pack('CCC', 0xef, 0xbb, 0xbf)) {
      $raw = substr($raw, 3);
    }
    return $raw;
  }

  /**
   * Sanitize the raw content string.
   *
   * Currently supported sanitizations:
   * - Remove BOM header from UTF-8 files.
   *
   * This accepts the raw contents by reference to prevent having the whole raw
   * contents in memory again.
   *
   * @param string $raw
   *   The raw content string to be sanitized.
   *
   * @return string
   *   The sanitized content as a string.
   */
  public function sanitizeRawOptimized(&$raw) {
    if (substr($raw, 0, 3) == pack('CCC', 0xef, 0xbb, 0xbf)) {
      $raw = substr($raw, 3);
    }
    return $raw;
  }

  /**
   * Sanitize the file in place.
   *
   * Currently supported sanitizations:
   * - Remove BOM header from UTF-8 files.
   *
   * @param string $filepath
   *   The file path of the file to be sanitized.
   *
   * @return string
   *   The file path of the sanitized file.
   *
   * @throws RuntimeException
   *   Thrown if the file is not writeable.
   */
  public function sanitizeFile($filepath) {
    $handle = fopen($filepath, 'r');
    $line = fgets($handle);
    fclose($handle);

    // If BOM header is present, read entire contents of file and overwrite the
    // file with corrected contents.
    if (substr($line, 0, 3) !== pack('CCC', 0xef, 0xbb, 0xbf)) {
      return $filepath;
    }
    if (!is_writable($filepath)) {
      throw new RuntimeException(t('File %filepath is not writable.', array(
        '%filepath' => $filepath,
      )));
    }
    $contents = file_get_contents($filepath);
    $contents = substr($contents, 3);
    $status = file_put_contents($filepath, $contents);
    return $filepath;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FeedsFetcherResult::$file_path protected property The path to a file where the raw data is stored.
FeedsFetcherResult::$raw protected property The raw fetched data.
FeedsFetcherResult::checkFile protected function Checks that a file exists and is readable.
FeedsFetcherResult::constructFilePath public function Constructs file name for saving the raw data.
FeedsFetcherResult::fileExists public function Returns if the file to parse exists.
FeedsFetcherResult::getFeedsInProgressDir public function Returns directory for storing files that are in progress of import.
FeedsFetcherResult::getFileContents public function Returns the contents of a file, if it exists.
FeedsFetcherResult::getFilePath public function Get a path to a temporary file containing the resource provided by the fetcher. 1
FeedsFetcherResult::getRaw public function Returns the raw content. 2
FeedsFetcherResult::rawExists public function Returns if raw data exists.
FeedsFetcherResult::sanitizeFile public function Sanitize the file in place.
FeedsFetcherResult::sanitizeRaw public function Sanitize the raw content string.
FeedsFetcherResult::sanitizeRawOptimized public function Sanitize the raw content string.
FeedsFetcherResult::saveRawToFile public function Saves the raw fetcher result to a file.
FeedsFetcherResult::__construct public function Constructor. 2
FeedsFetcherResult::__sleep public function Prevent saving the raw result when serializing object.