diff options
Diffstat (limited to 'engine/classes/ElggFile.php')
-rw-r--r-- | engine/classes/ElggFile.php | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php new file mode 100644 index 000000000..23080834b --- /dev/null +++ b/engine/classes/ElggFile.php @@ -0,0 +1,440 @@ +<?php + +/** + * This class represents a physical file. + * + * Create a new ElggFile object and specify a filename, and optionally a + * FileStore (if one isn't specified then the default is assumed.) + * + * Open the file using the appropriate mode, and you will be able to + * read and write to the file. + * + * Optionally, you can also call the file's save() method, this will + * turn the file into an entity in the system and permit you to do + * things like attach tags to the file etc. This is not done automatically + * since there are many occasions where you may want access to file data + * on datastores using the ElggFile interface but do not want to create + * an Entity reference to it in the system (temporary files for example). + * + * @class ElggFile + * @package Elgg.Core + * @subpackage DataModel.File + */ +class ElggFile extends ElggObject { + /** Filestore */ + private $filestore; + + /** File handle used to identify this file in a filestore. Created by open. */ + private $handle; + + /** + * Set subtype to 'file'. + * + * @return void + */ + protected function initializeAttributes() { + parent::initializeAttributes(); + + $this->attributes['subtype'] = "file"; + } + + /** + * Loads an ElggFile entity. + * + * @param int $guid GUID of the ElggFile object + */ + public function __construct($guid = null) { + parent::__construct($guid); + + // Set default filestore + $this->filestore = $this->getFilestore(); + } + + /** + * Set the filename of this file. + * + * @param string $name The filename. + * + * @return void + */ + public function setFilename($name) { + $this->filename = $name; + } + + /** + * Return the filename. + * + * @return string + */ + public function getFilename() { + return $this->filename; + } + + /** + * Return the filename of this file as it is/will be stored on the + * filestore, which may be different to the filename. + * + * @return string + */ + public function getFilenameOnFilestore() { + return $this->filestore->getFilenameOnFilestore($this); + } + + /** + * Return the size of the filestore associated with this file + * + * @param string $prefix Storage prefix + * @param int $container_guid The container GUID of the checked filestore + * + * @return int + */ + public function getFilestoreSize($prefix = '', $container_guid = 0) { + if (!$container_guid) { + $container_guid = $this->container_guid; + } + $fs = $this->getFilestore(); + // @todo add getSize() to ElggFilestore + return $fs->getSize($prefix, $container_guid); + } + + /** + * Get the mime type of the file. + * + * @return string + */ + public function getMimeType() { + if ($this->mimetype) { + return $this->mimetype; + } + + // @todo Guess mimetype if not here + } + + /** + * Set the mime type of the file. + * + * @param string $mimetype The mimetype + * + * @return bool + */ + public function setMimeType($mimetype) { + return $this->mimetype = $mimetype; + } + + /** + * Detects mime types based on filename or actual file. + * + * @param mixed $file The full path of the file to check. For uploaded files, use tmp_name. + * @param mixed $default A default. Useful to pass what the browser thinks it is. + * @since 1.7.12 + * + * @note If $file is provided, this may be called statically + * + * @return mixed Detected type on success, false on failure. + */ + public function detectMimeType($file = null, $default = null) { + if (!$file) { + if (isset($this) && $this->filename) { + $file = $this->filename; + } else { + return false; + } + } + + $mime = false; + + // for PHP5 folks. + if (function_exists('finfo_file') && defined('FILEINFO_MIME_TYPE')) { + $resource = finfo_open(FILEINFO_MIME_TYPE); + if ($resource) { + $mime = finfo_file($resource, $file); + } + } + + // for everyone else. + if (!$mime && function_exists('mime_content_type')) { + $mime = mime_content_type($file); + } + + // default + if (!$mime) { + return $default; + } + + return $mime; + } + + /** + * Set the optional file description. + * + * @param string $description The description. + * + * @return bool + */ + public function setDescription($description) { + $this->description = $description; + } + + /** + * Open the file with the given mode + * + * @param string $mode Either read/write/append + * + * @return resource File handler + * + * @throws IOException|InvalidParameterException + */ + public function open($mode) { + if (!$this->getFilename()) { + throw new IOException(elgg_echo('IOException:MissingFileName')); + } + + // See if file has already been saved + // seek on datastore, parameters and name? + + // Sanity check + if ( + ($mode != "read") && + ($mode != "write") && + ($mode != "append") + ) { + $msg = elgg_echo('InvalidParameterException:UnrecognisedFileMode', array($mode)); + throw new InvalidParameterException($msg); + } + + // Get the filestore + $fs = $this->getFilestore(); + + // Ensure that we save the file details to object store + //$this->save(); + + // Open the file handle + $this->handle = $fs->open($this, $mode); + + return $this->handle; + } + + /** + * Write data. + * + * @param string $data The data + * + * @return bool + */ + public function write($data) { + $fs = $this->getFilestore(); + + return $fs->write($this->handle, $data); + } + + /** + * Read data. + * + * @param int $length Amount to read. + * @param int $offset The offset to start from. + * + * @return mixed Data or false + */ + public function read($length, $offset = 0) { + $fs = $this->getFilestore(); + + return $fs->read($this->handle, $length, $offset); + } + + /** + * Gets the full contents of this file. + * + * @return mixed The file contents. + */ + public function grabFile() { + $fs = $this->getFilestore(); + return $fs->grabFile($this); + } + + /** + * Close the file and commit changes + * + * @return bool + */ + public function close() { + $fs = $this->getFilestore(); + + if ($fs->close($this->handle)) { + $this->handle = NULL; + + return true; + } + + return false; + } + + /** + * Delete this file. + * + * @return bool + */ + public function delete() { + $fs = $this->getFilestore(); + + $result = $fs->delete($this); + + if ($this->getGUID() && $result) { + $result = parent::delete(); + } + + return $result; + } + + /** + * Seek a position in the file. + * + * @param int $position Position in bytes + * + * @return bool + */ + public function seek($position) { + $fs = $this->getFilestore(); + + // @todo add seek() to ElggFilestore + return $fs->seek($this->handle, $position); + } + + /** + * Return the current position of the file. + * + * @return int The file position + */ + public function tell() { + $fs = $this->getFilestore(); + + return $fs->tell($this->handle); + } + + /** + * Return the size of the file in bytes. + * + * @return int + */ + public function size() { + return $this->filestore->getFileSize($this); + } + + /** + * Return a boolean value whether the file handle is at the end of the file + * + * @return bool + */ + public function eof() { + $fs = $this->getFilestore(); + + return $fs->eof($this->handle); + } + + /** + * Returns if the file exists + * + * @return bool + */ + public function exists() { + $fs = $this->getFilestore(); + + return $fs->exists($this); + } + + /** + * Set a filestore. + * + * @param ElggFilestore $filestore The file store. + * + * @return void + */ + public function setFilestore(ElggFilestore $filestore) { + $this->filestore = $filestore; + } + + /** + * Return a filestore suitable for saving this file. + * This filestore is either a pre-registered filestore, + * a filestore as recorded in metadata or the system default. + * + * @return ElggFilestore + * + * @throws ClassNotFoundException + */ + protected function getFilestore() { + // Short circuit if already set. + if ($this->filestore) { + return $this->filestore; + } + + // ask for entity specific filestore + // saved as filestore::className in metadata. + // need to get all filestore::* metadata because the rest are "parameters" that + // get passed to filestore::setParameters() + if ($this->guid) { + $options = array( + 'guid' => $this->guid, + 'where' => array("n.string LIKE 'filestore::%'"), + ); + + $mds = elgg_get_metadata($options); + + $parameters = array(); + foreach ($mds as $md) { + list($foo, $name) = explode("::", $md->name); + if ($name == 'filestore') { + $filestore = $md->value; + } + $parameters[$name] = $md->value; + } + } + + // need to check if filestore is set because this entity is loaded in save() + // before the filestore metadata is saved. + if (isset($filestore)) { + if (!class_exists($filestore)) { + $msg = elgg_echo('ClassNotFoundException:NotFoundNotSavedWithFile', + array($filestore, $this->guid)); + throw new ClassNotFoundException($msg); + } + + $this->filestore = new $filestore(); + $this->filestore->setParameters($parameters); + // @todo explain why $parameters will always be set here (PhpStorm complains) + } + + // this means the entity hasn't been saved so fallback to default + if (!$this->filestore) { + $this->filestore = get_default_filestore(); + } + + return $this->filestore; + } + + /** + * Save the file + * + * Write the file's data to the filestore and save + * the corresponding entity. + * + * @see ElggObject::save() + * + * @return bool + */ + public function save() { + if (!parent::save()) { + return false; + } + + // Save datastore metadata + $params = $this->filestore->getParameters(); + foreach ($params as $k => $v) { + $this->setMetaData("filestore::$k", $v); + } + + // Now make a note of the filestore class + $this->setMetaData("filestore::filestore", get_class($this->filestore)); + + return true; + } +} |