123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- <?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. If you do not save the file, no
- * entity is created in the database. This is because there are occasions
- * when you may want access to file data on datastores using the \ElggFile
- * interface without a need to persist information such as temporary files.
- *
- * @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 \stdClass $row Database result or null for new \ElggFile
- */
- public function __construct($row = null) {
- parent::__construct($row);
- // 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.
- *
- * @note This method can be called both dynamically and statically
- *
- * @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
- *
- * @return mixed Detected type on success, false on failure.
- * @todo Move this out into a utility class
- */
- public function detectMimeType($file = null, $default = null) {
- $class = __CLASS__;
- if (!$file && isset($this) && $this instanceof $class) {
- $file = $this->getFilenameOnFilestore();
- }
- if (!is_readable($file)) {
- return false;
- }
- $mime = $default;
- // 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);
- }
- $original_filename = isset($this) && $this instanceof $class ? $this->originalfilename : basename($file);
- $params = array(
- 'filename' => $file,
- 'original_filename' => $original_filename, // @see file upload action
- 'default' => $default,
- );
- return _elgg_services()->hooks->trigger('mime_type', 'file', $params, $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("You must specify a name before opening a file.");
- }
- // See if file has already been saved
- // seek on datastore, parameters and name?
- // Sanity check
- if (
- ($mode != "read") &&
- ($mode != "write") &&
- ($mode != "append")
- ) {
- $msg = "Unrecognized file mode '" . $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
- * @since 1.9
- */
- public function getSize() {
- return $this->filestore->getFileSize($this);
- }
- /**
- * Return the size of the file in bytes.
- *
- * @return int
- * @deprecated 1.8 Use getSize()
- */
- public function size() {
- elgg_deprecated_notice("Use \ElggFile::getSize() instead of \ElggFile::size()", 1.9);
- return $this->getSize();
- }
- /**
- * 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( , $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 = "Unable to load filestore class " . $filestore . " for file " . $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;
- }
- /**
- * Get property names to serialize.
- *
- * @return string[]
- */
- public function __sleep() {
- return array_diff(array_keys(get_object_vars($this)), array(
- // Don't persist filestore, which contains CONFIG
- // https://github.com/Elgg/Elgg/issues/9081#issuecomment-152859856
- 'filestore',
- // a resource
- 'handle',
- ));
- }
- /**
- * Reestablish filestore property
- *
- * @return void
- * @throws ClassNotFoundException
- */
- public function __wakeup() {
- $this->getFilestore();
- }
- }
|