You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
12 KiB
413 lines
12 KiB
<?php
|
|
|
|
|
|
/**
|
|
* CodeIgniter
|
|
*
|
|
* An open source application development framework for PHP
|
|
*
|
|
* This content is released under the MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
|
* Copyright (c) 2019 CodeIgniter Foundation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* @package CodeIgniter
|
|
* @author CodeIgniter Dev Team
|
|
* @copyright 2019 CodeIgniter Foundation
|
|
* @license https://opensource.org/licenses/MIT MIT License
|
|
* @link https://codeigniter.com
|
|
* @since Version 4.0.0
|
|
* @filesource
|
|
*/
|
|
|
|
namespace CodeIgniter\HTTP\Files;
|
|
|
|
use CodeIgniter\Files\File;
|
|
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
|
use Config\Mimes;
|
|
use Exception;
|
|
|
|
/**
|
|
* Value object representing a single file uploaded through an
|
|
* HTTP request. Used by the IncomingRequest class to
|
|
* provide files.
|
|
*
|
|
* Typically, implementors will extend the SplFileInfo class.
|
|
*
|
|
* @package CodeIgniter\HTTP
|
|
*/
|
|
class UploadedFile extends File implements UploadedFileInterface
|
|
{
|
|
|
|
/**
|
|
* The path to the temporary file.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $path;
|
|
|
|
/**
|
|
* The original filename as provided by the client.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $originalName;
|
|
|
|
/**
|
|
* The filename given to a file during a move.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $name;
|
|
|
|
/**
|
|
* The type of file as provided by PHP
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $originalMimeType;
|
|
|
|
/**
|
|
* The error constant of the upload
|
|
* (one of PHP's UPLOADERRXXX constants)
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $error;
|
|
|
|
/**
|
|
* Whether the file has been moved already or not.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $hasMoved = false;
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Accepts the file information as would be filled in from the $_FILES array.
|
|
*
|
|
* @param string $path The temporary location of the uploaded file.
|
|
* @param string $originalName The client-provided filename.
|
|
* @param string $mimeType The type of file as provided by PHP
|
|
* @param integer $size The size of the file, in bytes
|
|
* @param integer $error The error constant of the upload (one of PHP's UPLOADERRXXX constants)
|
|
*/
|
|
public function __construct(string $path, string $originalName, string $mimeType = null, int $size = null, int $error = null)
|
|
{
|
|
$this->path = $path;
|
|
$this->name = $originalName;
|
|
$this->originalName = $originalName;
|
|
$this->originalMimeType = $mimeType;
|
|
$this->size = $size;
|
|
$this->error = $error;
|
|
|
|
parent::__construct($path, false);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Move the uploaded file to a new location.
|
|
*
|
|
* $targetPath may be an absolute path, or a relative path. If it is a
|
|
* relative path, resolution should be the same as used by PHP's rename()
|
|
* function.
|
|
*
|
|
* The original file MUST be removed on completion.
|
|
*
|
|
* If this method is called more than once, any subsequent calls MUST raise
|
|
* an exception.
|
|
*
|
|
* When used in an SAPI environment where $_FILES is populated, when writing
|
|
* files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
|
|
* used to ensure permissions and upload status are verified correctly.
|
|
*
|
|
* If you wish to move to a stream, use getStream(), as SAPI operations
|
|
* cannot guarantee writing to stream destinations.
|
|
*
|
|
* @see http://php.net/is_uploaded_file
|
|
* @see http://php.net/move_uploaded_file
|
|
*
|
|
* @param string $targetPath Path to which to move the uploaded file.
|
|
* @param string $name the name to rename the file to.
|
|
* @param boolean $overwrite State for indicating whether to overwrite the previously generated file with the same
|
|
* name or not.
|
|
*
|
|
* @return boolean
|
|
*
|
|
* @throws \InvalidArgumentException if the $path specified is invalid.
|
|
* @throws \RuntimeException on any error during the move operation.
|
|
* @throws \RuntimeException on the second or subsequent call to the method.
|
|
*/
|
|
public function move(string $targetPath, string $name = null, bool $overwrite = false)
|
|
{
|
|
$targetPath = $this->setPath($targetPath); //set the target path
|
|
|
|
if ($this->hasMoved)
|
|
{
|
|
throw HTTPException::forAlreadyMoved();
|
|
}
|
|
|
|
if (! $this->isValid())
|
|
{
|
|
throw HTTPException::forInvalidFile();
|
|
}
|
|
|
|
$targetPath = rtrim($targetPath, '/') . '/';
|
|
$name = is_null($name) ? $this->getName() : $name;
|
|
$destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name);
|
|
|
|
try
|
|
{
|
|
move_uploaded_file($this->path, $destination);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
$error = error_get_last();
|
|
throw HTTPException::forMoveFailed(basename($this->path), $targetPath, strip_tags($error['message']));
|
|
}
|
|
|
|
@chmod($targetPath, 0777 & ~umask());
|
|
|
|
// Success, so store our new information
|
|
$this->path = $targetPath;
|
|
$this->name = basename($destination);
|
|
$this->hasMoved = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* create file target path if
|
|
* the set path does not exist
|
|
*
|
|
* @param string $path
|
|
*
|
|
* @return string The path set or created.
|
|
*/
|
|
protected function setPath(string $path): string
|
|
{
|
|
if (! is_dir($path))
|
|
{
|
|
mkdir($path, 0777, true);
|
|
//create the index.html file
|
|
if (! is_file($path . 'index.html'))
|
|
{
|
|
$file = fopen($path . 'index.html', 'x+');
|
|
fclose($file);
|
|
}
|
|
}
|
|
return $path;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns whether the file has been moved or not. If it has,
|
|
* the move() method will not work and certain properties, like
|
|
* the tempName, will no longer be available.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function hasMoved(): bool
|
|
{
|
|
return $this->hasMoved;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Retrieve the error associated with the uploaded file.
|
|
*
|
|
* The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
|
|
*
|
|
* If the file was uploaded successfully, this method MUST return
|
|
* UPLOAD_ERR_OK.
|
|
*
|
|
* Implementations SHOULD return the value stored in the "error" key of
|
|
* the file in the $_FILES array.
|
|
*
|
|
* @see http://php.net/manual/en/features.file-upload.errors.php
|
|
* @return integer One of PHP's UPLOAD_ERR_XXX constants.
|
|
*/
|
|
public function getError(): int
|
|
{
|
|
if (is_null($this->error))
|
|
{
|
|
return UPLOAD_ERR_OK;
|
|
}
|
|
|
|
return $this->error;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Get error string
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getErrorString(): string
|
|
{
|
|
$errors = [
|
|
UPLOAD_ERR_OK => lang('HTTP.uploadErrOk'),
|
|
UPLOAD_ERR_INI_SIZE => lang('HTTP.uploadErrIniSize'),
|
|
UPLOAD_ERR_FORM_SIZE => lang('HTTP.uploadErrFormSize'),
|
|
UPLOAD_ERR_PARTIAL => lang('HTTP.uploadErrPartial'),
|
|
UPLOAD_ERR_NO_FILE => lang('HTTP.uploadErrNoFile'),
|
|
UPLOAD_ERR_CANT_WRITE => lang('HTTP.uploadErrCantWrite'),
|
|
UPLOAD_ERR_NO_TMP_DIR => lang('HTTP.uploadErrNoTmpDir'),
|
|
UPLOAD_ERR_EXTENSION => lang('HTTP.uploadErrExtension'),
|
|
];
|
|
|
|
$error = is_null($this->error) ? UPLOAD_ERR_OK : $this->error;
|
|
|
|
return sprintf($errors[$error] ?? lang('HTTP.uploadErrUnknown'), $this->getName());
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns the mime type as provided by the client.
|
|
* This is NOT a trusted value.
|
|
* For a trusted version, use getMimeType() instead.
|
|
*
|
|
* @return string The media type sent by the client or null if none was provided.
|
|
*/
|
|
public function getClientMimeType(): string
|
|
{
|
|
return $this->originalMimeType;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Retrieve the filename. This will typically be the filename sent
|
|
* by the client, and should not be trusted. If the file has been
|
|
* moved, this will return the final name of the moved file.
|
|
*
|
|
* @return string The filename sent by the client or null if none was provided.
|
|
*/
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns the name of the file as provided by the client during upload.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getClientName(): string
|
|
{
|
|
return $this->originalName;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Gets the temporary filename where the file was uploaded to.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getTempName(): string
|
|
{
|
|
return $this->path;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Overrides SPLFileInfo's to work with uploaded files, since
|
|
* the temp file that's been uploaded doesn't have an extension.
|
|
*
|
|
* Is simply an alias for guessExtension for a safer method
|
|
* than simply relying on the provided extension.
|
|
* Additionally it will return clientExtension in case if there are
|
|
* other extensions with the same mime type.
|
|
*/
|
|
public function getExtension(): string
|
|
{
|
|
return $this->guessExtension();
|
|
}
|
|
|
|
/**
|
|
* Attempts to determine the best file extension.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function guessExtension(): string
|
|
{
|
|
return Mimes::guessExtensionFromType($this->getClientMimeType(), $this->getClientExtension()) ?? $this->getClientExtension();
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns the original file extension, based on the file name that
|
|
* was uploaded. This is NOT a trusted source.
|
|
* For a trusted version, use guessExtension() instead.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getClientExtension(): string
|
|
{
|
|
return pathinfo($this->originalName, PATHINFO_EXTENSION) ?? '';
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns whether the file was uploaded successfully, based on whether
|
|
* it was uploaded via HTTP and has no errors.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function isValid(): bool
|
|
{
|
|
return is_uploaded_file($this->path) && $this->error === UPLOAD_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* Save the uploaded file to a new location.
|
|
*
|
|
* By default, upload files are saved in writable/uploads directory. The YYYYMMDD folder
|
|
* and random file name will be created.
|
|
*
|
|
* @param string $folderName the folder name to writable/uploads directory.
|
|
* @param string $fileName the name to rename the file to.
|
|
* @return string file full path
|
|
*/
|
|
public function store(string $folderName = null, string $fileName = null): string
|
|
{
|
|
$folderName = rtrim($folderName ?? date('Ymd'), '/') . '/' ;
|
|
$fileName = $fileName ?? $this->getRandomName();
|
|
|
|
// Move the uploaded file to a new location.
|
|
return ($this->move(WRITEPATH . 'uploads/' . $folderName, $fileName)) ?
|
|
$folderName . $this->name : null;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
}
|
|
|