diff --git a/example/upload-file.php b/example/upload-file.php index 426e7a5..2ffc3a7 100644 --- a/example/upload-file.php +++ b/example/upload-file.php @@ -1,4 +1,5 @@ uploadAsFileObj(); $file = $files[0]; + if ($file instanceof UploadedFile) { if (!$file->isUploaded()) { http_response_code(404); @@ -22,4 +24,4 @@ } else { echo 'Successfully uploaded.'; } -} \ No newline at end of file +} diff --git a/php_cs.php.dist b/php_cs.php.dist index ea7b2cb..ac0a570 100644 --- a/php_cs.php.dist +++ b/php_cs.php.dist @@ -6,8 +6,8 @@ $finder = PhpCsFixer\Finder::create() ->in(__DIR__) ; -return PhpCsFixer\Config::create() - ->setRules([ +$config = new PhpCsFixer\Config(); +return $config->setRules([ 'align_multiline_comment' => [ 'comment_type' => 'phpdocs_only' ], @@ -54,7 +54,7 @@ return PhpCsFixer\Config::create() 'sort_algorithm' => 'alpha' ], 'ordered_class_elements' => [ - 'sortAlgorithm' => 'alpha', + 'sort_algorithm' => 'alpha', 'order' => [ 'constant_public', 'constant_protected', diff --git a/webfiori/file/File.php b/webfiori/file/File.php index 66ef36e..1c3d5fa 100644 --- a/webfiori/file/File.php +++ b/webfiori/file/File.php @@ -21,6 +21,7 @@ class File implements JsonI { * */ const DEFAULT_MIME = 'application/octet-stream'; + private static $errFunc; /** * The name of the attachment. * @@ -67,7 +68,6 @@ class File implements JsonI { * @since 1.0 */ private $rawData; - private static $errFunc; /** * Creates new instance of the class. * @@ -90,6 +90,7 @@ public function __construct(string $fNameOrAbsPath = '', string $fPath = '') { $this->fileName = ''; $this->rawData = ''; self::initErrHandler(); + if (!$this->setPath($fPath)) { $info = $this->extractPathAndName($fNameOrAbsPath); $this->setDir($info['path']); @@ -105,83 +106,14 @@ public function __construct(string $fNameOrAbsPath = '', string $fPath = '') { } $this->id = -1; } - /** - * Returns an array that contains directories names of the calling script. - * - * This method is used to extract the pathes of files at which they called - * this method. - * - * @return array An array that contains directories names of the calling files. - */ - public static function getCallingFilesPaths() : array { - $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); - $retVal = []; - - foreach ($debugTrace as $traceEntry) { - if (isset($traceEntry['file'])) { - $file = $traceEntry['file']; - $split = explode(DIRECTORY_SEPARATOR, $file); - $withoutFile = array_diff($split, [$split[count($split) - 1]]); - $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); - - if (strlen($dir) != 0) { - $dir .= DIRECTORY_SEPARATOR; - - if (!in_array($dir, $retVal)) { - $retVal[] = $dir; - } - } - } - } - - return $retVal; - } - private static function initErrHandler() { - self::$errFunc = function (int $errno, string $errstr, string $errfile, int $errline) { - throw new FileException($errstr.' At class File line '.$errline, $errno); - }; - } /** * Returns JSON string that represents basic file info. * * @return string */ public function __toString() { - return $this->toJSON().''; } - - /** - * Returns an array that contains integer values which represents file data as binary. - * - * Each index of the array will have an integer value between 0 and 255 inclusive. - * - * @return array An array that contains integer values which represents file data as binary. - */ - public function toBytesArray() : array { - $raw = $this->getRawData(); - $asArray = unpack('C*', $raw); - - return array_values($asArray); - } - - /** - * Returns an array of strings which represents file data as hex. - * - * Each index will have a string of two characters which represents each byte. - * - * @return array An array of strings which represents file data as hex. - */ - public function toHexArray() : array { - $bytes = $this->toBytesArray(); - $hexArr = []; - - foreach ($bytes as $byte) { - $hexArr[] = sprintf("%02X", $byte); - } - - return $hexArr; - } /** * Appends a string of data to the already existing data. * @@ -224,6 +156,29 @@ public function create(bool $createDirIfNotExist = false) { } } } + public static function fixPath($fPath) { + $DS = DIRECTORY_SEPARATOR; + $trimmedPath = str_replace('/', $DS, str_replace('\\', $DS, trim($fPath))); + $len = strlen($trimmedPath); + $start = ''; + + if ($len != 0) { + $start = $trimmedPath[0] == $DS ? $DS : ''; + + while ($trimmedPath[$len - 1] == '/' || $trimmedPath[$len - 1] == '\\') { + $tmpDir = trim($trimmedPath,'/'); + $trimmedPath = trim($tmpDir,'\\'); + $len = strlen($trimmedPath); + } + + while ($trimmedPath[0] == '/' || $trimmedPath[0] == '\\') { + $tmpDir = trim($trimmedPath,'/'); + $trimmedPath = trim($tmpDir,'\\'); + } + } + + return $start.$trimmedPath; + } /** * Returns the full path to the file. * @@ -248,6 +203,37 @@ public function getAbsolutePath() : string { return ''; } + /** + * Returns an array that contains directories names of the calling script. + * + * This method is used to extract the pathes of files at which they called + * this method. + * + * @return array An array that contains directories names of the calling files. + */ + public static function getCallingFilesPaths() : array { + $debugTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15); + $retVal = []; + + foreach ($debugTrace as $traceEntry) { + if (isset($traceEntry['file'])) { + $file = $traceEntry['file']; + $split = explode(DIRECTORY_SEPARATOR, $file); + $withoutFile = array_diff($split, [$split[count($split) - 1]]); + $dir = implode(DIRECTORY_SEPARATOR, $withoutFile); + + if (strlen($dir) != 0) { + $dir .= DIRECTORY_SEPARATOR; + + if (!in_array($dir, $retVal)) { + $retVal[] = $dir; + } + } + } + } + + return $retVal; + } /** * Split file raw data into chunks of fixed size. * @@ -701,6 +687,38 @@ public function setRawDataDecoded(string $raw, bool $strict = false) { $this->rawData = $decoded; $this->setSize(strlen($this->rawData)); } + + /** + * Returns an array that contains integer values which represents file data as binary. + * + * Each index of the array will have an integer value between 0 and 255 inclusive. + * + * @return array An array that contains integer values which represents file data as binary. + */ + public function toBytesArray() : array { + $raw = $this->getRawData(); + $asArray = unpack('C*', $raw); + + return array_values($asArray); + } + + /** + * Returns an array of strings which represents file data as hex. + * + * Each index will have a string of two characters which represents each byte. + * + * @return array An array of strings which represents file data as hex. + */ + public function toHexArray() : array { + $bytes = $this->toBytesArray(); + $hexArr = []; + + foreach ($bytes as $byte) { + $hexArr[] = sprintf("%02X", $byte); + } + + return $hexArr; + } /** * Returns a JSON string that represents the file. * @@ -714,6 +732,7 @@ public function setRawDataDecoded(string $raw, bool $strict = false) { */ public function toJSON() : Json { $size = $this->getSize() != -1 ? $this->getSize() : 0; + return new Json([ 'id' => $this->getID(), 'mime' => $this->getMIME(), @@ -772,6 +791,7 @@ public function view(bool $asAttachment = false) { */ public function write(bool $append = true, bool $createIfNotExist = false) { $pathV = $this->checkNameAndPath(); + if ($createIfNotExist) { $this->create(true); } @@ -812,10 +832,11 @@ private function checkNameAndPath(): string { if (strlen($fPath) == 0) { $possiblePaths = self::getCallingFilesPaths(); + foreach ($possiblePaths as $fPath) { - if (self::isFileExist($fPath.DIRECTORY_SEPARATOR.$fName)) { $this->setDir($fPath); + return $this->getAbsolutePath(); } } @@ -837,6 +858,34 @@ private function createResource($mode, $path) { return false; } + + private function doNotUseClassResponse($contentType, $asAttachment) { + $headersArr = [ + 'Accept-Ranges: bytes', + 'content-type: '.$contentType + ]; + + if (isset($_SERVER['HTTP_RANGE'])) { + $expl = $this->readRange(); + http_response_code(206); + $headersArr[] = 'content-range: '.'bytes '.$expl[0].'-'.$expl[1].'/'.$this->getSize(); + $headersArr[] = 'content-length: '.($expl[1] - $expl[0]); + } else { + $headersArr[] = 'Content-Length: '.$this->getSize(); + } + + if ($asAttachment === true) { + $headersArr[] = 'Content-Disposition: attachment; filename="'.$this->getName().'"'; + } else { + $headersArr[] = 'Content-Disposition: inline; filename="'.$this->getName().'"'; + } + + foreach ($headersArr as $h) { + header($h); + } + echo $this->getRawData(); + die(); + } private function extractMimeFromName() { $exp = explode('.', $this->getName()); @@ -869,6 +918,12 @@ private function extractPathAndName($absPath): array { 'path' => '' ]; } + private static function initErrHandler() { + self::$errFunc = function (int $errno, string $errstr, string $errfile, int $errline) + { + throw new FileException($errstr.' At class File line '.$errline, $errno); + }; + } private function readHelper($fPath,$from,$to) { if ($this->isExist()) { $fSize = filesize($fPath); @@ -899,103 +954,6 @@ private function readHelper($fPath,$from,$to) { throw new FileException('File not found: \''.$fPath.'\'.'); } } - private function setSize($size) { - if ($size >= 0) { - $this->fileSize = $size; - } - } - public static function fixPath($fPath) { - $DS = DIRECTORY_SEPARATOR; - $trimmedPath = str_replace('/', $DS, str_replace('\\', $DS, trim($fPath))); - $len = strlen($trimmedPath); - $start = ''; - - if ($len != 0) { - $start = $trimmedPath[0] == $DS ? $DS : ''; - - while ($trimmedPath[$len - 1] == '/' || $trimmedPath[$len - 1] == '\\') { - $tmpDir = trim($trimmedPath,'/'); - $trimmedPath = trim($tmpDir,'\\'); - $len = strlen($trimmedPath); - } - - while ($trimmedPath[0] == '/' || $trimmedPath[0] == '\\') { - $tmpDir = trim($trimmedPath,'/'); - $trimmedPath = trim($tmpDir,'\\'); - } - } - - return $start.$trimmedPath; - } - private function viewFileHelper($asAttachment) { - $contentType = $this->getMIME(); - - if (class_exists('\webfiori\http\Response')) { - $this->useClassResponse($contentType, $asAttachment); - } else { - $this->doNotUseClassResponse($contentType, $asAttachment); - } - } - - /** - * - * @param string $fPath - * @param bool $append - * @param bool $encode - * @return bool - * @throws FileException - */ - private function writeHelper(string $fPath, bool $append, bool $encode = false) { - if (strlen($this->getRawData()) == 0) { - throw new FileException("No data is set to write."); - } - - if (!$this->isExist()) { - throw new FileException("File not found: '$fPath'."); - } else { - if ($append) { - $resource = $this->createResource('ab', $fPath); - } else { - $resource = $this->createResource('rb+', $fPath); - } - } - - if (!is_resource($resource)) { - throw new FileException('Unable to open the file at \''.$fPath.'\'.'); - } else { - fwrite($resource, $this->getRawData($encode)); - fclose($resource); - - return true; - } - } - - private function doNotUseClassResponse($contentType, $asAttachment) { - $headersArr = [ - 'Accept-Ranges: bytes', - 'content-type: '.$contentType - ]; - - if (isset($_SERVER['HTTP_RANGE'])) { - $expl = $this->readRange(); - http_response_code(206); - $headersArr[] = 'content-range: '.'bytes '.$expl[0].'-'.$expl[1].'/'.$this->getSize(); - $headersArr[] = 'content-length: '. ($expl[1] - $expl[0]); - } else { - $headersArr[] = 'Content-Length: '.$this->getSize(); - } - - if ($asAttachment === true) { - $headersArr[] = 'Content-Disposition: attachment; filename="'.$this->getName().'"'; - } else { - $headersArr[] = 'Content-Disposition: inline; filename="'.$this->getName().'"'; - } - foreach ($headersArr as $h) { - header($h); - } - echo $this->getRawData(); - die(); - } private function readRange() { $range = filter_var($_SERVER['HTTP_RANGE']); $rangeArr = explode('=', $range); @@ -1037,11 +995,16 @@ private function setPath(string $fPath) { return $retVal; } + private function setSize($size) { + if ($size >= 0) { + $this->fileSize = $size; + } + } private function useClassResponse($contentType, $asAttachment) { Response::addHeader('Accept-Ranges', 'bytes'); Response::addHeader('content-type', $contentType); - + if (isset($_SERVER['HTTP_RANGE'])) { $expl = $this->readRange(); Response::setCode(206); @@ -1057,9 +1020,51 @@ private function useClassResponse($contentType, $asAttachment) { Response::addHeader('Content-Disposition', 'inline; filename="'.$this->getName().'"'); } Response::write($this->getRawData()); - + if (!defined('__PHPUNIT_PHAR__')) { Response::send(); } } + private function viewFileHelper($asAttachment) { + $contentType = $this->getMIME(); + + if (class_exists('\webfiori\http\Response')) { + $this->useClassResponse($contentType, $asAttachment); + } else { + $this->doNotUseClassResponse($contentType, $asAttachment); + } + } + + /** + * + * @param string $fPath + * @param bool $append + * @param bool $encode + * @return bool + * @throws FileException + */ + private function writeHelper(string $fPath, bool $append, bool $encode = false) { + if (strlen($this->getRawData()) == 0) { + throw new FileException("No data is set to write."); + } + + if (!$this->isExist()) { + throw new FileException("File not found: '$fPath'."); + } else { + if ($append) { + $resource = $this->createResource('ab', $fPath); + } else { + $resource = $this->createResource('rb+', $fPath); + } + } + + if (!is_resource($resource)) { + throw new FileException('Unable to open the file at \''.$fPath.'\'.'); + } else { + fwrite($resource, $this->getRawData($encode)); + fclose($resource); + + return true; + } + } } diff --git a/webfiori/file/FileUploader.php b/webfiori/file/FileUploader.php index f793040..6c4ffcc 100644 --- a/webfiori/file/FileUploader.php +++ b/webfiori/file/FileUploader.php @@ -2,7 +2,6 @@ namespace webfiori\file; use webfiori\file\exceptions\FileException; -use webfiori\file\MIME; use webfiori\json\Json; use webfiori\json\JsonI; /** @@ -96,7 +95,7 @@ public function __construct(string $uploadPath = '', array $allowedTypes = []) { $this->setAssociatedFileName('files'); $this->addExts($allowedTypes); $this->uploadDir = ''; - + if (strlen($uploadPath) != 0) { $this->setUploadDir($uploadPath); } @@ -260,17 +259,16 @@ public function removeExt(string $ext) : bool { $retVal = false; $temp = []; $ext = str_replace('.', '', $ext); - + for ($x = 0 ; $x < $count ; $x++) { - if ($exts[$x] != $ext) { - $temp[] = $exts[$x]; + $temp[] = $exts[$x]; } else { $retVal = true; } } $this->extentions = $temp; - + return $retVal; } /** @@ -291,6 +289,7 @@ public function removeExt(string $ext) : bool { */ public function setAssociatedFileName(string $name) { $trimmed = trim($name); + if (strlen($trimmed) != 0) { $this->asscociatedName = $trimmed; } @@ -311,10 +310,10 @@ public function setAssociatedFileName(string $name) { * @since 1.0 */ public function setUploadDir(string $dir) { - $fixedPath = File::fixPath($dir); - + $dir = str_replace('/', '\\', $fixedPath); + if (strlen($dir) == 0) { throw new FileException('Upload directory should not be an empty string.'); } @@ -325,9 +324,8 @@ public function setUploadDir(string $dir) { throw new FileException('Invalid upload directory: '.$this->uploadDir); } } catch (FileException $ex) { - throw new FileException('Invalid upload directory: '.$dir); + throw new FileException('Invalid upload directory: '.$dir); } - } /** * Returns a JSON representation of the object. @@ -376,11 +374,11 @@ public function toJSON() : Json { */ public function upload(bool $replaceIfExist = false) : array { $this->files = []; - + if (strlen($this->getUploadDir()) == 0) { throw new FileException('Upload path is not set.'); } - + $meth = getenv('REQUEST_METHOD'); if ($meth === false) { @@ -444,7 +442,7 @@ public function uploadAsFileObj(bool $replaceIfExist = false) : array { private function createFileObjFromArray($arr) : UploadedFile { $fName = filter_var($arr[UploaderConst::NAME_INDEX], FILTER_SANITIZE_FULL_SPECIAL_CHARS); $fPath = $arr[UploaderConst::PATH_INDEX]; - + $file = new UploadedFile($fName, $fPath); $file->setMIME($arr[UploaderConst::MIME_INDEX]); @@ -457,7 +455,6 @@ private function createFileObjFromArray($arr) : UploadedFile { return $file; } private function getFileArr($fileOrFiles,$replaceIfExist, $idx = null): array { - $errIdx = 'error'; $tempIdx = 'tmp_name'; $fileInfoArr = []; @@ -580,7 +577,7 @@ private function isError(int $code): bool { * * @since 1.0 */ - private function isValidExt(string $fileName) : bool{ + private function isValidExt(string $fileName) : bool { $ext = pathinfo($fileName, PATHINFO_EXTENSION); return in_array($ext, $this->getExts(),true) || in_array(strtolower($ext), $this->getExts(),true); diff --git a/webfiori/file/UploaderConst.php b/webfiori/file/UploaderConst.php index 2d462f1..d7afa44 100644 --- a/webfiori/file/UploaderConst.php +++ b/webfiori/file/UploaderConst.php @@ -8,38 +8,38 @@ */ class UploaderConst { /** - * A constant that is used to indicate upload directory does not exist. - */ - const ERR_NO_SUCH_DIR = 'no_such_dir'; - /** - * A constant that is used to indicate uploaded file type is not allowed. + * One of the constants which is used to initialize uploaded file array. */ - const ERR_NOT_ALLOWED = 'not_allowed_type'; + const ERR_INDEX = 'upload-error'; /** * A constant that is used to indicate that original file was not created * from temporary uploaded file. */ const ERR_MOVE_TEMP = 'temp_file_not_moved'; /** - * One of the constants which is used to initialize uploaded file array. + * A constant that is used to indicate upload directory does not exist. */ - const NAME_INDEX = 'name'; + const ERR_NO_SUCH_DIR = 'no_such_dir'; + /** + * A constant that is used to indicate uploaded file type is not allowed. + */ + const ERR_NOT_ALLOWED = 'not_allowed_type'; /** * One of the constants which is used to initialize uploaded file array. */ - const SIZE_INDEX = 'size'; + const EXIST_INDEX = 'is-exist'; /** * One of the constants which is used to initialize uploaded file array. */ - const PATH_INDEX = 'upload-path'; + const MIME_INDEX = 'mime'; /** * One of the constants which is used to initialize uploaded file array. */ - const ERR_INDEX = 'upload-error'; + const NAME_INDEX = 'name'; /** * One of the constants which is used to initialize uploaded file array. */ - const EXIST_INDEX = 'is-exist'; + const PATH_INDEX = 'upload-path'; /** * One of the constants which is used to initialize uploaded file array. */ @@ -47,7 +47,7 @@ class UploaderConst { /** * One of the constants which is used to initialize uploaded file array. */ - const MIME_INDEX = 'mime'; + const SIZE_INDEX = 'size'; /** * One of the constants which is used to initialize uploaded file array. */