From c9998cfb068647603882e244f6b7db530a92c01f Mon Sep 17 00:00:00 2001 From: rodolfoberrios Date: Tue, 19 Feb 2019 17:35:48 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=91=20Fixed=20bug=20detected=20in=201.?= =?UTF-8?q?1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.php | 2 +- app/install/installer.php | 1 + lib/G/G.php | 96 ++- lib/G/classes/class.db.php | 1060 +++++++++++++++--------------- lib/G/classes/class.handler.php | 1084 +++++++++++++++---------------- lib/G/functions.php | 241 +------ lib/G/functions.render.php | 220 +++---- 7 files changed, 1218 insertions(+), 1486 deletions(-) diff --git a/app/app.php b/app/app.php index 61de49fb..e36c5151 100644 --- a/app/app.php +++ b/app/app.php @@ -1,6 +1,6 @@ NULL, '1.1.2' => NULL, + '1.1.3' => NULL, ]; // Settings that must be renamed from NAME to NEW NAME and DELETE old NAME $settings_rename = []; diff --git a/lib/G/G.php b/lib/G/G.php index db608aeb..27804247 100644 --- a/lib/G/G.php +++ b/lib/G/G.php @@ -16,14 +16,12 @@ 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 @@ -33,7 +31,7 @@ @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/'); @@ -54,75 +52,65 @@ // 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 @@ -130,24 +118,24 @@ $_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 @@ -157,11 +145,11 @@ // 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)); -} +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 diff --git a/lib/G/classes/class.db.php b/lib/G/classes/class.db.php index 9c889892..93e97dcc 100644 --- a/lib/G/classes/class.db.php +++ b/lib/G/classes/class.db.php @@ -20,550 +20,528 @@ */ namespace G; - -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 +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 `'.$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); - } - } - - /** - * 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); - } - } - - $table = DB::getTable($table); - $query = 'UPDATE `'.$table.'` SET '; - - 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),'; - } - } - } - - $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); - 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); - } - } - - /** - * 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); - } - - 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->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); + } + } + + $table = DB::getTable($table); + $query = 'UPDATE `'.$table.'` SET '; + + 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),'; + } + } + } + + $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); + 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); + } + + } + + /** + * 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); + } + + 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); + } + } + } + } // DB class own Exception -class DBException extends Exception -{ -} +class DBException extends Exception {} \ No newline at end of file diff --git a/lib/G/classes/class.handler.php b/lib/G/classes/class.handler.php index edc58f51..e009f38a 100644 --- a/lib/G/classes/class.handler.php +++ b/lib/G/classes/class.handler.php @@ -20,571 +20,527 @@ */ namespace G; - use Exception; -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 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 HandlerException extends Exception -{ } + +class HandlerException extends Exception {} \ No newline at end of file diff --git a/lib/G/functions.php b/lib/G/functions.php index c3ed6841..54f698a4 100644 --- a/lib/G/functions.php +++ b/lib/G/functions.php @@ -310,12 +310,9 @@ function array_remove_empty($haystack) */ function abbreviate_number($number) { - if ($number === null) { - $number = 0; - } else { - // strip any formatting - $number = (0+str_replace(',', '', $number)); - } + + // strip any formatting + $number = (0+str_replace(',', '', $number)); // Not a number, keep it "as is" if (!is_numeric($number) or $number == 0) { @@ -563,7 +560,7 @@ function linkify_safe($text, $options=[]) function exception_to_error($e, $die=true) { $internal_code = 500; - $internal_error = 'Aw, snap! ' . get_set_status_header_desc($internal_code) . ' - Check your error_log or enable debug_mode = 3 (chevereto.com/docs/debug).'; + $internal_error = ''.G_APP_NAME.' error: ' . get_set_status_header_desc($internal_code); set_status_header($internal_code); @@ -819,20 +816,18 @@ function dateinterval($var) function get_client_ip() { - if(isset($_SERVER['G_CLIENT_IP'])) { - return $_SERVER['G_CLIENT_IP']; - } - 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; + $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['HTTP_X_FORWARDED_FOR']) && $client_ip != $_SERVER['HTTP_X_FORWARDED_FOR']) { + + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $entries = preg_split('/[\s,]/', $_SERVER['HTTP_X_FORWARDED_FOR'], -1, PREG_SPLIT_NO_EMPTY); reset($entries); - foreach ($entries as $entry) { + while (list(, $entry) = each($entries)) { $entry = trim($entry); if (preg_match('/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/', $entry, $ip_list)) { $private_ip = array( @@ -843,6 +838,7 @@ function get_client_ip() '/^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; @@ -850,7 +846,7 @@ function get_client_ip() } } } - $_SERVER['G_CLIENT_IP'] = $client_ip; + return $client_ip; } @@ -1133,23 +1129,6 @@ function sanitize_relative_path($path) 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) @@ -1459,7 +1438,7 @@ function relative_to_absolute($filepath) function relative_to_url($filepath, $root_url=null) { if (!check_value($root_url)) { - $root_url = get_root_url(); + $root_url = G_ROOT_URL; } return str_replace(G_ROOT_PATH_RELATIVE, $root_url, forward_slash($filepath)); } @@ -1468,7 +1447,7 @@ function relative_to_url($filepath, $root_url=null) function url_to_relative($url, $root_url=null) { if (!check_value($root_url)) { - $root_url = get_root_url(); + $root_url = G_ROOT_URL; } return str_replace($root_url, G_ROOT_PATH_RELATIVE, $url); } @@ -1483,7 +1462,7 @@ function absolute_to_relative($filepath) function absolute_to_url($filepath, $root_url=null) { if (!check_value($root_url)) { - $root_url = get_root_url(); + $root_url = G_ROOT_URL; } if (G_ROOT_PATH === G_ROOT_PATH_RELATIVE) { return $root_url . ltrim($filepath, '/'); @@ -1495,7 +1474,7 @@ function absolute_to_url($filepath, $root_url=null) function url_to_absolute($url, $root_url=null) { if (!check_value($root_url)) { - $root_url = get_root_url(); + $root_url = G_ROOT_URL; } return str_replace($root_url, G_ROOT_PATH, $url); } @@ -1529,43 +1508,21 @@ function get_app_setting($key) return get_global('settings')[$key]; } - function get_base_url($path='') + function get_domain() { - $path = sanitize_relative_path($path); - $return = get_root_url() . ltrim($path, '/'); - return rtrim($return, '/'); - } - - function get_host() - { - return defined('APP_G_HTTP_HOST') ? APP_G_HTTP_HOST : G_HTTP_HOST; + return HTTP_HOST; } - function get_root_url() + function get_base_url($path='') { - return defined('APP_G_ROOT_URL') ? APP_G_ROOT_URL : G_ROOT_URL; + $path = sanitize_relative_path($path); + $return = G_ROOT_URL . ltrim($path, '/'); + return rtrim($return, '/'); } - /** - * @param string Querystring keys to remove (comma separated) - */ - function get_current_url($safe=true, $removeQs=[]) + function get_current_url() { - $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); + return get_base_url(preg_replace('#'.G_ROOT_PATH_RELATIVE.'#', '', $_SERVER['REQUEST_URI'], 1)); } function settings_has_db_info() @@ -1937,26 +1894,9 @@ function get_filename($file) return basename($file); } - function get_basename_without_extension($filename) - { - $extension = pathinfo($filename, PATHINFO_EXTENSION); - $filename = basename($filename); - return str_replace_last(".$extension", null, $filename); - } - - function get_pathname_without_extension($filename) + function get_filename_without_extension($file) { - $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"; + return preg_replace('/\\.[^.\\s]{2,4}$/', '', basename($file)); } /** @@ -2262,9 +2202,9 @@ function get_mask_bit_shift($bits, $mask) // 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); @@ -2585,126 +2525,6 @@ function clean_header_comment($string) 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 @@ -2947,4 +2767,5 @@ function password_verify($password, $hash) return $status === 0; } } + } diff --git a/lib/G/functions.render.php b/lib/G/functions.render.php index 1dbde36d..74eee0d0 100644 --- a/lib/G/functions.render.php +++ b/lib/G/functions.render.php @@ -8,14 +8,13 @@ @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; /** @@ -23,38 +22,34 @@ * --------------------------------------------------------------------- */ -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; } /** @@ -62,9 +57,8 @@ 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; } /** @@ -73,15 +67,13 @@ function get_theme_file_url($string) */ // Return app lib file url -function get_app_lib_file_url($string) -{ - return (defined('APP_G_APP_LIB_URL') ? APP_G_APP_LIB_URL : G_APP_LIB_URL) . $string; +function get_app_lib_file_url($string){ + return 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 ''; } @@ -91,84 +83,80 @@ 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(); -} +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