Added Index library to Uiharu.
This commit is contained in:
parent
1ae7ec9f19
commit
0a668992d9
12 changed files with 217 additions and 548 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,5 +2,5 @@
|
|||
[Dd]esktop.ini
|
||||
.DS_Store
|
||||
/.debug
|
||||
/config.php
|
||||
/config.ini
|
||||
/public/robots.txt
|
||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "lib/index"]
|
||||
path = lib/index
|
||||
url = https://github.com/flashwave/index.git
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
namespace Database;
|
||||
|
||||
use PDO;
|
||||
|
||||
class Database {
|
||||
public $pdo;
|
||||
private $stmts = [];
|
||||
|
||||
public function __construct(string $dsn, string $username = '', string $password = '', array $options = []) {
|
||||
$this->pdo = new PDO($dsn, $username, $password, $options);
|
||||
}
|
||||
|
||||
public function getPDO(): PDO {
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
public function queries(): int {
|
||||
return (int)$this->query('SHOW SESSION STATUS LIKE "Questions"')->fetchColumn(1);
|
||||
}
|
||||
|
||||
public function exec(string $stmt): int {
|
||||
return $this->pdo->exec($stmt);
|
||||
}
|
||||
|
||||
public function prepare(string $stmt, array $options = []): DatabaseStatement {
|
||||
$encodedOptions = serialize($options);
|
||||
|
||||
if(empty($this->stmts[$stmt][$encodedOptions])) {
|
||||
$this->stmts[$stmt][$encodedOptions] = $this->pdo->prepare($stmt, $options);
|
||||
}
|
||||
|
||||
return new DatabaseStatement($this->stmts[$stmt][$encodedOptions], $this->pdo, false);
|
||||
}
|
||||
|
||||
public function query(string $stmt, ?int $fetchMode = null, ...$args): DatabaseStatement {
|
||||
if($fetchMode === null) {
|
||||
$pdoStmt = $this->pdo->query($stmt);
|
||||
} else {
|
||||
$pdoStmt = $this->pdo->query($stmt, $fetchMode, ...$args);
|
||||
}
|
||||
|
||||
return new DatabaseStatement($pdoStmt, $this->pdo, true);
|
||||
}
|
||||
|
||||
public function lastId(): int {
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
namespace Database;
|
||||
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
|
||||
class DatabaseStatement {
|
||||
public $pdo;
|
||||
public $stmt;
|
||||
private $isQuery;
|
||||
|
||||
public function __construct(PDOStatement $stmt, PDO $pdo, bool $isQuery) {
|
||||
$this->stmt = $stmt;
|
||||
$this->pdo = $pdo;
|
||||
$this->isQuery = $isQuery;
|
||||
}
|
||||
|
||||
public function bind($param, $value, int $dataType = PDO::PARAM_STR): DatabaseStatement {
|
||||
$this->stmt->bindValue($param, $value, $dataType);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute(array $params = []): bool {
|
||||
return count($params) ? $this->stmt->execute($params) : $this->stmt->execute();
|
||||
}
|
||||
|
||||
public function executeGetId(array $params = []): int {
|
||||
return $this->execute($params) ? $this->pdo->lastInsertId() : 0;
|
||||
}
|
||||
|
||||
public function fetch($default = []) {
|
||||
$out = $this->isQuery || $this->execute() ? $this->stmt->fetch(PDO::FETCH_ASSOC) : false;
|
||||
return $out ? $out : $default;
|
||||
}
|
||||
|
||||
public function fetchAll($default = []) {
|
||||
$out = $this->isQuery || $this->execute() ? $this->stmt->fetchAll(PDO::FETCH_ASSOC) : false;
|
||||
return $out ? $out : $default;
|
||||
}
|
||||
|
||||
public function fetchColumn(int $num = 0, $default = null) {
|
||||
$out = $this->isQuery || $this->execute() ? $this->stmt->fetchColumn($num) : false;
|
||||
return $out ? $out : $default;
|
||||
}
|
||||
|
||||
public function fetchObject(string $className = 'stdClass', ?array $args = null, $default = null) {
|
||||
$out = false;
|
||||
|
||||
if($this->isQuery || $this->execute()) {
|
||||
$out = $args === null ? $this->stmt->fetchObject($className) : $this->stmt->fetchObject($className, $args);
|
||||
}
|
||||
|
||||
return $out !== false ? $out : $default;
|
||||
}
|
||||
|
||||
public function fetchObjects(string $className = 'stdClass', ?array $args = null): array {
|
||||
$objects = [];
|
||||
|
||||
if($this->isQuery || $this->execute()) {
|
||||
while(($object = ($args === null ? $this->stmt->fetchObject($className) : $this->stmt->fetchObject($className, $args))) !== false) {
|
||||
$objects[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
}
|
24
lib/db.php
24
lib/db.php
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
use Database\Database;
|
||||
|
||||
final class DB {
|
||||
private static $instance;
|
||||
|
||||
public const ATTRS = [
|
||||
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET SESSION time_zone = \'+00:00\''
|
||||
. ', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'',
|
||||
];
|
||||
|
||||
public static function init(...$args) {
|
||||
self::$instance = new Database(...$args);
|
||||
}
|
||||
|
||||
public static function __callStatic(string $name, array $args) {
|
||||
return self::$instance->{$name}(...$args);
|
||||
}
|
||||
}
|
1
lib/index
Submodule
1
lib/index
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 9d99e10541cf8ba7407be0b53323cfa3ebfc37f5
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
class MediaType implements JsonSerializable {
|
||||
private $type = '';
|
||||
private $subtype = '';
|
||||
private $suffix = '';
|
||||
private $params = [];
|
||||
|
||||
public function __construct(string $mediaType) {
|
||||
if(preg_match('#^([A-Za-z0-9\!\#\$%&\'\*\+\.\-_\{\}\|]+)/([A-Za-z0-9\!\#\$%&\'\*\+\.\-_\{\}\|]+)(?: ?; ?([A-Za-z0-9\!\#\$%&\'\*\+\.\-_\{\}\|\=; ]+))?$#', $mediaType, $matches) !== 1)
|
||||
throw new InvalidArgumentException('Invalid media type supplied.');
|
||||
|
||||
$this->type = $matches[1];
|
||||
|
||||
$subTypeSplit = explode('+', $matches[2], 2);
|
||||
$this->subtype = $subTypeSplit[0];
|
||||
if(isset($subTypeSplit[1]))
|
||||
$this->suffix = $subTypeSplit[1];
|
||||
|
||||
if(isset($matches[3])) {
|
||||
$params = explode(';', $matches[3]);
|
||||
foreach($params as $param) {
|
||||
$parts = explode('=', trim($param), 2);
|
||||
if(!isset($parts[1]))
|
||||
continue;
|
||||
$this->params[$parts[0]] = $parts[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getSubtype(): string {
|
||||
return $this->subtype;
|
||||
}
|
||||
|
||||
public function getSuffix(): string {
|
||||
return $this->subtype;
|
||||
}
|
||||
|
||||
public function getParams(): array {
|
||||
return $this->params;
|
||||
}
|
||||
public function getParam(string $name, int $filter = FILTER_DEFAULT, $options = null) {
|
||||
if(!isset($this->params[$name]))
|
||||
return null;
|
||||
return filter_var($this->params[$name], $filter, $options);
|
||||
}
|
||||
|
||||
public function getCharset(): string {
|
||||
return $this->getParam('charset', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) ?? 'utf-8';
|
||||
}
|
||||
public function getQuality(): float {
|
||||
return max(min(round($this->getParam('q', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) ?? 1, 2), 1), 0);
|
||||
}
|
||||
|
||||
public function match($other): bool {
|
||||
if(is_string($other))
|
||||
return $this->matchPattern($other);
|
||||
if($other instanceof self)
|
||||
return $this->matchType($other);
|
||||
return false;
|
||||
}
|
||||
public function matchPattern(string $pattern): bool {
|
||||
try {
|
||||
$mediaType = new static($pattern);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
return false;
|
||||
}
|
||||
return $this->matchType($mediaType);
|
||||
}
|
||||
public function matchType(MediaType $other): bool {
|
||||
return ($other->getType() === '*' && $other->getSubtype() === '*')
|
||||
|| (
|
||||
($other->getType() === $this->getType())
|
||||
&& ($other->getSubtype() === '*' || $other->getSubtype() === $this->getSubtype())
|
||||
);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$string = $this->type . '/';
|
||||
$string .= $this->subtype;
|
||||
if(!empty($this->suffix))
|
||||
$string .= '+' . $this->suffix;
|
||||
if(!empty($this->params))
|
||||
foreach($this->params as $key => $value) {
|
||||
$string .= ';';
|
||||
if(!empty($key))
|
||||
$string .= $key . '=';
|
||||
$string .= $value;
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): stdClass {
|
||||
$obj = new stdClass;
|
||||
$obj->string = (string)$this;
|
||||
$obj->type = $this->type;
|
||||
$obj->subtype = $this->subtype;
|
||||
if(!empty($this->suffix))
|
||||
$obj->suffix = $this->suffix;
|
||||
if(!empty($this->params))
|
||||
$obj->params = $this->params;
|
||||
return $obj;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
final class Stopwatch {
|
||||
private $startTime = 0;
|
||||
private $stopTime = 0;
|
||||
private $laps = [];
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public function __call(string $name, array $args) {
|
||||
if($name[0] === '_')
|
||||
return null;
|
||||
return $this->{'_' . $name}(...$args);
|
||||
}
|
||||
|
||||
public static function __callStatic(string $name, array $args) {
|
||||
if($name[0] === '_')
|
||||
return null;
|
||||
if(self::$instance === null)
|
||||
self::$instance = new static;
|
||||
return self::$instance->{'_' . $name}(...$args);
|
||||
}
|
||||
|
||||
private static function time() {
|
||||
return microtime(true);
|
||||
}
|
||||
|
||||
public function _start(): void {
|
||||
$this->startTime = self::time();
|
||||
}
|
||||
|
||||
public function _lap(string $text): void {
|
||||
$this->laps[$text] = self::time();
|
||||
}
|
||||
|
||||
public function _stop(): void {
|
||||
$this->stopTime = self::time();
|
||||
}
|
||||
|
||||
public function _reset(): void {
|
||||
$this->laps = [];
|
||||
$this->startTime = 0;
|
||||
$this->stopTime = 0;
|
||||
}
|
||||
|
||||
public function _elapsed(): float {
|
||||
return $this->stopTime - $this->startTime;
|
||||
}
|
||||
|
||||
public function _laps(): array {
|
||||
$laps = [];
|
||||
foreach($this->laps as $name => $time)
|
||||
$laps[$name] = $time - $this->startTime;
|
||||
return $laps;
|
||||
}
|
||||
}
|
182
lib/uri.php
182
lib/uri.php
|
@ -1,182 +0,0 @@
|
|||
<?php
|
||||
class Uri implements JsonSerializable {
|
||||
private $scheme = '';
|
||||
private $user = '';
|
||||
private $password = '';
|
||||
private $host = '';
|
||||
private $port = null;
|
||||
private $path = '';
|
||||
private $query = '';
|
||||
private $fragment = '';
|
||||
private $originalString = '';
|
||||
|
||||
public function __construct(string $uriString = '') {
|
||||
$this->originalString = $uriString;
|
||||
|
||||
if(!empty($uriString)) {
|
||||
$uri = parse_url($uriString);
|
||||
|
||||
if($uri === false)
|
||||
throw new InvalidArgumentException('URI cannot be parsed.');
|
||||
|
||||
$this->setScheme($uri['scheme'] ?? '');
|
||||
$this->setUserInfo($uri['user'] ?? '', $uri['pass'] ?? null);
|
||||
$this->setHost($uri['host'] ?? '');
|
||||
$this->setPort($uri['port'] ?? null);
|
||||
$this->setPath($uri['path'] ?? '');
|
||||
$this->setQuery($uri['query'] ?? '');
|
||||
$this->setFragment($uri['fragment'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
public function getHash(): string {
|
||||
return hash('sha256', (string)$this);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): stdClass {
|
||||
$obj = new stdClass;
|
||||
$obj->uri = (string)$this;
|
||||
if($this->scheme !== '')
|
||||
$obj->scheme = $this->scheme;
|
||||
if($this->user !== '')
|
||||
$obj->user = $this->user;
|
||||
if($this->password !== null)
|
||||
$obj->password = $this->password;
|
||||
if($this->host !== '')
|
||||
$obj->host = $this->host;
|
||||
if($this->port !== null)
|
||||
$obj->port = $this->port;
|
||||
if($this->path !== '')
|
||||
$obj->path = $this->path;
|
||||
if($this->query !== '')
|
||||
$obj->query = $this->query;
|
||||
if($this->fragment !== '')
|
||||
$obj->fragment = $this->fragment;
|
||||
//$obj->hash = $this->getHash();
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getOriginalString(): string {
|
||||
return $this->originalString;
|
||||
}
|
||||
|
||||
public function getScheme() {
|
||||
return $this->scheme;
|
||||
}
|
||||
public function setScheme(string $scheme): self {
|
||||
$this->scheme = $scheme;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthority() {
|
||||
$authority = '';
|
||||
|
||||
if(!empty($userInfo = $this->getUserInfo()))
|
||||
$authority .= $userInfo . '@';
|
||||
|
||||
$authority .= $this->getHost();
|
||||
|
||||
if(($port = $this->getPort()) !== null)
|
||||
$authority .= ':' . $port;
|
||||
|
||||
return $authority;
|
||||
}
|
||||
|
||||
public function getUserInfo() {
|
||||
$userInfo = $this->user;
|
||||
|
||||
if(!empty($this->password))
|
||||
$userInfo .= ':' . $this->password;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
public function setUserInfo(string $user, ?string $password = null): self {
|
||||
$this->user = $user;
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHost() {
|
||||
return $this->host;
|
||||
}
|
||||
public function setHost(string $host): self {
|
||||
$this->host = $host;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPort() {
|
||||
return $this->port;
|
||||
}
|
||||
public function setPort(?int $port): self {
|
||||
if($port !== null && ($port < 1 || $port > 0xFFFF))
|
||||
throw new InvalidArgumentException('Invalid port.');
|
||||
|
||||
$this->port = $port;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath() {
|
||||
return $this->path;
|
||||
}
|
||||
public function setPath(string $path): self {
|
||||
$this->path = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuery() {
|
||||
return $this->query;
|
||||
}
|
||||
public function setQuery(string $query): self {
|
||||
$this->query = $query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFragment() {
|
||||
return $this->fragment;
|
||||
}
|
||||
public function setFragment(string $fragment): self {
|
||||
$this->fragment = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$string = '';
|
||||
|
||||
if(!empty($scheme = $this->getScheme()))
|
||||
$string .= $scheme . ':';
|
||||
|
||||
$authority = $this->getAuthority();
|
||||
$hasAuthority = !empty($authority);
|
||||
|
||||
if($hasAuthority)
|
||||
$string .= '//' . $authority;
|
||||
|
||||
$path = $this->getPath();
|
||||
$hasPath = !empty($path);
|
||||
|
||||
if($hasAuthority && (!$hasPath || $path[0] !== '/'))
|
||||
$string .= '/';
|
||||
elseif(!$hasAuthority && $path[1] === '/')
|
||||
$path = '/' . trim($path, '/');
|
||||
|
||||
$string .= $path;
|
||||
|
||||
if(!empty($query = $this->getQuery())) {
|
||||
$string .= '?';
|
||||
$queryParts = explode('&', $query);
|
||||
foreach($queryParts as $queryPart) {
|
||||
$kvp = explode('=', $queryPart, 2);
|
||||
$string .= rawurlencode($kvp[0]);
|
||||
if(isset($kvp[1]))
|
||||
$string .= '=' . rawurlencode($kvp[1]);
|
||||
$string .= '&';
|
||||
}
|
||||
$string = substr($string, 0, -1);
|
||||
}
|
||||
|
||||
if(!empty($fragment = $this->getFragment()))
|
||||
$string .= '#' . rawurlencode($fragment);
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
210
public/index.php
210
public/index.php
|
@ -1,10 +1,100 @@
|
|||
<?php
|
||||
define('UIH_VERSION', '20201009');
|
||||
define('UIH_DEBUG', isset($_GET['_debug']));
|
||||
define('UIH_CACHE', !UIH_DEBUG && !isset($_GET['_skip']));
|
||||
namespace Uiharu;
|
||||
|
||||
use stdClass;
|
||||
use Index\MediaType;
|
||||
use Index\Performance\Stopwatch;
|
||||
|
||||
require_once __DIR__ . '/../uiharu.php';
|
||||
|
||||
define('UIH_CACHE', !UIH_DEBUG || isset($_GET['_cache']));
|
||||
define('UIH_INCLUDE_RAW', UIH_DEBUG || isset($_GET['include_raw']));
|
||||
define('UIH_SEM_NAME', 'U');
|
||||
define('UIH_SEM_PATH', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'uiharu');
|
||||
|
||||
function uih_media_type_json(MediaType $mediaType): array {
|
||||
$parts = [
|
||||
'string' => (string)$mediaType,
|
||||
'type' => $mediaType->getCategory(),
|
||||
'subtype' => $mediaType->getKind(),
|
||||
];
|
||||
|
||||
if(!empty($suffix = $mediaType->getSuffix()))
|
||||
$parts['suffix'] = $suffix;
|
||||
|
||||
if(!empty($params = $mediaType->getParams()))
|
||||
$parts['params'] = $params;
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
function uih_parse_url(string $url): array|false {
|
||||
$parts = parse_url($url);
|
||||
if($parts === false)
|
||||
return false;
|
||||
|
||||
// v1 compat
|
||||
$parts['uri'] = uih_build_url($parts);
|
||||
if(isset($parts['pass']))
|
||||
$parts['password'] = $parts['pass'];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
function uih_build_url(array $parts): string {
|
||||
$string = '';
|
||||
|
||||
if(!empty($parts['scheme']))
|
||||
$string .= $parts['scheme'] . ':';
|
||||
|
||||
$authority = '';
|
||||
|
||||
if(isset($parts['user']) || isset($parts['pass'])) {
|
||||
if(isset($parts['user']))
|
||||
$authority .= $parts['user'];
|
||||
if(isset($parts['pass']))
|
||||
$authority .= ':' . $parts['pass'];
|
||||
$authority .= '@';
|
||||
}
|
||||
|
||||
if(isset($parts['host'])) {
|
||||
$authority .= $parts['host'];
|
||||
if(isset($parts['port']))
|
||||
$authority .= ':' . $parts['port'];
|
||||
}
|
||||
|
||||
$hasAuthority = !empty($authority);
|
||||
if($hasAuthority)
|
||||
$string .= '//' . $authority;
|
||||
|
||||
$path = $parts['path'] ?? '';
|
||||
$hasPath = !empty($path);
|
||||
|
||||
if($hasAuthority && (!$hasPath || $path[0] !== '/'))
|
||||
$string .= '/';
|
||||
elseif(!$hasAuthority && $path[1] === '/')
|
||||
$path = '/' . trim($path, '/');
|
||||
|
||||
$string .= $path;
|
||||
|
||||
if(!empty($parts['query'])) {
|
||||
$string .= '?';
|
||||
$queryParts = explode('&', $parts['query']);
|
||||
|
||||
foreach($queryParts as $queryPart) {
|
||||
$kvp = explode('=', $queryPart, 2);
|
||||
$string .= rawurlencode($kvp[0]);
|
||||
if(isset($kvp[1]))
|
||||
$string .= '=' . rawurlencode($kvp[1]);
|
||||
$string .= '&';
|
||||
}
|
||||
|
||||
$string = substr($string, 0, -1);
|
||||
}
|
||||
|
||||
if(!empty($parts['fragment']))
|
||||
$string .= '#' . rawurlencode($parts['fragment']);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
function uih_eeprom_lookup(stdClass $resp, string $eepromFileId, string $domain = 'flashii'): void {
|
||||
$resp->type = 'eeprom:file';
|
||||
|
@ -39,22 +129,12 @@ function uih_eeprom_lookup(stdClass $resp, string $eepromFileId, string $domain
|
|||
if(!is_dir(UIH_SEM_PATH))
|
||||
mkdir(UIH_SEM_PATH, 0777, true);
|
||||
|
||||
require_once __DIR__ . '/../config.php';
|
||||
|
||||
header('X-Powered-By: Uiharu');
|
||||
|
||||
ini_set('display_errors', UIH_DEBUG ? 'on' : 'off');
|
||||
error_reporting(UIH_DEBUG ? -1 : 0);
|
||||
$db->execute('DELETE FROM `uih_metadata_cache` WHERE `metadata_created` < NOW() - INTERVAL 7 DAY');
|
||||
|
||||
set_include_path(realpath(__DIR__ . '/../lib/') . PATH_SEPARATOR . get_include_path());
|
||||
spl_autoload_extensions('.php');
|
||||
spl_autoload_register();
|
||||
|
||||
DB::init(UIH_PDO_DSN, UIH_PDO_USER, UIH_PDO_PASS, DB::ATTRS);
|
||||
DB::exec('DELETE FROM `uih_metadata_cache` WHERE `metadata_created` < NOW() - INTERVAL 7 DAY');
|
||||
|
||||
$reqMethod = filter_input(INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING);
|
||||
$reqPath = '/' . trim(parse_url(filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING), PHP_URL_PATH), '/');
|
||||
$reqMethod = filter_input(INPUT_SERVER, 'REQUEST_METHOD');
|
||||
$reqPath = '/' . trim(parse_url(filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/');
|
||||
$reqHead = false;
|
||||
|
||||
if($reqMethod == 'HEAD') {
|
||||
|
@ -98,15 +178,15 @@ if($reqPath === '/metadata') {
|
|||
if($reqHead)
|
||||
return;
|
||||
|
||||
Stopwatch::start();
|
||||
$sw = Stopwatch::startNew();
|
||||
$resp = new stdClass;
|
||||
|
||||
if($_SERVER['HTTP_HOST'] === 'mii.flashii.net') {
|
||||
$resp->type = 'object';
|
||||
$resp->content_type = new stdClass;
|
||||
$resp->content_type->string = 'application/x-update-your-script-to-use-uiharu.flashii.net-instead-of-mii.flashii.net';
|
||||
$resp->content_type->type = 'text';
|
||||
$resp->content_type->subtype = 'deprecation';
|
||||
$resp->content_type = [];
|
||||
$resp->content_type['string'] = 'application/x-update-your-script-to-use-uiharu.flashii.net-instead-of-mii.flashii.net';
|
||||
$resp->content_type['type'] = 'text';
|
||||
$resp->content_type['subtype'] = 'deprecation';
|
||||
$resp->title = 'Update your URLs: mii.flashii.net -> uiharu.flashii.net';
|
||||
$resp->description = 'Update your URLs: mii.flashii.net -> uiharu.flashii.net';
|
||||
$resp->site_name = 'Deprecation notice';
|
||||
|
@ -121,20 +201,23 @@ if($reqPath === '/metadata') {
|
|||
$targetUrl = (string)filter_input(INPUT_GET, 'url');
|
||||
}
|
||||
|
||||
try {
|
||||
$resp->uri = $parsedUrl = new Uri($targetUrl);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$parsedUrl = uih_parse_url($targetUrl);
|
||||
if($parsedUrl === false) {
|
||||
http_response_code(400);
|
||||
$resp->error = 'metadata:uri';
|
||||
echo json_encode($resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// if no scheme is specified, try https
|
||||
if($parsedUrl->getScheme() === '')
|
||||
$parsedUrl = new Uri('https://' . (string)$parsedUrl);
|
||||
$resp->uri = $parsedUrl;
|
||||
|
||||
$urlHash = $parsedUrl->getHash();
|
||||
// if no scheme is specified, try https
|
||||
if(empty($parsedUrl['scheme'])) {
|
||||
$parsedUrl['scheme'] = 'https';
|
||||
$parsedUrl = uih_parse_url(uih_build_url($parsedUrl));
|
||||
}
|
||||
|
||||
$urlHash = hash('sha256', uih_build_url($parsedUrl));
|
||||
|
||||
try {
|
||||
$semPath = UIH_SEM_PATH . DIRECTORY_SEPARATOR . $urlHash;
|
||||
|
@ -145,30 +228,31 @@ if($reqPath === '/metadata') {
|
|||
while(!sem_acquire($semaphore)) usleep(100);
|
||||
|
||||
if(UIH_CACHE) {
|
||||
$loadCache = DB::prepare('SELECT `metadata_resp` AS `resp` FROM `uih_metadata_cache` WHERE `metadata_url` = UNHEX(:hash) AND `metadata_created` > NOW() - INTERVAL 10 MINUTE')
|
||||
->bind('hash', $urlHash)
|
||||
->fetchObject();
|
||||
if(isset($loadCache->resp)) {
|
||||
$cacheResp = json_decode($loadCache->resp);
|
||||
$cacheFetch = $db->prepare('SELECT `metadata_resp` FROM `uih_metadata_cache` WHERE `metadata_url` = UNHEX(?) AND `metadata_created` > NOW() - INTERVAL 10 MINUTE');
|
||||
$cacheFetch->addParameter(1, $urlHash);
|
||||
$cacheFetch->execute();
|
||||
$cacheResult = $cacheFetch->getResult();
|
||||
if($cacheResult->next()) {
|
||||
$cacheResp = json_decode($cacheResult->getString(0));
|
||||
if($cacheResp !== null)
|
||||
$resp = $cacheResp;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($resp->type)) {
|
||||
$urlScheme = strtolower($parsedUrl->getScheme());
|
||||
$urlHost = strtolower($parsedUrl->getHost());
|
||||
$urlPath = '/' . trim($parsedUrl->getPath(), '/');
|
||||
$urlScheme = strtolower($parsedUrl['scheme']);
|
||||
$urlHost = strtolower($parsedUrl['host']);
|
||||
$urlPath = '/' . trim($parsedUrl['path'], '/');
|
||||
|
||||
if($urlScheme === 'eeprom') {
|
||||
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl->getPath(), $matches)) {
|
||||
$resp->uri = $parsedUrl = new Uri('https://i.fii.moe/' . $matches[1]);
|
||||
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl['path'], $matches)) {
|
||||
$resp->uri = $parsedUrl = uih_parse_url('https://i.fii.moe/' . $matches[1]);
|
||||
$continueRaw = true;
|
||||
uih_eeprom_lookup($resp, $matches[1]);
|
||||
}
|
||||
} elseif($urlScheme === 'devrom') {
|
||||
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl->getPath(), $matches)) {
|
||||
$resp->uri = $parsedUrl = new Uri('https://i.edgii.net/' . $matches[1]);
|
||||
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl['path'], $matches)) {
|
||||
$resp->uri = $parsedUrl = uih_parse_url('https://i.edgii.net/' . $matches[1]);
|
||||
$continueRaw = true;
|
||||
uih_eeprom_lookup($resp, $matches[1], 'edgii');
|
||||
}
|
||||
|
@ -219,7 +303,7 @@ if($reqPath === '/metadata') {
|
|||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => 'Uiharu/' . UIH_VERSION,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Authorization: Bearer ' . TWITTER_API_TOKEN,
|
||||
'Authorization: Bearer ' . Config::get('Twitter', 'apiToken'),
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
|
@ -253,7 +337,7 @@ if($reqPath === '/metadata') {
|
|||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => 'Uiharu/' . UIH_VERSION,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Authorization: Bearer ' . TWITTER_API_TOKEN,
|
||||
'Authorization: Bearer ' . Config::get('Twitter', 'apiToken'),
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
|
@ -275,7 +359,7 @@ if($reqPath === '/metadata') {
|
|||
$youtubeVideoId = substr($urlPath, 1);
|
||||
case 'youtube.com': case 'www.youtube.com':
|
||||
case 'youtube-nocookie.com': case 'www.youtube-nocookie.com':
|
||||
parse_str($parsedUrl->getQuery(), $queryString);
|
||||
parse_str($parsedUrl['query'], $queryString);
|
||||
|
||||
if(!isset($youtubeVideoId) && $urlPath === '/watch')
|
||||
$youtubeVideoId = $queryString['v'] ?? null;
|
||||
|
@ -292,7 +376,7 @@ if($reqPath === '/metadata') {
|
|||
if(isset($queryString['index']))
|
||||
$resp->youtube_playlist_index = $queryString['index'];
|
||||
|
||||
$curl = curl_init("https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id={$resp->youtube_video_id}&key=" . GOOGLE_API_KEY);
|
||||
$curl = curl_init("https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id={$resp->youtube_video_id}&key=" . Config::get('Google', 'apiKey'));
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_CERTINFO => false,
|
||||
|
@ -326,8 +410,8 @@ if($reqPath === '/metadata') {
|
|||
$resp->error = 'metadata:scheme';
|
||||
}
|
||||
|
||||
if((empty($resp->type) || isset($continueRaw)) && in_array($parsedUrl->getScheme(), ['http', 'https'])) {
|
||||
$curl = curl_init((string)$parsedUrl);
|
||||
if((empty($resp->type) || isset($continueRaw)) && in_array($parsedUrl['scheme'], ['http', 'https'])) {
|
||||
$curl = curl_init(uih_build_url($parsedUrl));
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_CERTINFO => false,
|
||||
|
@ -377,15 +461,15 @@ if($reqPath === '/metadata') {
|
|||
}
|
||||
|
||||
try {
|
||||
$contentType = new MediaType($headers['content-type'] ?? '');
|
||||
$contentType = MediaType::parse($headers['content-type'] ?? '');
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$contentType = new MediaType('application/octet-stream');
|
||||
$contentType = MediaType::parse('application/octet-stream');
|
||||
}
|
||||
|
||||
$resp->content_type = $contentType;
|
||||
$resp->content_type = uih_media_type_json($contentType);
|
||||
|
||||
$isHTML = $contentType->match('text/html');
|
||||
$isXHTML = $contentType->match('application/xhtml+xml');
|
||||
$isHTML = $contentType->equals('text/html');
|
||||
$isXHTML = $contentType->equals('application/xhtml+xml');
|
||||
|
||||
if($isHTML || $isXHTML) {
|
||||
curl_setopt_array($curl, [
|
||||
|
@ -469,14 +553,14 @@ if($reqPath === '/metadata') {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
$resp->is_image = $isImage = $contentType->match('image/*');
|
||||
$resp->is_audio = $isAudio = $contentType->match('audio/*');
|
||||
$resp->is_video = $isVideo = $contentType->match('video/*');
|
||||
$resp->is_image = $isImage = $contentType->matchCategory('image');
|
||||
$resp->is_audio = $isAudio = $contentType->matchCategory('audio');
|
||||
$resp->is_video = $isVideo = $contentType->matchCategory('video');
|
||||
|
||||
if($isImage || $isAudio || $isVideo) {
|
||||
curl_close($curl);
|
||||
$resp->media = new stdClass;
|
||||
$ffmpeg = json_decode(shell_exec(sprintf('ffprobe -show_streams -show_format -print_format json -v quiet -i %s', escapeshellarg((string)$parsedUrl))));
|
||||
$ffmpeg = json_decode(shell_exec(sprintf('ffprobe -show_streams -show_format -print_format json -v quiet -i %s', escapeshellarg(uih_build_url($parsedUrl)))));
|
||||
|
||||
if(!empty($ffmpeg)) {
|
||||
if(!empty($ffmpeg->format)) {
|
||||
|
@ -562,13 +646,13 @@ if($reqPath === '/metadata') {
|
|||
}
|
||||
}
|
||||
|
||||
Stopwatch::stop();
|
||||
$resp->took = Stopwatch::elapsed();
|
||||
$sw->stop();
|
||||
$resp->took = $sw->getElapsedTime() / 1000;
|
||||
$respJson = json_encode($resp);
|
||||
DB::prepare('REPLACE INTO `uih_metadata_cache` (`metadata_url`, `metadata_resp`) VALUES (UNHEX(:hash), :resp)')
|
||||
->bind('hash', $urlHash)
|
||||
->bind('resp', $respJson)
|
||||
->execute();
|
||||
$replaceCache = $db->prepare('REPLACE INTO `uih_metadata_cache` (`metadata_url`, `metadata_resp`) VALUES (UNHEX(?), ?)');
|
||||
$replaceCache->addParameter(1, $urlHash);
|
||||
$replaceCache->addParameter(2, $respJson);
|
||||
$replaceCache->execute();
|
||||
}
|
||||
} finally {
|
||||
if(!empty($semaphore))
|
||||
|
|
25
src/Config.php
Normal file
25
src/Config.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace Uiharu;
|
||||
|
||||
final class Config {
|
||||
private static array $config = [];
|
||||
|
||||
public static function load(string $path): void {
|
||||
$config = parse_ini_file($path, true, INI_SCANNER_TYPED);
|
||||
|
||||
if(!empty($config))
|
||||
self::$config = array_merge(self::$config, $config);
|
||||
}
|
||||
|
||||
public static function get(string $section, string $key, $default = null) {
|
||||
if(!self::has($section, $key))
|
||||
return $default;
|
||||
return self::$config[$section][$key];
|
||||
}
|
||||
|
||||
public static function has(string $section, string $key) {
|
||||
return array_key_exists($section, self::$config)
|
||||
&& array_key_exists($key, self::$config[$section])
|
||||
&& !empty(self::$config[$section][$key]);
|
||||
}
|
||||
}
|
40
uiharu.php
Normal file
40
uiharu.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace Uiharu;
|
||||
|
||||
use Index\Autoloader;
|
||||
use Index\Environment;
|
||||
use Index\Data\ConnectionFailedException;
|
||||
use Index\Data\DbTools;
|
||||
|
||||
define('UIH_STARTUP', microtime(true));
|
||||
define('UIH_ROOT', __DIR__);
|
||||
define('UIH_DEBUG', is_file(UIH_ROOT . '/.debug'));
|
||||
define('UIH_PUBLIC', UIH_ROOT . '/public');
|
||||
define('UIH_SOURCE', UIH_ROOT . '/src');
|
||||
define('UIH_LIBRARY', UIH_ROOT . '/lib');
|
||||
define('UIH_VERSION', '20220714');
|
||||
define('UIH_SEM_NAME', 'U');
|
||||
define('UIH_SEM_PATH', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'uiharu');
|
||||
|
||||
require_once UIH_LIBRARY . '/index/index.php';
|
||||
|
||||
Autoloader::addNamespace(__NAMESPACE__, UIH_SOURCE);
|
||||
Environment::setDebug(UIH_DEBUG);
|
||||
|
||||
mb_internal_encoding('utf-8');
|
||||
date_default_timezone_set('utc');
|
||||
|
||||
$configPath = UIH_ROOT . '/config.ini';
|
||||
if(!is_file($configPath))
|
||||
die('Uiharu configuration is missing.');
|
||||
|
||||
Config::load($configPath);
|
||||
if(!Config::has('Database', 'dsn'))
|
||||
die('Uiharu database is not configured.');
|
||||
|
||||
try {
|
||||
$db = DbTools::create(Config::get('Database', 'dsn'));
|
||||
} catch(ConnectionFailedException $ex) {
|
||||
echo '<h3>Unable to connect to database</h3>';
|
||||
die($ex->getMessage());
|
||||
}
|
Loading…
Reference in a new issue