GET /sitemap.xml
/srv/phlo/functions.php:102
Class "app" not found
phlo() phlo.php:30
phlo_app() phlo.php:8
phlo_app_jsonfile() app.php:5
  1. <?php
  2. function view(?string $body = null, ?string $title = null, array|string $css = [], array|string $js = [], array|string $defer = [], array|string $options = [], array $settings = [], ?string $ns = null, bool|string $uri = req, ...$cmds){
  3. !async && !is_bool($uri) && $uri !== req && location("/$uri");
  4. $app = phlo('app');
  5. $title && title($title);
  6. $css = array_merge((array)$css, (array)$app->css);
  7. $js = array_merge((array)$js, (array)$app->js);
  8. $defer = array_merge((array)$defer, (array)$app->defer);
  9. $options = implode(space, array_merge((array)$options, (array)$app->options, debug ? ['debug'] : []));
  10. $settings = array_merge($settings, (array)$app->settings);
  11. if (async){
  12. $uri !== false && $cmds['uri'] = $uri;
  13. $cmds['trans'] ??= true;
  14. $cmds['title'] = title();
  15. $css && $cmds['css'] = $css;
  16. $js && $cmds['js'] = $js;
  17. $defer && $cmds['defer'] = $defer;
  18. $cmds['options'] = $options;
  19. $cmds['settings'] = $settings;
  20. !is_null($body) && $cmds['inner']['body'] = $body;
  21. apply(...$cmds);
  22. }
  23. $body ??= $cmds['main'] ?? void;
  24. debug && $body .= lf.debug_render();
  25. $ns ??= $app->ns ?? 'app';
  26. $link = [];
  27. $head = tag('title', inner: title()).lf;
  28. $head .= '<meta name="viewport" content="'.($cmds['viewport'] ?? $app->viewport ?? 'width=device-width').'">'.lf;
  29. $app->description && $head .= "<meta name=\"description\" content=\"$app->description\">\n";
  30. $app->themeColor && $head .= "<meta name=\"theme-color\" content=\"$app->themeColor\">\n";
  31. $app->image && $head .= "<meta property=\"og:image\" content=\"$app->image\">\n";
  32. file_exists(www.$filename = 'favicon.ico') && $head .= "<link rel=\"favicon\" href=\"/$filename?".version."\">\n";
  33. file_exists(www.$filename = 'manifest.json') && $head .= "<link rel=\"manifest\" href=\"/$filename?".version."\">\n";
  34. file_exists(www.$filename = 'icons.png') && $link[] = "</$filename?".version.">; rel=preload; as=image";
  35. file_exists(www.$filename = "$ns.css") && [$link[] = "</$filename?".version.">; rel=preload; as=style", $head .= '<link rel="stylesheet" href="'.esc(slash.$filename.qm.version).'">'.lf];
  36. foreach ($css AS $item) $head .= '<link rel="stylesheet" href="'.esc($item).'">'.lf;
  37. file_exists(www.$filename = "$ns.js") && [$link[] = "</$ns.js?".version.">; rel=preload; as=script", $head .= '<script src="'.esc(slash.$filename.qm.version).'" defer></script>'.lf];
  38. foreach ($js AS $item) $head .= '<script src="'.esc($item).'"></script>'.lf;
  39. foreach ($defer AS $item) $head .= '<script src="'.esc($item).'" defer></script>'.lf;
  40. $app->head && $head .= $app->head;
  41. !build && $link && header('Link: '.implode(comma, $link), false);
  42. if ($lang = $cmds['lang'] ?? $app->lang ?? 'en') unset($cmds['lang']);
  43. $bodyAttrs = void;
  44. $options && $bodyAttrs .= " class=\"$options\"";
  45. $settings && $bodyAttrs .= loop($settings, fn($value, $key) => ' data-'.$key.'="'.esc($value).'"', void);
  46. die(DOM($body, $head, $lang, $bodyAttrs));
  47. }
  48. //Output instructions or start an output stream
  49. function apply(...$cmds){
  50. cli || phlo('app')->streaming || [header('Content-Type: application/json'), header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'), header('Pragma: no-cache')];
  51. debug && $cmds = debug_apply($cmds);
  52. die(json_encode($cmds, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  53. }
  54. function chunk(...$cmds){
  55. static $header;
  56. $header ??= first(true, cli || [header('Content-Type: text/event-stream'), phlo('app')->streaming = true]);
  57. echo json_encode($cmds, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES).lf;
  58. cli || [ob_flush(), flush()];
  59. }
  60. function output($content = null, $filename = null, $attachment = null, $file = null){
  61. header('Content-Type: '.mime($filename ?? basename($file ?? req)));
  62. header('Content-Length: '.strlen($content ??= file_get_contents($file)));
  63. if (is_bool($attachment) || $filename) header('Content-Disposition: '.($attachment ? 'attachment' : 'inline').';filename='.rawurlencode($filename ?? basename($file ?? req)));
  64. echo $content;
  65. exit;
  66. }
  67. //HTML tag functions
  68. function DOM($body = void, $head = void, $lang = 'en', $bodyAttrs = void){ return "<!DOCTYPE html>\n<html lang=\"$lang\">\n<head>\n$head</head>\n<body$bodyAttrs>\n$body\n</body>\n</html>"; }
  69. function tag($tagName, $inner = null, ...$args){ return "<$tagName".loop(array_filter($args, fn($value) => !is_null($value)), fn($value, $key) => space.strtr($key, [us => dash]).($value === true ? void : '="'.esc($value).'"'), void).'>'.(is_null($inner) ? void : "$inner</$tagName>"); }
  70. function button(...$args){ return tag('button', ...$args); }
  71. function input(...$args){ return tag('input', ...$args); }
  72. function select(...$args){ return tag('select', ...$args); }
  73. function textarea(...$args){ return tag('textarea', ...$args); }
  74. function title($title = null, $implode = ' - '){
  75. static $titles = [];
  76. if ($title) return $titles[] = $title;
  77. $titles[] = phlo('app')->title ?: 'Phlo '.phlo;
  78. return implode($implode, $titles);
  79. }
  80. function error(string $msg){
  81. if (cli) die("$msg\n");
  82. if (async) apply(error: $msg);
  83. print(DOM(strtr(esc($msg), [lf => br])).lf);
  84. exit(1);
  85. }
  86. //Handle Phlo object instances
  87. function phlo(?string $phloName = null, ...$args){
  88. static $list = [];
  89. if (is_null($phloName)) return array_keys($list);
  90. $phloName = strtr($phloName, [slash => us]);
  91. $handle = method_exists($phloName, '__handle') ? $phloName::__handle(...$args) : ($args ? null : $phloName);
  92. if ($handle === true){
  93. if (isset($list[$phloName])) return $list[$phloName]->objImport(...$args);
  94. $handle = $phloName;
  95. }
  96. elseif ($handle && isset($list[$handle])) return $list[$handle];
  97. $phlo = new $phloName(...$args);
  98. if ($handle) $list[$handle] = $phlo;
  99. if ($phlo->hasMethod('controller') && (!cli || $phloName !== 'app')) $phlo->controller();
  100. return $phlo;
  101. }
  102. //Phlo execution functions
  103. function phlo_exec($path, $obj, $call, $sync = true, ...$args){ return last(exec('/usr/bin/php '.rtrim($path, slash).'/app.php '.$obj.space.$call.loop($args, fn($arg) => str_contains($arg, space) || str_contains($arg, dq) || str_contains($arg, sq) || str_contains($arg, bt) || str_contains($arg, eq) || str_contains($arg, bs) || str_contains($arg, slash) ? ' "'.strtr($arg, [dq => bs.dq, '`' => '\`']).'"' : space.$arg, void).($sync ? void : ' > /dev/null 2>&1 &'), $res), $sync ? implode(lf, $res) : true); }
  104. function phlo_sync($obj, $call, ...$args){ return phlo_exec(www, $obj, $call, true, ...$args); }
  105. function phlo_async($obj, $call, ...$args){ return phlo_exec(www, $obj, $call, false, ...$args); }
  106. function phlo_exists($obj){ return file_exists(php.strtr($obj, [us => dot]).'.php'); }
  107. //Obj/array generator functions
  108. function arr(...$array){ return $array; }
  109. function obj(...$data){ return new obj(...$data); }
  110. function HTTP(string $url, array $headers = [], bool $JSON = false, $POST = null, $PUT = null, $PATCH = null, bool $DELETE = false, $agent = null){
  111. $curl = curl_init($url);
  112. if ($POST || $PUT || $PATCH){
  113. if (!is_null($POST)) [$method = 'POST', $content = $POST];
  114. elseif (!is_null($PUT)) [$method = 'PUT', $content = $PUT];
  115. elseif (!is_null($PATCH)) [$method = 'PATCH', $content = $PATCH];
  116. curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
  117. if ($JSON){
  118. !is_string($content) && $content = json_encode($content);
  119. array_push($headers, 'Content-Type: application/json', 'Content-Length: '.strlen($content));
  120. }
  121. curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
  122. }
  123. elseif ($DELETE) curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
  124. $agent && curl_setopt($curl, CURLOPT_USERAGENT, $agent === true ? $_SERVER['HTTP_USER_AGENT'] : $agent);
  125. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  126. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
  127. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  128. return curl_exec($curl);
  129. }
  130. //Auth functions on IP or landing URL
  131. function auth_IP(...$list){
  132. if (($return = cli || phlo('session')->auth) || !$return = ($index = array_search($_SERVER['REMOTE_ADDR'], $list)) !== false) return $return;
  133. phlo('session')->auth = true;
  134. return is_int($index) ?: $index;
  135. }
  136. function auth_uri(...$list){
  137. if (($return = cli || phlo('session')->auth) || !$return = ($index = array_search(req, $list)) !== false) return $return;
  138. phlo('session')->auth = true;
  139. return is_int($index) ?: $index;
  140. }
  141. function auth_log($user){ file_put_contents(data.'access.log', date('j-n-Y H:i:s')." - $user - $_SERVER[REMOTE_ADDR]\n", FILE_APPEND); }
  142. //APCu cache function
  143. function apcu($key, $cb, $duration = 3600, bool $log = true){ return first($value = apcu_entry($key, $cb, $duration), $log && debug('C: '.(strlen($key) > 58 ? substr($key, 0, 55).'...' : $key).(is_array($value) ? ' ('.count($value).')' : (is_numeric($value) ? ":$value" : (is_string($value) ? ':string:'.strlen($value) : colon.gettype($value)))))); }
  144. //JSON functions
  145. function json_read($file, $assoc = null){ return json_decode(file_get_contents($file), $assoc) ?? error('Error reading '.esc($file)); }
  146. function json_write($file, $data, $flags = null){ return file_put_contents($file, json_encode($data, $flags ?? jsonFlags)); }
  147. //Argument handling functions
  148. function first(...$args){ return current($args); }
  149. function last(...$args){ return end($args); }
  150. //Encryption functions
  151. function encrypt($data, $key){ return base64_encode(($nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)).sodium_crypto_secretbox($data, $nonce, hash('sha256', $key, true))); }
  152. function decrypt($encrypted, $key){ return sodium_crypto_secretbox_open($ciphertext = substr($decoded = base64_decode($encrypted), SODIUM_CRYPTO_SECRETBOX_NONCEBYTES), substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES), hash('sha256', $key, true)); }
  153. // Time functions
  154. function age(int $time){ return time() - $time; }
  155. function age_human(int $age){ time_human(time() - $age); }
  156. function duration(int $decimals = 4, bool $float = false){ return ltrim(round($duration = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], $decimals), 0).($float ? void : 's'.($duration < .5 ? ' ('.round(1 / $duration).'/s)' : void)); }
  157. function time_human(?int $time = null){
  158. static $labels;
  159. $labels ??= last($labels = arr(seconds: 60, minutes: 60, hours: 24, days: 7, weeks: 4, months: 13, years: 1), defined('tsLabels') && $labels = array_combine(tsLabels, $labels), $labels);
  160. $age = time() - $time;
  161. foreach ($labels AS $range => $multiplier){
  162. if ($age / $multiplier < 1.66) break;
  163. $age /= $multiplier;
  164. }
  165. return round($age)." $range";
  166. }
  167. //Generic functions
  168. function active(bool $cond, string $classList = void){ return $cond || $classList ? ' class="'.$classList.($cond ? ($classList ? space : void).'active' : void).'"' : void; }
  169. function camel($text){ return lcfirst(str_replace(space, void, ucwords(lcfirst($text)))); }
  170. function create($items, Closure $keyCb, ?Closure $valueCb = null){ return array_combine(loop($items, $keyCb), $valueCb ? loop($items, $valueCb) : $items); }
  171. function debug(?string $msg = null){
  172. if (!debug) return;
  173. static $debug = [];
  174. if (!$msg) return debug ? $debug : null;
  175. $debug[] = substr($msg, 0, 160);
  176. }
  177. function esc($string){ return htmlspecialchars((string)$string); }
  178. function files(string|array $paths, string $ext = '*.*'){ return array_merge(...loop((array)$paths, fn($path) => glob("$path$ext"))); }
  179. function indent(string $string, int $depth = 1){ return ($tab = str_repeat(tab, $depth)).rtrim(strtr($string, [lf => lf.$tab]), tab); }
  180. function indentView(string $string, int $depth = 1){ return last($tab = str_repeat(tab, $depth), rtrim(preg_replace('/\n(\t*)</', "\n$1$tab<", $string), tab)); }
  181. function location(?string $location = null){ async ? apply(location: $location ?? true) : [header('Location: '.($location ?? ($_SERVER['HTTP_REFERER'] ?? slash))), exit]; }
  182. function loop(iterable $data, closure|array $cb, ?string $implode = null){
  183. $return = [];
  184. $isArray = is_array($cb);
  185. foreach ($data AS $key => $value) $return[$key] = $isArray ? $cb[0]->{$cb[1]}($value, $key) : $cb($value, $key);
  186. return is_null($implode) ? $return : implode($implode, $return);
  187. }
  188. function mime($filename){ return ['html' => 'text/html', 'css' => 'text/css', 'gif' => 'image/gif', 'ico' => 'image/x-icon', 'ini' => 'text/plain', 'js' => 'application/javascript', 'json' => 'application/json', 'jpg' => 'image/jpeg', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'ogg' => 'audio/ogg', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'pdf' => 'application/pdf', 'phlo' => 'application/phlo', 'php' => 'application/x-httpd-php', 'png' => 'image/png', 'svg' => 'image/svg+xml', 'txt' => 'text/plain', 'webp' => 'image/webp'][pathinfo($filename, PATHINFO_EXTENSION)] ?? 'application/octet-stream'; }
  189. function regex(string $pattern, string $subject, int $flags = 0, int $offset = 0):array { return last(str_starts_with($pattern, slash) || $pattern = "/$pattern/", preg_match($pattern, $subject, $match, $flags, $offset) ? $match : []); }
  190. function regex_all(string $pattern, string $subject, int $flags = 0, int $offset = 0):array { return last(str_starts_with($pattern, slash) || $pattern = "/$pattern/", preg_match_all($pattern, $subject, $matches, $flags, $offset) ? $matches : []); }
  191. function req(int $index, $length = null){
  192. static $parts;
  193. $parts ??= explode(slash, req);
  194. return is_null($length) ? ($parts[$index] ?? null) : (implode(slash, array_slice($parts, $index, $length < 0 ? null : $length)) ?: null);
  195. }
  196. function route(?string $method = null, string $path = void, ?bool $async = null, ?string $data = null, ?string $cb = null){
  197. if ($method && $method !== method) return;
  198. if (!is_null($async) && $async !== async) return;
  199. if ($data && phlo('payload')->objKeys !== explode(comma, $data)) return;
  200. $req = array_filter(explode(slash, req));
  201. $cbArgs = [];
  202. $index = -1;
  203. foreach (array_filter(explode(space, $path)) AS $index => $item){
  204. $reqItem = req($index);
  205. if (strpos($item, '$') === 0){
  206. $item = substr($item, 1);
  207. if (str_ends_with($item, '=*')){
  208. $cbArgs[substr($item, 0, -2)] = implode(slash, array_slice($req, $index));
  209. $index = count($req) - 1;
  210. break;
  211. }
  212. elseif (str_ends_with($item, '?')){
  213. $item = substr($item, 0, -1);
  214. if ($reqItem && $item !== $reqItem) return;
  215. $reqItem = $item === $reqItem;
  216. }
  217. elseif (strpos($item, '=')){
  218. list ($item, $default) = explode('=', $item, 2);
  219. $default = $default ?: null;
  220. }
  221. elseif (is_null($reqItem)) return;
  222. if (strpos($item, '.') && (list($item, $length) = explode(dot, $item, 2)) && strlen($reqItem) != $length) return false;
  223. if (strpos($item, ':')){
  224. (list ($item, $list) = explode(':', $item, 2)) && $list = explode(comma, $list);
  225. if (!$reqItem || in_array($reqItem, $list)) $cbArgs[$item] = $reqItem ?: $default ?? null;
  226. else return;
  227. }
  228. else $cbArgs[$item] = $reqItem ?? $default;
  229. }
  230. elseif ($item !== $reqItem) return;
  231. }
  232. if (isset($req[$index + 1])) return;
  233. if (!$cb) return obj(...$cbArgs);
  234. if ($cb(...$cbArgs) === false) return;
  235. exit;
  236. }
  237. function size_human(int $size, int $precision = 0){
  238. foreach (['b', 'Kb', 'Mb', 'Gb', 'Tb'] AS $range){
  239. if ($size / 1024 < 1) break;
  240. $size /= 1024;
  241. }
  242. return round($size, $precision).$range;
  243. }
  244. function slug(string $text):string { return trim(preg_replace('/[^a-z0-9]+/', dash, strtolower(iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text))), dash); }
  245. function token(int $length = 8, ?string $input = null, $sha1 = null){
  246. $sha1 ??= sha1($input ?? random_int(date('Y'), PHP_INT_MAX), true);
  247. $token = void;
  248. for ($i = 0; strlen($token) < $length; $i++) $token .= chr(ord('a') + (ord($sha1[$i % 20]) % 26));
  249. return $token;
  250. }