Import old Aitemu stuff.

This commit is contained in:
flash 2017-12-19 09:11:55 +01:00
parent 69f2604b5b
commit 4cc0d69351
14 changed files with 945 additions and 0 deletions

View file

@ -9,6 +9,7 @@
"require": {
"php": ">=7.2",
"flashwave/aitemu": "dev-master#ac3050d17e3aa31a088caf15469bb51e5b165d40",
"twig/twig": "~2.4",
"nesbot/carbon": "~1.22",
"illuminate/database": "~5.5",
"illuminate/filesystem": "~5.5",

115
src/IO/Directory.php Normal file
View file

@ -0,0 +1,115 @@
<?php
namespace Misuzu\IO;
/**
* Directory container.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class Directory
{
/**
* Path to this directory.
* @var string
*/
public $path;
/**
* Fixes the path, sets proper slashes and checks if the directory exists.
* @param string $path
* @throws DirectoryDoesNotExistException
*/
public function __construct(string $path)
{
$this->path = static::fixSlashes(rtrim($path, '/\\'));
if (!static::exists($this->path)) {
throw new DirectoryDoesNotExistException;
}
}
/**
* 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 . '/' . $pattern));
}
/**
* Creates a directory if it doesn't already exist.
* @param string $path
* @throws DirectoryExistsException
* @return Directory
*/
public static function create(string $path): Directory
{
$path = static::fixSlashes($path);
if (static::exists($path)) {
throw new DirectoryExistsException;
}
mkdir($path);
return new static($path);
}
/**
* Deletes a directory, recursively if requested. Use $purge with care!
* @param string $path
* @param bool $purge
* @throws DirectoryDoesNotExistException
*/
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
* @return string
*/
public static function fixSlashes(string $path): string
{
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
}
}

View file

@ -0,0 +1,11 @@
<?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

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

333
src/IO/File.php Normal file
View file

@ -0,0 +1,333 @@
<?php
namespace Misuzu\IO;
use ErrorException;
/**
* Wrapper for f* (file stream) functions and other cool stuff.
* @package Misuzu\IO
* @author flashwave <me@flash.moe>
*/
class File
{
/**
* Open + Read flag.
*/
public const OPEN_READ = 'rb';
/**
* Open + Write flag.
*/
public const OPEN_WRITE = 'xb';
/**
* Open + Read + Write flag.
*/
public const OPEN_READ_WRITE = 'rb+';
/**
* Create (truncates!) + Write flag.
*/
public const CREATE_WRITE = 'wb';
/**
* Create (truncates!) + Read + Write flag.
*/
public const CREATE_READ_WRITE = 'wb+';
/**
* Open + Write flag.
*/
public const OPEN_CREATE_WRITE = 'cb';
/**
* Open or Create + Read + Write flag.
*/
public const OPEN_CREATE_READ_WRITE = 'cb+';
/**
* Resource/stream container.
* @var resource
*/
private $resource;
/**
* Filename.
* @var string
*/
public $name = '';
/**
* Real, fixed path.
* @var string
*/
public $path = '';
/**
* Filesize in bytes.
* @var int
*/
public $size = 0;
/**
* ID of file owner.
* @var int
*/
public $owner = 0;
/**
* ID of file's owning group.
* @var int
*/
public $group = 0;
/**
* Last time this file has been accessed.
* @var int
*/
public $accessTime = 0;
/**
* Last time this file has been modified.
* @var int
*/
public $modifiedTime = 0;
/**
* Current stream position.
* @var int
*/
public $position = 0;
/**
* Fixes path and opens resource.
* @param string $path
* @param string $mode
* @throws FileDoesNotExistException
* @throws IOException
*/
public function __construct(string $path, string $mode)
{
$path = Directory::fixSlashes($path);
$this->path = realpath($path);
$this->name = basename($this->path);
try {
$this->resource = fopen($path, $mode, false);
} catch (ErrorException $e) {
throw new FileDoesNotExistException($e->getMessage());
}
if (!is_resource($this->resource)) {
throw new IOException('Failed to create resource.');
}
$this->updateMetaData();
}
/**
* Updates the meta data of the resource.
*/
private function updateMetaData(): void
{
$meta = fstat($this->resource);
$this->size = intval($meta['size']);
$this->owner = intval($meta['uid']);
$this->group = intval($meta['gid']);
$this->accessTime = intval($meta['atime']);
$this->modifiedTime = intval($meta['mtime']);
}
/**
* Updates the position variable.
*/
private function updatePosition(): void
{
$this->position = ftell($this->resource);
}
/**
* Calls the close method.
*/
public function __destruct()
{
$this->close();
}
/**
* Creates an instance of a temporary file.
* @param string $prefix
* @return File
*/
public static function temp(string $prefix = 'Misuzu'): File
{
return new static(tempnam(sys_get_temp_dir(), $prefix));
}
/**
* 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);
}
/**
* Closes the resource context.
*/
public function close(): void
{
if (is_resource($this->resource)) {
fclose($this->resource);
}
}
/**
* Moves the position to 0.
*/
public function start(): void
{
rewind($this->resource);
$this->updatePosition();
}
/**
* Moves the position to the end of the file.
*/
public function end(): void
{
fseek($this->resource, 0, SEEK_END);
$this->updatePosition();
}
/**
* Sets the position.
* @param int $offset
* @param bool $relative
* @return int
*/
public function position(int $offset, bool $relative = false): int
{
fseek($this->resource, $offset, $relative ? SEEK_CUR : SEEK_SET);
$this->updatePosition();
return $this->position;
}
/**
* Checks if the current position is the end of the file.
* @return bool
*/
public function atEOF(): bool
{
return feof($this->resource);
}
/**
* Tries to find the position of a string in the context.
* @param string $string
* @return int
*/
public function find(string $string): int
{
while ($this->position < $this->size) {
$find = strpos($this->read(8192), $string);
if ($find !== false) {
return $find + $this->position;
}
$this->position($this->position + 8192);
}
return -1;
}
/**
* Locks the file and reads from it.
* @param int $length
* @throws IOException
* @return string
*/
public function read(int $length = null): string
{
if ($length === null) {
$length = $this->size;
$this->start();
}
flock($this->resource, LOCK_SH);
$data = fread($this->resource, $length);
flock($this->resource, LOCK_UN);
if ($data === false) {
throw new IOException('Read failed.');
}
$this->updateMetaData();
return $data;
}
/**
* Gets the character at the current position.
* @return string
*/
public function char(): string
{
return fgetc($this->resource);
}
/**
* Locks the file, writes to the stream and flushes to file.
* @param string $data
* @param int $length
* @throws IOException
*/
public function write(string $data, int $length = 0): void
{
if ($length > 0) {
$length = strlen($data);
}
flock($this->resource, LOCK_EX);
$write = fwrite($this->resource, $data, $length);
$flush = fflush($this->resource);
flock($this->resource, LOCK_UN);
if ($write === false || $flush === false) {
throw new IOException('Write failed.');
}
$this->updateMetaData();
}
/**
* The same as write except it moves to the end of the file first.
* @param string $data
* @param int $length
*/
public function append(string $data, int $length = 0): void
{
$this->end();
$this->write($data, $length);
}
}

View file

@ -0,0 +1,11 @@
<?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

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

11
src/IO/IOException.php Normal file
View file

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

98
src/Net/CIDR.php Normal file
View file

@ -0,0 +1,98 @@
<?php
namespace Misuzu\Net;
/**
* CIDR functions.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class CIDR
{
/**
* Matches an IP to a CIDR range.
* @param string $ip
* @param string $range
* @return bool
*/
public static function match($ip, $range)
{
[$net, $mask] = explode('/', $range);
$ipv = IP::version($ip);
$rangev = IP::version($net);
if (!$ipv || !$rangev || $ipv !== $rangev) {
return false;
}
switch ($ipv) {
case IP::V6:
return static::matchV6($ip, $net, $mask);
case IP::V4:
return static::matchV4($ip, $net, $mask);
default:
return false;
}
}
/**
* Matches an IPv4 to a CIDR range.
* @param string $ip
* @param string $net
* @param int $mask
* @return bool
*/
private static function matchV4($ip, $net, $mask)
{
$ip = ip2long($ip);
$net = ip2long($net);
$mask = -1 << (32 - $mask);
return ($ip & $mask) === $net;
}
/**
* Matches an IPv6 to a CIDR range.
* @param string $ip
* @param string $net
* @param int $mask
* @return bool
*/
private static function matchV6($ip, $net, $mask)
{
$ip = inet_pton($ip);
$net = inet_pton($net);
$mask = static::createV6Mask($mask);
return ($ip & $mask) === $net;
}
/**
* Converts an IPv6 mask to bytes.
* @param int $mask
* @return int
*/
private static function createV6Mask($mask)
{
$range = str_repeat("f", $mask / 4);
switch ($mask % 4) {
case 1:
$range .= '8';
break;
case 2:
$range .= 'c';
break;
case 3:
$range .= 'e';
break;
}
$range = str_pad($range, 32, '0');
$range = pack('H*', $range);
return $range;
}
}

82
src/Net/IP.php Normal file
View file

@ -0,0 +1,82 @@
<?php
namespace Misuzu\Net;
/**
* IP functions.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class IP
{
public const V4 = 4;
public const V6 = 6;
/**
* Attempts to get the remote ip address, falls back to IPv6 localhost.
* @return string
*/
public static function remote(string $fallback = '::1'): string
{
return $_SERVER['REMOTE_ADDR'] ?? $fallback;
}
/**
* Detects IP version.
* @param string $ip
* @return int
*/
public static function version(string $ip): int
{
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
return 0;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return static::V6;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return static::V4;
}
return 0;
}
/**
* Converts a printable IP address into an packed binary string.
* @param string $ip
* @throws NetInvalidAddressException
* @return string
*/
public static function unpack($ip)
{
$ipv = static::version($ip);
if ($ipv === 6) {
return current(unpack('A16', inet_pton($ip)));
}
if ($ipv === 4) {
return current(unpack('A4', inet_pton($ip)));
}
throw new NetInvalidAddressException;
}
/**
* Converts a binary unpacked IP to a printable unpacked IP.
* @param string $bin
* @throws NetAddressTypeException
* @return string
*/
public static function pack($bin)
{
$len = strlen($bin);
if ($len !== 4 && $len !== 16) {
throw new NetAddressTypeException;
}
return inet_ntop(pack("A{$len}", $bin));
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace Misuzu\Net;
/**
* Thrown when Net doesn't know how to handle the IP type.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class NetAddressTypeException extends NetException
{
}

11
src/Net/NetException.php Normal file
View file

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

View file

@ -0,0 +1,11 @@
<?php
namespace Misuzu\Net;
/**
* Thrown when Net has to handle an invalid IP.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class NetInvalidAddressException extends NetException
{
}

228
src/TemplateEngine.php Normal file
View file

@ -0,0 +1,228 @@
<?php
namespace Misuzu;
use Twig_Environment;
use Twig_Loader_Filesystem;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
/**
* Wrapper for Twig.
* @package Misuzu
* @author flashwave <me@flash.moe>
*/
class TemplateEngine
{
/**
* Utility |filters().
*/
private const UTILITY_FILTERS = [
'json_decode',
//'byte_symbol',
];
/**
* Utility functions().
*/
private const UTILITY_FUNCTIONS = [
//'route',
//'config',
//'session_id',
];
/**
* Template file extension.
*/
private const FILE_EXTENSION = '.twig';
/**
* Container for twig.
* @var Twig_Environment
*/
private $twig;
private $loader;
/**
* Render arguments.
* @var array
*/
private $vars = [];
/**
* Creates the twig environment and registers the utility filters and functions.
*/
public function __construct()
{
$this->loader = new Twig_Loader_Filesystem;
$this->twig = new Twig_Environment($this->loader, [
'cache' => false,
'strict_variables' => true,
'auto_reload' => false,
'debug' => false,
]);
foreach (static::UTILITY_FILTERS as $filter) {
$this->addFilter($filter, $filter);
}
foreach (static::UTILITY_FUNCTIONS as $function) {
$this->addFunction($function, $function);
}
}
/**
* Toggles debug mode on or off.
* @param bool $mode
*/
public function debug(bool $mode): void
{
if ($this->twig->isDebug() === $mode) {
return;
}
if ($mode) {
$this->twig->enableDebug();
return;
}
$this->twig->disableDebug();
}
/**
* Toggles cache auto reloading on or off.
* @param bool $mode
*/
public function autoReload(bool $mode): void
{
if ($this->twig->isAutoReload() === $mode) {
return;
}
if ($mode) {
$this->twig->enableAutoReload();
return;
}
$this->twig->disableAutoReload();
}
/**
* Sets the cache path and alternatively turns it off.
* @param string $path
*/
public function cache(bool $path): void
{
$this->twig->setCache($path);
}
/**
* Adds a template path, first one is regarded as the master.
* @param string $name
* @param string $path
*/
public function addPath(string $name, string $path): void
{
if (count($this->loader->getPaths()) < 1) {
$this->loader->addPath($path);
}
$this->loader->addPath($path, $name);
}
/**
* Sets render vars.
* @param array $vars
*/
public function vars(array $vars): void
{
$this->vars = array_merge($this->vars, $vars);
}
/**
* Converts . to / and appends the file extension.
* @param string $path
* @return string
*/
private function fixPath(string $path): string
{
// if the .twig extension if already present just assume that the path is already correct
if (substr($path, 0 - strlen(static::FILE_EXTENSION)) === static::FILE_EXTENSION) {
return $path;
}
return str_replace('.', '/', $path) . static::FILE_EXTENSION;
}
/**
* Renders a template file.
* @param string $path
* @param array $vars
* @return string
*/
public function render(string $path, array $vars = null): string
{
$path = static::fixPath($path);
if ($vars !== null) {
$this->vars($vars);
}
if (!$this->exists($path, Twig_Loader_Filesystem::MAIN_NAMESPACE)) {
$namespace = $this->findNamespace($path);
if ($namespace !== null) {
$path = '@' . $this->findNamespace($path) . '/' . $path;
}
}
return $this->twig->render($path, $this->vars);
}
/**
* Adds a function.
* @param string $name
* @param Callable $callable
*/
public function addFunction(string $name, Callable $callable = null): void
{
$this->twig->addFunction(new Twig_SimpleFunction($name, $callable === null ? $name : $callable));
}
/**
* Adds a filter.
* @param string $name
* @param Callable $callable
*/
public function addFilter(string $name, Callable $callable = null): void
{
$this->twig->addFilter(new Twig_SimpleFilter($name, $callable === null ? $name : $callable));
}
/**
* Finds in which namespace a template exists.
* @param string $path
* @return string
*/
public function findNamespace(string $path): string
{
foreach ($this->loader->getNamespaces() as $namespace) {
if ($this->exists($path, $namespace)) {
return $namespace;
}
}
return null;
}
/**
* Checks if a template exists.
* @param string $path
* @param string $namespace
* @return bool
*/
public function exists(string $path, string $namespace): bool
{
return $this->loader->exists('@' . $namespace . '/' . static::fixPath($path));
}
}