From f12ac036293e85a3d83a05cabdbb02c012c9afbe Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 16 Sep 2018 02:21:13 +0200 Subject: [PATCH] and also au revoir to all object trashy wrappings --- build.php | 2 +- misuzu.php | 4 +- public/profile.php | 18 ++- public/settings.php | 2 +- src/Application.php | 47 +----- src/IO/Directory.php | 183 ---------------------- src/IO/DirectoryDoesNotExistException.php | 11 -- src/IO/DirectoryExistsException.php | 11 -- src/IO/File.php | 68 -------- src/IO/FileDoesNotExistException.php | 11 -- src/IO/FileExistsException.php | 11 -- src/IO/IOException.php | 11 -- src/Users/user.php | 14 +- utility.php | 61 ++++++++ 14 files changed, 92 insertions(+), 362 deletions(-) delete mode 100644 src/IO/Directory.php delete mode 100644 src/IO/DirectoryDoesNotExistException.php delete mode 100644 src/IO/DirectoryExistsException.php delete mode 100644 src/IO/File.php delete mode 100644 src/IO/FileDoesNotExistException.php delete mode 100644 src/IO/FileExistsException.php delete mode 100644 src/IO/IOException.php diff --git a/build.php b/build.php index 6633e277..43cc4fbd 100644 --- a/build.php +++ b/build.php @@ -71,7 +71,7 @@ function deleteAllFilesInDir(string $dir, string $pattern): void $files = globDir($dir, $pattern); foreach ($files as $file) { - unlink($file); + safe_delete($file); misuzu_log("Deleted '{$file}'"); } } diff --git a/misuzu.php b/misuzu.php index 964cf30b..f63600bc 100644 --- a/misuzu.php +++ b/misuzu.php @@ -211,9 +211,7 @@ MIG; // we're running this again because ob_clean breaks gzip otherwise ob_start(); - $storage_dir = $app->getStoragePath(); - if (!$storage_dir->isReadable() - || !$storage_dir->isWritable()) { + if (!$app->canAccessStorage()) { echo 'Cannot access storage directory.'; exit; } diff --git a/public/profile.php b/public/profile.php index 3274661f..d4eb96b5 100644 --- a/public/profile.php +++ b/public/profile.php @@ -11,18 +11,22 @@ switch ($mode) { case 'avatar': $avatar_filename = $app->getDefaultAvatar(); $user_avatar = "{$user_id}.msz"; - $cropped_avatar = $app->getStore('avatars/200x200')->filename($user_avatar); + $cropped_avatar = build_path( + create_directory(build_path($app->getStoragePath(), 'avatars/200x200')), + $user_avatar + ); - if (File::exists($cropped_avatar)) { + if (is_file($cropped_avatar)) { $avatar_filename = $cropped_avatar; } else { - $original_avatar = $app->getStore('avatars/original')->filename($user_avatar); + $original_avatar = build_path($app->getStoragePath(), 'avatars/original', $user_avatar); - if (File::exists($original_avatar)) { + if (is_file($original_avatar)) { try { - File::writeAll( + file_put_contents( $cropped_avatar, - crop_image_centred_path($original_avatar, 200, 200)->getImagesBlob() + crop_image_centred_path($original_avatar, 200, 200)->getImagesBlob(), + LOCK_EX ); $avatar_filename = $cropped_avatar; @@ -32,7 +36,7 @@ switch ($mode) { } header('Content-Type: ' . mime_content_type($avatar_filename)); - echo File::readToEnd($avatar_filename); + echo file_get_contents($avatar_filename); break; case 'view': diff --git a/public/settings.php b/public/settings.php index e773a419..d4d0246e 100644 --- a/public/settings.php +++ b/public/settings.php @@ -303,7 +303,7 @@ switch ($settingsMode) { '); $getMail->bindValue('user_id', $app->getUserId()); $currentEmail = $getMail->execute() ? $getMail->fetchColumn() : 'Failed to fetch e-mail address.'; - $userHasAvatar = File::exists($app->getStore('avatars/original')->filename($avatarFileName)); + $userHasAvatar = is_file(build_path($app->getStoragePath(), 'avatars/original', $avatarFileName)); tpl_vars([ 'avatar_user_id' => $app->getUserId(), diff --git a/src/Application.php b/src/Application.php index 95e57b11..2332d6ed 100644 --- a/src/Application.php +++ b/src/Application.php @@ -2,8 +2,6 @@ namespace Misuzu; use Carbon\Carbon; -use Misuzu\IO\Directory; -use Misuzu\IO\DirectoryDoesNotExistException; use Misuzu\Users\Session; use UnexpectedValueException; use InvalidArgumentException; @@ -121,51 +119,22 @@ final class Application $path = __DIR__ . '/../' . $path; } - return Directory::fixSlashes(rtrim($path, '/')); + return fix_path_separator(rtrim($path, '/')); } /** - * Gets a data storage path, with config storage path prefix. - * @param string $append - * @return Directory - * @throws DirectoryDoesNotExistException - * @throws IO\DirectoryExistsException + * Gets a data storage path. + * @return string */ - public function getStoragePath(string $append = ''): Directory + public function getStoragePath(): string { - if (starts_with($append, '/')) { - $path = $append; - } else { - $path = $this->config['Storage']['path'] ?? __DIR__ . '/../store'; - - if (!empty($append)) { - $path .= '/' . $append; - } - } - - return Directory::createOrOpen($this->getPath($path)); + return create_directory($this->config['Storage']['path'] ?? __DIR__ . '/../store'); } - /** - * Gets a data store, with config overrides! - * @param string $purpose - * @return Directory - * @throws DirectoryDoesNotExistException - * @throws IO\DirectoryExistsException - */ - public function getStore(string $purpose): Directory + public function canAccessStorage(): bool { - $override_key = 'override_' . str_replace('/', '_', $purpose); - - if (array_key_exists('Storage', $this->config)) { - try { - return new Directory($this->config['Storage'][$override_key] ?? ''); - } catch (DirectoryDoesNotExistException $ex) { - // fall through and just get the default path. - } - } - - return $this->getStoragePath($purpose); + $path = $this->getStoragePath(); + return is_readable($path) && is_writable($path); } /** diff --git a/src/IO/Directory.php b/src/IO/Directory.php deleted file mode 100644 index 2027f8fd..00000000 --- a/src/IO/Directory.php +++ /dev/null @@ -1,183 +0,0 @@ - - */ -class Directory -{ - /** - * Path to this directory. - * @var string - */ - private $path; - - /** - * Directory separator used on this system, usually either \ for Windows or / for basically everything else. - */ - public const SEPARATOR = DIRECTORY_SEPARATOR; - - /** - * Get the path of this directory. - * @return string - */ - public function getPath(): string - { - return $this->path; - } - - /** - * @return bool - */ - public function isReadable(): bool - { - return is_readable($this->getPath()); - } - - /** - * @return bool - */ - public function isWritable(): bool - { - return is_writable($this->getPath()); - } - - /** - * Fixes the path, sets proper slashes and checks if the directory exists. - * @param string $path - * @throws DirectoryDoesNotExistException - */ - public function __construct(string $path) - { - $path = static::fixSlashes(rtrim($path, '/\\')); - - if (!static::exists($path)) { - throw new DirectoryDoesNotExistException; - } - - $this->path = realpath($path); - } - - /** - * Gets contents of this directory, subdirs get their own instance. - * @param string $pattern - * @return array - */ - public function files(string $pattern = '*'): array - { - return array_map(function ($path) { - if (static::exists($path)) { - return new static($path); - } - - return realpath($path); - }, glob($this->path . self::SEPARATOR . $pattern)); - } - - public function filename(string $filename): string - { - return $this->getPath() . self::SEPARATOR . $filename; - } - - /** - * Creates a directory if it doesn't already exist. - * @param string $path - * @return Directory - * @throws DirectoryDoesNotExistException - * @throws DirectoryExistsException - */ - public static function create(string $path): Directory - { - if (static::exists($path)) { - throw new DirectoryExistsException; - } - - $on_windows = running_on_windows(); - $path = Directory::fixSlashes($path); - $split_path = explode(self::SEPARATOR, $path); - $existing_path = $on_windows ? '' : self::SEPARATOR; - - foreach ($split_path as $path_part) { - $existing_path .= $path_part . self::SEPARATOR; - - if ($on_windows && mb_substr($path_part, 1, 2) === ':\\') { - continue; - } - - if (!Directory::exists($existing_path)) { - mkdir($existing_path); - } - } - - return new static($path); - } - - /** - * @param string $path - * @return Directory - * @throws DirectoryDoesNotExistException - * @throws DirectoryExistsException - */ - public static function createOrOpen(string $path): Directory - { - if (static::exists($path)) { - return new Directory($path); - } else { - return Directory::create($path); - } - } - - /** - * Deletes a directory, recursively if requested. Use $purge with care! - * @param string $path - * @param bool $purge - * @throws DirectoryDoesNotExistException - * @throws FileDoesNotExistException - */ - public static function delete(string $path, bool $purge = false): void - { - $path = static::fixSlashes($path); - - if (!static::exists($path)) { - throw new DirectoryDoesNotExistException; - } - - if ($purge) { - $dir = new static($path); - - foreach ($dir->files() as $file) { - if ($file instanceof self) { - static::delete($file->path, true); - } else { - File::delete($file); - } - } - } - - rmdir($path); - } - - /** - * Checks if a directory exists. - * @param string $path - * @return bool - */ - public static function exists(string $path): bool - { - $path = static::fixSlashes($path); - return file_exists($path) && is_dir($path); - } - - /** - * Fixes operating system specific slashing. - * @param string $path - * @param string $separator - * @return string - */ - public static function fixSlashes(string $path, string $separator = self::SEPARATOR): string - { - return str_replace(['/', '\\'], $separator, $path); - } -} diff --git a/src/IO/DirectoryDoesNotExistException.php b/src/IO/DirectoryDoesNotExistException.php deleted file mode 100644 index c8cc41f0..00000000 --- a/src/IO/DirectoryDoesNotExistException.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -class DirectoryDoesNotExistException extends IOException -{ -} diff --git a/src/IO/DirectoryExistsException.php b/src/IO/DirectoryExistsException.php deleted file mode 100644 index 77ec3409..00000000 --- a/src/IO/DirectoryExistsException.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -class DirectoryExistsException extends IOException -{ -} diff --git a/src/IO/File.php b/src/IO/File.php deleted file mode 100644 index a631f071..00000000 --- a/src/IO/File.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ -class File -{ - /** - * @param string $filename - * @param bool $lock - * @return string - */ - public static function readToEnd(string $filename, bool $lock = false): string - { - $lock = file_get_contents($filename); // reusing $lock bc otherwise sublime yells about unused vars - return $lock === false ? $lock : ''; - } - - /** - * @param string $filename - * @param string $data - * @throws FileDoesNotExistException - * @throws IOException - */ - public static function writeAll(string $filename, string $data): void - { - file_put_contents($filename, $data, LOCK_EX); - } - - /** - * Checks if a file exists. - * @param string $path - * @return bool - */ - public static function exists(string $path): bool - { - $path = realpath(Directory::fixSlashes($path)); - return file_exists($path) && is_file($path); - } - - /** - * Deletes a file permanently, use with care! - * @param string $path - * @throws FileDoesNotExistException - */ - public static function delete(string $path): void - { - $path = realpath(Directory::fixSlashes($path)); - - if (!is_string($path) || !static::exists($path)) { - throw new FileDoesNotExistException; - } - - unlink($path); - } - - public static function safeDelete(string $path): void - { - if (self::exists($path)) { - self::delete($path); - } - } -} diff --git a/src/IO/FileDoesNotExistException.php b/src/IO/FileDoesNotExistException.php deleted file mode 100644 index 18c34dca..00000000 --- a/src/IO/FileDoesNotExistException.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -class FileDoesNotExistException extends IOException -{ -} diff --git a/src/IO/FileExistsException.php b/src/IO/FileExistsException.php deleted file mode 100644 index bcb17ba7..00000000 --- a/src/IO/FileExistsException.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -class FileExistsException extends IOException -{ -} diff --git a/src/IO/IOException.php b/src/IO/IOException.php deleted file mode 100644 index 3dc1bb23..00000000 --- a/src/IO/IOException.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -class IOException extends \Exception -{ -} diff --git a/src/Users/user.php b/src/Users/user.php index 9a5afb57..129ed31c 100644 --- a/src/Users/user.php +++ b/src/Users/user.php @@ -61,14 +61,15 @@ function user_avatar_delete(int $userId): void { $app = Application::getInstance(); $avatarFileName = sprintf(MSZ_USER_AVATAR_FORMAT, $userId); + $storePath = $app->getStoragePath(); $deleteThis = [ - $app->getStore('avatars/original')->filename($avatarFileName), - $app->getStore('avatars/200x200')->filename($avatarFileName), + build_path($storePath, 'avatars/original', $avatarFileName), + build_path($storePath, 'avatars/200x200', $avatarFileName), ]; foreach ($deleteThis as $deleteAvatar) { - File::safeDelete($deleteAvatar); + safe_delete($deleteAvatar); } } @@ -135,7 +136,10 @@ function user_avatar_set_from_path(int $userId, string $path, array $options = [ user_avatar_delete($userId); $fileName = sprintf(MSZ_USER_AVATAR_FORMAT, $userId); - $avatarPath = Application::getInstance()->getStore('avatars/original')->filename($fileName); + $avatarPath = build_path( + create_directory(build_path(Application::getInstance()->getStoragePath(), 'avatars/original')), + $fileName + ); if (!copy($path, $avatarPath)) { return MSZ_USER_AVATAR_ERROR_STORE_FAILED; @@ -155,7 +159,7 @@ function user_avatar_set_from_data(int $userId, string $data, array $options = [ chmod($tmp, 644); file_put_contents($tmp, $data); $result = user_avatar_set_from_path($userId, $tmp, $options); - unlink($tmp); + safe_delete($tmp); return $result; } diff --git a/utility.php b/utility.php index 28662a20..6bc633e2 100644 --- a/utility.php +++ b/utility.php @@ -38,6 +38,67 @@ function password_entropy(string $password): int return count(count_chars(utf8_decode($password), 1)) * 8; } +function fix_path_separator(string $path, string $separator = DIRECTORY_SEPARATOR, array $separators = ['/', '\\']): string +{ + return str_replace($separators, $separator, rtrim($path, implode($separators))); +} + +function safe_delete(string $path): void +{ + $path = realpath($path); + + if (empty($path)) { + return; + } + + if (is_dir($path)) { + rmdir($path); + return; + } + + unlink($path); +} + +// mkdir + recursion +function create_directory(string $path): string +{ + if (is_file($path)) { + return ''; + } + + if (is_dir($path)) { + return realpath($path); + } + + $on_windows = running_on_windows(); + $path = fix_path_separator($path); + $split_path = explode(DIRECTORY_SEPARATOR, $path); + $existing_path = $on_windows ? '' : DIRECTORY_SEPARATOR; + + foreach ($split_path as $path_part) { + $existing_path .= $path_part . DIRECTORY_SEPARATOR; + + if ($on_windows && mb_substr($path_part, 1, 2) === ':\\') { + continue; + } + + if (!file_exists($existing_path)) { + mkdir($existing_path); + } + } + + return ($path = realpath($path)) === false ? '' : $path; +} + +function build_path(string ...$path): string +{ + for ($i = 0; $i < count($path); $i++) { + $path[$i] = fix_path_separator($path[$i]); + } + + return implode(DIRECTORY_SEPARATOR, $path); +} + function check_mx_record(string $email): bool { $domain = mb_substr(mb_strstr($email, '@'), 1);