diff --git a/app/app.php b/app/app.php index 91adc35..61de49f 100644 --- a/app/app.php +++ b/app/app.php @@ -1,6 +1,6 @@ diff --git a/app/install/installer.php b/app/install/installer.php index 96c4c52..c9a6cb0 100644 --- a/app/install/installer.php +++ b/app/install/installer.php @@ -253,6 +253,7 @@ try { 'listing_viewer' => 1, ], '1.1.1' => NULL, + '1.1.2' => NULL, ]; // Settings that must be renamed from NAME to NEW NAME and DELETE old NAME $settings_rename = []; diff --git a/app/routes/route.dashboard.php b/app/routes/route.dashboard.php index a607d83..2fe28f7 100644 --- a/app/routes/route.dashboard.php +++ b/app/routes/route.dashboard.php @@ -136,7 +136,7 @@ $route = function ($handler) { ], 'g_version' => [ 'label' => 'G\\', - 'content' => 'G\\ Library '.G\get_version().'' + 'content' => 'G\\ Library '.G\get_version().'' ], 'php_version' => [ 'label' => _s('PHP version'), diff --git a/content/pages/default/privacy.php b/content/pages/default/privacy.php index 9eabf52..fec3266 100644 --- a/content/pages/default/privacy.php +++ b/content/pages/default/privacy.php @@ -1,4 +1,6 @@ - +
@@ -20,7 +22,7 @@

More help

-

If you need more help we suggest you to go to Chevereto support and read the G\ Library documentation. View the code of this file will also help you to understand the magic behind this system.

+

If you need more help we suggest you to go to Chevereto support and read the G\ Library documentation. View the code of this file will also help you to understand the magic behind this system.

diff --git a/content/pages/default/tos.php b/content/pages/default/tos.php index 9eabf52..fec3266 100644 --- a/content/pages/default/tos.php +++ b/content/pages/default/tos.php @@ -1,4 +1,6 @@ - +
@@ -20,7 +22,7 @@

More help

-

If you need more help we suggest you to go to Chevereto support and read the G\ Library documentation. View the code of this file will also help you to understand the magic behind this system.

+

If you need more help we suggest you to go to Chevereto support and read the G\ Library documentation. View the code of this file will also help you to understand the magic behind this system.

diff --git a/lib/G/G.php b/lib/G/G.php index c24b92c..db608ae 100644 --- a/lib/G/G.php +++ b/lib/G/G.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. @@ -16,12 +16,14 @@ namespace G; -if(!defined('access') or !access) die("This file cannot be directly accessed."); +if (!defined('access') or !access) { + die("This file cannot be directly accessed."); +} define('G_VERSION', '1.0.42'); // Error reporting setup -@ini_set('log_errors', TRUE); +@ini_set('log_errors', true); error_reporting(E_ALL ^ E_NOTICE); // Set default locale @@ -31,7 +33,7 @@ setlocale(LC_ALL, 'en_US.UTF8'); @ini_set('default_charset', 'utf-8'); // Set G\ paths and files -define('G_ROOT_PATH', rtrim(str_replace('\\','/', dirname(dirname(__DIR__))), '/') . '/'); +define('G_ROOT_PATH', rtrim(str_replace('\\', '/', dirname(dirname(__DIR__))), '/') . '/'); define('G_ROOT_PATH_RELATIVE', rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/') . '/'); define('G_ROOT_LIB_PATH', G_ROOT_PATH . 'lib/'); define('G_PATH', G_ROOT_LIB_PATH . 'G/'); @@ -52,65 +54,75 @@ define('G_APP_SETTINGS_FILE_ERROR', '
There are errors in the %%FIL // Include the static app config file (file_exists(G_APP_PATH . 'settings.php')) ? require_once(G_APP_PATH . 'settings.php') : die("G\: Can't find app/settings.php"); -if(headers_sent()) die(str_replace('%%FILE%%', 'app/settings.php', G_APP_SETTINGS_FILE_ERROR)); // Stop on premature headers +if (headers_sent()) { + die(str_replace('%%FILE%%', 'app/settings.php', G_APP_SETTINGS_FILE_ERROR)); +} // Stop on premature headers // TZ failover $tz = @date_default_timezone_get(); $dtz = @date_default_timezone_set($tz); -if(!$dtz && !@date_default_timezone_set('America/Santiago')) { - die(strtr('Invalid timezone identifier: %i. Configure php.ini with a valid timezone identifier %l', ['%i' => $tz, '%l' => 'http://php.net/manual/en/timezones.php'])); +if (!$dtz && !@date_default_timezone_set('America/Santiago')) { + die(strtr('Invalid timezone identifier: %i. Configure php.ini with a valid timezone identifier %l', ['%i' => $tz, '%l' => 'http://php.net/manual/en/timezones.php'])); } // Session hack -if($settings['session.save_path']) { - session_save_path($settings['session.save_path']); +if ($settings['session.save_path']) { + session_save_path($settings['session.save_path']); } // Can work with sessions? -if(!@session_start()) die("G\: Sessions are not working on this server (session_start)."); +if (!@session_start()) { + die("G\: Sessions are not working on this server (session_start)."); +} // Is session save path OK? (you won't believe how many people has session issues!) $session_save_path = @realpath(session_save_path()); -if($session_save_path) { // realpath on this needs pre-webroot directories access - foreach(['write'] as $k) { - $fn = 'is_' . $k . 'able'; - if(!$fn($session_save_path)) $session_errors[] = $k; - } - if(isset($session_errors)) die(strtr("G\: Sessions are not working on this server due to missing %s permission on session save path (%f session.save_path).", ['%s' => implode('/', $session_errors), '%f' => $settings['session.save_path'] ? 'app/settings.php' : 'php.ini'])); +if ($session_save_path) { // realpath on this needs pre-webroot directories access + foreach (['write'] as $k) { + $fn = 'is_' . $k . 'able'; + if (!$fn($session_save_path)) { + $session_errors[] = $k; + } + } + if (isset($session_errors)) { + die(strtr("G\: Sessions are not working on this server due to missing %s permission on session save path (%f session.save_path).", ['%s' => implode('/', $session_errors), '%f' => $settings['session.save_path'] ? 'app/settings.php' : 'php.ini'])); + } } // Are sessions working properly? -$_SESSION['G'] = TRUE; -if(!$_SESSION['G']) die("G\: Sessions are not working properly. Check for any conflicting server setting."); +$_SESSION['G'] = true; +if (!$_SESSION['G']) { + die("G\: Sessions are not working properly. Check for any conflicting server setting."); +} // Set the starting execution time define('G_APP_TIME_EXECUTION_START', microtime(true)); // Include G\ core functions (file_exists(__DIR__ . '/functions.php')) ? require_once(__DIR__ . '/functions.php') : die("G\: Can't find " . __DIR__ . '/functions.php' . '. Make sure that this file exists.'); -if(file_exists(__DIR__ . '/functions.render.php')) { - require_once(__DIR__ . '/functions.render.php'); +if (file_exists(__DIR__ . '/functions.render.php')) { + require_once(__DIR__ . '/functions.render.php'); } -if(isset($settings) && $settings['error_reporting'] === false) { - error_reporting(0); +if (isset($settings) && $settings['error_reporting'] === false) { + error_reporting(0); } // Set the default timezone -if(isset($settings['default_timezone']) && is_valid_timezone($settings['default_timezone'])) { - if(!@date_default_timezone_set($settings['default_timezone'])) { - die(strtr("G\: Can't set %s timezone on line %l", ['%s' => $settings['default_timezone'], '%l' => __LINE__ - 1])); - } +if (isset($settings['default_timezone']) && is_valid_timezone($settings['default_timezone'])) { + if (!@date_default_timezone_set($settings['default_timezone'])) { + die(strtr("G\: Can't set %s timezone on line %l", ['%s' => $settings['default_timezone'], '%l' => __LINE__ - 1])); + } } // Set the system environment -if(isset($settings['environment'])) { - define('G_APP_ENV', $settings['environment']); +if (isset($settings['environment'])) { + define('G_APP_ENV', $settings['environment']); } // Set the HTTP definitions define('G_HTTP_HOST', $_SERVER['HTTP_HOST']); -define('G_HTTP_PROTOCOL', 'http' . ((((!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ) || $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || $settings['https']) ? 's' : NULL)); +define('G_HTTP_PROTOCOL', 'http' . ((((!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') || $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || $settings['https']) ? 's' : null)); // La cumbia me divierte y mesita @@ -118,24 +130,24 @@ define('G_HTTP_PROTOCOL', 'http' . ((((!empty($_SERVER['HTTPS']) && strtolower($ $_SERVER['SCRIPT_FILENAME'] = forward_slash($_SERVER['SCRIPT_FILENAME']); $_SERVER['SCRIPT_NAME'] = forward_slash($_SERVER['SCRIPT_NAME']); // Fix CloudFlare REMOTE_ADDR -if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { - $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; +if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { + $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; } // Inherit application definitions -if(file_exists(G_APP_PATH . 'app.php')) { - require_once(G_APP_PATH . 'app.php'); +if (file_exists(G_APP_PATH . 'app.php')) { + require_once(G_APP_PATH . 'app.php'); } // Set the DB constants -foreach(['host', 'port', 'name', 'user', 'pass', 'driver', 'pdo_attrs'] as $k) { - define('G_APP_DB_' . strtoupper($k), isset($settings['db_' . $k]) ? (is_array($settings['db_' . $k]) ? serialize($settings['db_' . $k]) : $settings['db_' . $k]) : NULL); +foreach (['host', 'port', 'name', 'user', 'pass', 'driver', 'pdo_attrs'] as $k) { + define('G_APP_DB_' . strtoupper($k), isset($settings['db_' . $k]) ? (is_array($settings['db_' . $k]) ? serialize($settings['db_' . $k]) : $settings['db_' . $k]) : null); } // Include app functions (file_exists(G_APP_FILE_FUNCTIONS)) ? require_once(G_APP_FILE_FUNCTIONS) : die("G\: Can't find " . G_APP_FILE_FUNCTIONS . '. Make sure that this file exists.'); -if(file_exists(G_APP_FILE_FUNCTIONS_RENDER)) { - require_once(G_APP_FILE_FUNCTIONS_RENDER); +if (file_exists(G_APP_FILE_FUNCTIONS_RENDER)) { + require_once(G_APP_FILE_FUNCTIONS_RENDER); } // Set the URLs @@ -145,11 +157,11 @@ define("G_APP_LIB_URL", absolute_to_url(G_APP_PATH_LIB)); // Define the app theme define('G_APP_PATH_THEMES', G_APP_PATH . 'themes/'); -if(!file_exists(G_APP_PATH_THEMES)) { - die("G\: Theme path doesn't exists!"); +if (!file_exists(G_APP_PATH_THEMES)) { + die("G\: Theme path doesn't exists!"); } -if(isset($settings['theme']) and file_exists(G_APP_PATH_THEMES . $settings['theme'])) { - define('G_APP_PATH_THEME', G_APP_PATH_THEMES . $settings['theme'].'/'); - define('BASE_URL_THEME', absolute_to_url(G_APP_PATH_THEME)); -} \ No newline at end of file +if (isset($settings['theme']) and file_exists(G_APP_PATH_THEMES . $settings['theme'])) { + define('G_APP_PATH_THEME', G_APP_PATH_THEMES . $settings['theme'].'/'); + define('BASE_URL_THEME', absolute_to_url(G_APP_PATH_THEME)); +} diff --git a/lib/G/classes/class.db.php b/lib/G/classes/class.db.php index ad7add3..9c88989 100644 --- a/lib/G/classes/class.db.php +++ b/lib/G/classes/class.db.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. @@ -20,528 +20,550 @@ */ namespace G; -use PDO, PDOException, Exception; - -class DB { - - private static $instance; - - private $host = G_APP_DB_HOST; - private $port = G_APP_DB_PORT; - private $name = G_APP_DB_NAME; - private $user = G_APP_DB_USER; - private $pass = G_APP_DB_PASS; - private $driver = G_APP_DB_DRIVER; - private $pdo_attrs = G_APP_DB_PDO_ATTRS; - - static $dbh; - public $query; - - /** - * Connect to the DB server - * Throws an Exception on error (tay weando? en serio?) - */ - public function __construct($conn=[]) { - - try { - // PDO already connected - if(empty($conn) and isset(self::$dbh) and get_class(self::$dbh) == 'PDO') { - return TRUE; - } - - if(!empty($conn)) { - // Inject connection info - foreach(['host', 'user', 'name', 'pass', 'port', 'driver', 'pdo_attrs'] as $k) { - $this->{$k} = $conn[$k]; - } - } - - $pdo_connect = $this->driver . ':host=' . $this->host . ';dbname=' . $this->name; - if($this->port) { - $pdo_connect .= ';port=' . $this->port; - } - - $this->pdo_attrs = @unserialize($this->pdo_attrs) ?: $this->pdo_attrs; - - // PDO defaults - $this->pdo_default_attrs = [ - PDO::ATTR_TIMEOUT => 30, - //PDO::ATTR_PERSISTENT => FALSE - ]; - - // Override PDO defaults ? - $this->pdo_attrs = (is_array($this->pdo_attrs) ? $this->pdo_attrs : []) + $this->pdo_default_attrs; - - // PDO hard overrides - $this->pdo_attrs[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; - $this->pdo_attrs[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; - - // Turn off PHP error reporting just for the connection here (invalid host names will trigger a PHP warning) - $error_reporting = error_reporting(); - error_reporting(0); - - // Note that PDO::ERRMODE_SILENT has no effect on connection. Connections always throw an exception if it fails - self::$dbh = new PDO($pdo_connect, $this->user, $this->pass, $this->pdo_attrs); - - // Re-enable the error_reporting level - error_reporting($error_reporting); - - // PDO emulate prepares if needed - if(version_compare(self::$dbh->getAttribute(PDO::ATTR_SERVER_VERSION), '5.1.17', '<')) { - self::$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); - } - - self::$instance = $this; - - } catch(Exception $e) { - self::$dbh = NULL; - throw new DBException($e->getMessage(), 400); - } - - } - - /** - * Singleton instance handler - * Used for the static methods of this class - */ - public static function getInstance() { - if(is_null(self::$instance)) { - self::$instance = new self; - } - return self::$instance; - } - - /** - * Populates the class DB own PDO attributes array with an entire array - * Attribute list here: http://php.net/manual/en/pdo.setattribute.php - */ - public function setPDOAttrs($attributes) { - $this->pdo_attrs = $attributes; - } - - /** - * Populates the class DB own PDO attributes array with a single key - * Attributes list here: http://php.net/manual/en/pdo.setattribute.php - */ - public function setPDOAttr($key, $value) { - $this->pdo_attrs[$key] = $value; - } - - public function getAttr($attr) { - return self::$dbh->getAttribute($attr); - } - - /** - * Prepares an SQL statement to be executed by the PDOStatement::execute() method - * http://php.net/manual/en/pdo.prepare.php - */ - public function query($query) { - $this->query = self::$dbh->prepare($query); - } - - public function errorInfo() { - return self::$dbh->errorInfo(); - } - - /** - * Binds a value to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement - * http://php.net/manual/en/pdostatement.bindvalue.php - */ - public function bind($param, $value, $type = null) { - if(is_null($type)) { - switch(true) { - case is_int($value): - $type = PDO::PARAM_INT; - break; - case is_bool($value): - $type = PDO::PARAM_BOOL; - break; - case is_null($value): - $type = PDO::PARAM_NULL; - break; - default: - $type = PDO::PARAM_STR; - break; - } - } - $this->query->bindValue($param, $value, $type); - } - - public function exec() { - return $this->query->execute(); - } - - public function fetchColumn() { - return $this->query->fetchColumn(); - } - - public function closeCursor() { - return $this->query->closeCursor(); - } - - public function fetchAll($mode=PDO::FETCH_ASSOC) { - $this->exec(); - return $this->query->fetchAll(is_int($mode) ? $mode : PDO::FETCH_ASSOC); - } - - /** - * Execute and returns the single result from the prepared statement - * http://php.net/manual/en/pdostatement.fetch.php - */ - public function fetchSingle($mode=PDO::FETCH_ASSOC) { - $this->exec(); - return $this->query->fetch(is_int($mode) ? $mode : PDO::FETCH_ASSOC); - } - - /** - * Query and exec, return number of affected rows or FALSE - */ - public static function queryExec($query) { - try { - $db = self::getInstance(); - $db->query($query); - return $db->exec() ? $db->rowCount() : FALSE; - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - } - - /** - * Query and fetch single record - */ - public static function queryFetchSingle($query, $fetch_style=NULL) { - try { - return self::queryFetch($query, 1, $fetch_style); - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - } - - /** - * Query and fetch all records - */ - public static function queryFetchAll($query, $fetch_style=NULL) { - try { - return self::queryFetch($query, NULL, $fetch_style); - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - } - - /** - * Query fetch (core version) - */ - public static function queryFetch($query, $limit=1, $fetch_style=NULL) { - try { - $db = self::getInstance(); - $db->query($query); - return $limit == 1 ? $db->fetchSingle($fetch_style) : $db->fetchAll($fetch_style); - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - } - - /** - * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed - * http://php.net/manual/en/pdostatement.rowcount.php - */ - public function rowCount() { - return $this->query->rowCount(); - } - - /** - * Returns the ID of the last inserted row, or the last value from a sequence object, depending on the underlying driver - * http://php.net/manual/en/pdo.lastinsertid.php - */ - public function lastInsertId() { - return self::$dbh->lastInsertId(); - } - - /** - * Turns off autocommit mode - * http://php.net/manual/en/pdo.begintransaction.php - */ - public function beginTransaction(){ - return self::$dbh->beginTransaction(); - } - - /** - * Commits a transaction, returning the database connection to autocommit mode until the next call to PDO::beginTransaction() starts a new transaction - * http://php.net/manual/en/pdo.commit.php - */ - public function endTransaction(){ - return self::$dbh->commit(); - } - - /** - * Rolls back the current transaction, as initiated by PDO::beginTransaction() - * http://php.net/manual/en/pdo.rollback.php - */ - public function cancelTransaction(){ - return self::$dbh->rollBack(); - } - - /** - * Dumps the informations contained by a prepared statement directly on the output - * http://php.net/manual/en/pdostatement.debugdumpparams.php - */ - public function debugDumpParams(){ - return $this->query->debugDumpParams(); - } - - /* Now the G\ fast DB methods, presented by Chevereto */ - - /** - * Get the table with its prefix - */ - public static function getTable($table) { - return get_app_setting('db_table_prefix') . $table; - } - - /** - * Get values from DB - */ - public static function get($table, $values, $clause='AND', $sort=[], $limit=NULL, $fetch_style=NULL) { - - if(!is_array($values) and $values !== 'all') { - throw new DBException('Expecting array values, '.gettype($values).' given in ' . __METHOD__, 100); - } - - self::validateClause($clause, __METHOD__); - - if(is_array($table)) { - $join = $table['join']; - $table = $table['table']; - } - - $table = DB::getTable($table); - - $query = 'SELECT * FROM '.$table; - - if($join) { - $query .= ' ' . $join . ' '; - } - - if(is_array($values) and !empty($values)) { - $query .= ' WHERE '; - foreach($values as $k => $v) { - if(is_null($v)) { - $query .= '`'.$k.'` IS :'.$k.' '.$clause.' '; - } else { - $query .= '`'.$k.'`=:'.$k.' '.$clause.' '; - } - } - } - - $query = rtrim($query, $clause . ' '); - - if(is_array($sort) and !empty($sort)) { - if(!$sort['field']) { - $sort['field'] = 'date'; - } - if(!$sort['order']) { - $sort['order'] = 'desc'; - } - $query .= ' ORDER BY '.$sort['field'].' '.strtoupper($sort['order']).' '; - } - - if($limit and is_int($limit)) { - $query .= " LIMIT $limit"; - } - - try { - $db = self::getInstance(); - $db->query($query); - if(is_array($values)) { - foreach($values as $k => $v) { - $db->bind(':'.$k, $v); - } - } - return $limit == 1 ? $db->fetchSingle($fetch_style) : $db->fetchAll($fetch_style); - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - } - - /** - * Update target table row(s) - * Returns the number of affected rows or false - */ - public static function update($table, $values, $wheres, $clause='AND') { - - if(!is_array($values)) { - throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); - } - if(!is_array($wheres)) { - throw new DBException('Expecting array values, '.gettype($wheres).' given in '. __METHOD__, 100); - } - - self::validateClause($clause, __METHOD__); - - $table = DB::getTable($table); - - $query = 'UPDATE `'.$table.'` SET '; - - // Set the value pairs - foreach($values as $k => $v) { - $query .= '`' . $k . '`=:value_' . $k . ','; - } - $query = rtrim($query, ',') . ' WHERE '; - - // Set the where pairs - foreach($wheres as $k => $v) { - $query .= '`'.$k.'`=:where_'.$k.' '.$clause.' '; - } - $query = rtrim($query, $clause.' '); - - try { - $db = self::getInstance(); - $db->query($query); - - // Bind the values - foreach($values as $k => $v) { - $db->bind(':value_'.$k, $v); - } - foreach($wheres as $k => $v) { - $db->bind(':where_'.$k, $v); - } - - return $db->exec() ? $db->rowCount() : FALSE; - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - - } - - /** - * Insert single row to the table - */ - public static function insert($table, $values) { - - if(!is_array($values)) { - throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); - } - - $table = DB::getTable($table); - - $table_fields = []; - foreach($values as $k => $v) { - $table_fields[] = $k; - } - - $query = 'INSERT INTO + +use PDO; +use PDOException; +use Exception; + +class DB +{ + private static $instance; + + private $host = G_APP_DB_HOST; + private $port = G_APP_DB_PORT; + private $name = G_APP_DB_NAME; + private $user = G_APP_DB_USER; + private $pass = G_APP_DB_PASS; + private $driver = G_APP_DB_DRIVER; + private $pdo_attrs = G_APP_DB_PDO_ATTRS; + + public static $dbh; + public $query; + + /** + * Connect to the DB server + * Throws an Exception on error (tay weando? en serio?) + */ + public function __construct($conn=[]) + { + try { + // PDO already connected + if (empty($conn) and isset(self::$dbh) and get_class(self::$dbh) == 'PDO') { + return true; + } + + if (!empty($conn)) { + // Inject connection info + foreach (['host', 'user', 'name', 'pass', 'port', 'driver', 'pdo_attrs'] as $k) { + $this->{$k} = $conn[$k]; + } + } + + $pdo_connect = $this->driver . ':host=' . $this->host . ';dbname=' . $this->name; + if ($this->port) { + $pdo_connect .= ';port=' . $this->port; + } + // $pdo_connect .= ';charset=UTF8'; + + $this->pdo_attrs = @unserialize($this->pdo_attrs) ?: $this->pdo_attrs; + + // PDO defaults + $this->pdo_default_attrs = [ + PDO::ATTR_TIMEOUT => 30, + ]; + + // Override PDO defaults ? + $this->pdo_attrs = (is_array($this->pdo_attrs) ? $this->pdo_attrs : []) + $this->pdo_default_attrs; + + // PDO hard overrides + $this->pdo_attrs[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; + $this->pdo_attrs[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET time_zone = '+00:00', NAMES 'UTF8'"; // UTC for timestamps + + // Turn off PHP error reporting just for the connection here (invalid host names will trigger a PHP warning) + $error_reporting = error_reporting(); + error_reporting(0); + + // Note that PDO::ERRMODE_SILENT has no effect on connection. Connections always throw an exception if it fails + self::$dbh = new PDO($pdo_connect, $this->user, $this->pass, $this->pdo_attrs); + + // Re-enable the error_reporting level + error_reporting($error_reporting); + + // PDO emulate prepares if needed + if (version_compare(self::$dbh->getAttribute(PDO::ATTR_SERVER_VERSION), '5.1.17', '<')) { + self::$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + } + + self::$instance = $this; + } catch (Exception $e) { + self::$dbh = null; + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Singleton instance handler + * Used for the static methods of this class + */ + public static function getInstance() + { + if (is_null(self::$instance)) { + self::$instance = new self; + } + return self::$instance; + } + + /** + * Populates the class DB own PDO attributes array with an entire array + * Attribute list here: http://php.net/manual/en/pdo.setattribute.php + */ + public function setPDOAttrs($attributes) + { + $this->pdo_attrs = $attributes; + } + + /** + * Populates the class DB own PDO attributes array with a single key + * Attributes list here: http://php.net/manual/en/pdo.setattribute.php + */ + public function setPDOAttr($key, $value) + { + $this->pdo_attrs[$key] = $value; + } + + public function getAttr($attr) + { + return self::$dbh->getAttribute($attr); + } + + /** + * Prepares an SQL statement to be executed by the PDOStatement::execute() method + * http://php.net/manual/en/pdo.prepare.php + */ + public function query($query) + { + $this->query = self::$dbh->prepare($query); + } + + public function errorInfo() + { + return self::$dbh->errorInfo(); + } + + /** + * Binds a value to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement + * http://php.net/manual/en/pdostatement.bindvalue.php + */ + public function bind($param, $value, $type = null) + { + if (is_null($type)) { + switch (true) { + case is_int($value): + $type = PDO::PARAM_INT; + break; + case is_bool($value): + $type = PDO::PARAM_BOOL; + break; + case is_null($value): + $type = PDO::PARAM_NULL; + break; + default: + $type = PDO::PARAM_STR; + break; + } + } + $this->query->bindValue($param, $value, $type); + } + + public function exec() + { + return $this->query->execute(); + } + + public function fetchColumn() + { + return $this->query->fetchColumn(); + } + + public function closeCursor() + { + return $this->query->closeCursor(); + } + + public function fetchAll($mode=PDO::FETCH_ASSOC) + { + $this->exec(); + return $this->query->fetchAll(is_int($mode) ? $mode : PDO::FETCH_ASSOC); + } + + /** + * Execute and returns the single result from the prepared statement + * http://php.net/manual/en/pdostatement.fetch.php + */ + public function fetchSingle($mode=PDO::FETCH_ASSOC) + { + $this->exec(); + return $this->query->fetch(is_int($mode) ? $mode : PDO::FETCH_ASSOC); + } + + /** + * Query and exec, return number of affected rows or FALSE + */ + public static function queryExec($query) + { + try { + $db = self::getInstance(); + $db->query($query); + return $db->exec() ? $db->rowCount() : false; + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Query and fetch single record + */ + public static function queryFetchSingle($query, $fetch_style=null) + { + try { + return self::queryFetch($query, 1, $fetch_style); + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Query and fetch all records + */ + public static function queryFetchAll($query, $fetch_style=null) + { + try { + return self::queryFetch($query, null, $fetch_style); + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Query fetch (core version) + */ + public static function queryFetch($query, $limit=1, $fetch_style=null) + { + try { + $db = self::getInstance(); + $db->query($query); + return $limit == 1 ? $db->fetchSingle($fetch_style) : $db->fetchAll($fetch_style); + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed + * http://php.net/manual/en/pdostatement.rowcount.php + */ + public function rowCount() + { + return $this->query->rowCount(); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, depending on the underlying driver + * http://php.net/manual/en/pdo.lastinsertid.php + */ + public function lastInsertId() + { + return self::$dbh->lastInsertId(); + } + + /** + * Turns off autocommit mode + * http://php.net/manual/en/pdo.begintransaction.php + */ + public function beginTransaction() + { + return self::$dbh->beginTransaction(); + } + + /** + * Commits a transaction, returning the database connection to autocommit mode until the next call to PDO::beginTransaction() starts a new transaction + * http://php.net/manual/en/pdo.commit.php + */ + public function endTransaction() + { + return self::$dbh->commit(); + } + + /** + * Rolls back the current transaction, as initiated by PDO::beginTransaction() + * http://php.net/manual/en/pdo.rollback.php + */ + public function cancelTransaction() + { + return self::$dbh->rollBack(); + } + + /** + * Dumps the informations contained by a prepared statement directly on the output + * http://php.net/manual/en/pdostatement.debugdumpparams.php + */ + public function debugDumpParams() + { + return $this->query->debugDumpParams(); + } + + /* Now the G\ fast DB methods, presented by Chevereto */ + + /** + * Get the table with its prefix + */ + public static function getTable($table) + { + return get_app_setting('db_table_prefix') . $table; + } + + /** + * Get values from DB + */ + public static function get($table, $values, $clause='AND', $sort=[], $limit=null, $fetch_style=null) + { + if (!is_array($values) and $values !== 'all') { + throw new DBException('Expecting array values, '.gettype($values).' given in ' . __METHOD__, 100); + } + + self::validateClause($clause, __METHOD__); + + if (is_array($table)) { + $join = $table['join']; + $table = $table['table']; + } + + $table = DB::getTable($table); + + $query = 'SELECT * FROM '.$table; + + if ($join) { + $query .= ' ' . $join . ' '; + } + + if (is_array($values) and !empty($values)) { + $query .= ' WHERE '; + foreach ($values as $k => $v) { + if (is_null($v)) { + $query .= '`'.$k.'` IS :'.$k.' '.$clause.' '; + } else { + $query .= '`'.$k.'`=:'.$k.' '.$clause.' '; + } + } + } + + $query = rtrim($query, $clause . ' '); + + if (is_array($sort) and !empty($sort)) { + if (!$sort['field']) { + $sort['field'] = 'date'; + } + if (!$sort['order']) { + $sort['order'] = 'desc'; + } + $query .= ' ORDER BY '.$sort['field'].' '.strtoupper($sort['order']).' '; + } + + if ($limit and is_int($limit)) { + $query .= " LIMIT $limit"; + } + + try { + $db = self::getInstance(); + $db->query($query); + if (is_array($values)) { + foreach ($values as $k => $v) { + $db->bind(':'.$k, $v); + } + } + return $limit == 1 ? $db->fetchSingle($fetch_style) : $db->fetchAll($fetch_style); + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Update target table row(s) + * Returns the number of affected rows or false + */ + public static function update($table, $values, $wheres, $clause='AND') + { + if (!is_array($values)) { + throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); + } + if (!is_array($wheres)) { + throw new DBException('Expecting array values, '.gettype($wheres).' given in '. __METHOD__, 100); + } + + self::validateClause($clause, __METHOD__); + + $table = DB::getTable($table); + + $query = 'UPDATE `'.$table.'` SET '; + + // Set the value pairs + foreach ($values as $k => $v) { + $query .= '`' . $k . '`=:value_' . $k . ','; + } + $query = rtrim($query, ',') . ' WHERE '; + + // Set the where pairs + foreach ($wheres as $k => $v) { + $query .= '`'.$k.'`=:where_'.$k.' '.$clause.' '; + } + $query = rtrim($query, $clause.' '); + + try { + $db = self::getInstance(); + $db->query($query); + + // Bind the values + foreach ($values as $k => $v) { + $db->bind(':value_'.$k, $v); + } + foreach ($wheres as $k => $v) { + $db->bind(':where_'.$k, $v); + } + + return $db->exec() ? $db->rowCount() : false; + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + + /** + * Insert single row to the table + */ + public static function insert($table, $values) + { + if (!is_array($values)) { + throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); + } + + $table = DB::getTable($table); + + $table_fields = []; + foreach ($values as $k => $v) { + $table_fields[] = $k; + } + + $query = 'INSERT INTO `'.$table.'` (`' . ltrim(implode('`,`', $table_fields), '`,`') . '`) VALUES (' . ':' . str_replace(':', ',:', implode(':', $table_fields)) . ')'; - try { - $db = self::getInstance(); - $db->query($query); - foreach($values as $k => $v) { - $db->bind(':'.$k, $v); - } - return $db->exec() ? $db->lastInsertId() : FALSE; - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } + try { + $db = self::getInstance(); + $db->query($query); + foreach ($values as $k => $v) { + $db->bind(':'.$k, $v); + } + return $db->exec() ? $db->lastInsertId() : false; + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } - } + /** + * Update target numecic table row(s) with and increment (positive or negative) + * Returns the number of affected rows or false + * Note: Minimum value to be set is zero, no negative values here + */ + public static function increment($table, $values, $wheres, $clause='AND') + { + foreach (['values', 'wheres'] as $k) { + if (!is_array(${$k})) { + throw new DBException('Expecting array values, '.gettype(${$k}).' given in '. __METHOD__, 100); + } + } - /** - * Update target numecic table row(s) with and increment (positive or negative) - * Returns the number of affected rows or false - * Note: Minimum value to be set is zero, no negative values here - */ - public static function increment($table, $values, $wheres, $clause='AND') { + $table = DB::getTable($table); + $query = 'UPDATE `'.$table.'` SET '; - foreach(['values', 'wheres'] as $k) { - if(!is_array(${$k})) { - throw new DBException('Expecting array values, '.gettype(${$k}).' given in '. __METHOD__, 100); - } - } + foreach ($values as $k => $v) { + if (preg_match('/^([+-]{1})\s*([\d]+)$/', $v, $matches)) { // 1-> op 2-> number + $query .= '`' . $k . '`='; + if ($matches[1] == '+') { + $query .= '`' . $k . '`' . $matches[1] . $matches[2] . ','; + } + if ($matches[1] == '-') { + $query .= 'GREATEST(cast(`'.$k.'` AS SIGNED) - '.$matches[2].', 0),'; + } + } + } - $table = DB::getTable($table); - $query = 'UPDATE `'.$table.'` SET '; + $query = rtrim($query, ',') . ' WHERE '; - foreach($values as $k => $v) { - if(preg_match('/^([+-]{1})\s*([\d]+)$/', $v, $matches)) { // 1-> op 2-> number - $query .= '`' . $k . '`='; - if($matches[1] == '+') { - $query .= '`' . $k . '`' . $matches[1] . $matches[2] . ','; - } - if($matches[1] == '-') { - $query .= 'GREATEST(cast(`'.$k.'` AS SIGNED) - '.$matches[2].', 0),'; - } - } - } + // Set the where pairs + foreach ($wheres as $k => $v) { + $query .= '`'.$k.'`=:where_'.$k.' '.$clause.' '; + } + $query = rtrim($query, $clause.' '); - $query = rtrim($query, ',') . ' WHERE '; + try { + $db = self::getInstance(); + $db->query($query); + foreach ($wheres as $k => $v) { + $db->bind(':where_'.$k, $v); + } + return $db->exec() ? $db->rowCount() : false; + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } - // Set the where pairs - foreach($wheres as $k => $v) { - $query .= '`'.$k.'`=:where_'.$k.' '.$clause.' '; - } - $query = rtrim($query, $clause.' '); + /** + * Delete row(s) from table + * Returns the number of affected rows or false + */ + public static function delete($table, $values, $clause='AND') + { + if (!is_array($values)) { + throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); + } - try { - $db = self::getInstance(); - $db->query($query); - foreach($wheres as $k => $v) { - $db->bind(':where_'.$k, $v); - } - return $db->exec() ? $db->rowCount() : false; - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } + self::validateClause($clause, __METHOD__); - } + $table = DB::getTable($table); + $query = 'DELETE FROM `'.$table.'` WHERE '; - /** - * Delete row(s) from table - * Returns the number of affected rows or false - */ - public static function delete($table, $values, $clause='AND') { + $table_fields = array(); + foreach ($values as $k => $v) { + $query .= '`'.$k.'`=:'.$k.' '.$clause.' '; + } + $query = rtrim($query, $clause.' '); - if(!is_array($values)) { - throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100); - } - - self::validateClause($clause, __METHOD__); - - $table = DB::getTable($table); - $query = 'DELETE FROM `'.$table.'` WHERE '; - - $table_fields = array(); - foreach($values as $k => $v) { - $query .= '`'.$k.'`=:'.$k.' '.$clause.' '; - } - $query = rtrim($query, $clause.' '); - - try { - $db = self::getInstance(); - $db->query($query); - foreach($values as $k => $v) { - $db->bind(':'.$k, $v); - } - return $db->exec() ? $db->rowCount() : FALSE; - } catch(Exception $e) { - throw new DBException($e->getMessage(), 400); - } - - } - - /** - * Validate clause - */ - private static function validateClause($clause, $method=NULL) { - if(!is_null($clause)) { - $clause = strtoupper($clause); - if(!in_array($clause, ['AND', 'OR'])) { - throw new DBException('Expecting clause string \'AND\' or \'OR\' in ' . (!is_null($method) ? $method : __CLASS__), 100); - } - } - } + try { + $db = self::getInstance(); + $db->query($query); + foreach ($values as $k => $v) { + $db->bind(':'.$k, $v); + } + return $db->exec() ? $db->rowCount() : false; + } catch (Exception $e) { + throw new DBException($e->getMessage(), 400); + } + } + /** + * Validate clause + */ + private static function validateClause($clause, $method=null) + { + if (!is_null($clause)) { + $clause = strtoupper($clause); + if (!in_array($clause, ['AND', 'OR'])) { + throw new DBException('Expecting clause string \'AND\' or \'OR\' in ' . (!is_null($method) ? $method : __CLASS__), 100); + } + } + } } // DB class own Exception -class DBException extends Exception {} \ No newline at end of file +class DBException extends Exception +{ +} diff --git a/lib/G/classes/class.gettext.php b/lib/G/classes/class.gettext.php index 42e8763..882f086 100644 --- a/lib/G/classes/class.gettext.php +++ b/lib/G/classes/class.gettext.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. diff --git a/lib/G/classes/class.handler.php b/lib/G/classes/class.handler.php index 227e2f9..edc58f5 100644 --- a/lib/G/classes/class.handler.php +++ b/lib/G/classes/class.handler.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. @@ -20,527 +20,571 @@ */ namespace G; + use Exception; -class Handler { - - public static $route, $route_request, $route_name, $base_request, $doctitle, $vars, $conds, $routes, $template_used, $prevented_route, $mapped_args; - - /** - * Build a valid request - */ - function __construct($hook=[]) { - - if(!defined('G_APP_PATH_THEME')) { - throw new HandlerException('G_APP_PATH_THEME is not defined', 100); - } - - // Parse the definitions to this object.. This is not necessary but in case of changes... - $this->relative_root = G_ROOT_PATH_RELATIVE; // nota: realmente necesitamos estos this? - $this->base_url = G_ROOT_URL; - $this->path_theme = G_APP_PATH_THEME; - - // Parse the request - $this->request_uri = $_SERVER['REQUEST_URI']; - $this->script_name = $_SERVER['SCRIPT_NAME']; - - $query_string = '?' . $_SERVER['QUERY_STRING']; - - if(!empty($_SERVER['QUERY_STRING'])) { - $this->request_uri = str_replace($query_string, '/', $this->request_uri); - } - - $this->valid_request = '/' . ltrim(rtrim(sanitize_path_slashes($this->request_uri), '/'), '/'); - - if(!empty($_SERVER['QUERY_STRING'])) { - $this->request_uri = $_SERVER['REQUEST_URI']; - $this->valid_request .= '/' . $query_string; - } - - // Store the canonical request, useful for redirect to a valid request - $this->canonical_request = $this->valid_request; - - if(is_dir(G_ROOT_PATH . $this->valid_request) && $this->valid_request !== '/') { - $this->canonical_request .= '/'; - } - - $this->handled_request = strtok($this->relative_root == '/' ? $this->valid_request : preg_replace('#' . $this->relative_root . '#', '/', $this->request_uri, 1),'?'); - $this->request_array = explode('/', rtrim(str_replace('//', '/', ltrim($this->handled_request, '/')), '/')); - - // Index request - if($this->request_array[0] == '') { - $this->request_array[0] = '/'; - } - - $this->request_array = array_values(array_filter($this->request_array, 'strlen')); - self::$base_request = $this->request_array[0]; - - // Reserved route (index) - if(self::$base_request == 'index') { - redirect('/', 301); - } - - // Fix the canonical request /something?q= to /something/?q= - if(self::$base_request !== '' && !empty($_SERVER['QUERY_STRING'])) { - $path_request = add_trailing_slashes(rtrim(str_replace($_SERVER['QUERY_STRING'], '', $this->canonical_request), '?')); - $fixed_qs_request = $path_request.'?'.$_SERVER['QUERY_STRING']; - $this->canonical_request = $fixed_qs_request; - } - - // No /index.php request - if(self::$base_request == 'index.php') { - $this->canonical_request = rtrim($this->canonical_request, '/'); - redirect((sanitize_path_slashes(str_replace('index.php', '', $this->canonical_request))), 301); - } - - // If the request is invalid we make a 301 redirection to the canonical url. - if($this->relative_root !== $this->request_uri and $this->canonical_request !== $this->request_uri) { - $this->baseRedirection($this->canonical_request); - } - - if(in_array(self::$base_request, ['', 'index.php', '/'])) { - self::$base_request = 'index'; - } - - $this->template = self::$base_request; - $this->request = $this->request_array; - - self::$route_request = $this->request_array; - self::$route = $this->template !== 404 ? $this->request_array[0] == '/' ? 'index' : $this->request_array : 404; - - unset($this->request[0]); - $this->request = array_values($this->request); - - // Hook a fn BEFORE the process - if(is_array($hook) and is_callable($hook['before'])) { - $hook['before']($this); - } - - // It is a valid request on index.php? - if($this->isIndex()) $this->processRequest(); - - // Hook a fn AFTER the process - if(is_array($hook) and is_callable($hook['after'])) { - $hook['after']($this); - } - - // Auto-bind the route vars - if(is_array(self::$vars)) { - foreach(self::$vars as $k => $v) { - $this->bindGetFn($k, $v); - } - } - // Auto-bind the route conditionals - if(is_array(self::$conds)) { - foreach(self::$conds as $k => $v) { - $this->bindIsFn($k, $v); - } - } - - $this->loadTemplate(); - - } - - /** - * Iterate over the route app folder - * This populates Handler::$routes with all the valid routes - */ - private static function routeIterator($path) { - - if(!file_exists($path)) return; - - foreach(new \DirectoryIterator($path) as $fileInfo) { - - if($fileInfo->isDot() or $fileInfo->isDir()) continue; - - $route_file = $path . $fileInfo->getFilename(); - $route_override = $path . 'overrides/' . $fileInfo->getFilename(); - - if(file_exists($route_override)) { - $route_file = $route_override; - } - - if(file_exists($route_file)) { - require_once($route_file); - $route = array(substr(substr($fileInfo->getFilename(), 0, -4), 6) => $route); - self::$routes += $route; - } - } - } - - /** - * Stock (save) the valid routes of the G\ app - * This method is optional because the routeIterator takes some memory - */ - public static function stockRoutes() { - self::$routes = []; - self::routeIterator(G_APP_PATH_ROUTES); - self::routeIterator(G_APP_PATH_ROUTES_OVERRIDES); - } - - /** - * Process the dynamic request - */ - private function processRequest() { - - if(is_null(self::$routes)) { // Route array is not set - $route = $this->getRouteFn(self::$base_request); - if(is_callable($route)) { - $routes[self::$base_request] = $route; // Build a single $routes array - } - } else { - $routes = self::$routes; - } - - if(is_array($routes) and array_key_exists(self::$base_request, $routes)) { - - // Autoset some magic - $magic = array( - 'post' => $_POST ? $_POST : NULL, - 'get' => $_GET ? $_GET : NULL, - 'request' => $_REQUEST ? $_REQUEST : NULL, - 'safe_post' => $_POST ? safe_html($_POST) : NULL, - 'safe_get' => $_GET ? safe_html($_GET) : NULL, - 'safe_request' => $_REQUEST ? safe_html($_REQUEST) : NULL, - 'auth_token' => self::getAuthToken() - ); - - if(self::$vars && count(self::$vars) > 0) { - self::$vars = array_merge(self::$vars, $magic); - } else { - self::$vars = $magic; - } - - // Only call a valid route fn - if(!self::$prevented_route and is_callable($routes[self::$base_request])) { - $routes[self::$base_request]($this); - } - - } else { - $this->issue404(); - $this->request = $this->request_array; - } - - if($this->template == 404) { - self::$route = 404; - } - self::setCond('404', $this->template == 404); // is_404 binding - - if(self::$vars['pre_doctitle']) { - $stock_doctitle = self::$vars['doctitle']; - self::$vars['doctitle'] = self::$vars['pre_doctitle']; - if($stock_doctitle) { - self::$vars['doctitle'] .= ' - ' . $stock_doctitle; - } - } - - self::$template_used = $this->template; - - } - - /** - * Bind route var to global functions - */ - public function bindGetFn($var, $value) { - $fn_name = strtolower(str_replace('-', '_', $var)); - if(!function_exists('get_' . $fn_name)) { - eval('function get_' . $fn_name . '(){ return G\Handler::$vars["' . $var . '"]; }'); - } - } - - /** - * Bind route conditional to global functions - */ - public function bindIsFn($var, $value) { - $fn_name = strtolower(str_replace('-', '_', $var)); - if(!function_exists('is_' . $fn_name)) { - eval('function is_' . $fn_name . '(){ return G\Handler::$conds["' . $var . '"]; }'); - } - } - - /** - * Inject the 404 page - */ - public function issue404() { - set_status_header(404); - if($this->getCond('mapped_route')) { - self::$base_request = self::$route_request[0]; - self::$route_name = 404; - } - $this->template = 404; - } - - /** - * Prevent the rest of the execution loading the target view - */ - public function preventRoute($tpl=NULL) { - if($tpl) { - $this->template = $tpl; - } - self::$prevented_route = true; - } - - /** - * Get the route fn for a given route - * If the route doesn't exists it will add it to the routes stack - */ - public function getRouteFn($route_name) { - // Route is already in the stack - if(is_array(self::$routes) and array_key_exists($route_name, Handler::$routes)) { - return self::$routes[$route_name]; - } - // Route doesn't exists in the stack - $filename = 'route.' . $route_name . '.php'; - $route_file = G_APP_PATH_ROUTES . $filename; - $route_override_file = G_APP_PATH_ROUTES_OVERRIDES . $filename; - if(file_exists($route_override_file)) { - $route_file = $route_override_file; - } - if(file_exists($route_file)) { - require($route_file); - // Append this new route fn to the Handler::$routes stack - self::$routes[$route_name] = $route; - self::$route_name = $route_name; - return $route; - } else { - return false; - } - } - - /** - * Maps the current route which is useful to make route aliases - */ - public function mapRoute($route_name, $args=NULL) { - $this->template = $route_name; - self::$base_request = $route_name; - self::setCond('mapped_route', true); - if(!is_null($args)) { - self::$mapped_args = $args; - } - return $this->getRouteFn($route_name); - } - - /** - * Return (bool) the request level of the current request - */ - public function isRequestLevel($level) { - return isset($this->request_array[$level - 1]); - } - - /** - * Redirect to the base url/request - */ - public function baseRedirection($request) { - $request = trim(sanitize_path_slashes($request), '/'); - $url = preg_replace('{'.$this->relative_root.'}', '/', $this->base_url, 1) . $request; - redirect($url, 301); - } - - /** - * Return (bool) if the request is handled by index.php - */ - private function isIndex() { - return preg_match('{/index.php$}', $this->script_name); - } - - /** - * Hook code for loadTemplate() - * @args ['code' => '', 'where' => 'before|after'] - */ - public function hookTemplate($args=[]) { - if(in_array($args['where'], ['before', 'after']) and $args['code']) { - if(!isset($this->hook_template)) { - $this->hook_template = []; - } - $this->hook_template[$args['where']] = $args['code']; - } - } - - /** - * load the setted (or argument) template view - */ - private function loadTemplate($template=NULL) { - if(!is_null($template)) { - $this->template = $template; - } - - /** Overrides are loaded from highest to lowest priority **/ - - $functions_basename = 'functions.php'; - $template_functions = [ - $this->path_theme . 'overrides/' . $functions_basename, - $this->path_theme . $functions_basename - ]; - foreach($template_functions as $file) { - if(file_exists($file)) { - require_once($file); - break; - } - } - - $view_basename = $this->template; - $view_extension = get_file_extension($this->template); - if(!$view_extension) { - $view_extension = 'php'; - $view_basename .= '.php'; - } - $template_file = [ - $this->path_theme . 'overrides/views/' . $view_basename, - $this->path_theme . 'overrides/' . $view_basename, - $this->path_theme . 'views/'. $view_basename, - $this->path_theme . $view_basename, - ]; - foreach($template_file as $file) { - if(file_exists($file)) { - if($view_extension == 'html') { - Render\include_theme_header(); - } - if($this->hook_template['before']) { - echo $this->hook_template['before']; - } - if($view_extension == 'php') { - require_once($file); - } else { - echo file_get_contents($file); - } - if($this->hook_template['after']) { - echo $this->hook_template['after']; - } - if($view_extension == 'html') { - Render\include_theme_footer(); - } - return; - } - } - - $end = end($template_file); - $key = key($template_file); - - throw new HandlerException('Missing ' . absolute_to_relative($template_file[$key]) . ' template file', 400); - } - - /** - * Returns the 40 char length safe request token - */ - public static function getAuthToken() { - $token = isset($_SESSION['G_auth_token']) ? $_SESSION['G_auth_token'] : random_string(40); - $_SESSION['G_auth_token'] = $token; - return $token; - } - - /** - * Checks the integrity and validation of the given request token - */ - public static function checkAuthToken($token) { - if(strlen($token) < 40) return false; - return timing_safe_compare($_SESSION['G_auth_token'], $token); - } - - /** - * Sets a Handler::$var > get_var() binding - */ - public static function setVar($var, $value) { - self::$vars[$var] = $value; - } - - /** - * Sets a multiple Handler::$var > get_var() binding - */ - public static function setVars($array) { - foreach((array)$array as $var => $value) { - self::$vars[$var] = $value; - } - } - - /** - * Sets a Handler::$conds -> is_cond() binding - */ - public static function setCond($conds, $bool) { - self::$conds[$conds] = !$bool ? false : true; - } - - /** - * Sets a multiple Handler::$conds -> is_cond() binding - */ - public static function setConds($array=[]) { - foreach((array)$array as $conds => $bool) { - self::$conds[$conds] = !$bool ? false : true; - } - } - - /** - * Get a Handler::$vars[var] - */ - public static function getVar($var) { - return self::getVars()[$var]; - } - - /** - * Get all Handler::$vars - */ - public static function getVars() { - return self::$vars; - } - - /** - * Get a Handler::$condss[cond] - */ - public static function getCond($cond) { - return self::getConds()[$cond]; - } - - /** - * Get all Handler::$conds - */ - public static function getConds() { - return self::$conds; - } - - /** - * Smart update a Handler::$vars - */ - public static function updateVar($var, $value) { - if(is_array(self::$vars[$var]) and is_array($value)) { - //self::$vars[$var] = array_merge(self::$vars[$var], $value); - $value += self::$vars[$var]; // replacement + replaced - ksort($value); - } - self::$vars[$var] = $value; - } - - /** - * Unset a given var - */ - public static function unsetVar($var) { - unset(self::$vars[$var]); - } - - /** - * Get the template file basename used - */ - public static function getTemplateUsed() { - return self::$template_used; - } - - /** - * Get the current route path - * @args $full (bool true) outputs the full route 'like/this' or 'this' - */ - public static function getRoutePath($full=true) { - if(is_array(self::$route)) { - return $full ? implode('/', self::$route) : self::$route[0]; - } else { - return self::$route; - } - } - - /** - * Get the current route name from route.name.php - */ - public static function getRouteName() { - return self::$route_name; - } - +class Handler +{ + public static $route; + public static $route_request; + public static $route_name; + public static $base_request; + public static $doctitle; + public static $vars; + public static $conds; + public static $routes; + public static $template_used; + public static $prevented_route; + public static $mapped_args; + + /** + * Build a valid request + */ + public function __construct($hook=[]) + { + if (!defined('G_APP_PATH_THEME')) { + throw new HandlerException('G_APP_PATH_THEME is not defined', 100); + } + + // Parse the definitions to this object.. This is not necessary but in case of changes... + $this->relative_root = G_ROOT_PATH_RELATIVE; // nota: realmente necesitamos estos this? + $this->base_url = get_root_url(); + $this->path_theme = G_APP_PATH_THEME; + + // Parse the request + $this->request_uri = $_SERVER['REQUEST_URI']; + $this->script_name = $_SERVER['SCRIPT_NAME']; + + $query_string = '?' . $_SERVER['QUERY_STRING']; + + if (!empty($_SERVER['QUERY_STRING'])) { + $this->request_uri = str_replace($query_string, '/', $this->request_uri); + } + + $this->valid_request = '/' . ltrim(rtrim(sanitize_path_slashes($this->request_uri), '/'), '/'); + + if (!empty($_SERVER['QUERY_STRING'])) { + $this->request_uri = $_SERVER['REQUEST_URI']; + $this->valid_request .= /*'/' .*/ $query_string; + } + + // Store the canonical request, used for redirect to a valid request + $this->canonical_request = $this->valid_request; + + if (is_dir(G_ROOT_PATH . $this->valid_request) && $this->valid_request !== '/') { + $this->canonical_request .= '/'; + } + + $this->handled_request = strtok($this->relative_root == '/' ? $this->valid_request : preg_replace('#' . $this->relative_root . '#', '/', $this->request_uri, 1), '?'); + $this->request_array = explode('/', rtrim(str_replace('//', '/', ltrim($this->handled_request, '/')), '/')); + + // Index request + if ($this->request_array[0] == '') { + $this->request_array[0] = '/'; + } + + $this->request_array = array_values(array_filter($this->request_array, 'strlen')); + self::$base_request = $this->request_array[0]; + + // Reserved route (index) + if (self::$base_request == 'index') { + redirect('/', 301); + } + + // Fix the canonical request /something?q= to /something/?q= + if (self::$base_request !== '' && !empty($_SERVER['QUERY_STRING'])) { + $path_request = add_trailing_slashes(rtrim(str_replace($_SERVER['QUERY_STRING'], '', $this->canonical_request), '?')); + $fixed_qs_request = $path_request.'?'.$_SERVER['QUERY_STRING']; + $this->canonical_request = $fixed_qs_request; + } + + // No /index.php request + if (self::$base_request == 'index.php') { + $this->canonical_request = rtrim($this->canonical_request, '/'); + redirect((sanitize_path_slashes(str_replace('index.php', '', $this->canonical_request))), 301); + } + + // If the request is invalid we make a 301 redirection to the canonical url. + if ($this->relative_root !== $this->request_uri and $this->canonical_request !== $this->request_uri) { + $this->baseRedirection($this->canonical_request); + } + + if (in_array(self::$base_request, ['', 'index.php', '/'])) { + self::$base_request = 'index'; + } + + $this->template = self::$base_request; + $this->request = $this->request_array; + + self::$route_request = $this->request_array; + self::$route = $this->template !== 404 ? $this->request_array[0] == '/' ? 'index' : $this->request_array : 404; + + unset($this->request[0]); + $this->request = array_values($this->request); + + // Hook a fn BEFORE the process + if (is_array($hook) and is_callable($hook['before'])) { + $hook['before']($this); + } + + // It is a valid request on index.php? + if ($this->isIndex()) { + $this->processRequest(); + } + + // Hook a fn AFTER the process + if (is_array($hook) and is_callable($hook['after'])) { + $hook['after']($this); + } + + // Auto-bind the route vars + if (is_array(self::$vars)) { + foreach (self::$vars as $k => $v) { + $this->bindGetFn($k, $v); + } + } + // Auto-bind the route conditionals + if (is_array(self::$conds)) { + foreach (self::$conds as $k => $v) { + $this->bindIsFn($k, $v); + } + } + + $this->loadTemplate(); + } + + /** + * Iterate over the route app folder + * This populates Handler::$routes with all the valid routes + */ + private static function routeIterator($path) + { + if (!file_exists($path)) { + return; + } + + foreach (new \DirectoryIterator($path) as $fileInfo) { + if ($fileInfo->isDot() or $fileInfo->isDir()) { + continue; + } + + $route_file = $path . $fileInfo->getFilename(); + $route_override = $path . 'overrides/' . $fileInfo->getFilename(); + + if (file_exists($route_override)) { + $route_file = $route_override; + } + + if (file_exists($route_file)) { + require_once($route_file); + $route = array(substr(substr($fileInfo->getFilename(), 0, -4), 6) => $route); + self::$routes += $route; + } + } + } + + /** + * Stock (save) the valid routes of the G\ app + * This method is optional because the routeIterator takes some memory + */ + public static function stockRoutes() + { + self::$routes = []; + self::routeIterator(G_APP_PATH_ROUTES); + self::routeIterator(G_APP_PATH_ROUTES_OVERRIDES); + } + + /** + * Process the dynamic request + */ + private function processRequest() + { + if (is_null(self::$routes)) { // Route array is not set + $route = $this->getRouteFn(self::$base_request); + if (is_callable($route)) { + $routes[self::$base_request] = $route; // Build a single $routes array + } + } else { + $routes = self::$routes; + } + + if (is_array($routes) and array_key_exists(self::$base_request, $routes)) { + + // Autoset some magic + $magic = array( + 'post' => $_POST ? $_POST : null, + 'get' => $_GET ? $_GET : null, + 'request' => $_REQUEST ? $_REQUEST : null, + 'safe_post' => $_POST ? safe_html($_POST) : null, + 'safe_get' => $_GET ? safe_html($_GET) : null, + 'safe_request' => $_REQUEST ? safe_html($_REQUEST) : null, + 'auth_token' => self::getAuthToken() + ); + + if (self::$vars && count(self::$vars) > 0) { + self::$vars = array_merge(self::$vars, $magic); + } else { + self::$vars = $magic; + } + + // Only call a valid route fn + if (!self::$prevented_route and is_callable($routes[self::$base_request])) { + $routes[self::$base_request]($this); + } + } else { + $this->issue404(); + $this->request = $this->request_array; + } + + if ($this->template == 404) { + self::$route = 404; + } + self::setCond('404', $this->template == 404); // is_404 binding + + if (self::$vars['pre_doctitle']) { + $stock_doctitle = self::$vars['doctitle']; + self::$vars['doctitle'] = self::$vars['pre_doctitle']; + if ($stock_doctitle) { + self::$vars['doctitle'] .= ' - ' . $stock_doctitle; + } + } + + self::$template_used = $this->template; + } + + /** + * Bind route var to global functions + */ + public function bindGetFn($var, $value) + { + $fn_name = strtolower(str_replace('-', '_', $var)); + if (!function_exists('get_' . $fn_name)) { + eval('function get_' . $fn_name . '(){ return G\Handler::$vars["' . $var . '"]; }'); + } + } + + /** + * Bind route conditional to global functions + */ + public function bindIsFn($var, $value) + { + $fn_name = strtolower(str_replace('-', '_', $var)); + if (!function_exists('is_' . $fn_name)) { + eval('function is_' . $fn_name . '(){ return G\Handler::$conds["' . $var . '"]; }'); + } + } + + /** + * Inject the 404 page + */ + public function issue404() + { + set_status_header(404); + if ($this->getCond('mapped_route')) { + self::$base_request = self::$route_request[0]; + self::$route_name = 404; + } + $this->is404 = true; + $this->template = 404; + } + + /** + * Prevent the rest of the execution loading the target view + */ + public function preventRoute($tpl=null) + { + if ($tpl) { + $this->template = $tpl; + } + self::$prevented_route = true; + } + + /** + * Get the route fn for a given route + * If the route doesn't exists it will add it to the routes stack + */ + public function getRouteFn($route_name) + { + // Route is already in the stack + if (is_array(self::$routes) and array_key_exists($route_name, Handler::$routes)) { + return self::$routes[$route_name]; + } + // Route doesn't exists in the stack + $filename = 'route.' . $route_name . '.php'; + $route_file = G_APP_PATH_ROUTES . $filename; + $route_override_file = G_APP_PATH_ROUTES_OVERRIDES . $filename; + if (file_exists($route_override_file)) { + $route_file = $route_override_file; + } + if (file_exists($route_file)) { + require($route_file); + // Append this new route fn to the Handler::$routes stack + self::$routes[$route_name] = $route; + self::$route_name = $route_name; + return $route; + } else { + return false; + } + } + + /** + * Maps the current route which is useful to make route aliases + */ + public function mapRoute($route_name, $args=null) + { + $this->template = $route_name; + self::$base_request = $route_name; + self::setCond('mapped_route', true); + if (!is_null($args)) { + self::$mapped_args = $args; + } + return $this->getRouteFn($route_name); + } + + /** + * Return (bool) the request level of the current request + */ + public function isRequestLevel($level) + { + return isset($this->request_array[$level - 1]); + } + + /** + * Redirect to the base url/request + */ + public function baseRedirection($request) + { + $request = trim(sanitize_path_slashes($request), '/'); + $url = preg_replace('{'.$this->relative_root.'}', '/', $this->base_url, 1) . $request; + redirect($url, 301); + } + + /** + * Return (bool) if the request is handled by index.php + */ + private function isIndex() + { + return preg_match('{/index.php$}', $this->script_name); + } + + /** + * Hook code for loadTemplate() + * @args ['code' => '', 'where' => 'before|after'] + */ + public function hookTemplate($args=[]) + { + if (in_array($args['where'], ['before', 'after']) and $args['code']) { + if (!isset($this->hook_template)) { + $this->hook_template = []; + } + $this->hook_template[$args['where']] = $args['code']; + } + } + + /** + * load the setted (or argument) template view + */ + private function loadTemplate($template=null) + { + if (!is_null($template)) { + $this->template = $template; + } + + /** Overrides are loaded from highest to lowest priority **/ + + $functions_basename = 'functions.php'; + $template_functions = [ + $this->path_theme . 'overrides/' . $functions_basename, + $this->path_theme . $functions_basename + ]; + foreach ($template_functions as $file) { + if (file_exists($file)) { + require_once($file); + break; + } + } + + $view_basename = $this->template; + $view_extension = get_file_extension($this->template); + if (!$view_extension) { + $view_extension = 'php'; + $view_basename .= '.php'; + } + $template_file = [ + $this->path_theme . 'overrides/views/' . $view_basename, + $this->path_theme . 'overrides/' . $view_basename, + $this->path_theme . 'views/'. $view_basename, + $this->path_theme . $view_basename, + ]; + foreach ($template_file as $file) { + if (file_exists($file)) { + if ($view_extension == 'html') { + Render\include_theme_header(); + } + if ($this->hook_template['before']) { + echo $this->hook_template['before']; + } + if ($view_extension == 'php') { + require_once($file); + } else { + echo file_get_contents($file); + } + if ($this->hook_template['after']) { + echo $this->hook_template['after']; + } + if ($view_extension == 'html') { + Render\include_theme_footer(); + } + return; + } + } + + $end = end($template_file); + $key = key($template_file); + + throw new HandlerException('Missing ' . absolute_to_relative($template_file[$key]) . ' template file', 400); + } + + /** + * Returns the 40 char length safe request token + */ + public static function getAuthToken() + { + $token = isset($_SESSION['G_auth_token']) ? $_SESSION['G_auth_token'] : random_string(40); + $_SESSION['G_auth_token'] = $token; + return $token; + } + + /** + * Checks the integrity and validation of the given request token + */ + public static function checkAuthToken($token) + { + if (strlen($token) < 40) { + return false; + } + return timing_safe_compare($_SESSION['G_auth_token'], $token); + } + + /** + * Sets a Handler::$var > get_var() binding + */ + public static function setVar($var, $value) + { + self::$vars[$var] = $value; + } + + /** + * Sets a multiple Handler::$var > get_var() binding + */ + public static function setVars($array) + { + foreach ((array)$array as $var => $value) { + self::$vars[$var] = $value; + } + } + + /** + * Sets a Handler::$conds -> is_cond() binding + */ + public static function setCond($conds, $bool) + { + self::$conds[$conds] = !$bool ? false : true; + } + + /** + * Sets a multiple Handler::$conds -> is_cond() binding + */ + public static function setConds($array=[]) + { + foreach ((array)$array as $conds => $bool) { + self::$conds[$conds] = !$bool ? false : true; + } + } + + /** + * Get a Handler::$vars[var] + */ + public static function getVar($var) + { + return self::getVars()[$var]; + } + + /** + * Get all Handler::$vars + */ + public static function getVars() + { + return self::$vars; + } + + /** + * Get a Handler::$condss[cond] + */ + public static function getCond($cond) + { + return self::getConds()[$cond]; + } + + /** + * Get all Handler::$conds + */ + public static function getConds() + { + return self::$conds; + } + + /** + * Smart update a Handler::$vars + */ + public static function updateVar($var, $value) + { + if (is_array(self::$vars[$var]) and is_array($value)) { + //self::$vars[$var] = array_merge(self::$vars[$var], $value); + $value += self::$vars[$var]; // replacement + replaced + ksort($value); + } + self::$vars[$var] = $value; + } + + /** + * Unset a given var + */ + public static function unsetVar($var) + { + unset(self::$vars[$var]); + } + + /** + * Get the template file basename used + */ + public static function getTemplateUsed() + { + return self::$template_used; + } + + /** + * Get the current route path + * @args $full (bool true) outputs the full route 'like/this' or 'this' + */ + public static function getRoutePath($full=true) + { + if (is_array(self::$route)) { + return $full ? implode('/', self::$route) : self::$route[0]; + } else { + return self::$route; + } + } + + /** + * Get the current route name from route.name.php + */ + public static function getRouteName() + { + return self::$route_name; + } } -class HandlerException extends Exception {} \ No newline at end of file +class HandlerException extends Exception +{ +} diff --git a/lib/G/classes/class.minify.php b/lib/G/classes/class.minify.php index 2fd5634..b7a7f36 100644 --- a/lib/G/classes/class.minify.php +++ b/lib/G/classes/class.minify.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. diff --git a/lib/G/functions.php b/lib/G/functions.php index 1ba2b65..c3ed684 100644 --- a/lib/G/functions.php +++ b/lib/G/functions.php @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. @@ -310,9 +310,12 @@ namespace G { */ function abbreviate_number($number) { - - // strip any formatting - $number = (0+str_replace(',', '', $number)); + if ($number === null) { + $number = 0; + } else { + // strip any formatting + $number = (0+str_replace(',', '', $number)); + } // Not a number, keep it "as is" if (!is_numeric($number) or $number == 0) { @@ -560,7 +563,7 @@ namespace G { function exception_to_error($e, $die=true) { $internal_code = 500; - $internal_error = ''.G_APP_NAME.' error: ' . get_set_status_header_desc($internal_code); + $internal_error = 'Aw, snap! ' . get_set_status_header_desc($internal_code) . ' - Check your error_log or enable debug_mode = 3 (chevereto.com/docs/debug).'; set_status_header($internal_code); @@ -816,18 +819,20 @@ namespace G { function get_client_ip() { - $client_ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : (!empty($_ENV['REMOTE_ADDR']) ? $_ENV['REMOTE_ADDR'] : null); - - if (array_key_exists('HTTP_CF_CONNECTING_IP', $_SERVER) && $_SERVER['HTTP_CF_CONNECTING_IP'] == $_SERVER['REMOTE_ADDR']) { - return $_SERVER['HTTP_CF_CONNECTING_IP']; + if(isset($_SERVER['G_CLIENT_IP'])) { + return $_SERVER['G_CLIENT_IP']; } - - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { + $client_ip = $_SERVER['HTTP_CF_CONNECTING_IP']; + } else { + $client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + } + if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $client_ip != $_SERVER['HTTP_X_FORWARDED_FOR']) { $entries = preg_split('/[\s,]/', $_SERVER['HTTP_X_FORWARDED_FOR'], -1, PREG_SPLIT_NO_EMPTY); reset($entries); - while (list(, $entry) = each($entries)) { + foreach ($entries as $entry) { $entry = trim($entry); if (preg_match('/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/', $entry, $ip_list)) { $private_ip = array( @@ -838,7 +843,6 @@ namespace G { '/^10\..*/'); $found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]); - if ($client_ip != $found_ip) { // and !isset($_SERVER['HTTP_CF_CONNECTING_IP'] $client_ip = $found_ip; break; @@ -846,7 +850,7 @@ namespace G { } } } - + $_SERVER['G_CLIENT_IP'] = $client_ip; return $client_ip; } @@ -1129,6 +1133,23 @@ namespace G { return $path; } + function rrmdir($dir) + { + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (is_dir($dir."/".$object)) { + rrmdir($dir."/".$object); + } else { + unlink($dir."/".$object); + } + } + } + rmdir($dir); + } + } + /** * Returns a sanitized string, typically for URLs * This function was borrowed from chyrp.net (MIT License) @@ -1438,7 +1459,7 @@ namespace G { function relative_to_url($filepath, $root_url=null) { if (!check_value($root_url)) { - $root_url = G_ROOT_URL; + $root_url = get_root_url(); } return str_replace(G_ROOT_PATH_RELATIVE, $root_url, forward_slash($filepath)); } @@ -1447,7 +1468,7 @@ namespace G { function url_to_relative($url, $root_url=null) { if (!check_value($root_url)) { - $root_url = G_ROOT_URL; + $root_url = get_root_url(); } return str_replace($root_url, G_ROOT_PATH_RELATIVE, $url); } @@ -1462,7 +1483,7 @@ namespace G { function absolute_to_url($filepath, $root_url=null) { if (!check_value($root_url)) { - $root_url = G_ROOT_URL; + $root_url = get_root_url(); } if (G_ROOT_PATH === G_ROOT_PATH_RELATIVE) { return $root_url . ltrim($filepath, '/'); @@ -1474,7 +1495,7 @@ namespace G { function url_to_absolute($url, $root_url=null) { if (!check_value($root_url)) { - $root_url = G_ROOT_URL; + $root_url = get_root_url(); } return str_replace($root_url, G_ROOT_PATH, $url); } @@ -1508,21 +1529,43 @@ namespace G { return get_global('settings')[$key]; } - function get_domain() - { - return HTTP_HOST; - } - function get_base_url($path='') { $path = sanitize_relative_path($path); - $return = G_ROOT_URL . ltrim($path, '/'); + $return = get_root_url() . ltrim($path, '/'); return rtrim($return, '/'); } - function get_current_url() + function get_host() { - return get_base_url(preg_replace('#'.G_ROOT_PATH_RELATIVE.'#', '', $_SERVER['REQUEST_URI'], 1)); + return defined('APP_G_HTTP_HOST') ? APP_G_HTTP_HOST : G_HTTP_HOST; + } + + function get_root_url() + { + return defined('APP_G_ROOT_URL') ? APP_G_ROOT_URL : G_ROOT_URL; + } + + /** + * @param string Querystring keys to remove (comma separated) + */ + function get_current_url($safe=true, $removeQs=[]) + { + $request_uri = $_SERVER['REQUEST_URI']; + $request_path = rtrim(strtok($request_uri, '?'), '/'); + if ($_SERVER['QUERY_STRING'] && $removeQs) { + parse_str($_SERVER['QUERY_STRING'], $parse); + foreach ($removeQs as $v) { + unset($parse[$v]); + } + $querystring = $parse ? http_build_query($parse) : null; + $request_uri = $request_path; + if ($querystring) { + $request_uri .= '/?' . $querystring; + } + } + $path = preg_replace('#'.G_ROOT_PATH_RELATIVE.'#', '', rtrim($request_uri, '/') . '/', 1); + return get_base_url($path); } function settings_has_db_info() @@ -1894,9 +1937,26 @@ namespace G { return basename($file); } - function get_filename_without_extension($file) + function get_basename_without_extension($filename) { - return preg_replace('/\\.[^.\\s]{2,4}$/', '', basename($file)); + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $filename = basename($filename); + return str_replace_last(".$extension", null, $filename); + } + + function get_pathname_without_extension($filename) + { + $extension = pathinfo($filename, PATHINFO_EXTENSION); + return str_replace_last(".$extension", null, $filename); + } + + function change_pathname_extension($filename, $extension) + { + $chop = get_pathname_without_extension($filename); + if ($chop == $filename) { + return $filename; + } + return "$chop.$extension"; } /** @@ -2202,9 +2262,9 @@ namespace G { // https://github.com/Chevereto/Chevereto-Free/pull/35 function imagecreatefrombmp($file) { - if (function_exists('imagecreatefrombmp')) { - return imagecreatefrombmp($file); - } + // if (function_exists('imagecreatefrombmp')) { + // return imagecreatefrombmp($file); + // } // version 1.00 if (!($fh = fopen($file, 'rb'))) { trigger_error('imagecreatefrombmp: Can not open ' . $file, E_USER_WARNING); @@ -2525,6 +2585,126 @@ namespace G { return trim(preg_replace('/\s*(?:\*\/|\?>).*/', '', $string)); } + /** + * function xml2array + * + * This function is part of the PHP manual. + * + * The PHP manual text and comments are covered by the Creative Commons + * Attribution 3.0 License, copyright (c) the PHP Documentation Group + * + * @author k dot antczak at livedata dot pl + * @date 2011-04-22 06:08 UTC + * @link http://www.php.net/manual/en/ref.simplexml.php#103617 + * @license http://www.php.net/license/index.php#doc-lic + * @license http://creativecommons.org/licenses/by/3.0/ + * @license CC-BY-3.0 + */ + function xml2array($xmlObject, $out = array()) + { + foreach ((array) $xmlObject as $index => $node) { + $out[$index] = (is_object($node)) ? xml2array($node) : $node; + } + + return $out; + } + + /** + * @param string $domain Pass $_SERVER['SERVER_NAME'] here + * @param bool $debug + * + * @debug bool $debug + * @return string + * + * @link https://gist.github.com/pocesar/5366899 + */ + function get_domain($domain, $debug = false) + { + $original = $domain = strtolower($domain); + if (filter_var($domain, FILTER_VALIDATE_IP)) { return $domain; } + $debug ? print('» Parsing: '.$original) : false; + $arr = array_slice(array_filter(explode('.', $domain, 4), function($value){ + return $value !== 'www'; + }), 0); //rebuild array indexes + if (count($arr) > 2) + { + $count = count($arr); + $_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]); + $debug ? print(" (parts count: {$count})") : false; + if (count($_sub) === 2) // two level TLD + { + $removed = array_shift($arr); + if ($count === 4) // got a subdomain acting as a domain + { + $removed = array_shift($arr); + } + $debug ? print("
\n" . '[*] Two level TLD: ' . join('.', $_sub) . ' ') : false; + } + elseif (count($_sub) === 1) // one level TLD + { + $removed = array_shift($arr); //remove the subdomain + if (strlen($_sub[0]) === 2 && $count === 3) // TLD domain must be 2 letters + { + array_unshift($arr, $removed); + } + else + { + // non country TLD according to IANA + $tlds = array( + 'aero', + 'arpa', + 'asia', + 'biz', + 'cat', + 'com', + 'coop', + 'edu', + 'gov', + 'info', + 'jobs', + 'mil', + 'mobi', + 'museum', + 'name', + 'net', + 'org', + 'post', + 'pro', + 'tel', + 'travel', + 'xxx', + ); + if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false) //special TLD don't have a country + { + array_shift($arr); + } + } + $debug ? print("
\n" .'[*] One level TLD: '.join('.', $_sub).' ') : false; + } + else // more than 3 levels, something is wrong + { + for ($i = count($_sub); $i > 1; $i--) + { + $removed = array_shift($arr); + } + $debug ? print("
\n" . '[*] Three level TLD: ' . join('.', $_sub) . ' ') : false; + } + } + elseif (count($arr) === 2) + { + $arr0 = array_shift($arr); + if (strpos(join('.', $arr), '.') === false + && in_array($arr[0], array('localhost','test','invalid')) === false) // not a reserved domain + { + $debug ? print("
\n" .'Seems invalid domain: '.join('.', $arr).' re-adding: '.$arr0.' ') : false; + // seems invalid domain, restore it + array_unshift($arr, $arr0); + } + } + $debug ? print("
\n".'« Done parsing: ' . $original . ' as '. join('.', $arr) ."
\n") : false; + return join('.', $arr); + } + } // G Namespace // Global namespace @@ -2767,5 +2947,4 @@ namespace { return $status === 0; } } - } diff --git a/lib/G/functions.render.php b/lib/G/functions.render.php index f0caf33..1dbde36 100644 --- a/lib/G/functions.render.php +++ b/lib/G/functions.render.php @@ -3,18 +3,19 @@ /* -------------------------------------------------------------------- G\ library - http://gbackslash.com + https://g.chevereto.com @author Rodolfo Berrios A. Copyright (c) Rodolfo Berrios All rights reserved. - + Licensed under the MIT license http://opensource.org/licenses/MIT - + --------------------------------------------------------------------- */ namespace G\Render; + use G; /** @@ -22,34 +23,38 @@ use G; * --------------------------------------------------------------------- */ -function include_theme_file($filename, $args=[]) { - $file = G_APP_PATH_THEME . $filename; - $override = G_APP_PATH_THEME . 'overrides/' . $filename; - if(!file_exists($file)) { - $file .= '.php'; - $override .= '.php'; - } - if(file_exists($override)) { - $file = $override; - } - if(file_exists($file)) { - $GLOBALS['theme_include_args'] = $args; - include($file); - unset($GLOBALS['theme_include_args']); - } +function include_theme_file($filename, $args=[]) +{ + $file = G_APP_PATH_THEME . $filename; + $override = G_APP_PATH_THEME . 'overrides/' . $filename; + if (!file_exists($file)) { + $file .= '.php'; + $override .= '.php'; + } + if (file_exists($override)) { + $file = $override; + } + if (file_exists($file)) { + $GLOBALS['theme_include_args'] = $args; + include($file); + unset($GLOBALS['theme_include_args']); + } } -function include_theme_header() { - include_theme_file('header'); +function include_theme_header() +{ + include_theme_file('header'); } -function include_theme_footer() { - include_theme_file('footer'); +function include_theme_footer() +{ + include_theme_file('footer'); } -function get_theme_file_contents($filename) { - $file = G_APP_PATH_THEME . $filename; - return file_exists($file) ? file_get_contents($file) : null; +function get_theme_file_contents($filename) +{ + $file = G_APP_PATH_THEME . $filename; + return file_exists($file) ? file_get_contents($file) : null; } /** @@ -57,8 +62,9 @@ function get_theme_file_contents($filename) { * --------------------------------------------------------------------- */ -function get_theme_file_url($string) { - return BASE_URL_THEME . $string; +function get_theme_file_url($string) +{ + return BASE_URL_THEME . $string; } /** @@ -67,13 +73,15 @@ function get_theme_file_url($string) { */ // Return app lib file url -function get_app_lib_file_url($string){ - return G_APP_LIB_URL . $string; +function get_app_lib_file_url($string) +{ + return (defined('APP_G_APP_LIB_URL') ? APP_G_APP_LIB_URL : G_APP_LIB_URL) . $string; } // Returns the HTML input with the auth token -function get_input_auth_token($name='auth_token') { - return ''; +function get_input_auth_token($name='auth_token') +{ + return ''; } @@ -83,80 +91,84 @@ function get_input_auth_token($name='auth_token') { */ // Outputs the REST_API array to xml -function xml_output($array=array()) { - error_reporting(0); - //@ini_set('display_errors', false); - if(ob_get_level() === 0 and !ob_start('ob_gzhandler')) { - ob_start(); - } - header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT"); - header("Cache-Control: no-cache, must-revalidate"); - header("Pragma: no-cache"); - header("Content-Type:text/xml; charset=UTF-8"); - $out = ''."\n"; - $out .= "\n"; - $out .= " $array[status_code]\n"; - $out .= " $array[status_txt]\n"; - if(count($array["data"])>0) { - $out .= " \n"; - foreach($array["data"] as $key => $value) { - $out .= " <$key>$value\n"; - } - $out .= " \n"; - } - $out .= ""; - echo $out; +function xml_output($array=array()) +{ + error_reporting(0); + //@ini_set('display_errors', false); + if (ob_get_level() === 0 and !ob_start('ob_gzhandler')) { + ob_start(); + } + header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT"); + header("Cache-Control: no-cache, must-revalidate"); + header("Pragma: no-cache"); + header("Content-Type:text/xml; charset=UTF-8"); + $out = ''."\n"; + $out .= "\n"; + $out .= " $array[status_code]\n"; + $out .= " $array[status_txt]\n"; + if (count($array["data"])>0) { + $out .= " \n"; + foreach ($array["data"] as $key => $value) { + $out .= " <$key>$value\n"; + } + $out .= " \n"; + } + $out .= ""; + echo $out; } // Procedural function to output an array to json -function json_output($data=[], $callback=NULL) { - error_reporting(0); - //@ini_set('display_errors', false); - if(ob_get_level() === 0 and !ob_start('ob_gzhandler')) ob_start(); - header('Last-Modified: '.gmdate('D, d M Y H:i:s').'GMT'); - header('Cache-Control: no-cache, must-revalidate'); - header('Pragma: no-cache'); - header('Content-type: application/json; charset=UTF-8'); - - // Invalid json request - if(!G\check_value($data) || (G\check_value($callback) and preg_match('/\W/', $callback))) { - G\set_status_header(400); - $json_fail = [ - 'status_code' => 400, - 'status_txt' => G\get_set_status_header_desc(400), - 'error' => [ - 'message' => 'no request data present', - 'code' => NULL - ] - ]; - die(json_encode($json_fail)); - } - - // Populate missing values - if($data['status_code'] && !$data['status_txt']){ - $data['status_txt'] = G\get_set_status_header_desc($data['status_code']); - } - - $json_encode = json_encode($data); - - if(!$json_encode) { // Json failed - G\set_status_header(500); - $json_fail = [ - 'status_code' => 500, - 'status_txt' => G\get_set_status_header_desc(500), - 'error' => [ - 'message' => "data couldn't be encoded into json", - 'code' => NULL - ] - ]; - die(json_encode($json_fail)); - } - G\set_status_header($data['status_code']); - - if(!is_null($callback)) { - print sprintf('%s(%s);', $callback, $json_encode); - } else { - print $json_encode; - } - die(); -} \ No newline at end of file +function json_output($data=[], $callback=null) +{ + error_reporting(0); + //@ini_set('display_errors', false); + if (ob_get_level() === 0 and !ob_start('ob_gzhandler')) { + ob_start(); + } + header('Last-Modified: '.gmdate('D, d M Y H:i:s').'GMT'); + header('Cache-Control: no-cache, must-revalidate'); + header('Pragma: no-cache'); + header('Content-type: application/json; charset=UTF-8'); + + // Invalid json request + if (!G\check_value($data) || (G\check_value($callback) and preg_match('/\W/', $callback))) { + G\set_status_header(400); + $json_fail = [ + 'status_code' => 400, + 'status_txt' => G\get_set_status_header_desc(400), + 'error' => [ + 'message' => 'no request data present', + 'code' => null + ] + ]; + die(json_encode($json_fail)); + } + + // Populate missing values + if ($data['status_code'] && !$data['status_txt']) { + $data['status_txt'] = G\get_set_status_header_desc($data['status_code']); + } + + $json_encode = json_encode($data); + + if (!$json_encode) { // Json failed + G\set_status_header(500); + $json_fail = [ + 'status_code' => 500, + 'status_txt' => G\get_set_status_header_desc(500), + 'error' => [ + 'message' => "data couldn't be encoded into json", + 'code' => null + ] + ]; + die(json_encode($json_fail)); + } + G\set_status_header($data['status_code']); + + if (!is_null($callback)) { + print sprintf('%s(%s);', $callback, $json_encode); + } else { + print $json_encode; + } + die(); +}