Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
primogenial
/
languages
/
wp-content
/
plugins
/
jetpack
/
jetpack_vendor
/
automattic
/
jetpack-videopress
/
src
/
tus
:
class-tus-file.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Main * * @package VideoPressUploader **/ namespace VideoPressUploader; use InvalidArgumentException; // Avoid direct calls to this file. if ( ! defined( 'ABSPATH' ) ) { die(); } /** * Class Tus_File. * * @package VideoPressUploader */ class Tus_File { const CHUNK_SIZE = 8192; // 8 kilobytes. const INPUT_STREAM = 'php://input'; const READ_BINARY = 'rb'; const APPEND_BINARY = 'ab'; /** * The input stream. * * @var string */ protected static $input_stream = self::INPUT_STREAM; /** * The key. * * @var string The key. */ protected $key; /** * The file checksum. * * @var string */ protected $checksum; /** * The file name. * * @var string */ protected $name; /** * The cache we are using. * * @var Tus_Abstract_Cache */ protected $cache; /** * The current file offset. * * @var int */ protected $offset; /** * The location. * * @var string */ protected $location; /** * The file path. * * @var string */ protected $file_path; /** * The file size. * * @var int */ protected $file_size; /** * The upload metadata. * * @var array */ private $upload_metadata = array(); /** * File constructor. * * @param string|null $name Name. * @param Tus_Abstract_Cache|null $cache Cache. */ public function __construct( $name = null, $cache = null ) { $this->name = $name; $this->cache = $cache; } /** * Returns an integer if it's castable. Otherwise it throws * * @param string|int $number Number to cast. * @throws InvalidArgumentException If argument is invalid. * @return int */ public function ensure_integer( $number ) { if ( ! is_numeric( $number ) ) { throw new InvalidArgumentException( 'argument needs to be an integer. Check stacktrace' ); } return (int) $number; } /** * Set file meta. * * @param int $offset Offset. * @param int $file_size File size. * @param string $file_path File path. * @param string $location Location. * * @throws InvalidArgumentException If argument is invalid. * @return $this */ public function set_meta( $offset, $file_size, $file_path, $location = null ) { $offset = $this->ensure_integer( $offset ); $file_size = $this->ensure_integer( $file_size ); if ( ! is_string( $file_path ) ) { throw new InvalidArgumentException( '$file_path needs to be a string' ); } $this->offset = absint( $offset ); $this->file_size = absint( $file_size ); $this->file_path = $file_path; $this->location = $location; return $this; } /** * Set name. * * @param string $name Name. * * @throws InvalidArgumentException If argument is invalid. * * @return $this */ public function set_name( $name ) { if ( ! is_string( $name ) ) { throw new InvalidArgumentException( '$name needs to be a string' ); } $this->name = $name; return $this; } /** * Get name. * * @return string */ public function get_name() { return $this->name; } /** * Set file size. * * @param int $size The size. * * @throws InvalidArgumentException If argument is invalid. * @return Tus_File */ public function set_file_size( $size ) { $size = $this->ensure_integer( $size ); $this->file_size = $size; return $this; } /** * Get file size. * * @return int */ public function get_file_size() { return $this->file_size; } /** * Set key. * * @param string $key The key. * * @throws InvalidArgumentException If argument is invalid. * @return Tus_File */ public function set_key( $key ) { if ( ! is_string( $key ) ) { throw new InvalidArgumentException( '$key needs to be a string' ); } $this->key = $key; return $this; } /** * Get key. * * @return string */ public function get_key() { return $this->key; } /** * Set checksum. * * @param string $checksum The checksum. * * @throws InvalidArgumentException If argument is invalid. * @return Tus_File */ public function set_checksum( $checksum ) { if ( ! is_string( $checksum ) ) { throw new InvalidArgumentException( '$checksum needs to be a string' ); } $this->checksum = $checksum; return $this; } /** * Get checksum. * * @return string */ public function get_checksum() { return $this->checksum; } /** * Set offset. * * @param int $offset The offset. * * @throws InvalidArgumentException If argument is invalid. * @return self */ public function set_offset( $offset ) { $offset = $this->ensure_integer( $offset ); $this->offset = absint( $offset ); return $this; } /** * Get offset. * * @return int */ public function get_offset() { return $this->offset; } /** * Set location. * * @param string $location The location. * * @throws InvalidArgumentException If argument is invalid. * @return self */ public function set_location( $location ) { if ( ! is_string( $location ) ) { throw new InvalidArgumentException( '$location needs to be a string' ); } $this->location = $location; return $this; } /** * Get location. * * @return string */ public function get_location() { return $this->location; } /** * Set absolute file location. * * @param string $path The path. * * @return Tus_File */ public function set_file_path( $path ) { $this->file_path = $path; return $this; } /** * Get absolute location. * * @return string */ public function get_file_path() { return $this->file_path; } /** * Set the upload meta. * * @param array $metadata The metadata. * * @return Tus_File */ public function set_upload_metadata( array $metadata ) { $this->upload_metadata = $metadata; return $this; } /** * Get input stream. * * @return string */ public function get_input_stream() { return self::$input_stream; } /** * Set input stream. Useful for testing. * * @param string $stream The stream. * * @return void */ public static function set_input_stream( $stream ) { self::$input_stream = $stream; } /** * Get file meta. * * @return array * @throws \Exception If date fails. */ public function details() { $now = Tus_Date_Utils::date_utc(); $ttl = $this->cache->get_ttl(); return array( 'name' => $this->name, 'size' => $this->file_size, 'offset' => $this->offset, 'checksum' => $this->checksum, 'location' => $this->location, 'file_path' => $this->file_path, 'metadata' => $this->upload_metadata, 'created_at' => $now->format( Tus_Abstract_Cache::RFC_7231 ), 'expires_at' => Tus_Date_Utils::add_seconds( $now, $ttl )->format( Tus_Abstract_Cache::RFC_7231 ), ); } /** * Upload file to server. * * @param int $total_bytes The total bytes of the file. * * @return int * @throws Out_Of_Range_Exception Various exceptions. * @throws Connection_Exception Various exceptions. * @throws File_Exception Various exceptions. */ public function upload( $total_bytes ) { if ( $this->offset === $total_bytes ) { return $this->offset; } try { $input = $this->open( $this->get_input_stream(), self::READ_BINARY ); $output = $this->open( $this->get_file_path(), self::APPEND_BINARY ); $key = $this->get_key(); } catch ( File_Exception $fe ) { Logger::log( 'error', $fe ); throw new File_Exception( 'Upload failed.' ); } try { $this->seek( $output, $this->offset ); while ( ! feof( $input ) ) { if ( CONNECTION_NORMAL !== connection_status() ) { throw new Connection_Exception( 'Connection aborted by user.' ); } $data = $this->read( $input, self::CHUNK_SIZE ); $bytes = $this->write( $output, $data, self::CHUNK_SIZE ); $this->offset += $bytes; $this->cache->set( $key, array( 'offset' => $this->offset ) ); if ( $this->offset > $total_bytes ) { throw new Out_Of_Range_Exception( 'The uploaded file is corrupt.' ); } if ( $this->offset === $total_bytes ) { break; } } } finally { $this->close( $input ); $this->close( $output ); } return $this->offset; } /** * Open file in given mode. * * @param string $file_path The file path. * @param string $mode The mode. * * @return resource * @throws File_Exception Exc. * @throws InvalidArgumentException If argument is invalid. */ public function open( $file_path, $mode ) { if ( ! is_string( $file_path ) ) { throw new InvalidArgumentException( '$file_path needs to be a string' ); } if ( ! is_string( $mode ) ) { throw new InvalidArgumentException( '$mode needs to be a string' ); } $this->exists( $file_path, $mode ); // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fopen // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged $ptr = @fopen( $file_path, $mode ); if ( false === $ptr ) { Logger::log( 'error', "Unable to open file at $file_path." ); throw new File_Exception( 'Unable to open file.' ); } return $ptr; } /** * Check if file to read exists. * * @param string $file_path The file path. * @param string $mode The mode. * * @return bool * @throws File_Exception File. * @throws InvalidArgumentException If argument is invalid. */ public function exists( $file_path, $mode = self::READ_BINARY ) { if ( ! is_string( $file_path ) ) { throw new InvalidArgumentException( '$file_path needs to be a string' ); } if ( self::INPUT_STREAM === $file_path ) { return true; } if ( self::READ_BINARY === $mode && ! file_exists( $file_path ) ) { throw new File_Exception( 'File not found.' ); } return true; } /** * Move file pointer to given offset using fseek. * * @param resource $handle The handle. * @param int $offset The offset. * @param int $whence The whence. * * @throws File_Exception Exc. * * @return int */ public function seek( $handle, $offset, $whence = SEEK_SET ) { $offset = $this->ensure_integer( $offset ); $position = fseek( $handle, $offset, $whence ); if ( -1 === $position ) { throw new File_Exception( 'Cannot move pointer to desired position.' ); } return $position; } /** * Read data from file. * * @param resource $handle The handle. * @param int $chunk_size Chunk size. * * @return string * @throws File_Exception If no data is read. */ public function read( $handle, $chunk_size ) { $chunk_size = $this->ensure_integer( $chunk_size ); // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fread $data = fread( $handle, $chunk_size ); if ( false === $data ) { throw new File_Exception( 'Cannot read file.' ); } return (string) $data; } /** * Write data to file. * * @param resource $handle The file handle. * @param string $data The data to write. * @param int|null $length Possibly the length of the data. * * @throws File_Exception When can't write. * * @return int */ public function write( $handle, $data, $length = null ) { // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fclose // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fwrite $bytes_written = \is_numeric( $length ) ? fwrite( $handle, $data, intval( $length ) ) : fwrite( $handle, $data ); if ( false === $bytes_written ) { throw new File_Exception( 'Cannot write to a file.' ); } return $bytes_written; } /** * Merge 2 or more files. * * @param array $files File data with meta info. * * @return int * @throws File_Exception When the file to be merged is not found. */ public function merge( array $files ) { $destination = $this->get_file_path(); $first_file = array_shift( $files ); // First partial file can directly be copied. $this->copy( $first_file['file_path'], $destination ); $this->offset = $first_file['offset']; $this->file_size = filesize( $first_file['file_path'] ); $handle = $this->open( $destination, self::APPEND_BINARY ); foreach ( $files as $file ) { if ( ! file_exists( $file['file_path'] ) ) { throw new File_Exception( 'File to be merged not found.' ); } $this->file_size += $this->write( $handle, $this->get_wp_filesystem()->get_contents( $file['file_path'] ) ); $this->offset += $file['offset']; } $this->close( $handle ); return $this->file_size; } /** * Copy file from source to destination. * * @param string $source The source. * @param string $destination The destination. * * @return bool * @throws File_Exception If copy fails. */ public function copy( $source, $destination ) { $status = copy( $source, $destination ); if ( false === $status ) { Logger::log( 'error', sprintf( 'Cannot copy source (%s) to destination (%s).', $source, $destination ) ); throw new File_Exception( 'Cannot copy source file to destination file.' ); } return $status; } /** * Delete file and/or folder. * * @param array $files The files. * @param bool $folder The folder. * * @return bool */ public function delete( array $files, $folder = false ) { $status = $this->delete_files( $files ); if ( $status && $folder ) { return $this->get_wp_filesystem()->rmdir( \dirname( current( $files ) ) ); } return $status; } /** * Delete multiple files. * * @param array $files The files. * * @return bool */ public function delete_files( array $files ) { if ( empty( $files ) ) { return false; } $status = true; foreach ( $files as $file ) { if ( $this->get_wp_filesystem()->exists( $file ) ) { $r = unlink( $file ); $status = $status && $r; } } return $status; } /** * Close file. * * @param mixed $handle The handle. * * @return bool */ public function close( $handle ) { return fclose( $handle ); } /** * Get the wp filesystem. * * @return \WP_Filesystem_Direct */ private function get_wp_filesystem() { global $wp_filesystem; if ( ! isset( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } return $wp_filesystem; } }