and also au revoir to all object trashy wrappings

This commit is contained in:
flash 2018-09-16 02:21:13 +02:00
parent 5db6d59a49
commit f12ac03629
14 changed files with 92 additions and 362 deletions

View file

@ -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}'");
}
}

View file

@ -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;
}

View file

@ -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':

View file

@ -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(),

View file

@ -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);
}
/**

View file

@ -1,183 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Directory container.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
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);
}
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Thrown when a directory doesn't exist.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class DirectoryDoesNotExistException extends IOException
{
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Thrown when a folder already exists.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class DirectoryExistsException extends IOException
{
}

View file

@ -1,68 +0,0 @@
<?php
namespace Misuzu\IO;
use Exception;
/**
* Static file meta functions.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
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);
}
}
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Thrown when a file doesn't exist.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class FileDoesNotExistException extends IOException
{
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Thrown when a file already exists.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class FileExistsException extends IOException
{
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Holds the base I/O exception.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class IOException extends \Exception
{
}

View file

@ -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;
}

View file

@ -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);