cloudinary-8.x-1.x-dev/modules/cloudinary_stream_wrapper/src/StreamWrapper/CloudinaryStreamWrapper.php

modules/cloudinary_stream_wrapper/src/StreamWrapper/CloudinaryStreamWrapper.php
<?php

namespace Drupal\cloudinary_stream_wrapper\StreamWrapper;

// These classes are used to implement a stream wrapper class.
use Cloudinary\Asset\AssetType;
use Cloudinary\Asset\DeliveryType;
use Cloudinary\Configuration\Configuration;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;

/**
 * Implement DrupalStreamWrapperInterface with cloudinary[.folder]://.
 */
class CloudinaryStreamWrapper implements StreamWrapperInterface {
  /**
   * Instance URI (stream).
   *
   * A stream is referenced as "scheme://target".
   *
   * @var String
   */
  protected $uri;

  /**
   * Folder name as a prefix name of public_id.
   *
   * @var String
   */
  protected $folderName = NULL;

  /**
   * The resource type of Cloudinary (image, raw).
   *
   * @var String
   */
  protected $resourceType = AssetType::RAW;

  /**
   * The pointer to the next read or write.
   *
   * @var Int
   */
  protected $streamPointer = 0;

  /**
   * A buffer for reading/wrting.
   *
   * @var String
   */
  protected $streamData = NULL;

  /**
   * This $stream_write property is flagged for data written.
   *
   * @var Boolean
   */
  protected $streamWrite = FALSE;

  /**
   * List of files in a given directory.
   */
  protected $directoryList = array();

  /**
   * A current file resource of Cloudinary.
   *
   * @var Array
   */
  protected $resource = NULL;

  /**
   * Returns the type of stream wrapper.
   *
   * @return int
   *   See StreamWrapperInterface for permissible values.
   */
  public static function getType() {
    return StreamWrapperInterface::NORMAL;
  }

  /**
   * Base implementation of setUri().
   */
  public function setUri($uri) {
    $this->uri = $uri;
  }

  /**
   * Base implementation of getUri().
   */
  public function getUri() {
    return $this->uri;
  }

  /**
   * Object constructor.
   *
   * Load Cloudinary PHP SDK & initialize Cloudinary configuration.
   */
  public function __construct() {
    if (!Configuration::instance()) {
      $cloudinaryConfig = cloudinary_sdk_config_load();
      Configuration::instance(['cloud' => $cloudinaryConfig]);
    }
  }

  /**
   * Returns the asset helper service.
   *
   * @return \Drupal\cloudinary_sdk\Service\AssetHelperInterface
   *   The asset helper service
   */
  public static function assetHelper() {
    return \Drupal::service('cloudinary_sdk.asset_helper');
  }

  /**
   * Returns the name of the stream wrapper for use in the UI.
   *
   * @return string
   *   The stream wrapper name.
   */
  public function getName() {
    return t('Cloudinary files');
  }

  /**
   * {@inheritdoc}
   */
  public function getDescription() {
    return t('File system using Cloudinary');
  }

  /**
   * Load file or directory resource for Cloudinary.
   */
  protected function loadResource($uri) {
    static $resources;
    static $loaded;

    if (isset($resources[$uri])) {
      return $resources[$uri];
    }

    $info = static::parseUri($uri);

    $key = implode('-', [
      $info['public_id'],
      $info['resource_type'],
      $info['delivery_type'],
    ]);

    if (isset($loaded[$key])) {
      $resource = $loaded[$key];
    }
    else {
      $resource = cloudinary_stream_wrapper_load_file($info['public_id'], [
        AssetType::KEY => $info['resource_type'],
        DeliveryType::KEY => $info['delivery_type'],
      ]);

      $loaded[$key] = $resource;
    }

    if (!$resource) {
      $resources[$uri] = FALSE;

      return $resources[$uri];
    }

    $url = $this->getExternalUrl($uri);

    $resource['url'] = $url;
    $resource['secure_url'] = $url;

    // If format does not match then we need to update bytes property.
    if ($resource && $info['format'] !== $resource['format']) {
      $size = filesize($url);

      if (!$size) {
        $headers = get_headers($url, TRUE);
        $size = $headers['Content-Length'] ?? $resource['bytes'];
      }
      $resource['bytes'] = $size;
      $resource['format'] = $info['format'];
    }

    $resources[$uri] = $resource;

    return $resources[$uri];
  }

  /**
   * Get file stream data from Cloudinary by http url.
   */
  protected function streamReadCloudinary() {
    static $data;
    if (isset($data[$this->uri])) {
      return $data[$this->uri];
    }

    $resource = $this->loadResource($this->uri);
    if (!$resource || empty($resource['url'])) {
      return FALSE;
    }

    try {
      $client = \Drupal::httpClient();
      $request = $client->request('GET', $resource['url']);
      // Expected result.
      $data[$this->uri] = $request->getBody();
    }
    catch (\Exception $e) {
      watchdog_exception('cloudinary_stream_wrapper', $e);
    }

    return $data[$this->uri];
  }

  /**
   * Get file status.
   *
   * @return bool|array
   *   An array with file status, or FALSE in case of an error - see fstat()
   *   for a description of this array.
   *
   * @see http://php.net/manual/en/streamwrapper.stream-stat.php
   */
  protected function stat() {
    $resource = $this->loadResource($this->uri);
    if (!$resource) {
      return FALSE;
    }

    $stat = array();
    $stat[0] = $stat['dev'] = 0;
    $stat[1] = $stat['ino'] = 0;
    $stat[2] = $stat['mode'] = $resource['mode'];
    $stat[3] = $stat['nlink'] = 0;
    $stat[4] = $stat['uid'] = 0;
    $stat[5] = $stat['gid'] = 0;
    $stat[6] = $stat['rdev'] = 0;
    $stat[7] = $stat['size'] = $resource['bytes'];
    $stat[8] = $stat['atime'] = $resource['timestamp'];
    $stat[9] = $stat['mtime'] = $resource['timestamp'];
    $stat[10] = $stat['ctime'] = $resource['timestamp'];
    $stat[11] = $stat['blksize'] = 0;
    $stat[12] = $stat['blocks'] = 0;

    return $stat;
  }

  /**
   * Flush the stream buffers.
   */
  protected function flush() {
    $this->folderName = NULL;
    $this->directoryList = array();
    $this->streamData = NULL;
    $this->streamPointer = 0;
    $this->streamWrite = FALSE;
  }

  /**
   * Get public ID from the uri.
   *
   * @param string|null $uri
   *   The uri to be parsed.
   *
   * @return string
   *   The public id of the asset.
   */
  protected function getPublicId(string $uri) {
    $info = static::parseUri($uri);

    return $info['public_id'];
  }

  /**
   * Parse file uri.
   *
   * @param string $uri
   *   The file uri to be parsed.
   *
   * @return array
   *   Parsed uri as array.
   */
  public static function parseUri(string $uri) {
    [$scheme, $target] = explode('://', $uri);

    // Work only with cloudinary scheme.
    assert($scheme === 'cloudinary');

    // Parse image styles.
    if (preg_match('/^styles\/([^\/]*)\/([^\/]*)\/(.+)$/', $target, $matches)) {
      [, $style_name, $scheme, $target] = $matches;

      // Work only with cloudinary scheme when it's image style.
      assert($scheme === 'cloudinary');
    }

    // Parse new detailed format.
    if (preg_match('/^(image|video|raw):(upload|private|authenticated):([^:]*)(:(.+))?$/', $target, $matches)) {
      [, $resource_type, $delivery_type, $target,, $transformation] = array_pad($matches, 6, NULL);
    }

    // Support old format (file uri with transformation).
    if (preg_match('/^(.+)\/v1\/(.+)$/', $target, $matches)) {
      [, $transformation, $target] = $matches;
    }

    // Extract format.
    $parts = explode('.', $target);
    if (count($parts) > 1) {
      $format = array_pop($parts);
    }
    $public_id = implode('.', $parts);

    return [
      'style_name' => $style_name ?? NULL,
      'scheme' => $scheme,
      'public_id' => $public_id,
      'target' => $target,
      'transformation' => $transformation ?? NULL,
      'format' => $format ?? NULL,
      'resource_type' => $resource_type ?? AssetType::IMAGE,
      'delivery_type' => $delivery_type ?? DeliveryType::UPLOAD,
    ];
  }

  /**
   * Whether the uri contains image style path.
   *
   * @param string $uri
   *   The file uri to be checked.
   *
   * @return bool
   *   TRUE if it's image style path.
   */
  protected function isImageStyleUri(string $uri) {
    $info = static::parseUri($uri);

    return !empty($info['style_name']);
  }

  /**
   * Returns a web accessible URL for the resource.
   *
   * @return string
   *   A web accessible URL for the resource.
   */
  public function getExternalUrl(string $uri = NULL) {
    $uri = $uri ?? $this->uri;
    $info = static::parseUri($uri);

    $value = static::assetHelper()->generateStringValue($info);
    $asset = static::assetHelper()->getBaseMediaAsset($value);

    // No asset object available, simply load asset from api and return url.
    if (!$asset || $info['format'] === 'pdf') {
      $options = [
        AssetType::KEY => $info['resource_type'],
        DeliveryType::KEY => $info['delivery_type'],
      ];
      $resource = cloudinary_stream_wrapper_load_file($info['public_id'], $options);

      // Try to load resource by target (includes file extension).
      if (!$resource) {
        $resource = cloudinary_stream_wrapper_load_file($info['target'], $options);
      }

      return $resource['secure_url'];
    }

    // Force extension to get proper video thumbnail.
    if ($info['resource_type'] === AssetType::VIDEO && $info['format'] === 'jpeg') {
      $asset->extension($info['format']);
    }

    // Apply transformations in the following order:
    // default -> image style -> custom transformation.
    if ($default_transformation = static::getDefaultTransformation($info['resource_type'])) {
      $asset->addTransformation($default_transformation);
    }
    if ($style_name = $info['style_name']) {
      $qualifiers = cloudinary_stream_wrapper_transformation($style_name, NULL);
      $asset->addActionFromQualifiers($qualifiers);
    }
    if ($info['transformation']) {
      $asset->addTransformation($info['transformation']);
    }

    return (string) $asset->signUrl()->toUrl();
  }

  /**
   * Get default transformation for specific resource type.
   *
   * @param string $resource_type
   *   The resource type of the asset.
   *
   * @return string|null
   *   The raw transformation or NULL if not applicable.
   */
  public static function getDefaultTransformation(string $resource_type) {
    switch ($resource_type) {
      case AssetType::IMAGE:
        return \Drupal::config('cloudinary_media_library_widget.settings')
          ->get('cloudinary_image_optimizations');

      case AssetType::VIDEO:
        return \Drupal::config('cloudinary_video.settings')
          ->get('cloudinary_video_optimizations');
    }

    return NULL;
  }

  /**
   * Base implementation of getMimeType().
   */
  public function getMimeType($uri) {
    $info = static::parseUri($uri);

    if (!$info['format']) {
      $resource = $this->loadResource($uri);
      $info['target'] = implode('.', [$info['public_id'], $resource['format']]);
    }

    return \Drupal::service('file.mime_type.guesser')->guessMimeType($info['target']);
  }

  /**
   * Base implementation of chmod().
   */
  public function chmod($mode) {
    return TRUE;
  }

  /**
   * Base implementation of realpath().
   */
  public function realpath() {
    return trim($this->uri, '\/');
  }

  /**
   * Support for fopen(), file_get_contents(), file_put_contents() etc.
   *
   * @param string $uri
   *   A string containing the URI to the file to open.
   * @param string $mode
   *   The file mode ("r", "wb" etc.).
   * @param string $options
   *   A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
   * @param string $opened_path
   *   A string containing the path actually opened.
   *
   * @return bool
   *   Returns TRUE if file was opened successfully.
   *
   * @see http://php.net/manual/streamwrapper.stream-open.php
   */
  public function stream_open($uri, $mode, $options, &$opened_path) {
    $this->uri = $uri;

    // If this stream is being opened for writing, clear the object buffer
    // Return true as we'll create the object on flush call.
    if (strpbrk($mode, 'wax')) {
      $this->flush();
      $this->streamWrite = TRUE;
      return TRUE;
    }

    $resource = $this->loadResource($uri);

    if ($resource) {
      $this->flush();
      return TRUE;
    }

    return FALSE;
  }

  /**
   * Retrieve the underlying stream resource.
   *
   * This method is called in response to stream_select().
   *
   * @param int $cast_as
   *   Can be STREAM_CAST_FOR_SELECT when stream_select() is calling
   *   stream_cast() or STREAM_CAST_AS_STREAM when stream_cast() is called for
   *   other uses.
   *
   * @return resource|false
   *   The underlying stream resource or FALSE if stream_select() is not
   *   supported.
   *
   * @see stream_select()
   * @see http://php.net/manual/streamwrapper.stream-cast.php
   */
  public function stream_cast($cast_as) {
    return FALSE;
  }

  /**
   * Sets metadata on the stream.
   *
   * @param string $path
   *   A string containing the URI to the file to set metadata on.
   * @param int $option
   *   One of:
   *   - STREAM_META_TOUCH: The method was called in response to touch().
   *   - STREAM_META_OWNER_NAME: The method was called in response to chown()
   *     with string parameter.
   *   - STREAM_META_OWNER: The method was called in response to chown().
   *   - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
   *   - STREAM_META_GROUP: The method was called in response to chgrp().
   *   - STREAM_META_ACCESS: The method was called in response to chmod().
   * @param mixed $value
   *   If option is:
   *   - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
   *     function.
   *   - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
   *     user/group as string.
   *   - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
   *     user/group as integer.
   *   - STREAM_META_ACCESS: The argument of the chmod() as integer.
   *
   * @return bool
   *   Returns TRUE on success or FALSE on failure. If $option is not
   *   implemented, FALSE should be returned.
   *
   * @see http://www.php.net/manual/streamwrapper.stream-metadata.php
   */
  public function stream_metadata($path, $option, $value) {
    // We don't really do any of these, but we want to reassure the calling code
    // that there is no problem with chown or chgrp, even though we do not
    // actually support these.
    return TRUE;
  }


  /**
   * Change stream options.
   *
   * This method is called to set options on the stream.
   *
   * @param int $option
   *   One of:
   *   - STREAM_OPTION_BLOCKING: The method was called in response to
   *     stream_set_blocking().
   *   - STREAM_OPTION_READ_TIMEOUT: The method was called in response to
   *     stream_set_timeout().
   *   - STREAM_OPTION_WRITE_BUFFER: The method was called in response to
   *     stream_set_write_buffer().
   * @param int $arg1
   *   If option is:
   *   - STREAM_OPTION_BLOCKING: The requested blocking mode:
   *     - 1 means blocking.
   *     - 0 means not blocking.
   *   - STREAM_OPTION_READ_TIMEOUT: The timeout in seconds.
   *   - STREAM_OPTION_WRITE_BUFFER: The buffer mode, STREAM_BUFFER_NONE or
   *     STREAM_BUFFER_FULL.
   * @param int $arg2
   *   If option is:
   *   - STREAM_OPTION_BLOCKING: This option is not set.
   *   - STREAM_OPTION_READ_TIMEOUT: The timeout in microseconds.
   *   - STREAM_OPTION_WRITE_BUFFER: The requested buffer size.
   *
   * @return bool
   *   TRUE on success, FALSE otherwise. If $option is not implemented, FALSE
   *   should be returned.
   */
  public function stream_set_option($option, $arg1, $arg2) {
    return FALSE;
  }

  /**
   * Truncate stream.
   *
   * Will respond to truncation; e.g., through ftruncate().
   *
   * @param int $new_size
   *   The new size.
   *
   * @return bool
   *   TRUE on success, FALSE otherwise.
   *
   * @todo
   *   This one actually makes sense for the example.
   */
  public function stream_truncate($new_size) {
    return FALSE;
  }

  /**
   * Support for flock().
   *
   * @param string $operation
   *   One of the following:
   *   - LOCK_SH to acquire a shared lock (reader).
   *   - LOCK_EX to acquire an exclusive lock (writer).
   *   - LOCK_UN to release a lock (shared or exclusive).
   *   - LOCK_NB if you don't want flock() to block while locking (not
   *     supported on Windows).
   *
   * @return bool
   *   Always returns TRUE at the present time.
   *
   * @see http://php.net/manual/streamwrapper.stream-lock.php
   */
  public function stream_lock($operation) {
    return FALSE;
  }

  /**
   * Support for fread(), file_get_contents() etc.
   *
   * @param int $count
   *   Maximum number of bytes to be read.
   *
   * @return string
   *   The string that was read, or FALSE in case of an error.
   *
   * @see http://php.net/manual/streamwrapper.stream-read.php
   */
  public function stream_read($count) {
    if (!$this->streamData) {
      $stream_data = $this->streamReadCloudinary();

      if (!$stream_data) {
        return FALSE;
      }

      $this->streamData = $stream_data;
    }

    $data = substr($this->streamData, $this->streamPointer, $count);
    $this->streamPointer += $count;

    return $data;
  }

  /**
   * Support for fwrite(), file_put_contents() etc.
   *
   * @param string $data
   *   The string to be written.
   *
   * @return int
   *   The number of bytes written (integer).
   *
   * @see http://php.net/manual/streamwrapper.stream-write.php
   */
  public function stream_write($data) {
    // Write when flushed.
    $this->streamWrite = TRUE;
    $this->streamData .= $data;
    // Calculate date size.
    $bytes = strlen($data);
    $this->streamPointer += $bytes;

    return $bytes;
  }

  /**
   * Support for feof().
   *
   * @return bool
   *   TRUE if end-of-file has been reached.
   *
   * @see http://php.net/manual/streamwrapper.stream-eof.php
   */
  public function stream_eof() {
    if (!$this->streamData) {
      $stream_data = $this->streamReadCloudinary();

      if (!$stream_data) {
        return TRUE;
      }

      $this->streamData = $stream_data;
    }

    return $this->streamPointer >= strlen($this->streamData);
  }


  /**
   * Support for fseek().
   *
   * @param int $offset
   *   The byte offset to got to.
   * @param int $whence
   *   SEEK_SET, SEEK_CUR, or SEEK_END.
   *
   * @return bool
   *   TRUE on success.
   *
   * @see http://php.net/manual/en/streamwrapper.stream-seek.php
   */
  public function stream_seek($offset, $whence = SEEK_SET) {
    $seek = FALSE;

    switch ($whence) {
      case SEEK_SET:
        if (strlen($this->streamData) >= $offset && $offset >= 0) {
          $this->streamPointer = $offset;
          $seek = TRUE;
        }
        break;

      case SEEK_CUR:
        if ($offset >= 0) {
          $this->streamPointer += $offset;
          $seek = TRUE;
        }
        break;

      case SEEK_END:
        if (strlen($this->streamData) + $offset >= 0) {
          $this->streamPointer = strlen($this->streamData) + $offset;
          $seek = TRUE;
        }
        break;
    }

    return $seek;
  }

  /**
   * Support for fflush().
   *
   * @return bool
   *   TRUE if data was successfully stored (or there was no data to store).
   *
   * @see http://php.net/manual/streamwrapper.stream-flush.php
   */
  public function stream_flush() {
    if ($this->streamWrite) {
      $public_id = $this->getPublicId($this->uri);
      $base64_data = 'data:' . $this->getMimeType($this->uri) . ';base64,' . base64_encode($this->streamData);
      $dirname = dirname($public_id);
      if ($dirname == '.') {
        $dirname = '';
      }

      $options = array(
        'public_id' => $public_id,
        AssetType::KEY => $this->resourceType,
        'tags' => CLOUDINARY_STREAM_WRAPPER_FOLDER_TAG_PREFIX . $dirname,
      );

      if (cloudinary_stream_wrapper_create_file($base64_data, $options)) {
        return TRUE;
      }
    }

    $this->flush();

    return FALSE;
  }

  /**
   * Support for ftell().
   *
   * @return int
   *   The current offset in bytes from the beginning of file.
   *
   * @see http://php.net/manual/streamwrapper.stream-tell.php
   */
  public function stream_tell() {
    return $this->streamPointer;
  }

  /**
   * Support for fstat().
   *
   * @return array
   *   An array with file status, or FALSE in case of an error - see fstat()
   *   for a description of this array.
   *
   * @see http://php.net/manual/streamwrapper.stream-stat.php
   */
  public function stream_stat() {
    return $this->stat();
  }

  /**
   * Support for fclose().
   *
   * @return bool
   *   TRUE if stream was successfully closed.
   *
   * @see http://php.net/manual/streamwrapper.stream-close.php
   */
  public function stream_close() {
    $this->flush();

    return TRUE;
  }

  /**
   * Support for unlink().
   *
   * @param string $uri
   *   A string containing the URI to the resource to delete.
   *
   * @return bool
   *   TRUE if resource was successfully deleted.
   *
   * @see http://php.net/manual/streamwrapper.unlink.php
   */
  public function unlink($uri) {
    if ($this->isImageStyleUri($uri)) {
      return TRUE;
    }

    $resource = $this->loadResource($uri);

    if ($resource) {
      return cloudinary_stream_wrapper_delete_resource($resource);
    }

    return FALSE;
  }

  /**
   * Support for rename().
   *
   * @param string $from_uri
   *   The URI to the file to rename.
   * @param string $to_uri
   *   The new URI for file.
   *
   * @return bool
   *   TRUE if file was successfully renamed.
   *
   * @see http://php.net/manual/streamwrapper.rename.php
   */
  public function rename($from_uri, $to_uri) {
    // Check from_uri exist on Cloudinary.
    $from_resource = $this->loadResource($from_uri);

    if (!$from_resource) {
      return FALSE;
    }
    // Doesn't support folder rename.
    elseif ($from_resource['mode'] != CLOUDINARY_STREAM_WRAPPER_FILE) {
      return FALSE;
    }

    // Check to_uri exist on Cloudinary.
    $to_resource = $this->loadResource($to_uri);

    if ($to_resource) {
      return FALSE;
    }

    // Return false if different resource type.
    $to_resource_type = cloudinary_stream_wrapper_is_image($to_uri) ? AssetType::IMAGE : AssetType::RAW;

    if ($from_resource['resource_type' != $to_resource_type]) {
      return FALSE;
    }

    $to_public_id = $this->getPublicId($to_uri);

    return cloudinary_stream_wrapper_rename_file($from_resource, $to_public_id);
  }

  /**
   * Gets the name of the directory from a given path.
   *
   * This method is usually accessed through drupal_dirname(), which wraps
   * around the PHP dirname() function because it does not support stream
   * wrappers.
   *
   * @param string $uri
   *   A URI or path.
   *
   * @return string
   *   A string containing the directory name.
   *
   * @see drupal_dirname()
   */
  public function dirname($uri = NULL) {
    $uri = $uri ?? $this->uri;
    $info = static::parseUri($uri);
    $dirname = dirname($info['target']);

    if ($dirname == '.') {
      $dirname = '';
    }

    return $info['scheme'] . '://' . $dirname;
  }

  /**
   * Support for mkdir().
   *
   * @param string $uri
   *   A string containing the URI to the directory to create.
   * @param string $mode
   *   Permission flags - see mkdir().
   * @param string $options
   *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
   *
   * @return bool
   *   TRUE if directory was successfully created.
   *
   * @see http://php.net/manual/streamwrapper.mkdir.php
   */
  public function mkdir($uri, $mode, $options) {
    $resource = $this->loadResource($uri);

    if (!empty($resource)) {
      return TRUE;
    }
    $public_id = $this->getPublicId($uri);

    return cloudinary_stream_wrapper_create_folder($public_id);
  }

  /**
   * Support for rmdir().
   *
   * @param string $uri
   *   A string containing the URI to the directory to delete.
   * @param string $options
   *   A bit mask of STREAM_REPORT_ERRORS.
   *
   * @return bool
   *   TRUE if directory was successfully removed.
   *
   * @see http://php.net/manual/streamwrapper.rmdir.php
   */
  public function rmdir($uri, $options) {
    $resource = $this->loadResource($uri);

    if ($resource) {
      return cloudinary_stream_wrapper_delete_folder($resource);
    }

    return FALSE;
  }

  /**
   * Support for stat().
   *
   * @param string $uri
   *   A string containing the URI to get information about.
   * @param string $flags
   *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
   *
   * @return array
   *   An array with file status, or FALSE in case of an error - see fstat()
   *   for a description of this array.
   *
   * @see http://php.net/manual/streamwrapper.url-stat.php
   */
  public function url_stat($uri, $flags) {
    $this->uri = $uri;

    return $this->stat();
  }

  /**
   * Support for opendir().
   *
   * @param string $uri
   *   A string containing the URI to the directory to open.
   * @param string $options
   *   Unknown (parameter is not documented in PHP Manual).
   *
   * @return bool
   *   TRUE on success.
   *
   * @see http://php.net/manual/streamwrapper.dir-opendir.php
   */
  public function dir_opendir($uri, $options) {
    $resource = $this->loadResource($uri);

    if ($resource) {
      $list = array('.', '..');

      if (isset($this->resource['folders']) && !empty($this->resource['folders'])) {
        $list = array_merge($list, $this->resource['folders']);
      }

      if (isset($this->resource['files']) && !empty($this->resource['files'])) {
        $list = array_merge($list, $this->resource['files']);
      }

      // Append default file "sample.jpg" into root folder.
      $public_id = $this->getPublicId($uri);
      if ('' == $public_id && !in_array(CLOUDINARY_STREAM_WRAPPER_SAMPLE, $list)) {
        $list[] = CLOUDINARY_STREAM_WRAPPER_SAMPLE;
      }

      sort($list);
      $this->directoryList = $list;

      return TRUE;
    }

    return FALSE;
  }

  /**
   * Support for readdir().
   *
   * @return string
   *   The next filename, or FALSE if there are no more files in the directory.
   *
   * @see http://php.net/manual/streamwrapper.dir-readdir.php
   */
  public function dir_readdir() {
    $filename = current($this->directoryList);

    if ($filename !== FALSE) {
      next($this->directoryList);
    }

    return $filename;
  }

  /**
   * Support for rewinddir().
   *
   * @return bool
   *   TRUE on success.
   *
   * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
   */
  public function dir_rewinddir() {
    reset($this->directoryList);

    return TRUE;
  }

  /**
   * Support for closedir().
   *
   * @return bool
   *   TRUE on success.
   *
   * @see http://php.net/manual/streamwrapper.dir-closedir.php
   */
  public function dir_closedir() {
    $this->directoryList = array();

    return TRUE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc