<?php
namespace Lia;
class Simple extends \Lia {
public array $phads = [];
public bool $debug = false;
/**
* The feature currently being enabled or the last feature enabled if one is not currently being enable.
*/
public ?string $last_feature = null;
/**
* array of `$feature_name=>true`
*/
public array $features = [];
public function __construct(){
parent::__construct();
$server_dir = \Lia\Package\Server::main_dir();
$server = new \Lia\Package\Server($this, 'server', $server_dir);
// some basic configs
$this->set('lia:server.cache.dir',$server->dir.'/cache/');
$this->set('lia:server.router.varDelim', '\\.\\/\\:'); //default includes a hyphen, which is dumb as hek
}
public function require_feature(string $feature){
$last = $this->last_feature;
if (!isset($this->features[$feature]))throw new \Exception("'$last' requires feature '$feature'. Please add '$feature' to your config.json like `features:[\"$feature\", \"$last\"]`");
}
public function setup(){
foreach ($this->packages as $p){
$p->ready();
}
}
public function deliver_files(...$public_dirs){
// var_dump($public_dirs);
foreach ($public_dirs as $d){
\Lia\FastFileRouter::file($d);
}
}
public function load_apps(...$app_dirs){
$apps = array_map([$this,'load_app'], $app_dirs);
return $apps;
}
public function load_app($app_dir){
$name = strtolower(basename($app_dir));
$url_prefix = '/';
$app = new \Lia\Package\Server($this, $name, $app_dir, $url_prefix);
$app->base_url = $url_prefix;
$app->dir = $app_dir;
$config = [];
if (file_exists($app_dir.'/config.json')){
$config = json_decode(file_get_contents($app_dir.'/config.json'),true);
}
if (!isset($config['features']))$config['features'] = ['phad'];
$app->config = $config;
if (file_exists($init_php=$app_dir.'/init.php')){
$lia = $this;
require($init_php);
}
if (file_exists($fn_php = $app_dir.'/functions.php')){
$lia = $this;
require_once($fn_php);
}
foreach ($config['features'] as $feature_name){
$this->enable($feature_name, $app);
}
if (file_exists($post_init_php=$app_dir.'/post-init.php')){
$lia = $this;
$env = $this->env ?? null;
$user = $this->user ?? null;
require($post_init_php);
}
return $app;
}
public function enable(string $feature, $app=null){
$this->last_feature = $feature;
$this->features[$feature] = true;
if (method_exists($this,$method='enable_'.$feature))return $this->$method($app);
switch ($feature){
case "seo_extractor":
\Lia\Addon\SeoExtractor::enable($this);
return;
case "kitchen_sink":
\Lia\App\KitchenSink::enable($this);
return;
case "R":
$r = \R::setup();
$dir = $this->root_dir;
$r->load($dir.'/RSettings.json');
$r->load($dir.'/.env/secret.json');
return $r;
case "debug_empty_cache":
if ($this->debug!==false&&$this->env->is_bool(['host.local'])
&&substr($_SERVER['REQUEST_URI'],-1)=='/'
||$_SERVER['REQUEST_METHOD']=='POST'
){
// echo 'good';
// exit;
$this->cache->delete_all_cache_files();
$this->resources->cache->delete_all_cache_files();
}
return;
case "debug_show_errors":
if ($this->debug===true){
$this->env->showErrors();
}
return;
}
throw new \Exception("Feature '$feature' does not exist.");
}
public function enable_phad($app){
if (!class_exists('\Phad', true)){
throw new \Exception("\nPhad is not installed. Run `composer require taeluf/phad v0.4`");
}
$this->require_feature('pdo');
$dir = $app->dir;
$pdo = $this->pdo ?? $app->lia->pdo ?? null;
$debug = $this->debug;
$route_prefix = $app->base_url;
$options = [
'item_dir'=>$dir.'/phad/',
'cache_dir'=>$dir.'/cache/',
'sitemap_dir'=>$dir.'/sitemap/',
'pdo' => $pdo,// a pdo object
'router' => $this->addon('lia:server.router'),
'throw_on_query_failure'=>$debug,
'force_compile'=>$debug,
];
$phad_class = $app->config['phad.class'] ?? '\\Phad';
// var_dump($phad_class);
$phad = $phad_class::main($options);
$app->phad = $phad;
$phad->route_prefix = $route_prefix;
$phad->global_phad_args['lia'] = $this;
// $this->set('phad', $phad);
$this->addMethod('phad', [$this, 'phad_item']);
$phad->filters['markdown'] = [$this, 'markdown_to_html'];
$phad->integration->setup_liaison_routes($this);
$phad->integration->setup_liaison_route($this, '/sitemap.xml', $phad->sitemap_dir.'/sitemap.xml');
$phad->handlers['user_has_role'] =
function(string $role){
return true;
}
;
$phad->handlers['can_read_row'] =
function(array $ItemRow,object $ItemInfo,string $ItemName){
return true;
};
if (isset($app->config['phad.handler']) && isset($this->user)){
$handler = $app->config['phad.handler'];
$phad->set_handler(new $handler($this->user));
}
$this->phads[] = $phad;
return $phad;
}
public function enable_user($app){
if (!class_exists('\Tlf\User\Lib', true)){
throw new \Exception("User lib not installed. Run `composer require taeluf/user-gui v0.3`");
}
$user_lib_dir = $this->root_dir.'/vendor/taeluf/user-gui/code/';
$pdo = $this->pdo;
// var_dump($user_lib_dir);
// exit;
$this->set('user.base_url', '/user/');
$user_package = new \Lia\Package\Server($this, 'user', $user_lib_dir);
$user_package->base_url = '/user/';
// load db settings from env file & make pdo
// $settings = json_decode(file_get_contents(dirname(__DIR__).'/db-env.json'),true);
// $pdo = new \PDO("mysql:dbname=".$settings['db'], $settings['user'], $settings['password']);
// configure the user login library
$lib = new \Tlf\User\Lib($pdo);
$lib->disabled_pages = $app->config['user.disabled_pages'] ?? [];
$user_lib = $lib;
$lib->user_class = $app->config['user.class'] ?? null;
$user_class = $lib->user_class;
$lib->config = [
'web_address'=>$app->config['user.web_address'],
'email_from'=>$app->config['user.email_from'],
];
// uncomment this line, run once, then re-comment this line
// $lib->init_db();
// log the user in
$current_user = $lib->user_from_cookie();
// if there was no cookie, we'll use an unregistered user
if ($current_user===false)$current_user = new $user_class($pdo);
$this->set('user', $current_user);
$this->addon('lia:server.view')->globalArgs['user'] = $current_user;
// passes the user & library to user pages (login, register, etc)
$user_package->public_file_params = [
'user'=>$current_user,
'lib'=>$lib
];
$user_package->lib = $lib;
// $this->dump($app);
$app->public_file_params = [
'user'=>$current_user,
'lib'=>$lib
];
}
public function enable_env($app){
$this->env = new \Env();
if (file_exists($env_file=$this->root_dir.'/.env/secret.json')){}
$this->env->load($env_file);
// $this->dump();
}
public function enable_pdo($app){
// var_dump(isset($this->env));
// exit;
if (!isset($this->env)){
throw new \Exception("Feature 'env' is not enabled. Call `\$lia->enable_env()` or add `\"env\"` to the `\"features:[\"env\"]\"` of an app's confing.json");
}
$env = $this->env;
$this->pdo = $pdo = new \PDO(
'mysql:host='.$env->get('mysql.host').';dbname='.$env->get('mysql.dbname'),
$env->get('mysql.user'), $env->get('mysql.password')
);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public function enable_ldb($app){
if (!isset($this->pdo)){
throw new \Exception("Feature 'pdo' is not enabled. Call `\$lia->enable_pdo()` or add `\"pdo\"` to the `\"features:[\"pdo\"]\"` of an app's confing.json");
}
if (!class_exists('\Tlf\LilDb', true)){
throw new \Exception("\nLilDb is not installed. Run `composer require taeluf/lildb v0.1`");
}
require_once(__DIR__.'/ldb.php');
$this->addMethod('ldb', 'ldb');
return $this->ldb = \ldb($this->pdo);
}
/**
* 1. Save the current page to analytics
* 2. Enable the analytics route, if phad is enabled
*/
public function enable_analytics($app){
$pdo = $this->pdo;
// still need the phad view
$this->require_feature('pdo');
$this->require_feature('phad');
$url = @$_SERVER['REQUEST_URI'];
$pos = strpos($url, '?');
$url = str_replace(['<','>'],'-',$url);
if ($pos!==false)$url = substr($url,0,$pos);
// $ip_hash = md5(@$_SERVER["REMOTE_ADDR"]);
$ip_hash = 'na';
$datetime = new \DateTime();
$year_month = $datetime->format("Y-m")."-01";
$end = substr($url,-4);
// var_dump($end);
if (substr($url,-4)=='.css'||substr($url,-3)=='.js'){
} else {
$stmt = $pdo->prepare(
'INSERT INTO tlf_analytics (`url`, `ip_hash`, `year_month`)
VALUES (:url, :ip_hash, :year_month)
');
if ($stmt==false){
// print_r($pdo->errorInfo());
// exit;
return;
}
$stmt->execute( ['url'=>$url,'ip_hash'=>$ip_hash, 'year_month'=>$year_month]);
}
if (!isset($app->phad)||!is_object($app->phad))return;
$args = [
'pdo'=>$pdo,
];
$item = $app->phad->item_from_file(__DIR__.'/../phad/AnalyticsView.php', $args);
$this->addRoute(
'/analytics/',
function($route,$response) use ($item){
$response->content = $item->html();
}
);
}
/**
* enable the route /generic-error-page/ to display 'Unknown Error' message.
*/
public function enable_error_page($app){
// route to error page
if ($_SERVER['REQUEST_URI']=='/generic-error-page/'){ // save a small amount of overhead when this isn't the requested page
$this->addon('lia:server.seo')->html['meta-noindex'] = ['<meta name="robots" content="noindex" />'];
if (!$this->addon('lia:server.router')->has_static_route('/generic-error-page/')){
$this->addRoute('/generic-error-page/',function($route,$response){
$response->content = "Unknown Error. Please return to the <a href=\"/\">Home Page</a> or try again. If this persists, contact the website owner.";
});
}
// $this->hook('ResponseReady',
// function($response){
// if (!file_exists($file = $this->cache->dir.'/generic-error-page.html')){
// file_put_contents($file, $response->content);
// }
// }
// );
}
}
public function respond(){
// if (static::$IS_TLFSERV_CLI)return;
$debug = $this->debug ?? false;
try {
if ($debug && $_SERVER['REQUEST_URI']=='/debug/'){
$this->debug();
exit;
}
$env = $this->env;
try {
$this->deliver();
} catch (\Exception $e){
\Lia\ExceptionCatcher::throw($e,$debug);
}
} catch (\Throwable $e){
if ($debug){
echo nl2br("\n\n---------------------------------------\n\nError:\n");
throw $e;
} else {
ob_get_clean();
try {
$host = $_SERVER['HTTP_HOST'];
$file = $this->cache->dir.'/generic-error-page.html';
if (is_file($file)){
$i=0;
while(ob_get_level()>0&&$i++<10){
ob_end_clean();
}
echo file_get_contents($file);
}
exit;
} catch (\Throwable $e){
echo "There was an error. Try returning to the "
.'<a href="/">Home Page</a> at '.$host;
exit;
}
}
}
}
public function debug(){
$lia = $this;
$router = $lia->addon('lia:server.router');
$routes = $router->routeMap;
print_r(array_keys($routes['POST']));
exit;
}
/**
* Convert markdown to html, if CommonMark is installed
*
* @param $markdown
* @return html, or if CommonMark is not installed, returns the markdown
*/
public function markdown_to_html(string $markdown){
$class = "League\\CommonMark\\CommonMarkConverter";
if (class_exists($class,true)){
$converter = new \League\CommonMark\CommonMarkConverter();
return $converter->convert($markdown).'';
}
throw new \Exception("\nCommonMark is not installed. Run `composer require league/commonmark 2.3`\n");
}
public function phad_item(string $item, array $args = []){
foreach ($this->phads as $phad){
if ($phad->has_item($item)){
$item = $phad->item($item, $args);
foreach ($item->resource_files()['css'] as $f){
$this->addResourceFile($f);
}
foreach ($item->resource_files()['js'] as $f){
$this->addResourceFile($f);
}
return $item;
}
}
throw new \Exception("Phad item '$item' not found");
}
/**
* Initialize your analytics table. Recommend using a local sqlite database.
*/
public function init_analytics_table(\PDO $pdo){
$pdo->exec(
'CREATE TABLE IF NOT EXISTS `tlf_analytics`(
`id` int AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`ip_hash` varchar(255) NOT NULL,
`year_month` varchar(12) NOT NULL,
PRIMARY KEY(`id`)
);
'
);
print_r($pdo->errorInfo());
// exit;
}
public function __isset($prop){
return isset($this->$prop) ? true : isset($this->props[$prop]);
}
/**
* get the named property or addon if property not found
* @param $addon the name of the addon
*/
public function __get($prop){
if (!isset($this->props[$prop])&&!isset($this->addons[$prop]))throw new \Exception("Property '$prop' not found");
$a = $this->props[$prop] ?? $this->addons[$prop];
return $a;
}
public function __set($prop, $value){
$this->props[$prop] = $value;
}
}