pull/58/head
Rodolfo Berrios 2019-09-30 15:26:56 -03:00
parent cb83b2b98c
commit ec8469333e
2 changed files with 911 additions and 862 deletions

View File

@ -16,9 +16,12 @@
--------------------------------------------------------------------- */
namespace CHV;
use G, Exception;
class Upload {
use G;
use Exception;
class Upload
{
// filename => name.ext
// file => /full/path/to/name.ext
// name => name
@ -27,47 +30,55 @@ class Upload {
public $uploaded;
// Sets the type of resource being uploaded
public function setType($type) {
public function setType($type)
{
$this->type = $type;
}
// Set source
public function setSource($source) {
public function setSource($source)
{
$this->source = $source;
$this->type = G\is_url($this->source) ? 'url' : 'file';
}
// Set destination
public function setDestination($destination) {
public function setDestination($destination)
{
$this->destination = G\forward_slash($destination);
}
// Set storage
public function setStorageId($storage_id) {
$this->storage_id = is_numeric($storage_id) ? $storage_id : NULL;
public function setStorageId($storage_id)
{
$this->storage_id = is_numeric($storage_id) ? $storage_id : null;
}
// Set file basename
public function setFilename($name) {
public function setFilename($name)
{
$this->name = $name;
}
// Set options
public function setOptions($options) {
public function setOptions($options)
{
$this->options = $options;
}
// Set individual option
public function setOption($key, $value) {
public function setOption($key, $value)
{
$this->options[$key] = $value;
}
// Default options
public static function getDefaultOptions() {
public static function getDefaultOptions()
{
return array(
'max_size' => G\get_bytes('2 MB'), // it should be 'max_filesize'
'filenaming' => 'original',
'exif' => TRUE,
'exif' => true,
'allowed_formats' => self::getAvailableImageFormats(), // array
);
}
@ -76,7 +87,8 @@ class Upload {
* Do the thing
* @Exeption 4xx
*/
public function exec() {
public function exec()
{
// Merge options
$this->options = array_merge(self::getDefaultOptions(), (array) $this->options);
@ -87,18 +99,18 @@ class Upload {
$this->validateSourceFile(); // Exception 3
if(!is_array($this->options['allowed_formats'])) {
if (!is_array($this->options['allowed_formats'])) {
$this->options['allowed_formats'] = explode(',', $this->options['allowed_formats']);
}
// Save the source name
$this->source_name = G\get_filename_without_extension($this->type == "url" ? $this->source : $this->source["name"]);
$this->source_name = G\get_filename_without_extension($this->type == "url" ? $this->getNameFromURL($this->source) : $this->source["name"]);
// Set file extension
$this->extension = $this->source_image_fileinfo["extension"];
// Workaround the $name
if(!$this->name) {
if (!$this->name) {
$this->name = $this->source_name;
}
@ -106,7 +118,7 @@ class Upload {
$this->name = ltrim($this->name, '.');
// Fix file extension
if(G\get_file_extension($this->name) == $this->extension) {
if (G\get_file_extension($this->name) == $this->extension) {
$this->name = G\get_filename_without_extension($this->name);
}
@ -114,20 +126,20 @@ class Upload {
$this->fixed_filename = preg_replace('/(.*)\.(th|md|original|lg)\.([\w]+)$/', '$1.$3', $this->name . '.' . $this->extension);
// Workaround for JPEG Exif data
if($this->extension == 'jpg' and array_key_exists('exif', $this->options)) {
$this->source_image_exif = NULL;
if($this->options['exif']) {
if ($this->extension == 'jpg' and array_key_exists('exif', $this->options)) {
$this->source_image_exif = null;
if ($this->options['exif']) {
// Fetch JPEG Exif data (when available)
if(function_exists('exif_read_data')) {
if (function_exists('exif_read_data')) {
$this->source_image_exif = @exif_read_data($this->downstream);
if($this->source_image_exif) {
if ($this->source_image_exif) {
$this->source_image_exif['FileName'] = $this->source_filename;
}
}
} else {
// Remove JPEG Exif data
$img = imagecreatefromjpeg($this->downstream);
if($img) {
if ($img) {
imagejpeg($img, $this->downstream, 90);
imagedestroy($img);
}
@ -149,23 +161,23 @@ class Upload {
];
// Fix image orientation
if($this->source_image_exif and $this->source_image_exif["Orientation"]) {
if ($this->source_image_exif and $this->source_image_exif["Orientation"]) {
$this->fixImageOrientation($this->downstream, $this->source_image_exif);
}
$uploaded = @rename($this->downstream, $this->uploaded_file);
@unlink($this->downstream);
if(file_exists($this->downstream)) {
if (file_exists($this->downstream)) {
error_log("Warning: temp file " . $this->downstream . "wasn't removed.");
}
if(!$uploaded) {
if (!$uploaded) {
throw new UploadException("Can't move temp file to its destination", 400);
}
// For some PHP environments
if(!$this->storage_id) {
if (!$this->storage_id) {
@chmod($this->uploaded_file, 0644);
}
@ -175,17 +187,28 @@ class Upload {
'name' => G\get_filename_without_extension($this->uploaded_file),
'fileinfo' => G\get_image_fileinfo($this->uploaded_file)
);
}
// Get available (supported) extensions
public static function getAvailableImageFormats() {
public static function getAvailableImageFormats()
{
$formats = Settings::get('upload_available_image_formats');
return explode(',', $formats);
}
//remove query string from url to get correct image name
protected function getNameFromURL()
{
if (strpos($this->source, '?')) {
return substr($this->source, 0, strpos($this->source, '?'));
} else {
return $this->source;
}
}
// Failover since v3.8.12
public static function getEnabledImageFormats() {
public static function getEnabledImageFormats()
{
return Image::getEnabledImageFormats();
}
@ -194,35 +217,35 @@ class Upload {
* This checks for valid input source data
* @Exception 1XX
*/
protected function validateInput() {
protected function validateInput()
{
$check_missing = ["type", "source", "destination"];
missing_values_to_exception($this, "CHV\UploadException", $check_missing, 100);
// Validate $type
if(!preg_match("/^(url|file)$/", $this->type)) {
if (!preg_match("/^(url|file)$/", $this->type)) {
throw new UploadException('Invalid $type "'.$this->type.'"', 110);
}
// Handle flood
$flood = self::handleFlood();
if($flood) {
if ($flood) {
throw new UploadException(strtr('Flood detected. You can only upload %limit% images per %time%', ['%limit%' => $flood['limit'], '%time%' => $flood['by']]), 130);
}
// Validate $source
if($this->type == 'file') {
if(count($this->source) < 5) { // Valid $_FILES ?
if ($this->type == 'file') {
if (count($this->source) < 5) { // Valid $_FILES ?
throw new UploadException("Invalid file source", 120);
}
} else if($this->type == "url") {
if(!G\is_image_url($this->source) && !G\is_url($this->source)) {
} elseif ($this->type == "url") {
if (!G\is_image_url($this->source) && !G\is_url($this->source)) {
throw new UploadException("Invalid image URL", 122);
}
}
// Validate $destination
if(!is_dir($this->destination)) { // Try to create the missing directory
if (!is_dir($this->destination)) { // Try to create the missing directory
$base_dir = G\add_ending_slash(G_ROOT_PATH . explode('/', preg_replace('#'.G_ROOT_PATH.'#', '', $this->destination, 1))[0]);
$base_perms = fileperms($base_dir);
@ -232,48 +255,45 @@ class Upload {
chmod($this->destination, $base_perms);
umask($old_umask);
if(!$make_destination) {
if (!$make_destination) {
throw new UploadException('$destination '.$this->destination.' is not a dir', 130);
}
}
// Can read $destination dir?
if(!is_readable($this->destination)) {
if (!is_readable($this->destination)) {
throw new UploadException("Can't read target destination dir", 131);
}
// Can write $destination dir?
if(!is_writable($this->destination)) {
if (!is_writable($this->destination)) {
throw new UploadException("Can't write target destination dir", 132);
}
// Fix $destination trailing
$this->destination = G\add_ending_slash($this->destination);
}
/**
* Fetch the $source file
* @Exception 2XX
*/
protected function fetchSource() {
protected function fetchSource()
{
// Set the downstream file
$this->downstream = @tempnam(sys_get_temp_dir(), 'chvtemp');
if(!$this->downstream || !@is_writable($this->downstream)) {
if (!$this->downstream || !@is_writable($this->downstream)) {
$this->downstream = @tempnam($this->destination, 'chvtemp');
if(!$this->downstream) {
if (!$this->downstream) {
throw new UploadException("Can't get a tempnam", 200);
}
}
if($this->type == 'file') {
if($this->source['error'] !== UPLOAD_ERR_OK) {
switch($this->source['error']) {
if ($this->type == 'file') {
if ($this->source['error'] !== UPLOAD_ERR_OK) {
switch ($this->source['error']) {
case UPLOAD_ERR_INI_SIZE: // 1
throw new UploadException('File too big', 201);
break;
@ -296,33 +316,33 @@ class Upload {
throw new UploadException('The upload was stopped', 201);
break;
}
}
if(!@rename($this->source['tmp_name'], $this->downstream)) {
if (!@rename($this->source['tmp_name'], $this->downstream)) {
throw new UploadException("Can't move temp file to the target upload dir", 203);
}
} else if($this->type == "url") {
} elseif ($this->type == "url") {
try {
G\fetch_url($this->source, $this->downstream);
} catch(Exception $e) {
} catch (Exception $e) {
throw new UploadException($e->getMessage(), 202);
}
}
$this->source_filename = basename($this->type == "file" ? $this->source["name"] : $this->source);
}
protected function fixImageOrientation($image_filename, $exif) {
if($exif['Orientation'] == 1) return;
switch($this->extension) {
protected function fixImageOrientation($image_filename, $exif)
{
if ($exif['Orientation'] == 1) {
return;
}
switch ($this->extension) {
case 'jpg':
$image = imagecreatefromjpeg($image_filename);
break;
}
switch($exif['Orientation']) {
switch ($exif['Orientation']) {
case 3:
$image = imagerotate($image, 180, 0);
break;
@ -341,68 +361,68 @@ class Upload {
* This checks for valid input source data
* @Exception 3XX
*/
protected function validateSourceFile() {
protected function validateSourceFile()
{
// Nothing to do here
if(!file_exists($this->downstream)) {
if (!file_exists($this->downstream)) {
throw new UploadException("Can't fetch target upload source (downstream)", 300);
}
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
// file info?
if(!$this->source_image_fileinfo) {
if (!$this->source_image_fileinfo) {
throw new UploadException("Can't get target upload source info", 310);
}
// Valid image fileinto?
if($this->source_image_fileinfo['width'] == '' || $this->source_image_fileinfo['height'] == '') {
if ($this->source_image_fileinfo['width'] == '' || $this->source_image_fileinfo['height'] == '') {
throw new UploadException("Invalid image", 311);
}
// Available image format?
if(!in_array($this->source_image_fileinfo['extension'], self::getAvailableImageFormats())) {
if (!in_array($this->source_image_fileinfo['extension'], self::getAvailableImageFormats())) {
throw new UploadException("Unavailable image format", 313);
}
// Enabled image format?
if(!in_array($this->source_image_fileinfo['extension'], $this->options['allowed_formats'])) {
if (!in_array($this->source_image_fileinfo['extension'], $this->options['allowed_formats'])) {
throw new UploadException(sprintf("Disabled image format (%s)", $this->source_image_fileinfo['extension']), 314);
}
// Mime
if(!$this->isValidImageMime($this->source_image_fileinfo["mime"])) {
if (!$this->isValidImageMime($this->source_image_fileinfo["mime"])) {
throw new UploadException("Invalid image mimetype", 312);
}
// Size
if(!$this->options['max_size']) {
if (!$this->options['max_size']) {
$this->options['max_size'] = self::getDefaultOptions()['max_size'];
}
if($this->source_image_fileinfo["size"] > $this->options["max_size"]) {
if ($this->source_image_fileinfo["size"] > $this->options["max_size"]) {
throw new UploadException("File too big - max " . G\format_bytes($this->options["max_size"]), 313);
}
// BMP?
if($this->source_image_fileinfo['extension'] == 'bmp') {
if ($this->source_image_fileinfo['extension'] == 'bmp') {
$this->ImageConvert = new ImageConvert($this->downstream, "png", $this->downstream);
$this->downstream = $this->ImageConvert->out;
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
}
}
// Handle flood uploads
protected static function handleFlood() {
protected static function handleFlood()
{
$logged_user = Login::getUser();
if(!getSetting('flood_uploads_protection') || $logged_user['is_admin']) {
return FALSE;
if (!getSetting('flood_uploads_protection') || $logged_user['is_admin']) {
return false;
}
$flood_limit = [];
foreach(['minute', 'hour', 'day', 'week', 'month'] as $v) {
foreach (['minute', 'hour', 'day', 'week', 'month'] as $v) {
$flood_limit[$v] = getSetting('flood_uploads_' . $v);
}
@ -415,24 +435,26 @@ class Upload {
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 DAY), 1, NULL)) AS day,
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 WEEK), 1, NULL)) AS week,
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH), 1, NULL)) AS month
FROM ".DB::getTable('images')." WHERE image_uploader_ip='".G\get_client_ip()."' AND image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH)");
} catch(Exception $e) {} // Silence
FROM ".DB::getTable('images')." WHERE image_uploader_ip='".G\get_client_ip()."' AND image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH)"
);
} catch (Exception $e) {
} // Silence
$is_flood = FALSE;
$is_flood = false;
$flood_by = '';
foreach(['minute', 'hour', 'day', 'week', 'month'] as $v) {
if($flood_limit[$v] > 0 and $flood_db[$v] >= $flood_limit[$v]) {
foreach (['minute', 'hour', 'day', 'week', 'month'] as $v) {
if ($flood_limit[$v] > 0 and $flood_db[$v] >= $flood_limit[$v]) {
$flood_by = $v;
$is_flood = TRUE;
$is_flood = true;
break;
}
}
if($is_flood) {
if(getSetting('flood_uploads_notify') and !$_SESSION['flood_uploads_notify'][$flood_by]) {
if ($is_flood) {
if (getSetting('flood_uploads_notify') and !$_SESSION['flood_uploads_notify'][$flood_by]) {
try {
$message = strtr('Flooding IP <a href="'.G\get_base_url('search/images/?q=ip:%ip').'">%ip</a>', ['%ip' => G\get_client_ip()]) . '<br>';
if($logged_user) {
if ($logged_user) {
$message .= 'User <a href="'.$logged_user['url'].'">'.$logged_user['name'].'</a><br>';
}
$message .= '<br>';
@ -443,22 +465,27 @@ class Upload {
$message .= 'Month: '.$flood_db['week']."<br>";
system_notification_email(['subject' => 'Flood report IP '. G\get_client_ip(), 'message' => $message]);
$_SESSION['flood_uploads_notify'][$flood_by] = true;
} catch(Exception $e) {} // Silence
} catch (Exception $e) {
} // Silence
}
return ['flood' => TRUE, 'limit' => $flood_limit[$flood_by], 'count' => $flood_db[$flood_by], 'by' => $flood_by];
return ['flood' => true, 'limit' => $flood_limit[$flood_by], 'count' => $flood_db[$flood_by], 'by' => $flood_by];
}
return FALSE;
return false;
}
protected function isValidImageMime($mime) {
protected function isValidImageMime($mime)
{
return preg_match("@image/(gif|pjpeg|jpeg|png|x-png|bmp|x-ms-bmp|x-windows-bmp)$@", $mime);
}
protected function isValidNamingOption($string) {
protected function isValidNamingOption($string)
{
return in_array($string, array("mixed", "random", "original"));
}
}
class UploadException extends Exception {}
class UploadException extends Exception
{
}

View File

@ -20,10 +20,13 @@
*/
namespace G;
use PDO, PDOException, Exception;
class DB {
use PDO;
use PDOException;
use Exception;
class DB
{
private static $instance;
private $host = G_APP_DB_HOST;
@ -34,30 +37,30 @@ class DB {
private $driver = G_APP_DB_DRIVER;
private $pdo_attrs = G_APP_DB_PDO_ATTRS;
static $dbh;
public static $dbh;
public $query;
/**
* Connect to the DB server
* Throws an Exception on error (tay weando? en serio?)
*/
public function __construct($conn=[]) {
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) and isset(self::$dbh) and get_class(self::$dbh) == 'PDO') {
return true;
}
if(!empty($conn)) {
if (!empty($conn)) {
// Inject connection info
foreach(['host', 'user', 'name', 'pass', 'port', 'driver', 'pdo_attrs'] as $k) {
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) {
if ($this->port) {
$pdo_connect .= ';port=' . $this->port;
}
@ -87,25 +90,24 @@ class DB {
error_reporting($error_reporting);
// PDO emulate prepares if needed
if(version_compare(self::$dbh->getAttribute(PDO::ATTR_SERVER_VERSION), '5.1.17', '<')) {
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;
} 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)) {
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
@ -115,7 +117,8 @@ class DB {
* 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) {
public function setPDOAttrs($attributes)
{
$this->pdo_attrs = $attributes;
}
@ -123,11 +126,13 @@ class DB {
* 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) {
public function setPDOAttr($key, $value)
{
$this->pdo_attrs[$key] = $value;
}
public function getAttr($attr) {
public function getAttr($attr)
{
return self::$dbh->getAttribute($attr);
}
@ -135,11 +140,13 @@ class DB {
* Prepares an SQL statement to be executed by the PDOStatement::execute() method
* http://php.net/manual/en/pdo.prepare.php
*/
public function query($query) {
public function query($query)
{
$this->query = self::$dbh->prepare($query);
}
public function errorInfo() {
public function errorInfo()
{
return self::$dbh->errorInfo();
}
@ -147,9 +154,10 @@ class DB {
* 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) {
public function bind($param, $value, $type = null)
{
if (is_null($type)) {
switch (true) {
case is_int($value):
$type = PDO::PARAM_INT;
break;
@ -167,19 +175,23 @@ class DB {
$this->query->bindValue($param, $value, $type);
}
public function exec() {
public function exec()
{
return $this->query->execute();
}
public function fetchColumn() {
public function fetchColumn()
{
return $this->query->fetchColumn();
}
public function closeCursor() {
public function closeCursor()
{
return $this->query->closeCursor();
}
public function fetchAll($mode=PDO::FETCH_ASSOC) {
public function fetchAll($mode=PDO::FETCH_ASSOC)
{
$this->exec();
return $this->query->fetchAll(is_int($mode) ? $mode : PDO::FETCH_ASSOC);
}
@ -188,7 +200,8 @@ class DB {
* 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) {
public function fetchSingle($mode=PDO::FETCH_ASSOC)
{
$this->exec();
return $this->query->fetch(is_int($mode) ? $mode : PDO::FETCH_ASSOC);
}
@ -196,12 +209,13 @@ class DB {
/**
* Query and exec, return number of affected rows or FALSE
*/
public static function queryExec($query) {
public static function queryExec($query)
{
try {
$db = self::getInstance();
$db->query($query);
return $db->exec() ? $db->rowCount() : FALSE;
} catch(Exception $e) {
return $db->exec() ? $db->rowCount() : false;
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
@ -209,10 +223,11 @@ class DB {
/**
* Query and fetch single record
*/
public static function queryFetchSingle($query, $fetch_style=NULL) {
public static function queryFetchSingle($query, $fetch_style=null)
{
try {
return self::queryFetch($query, 1, $fetch_style);
} catch(Exception $e) {
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
@ -220,10 +235,11 @@ class DB {
/**
* Query and fetch all records
*/
public static function queryFetchAll($query, $fetch_style=NULL) {
public static function queryFetchAll($query, $fetch_style=null)
{
try {
return self::queryFetch($query, NULL, $fetch_style);
} catch(Exception $e) {
return self::queryFetch($query, null, $fetch_style);
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
@ -231,12 +247,13 @@ class DB {
/**
* Query fetch (core version)
*/
public static function queryFetch($query, $limit=1, $fetch_style=NULL) {
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) {
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
@ -245,7 +262,8 @@ class DB {
* 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() {
public function rowCount()
{
return $this->query->rowCount();
}
@ -253,7 +271,8 @@ class DB {
* 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() {
public function lastInsertId()
{
return self::$dbh->lastInsertId();
}
@ -261,7 +280,8 @@ class DB {
* Turns off autocommit mode
* http://php.net/manual/en/pdo.begintransaction.php
*/
public function beginTransaction(){
public function beginTransaction()
{
return self::$dbh->beginTransaction();
}
@ -269,7 +289,8 @@ class DB {
* 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(){
public function endTransaction()
{
return self::$dbh->commit();
}
@ -277,7 +298,8 @@ class DB {
* Rolls back the current transaction, as initiated by PDO::beginTransaction()
* http://php.net/manual/en/pdo.rollback.php
*/
public function cancelTransaction(){
public function cancelTransaction()
{
return self::$dbh->rollBack();
}
@ -285,7 +307,8 @@ class DB {
* Dumps the informations contained by a prepared statement directly on the output
* http://php.net/manual/en/pdostatement.debugdumpparams.php
*/
public function debugDumpParams(){
public function debugDumpParams()
{
return $this->query->debugDumpParams();
}
@ -294,22 +317,23 @@ class DB {
/**
* Get the table with its prefix
*/
public static function getTable($table) {
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') {
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)) {
if (is_array($table)) {
$join = $table['join'];
$table = $table['table'];
}
@ -318,14 +342,14 @@ class DB {
$query = 'SELECT * FROM '.$table;
if($join) {
if (isset($join) && $join) {
$query .= ' ' . $join . ' ';
}
if(is_array($values) and !empty($values)) {
if (is_array($values) and !empty($values)) {
$query .= ' WHERE ';
foreach($values as $k => $v) {
if(is_null($v)) {
foreach ($values as $k => $v) {
if (is_null($v)) {
$query .= '`'.$k.'` IS :'.$k.' '.$clause.' ';
} else {
$query .= '`'.$k.'`=:'.$k.' '.$clause.' ';
@ -335,30 +359,30 @@ class DB {
$query = rtrim($query, $clause . ' ');
if(is_array($sort) and !empty($sort)) {
if(!$sort['field']) {
if (is_array($sort) and !empty($sort)) {
if (!$sort['field']) {
$sort['field'] = 'date';
}
if(!$sort['order']) {
if (!$sort['order']) {
$sort['order'] = 'desc';
}
$query .= ' ORDER BY '.$sort['field'].' '.strtoupper($sort['order']).' ';
}
if($limit and is_int($limit)) {
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) {
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) {
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
@ -367,12 +391,12 @@ class DB {
* 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)) {
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)) {
if (!is_array($wheres)) {
throw new DBException('Expecting array values, '.gettype($wheres).' given in '. __METHOD__, 100);
}
@ -383,13 +407,13 @@ class DB {
$query = 'UPDATE `'.$table.'` SET ';
// Set the value pairs
foreach($values as $k => $v) {
foreach ($values as $k => $v) {
$query .= '`' . $k . '`=:value_' . $k . ',';
}
$query = rtrim($query, ',') . ' WHERE ';
// Set the where pairs
foreach($wheres as $k => $v) {
foreach ($wheres as $k => $v) {
$query .= '`'.$k.'`=:where_'.$k.' '.$clause.' ';
}
$query = rtrim($query, $clause.' ');
@ -399,33 +423,32 @@ class DB {
$db->query($query);
// Bind the values
foreach($values as $k => $v) {
foreach ($values as $k => $v) {
$db->bind(':value_'.$k, $v);
}
foreach($wheres as $k => $v) {
foreach ($wheres as $k => $v) {
$db->bind(':where_'.$k, $v);
}
return $db->exec() ? $db->rowCount() : FALSE;
} catch(Exception $e) {
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)) {
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) {
foreach ($values as $k => $v) {
$table_fields[] = $k;
}
@ -436,14 +459,13 @@ class DB {
try {
$db = self::getInstance();
$db->query($query);
foreach($values as $k => $v) {
foreach ($values as $k => $v) {
$db->bind(':'.$k, $v);
}
return $db->exec() ? $db->lastInsertId() : FALSE;
} catch(Exception $e) {
return $db->exec() ? $db->lastInsertId() : false;
} catch (Exception $e) {
throw new DBException($e->getMessage(), 400);
}
}
/**
@ -451,10 +473,10 @@ class DB {
* 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})) {
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);
}
}
@ -462,13 +484,13 @@ class DB {
$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
foreach ($values as $k => $v) {
if (preg_match('/^([+-]{1})\s*([\d]+)$/', $v, $matches)) { // 1-> op 2-> number
$query .= '`' . $k . '`=';
if($matches[1] == '+') {
if ($matches[1] == '+') {
$query .= '`' . $k . '`' . $matches[1] . $matches[2] . ',';
}
if($matches[1] == '-') {
if ($matches[1] == '-') {
$query .= 'GREATEST(cast(`'.$k.'` AS SIGNED) - '.$matches[2].', 0),';
}
}
@ -477,7 +499,7 @@ class DB {
$query = rtrim($query, ',') . ' WHERE ';
// Set the where pairs
foreach($wheres as $k => $v) {
foreach ($wheres as $k => $v) {
$query .= '`'.$k.'`=:where_'.$k.' '.$clause.' ';
}
$query = rtrim($query, $clause.' ');
@ -485,23 +507,22 @@ class DB {
try {
$db = self::getInstance();
$db->query($query);
foreach($wheres as $k => $v) {
foreach ($wheres as $k => $v) {
$db->bind(':where_'.$k, $v);
}
return $db->exec() ? $db->rowCount() : false;
} catch(Exception $e) {
} 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)) {
public static function delete($table, $values, $clause='AND')
{
if (!is_array($values)) {
throw new DBException('Expecting array values, '.gettype($values).' given in '. __METHOD__, 100);
}
@ -511,7 +532,7 @@ class DB {
$query = 'DELETE FROM `'.$table.'` WHERE ';
$table_fields = array();
foreach($values as $k => $v) {
foreach ($values as $k => $v) {
$query .= '`'.$k.'`=:'.$k.' '.$clause.' ';
}
$query = rtrim($query, $clause.' ');
@ -519,29 +540,30 @@ class DB {
try {
$db = self::getInstance();
$db->query($query);
foreach($values as $k => $v) {
foreach ($values as $k => $v) {
$db->bind(':'.$k, $v);
}
return $db->exec() ? $db->rowCount() : FALSE;
} catch(Exception $e) {
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)) {
private static function validateClause($clause, $method=null)
{
if (!is_null($clause)) {
$clause = strtoupper($clause);
if(!in_array($clause, ['AND', 'OR'])) {
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
{
}